From ab33eed8d37672e3acadb69c217685ad459a1a5b Mon Sep 17 00:00:00 2001 From: Diego <2248356998@qq.com> Date: Fri, 6 Jun 2025 21:13:58 +0800 Subject: [PATCH] 10.7.22 --- src/.editorconfig | 33 +- .../IgnoreRolePermissionAttribute.cs | 4 +- .../Const/CacheConst.cs | 2 +- .../Const/HubConst.cs | 2 +- .../Const/ResourceConst.cs | 2 +- .../Const/RoleConst.cs | 2 +- .../Const/SqlSugarConst.cs | 2 +- .../Filter/RequestAuditAttribute.cs | 12 +- .../Filter/SuppressRequestAuditAttribute.cs | 12 +- .../Gitee/AdminOAuthHandler.cs | 15 +- .../Logging/LoggingConst.cs | 2 +- .../Provider/BlazorAuthenticationHandler.cs | 4 +- .../Services/Resource/SysResourceService.cs | 2 +- .../ThingsGateway.Admin.Application.csproj | 2 +- .../Util/ClearTokenUtil.cs | 2 +- .../Util/NoticeUtil.cs | 2 +- .../Util/OpenApiUtil.cs | 2 +- .../Util/OrgUtil.cs | 2 +- .../Util/PositionUtil.cs | 2 +- .../Util/RoleUtil.cs | 2 +- .../Util/UserUtil.cs | 2 +- .../Util/VerificatInfoUtil.cs | 2 +- .../Const/AdminOperConst.cs | 2 +- .../Pages/User/SysUserAvatarEdit.razor.cs | 2 +- .../UserCenter/UserInfoEditComponent.razor.cs | 2 +- .../Util/ResourceUtil.cs | 2 +- .../ThingsGateway.AdminServer/GlobalUsings.cs | 13 +- .../Attributes/IgnoreExcelAttribute.cs | 0 .../Attributes/IgnoreSeedDataAttribute.cs | 0 .../Const/ClaimConst.cs | 2 +- .../Entity/BaseEntity.cs | 0 .../Extensions/ExportExcelExtensions.cs | 0 .../Extensions/FileExtensions.cs | 0 .../Extensions/QueryPageOptionsExtensions.cs | 0 .../Extensions/SqlSugarExtensions.cs | 2 +- src/Admin/ThingsGateway.DB/GlobalUsings.cs | 11 + .../AppService/ClaimsPrincipalService.cs | 0 .../AppService/IClaimsPrincipalService.cs | 0 .../SugarAopService/ISugarAopService.cs | 0 .../SugarAopService/SugarAopService.cs | 0 .../Services/SugarService/BaseService.cs | 0 .../SqlSugar/CodeFirstUtils.cs | 0 .../SqlSugar/DbContext.cs | 0 .../SqlSugar/ISqlSugarEntitySeedData.cs | 0 .../SqlSugar/SeedDataUtil.cs | 2 +- .../SqlSugar/SqlSugarOptions.cs | 0 .../Startup.cs | 0 .../Static/UserManager.cs | 0 .../ThingsGateway.DB/ThingsGateway.DB.csproj | 33 + .../Util/CommonUtils.cs | 0 src/Admin/ThingsGateway.Furion/App/App.cs | 4 +- .../App/Extensions/ObjectExtensions.cs | 4 +- .../Binders/FromConvertBinderProvider.cs | 3 +- .../Attributes/AppAuthorizeAttribute.cs | 2 +- .../Attributes/DependsOnAttribute.cs | 4 +- .../CorsAccessor/Internal/Penetrates.cs | 2 +- .../Encryptions/AESEncryption.cs | 2 +- .../Encryptions/DESEncryption.cs | 2 +- .../Encryptions/KSortEncryption.cs | 2 +- .../Encryptions/PBKDF2Encryption.cs | 2 +- .../Encryptions/SHA1Encryption.cs | 2 +- .../Attributes/DataValidationAttribute.cs | 1 + ...ncyInjectionServiceCollectionExtensions.cs | 2 +- ...ApiControllerApplicationModelConvention.cs | 3 +- ...piControllerServiceCollectionExtensions.cs | 2 +- .../DynamicApiRuntimeChangeProvider.cs | 12 +- .../Exceptions/AppFriendlyException.cs | 8 + .../FriendlyException/Retry.cs | 2 +- .../Monitors/LoggingMonitorAttribute.cs | 5 +- .../Internal/StringLoggingPartSetters.cs | 2 +- ...ObjectMapperServiceCollectionExtensions.cs | 2 +- .../Schedule/Builders/SchedulerBuilder.cs | 2 +- .../Schedule/Extensions/ScheduleExtensions.cs | 2 +- .../HostedServices/ScheduleHostedService.cs | 2 +- .../Schedule/Http/HttpJob.cs | 2 +- .../Builders/SpecificationDocumentBuilder.cs | 2 +- .../ThingsGateway.Furion/Templates/TP.cs | 2 +- .../TimeCrontab/Crontab.Internal.cs | 2 +- .../UnifyResult/UnifyContext.cs | 4 +- .../Core/Extensions/TypeExtensions.cs | 5 +- .../Core/Extensions/V5_ObjectExtensions.cs | 4 +- .../Builders/HttpRequestBuilder.Methods.cs | 2 +- .../Shapeless/Clay/Clay.Events.cs | 2 +- .../Shapeless/Clay/Clay.Exports.cs | 11 +- .../Attributes/MinValueAttribute.cs | 2 + .../Buffers/SpanReader.cs | 1 - .../Caching/MemoryCache.cs | 6 +- .../Collections/ObjectPool.cs | 2 +- .../Common/FileUtil.cs | 2 +- .../Common/MachineInfo.cs | 8 +- .../ThingsGateway.NewLife.X/Common/PinYin.cs | 2 +- .../ThingsGateway.NewLife.X/Common/Runtime.cs | 4 +- .../Configuration/ConfigHelper.cs | 2 +- .../Configuration/IConfigSection.cs | 2 +- .../Configuration/IniConfigProvider.cs | 2 +- .../Event/WeakAction.cs | 2 +- .../Extension/DateExtensions.cs | 12 +- .../Extension/EndPointExtensions.cs | 23 +- .../Extension/ProcessHelper.cs | 8 +- .../Extension/StringHelper.cs | 10 +- .../ThingsGateway.NewLife.X/IO/ExcelReader.cs | 6 +- .../ThingsGateway.NewLife.X/IO/FileSource.cs | 2 +- .../ThingsGateway.NewLife.X/IO/IOHelper.cs | 6 +- .../ThingsGateway.NewLife.X/IO/PathHelper.cs | 6 +- .../ThingsGateway.NewLife.X/Logger/Logger.cs | 2 +- .../Logger/TextFileLog.cs | 4 +- .../Net/IDnsResolver.cs | 6 +- .../ThingsGateway.NewLife.X/Net/NetHelper.cs | 18 +- .../ThingsGateway.NewLife.X/Net/NetUri.cs | 2 +- .../Net/TcpConnectionInformation2.cs | 4 +- .../Reflection/AssemblyX.cs | 10 +- .../Reflection/AttributeX.cs | 10 +- .../Reflection/IReflect.cs | 6 +- .../Reflection/Reflect.cs | 4 +- .../Reflection/ScriptEngine.cs | 2 +- .../ThingsGateway.NewLife.X/Security/Asn1.cs | 2 +- .../Security/Certificate.cs | 4 +- .../Security/DSAHelper.cs | 2 +- .../Security/RSAHelper.cs | 2 +- .../Security/SecurityHelper.cs | 8 +- .../Serialization/Binary/Binary.cs | 4 +- .../Serialization/DataMemberResolver.cs | 2 +- .../Serialization/SerialHelper.cs | 4 +- .../Serialization/Xml/Xml.cs | 4 +- .../Stub/ScriptIgnoreAttribute.cs | 1 + .../Threading/TimerScheduler.cs | 2 +- .../Threading/TimerX.cs | 6 +- src/Admin/ThingsGateway.NewLife.X/Web/Link.cs | 4 +- .../Xml/SerializableDictionary.cs | 3 - .../ThingsGateway.NewLife.X/Xml/XmlConfig.cs | 2 +- .../ThingsGateway.NewLife.X/Xml/XmlHelper.cs | 4 +- .../Common/RandomHelper.cs | 2 +- .../ThingsGateway.Razor/Const/WebsiteConst.cs | 2 +- .../Extensions/ObjectExtensions.cs | 7 +- .../ThingsGateway.Razor/Util/LocalizerUtil.cs | 2 +- src/Admin/ThingsGateway.SqlSugar/.txt | 3 + .../QuestDb/CsvHelperEnumToIntConverter.cs | 31 + .../QuestDb/QuestDbPageSizeBulkCopy.cs | 33 + .../QuestDb/QuestDbRestAPHelper.cs | 56 + .../QuestDb/QuestDbRestAPI.cs | 240 + .../QuestDbSqlSugarClientExtensions.cs | 10 + .../Abstract/AdoProvider/AdoAccessory.cs | 82 + .../Sugar/Abstract/AdoProvider/AdoProvider.cs | 1836 ++++++++ .../Sugar/Abstract/AopProvider/AopProvider.cs | 27 + .../Abstract/CacheProvider/CacheProvider.cs | 37 + .../CodeFirstProvider/CodeFirstProvider.cs | 758 +++ .../SplitCodeFirstProvider.cs | 66 + .../TableDifferenceProvider.cs | 176 + .../DbBindProvider/DbBindAccessory.cs | 446 ++ .../Abstract/DbBindProvider/DbBindProvider.cs | 376 ++ .../IDataReaderEntityBuilder.cs | 511 +++ .../DbBindProvider/IDataRecordExtensions.cs | 399 ++ .../DbFirstProvider/DbFirstProvider.cs | 605 +++ .../DbFirstProvider/DbFirstTemplate.cs | 73 + .../Sugar/Abstract/DbFirstProvider/DbRazor.cs | 93 + .../Abstract/DbMaintenanceProvider/Methods.cs | 908 ++++ .../DbMaintenanceProvider/Properties.cs | 67 + .../DeleteProvider/DeleteMethodInfo.cs | 61 + .../DeleteProvider/DeleteNavMethodInfo.cs | 49 + .../Abstract/DeleteProvider/DeleteablePage.cs | 81 + .../DeleteProvider/DeleteableProvider.cs | 781 ++++ .../DeleteProvider/LogicDeleteProvider.cs | 162 + .../SplitTableDeleteByObjectProvider.cs | 56 + .../SplitTableDeleteProvider.cs | 93 + .../DynamicBuilder/CommonMethodInfo.cs | 90 + .../DynamicBuilder/DynamicBuilderHelper.cs | 77 + .../DynamicBuilder/DynamicOneselfType.cs | 28 + .../DynamicBuilder/DynamicProperyBuilder.cs | 129 + .../Sugar/Abstract/DynamicBuilder/EmitTool.cs | 62 + .../Sugar/Abstract/DynamicBuilder/Helper.cs | 115 + .../Sugar/Abstract/DynamicBuilder/Master.cs | 70 + .../EntityColumnExtension.cs | 63 + .../EntityMaintenance/EntityMaintenance.cs | 483 ++ .../ExecuteNavProvider/DeleteNavManyToMany.cs | 81 + .../ExecuteNavProvider/DeleteNavOneToMany.cs | 106 + .../ExecuteNavProvider/DeleteNavOneToOne.cs | 33 + .../ExecuteNavProvider/DeleteNavProvider.cs | 167 + .../ExecuteNavProvider/DeleteNavTask.cs | 175 + .../ExecuteNavProvider/InsertNavProvider.cs | 145 + .../InsertNavProviderHelper.cs | 206 + .../InsertNavProviderManyToMany.cs | 165 + .../InsertNavProviderOneToMany.cs | 104 + .../InsertNavProviderOneToOne.cs | 59 + .../ExecuteNavProvider/InsertNavTask.cs | 249 + .../Abstract/ExecuteNavProvider/NavContext.cs | 12 + .../ExecuteNavProvider/UpdateNavManyToMany.cs | 194 + .../ExecuteNavProvider/UpdateNavOneToMany.cs | 338 ++ .../ExecuteNavProvider/UpdateNavOneToOne.cs | 68 + .../ExecuteNavProvider/UpdateNavProvider.cs | 230 + .../UpdateNavProviderHelper.cs | 198 + .../ExecuteNavProvider/UpdateNavTask.cs | 226 + .../ExpressionableProvider/Expressionable.cs | 585 +++ .../Abstract/FastestProvider/FastBuilder.cs | 72 + .../FastestProvider/FastestProvider.cs | 540 +++ .../Sugar/Abstract/FastestProvider/Private.cs | 350 ++ .../Sugar/Abstract/FastestProvider/Setting.cs | 54 + .../Abstract/FastestProvider/SplitFastest.cs | 141 + .../Abstract/FilterProvider/FilterProvider.cs | 146 + .../Abstract/GridSave/GridSaveProvider.cs | 90 + .../InsertableProvider/InsertMethodInfo.cs | 125 + .../InsertableProvider/InsertNavMethodInfo.cs | 49 + .../InsertableProvider/InsertableHelper.cs | 744 +++ .../InsertableProvider/InsertablePage.cs | 160 + .../InsertableProvider/InsertableProvider.cs | 793 ++++ .../InsertableProvider/ParameterInsertable.cs | 209 + .../InsertableProvider/SplitInsertable.cs | 245 + .../InsertableProvider/SubInserable.cs | 323 ++ .../Entities/AppendNavInfoList.cs | 21 + .../Entities/QueryableAppendColumn.cs | 10 + .../Entities/QueryableFormat.cs | 14 + .../QueryableProvider/Entities/SqlInfo.cs | 17 + .../Entities/SubQueryToListDefaultT.cs | 8 + .../Abstract/QueryableProvider/Includes.cs | 501 ++ .../Abstract/QueryableProvider/Includes.txt | 105 + .../QueryableProvider/IncludesHelper.cs | 293 ++ .../QueryableProvider/MappingFieldsHelper.cs | 216 + .../QueryableProvider/NavSelectHelper.cs | 352 ++ .../QueryableProvider/NavigatManager.cs | 1093 +++++ .../QueryableProvider/QueryMethodInfo.cs | 428 ++ .../QueryableProvider/QueryableAccessory.cs | 9 + .../QueryableProvider/QueryableContext.cs | 10 + .../QueryableProvider/QueryableExecuteSql.cs | 948 ++++ .../QueryableExecuteSqlAsync.cs | 785 ++++ .../QueryableProvider/QueryableHelper.cs | 2544 ++++++++++ .../QueryableProvider/QueryableProperties.cs | 21 + .../QueryableProvider/QueryableProvider.cs | 1830 ++++++++ .../QueryableProvider02-05.cs | 3229 +++++++++++++ .../QueryableProvider06-10.cs | 4082 +++++++++++++++++ .../QueryableProvider11-12.cs | 1436 ++++++ .../Abstract/Reportable/ReportableProvider.cs | 285 ++ .../SaveableProvider/SaveableProvider.cs | 198 + .../Abstract/SaveableProvider/Storageable.cs | 628 +++ .../SaveableProvider/StorageableDataTable.cs | 239 + .../SaveableProvider/StorageableMethodInfo.cs | 157 + .../SaveableProvider/StorageablePage.cs | 151 + .../StorageableSplitProvider.cs | 205 + .../SqlBuilderProvider/DeleteBuilder.cs | 181 + .../Abstract/SqlBuilderProvider/Entities.cs | 14 + .../SqlBuilderProvider/InsertBuilder.cs | 396 ++ .../SqlBuilderProvider/QueryBuilder.cs | 1183 +++++ .../SqlBuilderProvider/SqlBuilderAccessory.cs | 15 + .../SqlBuilderProvider/SqlBuilderProvider.cs | 195 + .../SqlBuilderProvider_Condition.cs | 457 ++ .../SqlBuilderProvider/SqlQueryBuilder.cs | 80 + .../SqlBuilderProvider/UpdateBuilder.cs | 614 +++ .../SugarProvider/SqlSugarAccessory.cs | 683 +++ .../SugarProvider/SqlSugarCoreProvider.cs | 84 + .../SugarProvider/SqlSugarProvider.cs | 1864 ++++++++ .../SugarProvider/SqlSugarScopeProvider.cs | 862 ++++ .../UpdateProvider/ParameterUpdateable.cs | 197 + .../SplitTableUpdateByObjectProvider.cs | 164 + .../SplitTableUpdateProvider.cs | 109 + .../UpdateExpressionMethodInfo.cs | 61 + .../UpdateProvider/UpdateMethodInfo.cs | 122 + .../UpdateProvider/UpdateNavMethodInfo.cs | 49 + .../UpdateProvider/UpdateableFilter.cs | 102 + .../UpdateProvider/UpdateableHelper.cs | 889 ++++ .../Abstract/UpdateProvider/UpdateablePage.cs | 101 + .../UpdateProvider/UpdateableProvider.cs | 1015 ++++ .../UpdateProvider/UpdateableProviderT2.cs | 57 + .../UpdateProvider/UpdateableProviderT3.cs | 55 + .../UpdateProvider/UpdateableProviderT4.cs | 44 + .../Sugar/CacheScheme/CacheKeyBuider.cs | 73 + .../Sugar/CacheScheme/CacheSchemeMain.cs | 61 + .../Snowflake/DisposableAction.cs | 19 + .../DistributedSystem/Snowflake/IdWorker.cs | 129 + .../Snowflake/InvalidSystemClock.cs | 15 + .../Snowflake/SnowFlakeSingle.cs | 39 + .../Snowflake/TimeExtensions.cs | 38 + .../Snowflake/ValueToStringConverter.cs | 20 + .../Sugar/Entities/AsyncRef.cs | 16 + .../Sugar/Entities/CacheKey.cs | 16 + .../Sugar/Entities/ConditionalModel.cs | 38 + .../Sugar/Entities/ConfigQuery.cs | 62 + .../Sugar/Entities/ConnMoreSettings.cs | 37 + .../Sugar/Entities/ConnectionConfig.cs | 155 + .../Sugar/Entities/DbColumnInfo.cs | 31 + .../Sugar/Entities/DbFastestProperties.cs | 13 + .../Sugar/Entities/DbResult.cs | 10 + .../Sugar/Entities/DbTableInfo.cs | 30 + .../Sugar/Entities/DefaultCustom.cs | 107 + .../Sugar/Entities/DefaultServices.cs | 9 + .../Sugar/Entities/DeleteNavOptions.cs | 63 + .../Sugar/Entities/DiffLogModel.cs | 26 + .../Sugar/Entities/DiffType.cs | 9 + .../Sugar/Entities/DiscriminatorObject .cs | 8 + .../Sugar/Entities/DynamicSelectModel.cs | 183 + .../Sugar/Entities/EntityColumnInfo.cs | 49 + .../Sugar/Entities/EntityInfo.cs | 17 + .../Sugar/Entities/JoinInfoParameter.cs | 10 + .../Sugar/Entities/JoinMapper.cs | 8 + .../Sugar/Entities/JoinQueryInfo.cs | 61 + .../Sugar/Entities/ManyToManyConfig.cs | 10 + .../Sugar/Entities/MapperCache.cs | 153 + .../Sugar/Entities/Mapping/IgnoreComumn.cs | 8 + .../Sugar/Entities/Mapping/MappingColumn.cs | 9 + .../Sugar/Entities/Mapping/MappingTable.cs | 9 + .../Entities/Mapping/SugarMappingAttribute.cs | 463 ++ .../Sugar/Entities/ModelContext.cs | 19 + .../Sugar/Entities/NavigationInitializer.cs | 54 + .../Sugar/Entities/PageModel.cs | 12 + .../Sugar/Entities/PropertyMetadata.cs | 11 + .../Sugar/Entities/QueueItem.cs | 8 + .../Sugar/Entities/SchemaInfo.cs | 8 + .../Sugar/Entities/SingleColumnsEntity.cs | 7 + .../Sugar/Entities/SlaveConnectionConfig.cs | 12 + .../Sugar/Entities/SqlFilter.cs | 53 + .../Sugar/Entities/SqlSguarTransaction.cs | 47 + .../Sugar/Entities/SqlWith.cs | 15 + .../Sugar/Entities/StackTraceInfo.cs | 18 + .../Sugar/Entities/SubInsertTree.cs | 16 + .../Sugar/Entities/SugarAbMapping.cs | 8 + .../Sugar/Entities/SugarConnection.cs | 17 + .../Sugar/Entities/SugarDebugger.cs | 13 + .../Sugar/Entities/SugarList.cs | 66 + .../Sugar/Entities/SugarTerant.cs | 8 + .../Sugar/Enum/ApplyType.cs | 8 + .../Sugar/Enum/ConditionalType.cs | 23 + .../Sugar/Enum/DataFilterType.cs | 64 + .../Sugar/Enum/DbLockType.cs | 8 + .../Sugar/Enum/DbObjectType.cs | 9 + .../Sugar/Enum/DbType.cs | 40 + .../Sugar/Enum/InitKeyType.cs | 10 + .../Sugar/Enum/JoinType.cs | 11 + .../Sugar/Enum/LanguageType.cs | 9 + .../Sugar/Enum/NavigatType.cs | 11 + .../Sugar/Enum/OrderByType.cs | 8 + .../Sugar/Enum/ProperyType.cs | 27 + .../Sugar/Enum/ReportableDateType.cs | 12 + .../Sugar/Enum/SampleByUnit.cs | 14 + .../Sugar/Enum/SugarActionType.cs | 11 + .../Sugar/Enum/SugarDateTimeFormat.cs | 8 + .../Sugar/Enum/WhereType.cs | 9 + .../ExpressionsToSql/CaseWhen/CaseWhen.cs | 26 + .../CaseWhen/CaseWhenResolve.cs | 89 + .../Common/BinaryExpressionInfo.cs | 9 + .../Common/CommonTempDataType.cs | 10 + .../Sugar/ExpressionsToSql/Common/DateType.cs | 15 + .../ExpressionsToSql/Common/ErrorMessage.cs | 51 + .../Common/ExpResolveAccessory.cs | 8 + .../Common/ExpressionConst.cs | 14 + .../Common/ExpressionContextCase.cs | 7 + .../Common/ExpressionItems.cs | 29 + .../Common/ExpressionOutParameter.cs | 8 + .../Common/ExpressionParameter.cs | 42 + .../Common/ExpressionResult.cs | 191 + .../Common/ExpressionResultAcceptType.cs | 8 + .../ExpressionsToSql/Common/ExpressionTool.cs | 1100 +++++ .../Common/ListAnyParameter.cs | 10 + .../Common/MapperExpression.cs | 21 + .../ExpressionsToSql/Common/MapperSql.cs | 15 + .../Common/MethodCallExpressionModel.cs | 23 + .../Common/NewExpressionInfo.cs | 11 + .../Common/ParameterExpressionVisitor.cs | 84 + .../Common/ParameterReplacer.cs | 21 + .../Common/ResolveExpressType.cs | 18 + .../ExpressionsToSql/Common/SugarParameter.cs | 292 ++ .../DbMethods/DefaultDbMethod.cs | 1403 ++++++ .../ExpressionsToSql/DbMethods/IDbMethods.cs | 127 + .../ExpressionsToSql/DbMethods/SqlFunc.cs | 459 ++ .../DbMethods/SqlFuncExtendsion.cs | 24 + .../DbMethods/SqlFuncExternal.cs | 8 + .../ExpressionsToSql/ExpressionContext.cs | 266 ++ .../ResolveItems/BaseResolve.cs | 97 + .../ResolveItems/BaseResolve_Append.cs | 296 ++ .../ResolveItems/BaseResolve_Helper.cs | 207 + .../ResolveItems/BaseResolve_Item.cs | 471 ++ .../ResolveItems/BaseResolve_NewExp.cs | 110 + .../ResolveItems/BaseResolve_Property.cs | 109 + .../ResolveItems/BaseResolve_Validate.cs | 114 + .../ResolveItems/BinaryExpressionResolve.cs | 407 ++ .../ResolveItems/BlockExpressionResolve.cs | 10 + .../ResolveItems/CoalesceResolveItems.cs | 39 + .../ConditionalExpressionResolve.cs | 93 + .../ResolveItems/ConstantExpressionResolve.cs | 166 + .../ResolveItems/LambdaExpressionResolve.cs | 18 + .../ResolveItems/MapperExpressionResolve.cs | 325 ++ .../MemberConstExpressionResolve.cs | 69 + .../ResolveItems/MemberExpressionResolve.cs | 869 ++++ .../MemberInitExpressionResolve.cs | 385 ++ .../MemberNewExpressionResolve.cs | 14 + .../ResolveItems/MemberNoExpressionResolve.cs | 61 + .../MethodCallExpressionResolve.cs | 339 ++ ...thodCallExpressionResolve_BaseDateFomat.cs | 284 ++ .../MethodCallExpressionResolve_Helper.cs | 1207 +++++ .../ResolveItems/NewArrayExpessionResolve.cs | 112 + .../ResolveItems/NewExpressionResolve.cs | 174 + .../OneToManyNavgateExpression.cs | 337 ++ .../OneToManyNavgateExpressionN.cs | 311 ++ .../ResolveItems/OneToOneNavgateExpression.cs | 203 + .../OneToOneNavgateExpressionN.cs | 187 + .../TypeParameterExpressionReolve.cs | 48 + .../ResolveItems/UnaryExpressionResolve.cs | 190 + .../Subquery/Items/ISubOperation.cs | 14 + .../ExpressionsToSql/Subquery/Items/SubAnd.cs | 112 + .../Subquery/Items/SubAndIF.cs | 74 + .../ExpressionsToSql/Subquery/Items/SubAny.cs | 43 + .../ExpressionsToSql/Subquery/Items/SubAs.cs | 52 + .../Subquery/Items/SubAsWithAttr.cs | 51 + .../ExpressionsToSql/Subquery/Items/SubAvg.cs | 45 + .../Subquery/Items/SubBegin.cs | 43 + .../Subquery/Items/SubCount.cs | 44 + .../Subquery/Items/SubDistinctCount.cs | 77 + .../Subquery/Items/SubEnableTableFilter.cs | 72 + .../Subquery/Items/SubFirst.cs | 195 + .../Subquery/Items/SubFromTable.cs | 58 + .../Subquery/Items/SubGroupBy.cs | 76 + .../Subquery/Items/SubHaving.cs | 46 + .../Subquery/Items/SubInnerJoin.cs | 75 + .../Subquery/Items/SubLeftBracket.cs | 43 + .../Subquery/Items/SubLeftJoin.cs | 75 + .../ExpressionsToSql/Subquery/Items/SubMax.cs | 64 + .../ExpressionsToSql/Subquery/Items/SubMin.cs | 64 + .../Subquery/Items/SubNotAny.cs | 43 + .../Subquery/Items/SubOrderBy.cs | 113 + .../Subquery/Items/SubRightBracket.cs | 43 + .../Subquery/Items/SubSelect.cs | 92 + .../Subquery/Items/SubSelectDefault.cs | 43 + .../Subquery/Items/SubSelectStringJoin.cs | 104 + .../ExpressionsToSql/Subquery/Items/SubSum.cs | 64 + .../Subquery/Items/SubTake.cs | 89 + .../Subquery/Items/SubToList.cs | 210 + .../ExpressionsToSql/Subquery/Items/SubTop.cs | 75 + .../Subquery/Items/SubWhere.cs | 128 + .../Subquery/Items/SubWhereIF.cs | 81 + .../Subquery/Items/SubWithNoLock.cs | 50 + .../ExpressionsToSql/Subquery/SubResolve.cs | 326 ++ .../ExpressionsToSql/Subquery/SubTemplate.cs | 40 + .../ExpressionsToSql/Subquery/SubTools.cs | 91 + .../ExpressionsToSql/Subquery/Subquerable.cs | 229 + .../Subquery/SubqueryableN.cs | 695 +++ .../ExternalServiceInterface/ICacheService.cs | 13 + .../ExternalServiceInterface/IRazorService.cs | 7 + .../ISerializeService.cs | 9 + .../ISplitTableService.cs | 11 + .../Sugar/Infrastructure/ContextMethods.cs | 1282 ++++++ .../Infrastructure/DependencyManagement.cs | 166 + .../Sugar/Infrastructure/InstanceFactory.cs | 823 ++++ .../Sugar/Infrastructure/Mapper.cs | 73 + .../Sugar/Infrastructure/StaticConfig.cs | 38 + .../Sugar/IntegrationServices/CacheService.cs | 139 + .../IntegrationServices/SerializeService.cs | 105 + .../IntegrationServices/SplitTableService.cs | 274 ++ .../Sugar/Interface/IAdo.cs | 198 + .../Sugar/Interface/ICodeFirst.cs | 26 + .../Sugar/Interface/IContextMethods.cs | 51 + .../Sugar/Interface/ICustomConditionalFunc.cs | 7 + .../Sugar/Interface/IDMLBuilder.cs | 14 + .../Sugar/Interface/IDbBind.cs | 28 + .../Sugar/Interface/IDbFirst.cs | 29 + .../Sugar/Interface/IDbMaintenance.cs | 113 + .../Sugar/Interface/IDeleteable.cs | 52 + .../Sugar/Interface/IFastBuilder.cs | 18 + .../Sugar/Interface/IFastest.cs | 45 + .../Sugar/Interface/IFilter.cs | 9 + .../Sugar/Interface/IIncludes.cs | 286 ++ .../Sugar/Interface/IIncludes.txt | 106 + .../Sugar/Interface/ILambdaExpressions.cs | 33 + .../Sugar/Interface/IParameterInsertable.cs | 8 + .../Sugar/Interface/IQueryable.cs | 1970 ++++++++ .../Sugar/Interface/IReportable.cs | 10 + .../Sugar/Interface/ISaveable.cs | 22 + .../Sugar/Interface/ISimpleClient.cs | 142 + .../Sugar/Interface/ISqlBuilder.cs | 49 + .../Sugar/Interface/ISqlSugarClient.cs | 245 + .../Sugar/Interface/IStorageable.cs | 191 + .../Sugar/Interface/ISubInsertable.cs | 14 + .../Sugar/Interface/ISugarDataConverter.cs | 11 + .../Sugar/Interface/ISugarRepository.cs | 7 + .../Sugar/Interface/ITenant.cs | 46 + .../Sugar/Interface/IUpdateable.cs | 136 + .../Sugar/Interface/Insertable.cs | 67 + .../Json2Sql/DynamicLinq/DynamicCoreHelper.cs | 171 + .../Json2Sql/DynamicLinq/DynamicParameters.cs | 50 + .../SqlSugarDynamicExpressionParser.cs | 53 + .../Json2Sql/Entities/JsonDeleteResult.cs | 7 + .../Json2Sql/Entities/JsonInsertResult.cs | 8 + .../Json2Sql/Entities/JsonQueryResult.cs | 9 + .../Json2Sql/Entities/JsonTableConfig.cs | 23 + .../Json2Sql/Entities/JsonUpdateResult.cs | 7 + .../Sugar/Json2Sql/Enums/AsNameFormatType.cs | 8 + .../Sugar/Json2Sql/Enums/Json2SqlType.cs | 12 + .../Sugar/Json2Sql/Interface/IFuncModel.cs | 6 + .../Sugar/Json2Sql/Interface/IJsonClient.cs | 13 + .../Interface/IJsonDeleteableProvider.cs | 7 + .../Interface/IJsonInsertableProvider.cs | 7 + .../Sugar/Json2Sql/Interface/IJsonProvider.cs | 11 + .../Interface/IJsonQueryableProvider.cs | 9 + .../Sugar/Json2Sql/Interface/IJsonToModel.cs | 15 + .../Interface/IJsonUpdateableProvider.cs | 7 + .../Sugar/Json2Sql/Interface/IModelToSql.cs | 14 + .../Json2Sql/Interface/ISugarQueryable.cs | 16 + .../Sugar/Json2Sql/JsonModels/FuncModel.cs | 18 + .../Sugar/Json2Sql/JsonModels/GroupByModel.cs | 11 + .../Sugar/Json2Sql/JsonModels/JoinModel.cs | 11 + .../Json2Sql/JsonModels/JsonQueryParameter.cs | 13 + .../Json2Sql/JsonModels/JsonSqlModels.cs | 18 + .../Json2Sql/JsonModels/JsonTableNameInfo.cs | 9 + .../Sugar/Json2Sql/JsonModels/OrderByModel.cs | 12 + .../Json2Sql/JsonModels/SelectFieldModel.cs | 17 + .../Sugar/Json2Sql/JsonToModel/Helper.cs | 43 + .../JsonToModel/JsonSqlFuncToModel.cs | 84 + .../JsonToModel/JsonSqlFuncToParameters.cs | 62 + .../JsonToModel/JsonToColumnsModels.cs | 57 + .../JsonToModel/JsonToGroupByModels.cs | 27 + .../Json2Sql/JsonToModel/JsonToJoinModels.cs | 26 + .../JsonToModel/JsonToOrderByModels.cs | 34 + .../JsonToModel/JsonToSelectModels.cs | 65 + .../Json2Sql/ModelToSql/FuncModelToSql.cs | 182 + .../Json2Sql/ModelToSql/GroupByModelToSql.cs | 31 + .../Json2Sql/ModelToSql/OrderByModelToSql.cs | 31 + .../Json2Sql/ModelToSql/SelectModelToSql.cs | 54 + .../Sugar/Json2Sql/ModelToSql/SqlPart.cs | 152 + .../Provider/Common/JsonCommonProvider.cs | 87 + .../Deleteable/JsonDeleteableProvider.cs | 88 + .../Provider/Insertable/AppendIdentity.cs | 12 + .../Provider/Insertable/AppendName.cs | 16 + .../Json2Sql/Provider/Insertable/AppendRow.cs | 13 + .../Json2Sql/Provider/Insertable/Helper.cs | 42 + .../Insertable/JsonInsertableProvider.cs | 60 + .../Provider/Insertable/PrivateProperty.cs | 16 + .../Provider/Queryable/ApendJoinLastAfter.cs | 43 + .../Json2Sql/Provider/Queryable/AppendFrom.cs | 26 + .../Provider/Queryable/AppendGroupBy.cs | 17 + .../Provider/Queryable/AppendHaving.cs | 17 + .../Json2Sql/Provider/Queryable/AppendJoin.cs | 52 + .../Provider/Queryable/AppendOrderBy.cs | 18 + .../Json2Sql/Provider/Queryable/AppendPage.cs | 24 + .../Provider/Queryable/AppendSelect.cs | 79 + .../Provider/Queryable/AppendWhere.cs | 33 + .../Json2Sql/Provider/Queryable/Entities.cs | 11 + .../Json2Sql/Provider/Queryable/Helper.cs | 100 + .../Queryable/JsonQueryableProvider.cs | 114 + .../Json2Sql/Provider/Queryable/Property.cs | 22 + .../Provider/Queryable/RegisterAop.cs | 37 + .../Provider/Queryable/ResultDefault.cs | 45 + .../Provider/Queryable/ResultHelper.cs | 64 + .../Json2Sql/Provider/Updateable/AppendRow.cs | 21 + .../Provider/Updateable/AppendTable.cs | 17 + .../Provider/Updateable/AppendWhere.cs | 15 + .../Provider/Updateable/AppendWhereColumns.cs | 15 + .../Json2Sql/Provider/Updateable/Helpercs.cs | 26 + .../Updateable/JsonUpdateableProvider.cs | 82 + .../Json2Sql/Queryable/QueryableProvider.cs | 112 + .../Sugar/Json2Sql/Utils/Json2SqlConfig.cs | 31 + .../Sugar/Json2Sql/Utils/Json2SqlHelper.cs | 36 + .../Sugar/JsonClient.cs | 34 + .../Sugar/OnlyCore/Compatible.cs | 7 + .../Sugar/OnlyCore/DataExtensions.cs | 1127 +++++ .../Sugar/OnlyCore/IDataExtensions.cs.cs | 48 + .../OnlyCore/PartialExpressionContexts.cs | 32 + .../Realization/Dm/CodeFirst/DmCodeFirst.cs | 105 + .../Sugar/Realization/Dm/DbBind/DmDbBind.cs | 170 + .../Sugar/Realization/Dm/DbFirst/DmDbFirst.cs | 6 + .../Dm/DbMaintenance/DmDbMaintenance.cs | 669 +++ .../Sugar/Realization/Dm/DmProvider.cs | 246 + .../Dm/Insertable/DmInserttable.cs | 7 + .../Realization/Dm/Queryable/DmQueryable.cs | 63 + .../Realization/Dm/SqlBuilder/DmBlukCopy.cs | 150 + .../Realization/Dm/SqlBuilder/DmBuilder.cs | 61 + .../Dm/SqlBuilder/DmDeleteBuilder.cs | 7 + .../Dm/SqlBuilder/DmExpressionContext.cs | 338 ++ .../Dm/SqlBuilder/DmFastBuilder.cs | 98 + .../Dm/SqlBuilder/DmInsertBuilder.cs | 139 + .../Dm/SqlBuilder/DmQueryBuilder.cs | 193 + .../Dm/SqlBuilder/DmUpdateBuilder.cs | 135 + .../Kdbndp/CodeFirst/KdbndpCodeFirst.cs | 91 + .../Realization/Kdbndp/DbBind/KdbndpBind.cs | 162 + .../Kdbndp/DbFirst/KdbndpDbFirst.cs | 6 + .../DbMaintenance/KdbndpDbMaintenance.cs | 712 +++ .../Kdbndp/Insertable/KdbndpInserttable.cs | 99 + .../Realization/Kdbndp/KdbndpSQLProvider.cs | 196 + .../Kdbndp/Queryable/KdbndpQueryable.cs | 63 + .../Kdbndp/SqlBuilder/KdbndpBuilder.cs | 117 + .../Kdbndp/SqlBuilder/KdbndpDeleteBuilder.cs | 7 + .../SqlBuilder/KdbndpExpressionContext.cs | 563 +++ .../Kdbndp/SqlBuilder/KdbndpFastBuilder.cs | 139 + .../Kdbndp/SqlBuilder/KdbndpInsertBuilder.cs | 131 + .../Kdbndp/SqlBuilder/KdbndpQueryBuilder.cs | 149 + .../Kdbndp/SqlBuilder/KdbndpUpdateBuilder.cs | 281 ++ .../MySql/CodeFirst/MySqlCodeFirst.cs | 140 + .../Realization/MySql/DbBind/MySqlDbBind.cs | 94 + .../Realization/MySql/DbFirst/MySqlDbFirst.cs | 30 + .../MySql/DbMaintenance/DorisHelper.cs | 54 + .../MySql/DbMaintenance/MySqlDbMaintenance.cs | 810 ++++ .../Sugar/Realization/MySql/MySqlProvider.cs | 323 ++ .../MySql/PatialClass/MySqlFastBuilder.cs | 31 + .../MySql/Queryable/MySqlQueryable.cs | 72 + .../MySql/SqlBuilder/MySqlBlukCopy.cs | 202 + .../MySql/SqlBuilder/MySqlBuilder.cs | 35 + .../MySql/SqlBuilder/MySqlDeleteBuilder.cs | 7 + .../SqlBuilder/MySqlExpressionContext.cs | 332 ++ .../MySql/SqlBuilder/MySqlFastBuilder.cs | 105 + .../MySql/SqlBuilder/MySqlInsertBuilder.cs | 191 + .../MySql/SqlBuilder/MySqlQueryBuilder.cs | 165 + .../MySql/SqlBuilder/MySqlUpdateBuilder.cs | 231 + .../Oracle/CodeFirst/OracleCodeFirst.cs | 63 + .../Realization/Oracle/DbBind/OracleDbBind.cs | 136 + .../Oracle/DbFirst/OracleDbFirst.cs | 7 + .../DbMaintenance/OracleDbMaintenance.cs | 702 +++ .../Oracle/Deleteable/OracleDeleteable.cs | 10 + .../Oracle/Insertable/OracleInsertable.cs | 219 + .../Realization/Oracle/OracleProvider.cs | 341 ++ .../Oracle/Queryable/OracleQueryable.cs | 60 + .../Oracle/SqlBuilder/OracleBlukCopy.cs | 318 ++ .../Oracle/SqlBuilder/OracleBuilder.cs | 85 + .../Oracle/SqlBuilder/OracleDeleteBuilder.cs | 7 + .../SqlBuilder/OracleExpressionContext.cs | 453 ++ .../Oracle/SqlBuilder/OracleFastBuilder.cs | 122 + .../Oracle/SqlBuilder/OracleInsertBuilder.cs | 222 + .../Oracle/SqlBuilder/OracleQueryBuilder.cs | 136 + .../Oracle/SqlBuilder/OracleUpdateBuilder.cs | 168 + .../Oracle/Updateable/OracleUpdateable.cs | 58 + .../Oscar/CodeFirst/OscarCodeFirst.cs | 73 + .../Realization/Oscar/DbBind/OscarDbBind.cs | 136 + .../Realization/Oscar/DbFirst/OscarDbFirst.cs | 6 + .../Oscar/DbMaintenance/OscarDbMaintenance.cs | 415 ++ .../Oscar/Insertable/OscarInserttable.cs | 67 + .../Sugar/Realization/Oscar/OscarProvider.cs | 158 + .../Oscar/Queryable/OscarQueryable.cs | 63 + .../Oscar/SqlBuilder/OscarBuilder.cs | 97 + .../Oscar/SqlBuilder/OscarDeleteBuilder.cs | 7 + .../SqlBuilder/OscarExpressionContext.cs | 258 ++ .../Oscar/SqlBuilder/OscarInsertBuilder.cs | 95 + .../Oscar/SqlBuilder/OscarQueryBuilder.cs | 93 + .../Oscar/SqlBuilder/OscarUpdateBuilder.cs | 104 + .../CodeFirst/PostgreSQLCodeFirst.cs | 85 + .../PostgreSQL/DbBind/PostgreSQLDbBind.cs | 189 + .../PostgreSQL/DbFirst/PostgreSQLDbFirst.cs | 6 + .../DbMaintenance/PostgreSQLDbMaintenance.cs | 656 +++ .../Insertable/PostgreSQLInserttable.cs | 98 + .../PostgreSQL/PostgreSQLProvider.cs | 275 ++ .../Queryable/PostgreSqlQueryable.cs | 63 + .../SqlBuilder/PostgreSQLBuilder.cs | 124 + .../SqlBuilder/PostgreSQLDeleteBuilder.cs | 7 + .../SqlBuilder/PostgreSQLExpressionContext.cs | 521 +++ .../SqlBuilder/PostgreSQLFastBuilder.cs | 166 + .../SqlBuilder/PostgreSQLInsertBuilder.cs | 268 ++ .../SqlBuilder/PostgreSQLQueryBuilder.cs | 129 + .../SqlBuilder/PostgreSQLUpdateBuilder.cs | 288 ++ .../QuestDB/CodeFirst/QuestDBCodeFirst.cs | 101 + .../QuestDB/DbBind/QuestDBDbBind.cs | 144 + .../QuestDB/DbFirst/QuestDBDbFirst.cs | 6 + .../DbMaintenance/QuestDBDbMaintenance.cs | 503 ++ .../QuestDB/Queryable/QuestDBQueryable.cs | 67 + .../Realization/QuestDB/QuestDBProvider.cs | 214 + .../QuestDB/SqlBuilder/QuestDBBuilder.cs | 113 + .../SqlBuilder/QuestDBDeleteBuilder.cs | 7 + .../SqlBuilder/QuestDBExpressionContext.cs | 379 ++ .../QuestDB/SqlBuilder/QuestDBFastBuilder.cs | 121 + .../SqlBuilder/QuestDBInsertBuilder.cs | 115 + .../QuestDB/SqlBuilder/QuestDBQueryBuilder.cs | 169 + .../SqlBuilder/QuestDBUpdateBuilder.cs | 103 + .../SqlServer/CodeFirst/SqlServerCodeFirst.cs | 50 + .../SqlServer/DbBind/SqlServerDbBind.cs | 69 + .../SqlServer/DbFirst/SqlServerDbFirst.cs | 6 + .../DbMaintenance/SqlServerDbMaintenance.cs | 786 ++++ .../SqlServer/Queryable/SqlServerQueryable.cs | 51 + .../SqlServer/SqlBuilder/SqlServerBlukCopy.cs | 150 + .../SqlServer/SqlBuilder/SqlServerBuilder.cs | 33 + .../SqlBuilder/SqlServerDeleteBuilder.cs | 7 + .../SqlBuilder/SqlServerExpressionContext.cs | 192 + .../SqlBuilder/SqlServerFastBuilder.cs | 92 + .../SqlBuilder/SqlServerInsertBuilder.cs | 82 + .../SqlBuilder/SqlServerQueryBuilder.cs | 130 + .../SqlBuilder/SqlServerUpdateBuilder.cs | 118 + .../SqlServer/SqlServerProvider.cs | 234 + .../Sqlite/CodeFirst/SqliteCodeFirst.cs | 178 + .../Realization/Sqlite/DbBind/SqliteDbBind.cs | 109 + .../Sqlite/DbFirst/SqliteDbFirst.cs | 6 + .../DbMaintenance/SqliteDbMaintenance.cs | 610 +++ .../Sqlite/Queryable/SqliteQueryable.cs | 60 + .../Sqlite/SqlBuilder/SqliteBuilder.cs | 39 + .../Sqlite/SqlBuilder/SqliteDeleteBuilder.cs | 7 + .../SqlBuilder/SqliteExpressionContext.cs | 347 ++ .../Sqlite/SqlBuilder/SqliteFastBuilder.cs | 212 + .../Sqlite/SqlBuilder/SqliteInsertBuilder.cs | 162 + .../Sqlite/SqlBuilder/SqliteQueryBuilder.cs | 105 + .../Sqlite/SqlBuilder/SqliteUpdateBuilder.cs | 126 + .../Realization/Sqlite/SqliteProvider.cs | 137 + .../Sugar/SimpleClient.cs | 758 +++ .../Sugar/SpliteTable/SplitTableAttribute.cs | 42 + .../Sugar/SpliteTable/SplitTableContext.cs | 108 + .../Sugar/SpliteTable/SplitTableExtensions.cs | 14 + .../Sugar/SpliteTable/SplitTableInfo.cs | 13 + .../Sugar/SpliteTable/SplitType.cs | 18 + .../Sugar/SpliteTable/SplitTypeExtensions.cs | 38 + .../Sugar/SqlSugarClient.cs | 1620 +++++++ .../Sugar/SqlSugarScope.cs | 917 ++++ .../Sugar/SugarUnitOfWork.cs | 109 + .../Sugar/Utilities/CallContext.cs | 8 + .../Sugar/Utilities/CallContextAsync.cs | 22 + .../Sugar/Utilities/Check.cs | 43 + .../Sugar/Utilities/CommonExtensions.cs | 156 + .../Sugar/Utilities/DataTableExtensions.cs | 125 + .../Sugar/Utilities/DbExtensions.cs | 172 + .../Sugar/Utilities/ErrorMessage.cs | 45 + .../Utilities/ExpressionBuilderHelper.cs | 205 + .../Sugar/Utilities/FastCopy.cs | 128 + .../Sugar/Utilities/FileHelper.cs | 69 + .../Utilities/PropertyCallAdapterProvider.cs | 76 + .../Sugar/Utilities/ReflectionExtensions.cs | 77 + .../Sugar/Utilities/SugarAsyncLock.cs | 25 + .../Sugar/Utilities/SugarRetry.cs | 159 + .../Sugar/Utilities/UtilConstants.cs | 80 + .../Sugar/Utilities/UtilConvert.cs | 192 + .../Sugar/Utilities/UtilExceptions.cs | 83 + .../Sugar/Utilities/UtilExtensions.cs | 119 + .../Sugar/Utilities/UtilMethods.cs | 1954 ++++++++ .../Sugar/Utilities/UtilRandom.cs | 28 + .../Sugar/Utilities/ValidateExtensions.cs | 186 + .../Sugar/dll/Dm.nuspec | 22 + .../Sugar/dll/Kdbndp.nuspec | 22 + .../Sugar/dll/NuGet.exe | Bin 0 -> 3957976 bytes .../ThingsGateway.SqlSugar/Sugar/dll/dm.bat | 1 + .../Sugar/dll/kdbndp.bat | 1 + .../ThingsGateway.SqlSugar/TDengine/STable.cs | 14 + .../TDengine/STableAttribute.cs | 13 + .../TDengine/CodeFirst/TDengineCodeFirst.cs | 309 ++ .../TDengine/DbBind/TDengineDbBind.cs | 146 + .../TDengine/DbFirst/TDengineDbFirst.cs | 6 + .../DbMaintenance/TDengineDbMaintenance.cs | 588 +++ .../Insertable/TDengineInserttable.cs | 90 + .../TDengine/Insertable/TagInserttable.cs | 129 + .../Queryable/TDengineSqlQueryable.cs | 63 + .../TDengine/SqlBuilder/TDengineBuilder.cs | 133 + .../SqlBuilder/TDengineDeleteBuilder.cs | 7 + .../SqlBuilder/TDengineExpressionContext.cs | 444 ++ .../SqlBuilder/TDengineFastBuilder.cs | 218 + .../SqlBuilder/TDengineInsertBuilder.cs | 184 + .../SqlBuilder/TDengineQueryBuilder.cs | 120 + .../SqlBuilder/TDengineUpdateBuilder.cs | 16 + .../TDengine/TDengine/TDengineProvider.cs | 124 + .../TDengine/TDengineDataAdapter.cs | 173 + .../TDengine/Tools/DateTime16.cs | 21 + .../TDengine/Tools/DateTime19.cs | 25 + .../TDengine/Tools/FileHelper.cs | 66 + .../TDengine/Tools/SqlSugarExtensions.cs | 36 + .../TDengine/Tools/UtilConstants.cs | 69 + .../TDengine/Tools/UtilExtensions.cs | 140 + .../TDengine/Tools/UtilMethods.cs | 513 +++ .../TDengine/Tools/ValidateExtensions.cs | 166 + .../ThingsGateway.SqlSugar.csproj | 23 +- src/Directory.Build.props | 19 +- .../Attributes/UriValidationAttribute.cs | 1 + .../Channel/DDP/DDPTcpSessionClientChannel.cs | 24 +- .../Channel/DDP/DDPUdpSessionChannel.cs | 4 +- .../Channel/OtherChannel.cs | 4 +- .../Device/DeviceBase.cs | 4 +- .../Extensions/StringExtensions.cs | 6 +- .../Extensions/TypeExtensions.cs | 5 +- .../Logger/TextFileReader.cs | 2 +- .../Utils/CRC16Utils.cs | 2 +- .../Utils/DataTransUtil.cs | 2 +- .../Components/BlazorDiagramsException.cs | 8 + .../Components/Renderers/GroupRenderer.cs | 2 +- .../Components/Renderers/NodeRenderer.cs | 2 +- .../Core/Behaviors/DragMovablesBehavior.cs | 2 +- .../Core/Behaviors/DragNewLinkBehavior.cs | 2 +- .../SvgPathProperties/Parser.cs | 2 +- .../SvgPathProperties/SvgPath.cs | 4 +- .../Const/ThingsGatewayCacheConst.cs | 2 +- .../Driver/CacheDB/CacheDBUtil.cs | 2 +- .../GlobalData/GlobalData.cs | 40 +- .../Model/DeviceRunTime.cs | 3 +- .../Services/Device/DeviceServiceHelpers.cs | 4 +- .../Services/Plugin/PluginServiceUtil.cs | 4 +- .../Services/Variable/VariableService.cs | 2 +- .../Variable/VariableServiceHelpers.cs | 2 +- .../Channel/ChannelRuntimeInfo1.razor.cs | 2 +- .../Device/DeviceRuntimeInfo1.razor.cs | 3 +- .../Pages/PluginPage/PluginDebugPage.razor.cs | 2 - .../Helper/ModbusHelper.cs | 2 +- .../Helper/PackHelper.cs | 2 +- .../COM/Comn/ComInterop.cs | 4 +- .../COM/Da/OpcServer.cs | 1 - .../COM/Rcw/Interop.cs | 10 +- .../COM/Rcw/ItemProperty.cs | 12 +- .../Utils/OpcUaUtils.cs | 4 +- .../QuestDB/QuestDBProducer.cs | 3 +- .../ThingsGateway.Plugin.DB.csproj | 4 +- .../Dlt645_2007AddressComponent.razor.cs | 3 +- .../Pages/Dlt645_2007Master.razor.cs | 5 +- .../Pages/ModbusAddressComponent.razor.cs | 3 +- .../Pages/ModbusMaster.razor.cs | 5 +- .../Pages/ModbusSlave.razor.cs | 5 +- .../MqttServer/MqttServer.cs | 5 +- .../Core/ThingsGatewayNodeManager.cs | 2 +- .../Pages/OpcUaImportVariable.razor.cs | 2 +- .../Pages/SiemensS7AddressComponent.razor.cs | 3 +- .../Pages/SiemensS7Master.razor.cs | 5 +- .../Services/FileConst.cs | 2 +- .../GlobalUsings.cs | 13 +- src/Version.props | 2 +- 794 files changed, 108577 insertions(+), 410 deletions(-) rename src/Admin/{ThingsGateway.SqlSugar => ThingsGateway.DB}/Attributes/IgnoreExcelAttribute.cs (100%) rename src/Admin/{ThingsGateway.SqlSugar => ThingsGateway.DB}/Attributes/IgnoreSeedDataAttribute.cs (100%) rename src/Admin/{ThingsGateway.SqlSugar => ThingsGateway.DB}/Const/ClaimConst.cs (98%) rename src/Admin/{ThingsGateway.SqlSugar => ThingsGateway.DB}/Entity/BaseEntity.cs (100%) rename src/Admin/{ThingsGateway.SqlSugar => ThingsGateway.DB}/Extensions/ExportExcelExtensions.cs (100%) rename src/Admin/{ThingsGateway.SqlSugar => ThingsGateway.DB}/Extensions/FileExtensions.cs (100%) rename src/Admin/{ThingsGateway.SqlSugar => ThingsGateway.DB}/Extensions/QueryPageOptionsExtensions.cs (100%) rename src/Admin/{ThingsGateway.SqlSugar => ThingsGateway.DB}/Extensions/SqlSugarExtensions.cs (99%) create mode 100644 src/Admin/ThingsGateway.DB/GlobalUsings.cs rename src/Admin/{ThingsGateway.SqlSugar => ThingsGateway.DB}/Services/AppService/ClaimsPrincipalService.cs (100%) rename src/Admin/{ThingsGateway.SqlSugar => ThingsGateway.DB}/Services/AppService/IClaimsPrincipalService.cs (100%) rename src/Admin/{ThingsGateway.SqlSugar => ThingsGateway.DB}/Services/SugarAopService/ISugarAopService.cs (100%) rename src/Admin/{ThingsGateway.SqlSugar => ThingsGateway.DB}/Services/SugarAopService/SugarAopService.cs (100%) rename src/Admin/{ThingsGateway.SqlSugar => ThingsGateway.DB}/Services/SugarService/BaseService.cs (100%) rename src/Admin/{ThingsGateway.SqlSugar => ThingsGateway.DB}/SqlSugar/CodeFirstUtils.cs (100%) rename src/Admin/{ThingsGateway.SqlSugar => ThingsGateway.DB}/SqlSugar/DbContext.cs (100%) rename src/Admin/{ThingsGateway.SqlSugar => ThingsGateway.DB}/SqlSugar/ISqlSugarEntitySeedData.cs (100%) rename src/Admin/{ThingsGateway.SqlSugar => ThingsGateway.DB}/SqlSugar/SeedDataUtil.cs (99%) rename src/Admin/{ThingsGateway.SqlSugar => ThingsGateway.DB}/SqlSugar/SqlSugarOptions.cs (100%) rename src/Admin/{ThingsGateway.SqlSugar => ThingsGateway.DB}/Startup.cs (100%) rename src/Admin/{ThingsGateway.SqlSugar => ThingsGateway.DB}/Static/UserManager.cs (100%) create mode 100644 src/Admin/ThingsGateway.DB/ThingsGateway.DB.csproj rename src/Admin/{ThingsGateway.SqlSugar => ThingsGateway.DB}/Util/CommonUtils.cs (100%) create mode 100644 src/Admin/ThingsGateway.SqlSugar/.txt create mode 100644 src/Admin/ThingsGateway.SqlSugar/QuestDb/CsvHelperEnumToIntConverter.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/QuestDb/QuestDbPageSizeBulkCopy.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/QuestDb/QuestDbRestAPHelper.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/QuestDb/QuestDbRestAPI.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/QuestDb/QuestDbSqlSugarClientExtensions.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/AdoProvider/AdoAccessory.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/AdoProvider/AdoProvider.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/AopProvider/AopProvider.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/CacheProvider/CacheProvider.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/CodeFirstProvider/CodeFirstProvider.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/CodeFirstProvider/SplitCodeFirstProvider.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/CodeFirstProvider/TableDifferenceProvider.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DbBindProvider/DbBindAccessory.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DbBindProvider/DbBindProvider.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DbBindProvider/IDataReaderEntityBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DbBindProvider/IDataRecordExtensions.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DbFirstProvider/DbFirstProvider.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DbFirstProvider/DbFirstTemplate.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DbFirstProvider/DbRazor.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DbMaintenanceProvider/Methods.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DbMaintenanceProvider/Properties.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DeleteProvider/DeleteMethodInfo.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DeleteProvider/DeleteNavMethodInfo.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DeleteProvider/DeleteablePage.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DeleteProvider/DeleteableProvider.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DeleteProvider/LogicDeleteProvider.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DeleteProvider/SplitTableDeleteByObjectProvider.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DeleteProvider/SplitTableDeleteProvider.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DynamicBuilder/CommonMethodInfo.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DynamicBuilder/DynamicBuilderHelper.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DynamicBuilder/DynamicOneselfType.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DynamicBuilder/DynamicProperyBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DynamicBuilder/EmitTool.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DynamicBuilder/Helper.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DynamicBuilder/Master.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/EntityMaintenance/EntityColumnExtension.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/EntityMaintenance/EntityMaintenance.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/DeleteNavManyToMany.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/DeleteNavOneToMany.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/DeleteNavOneToOne.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/DeleteNavProvider.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/DeleteNavTask.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/InsertNavProvider.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/InsertNavProviderHelper.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/InsertNavProviderManyToMany.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/InsertNavProviderOneToMany.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/InsertNavProviderOneToOne.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/InsertNavTask.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/NavContext.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/UpdateNavManyToMany.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/UpdateNavOneToMany.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/UpdateNavOneToOne.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/UpdateNavProvider.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/UpdateNavProviderHelper.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/UpdateNavTask.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExpressionableProvider/Expressionable.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/FastestProvider/FastBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/FastestProvider/FastestProvider.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/FastestProvider/Private.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/FastestProvider/Setting.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/FastestProvider/SplitFastest.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/FilterProvider/FilterProvider.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/GridSave/GridSaveProvider.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/InsertableProvider/InsertMethodInfo.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/InsertableProvider/InsertNavMethodInfo.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/InsertableProvider/InsertableHelper.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/InsertableProvider/InsertablePage.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/InsertableProvider/InsertableProvider.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/InsertableProvider/ParameterInsertable.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/InsertableProvider/SplitInsertable.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/InsertableProvider/SubInserable.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/Entities/AppendNavInfoList.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/Entities/QueryableAppendColumn.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/Entities/QueryableFormat.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/Entities/SqlInfo.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/Entities/SubQueryToListDefaultT.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/Includes.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/Includes.txt create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/IncludesHelper.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/MappingFieldsHelper.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/NavSelectHelper.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/NavigatManager.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/QueryMethodInfo.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/QueryableAccessory.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/QueryableContext.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/QueryableExecuteSql.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/QueryableExecuteSqlAsync.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/QueryableHelper.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/QueryableProperties.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/QueryableProvider.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/QueryableProvider02-05.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/QueryableProvider06-10.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/QueryableProvider11-12.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/Reportable/ReportableProvider.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SaveableProvider/SaveableProvider.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SaveableProvider/Storageable.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SaveableProvider/StorageableDataTable.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SaveableProvider/StorageableMethodInfo.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SaveableProvider/StorageablePage.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SaveableProvider/StorageableSplitProvider.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SqlBuilderProvider/DeleteBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SqlBuilderProvider/Entities.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SqlBuilderProvider/InsertBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SqlBuilderProvider/QueryBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SqlBuilderProvider/SqlBuilderAccessory.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SqlBuilderProvider/SqlBuilderProvider.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SqlBuilderProvider/SqlBuilderProvider_Condition.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SqlBuilderProvider/SqlQueryBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SqlBuilderProvider/UpdateBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SugarProvider/SqlSugarAccessory.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SugarProvider/SqlSugarCoreProvider.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SugarProvider/SqlSugarProvider.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SugarProvider/SqlSugarScopeProvider.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/UpdateProvider/ParameterUpdateable.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/UpdateProvider/SplitTableUpdateByObjectProvider.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/UpdateProvider/SplitTableUpdateProvider.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/UpdateProvider/UpdateExpressionMethodInfo.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/UpdateProvider/UpdateMethodInfo.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/UpdateProvider/UpdateNavMethodInfo.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/UpdateProvider/UpdateableFilter.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/UpdateProvider/UpdateableHelper.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/UpdateProvider/UpdateablePage.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/UpdateProvider/UpdateableProvider.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/UpdateProvider/UpdateableProviderT2.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/UpdateProvider/UpdateableProviderT3.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/UpdateProvider/UpdateableProviderT4.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/CacheScheme/CacheKeyBuider.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/CacheScheme/CacheSchemeMain.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/DistributedSystem/Snowflake/DisposableAction.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/DistributedSystem/Snowflake/IdWorker.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/DistributedSystem/Snowflake/InvalidSystemClock.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/DistributedSystem/Snowflake/SnowFlakeSingle.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/DistributedSystem/Snowflake/TimeExtensions.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/DistributedSystem/Snowflake/ValueToStringConverter.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/AsyncRef.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/CacheKey.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/ConditionalModel.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/ConfigQuery.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/ConnMoreSettings.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/ConnectionConfig.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/DbColumnInfo.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/DbFastestProperties.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/DbResult.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/DbTableInfo.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/DefaultCustom.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/DefaultServices.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/DeleteNavOptions.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/DiffLogModel.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/DiffType.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/DiscriminatorObject .cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/DynamicSelectModel.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/EntityColumnInfo.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/EntityInfo.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/JoinInfoParameter.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/JoinMapper.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/JoinQueryInfo.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/ManyToManyConfig.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/MapperCache.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/Mapping/IgnoreComumn.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/Mapping/MappingColumn.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/Mapping/MappingTable.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/Mapping/SugarMappingAttribute.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/ModelContext.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/NavigationInitializer.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/PageModel.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/PropertyMetadata.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/QueueItem.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/SchemaInfo.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/SingleColumnsEntity.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/SlaveConnectionConfig.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/SqlFilter.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/SqlSguarTransaction.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/SqlWith.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/StackTraceInfo.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/SubInsertTree.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/SugarAbMapping.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/SugarConnection.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/SugarDebugger.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/SugarList.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/SugarTerant.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/ApplyType.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/ConditionalType.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/DataFilterType.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/DbLockType.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/DbObjectType.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/DbType.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/InitKeyType.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/JoinType.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/LanguageType.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/NavigatType.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/OrderByType.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/ProperyType.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/ReportableDateType.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/SampleByUnit.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/SugarActionType.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/SugarDateTimeFormat.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/WhereType.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/CaseWhen/CaseWhen.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/CaseWhen/CaseWhenResolve.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/BinaryExpressionInfo.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/CommonTempDataType.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/DateType.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/ErrorMessage.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/ExpResolveAccessory.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/ExpressionConst.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/ExpressionContextCase.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/ExpressionItems.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/ExpressionOutParameter.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/ExpressionParameter.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/ExpressionResult.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/ExpressionResultAcceptType.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/ExpressionTool.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/ListAnyParameter.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/MapperExpression.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/MapperSql.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/MethodCallExpressionModel.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/NewExpressionInfo.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/ParameterExpressionVisitor.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/ParameterReplacer.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/ResolveExpressType.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/SugarParameter.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/DbMethods/DefaultDbMethod.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/DbMethods/IDbMethods.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/DbMethods/SqlFunc.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/DbMethods/SqlFuncExtendsion.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/DbMethods/SqlFuncExternal.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ExpressionContext.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/BaseResolve.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/BaseResolve_Append.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/BaseResolve_Helper.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/BaseResolve_Item.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/BaseResolve_NewExp.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/BaseResolve_Property.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/BaseResolve_Validate.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/BinaryExpressionResolve.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/BlockExpressionResolve.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/CoalesceResolveItems.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/ConditionalExpressionResolve.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/ConstantExpressionResolve.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/LambdaExpressionResolve.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/MapperExpressionResolve.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/MemberConstExpressionResolve.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/MemberExpressionResolve.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/MemberInitExpressionResolve.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/MemberNewExpressionResolve.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/MemberNoExpressionResolve.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/MethodCallExpressionResolve.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/MethodCallExpressionResolve_BaseDateFomat.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/MethodCallExpressionResolve_Helper.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/NewArrayExpessionResolve.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/NewExpressionResolve.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/OneToManyNavgateExpression.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/OneToManyNavgateExpressionN.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/OneToOneNavgateExpression.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/OneToOneNavgateExpressionN.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/TypeParameterExpressionReolve.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/UnaryExpressionResolve.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/ISubOperation.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubAnd.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubAndIF.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubAny.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubAs.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubAsWithAttr.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubAvg.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubBegin.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubCount.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubDistinctCount.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubEnableTableFilter.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubFirst.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubFromTable.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubGroupBy.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubHaving.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubInnerJoin.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubLeftBracket.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubLeftJoin.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubMax.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubMin.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubNotAny.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubOrderBy.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubRightBracket.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubSelect.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubSelectDefault.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubSelectStringJoin.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubSum.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubTake.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubToList.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubTop.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubWhere.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubWhereIF.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubWithNoLock.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/SubResolve.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/SubTemplate.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/SubTools.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Subquerable.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/SubqueryableN.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExternalServiceInterface/ICacheService.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExternalServiceInterface/IRazorService.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExternalServiceInterface/ISerializeService.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/ExternalServiceInterface/ISplitTableService.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Infrastructure/ContextMethods.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Infrastructure/DependencyManagement.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Infrastructure/InstanceFactory.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Infrastructure/Mapper.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Infrastructure/StaticConfig.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/IntegrationServices/CacheService.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/IntegrationServices/SerializeService.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/IntegrationServices/SplitTableService.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IAdo.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/ICodeFirst.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IContextMethods.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/ICustomConditionalFunc.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IDMLBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IDbBind.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IDbFirst.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IDbMaintenance.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IDeleteable.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IFastBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IFastest.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IFilter.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IIncludes.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IIncludes.txt create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/ILambdaExpressions.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IParameterInsertable.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IQueryable.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IReportable.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/ISaveable.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/ISimpleClient.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/ISqlBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/ISqlSugarClient.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IStorageable.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/ISubInsertable.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/ISugarDataConverter.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/ISugarRepository.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/ITenant.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IUpdateable.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/Insertable.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/DynamicLinq/DynamicCoreHelper.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/DynamicLinq/DynamicParameters.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/DynamicLinq/SqlSugarDynamicExpressionParser.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Entities/JsonDeleteResult.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Entities/JsonInsertResult.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Entities/JsonQueryResult.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Entities/JsonTableConfig.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Entities/JsonUpdateResult.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Enums/AsNameFormatType.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Enums/Json2SqlType.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Interface/IFuncModel.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Interface/IJsonClient.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Interface/IJsonDeleteableProvider.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Interface/IJsonInsertableProvider.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Interface/IJsonProvider.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Interface/IJsonQueryableProvider.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Interface/IJsonToModel.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Interface/IJsonUpdateableProvider.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Interface/IModelToSql.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Interface/ISugarQueryable.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonModels/FuncModel.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonModels/GroupByModel.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonModels/JoinModel.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonModels/JsonQueryParameter.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonModels/JsonSqlModels.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonModels/JsonTableNameInfo.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonModels/OrderByModel.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonModels/SelectFieldModel.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonToModel/Helper.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonToModel/JsonSqlFuncToModel.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonToModel/JsonSqlFuncToParameters.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonToModel/JsonToColumnsModels.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonToModel/JsonToGroupByModels.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonToModel/JsonToJoinModels.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonToModel/JsonToOrderByModels.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonToModel/JsonToSelectModels.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/ModelToSql/FuncModelToSql.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/ModelToSql/GroupByModelToSql.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/ModelToSql/OrderByModelToSql.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/ModelToSql/SelectModelToSql.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/ModelToSql/SqlPart.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Common/JsonCommonProvider.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Deleteable/JsonDeleteableProvider.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Insertable/AppendIdentity.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Insertable/AppendName.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Insertable/AppendRow.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Insertable/Helper.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Insertable/JsonInsertableProvider.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Insertable/PrivateProperty.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/ApendJoinLastAfter.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/AppendFrom.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/AppendGroupBy.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/AppendHaving.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/AppendJoin.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/AppendOrderBy.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/AppendPage.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/AppendSelect.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/AppendWhere.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/Entities.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/Helper.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/JsonQueryableProvider.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/Property.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/RegisterAop.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/ResultDefault.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/ResultHelper.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Updateable/AppendRow.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Updateable/AppendTable.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Updateable/AppendWhere.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Updateable/AppendWhereColumns.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Updateable/Helpercs.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Updateable/JsonUpdateableProvider.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Queryable/QueryableProvider.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Utils/Json2SqlConfig.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Utils/Json2SqlHelper.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/JsonClient.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/OnlyCore/Compatible.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/OnlyCore/DataExtensions.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/OnlyCore/IDataExtensions.cs.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/OnlyCore/PartialExpressionContexts.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/CodeFirst/DmCodeFirst.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/DbBind/DmDbBind.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/DbFirst/DmDbFirst.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/DbMaintenance/DmDbMaintenance.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/DmProvider.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/Insertable/DmInserttable.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/Queryable/DmQueryable.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/SqlBuilder/DmBlukCopy.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/SqlBuilder/DmBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/SqlBuilder/DmDeleteBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/SqlBuilder/DmExpressionContext.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/SqlBuilder/DmFastBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/SqlBuilder/DmInsertBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/SqlBuilder/DmQueryBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/SqlBuilder/DmUpdateBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Kdbndp/CodeFirst/KdbndpCodeFirst.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Kdbndp/DbBind/KdbndpBind.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Kdbndp/DbFirst/KdbndpDbFirst.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Kdbndp/DbMaintenance/KdbndpDbMaintenance.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Kdbndp/Insertable/KdbndpInserttable.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Kdbndp/KdbndpSQLProvider.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Kdbndp/Queryable/KdbndpQueryable.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Kdbndp/SqlBuilder/KdbndpBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Kdbndp/SqlBuilder/KdbndpDeleteBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Kdbndp/SqlBuilder/KdbndpExpressionContext.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Kdbndp/SqlBuilder/KdbndpFastBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Kdbndp/SqlBuilder/KdbndpInsertBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Kdbndp/SqlBuilder/KdbndpQueryBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Kdbndp/SqlBuilder/KdbndpUpdateBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/CodeFirst/MySqlCodeFirst.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/DbBind/MySqlDbBind.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/DbFirst/MySqlDbFirst.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/DbMaintenance/DorisHelper.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/DbMaintenance/MySqlDbMaintenance.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/MySqlProvider.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/PatialClass/MySqlFastBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/Queryable/MySqlQueryable.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/SqlBuilder/MySqlBlukCopy.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/SqlBuilder/MySqlBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/SqlBuilder/MySqlDeleteBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/SqlBuilder/MySqlExpressionContext.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/SqlBuilder/MySqlFastBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/SqlBuilder/MySqlInsertBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/SqlBuilder/MySqlQueryBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/SqlBuilder/MySqlUpdateBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/CodeFirst/OracleCodeFirst.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/DbBind/OracleDbBind.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/DbFirst/OracleDbFirst.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/DbMaintenance/OracleDbMaintenance.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/Deleteable/OracleDeleteable.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/Insertable/OracleInsertable.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/OracleProvider.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/Queryable/OracleQueryable.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/SqlBuilder/OracleBlukCopy.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/SqlBuilder/OracleBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/SqlBuilder/OracleDeleteBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/SqlBuilder/OracleExpressionContext.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/SqlBuilder/OracleFastBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/SqlBuilder/OracleInsertBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/SqlBuilder/OracleQueryBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/SqlBuilder/OracleUpdateBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/Updateable/OracleUpdateable.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oscar/CodeFirst/OscarCodeFirst.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oscar/DbBind/OscarDbBind.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oscar/DbFirst/OscarDbFirst.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oscar/DbMaintenance/OscarDbMaintenance.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oscar/Insertable/OscarInserttable.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oscar/OscarProvider.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oscar/Queryable/OscarQueryable.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oscar/SqlBuilder/OscarBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oscar/SqlBuilder/OscarDeleteBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oscar/SqlBuilder/OscarExpressionContext.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oscar/SqlBuilder/OscarInsertBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oscar/SqlBuilder/OscarQueryBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oscar/SqlBuilder/OscarUpdateBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/PostgreSQL/CodeFirst/PostgreSQLCodeFirst.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/PostgreSQL/DbBind/PostgreSQLDbBind.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/PostgreSQL/DbFirst/PostgreSQLDbFirst.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/PostgreSQL/DbMaintenance/PostgreSQLDbMaintenance.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/PostgreSQL/Insertable/PostgreSQLInserttable.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/PostgreSQL/PostgreSQLProvider.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/PostgreSQL/Queryable/PostgreSqlQueryable.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/PostgreSQL/SqlBuilder/PostgreSQLBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/PostgreSQL/SqlBuilder/PostgreSQLDeleteBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/PostgreSQL/SqlBuilder/PostgreSQLExpressionContext.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/PostgreSQL/SqlBuilder/PostgreSQLFastBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/PostgreSQL/SqlBuilder/PostgreSQLInsertBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/PostgreSQL/SqlBuilder/PostgreSQLQueryBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/PostgreSQL/SqlBuilder/PostgreSQLUpdateBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/QuestDB/CodeFirst/QuestDBCodeFirst.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/QuestDB/DbBind/QuestDBDbBind.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/QuestDB/DbFirst/QuestDBDbFirst.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/QuestDB/DbMaintenance/QuestDBDbMaintenance.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/QuestDB/Queryable/QuestDBQueryable.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/QuestDB/QuestDBProvider.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/QuestDB/SqlBuilder/QuestDBBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/QuestDB/SqlBuilder/QuestDBDeleteBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/QuestDB/SqlBuilder/QuestDBExpressionContext.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/QuestDB/SqlBuilder/QuestDBFastBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/QuestDB/SqlBuilder/QuestDBInsertBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/QuestDB/SqlBuilder/QuestDBQueryBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/QuestDB/SqlBuilder/QuestDBUpdateBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/SqlServer/CodeFirst/SqlServerCodeFirst.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/SqlServer/DbBind/SqlServerDbBind.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/SqlServer/DbFirst/SqlServerDbFirst.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/SqlServer/DbMaintenance/SqlServerDbMaintenance.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/SqlServer/Queryable/SqlServerQueryable.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/SqlServer/SqlBuilder/SqlServerBlukCopy.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/SqlServer/SqlBuilder/SqlServerBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/SqlServer/SqlBuilder/SqlServerDeleteBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/SqlServer/SqlBuilder/SqlServerExpressionContext.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/SqlServer/SqlBuilder/SqlServerFastBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/SqlServer/SqlBuilder/SqlServerInsertBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/SqlServer/SqlBuilder/SqlServerQueryBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/SqlServer/SqlBuilder/SqlServerUpdateBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/SqlServer/SqlServerProvider.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Sqlite/CodeFirst/SqliteCodeFirst.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Sqlite/DbBind/SqliteDbBind.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Sqlite/DbFirst/SqliteDbFirst.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Sqlite/DbMaintenance/SqliteDbMaintenance.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Sqlite/Queryable/SqliteQueryable.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Sqlite/SqlBuilder/SqliteBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Sqlite/SqlBuilder/SqliteDeleteBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Sqlite/SqlBuilder/SqliteExpressionContext.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Sqlite/SqlBuilder/SqliteFastBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Sqlite/SqlBuilder/SqliteInsertBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Sqlite/SqlBuilder/SqliteQueryBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Sqlite/SqlBuilder/SqliteUpdateBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Sqlite/SqliteProvider.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/SimpleClient.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/SpliteTable/SplitTableAttribute.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/SpliteTable/SplitTableContext.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/SpliteTable/SplitTableExtensions.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/SpliteTable/SplitTableInfo.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/SpliteTable/SplitType.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/SpliteTable/SplitTypeExtensions.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/SqlSugarClient.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/SqlSugarScope.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/SugarUnitOfWork.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/CallContext.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/CallContextAsync.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/Check.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/CommonExtensions.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/DataTableExtensions.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/DbExtensions.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/ErrorMessage.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/ExpressionBuilderHelper.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/FastCopy.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/FileHelper.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/PropertyCallAdapterProvider.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/ReflectionExtensions.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/SugarAsyncLock.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/SugarRetry.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/UtilConstants.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/UtilConvert.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/UtilExceptions.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/UtilExtensions.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/UtilMethods.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/UtilRandom.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/ValidateExtensions.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/dll/Dm.nuspec create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/dll/Kdbndp.nuspec create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/dll/NuGet.exe create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/dll/dm.bat create mode 100644 src/Admin/ThingsGateway.SqlSugar/Sugar/dll/kdbndp.bat create mode 100644 src/Admin/ThingsGateway.SqlSugar/TDengine/STable.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/TDengine/STableAttribute.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/CodeFirst/TDengineCodeFirst.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/DbBind/TDengineDbBind.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/DbFirst/TDengineDbFirst.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/DbMaintenance/TDengineDbMaintenance.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/Insertable/TDengineInserttable.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/Insertable/TagInserttable.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/Queryable/TDengineSqlQueryable.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/SqlBuilder/TDengineBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/SqlBuilder/TDengineDeleteBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/SqlBuilder/TDengineExpressionContext.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/SqlBuilder/TDengineFastBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/SqlBuilder/TDengineInsertBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/SqlBuilder/TDengineQueryBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/SqlBuilder/TDengineUpdateBuilder.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/TDengineProvider.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/TDengine/TDengineDataAdapter.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/TDengine/Tools/DateTime16.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/TDengine/Tools/DateTime19.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/TDengine/Tools/FileHelper.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/TDengine/Tools/SqlSugarExtensions.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/TDengine/Tools/UtilConstants.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/TDengine/Tools/UtilExtensions.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/TDengine/Tools/UtilMethods.cs create mode 100644 src/Admin/ThingsGateway.SqlSugar/TDengine/Tools/ValidateExtensions.cs diff --git a/src/.editorconfig b/src/.editorconfig index 38812cd3f..763ef2d0a 100644 --- a/src/.editorconfig +++ b/src/.editorconfig @@ -126,35 +126,8 @@ dotnet_style_qualification_for_property = false:silent dotnet_style_qualification_for_method = false:silent dotnet_style_qualification_for_event = false:silent - -dotnet_diagnostic.CA2208.severity = none -dotnet_diagnostic.CA2008.severity = none -dotnet_diagnostic.CA1812.severity = none -dotnet_diagnostic.CA1508.severity = none -dotnet_diagnostic.CA1512.severity = none -dotnet_diagnostic.CA1513.severity = none -dotnet_diagnostic.CA1810.severity = none -dotnet_diagnostic.CA1814.severity = none -dotnet_diagnostic.CA1815.severity = none -dotnet_diagnostic.CA1835.severity = none -dotnet_diagnostic.CA1819.severity = none -dotnet_diagnostic.CA1823.severity = none -dotnet_diagnostic.CA2002.severity = none -dotnet_diagnostic.CA5350.severity = none -dotnet_diagnostic.CA5351.severity = none -dotnet_diagnostic.CA5358.severity = none -dotnet_diagnostic.CA5384.severity = none -dotnet_diagnostic.CA5392.severity = none -dotnet_diagnostic.CA1805.severity = none -dotnet_diagnostic.CA1851.severity = none -dotnet_diagnostic.CA1510.severity = none -dotnet_diagnostic.CA5401.severity = none -dotnet_diagnostic.CA2022.severity = none -dotnet_diagnostic.CA1848.severity = none -dotnet_diagnostic.CA2000.severity = none -dotnet_diagnostic.CA5394.severity = none -dotnet_diagnostic.CA3003.severity = none -dotnet_diagnostic.CA1515.severity = none -dotnet_diagnostic.CA1849.severity = none +dotnet_diagnostic.RCS1146.severity = warning +dotnet_diagnostic.RCS1059.severity = none +dotnet_diagnostic.RCS1138.severity = suggestion dotnet_code_quality.CA1822.api_surface = private, internal \ No newline at end of file diff --git a/src/Admin/ThingsGateway.Admin.Application/Attributes/IgnoreRolePermissionAttribute.cs b/src/Admin/ThingsGateway.Admin.Application/Attributes/IgnoreRolePermissionAttribute.cs index 19045fbf1..922063946 100644 --- a/src/Admin/ThingsGateway.Admin.Application/Attributes/IgnoreRolePermissionAttribute.cs +++ b/src/Admin/ThingsGateway.Admin.Application/Attributes/IgnoreRolePermissionAttribute.cs @@ -13,14 +13,16 @@ namespace ThingsGateway.Admin.Application; /// /// 需要角色授权权限 /// -[AttributeUsage(AttributeTargets.Class)] +[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] public sealed class RolePermissionAttribute : Attribute { } + /// /// 忽略角色授权权限 /// +[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)] public sealed class IgnoreRolePermissionAttribute : Attribute { } diff --git a/src/Admin/ThingsGateway.Admin.Application/Const/CacheConst.cs b/src/Admin/ThingsGateway.Admin.Application/Const/CacheConst.cs index 16f3da5a6..5ccb5adee 100644 --- a/src/Admin/ThingsGateway.Admin.Application/Const/CacheConst.cs +++ b/src/Admin/ThingsGateway.Admin.Application/Const/CacheConst.cs @@ -11,7 +11,7 @@ namespace ThingsGateway.Admin.Application; [ThingsGateway.DependencyInjection.SuppressSniffer] -public class CacheConst +public static class CacheConst { /// /// Token表缓存Key diff --git a/src/Admin/ThingsGateway.Admin.Application/Const/HubConst.cs b/src/Admin/ThingsGateway.Admin.Application/Const/HubConst.cs index 569408420..51939736e 100644 --- a/src/Admin/ThingsGateway.Admin.Application/Const/HubConst.cs +++ b/src/Admin/ThingsGateway.Admin.Application/Const/HubConst.cs @@ -13,7 +13,7 @@ namespace ThingsGateway.Admin.Application; /// /// 通讯器常量 /// -public class HubConst +public static class HubConst { /// /// 系统HubUrl diff --git a/src/Admin/ThingsGateway.Admin.Application/Const/ResourceConst.cs b/src/Admin/ThingsGateway.Admin.Application/Const/ResourceConst.cs index 3152560fd..7fbddb814 100644 --- a/src/Admin/ThingsGateway.Admin.Application/Const/ResourceConst.cs +++ b/src/Admin/ThingsGateway.Admin.Application/Const/ResourceConst.cs @@ -14,7 +14,7 @@ namespace ThingsGateway.Admin.Application; /// 资源表常量 /// [ThingsGateway.DependencyInjection.SuppressSniffer] -public class ResourceConst +public static class ResourceConst { /// /// 系统内置编码 diff --git a/src/Admin/ThingsGateway.Admin.Application/Const/RoleConst.cs b/src/Admin/ThingsGateway.Admin.Application/Const/RoleConst.cs index 2ac3c3601..66350dd97 100644 --- a/src/Admin/ThingsGateway.Admin.Application/Const/RoleConst.cs +++ b/src/Admin/ThingsGateway.Admin.Application/Const/RoleConst.cs @@ -14,7 +14,7 @@ namespace ThingsGateway.Admin.Application; /// 角色常量 /// [ThingsGateway.DependencyInjection.SuppressSniffer] -public class RoleConst +public static class RoleConst { /// /// api角色 diff --git a/src/Admin/ThingsGateway.Admin.Application/Const/SqlSugarConst.cs b/src/Admin/ThingsGateway.Admin.Application/Const/SqlSugarConst.cs index 6ef6796f2..bca8c97a2 100644 --- a/src/Admin/ThingsGateway.Admin.Application/Const/SqlSugarConst.cs +++ b/src/Admin/ThingsGateway.Admin.Application/Const/SqlSugarConst.cs @@ -14,7 +14,7 @@ namespace ThingsGateway.Admin.Application; /// SqlSugar系统常量 /// [ThingsGateway.DependencyInjection.SuppressSniffer] -public class SqlSugarConst +public static class SqlSugarConst { /// /// DB_Admin diff --git a/src/Admin/ThingsGateway.Admin.Application/Filter/RequestAuditAttribute.cs b/src/Admin/ThingsGateway.Admin.Application/Filter/RequestAuditAttribute.cs index 3a7bfabe5..ecbc4b706 100644 --- a/src/Admin/ThingsGateway.Admin.Application/Filter/RequestAuditAttribute.cs +++ b/src/Admin/ThingsGateway.Admin.Application/Filter/RequestAuditAttribute.cs @@ -1,14 +1,4 @@ -// ------------------------------------------------------------------------ -// 版权信息 -// 版权归百小僧及百签科技(广东)有限公司所有。 -// 所有权利保留。 -// 官方网站:https://baiqian.com -// -// 许可证信息 -// 项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。 -// 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。 -// ------------------------------------------------------------------------ - + using ThingsGateway.DependencyInjection; namespace System; diff --git a/src/Admin/ThingsGateway.Admin.Application/Filter/SuppressRequestAuditAttribute.cs b/src/Admin/ThingsGateway.Admin.Application/Filter/SuppressRequestAuditAttribute.cs index 625b62c54..830c0b7df 100644 --- a/src/Admin/ThingsGateway.Admin.Application/Filter/SuppressRequestAuditAttribute.cs +++ b/src/Admin/ThingsGateway.Admin.Application/Filter/SuppressRequestAuditAttribute.cs @@ -1,14 +1,4 @@ -// ------------------------------------------------------------------------ -// 版权信息 -// 版权归百小僧及百签科技(广东)有限公司所有。 -// 所有权利保留。 -// 官方网站:https://baiqian.com -// -// 许可证信息 -// 项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。 -// 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。 -// ------------------------------------------------------------------------ - + using ThingsGateway.DependencyInjection; namespace System; diff --git a/src/Admin/ThingsGateway.Admin.Application/Gitee/AdminOAuthHandler.cs b/src/Admin/ThingsGateway.Admin.Application/Gitee/AdminOAuthHandler.cs index acb17b6dc..058073171 100644 --- a/src/Admin/ThingsGateway.Admin.Application/Gitee/AdminOAuthHandler.cs +++ b/src/Admin/ThingsGateway.Admin.Application/Gitee/AdminOAuthHandler.cs @@ -261,4 +261,17 @@ public class AdminOAuthHandler( } /// 自定义 Token 异常 -public class OAuthTokenException(string message) : Exception(message); +public class OAuthTokenException : Exception +{ + public OAuthTokenException() : base() + { + } + + public OAuthTokenException(string? message, Exception? innerException) : base(message, innerException) + { + } + + public OAuthTokenException(string? message) : base(message) + { + } +} diff --git a/src/Admin/ThingsGateway.Admin.Application/Logging/LoggingConst.cs b/src/Admin/ThingsGateway.Admin.Application/Logging/LoggingConst.cs index 470676394..39a69087b 100644 --- a/src/Admin/ThingsGateway.Admin.Application/Logging/LoggingConst.cs +++ b/src/Admin/ThingsGateway.Admin.Application/Logging/LoggingConst.cs @@ -13,7 +13,7 @@ namespace ThingsGateway.Admin.Application; /// /// 日志常量 /// -public class LoggingConst +public static class LoggingConst { /// /// 分类 diff --git a/src/Admin/ThingsGateway.Admin.Application/Provider/BlazorAuthenticationHandler.cs b/src/Admin/ThingsGateway.Admin.Application/Provider/BlazorAuthenticationHandler.cs index ccba7d4a1..5e7b3df51 100644 --- a/src/Admin/ThingsGateway.Admin.Application/Provider/BlazorAuthenticationHandler.cs +++ b/src/Admin/ThingsGateway.Admin.Application/Provider/BlazorAuthenticationHandler.cs @@ -87,7 +87,7 @@ public class BlazorAuthenticationHandler : AppAuthorizeHandler var roles = await _sysRoleService.GetRoleListByUserIdAsync(userId).ConfigureAwait(false); //这里鉴别用户使能状态 - if (user == null || !user.Status) + if (user?.Status != true) { return false; } @@ -137,7 +137,7 @@ public class BlazorAuthenticationHandler : AppAuthorizeHandler else { //这里鉴别用户使能状态 - if (user == null || !user.Status) + if (user?.Status != true) { return false; } diff --git a/src/Admin/ThingsGateway.Admin.Application/Services/Resource/SysResourceService.cs b/src/Admin/ThingsGateway.Admin.Application/Services/Resource/SysResourceService.cs index 7fa9e6d5b..69bc7f3ea 100644 --- a/src/Admin/ThingsGateway.Admin.Application/Services/Resource/SysResourceService.cs +++ b/src/Admin/ThingsGateway.Admin.Application/Services/Resource/SysResourceService.cs @@ -334,7 +334,7 @@ internal sealed class SysResourceService : BaseService, ISysResourc flatList.Add(node); // 如果当前节点有子节点,则递归处理每个子节点 - if (node.Children != null && node.Children.Count > 0) + if (node.Children?.Count > 0) { foreach (var child in node.Children) { diff --git a/src/Admin/ThingsGateway.Admin.Application/ThingsGateway.Admin.Application.csproj b/src/Admin/ThingsGateway.Admin.Application/ThingsGateway.Admin.Application.csproj index 43f084540..9a3ed00e5 100644 --- a/src/Admin/ThingsGateway.Admin.Application/ThingsGateway.Admin.Application.csproj +++ b/src/Admin/ThingsGateway.Admin.Application/ThingsGateway.Admin.Application.csproj @@ -47,7 +47,7 @@ - + diff --git a/src/Admin/ThingsGateway.Admin.Application/Util/ClearTokenUtil.cs b/src/Admin/ThingsGateway.Admin.Application/Util/ClearTokenUtil.cs index d7c6c03c4..da413987e 100644 --- a/src/Admin/ThingsGateway.Admin.Application/Util/ClearTokenUtil.cs +++ b/src/Admin/ThingsGateway.Admin.Application/Util/ClearTokenUtil.cs @@ -13,7 +13,7 @@ using Microsoft.Extensions.DependencyInjection; namespace ThingsGateway.Admin.Application; [ThingsGateway.DependencyInjection.SuppressSniffer] -public class ClearTokenUtil +public static class ClearTokenUtil { private static IRelationService RelationService; private static ISysUserService SysUserService; diff --git a/src/Admin/ThingsGateway.Admin.Application/Util/NoticeUtil.cs b/src/Admin/ThingsGateway.Admin.Application/Util/NoticeUtil.cs index d3a586684..1a6be390c 100644 --- a/src/Admin/ThingsGateway.Admin.Application/Util/NoticeUtil.cs +++ b/src/Admin/ThingsGateway.Admin.Application/Util/NoticeUtil.cs @@ -13,7 +13,7 @@ using Microsoft.Extensions.DependencyInjection; namespace ThingsGateway.Admin.Application; [ThingsGateway.DependencyInjection.SuppressSniffer] -public class NoticeUtil +public static class NoticeUtil { private static INoticeService NoticeService; diff --git a/src/Admin/ThingsGateway.Admin.Application/Util/OpenApiUtil.cs b/src/Admin/ThingsGateway.Admin.Application/Util/OpenApiUtil.cs index 7e7b6ba11..c880c59fd 100644 --- a/src/Admin/ThingsGateway.Admin.Application/Util/OpenApiUtil.cs +++ b/src/Admin/ThingsGateway.Admin.Application/Util/OpenApiUtil.cs @@ -14,7 +14,7 @@ namespace ThingsGateway.Admin.Application; /// [ThingsGateway.DependencyInjection.SuppressSniffer] -public class OpenApiUtil +public static class OpenApiUtil { /// /// 构建树节点,传入的列表已经是树结构 diff --git a/src/Admin/ThingsGateway.Admin.Application/Util/OrgUtil.cs b/src/Admin/ThingsGateway.Admin.Application/Util/OrgUtil.cs index 3461b7875..47e32c047 100644 --- a/src/Admin/ThingsGateway.Admin.Application/Util/OrgUtil.cs +++ b/src/Admin/ThingsGateway.Admin.Application/Util/OrgUtil.cs @@ -14,7 +14,7 @@ namespace ThingsGateway.Admin.Application; /// [ThingsGateway.DependencyInjection.SuppressSniffer] -public class OrgUtil +public static class OrgUtil { /// /// 构造选择项,ID/TITLE diff --git a/src/Admin/ThingsGateway.Admin.Application/Util/PositionUtil.cs b/src/Admin/ThingsGateway.Admin.Application/Util/PositionUtil.cs index 32c765d88..d136baa7e 100644 --- a/src/Admin/ThingsGateway.Admin.Application/Util/PositionUtil.cs +++ b/src/Admin/ThingsGateway.Admin.Application/Util/PositionUtil.cs @@ -14,7 +14,7 @@ namespace ThingsGateway.Admin.Application; /// [ThingsGateway.DependencyInjection.SuppressSniffer] -public class PositionUtil +public static class PositionUtil { /// diff --git a/src/Admin/ThingsGateway.Admin.Application/Util/RoleUtil.cs b/src/Admin/ThingsGateway.Admin.Application/Util/RoleUtil.cs index 35699ea48..62add004c 100644 --- a/src/Admin/ThingsGateway.Admin.Application/Util/RoleUtil.cs +++ b/src/Admin/ThingsGateway.Admin.Application/Util/RoleUtil.cs @@ -14,7 +14,7 @@ namespace ThingsGateway.Admin.Application; /// [ThingsGateway.DependencyInjection.SuppressSniffer] -public class RoleUtil +public static class RoleUtil { diff --git a/src/Admin/ThingsGateway.Admin.Application/Util/UserUtil.cs b/src/Admin/ThingsGateway.Admin.Application/Util/UserUtil.cs index 4a3998afd..35948a5da 100644 --- a/src/Admin/ThingsGateway.Admin.Application/Util/UserUtil.cs +++ b/src/Admin/ThingsGateway.Admin.Application/Util/UserUtil.cs @@ -14,7 +14,7 @@ namespace ThingsGateway.Admin.Application; /// [ThingsGateway.DependencyInjection.SuppressSniffer] -public class UserUtil +public static class UserUtil { /// diff --git a/src/Admin/ThingsGateway.Admin.Application/Util/VerificatInfoUtil.cs b/src/Admin/ThingsGateway.Admin.Application/Util/VerificatInfoUtil.cs index 894d5180e..9a78937cd 100644 --- a/src/Admin/ThingsGateway.Admin.Application/Util/VerificatInfoUtil.cs +++ b/src/Admin/ThingsGateway.Admin.Application/Util/VerificatInfoUtil.cs @@ -14,7 +14,7 @@ using ThingsGateway.Extension.Generic; namespace ThingsGateway.Admin.Application; -public class VerificatInfoUtil +public static class VerificatInfoUtil { private static IVerificatInfoService VerificatInfoService { get; set; } static VerificatInfoUtil() diff --git a/src/Admin/ThingsGateway.Admin.Razor/Const/AdminOperConst.cs b/src/Admin/ThingsGateway.Admin.Razor/Const/AdminOperConst.cs index 992e741d8..5345d29ff 100644 --- a/src/Admin/ThingsGateway.Admin.Razor/Const/AdminOperConst.cs +++ b/src/Admin/ThingsGateway.Admin.Razor/Const/AdminOperConst.cs @@ -11,7 +11,7 @@ namespace ThingsGateway.Admin.Razor; [ThingsGateway.DependencyInjection.SuppressSniffer] -public class AdminOperConst +public static class AdminOperConst { public const string Add = "新增"; public const string Delete = "删除"; diff --git a/src/Admin/ThingsGateway.Admin.Razor/Pages/User/SysUserAvatarEdit.razor.cs b/src/Admin/ThingsGateway.Admin.Razor/Pages/User/SysUserAvatarEdit.razor.cs index 8d1a059cf..62a546a40 100644 --- a/src/Admin/ThingsGateway.Admin.Razor/Pages/User/SysUserAvatarEdit.razor.cs +++ b/src/Admin/ThingsGateway.Admin.Razor/Pages/User/SysUserAvatarEdit.razor.cs @@ -48,7 +48,7 @@ public partial class SysUserAvatarEdit : IDisposable private async Task OnAvatarUpload(UploadFile file) { - if (file != null && file.File != null) + if (file?.File != null) { var format = file.File.ContentType; ReadAvatarToken ??= new CancellationTokenSource(); diff --git a/src/Admin/ThingsGateway.Admin.Razor/Pages/UserCenter/UserInfoEditComponent.razor.cs b/src/Admin/ThingsGateway.Admin.Razor/Pages/UserCenter/UserInfoEditComponent.razor.cs index 3572b9f88..1843e7445 100644 --- a/src/Admin/ThingsGateway.Admin.Razor/Pages/UserCenter/UserInfoEditComponent.razor.cs +++ b/src/Admin/ThingsGateway.Admin.Razor/Pages/UserCenter/UserInfoEditComponent.razor.cs @@ -48,7 +48,7 @@ public partial class UserInfoEditComponent private async Task OnAvatarUpload(UploadFile file) { - if (file != null && file.File != null) + if (file?.File != null) { var format = file.File.ContentType; ReadAvatarToken ??= new CancellationTokenSource(); diff --git a/src/Admin/ThingsGateway.Admin.Razor/Util/ResourceUtil.cs b/src/Admin/ThingsGateway.Admin.Razor/Util/ResourceUtil.cs index 4b93e004a..c260688bb 100644 --- a/src/Admin/ThingsGateway.Admin.Razor/Util/ResourceUtil.cs +++ b/src/Admin/ThingsGateway.Admin.Razor/Util/ResourceUtil.cs @@ -16,7 +16,7 @@ namespace ThingsGateway.Admin.Razor; /// [ThingsGateway.DependencyInjection.SuppressSniffer] -public class ResourceUtil +public static class ResourceUtil { /// diff --git a/src/Admin/ThingsGateway.AdminServer/GlobalUsings.cs b/src/Admin/ThingsGateway.AdminServer/GlobalUsings.cs index 29884ed1e..0a5641e42 100644 --- a/src/Admin/ThingsGateway.AdminServer/GlobalUsings.cs +++ b/src/Admin/ThingsGateway.AdminServer/GlobalUsings.cs @@ -1,12 +1 @@ -// ------------------------------------------------------------------------ -// 版权信息 -// 版权归百小僧及百签科技(广东)有限公司所有。 -// 所有权利保留。 -// 官方网站:https://baiqian.com -// -// 许可证信息 -// 项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。 -// 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。 -// ------------------------------------------------------------------------ - -global using ThingsGateway.Admin.Application; +global using ThingsGateway.Admin.Application; diff --git a/src/Admin/ThingsGateway.SqlSugar/Attributes/IgnoreExcelAttribute.cs b/src/Admin/ThingsGateway.DB/Attributes/IgnoreExcelAttribute.cs similarity index 100% rename from src/Admin/ThingsGateway.SqlSugar/Attributes/IgnoreExcelAttribute.cs rename to src/Admin/ThingsGateway.DB/Attributes/IgnoreExcelAttribute.cs diff --git a/src/Admin/ThingsGateway.SqlSugar/Attributes/IgnoreSeedDataAttribute.cs b/src/Admin/ThingsGateway.DB/Attributes/IgnoreSeedDataAttribute.cs similarity index 100% rename from src/Admin/ThingsGateway.SqlSugar/Attributes/IgnoreSeedDataAttribute.cs rename to src/Admin/ThingsGateway.DB/Attributes/IgnoreSeedDataAttribute.cs diff --git a/src/Admin/ThingsGateway.SqlSugar/Const/ClaimConst.cs b/src/Admin/ThingsGateway.DB/Const/ClaimConst.cs similarity index 98% rename from src/Admin/ThingsGateway.SqlSugar/Const/ClaimConst.cs rename to src/Admin/ThingsGateway.DB/Const/ClaimConst.cs index 7b6db1c3c..0bc0dd62b 100644 --- a/src/Admin/ThingsGateway.SqlSugar/Const/ClaimConst.cs +++ b/src/Admin/ThingsGateway.DB/Const/ClaimConst.cs @@ -14,7 +14,7 @@ namespace ThingsGateway.Admin.Application; /// 授权用户常量 /// [ThingsGateway.DependencyInjection.SuppressSniffer] -public class ClaimConst +public static class ClaimConst { /// /// 账号 diff --git a/src/Admin/ThingsGateway.SqlSugar/Entity/BaseEntity.cs b/src/Admin/ThingsGateway.DB/Entity/BaseEntity.cs similarity index 100% rename from src/Admin/ThingsGateway.SqlSugar/Entity/BaseEntity.cs rename to src/Admin/ThingsGateway.DB/Entity/BaseEntity.cs diff --git a/src/Admin/ThingsGateway.SqlSugar/Extensions/ExportExcelExtensions.cs b/src/Admin/ThingsGateway.DB/Extensions/ExportExcelExtensions.cs similarity index 100% rename from src/Admin/ThingsGateway.SqlSugar/Extensions/ExportExcelExtensions.cs rename to src/Admin/ThingsGateway.DB/Extensions/ExportExcelExtensions.cs diff --git a/src/Admin/ThingsGateway.SqlSugar/Extensions/FileExtensions.cs b/src/Admin/ThingsGateway.DB/Extensions/FileExtensions.cs similarity index 100% rename from src/Admin/ThingsGateway.SqlSugar/Extensions/FileExtensions.cs rename to src/Admin/ThingsGateway.DB/Extensions/FileExtensions.cs diff --git a/src/Admin/ThingsGateway.SqlSugar/Extensions/QueryPageOptionsExtensions.cs b/src/Admin/ThingsGateway.DB/Extensions/QueryPageOptionsExtensions.cs similarity index 100% rename from src/Admin/ThingsGateway.SqlSugar/Extensions/QueryPageOptionsExtensions.cs rename to src/Admin/ThingsGateway.DB/Extensions/QueryPageOptionsExtensions.cs diff --git a/src/Admin/ThingsGateway.SqlSugar/Extensions/SqlSugarExtensions.cs b/src/Admin/ThingsGateway.DB/Extensions/SqlSugarExtensions.cs similarity index 99% rename from src/Admin/ThingsGateway.SqlSugar/Extensions/SqlSugarExtensions.cs rename to src/Admin/ThingsGateway.DB/Extensions/SqlSugarExtensions.cs index a2b766cef..b12b1c0f9 100644 --- a/src/Admin/ThingsGateway.SqlSugar/Extensions/SqlSugarExtensions.cs +++ b/src/Admin/ThingsGateway.DB/Extensions/SqlSugarExtensions.cs @@ -225,7 +225,7 @@ public static class SqlSugarExtensions private static IEnumerable Sort(this IEnumerable list, BasePageInput basePageInput) { - if (basePageInput != null && basePageInput.SortField != null) + if (basePageInput?.SortField != null) { for (int i = 0; i < basePageInput.SortField.Count; i++) { diff --git a/src/Admin/ThingsGateway.DB/GlobalUsings.cs b/src/Admin/ThingsGateway.DB/GlobalUsings.cs new file mode 100644 index 000000000..ec2e58001 --- /dev/null +++ b/src/Admin/ThingsGateway.DB/GlobalUsings.cs @@ -0,0 +1,11 @@ +//------------------------------------------------------------------------------ +// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 +// 此代码版权(除特别声明外的代码)归作者本人Diego所有 +// 源代码使用协议遵循本仓库的开源协议及附加协议 +// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway +// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway +// 使用文档:https://thingsgateway.cn/ +// QQ群:605534569 +//------------------------------------------------------------------------------ + +global using ThingsGateway.NewLife.Extension; \ No newline at end of file diff --git a/src/Admin/ThingsGateway.SqlSugar/Services/AppService/ClaimsPrincipalService.cs b/src/Admin/ThingsGateway.DB/Services/AppService/ClaimsPrincipalService.cs similarity index 100% rename from src/Admin/ThingsGateway.SqlSugar/Services/AppService/ClaimsPrincipalService.cs rename to src/Admin/ThingsGateway.DB/Services/AppService/ClaimsPrincipalService.cs diff --git a/src/Admin/ThingsGateway.SqlSugar/Services/AppService/IClaimsPrincipalService.cs b/src/Admin/ThingsGateway.DB/Services/AppService/IClaimsPrincipalService.cs similarity index 100% rename from src/Admin/ThingsGateway.SqlSugar/Services/AppService/IClaimsPrincipalService.cs rename to src/Admin/ThingsGateway.DB/Services/AppService/IClaimsPrincipalService.cs diff --git a/src/Admin/ThingsGateway.SqlSugar/Services/SugarAopService/ISugarAopService.cs b/src/Admin/ThingsGateway.DB/Services/SugarAopService/ISugarAopService.cs similarity index 100% rename from src/Admin/ThingsGateway.SqlSugar/Services/SugarAopService/ISugarAopService.cs rename to src/Admin/ThingsGateway.DB/Services/SugarAopService/ISugarAopService.cs diff --git a/src/Admin/ThingsGateway.SqlSugar/Services/SugarAopService/SugarAopService.cs b/src/Admin/ThingsGateway.DB/Services/SugarAopService/SugarAopService.cs similarity index 100% rename from src/Admin/ThingsGateway.SqlSugar/Services/SugarAopService/SugarAopService.cs rename to src/Admin/ThingsGateway.DB/Services/SugarAopService/SugarAopService.cs diff --git a/src/Admin/ThingsGateway.SqlSugar/Services/SugarService/BaseService.cs b/src/Admin/ThingsGateway.DB/Services/SugarService/BaseService.cs similarity index 100% rename from src/Admin/ThingsGateway.SqlSugar/Services/SugarService/BaseService.cs rename to src/Admin/ThingsGateway.DB/Services/SugarService/BaseService.cs diff --git a/src/Admin/ThingsGateway.SqlSugar/SqlSugar/CodeFirstUtils.cs b/src/Admin/ThingsGateway.DB/SqlSugar/CodeFirstUtils.cs similarity index 100% rename from src/Admin/ThingsGateway.SqlSugar/SqlSugar/CodeFirstUtils.cs rename to src/Admin/ThingsGateway.DB/SqlSugar/CodeFirstUtils.cs diff --git a/src/Admin/ThingsGateway.SqlSugar/SqlSugar/DbContext.cs b/src/Admin/ThingsGateway.DB/SqlSugar/DbContext.cs similarity index 100% rename from src/Admin/ThingsGateway.SqlSugar/SqlSugar/DbContext.cs rename to src/Admin/ThingsGateway.DB/SqlSugar/DbContext.cs diff --git a/src/Admin/ThingsGateway.SqlSugar/SqlSugar/ISqlSugarEntitySeedData.cs b/src/Admin/ThingsGateway.DB/SqlSugar/ISqlSugarEntitySeedData.cs similarity index 100% rename from src/Admin/ThingsGateway.SqlSugar/SqlSugar/ISqlSugarEntitySeedData.cs rename to src/Admin/ThingsGateway.DB/SqlSugar/ISqlSugarEntitySeedData.cs diff --git a/src/Admin/ThingsGateway.SqlSugar/SqlSugar/SeedDataUtil.cs b/src/Admin/ThingsGateway.DB/SqlSugar/SeedDataUtil.cs similarity index 99% rename from src/Admin/ThingsGateway.SqlSugar/SqlSugar/SeedDataUtil.cs rename to src/Admin/ThingsGateway.DB/SqlSugar/SeedDataUtil.cs index 45bfc1238..2a4e7affd 100644 --- a/src/Admin/ThingsGateway.SqlSugar/SqlSugar/SeedDataUtil.cs +++ b/src/Admin/ThingsGateway.DB/SqlSugar/SeedDataUtil.cs @@ -25,7 +25,7 @@ namespace ThingsGateway.Admin.Application; /// 种子数据工具类 /// [ThingsGateway.DependencyInjection.SuppressSniffer] -public class SeedDataUtil +public static class SeedDataUtil { /// /// 获取List列表 diff --git a/src/Admin/ThingsGateway.SqlSugar/SqlSugar/SqlSugarOptions.cs b/src/Admin/ThingsGateway.DB/SqlSugar/SqlSugarOptions.cs similarity index 100% rename from src/Admin/ThingsGateway.SqlSugar/SqlSugar/SqlSugarOptions.cs rename to src/Admin/ThingsGateway.DB/SqlSugar/SqlSugarOptions.cs diff --git a/src/Admin/ThingsGateway.SqlSugar/Startup.cs b/src/Admin/ThingsGateway.DB/Startup.cs similarity index 100% rename from src/Admin/ThingsGateway.SqlSugar/Startup.cs rename to src/Admin/ThingsGateway.DB/Startup.cs diff --git a/src/Admin/ThingsGateway.SqlSugar/Static/UserManager.cs b/src/Admin/ThingsGateway.DB/Static/UserManager.cs similarity index 100% rename from src/Admin/ThingsGateway.SqlSugar/Static/UserManager.cs rename to src/Admin/ThingsGateway.DB/Static/UserManager.cs diff --git a/src/Admin/ThingsGateway.DB/ThingsGateway.DB.csproj b/src/Admin/ThingsGateway.DB/ThingsGateway.DB.csproj new file mode 100644 index 000000000..8d68e82e9 --- /dev/null +++ b/src/Admin/ThingsGateway.DB/ThingsGateway.DB.csproj @@ -0,0 +1,33 @@ + + + + + + + True + + + net8.0;net9.0; + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Admin/ThingsGateway.SqlSugar/Util/CommonUtils.cs b/src/Admin/ThingsGateway.DB/Util/CommonUtils.cs similarity index 100% rename from src/Admin/ThingsGateway.SqlSugar/Util/CommonUtils.cs rename to src/Admin/ThingsGateway.DB/Util/CommonUtils.cs diff --git a/src/Admin/ThingsGateway.Furion/App/App.cs b/src/Admin/ThingsGateway.Furion/App/App.cs index 92ed0775b..f203af482 100644 --- a/src/Admin/ThingsGateway.Furion/App/App.cs +++ b/src/Admin/ThingsGateway.Furion/App/App.cs @@ -471,7 +471,7 @@ public static class App IEnumerable pathOfExternalAssemblies = Array.Empty(); // 加载 appsettings.json 配置的外部程序集 - if (Settings.ExternalAssemblies != null && Settings.ExternalAssemblies.Length > 0) + if (Settings.ExternalAssemblies?.Length > 0) { var externalDlls = new List(); foreach (var item in Settings.ExternalAssemblies) @@ -552,7 +552,7 @@ public static class App } // 处理排除的程序集 - if (Settings.ExcludeAssemblies != null && Settings.ExcludeAssemblies.Length > 0) + if (Settings.ExcludeAssemblies?.Length > 0) { scanAssemblies = scanAssemblies.Where(ass => !Settings.ExcludeAssemblies.Contains(ass.GetName().Name, StringComparer.OrdinalIgnoreCase)); } diff --git a/src/Admin/ThingsGateway.Furion/App/Extensions/ObjectExtensions.cs b/src/Admin/ThingsGateway.Furion/App/Extensions/ObjectExtensions.cs index 386fed9c7..9aa793338 100644 --- a/src/Admin/ThingsGateway.Furion/App/Extensions/ObjectExtensions.cs +++ b/src/Admin/ThingsGateway.Furion/App/Extensions/ObjectExtensions.cs @@ -455,7 +455,7 @@ public static class ObjectExtensions foreach (var property in propertys) { var p = oldType.GetProperty(property.Name); - if (property.CanWrite && p != null && p.CanRead) + if (property.CanWrite && p?.CanRead == true) { property.SetValue(o, ChangeType(p.GetValue(obj, null), property.PropertyType), null); } @@ -647,7 +647,7 @@ public static class ObjectExtensions /// 实例,true 表示空集合,false 表示非空集合 internal static bool IsEmpty(this IEnumerable collection) { - return collection == null || !collection.Any(); + return collection?.Any() != true; } diff --git a/src/Admin/ThingsGateway.Furion/AspNetCore/ModelBinders/Binders/FromConvertBinderProvider.cs b/src/Admin/ThingsGateway.Furion/AspNetCore/ModelBinders/Binders/FromConvertBinderProvider.cs index da2dfc34b..409946fdb 100644 --- a/src/Admin/ThingsGateway.Furion/AspNetCore/ModelBinders/Binders/FromConvertBinderProvider.cs +++ b/src/Admin/ThingsGateway.Furion/AspNetCore/ModelBinders/Binders/FromConvertBinderProvider.cs @@ -54,8 +54,7 @@ public class FromConvertBinderProvider : IModelBinderProvider // 判断是否定义 [FromConvert] 特性 if (context.Metadata is DefaultModelMetadata actMetadata - && actMetadata.Attributes.ParameterAttributes != null - && actMetadata.Attributes.ParameterAttributes.Count > 0 + && actMetadata.Attributes.ParameterAttributes?.Count > 0 && actMetadata.Attributes.ParameterAttributes.Any(u => u.GetType() == typeof(FromConvertAttribute))) { return new FromConvertBinder(_modelBinderConverts); diff --git a/src/Admin/ThingsGateway.Furion/Authorization/Attributes/AppAuthorizeAttribute.cs b/src/Admin/ThingsGateway.Furion/Authorization/Attributes/AppAuthorizeAttribute.cs index 424263ef2..16ed6b241 100644 --- a/src/Admin/ThingsGateway.Furion/Authorization/Attributes/AppAuthorizeAttribute.cs +++ b/src/Admin/ThingsGateway.Furion/Authorization/Attributes/AppAuthorizeAttribute.cs @@ -25,7 +25,7 @@ public sealed class AppAuthorizeAttribute : AuthorizeAttribute /// 多个策略 public AppAuthorizeAttribute(params string[] policies) { - if (policies != null && policies.Length > 0) Policies = policies; + if (policies?.Length > 0) Policies = policies; } /// diff --git a/src/Admin/ThingsGateway.Furion/Components/Attributes/DependsOnAttribute.cs b/src/Admin/ThingsGateway.Furion/Components/Attributes/DependsOnAttribute.cs index 276ee3834..6bcaf135c 100644 --- a/src/Admin/ThingsGateway.Furion/Components/Attributes/DependsOnAttribute.cs +++ b/src/Admin/ThingsGateway.Furion/Components/Attributes/DependsOnAttribute.cs @@ -47,7 +47,7 @@ public sealed class DependsOnAttribute : Attribute var components = new List(); // 遍历所有依赖组件 - if (dependComponents != null && dependComponents.Length > 0) + if (dependComponents?.Length > 0) { foreach (var component in dependComponents) { @@ -102,7 +102,7 @@ public sealed class DependsOnAttribute : Attribute var components = new List(); // 遍历所有依赖组件 - if (value != null && value.Length > 0) + if (value?.Length > 0) { foreach (var component in value) { diff --git a/src/Admin/ThingsGateway.Furion/CorsAccessor/Internal/Penetrates.cs b/src/Admin/ThingsGateway.Furion/CorsAccessor/Internal/Penetrates.cs index 87376a99e..6f0615f68 100644 --- a/src/Admin/ThingsGateway.Furion/CorsAccessor/Internal/Penetrates.cs +++ b/src/Admin/ThingsGateway.Furion/CorsAccessor/Internal/Penetrates.cs @@ -65,7 +65,7 @@ internal static class Penetrates IEnumerable exposedHeaders = corsAccessorSettings.FixedClientToken == true ? _defaultExposedHeaders : Array.Empty(); - if (corsAccessorSettings.WithExposedHeaders != null && corsAccessorSettings.WithExposedHeaders.Length > 0) + if (corsAccessorSettings.WithExposedHeaders?.Length > 0) { exposedHeaders = exposedHeaders.Concat(corsAccessorSettings.WithExposedHeaders).Distinct(StringComparer.OrdinalIgnoreCase); } diff --git a/src/Admin/ThingsGateway.Furion/DataEncryption/Encryptions/AESEncryption.cs b/src/Admin/ThingsGateway.Furion/DataEncryption/Encryptions/AESEncryption.cs index e727df64c..32171adf0 100644 --- a/src/Admin/ThingsGateway.Furion/DataEncryption/Encryptions/AESEncryption.cs +++ b/src/Admin/ThingsGateway.Furion/DataEncryption/Encryptions/AESEncryption.cs @@ -19,7 +19,7 @@ namespace ThingsGateway.DataEncryption; /// AES 加解密 /// [SuppressSniffer] -public class AESEncryption +public static class AESEncryption { /// /// 加密 diff --git a/src/Admin/ThingsGateway.Furion/DataEncryption/Encryptions/DESEncryption.cs b/src/Admin/ThingsGateway.Furion/DataEncryption/Encryptions/DESEncryption.cs index ed34e864b..d65db4e11 100644 --- a/src/Admin/ThingsGateway.Furion/DataEncryption/Encryptions/DESEncryption.cs +++ b/src/Admin/ThingsGateway.Furion/DataEncryption/Encryptions/DESEncryption.cs @@ -20,7 +20,7 @@ namespace ThingsGateway.DataEncryption; /// DES 加解密 /// [SuppressSniffer] -public class DESEncryption +public static class DESEncryption { /// /// 加密 diff --git a/src/Admin/ThingsGateway.Furion/DataEncryption/Encryptions/KSortEncryption.cs b/src/Admin/ThingsGateway.Furion/DataEncryption/Encryptions/KSortEncryption.cs index 99d17c7eb..71f30ea38 100644 --- a/src/Admin/ThingsGateway.Furion/DataEncryption/Encryptions/KSortEncryption.cs +++ b/src/Admin/ThingsGateway.Furion/DataEncryption/Encryptions/KSortEncryption.cs @@ -18,7 +18,7 @@ namespace ThingsGateway.DataEncryption; /// KSort 加密(数据签名) /// [SuppressSniffer] -public class KSortEncryption +public static class KSortEncryption { private static DateTime _timeStampStartTime = new(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); diff --git a/src/Admin/ThingsGateway.Furion/DataEncryption/Encryptions/PBKDF2Encryption.cs b/src/Admin/ThingsGateway.Furion/DataEncryption/Encryptions/PBKDF2Encryption.cs index 38f007549..053a2af68 100644 --- a/src/Admin/ThingsGateway.Furion/DataEncryption/Encryptions/PBKDF2Encryption.cs +++ b/src/Admin/ThingsGateway.Furion/DataEncryption/Encryptions/PBKDF2Encryption.cs @@ -17,7 +17,7 @@ namespace ThingsGateway.DataEncryption; /// PBKDF2 加密 /// [SuppressSniffer] -public class PBKDF2Encryption +public static class PBKDF2Encryption { private const string SaltHashSeparator = ":"; diff --git a/src/Admin/ThingsGateway.Furion/DataEncryption/Encryptions/SHA1Encryption.cs b/src/Admin/ThingsGateway.Furion/DataEncryption/Encryptions/SHA1Encryption.cs index 9ef9a64f4..8037614f1 100644 --- a/src/Admin/ThingsGateway.Furion/DataEncryption/Encryptions/SHA1Encryption.cs +++ b/src/Admin/ThingsGateway.Furion/DataEncryption/Encryptions/SHA1Encryption.cs @@ -18,7 +18,7 @@ namespace ThingsGateway.DataEncryption; /// SHA1 加密 /// [SuppressSniffer] -public class SHA1Encryption +public static class SHA1Encryption { /// /// SHA1 加密 diff --git a/src/Admin/ThingsGateway.Furion/DataValidation/Attributes/DataValidationAttribute.cs b/src/Admin/ThingsGateway.Furion/DataValidation/Attributes/DataValidationAttribute.cs index c5908fc6f..baa907344 100644 --- a/src/Admin/ThingsGateway.Furion/DataValidation/Attributes/DataValidationAttribute.cs +++ b/src/Admin/ThingsGateway.Furion/DataValidation/Attributes/DataValidationAttribute.cs @@ -18,6 +18,7 @@ namespace System.ComponentModel.DataAnnotations; /// 数据类型验证特性 /// [SuppressSniffer] +[AttributeUsage(AttributeTargets.All, AllowMultiple = false)] public sealed class DataValidationAttribute : ValidationAttribute { /// diff --git a/src/Admin/ThingsGateway.Furion/DependencyInjection/Extensions/DependencyInjectionServiceCollectionExtensions.cs b/src/Admin/ThingsGateway.Furion/DependencyInjection/Extensions/DependencyInjectionServiceCollectionExtensions.cs index f77ba114e..3029ddf0b 100644 --- a/src/Admin/ThingsGateway.Furion/DependencyInjection/Extensions/DependencyInjectionServiceCollectionExtensions.cs +++ b/src/Admin/ThingsGateway.Furion/DependencyInjection/Extensions/DependencyInjectionServiceCollectionExtensions.cs @@ -215,7 +215,7 @@ public static class DependencyInjectionServiceCollectionExtensions private static void AddDispatchProxy(IServiceCollection services, Type dependencyType, Type type, Type proxyType, Type inter, bool hasTarget = true) { proxyType ??= GlobalServiceProxyType; - if (proxyType == null || (type != null && type.IsDefined(typeof(SuppressProxyAttribute), true))) return; + if (proxyType == null || (type?.IsDefined(typeof(SuppressProxyAttribute), true) == true)) return; var lifetime = TryGetServiceLifetime(dependencyType); diff --git a/src/Admin/ThingsGateway.Furion/DynamicApiController/Conventions/DynamicApiControllerApplicationModelConvention.cs b/src/Admin/ThingsGateway.Furion/DynamicApiController/Conventions/DynamicApiControllerApplicationModelConvention.cs index 58ca1f379..d1530f6b1 100644 --- a/src/Admin/ThingsGateway.Furion/DynamicApiController/Conventions/DynamicApiControllerApplicationModelConvention.cs +++ b/src/Admin/ThingsGateway.Furion/DynamicApiController/Conventions/DynamicApiControllerApplicationModelConvention.cs @@ -220,8 +220,7 @@ internal sealed class DynamicApiControllerApplicationModelConvention : IApplicat // 解决 Gitee 该 Issue:https://gitee.com/dotnetchina/Furion/issues/I59B74 if (CheckIsForceWithDefaultRoute(controllerApiDescriptionSettings) && !string.IsNullOrWhiteSpace(_dynamicApiControllerSettings.DefaultRoutePrefix) - && controller.Selectors[0] != null - && controller.Selectors[0].AttributeRouteModel != null + && controller.Selectors[0]?.AttributeRouteModel != null && !ForceWithDefaultPrefixRouteControllerTypes.Contains(controller.ControllerType)) { // 读取模块 diff --git a/src/Admin/ThingsGateway.Furion/DynamicApiController/Extensions/DynamicApiControllerServiceCollectionExtensions.cs b/src/Admin/ThingsGateway.Furion/DynamicApiController/Extensions/DynamicApiControllerServiceCollectionExtensions.cs index 32af1b237..8707a7197 100644 --- a/src/Admin/ThingsGateway.Furion/DynamicApiController/Extensions/DynamicApiControllerServiceCollectionExtensions.cs +++ b/src/Admin/ThingsGateway.Furion/DynamicApiController/Extensions/DynamicApiControllerServiceCollectionExtensions.cs @@ -126,7 +126,7 @@ public static class DynamicApiControllerServiceCollectionExtensions { var partManager = mvcBuilder.PartManager; // 载入程序集部件 - if (partManager != null && assemblies != null && assemblies.Any()) + if (partManager != null && assemblies?.Any() == true) { foreach (var assembly in assemblies) { diff --git a/src/Admin/ThingsGateway.Furion/DynamicApiController/Runtimes/DynamicApiRuntimeChangeProvider.cs b/src/Admin/ThingsGateway.Furion/DynamicApiController/Runtimes/DynamicApiRuntimeChangeProvider.cs index 345b93033..9e358f749 100644 --- a/src/Admin/ThingsGateway.Furion/DynamicApiController/Runtimes/DynamicApiRuntimeChangeProvider.cs +++ b/src/Admin/ThingsGateway.Furion/DynamicApiController/Runtimes/DynamicApiRuntimeChangeProvider.cs @@ -48,7 +48,7 @@ internal sealed class DynamicApiRuntimeChangeProvider : IDynamicApiRuntimeChange /// 程序集 public void AddAssemblies(params Assembly[] assemblies) { - if (assemblies != null && assemblies.Length > 0) + if (assemblies?.Length > 0) { foreach (var assembly in assemblies) { @@ -63,7 +63,7 @@ internal sealed class DynamicApiRuntimeChangeProvider : IDynamicApiRuntimeChange /// 程序集 public void AddAssembliesWithNotifyChanges(params Assembly[] assemblies) { - if (assemblies != null && assemblies.Length > 0) + if (assemblies?.Length > 0) { AddAssemblies(assemblies); NotifyChanges(); @@ -76,7 +76,7 @@ internal sealed class DynamicApiRuntimeChangeProvider : IDynamicApiRuntimeChange /// 程序集名称 public void RemoveAssemblies(params string[] assemblyNames) { - if (assemblyNames != null && assemblyNames.Length > 0) + if (assemblyNames?.Length > 0) { foreach (var assemblyName in assemblyNames) { @@ -93,7 +93,7 @@ internal sealed class DynamicApiRuntimeChangeProvider : IDynamicApiRuntimeChange /// 程序集 public void RemoveAssemblies(params Assembly[] assemblies) { - if (assemblies != null && assemblies.Length > 0) + if (assemblies?.Length > 0) { RemoveAssemblies(assemblies.Select(ass => ass.GetName().Name).ToArray()); } @@ -105,7 +105,7 @@ internal sealed class DynamicApiRuntimeChangeProvider : IDynamicApiRuntimeChange /// 程序集名称 public void RemoveAssembliesWithNotifyChanges(params string[] assemblyNames) { - if (assemblyNames != null && assemblyNames.Length > 0) + if (assemblyNames?.Length > 0) { RemoveAssemblies(assemblyNames); NotifyChanges(); @@ -118,7 +118,7 @@ internal sealed class DynamicApiRuntimeChangeProvider : IDynamicApiRuntimeChange /// 程序集 public void RemoveAssembliesWithNotifyChanges(params Assembly[] assemblies) { - if (assemblies != null && assemblies.Length > 0) + if (assemblies?.Length > 0) { RemoveAssemblies(assemblies); NotifyChanges(); diff --git a/src/Admin/ThingsGateway.Furion/FriendlyException/Exceptions/AppFriendlyException.cs b/src/Admin/ThingsGateway.Furion/FriendlyException/Exceptions/AppFriendlyException.cs index 1a125058b..bb6b9a754 100644 --- a/src/Admin/ThingsGateway.Furion/FriendlyException/Exceptions/AppFriendlyException.cs +++ b/src/Admin/ThingsGateway.Furion/FriendlyException/Exceptions/AppFriendlyException.cs @@ -49,6 +49,14 @@ public class AppFriendlyException : Exception ErrorCode = OriginErrorCode = errorCode; } + public AppFriendlyException(string? message) : base(message) + { + } + + public AppFriendlyException(string? message, Exception? innerException) : base(message, innerException) + { + } + /// /// 错误码 /// diff --git a/src/Admin/ThingsGateway.Furion/FriendlyException/Retry.cs b/src/Admin/ThingsGateway.Furion/FriendlyException/Retry.cs index 706484d27..0903dd2b5 100644 --- a/src/Admin/ThingsGateway.Furion/FriendlyException/Retry.cs +++ b/src/Admin/ThingsGateway.Furion/FriendlyException/Retry.cs @@ -102,7 +102,7 @@ public sealed class Retry } // 如果填写了 exceptionTypes 且异常类型不在 exceptionTypes 之内,则终止重试 - if (exceptionTypes != null && exceptionTypes.Length > 0 && !exceptionTypes.Any(u => u.IsAssignableFrom(ex.GetType()))) + if (exceptionTypes?.Length > 0 && !exceptionTypes.Any(u => u.IsAssignableFrom(ex.GetType()))) { if (finalThrow) { diff --git a/src/Admin/ThingsGateway.Furion/Logging/Implantations/Monitors/LoggingMonitorAttribute.cs b/src/Admin/ThingsGateway.Furion/Logging/Implantations/Monitors/LoggingMonitorAttribute.cs index 4a80674d3..d6a673e4a 100644 --- a/src/Admin/ThingsGateway.Furion/Logging/Implantations/Monitors/LoggingMonitorAttribute.cs +++ b/src/Admin/ThingsGateway.Furion/Logging/Implantations/Monitors/LoggingMonitorAttribute.cs @@ -604,7 +604,7 @@ public sealed class LoggingMonitorAttribute : Attribute, IAsyncActionFilter, IAs private string TrySerializeObject(object obj, LoggingMonitorMethod monitorMethod, out bool succeed) { // 排除 IQueryable<> 泛型 - if (obj != null && obj.GetType().HasImplementedRawGeneric(typeof(IQueryable<>))) + if (obj?.GetType().HasImplementedRawGeneric(typeof(IQueryable<>)) == true) { succeed = true; return "{}"; @@ -961,8 +961,7 @@ public sealed class LoggingMonitorAttribute : Attribute, IAsyncActionFilter, IAs // token 信息 // 判断是否是授权访问 var isAuth = actionMethod.GetFoundAttribute(true) == null - && resultHttpContext.User != null - && resultHttpContext.User.Identity.IsAuthenticated; + && resultHttpContext.User?.Identity.IsAuthenticated == true; // 获取响应头信息 var accessToken = resultHttpContext.Response.Headers["access-token"].ToString(); var authorization = string.IsNullOrWhiteSpace(accessToken) diff --git a/src/Admin/ThingsGateway.Furion/Logging/Internal/StringLoggingPartSetters.cs b/src/Admin/ThingsGateway.Furion/Logging/Internal/StringLoggingPartSetters.cs index b35a2951c..c0558d01d 100644 --- a/src/Admin/ThingsGateway.Furion/Logging/Internal/StringLoggingPartSetters.cs +++ b/src/Admin/ThingsGateway.Furion/Logging/Internal/StringLoggingPartSetters.cs @@ -47,7 +47,7 @@ public sealed partial class StringLoggingPart /// public StringLoggingPart SetArgs(params object[] args) { - if (args != null && args.Length > 0) Args = args; + if (args?.Length > 0) Args = args; return this; } diff --git a/src/Admin/ThingsGateway.Furion/ObjectMapper/Extensions/ObjectMapperServiceCollectionExtensions.cs b/src/Admin/ThingsGateway.Furion/ObjectMapper/Extensions/ObjectMapperServiceCollectionExtensions.cs index 5c5323dd8..36893adc4 100644 --- a/src/Admin/ThingsGateway.Furion/ObjectMapper/Extensions/ObjectMapperServiceCollectionExtensions.cs +++ b/src/Admin/ThingsGateway.Furion/ObjectMapper/Extensions/ObjectMapperServiceCollectionExtensions.cs @@ -47,7 +47,7 @@ public static class ObjectMapperServiceCollectionExtensions var config = TypeAdapterConfig.GlobalSettings; // 扫描所有继承 IRegister 接口的对象映射配置 - if (assemblies != null && assemblies.Length > 0) config.Scan(assemblies); + if (assemblies?.Length > 0) config.Scan(assemblies); // 配置支持依赖注入 services.AddSingleton(config); diff --git a/src/Admin/ThingsGateway.Furion/Schedule/Builders/SchedulerBuilder.cs b/src/Admin/ThingsGateway.Furion/Schedule/Builders/SchedulerBuilder.cs index 0ef606eb5..9a688409f 100644 --- a/src/Admin/ThingsGateway.Furion/Schedule/Builders/SchedulerBuilder.cs +++ b/src/Admin/ThingsGateway.Furion/Schedule/Builders/SchedulerBuilder.cs @@ -235,7 +235,7 @@ public sealed class SchedulerBuilder var schedulerBuilder = new SchedulerBuilder(jobBuilder); // 批量添加触发器 - if (triggerBuilders != null && triggerBuilders.Length > 0) + if (triggerBuilders?.Length > 0) { schedulerBuilder.TriggerBuilders.AddRange(triggerBuilders); } diff --git a/src/Admin/ThingsGateway.Furion/Schedule/Extensions/ScheduleExtensions.cs b/src/Admin/ThingsGateway.Furion/Schedule/Extensions/ScheduleExtensions.cs index 4ac714afb..92a0cfb9f 100644 --- a/src/Admin/ThingsGateway.Furion/Schedule/Extensions/ScheduleExtensions.cs +++ b/src/Admin/ThingsGateway.Furion/Schedule/Extensions/ScheduleExtensions.cs @@ -192,7 +192,7 @@ public static class ScheduleExtensions var underScoreCasePropertyName = Penetrates.GetNaming(propertyName, NamingConventions.UnderScoreCase); // 处理忽略属性问题 - if (ignorePropertyNames != null && ignorePropertyNames.Length > 0) + if (ignorePropertyNames?.Length > 0) { if (ignorePropertyNames.Contains(propertyName, StringComparer.OrdinalIgnoreCase) || ignorePropertyNames.Contains(camelCasePropertyName, StringComparer.OrdinalIgnoreCase) diff --git a/src/Admin/ThingsGateway.Furion/Schedule/HostedServices/ScheduleHostedService.cs b/src/Admin/ThingsGateway.Furion/Schedule/HostedServices/ScheduleHostedService.cs index d40b4ef89..3831f824a 100644 --- a/src/Admin/ThingsGateway.Furion/Schedule/HostedServices/ScheduleHostedService.cs +++ b/src/Admin/ThingsGateway.Furion/Schedule/HostedServices/ScheduleHostedService.cs @@ -327,7 +327,7 @@ internal sealed class ScheduleHostedService : BackgroundService }; // 是否定义 FallbackAsync 方法 - var isDefinedFallbackAsyncMethod = jobHandler != null && jobHandler.GetType().GetMethod(nameof(IJob.FallbackAsync) + var isDefinedFallbackAsyncMethod = jobHandler?.GetType().GetMethod(nameof(IJob.FallbackAsync) , BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly , null , new[] { typeof(JobExecutedContext), typeof(CancellationToken) } diff --git a/src/Admin/ThingsGateway.Furion/Schedule/Http/HttpJob.cs b/src/Admin/ThingsGateway.Furion/Schedule/Http/HttpJob.cs index 38c70c701..ea1590ae0 100644 --- a/src/Admin/ThingsGateway.Furion/Schedule/Http/HttpJob.cs +++ b/src/Admin/ThingsGateway.Furion/Schedule/Http/HttpJob.cs @@ -101,7 +101,7 @@ public class HttpJob : IJob } // 添加请求头 - if (httpJobMessage.Headers != null && httpJobMessage.Headers.Count > 0) + if (httpJobMessage.Headers?.Count > 0) { foreach (var (name, value) in httpJobMessage.Headers) { diff --git a/src/Admin/ThingsGateway.Furion/SpecificationDocument/Builders/SpecificationDocumentBuilder.cs b/src/Admin/ThingsGateway.Furion/SpecificationDocument/Builders/SpecificationDocumentBuilder.cs index df10d78cf..c3cf35755 100644 --- a/src/Admin/ThingsGateway.Furion/SpecificationDocument/Builders/SpecificationDocumentBuilder.cs +++ b/src/Admin/ThingsGateway.Furion/SpecificationDocument/Builders/SpecificationDocumentBuilder.cs @@ -711,7 +711,7 @@ public static class SpecificationDocumentBuilder .Union( actions.SelectMany(u => GetActionGroups(u)) ) - .Where(u => u != null && u.Visible) + .Where(u => u?.Visible == true) // 分组后取最大排序 .GroupBy(u => u.Group) .Select(u => new GroupExtraInfo diff --git a/src/Admin/ThingsGateway.Furion/Templates/TP.cs b/src/Admin/ThingsGateway.Furion/Templates/TP.cs index c65c9ddf9..96ce12aa1 100644 --- a/src/Admin/ThingsGateway.Furion/Templates/TP.cs +++ b/src/Admin/ThingsGateway.Furion/Templates/TP.cs @@ -47,7 +47,7 @@ public static class TP } // 添加项 - if (items != null && items.Length > 0) + if (items?.Length > 0) { var propMaxLength = items.Where(u => _lazyRegex.Value.IsMatch(u)) .DefaultIfEmpty(string.Empty) diff --git a/src/Admin/ThingsGateway.Furion/TimeCrontab/Crontab.Internal.cs b/src/Admin/ThingsGateway.Furion/TimeCrontab/Crontab.Internal.cs index 9898ffddb..0ba062fd3 100644 --- a/src/Admin/ThingsGateway.Furion/TimeCrontab/Crontab.Internal.cs +++ b/src/Admin/ThingsGateway.Furion/TimeCrontab/Crontab.Internal.cs @@ -343,7 +343,7 @@ public partial class Crontab // 如果存在且唯一,则进入下一轮判断 // 接下来的判断是处理 SUN + L 的情况,如 SUNL == 0L == SUNDAY,它们都是合法的 Cron 值 - if (replaceVal != null && replaceVal.Count == 1) + if (replaceVal?.Count == 1) { var missingParser = ""; diff --git a/src/Admin/ThingsGateway.Furion/UnifyResult/UnifyContext.cs b/src/Admin/ThingsGateway.Furion/UnifyResult/UnifyContext.cs index 975a7af66..8a7f0d2aa 100644 --- a/src/Admin/ThingsGateway.Furion/UnifyResult/UnifyContext.cs +++ b/src/Admin/ThingsGateway.Furion/UnifyResult/UnifyContext.cs @@ -140,10 +140,10 @@ public static class UnifyContext if (unifyResultSettings == null) return; // 篡改响应状态码 - if (unifyResultSettings.AdaptStatusCodes != null && unifyResultSettings.AdaptStatusCodes.Length > 0) + if (unifyResultSettings.AdaptStatusCodes?.Length > 0) { var adaptStatusCode = unifyResultSettings.AdaptStatusCodes.FirstOrDefault(u => u[0] == statusCode); - if (adaptStatusCode != null && adaptStatusCode.Length > 0 && adaptStatusCode[0] > 0) + if (adaptStatusCode?.Length > 0 && adaptStatusCode[0] > 0) { context.Response.StatusCode = adaptStatusCode[1]; return; diff --git a/src/Admin/ThingsGateway.Furion/V5_Experience/Core/Extensions/TypeExtensions.cs b/src/Admin/ThingsGateway.Furion/V5_Experience/Core/Extensions/TypeExtensions.cs index d9ff4ac72..4a2ebc1ca 100644 --- a/src/Admin/ThingsGateway.Furion/V5_Experience/Core/Extensions/TypeExtensions.cs +++ b/src/Admin/ThingsGateway.Furion/V5_Experience/Core/Extensions/TypeExtensions.cs @@ -139,8 +139,7 @@ internal static class TypeExtensions } // 类型限定名是否以 <> 开头且以 AnonymousType 结尾 - return type.FullName is not null - && type.FullName.StartsWith("<>") + return type.FullName?.StartsWith("<>") == true && type.FullName.Contains("AnonymousType"); } @@ -463,7 +462,7 @@ internal static class TypeExtensions var elementType = type.GetElementType(); // 检查元素类型是否是 KeyValuePair<,> 类型 - if (elementType is null || !elementType.IsKeyValuePair()) + if (elementType?.IsKeyValuePair() != true) { return false; } diff --git a/src/Admin/ThingsGateway.Furion/V5_Experience/Core/Extensions/V5_ObjectExtensions.cs b/src/Admin/ThingsGateway.Furion/V5_Experience/Core/Extensions/V5_ObjectExtensions.cs index a3cff3a2e..15a6117e9 100644 --- a/src/Admin/ThingsGateway.Furion/V5_Experience/Core/Extensions/V5_ObjectExtensions.cs +++ b/src/Admin/ThingsGateway.Furion/V5_Experience/Core/Extensions/V5_ObjectExtensions.cs @@ -135,7 +135,7 @@ internal static class V5_ObjectExtensions var runtimeProperty = obj.GetType().GetRuntimeProperty("Count"); // 反射获取 Count 属性值 - if (runtimeProperty is not null && runtimeProperty.CanRead && runtimeProperty.PropertyType == typeof(int)) + if (runtimeProperty?.CanRead == true && runtimeProperty.PropertyType == typeof(int)) { count = (int)runtimeProperty.GetValue(obj)!; return true; @@ -332,7 +332,7 @@ internal static class V5_ObjectExtensions var property = current.GetType().GetProperty(part, bindingFlags); // 空检查 - if (property is null || !property.CanRead) + if (property?.CanRead != true) { return null; } diff --git a/src/Admin/ThingsGateway.Furion/V5_Experience/HttpRemote/Builders/HttpRequestBuilder.Methods.cs b/src/Admin/ThingsGateway.Furion/V5_Experience/HttpRemote/Builders/HttpRequestBuilder.Methods.cs index bb13838e9..529642fea 100644 --- a/src/Admin/ThingsGateway.Furion/V5_Experience/HttpRemote/Builders/HttpRequestBuilder.Methods.cs +++ b/src/Admin/ThingsGateway.Furion/V5_Experience/HttpRemote/Builders/HttpRequestBuilder.Methods.cs @@ -1591,7 +1591,7 @@ public sealed partial class HttpRequestBuilder public HttpRequestBuilder SetBaseAddress(Uri? baseAddress) { // 检查基地址是否是绝对路径地址 - if (baseAddress is not null && !baseAddress.IsAbsoluteUri) + if (baseAddress?.IsAbsoluteUri == false) { throw new ArgumentException("The base address must be absolute.", nameof(baseAddress)); } diff --git a/src/Admin/ThingsGateway.Furion/V5_Experience/Shapeless/Clay/Clay.Events.cs b/src/Admin/ThingsGateway.Furion/V5_Experience/Shapeless/Clay/Clay.Events.cs index a3639d1eb..5a2e1bade 100644 --- a/src/Admin/ThingsGateway.Furion/V5_Experience/Shapeless/Clay/Clay.Events.cs +++ b/src/Admin/ThingsGateway.Furion/V5_Experience/Shapeless/Clay/Clay.Events.cs @@ -136,7 +136,7 @@ public partial class Clay { handler(this, new ClayEventArgs(identifier, Contains(identifier))); } - catch (Exception) + catch { // ignored } diff --git a/src/Admin/ThingsGateway.Furion/V5_Experience/Shapeless/Clay/Clay.Exports.cs b/src/Admin/ThingsGateway.Furion/V5_Experience/Shapeless/Clay/Clay.Exports.cs index 90d5dc38c..eef58ab7f 100644 --- a/src/Admin/ThingsGateway.Furion/V5_Experience/Shapeless/Clay/Clay.Exports.cs +++ b/src/Admin/ThingsGateway.Furion/V5_Experience/Shapeless/Clay/Clay.Exports.cs @@ -177,15 +177,6 @@ public partial class Clay enumerableClay = this; } - /// - /// - /// dynamic 类型的 - /// - /// - /// - /// - /// - /// public void Deconstruct(out dynamic clay, out IEnumerable enumerableClay, out Clay rawClay) { clay = this; @@ -888,7 +879,7 @@ public partial class Clay foreach (var item in values) { // 检查值是否为空值或基本类型的值 - if (item is null || item.GetType().IsBasicType()) + if (item?.GetType().IsBasicType() != false) { throw new InvalidOperationException("Cannot extend a single object with null or basic type values."); } diff --git a/src/Admin/ThingsGateway.NewLife.X/Attributes/MinValueAttribute.cs b/src/Admin/ThingsGateway.NewLife.X/Attributes/MinValueAttribute.cs index 6040f62c7..c7717f1f8 100644 --- a/src/Admin/ThingsGateway.NewLife.X/Attributes/MinValueAttribute.cs +++ b/src/Admin/ThingsGateway.NewLife.X/Attributes/MinValueAttribute.cs @@ -12,9 +12,11 @@ using System.ComponentModel.DataAnnotations; namespace ThingsGateway; + /// /// 最小值校验 /// +[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] public sealed class MinValueAttribute : ValidationAttribute { /// diff --git a/src/Admin/ThingsGateway.NewLife.X/Buffers/SpanReader.cs b/src/Admin/ThingsGateway.NewLife.X/Buffers/SpanReader.cs index 1e56db88a..17590de8d 100644 --- a/src/Admin/ThingsGateway.NewLife.X/Buffers/SpanReader.cs +++ b/src/Admin/ThingsGateway.NewLife.X/Buffers/SpanReader.cs @@ -244,7 +244,6 @@ public ref struct SpanReader while (true) { var bt = ReadByte(); - if (bt < 0) throw new Exception($"The data stream is out of range! The integer read is {rs: n0}"); b = (Byte)bt; // 必须转为Int32,否则可能溢出 diff --git a/src/Admin/ThingsGateway.NewLife.X/Caching/MemoryCache.cs b/src/Admin/ThingsGateway.NewLife.X/Caching/MemoryCache.cs index 81076244d..b8df8e09d 100644 --- a/src/Admin/ThingsGateway.NewLife.X/Caching/MemoryCache.cs +++ b/src/Admin/ThingsGateway.NewLife.X/Caching/MemoryCache.cs @@ -120,7 +120,7 @@ public class MemoryCache : Cache /// 是否包含缓存项 /// /// - public override Boolean ContainsKey(String key) => _cache.TryGetValue(key, out var item) && item != null && !item.Expired; + public override Boolean ContainsKey(String key) => _cache.TryGetValue(key, out var item) && item?.Expired == false; /// 添加缓存项,已存在时更新 /// 值类型 @@ -166,7 +166,7 @@ public class MemoryCache : Cache [return: MaybeNull] public override T Get(String key) { - if (!_cache.TryGetValue(key, out var item) || item == null || item.Expired) return default; + if (!_cache.TryGetValue(key, out var item) || item?.Expired != false) return default; return item.Visit(); } @@ -712,7 +712,7 @@ public class MemoryCache : Cache for (var i = 0; i < slist.Count && over > 0; i++) { var ss = slist.Values[i]; - if (ss != null && ss.Count > 0) + if (ss?.Count > 0) { foreach (var item in ss) { diff --git a/src/Admin/ThingsGateway.NewLife.X/Collections/ObjectPool.cs b/src/Admin/ThingsGateway.NewLife.X/Collections/ObjectPool.cs index 93d72b3ae..c71ac53ea 100644 --- a/src/Admin/ThingsGateway.NewLife.X/Collections/ObjectPool.cs +++ b/src/Admin/ThingsGateway.NewLife.X/Collections/ObjectPool.cs @@ -402,7 +402,7 @@ public class ObjectPool : DisposeBase, IPool where T : notnull /// public void WriteLog(String format, params Object?[] args) { - if (Log == null || !Log.Enable) return; + if (Log?.Enable != true) return; Log.Info(Name + "." + format, args); } diff --git a/src/Admin/ThingsGateway.NewLife.X/Common/FileUtil.cs b/src/Admin/ThingsGateway.NewLife.X/Common/FileUtil.cs index 0c499480e..b8dba675f 100644 --- a/src/Admin/ThingsGateway.NewLife.X/Common/FileUtil.cs +++ b/src/Admin/ThingsGateway.NewLife.X/Common/FileUtil.cs @@ -15,7 +15,7 @@ namespace ThingsGateway.NewLife; /// /// FileUtil /// -public class FileUtil +public static class FileUtil { /// /// 读取文件 diff --git a/src/Admin/ThingsGateway.NewLife.X/Common/MachineInfo.cs b/src/Admin/ThingsGateway.NewLife.X/Common/MachineInfo.cs index b1c47572b..fed41f0fc 100644 --- a/src/Admin/ThingsGateway.NewLife.X/Common/MachineInfo.cs +++ b/src/Admin/ThingsGateway.NewLife.X/Common/MachineInfo.cs @@ -633,7 +633,7 @@ public class MachineInfo if (!_excludes.Contains(nameof(Temperature))) { var temp = ReadWmic(@"/namespace:\\root\wmi path MSAcpi_ThermalZoneTemperature", "CurrentTemperature"); - if (temp != null && temp.Count > 0) + if (temp?.Count > 0) { if (temp.TryGetValue("CurrentTemperature", out var str) && !str.IsNullOrEmpty()) Temperature = (str.SplitAsInt().Average() - 2732) / 10.0; @@ -651,7 +651,7 @@ public class MachineInfo else if (!_excludes.Contains(nameof(Battery))) { var battery = ReadWmic("path win32_battery", "EstimatedChargeRemaining"); - if (battery != null && battery.Count > 0) + if (battery?.Count > 0) { if (battery.TryGetValue("EstimatedChargeRemaining", out var str) && !str.IsNullOrEmpty()) Battery = str.SplitAsInt().Average() / 100.0; @@ -937,7 +937,7 @@ public class MachineInfo foreach (var item in ss) { var ks = item?.Split('='); - if (ks != null && ks.Length >= 2) + if (ks?.Length >= 2) { var k = ks[0].Trim(); var v = ks[1].Trim().TrimInvisible(); @@ -1079,7 +1079,7 @@ public class MachineInfo if (root.IsNullOrEmpty()) return 0; var driveInfo = new DriveInfo(root); - if (driveInfo == null || !driveInfo.IsReady) return -1; + if (driveInfo?.IsReady != true) return -1; try { diff --git a/src/Admin/ThingsGateway.NewLife.X/Common/PinYin.cs b/src/Admin/ThingsGateway.NewLife.X/Common/PinYin.cs index 2602c5d42..0135da99d 100644 --- a/src/Admin/ThingsGateway.NewLife.X/Common/PinYin.cs +++ b/src/Admin/ThingsGateway.NewLife.X/Common/PinYin.cs @@ -6,7 +6,7 @@ namespace ThingsGateway.NewLife.Common; /// /// 文档 https://newlifex.com/core/pinyin /// -public class PinYin +public static class PinYin { #region 数组信息 private static readonly Int32[] pyValue = new[] { diff --git a/src/Admin/ThingsGateway.NewLife.X/Common/Runtime.cs b/src/Admin/ThingsGateway.NewLife.X/Common/Runtime.cs index f8f8b02c7..6b65be0f1 100644 --- a/src/Admin/ThingsGateway.NewLife.X/Common/Runtime.cs +++ b/src/Admin/ThingsGateway.NewLife.X/Common/Runtime.cs @@ -199,7 +199,7 @@ public static class Runtime if (processId != ProcessId) gc = false; var log = XTrace.Log; - if (log != null && log.Enable && log.Level <= LogLevel.Debug) + if (log?.Enable == true && log.Level <= LogLevel.Debug) { p ??= Process.GetCurrentProcess(); var gcm = GC.GetTotalMemory(false) / 1024; @@ -243,7 +243,7 @@ public static class Runtime } } - if (log != null && log.Enable && log.Level <= LogLevel.Debug) + if (log?.Enable == true && log.Level <= LogLevel.Debug) { p ??= Process.GetProcessById(processId); p.Refresh(); diff --git a/src/Admin/ThingsGateway.NewLife.X/Configuration/ConfigHelper.cs b/src/Admin/ThingsGateway.NewLife.X/Configuration/ConfigHelper.cs index 90a21aece..f5d2ccbc7 100644 --- a/src/Admin/ThingsGateway.NewLife.X/Configuration/ConfigHelper.cs +++ b/src/Admin/ThingsGateway.NewLife.X/Configuration/ConfigHelper.cs @@ -108,7 +108,7 @@ public static class ConfigHelper dic[cfg.Key] = cfg.Value; - if (cfg.Childs != null && cfg.Childs.Count > 0) + if (cfg.Childs?.Count > 0) dic[cfg.Key] = cfg.Childs; } diff --git a/src/Admin/ThingsGateway.NewLife.X/Configuration/IConfigSection.cs b/src/Admin/ThingsGateway.NewLife.X/Configuration/IConfigSection.cs index a4245d64f..582af4852 100644 --- a/src/Admin/ThingsGateway.NewLife.X/Configuration/IConfigSection.cs +++ b/src/Admin/ThingsGateway.NewLife.X/Configuration/IConfigSection.cs @@ -54,6 +54,6 @@ public class ConfigSection : IConfigSection /// 已重载。 /// - public override String ToString() => Childs != null && Childs.Count > 0 ? $"{Key}[{Childs.Count}]" : $"{Key}={Value}"; + public override String ToString() => Childs?.Count > 0 ? $"{Key}[{Childs.Count}]" : $"{Key}={Value}"; #endregion } \ No newline at end of file diff --git a/src/Admin/ThingsGateway.NewLife.X/Configuration/IniConfigProvider.cs b/src/Admin/ThingsGateway.NewLife.X/Configuration/IniConfigProvider.cs index a3c65f1db..d91cf392c 100644 --- a/src/Admin/ThingsGateway.NewLife.X/Configuration/IniConfigProvider.cs +++ b/src/Admin/ThingsGateway.NewLife.X/Configuration/IniConfigProvider.cs @@ -75,7 +75,7 @@ public class InIConfigProvider : FileConfigProvider var sb = new StringBuilder(); foreach (var item in section.Childs.ToArray()) { - if (item.Childs != null && item.Childs.Count > 0) + if (item.Childs?.Count > 0) { // 段前空一行 sb.AppendLine(); diff --git a/src/Admin/ThingsGateway.NewLife.X/Event/WeakAction.cs b/src/Admin/ThingsGateway.NewLife.X/Event/WeakAction.cs index 0a30f6b30..214dc1969 100644 --- a/src/Admin/ThingsGateway.NewLife.X/Event/WeakAction.cs +++ b/src/Admin/ThingsGateway.NewLife.X/Event/WeakAction.cs @@ -41,7 +41,7 @@ public class WeakAction var target = Target; if (target == null && Method.IsStatic) return true; - return target != null && target.IsAlive; + return target?.IsAlive == true; } } #endregion diff --git a/src/Admin/ThingsGateway.NewLife.X/Extension/DateExtensions.cs b/src/Admin/ThingsGateway.NewLife.X/Extension/DateExtensions.cs index 717361690..6bff46f59 100644 --- a/src/Admin/ThingsGateway.NewLife.X/Extension/DateExtensions.cs +++ b/src/Admin/ThingsGateway.NewLife.X/Extension/DateExtensions.cs @@ -65,7 +65,7 @@ public static class DateExtensions /// 开始时间 /// 结束时间 /// 时间差 - public static string GetDiffTime(this in DateTime beginTime, in DateTime endTime) + public static string GetDiffTime(this DateTime beginTime, DateTime endTime) { TimeSpan timeDifference = endTime - beginTime; if (timeDifference.TotalDays >= 1) @@ -88,7 +88,7 @@ public static class DateExtensions /// 开始时间 /// 结束时间 /// 时间差 - public static string GetDiffTime(this in DateTimeOffset beginTime, in DateTimeOffset endTime) + public static string GetDiffTime(this DateTimeOffset beginTime, DateTimeOffset endTime) { TimeSpan timeDifference = endTime - beginTime; if (timeDifference.TotalDays >= 1) @@ -108,7 +108,7 @@ public static class DateExtensions /// /// 返回yyyy-MM-ddTHH:mm:ss.fffffffzzz时间格式字符串 /// - public static string ToDefaultDateTimeFormat(this in DateTime dt, TimeSpan offset) + public static string ToDefaultDateTimeFormat(this DateTime dt, TimeSpan offset) { if (dt.Kind == DateTimeKind.Utc) return new DateTimeOffset(dt.ToLocalTime(), offset).ToString("yyyy-MM-ddTHH:mm:ss.fffffffzzz"); @@ -129,7 +129,7 @@ public static class DateExtensions /// /// 返回yyyy-MM-ddTHH:mm:ss.fffffffzzz时间格式字符串 /// - public static string ToDefaultDateTimeFormat(this in DateTime dt) + public static string ToDefaultDateTimeFormat(this DateTime dt) { return dt.ToString("yyyy-MM-ddTHH:mm:ss.fffffffzzz"); } @@ -137,7 +137,7 @@ public static class DateExtensions /// /// 返回yyyy-MM-dd HH-mm-ss-fff zz时间格式字符串 /// - public static string ToFileDateTimeFormat(this in DateTime dt) + public static string ToFileDateTimeFormat(this DateTime dt) { return ToDefaultDateTimeFormat(dt).Replace(":", "-"); } @@ -145,7 +145,7 @@ public static class DateExtensions /// /// 返回yyyy-MM-dd HH-mm-ss-fff zz时间格式字符串 /// - public static string ToFileDateTimeFormat(this in DateTime dt, TimeSpan offset) + public static string ToFileDateTimeFormat(this DateTime dt, TimeSpan offset) { return ToDefaultDateTimeFormat(dt, offset).Replace(":", "-"); } diff --git a/src/Admin/ThingsGateway.NewLife.X/Extension/EndPointExtensions.cs b/src/Admin/ThingsGateway.NewLife.X/Extension/EndPointExtensions.cs index 45312254d..79e547577 100644 --- a/src/Admin/ThingsGateway.NewLife.X/Extension/EndPointExtensions.cs +++ b/src/Admin/ThingsGateway.NewLife.X/Extension/EndPointExtensions.cs @@ -6,31 +6,18 @@ namespace ThingsGateway.NewLife.Extension; /// 网络结点扩展 public static class EndPointExtensions { - /// - /// - /// - /// - /// public static String ToAddress(this EndPoint endpoint) { return ((IPEndPoint)endpoint).ToAddress(); } - /// - /// - /// - /// - /// + public static String ToAddress(this IPEndPoint endpoint) { return String.Format("{0}:{1}", endpoint.Address, endpoint.Port); } private static readonly String[] SplitColon = new String[] { ":" }; - /// - /// - /// - /// - /// + public static IPEndPoint ToEndPoint(this String address) { var array = address.Split(SplitColon, StringSplitOptions.RemoveEmptyEntries); @@ -44,11 +31,7 @@ public static class EndPointExtensions } private static readonly String[] SplitComma = new String[] { "," }; - /// - /// - /// - /// - /// + public static IEnumerable ToEndPoints(this String addresses) { var array = addresses.Split(SplitComma, StringSplitOptions.RemoveEmptyEntries); diff --git a/src/Admin/ThingsGateway.NewLife.X/Extension/ProcessHelper.cs b/src/Admin/ThingsGateway.NewLife.X/Extension/ProcessHelper.cs index 763a05baa..9ff38c12a 100644 --- a/src/Admin/ThingsGateway.NewLife.X/Extension/ProcessHelper.cs +++ b/src/Admin/ThingsGateway.NewLife.X/Extension/ProcessHelper.cs @@ -24,7 +24,7 @@ public static class ProcessHelper if (pname == "dotnet" || "*/dotnet".IsMatch(pname)) { var args = GetCommandLineArgs(process.Id); - if (args != null && args.Length >= 2 && args[0].Contains("dotnet")) + if (args?.Length >= 2 && args[0].Contains("dotnet")) { return Path.GetFileNameWithoutExtension(args[1]); } @@ -32,7 +32,7 @@ public static class ProcessHelper if (pname == "java" || "*/java".IsMatch(pname)) { var args = GetCommandLineArgs(process.Id); - if (args != null && args.Length >= 3 && args[0].Contains("java") && args[1] == "-jar") + if (args?.Length >= 3 && args[0].Contains("java") && args[1] == "-jar") { return Path.GetFileNameWithoutExtension(args[2]); } @@ -210,7 +210,7 @@ public static class ProcessHelper /// public static Process? SafetyKill(this Process process, Int32 msWait = 5_000, Int32 times = 50, Int32 interval = 200) { - if (process == null || process.GetHasExited()) return process; + if (process?.GetHasExited() != false) return process; //XTrace.WriteLine("安全,温柔一刀!PID={0}/{1}", process.Id, process.ProcessName); @@ -248,7 +248,7 @@ public static class ProcessHelper /// public static Process? ForceKill(this Process process, Int32 msWait = 5_000) { - if (process == null || process.GetHasExited()) return process; + if (process?.GetHasExited() != false) return process; //XTrace.WriteLine("强杀,大力出奇迹!PID={0}/{1}", process.Id, process.ProcessName); diff --git a/src/Admin/ThingsGateway.NewLife.X/Extension/StringHelper.cs b/src/Admin/ThingsGateway.NewLife.X/Extension/StringHelper.cs index 6e44d7783..a46d41162 100644 --- a/src/Admin/ThingsGateway.NewLife.X/Extension/StringHelper.cs +++ b/src/Admin/ThingsGateway.NewLife.X/Extension/StringHelper.cs @@ -137,7 +137,7 @@ public static class StringHelper public static IDictionary SplitAsDictionary(this String? value, String nameValueSeparator = "=", String separator = ";", Boolean trimQuotation = false) { var dic = new NullableDictionary(StringComparer.OrdinalIgnoreCase); - if (value == null || value.IsNullOrWhiteSpace()) return dic; + if (value?.IsNullOrWhiteSpace() != false) return dic; if (nameValueSeparator.IsNullOrEmpty()) nameValueSeparator = "="; //if (separator == null || separator.Length <= 0) separator = new String[] { ",", ";" }; @@ -515,7 +515,7 @@ public static class StringHelper p += after.Length; // 记录位置 - if (positions != null && positions.Length > 0) positions[0] = p; + if (positions?.Length > 0) positions[0] = p; } if (String.IsNullOrEmpty(before)) return str[p..]; @@ -524,7 +524,7 @@ public static class StringHelper if (f < 0) return String.Empty; // 记录位置 - if (positions != null && positions.Length > 1) positions[1] = f; + if (positions?.Length > 1) positions[1] = f; if (p >= 0) return str[p..f]; @@ -770,7 +770,7 @@ public static class StringHelper { var rs = new List>(); - if (list == null || !list.Any()) return rs; + if (list?.Any() != true) return rs; if (keys.IsNullOrWhiteSpace()) return rs; if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); @@ -825,7 +825,7 @@ public static class StringHelper { var rs = new List>(); - if (list == null || !list.Any()) return rs; + if (list?.Any() != true) return rs; if (keys.IsNullOrWhiteSpace()) return rs; if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); diff --git a/src/Admin/ThingsGateway.NewLife.X/IO/ExcelReader.cs b/src/Admin/ThingsGateway.NewLife.X/IO/ExcelReader.cs index a770a1daf..ddeb12cb5 100644 --- a/src/Admin/ThingsGateway.NewLife.X/IO/ExcelReader.cs +++ b/src/Admin/ThingsGateway.NewLife.X/IO/ExcelReader.cs @@ -110,7 +110,7 @@ public class ExcelReader : DisposeBase // 加快样式判断速度 var styles = _styles; - if (styles != null && styles.Length == 0) styles = null; + if (styles?.Length == 0) styles = null; foreach (var row in data.Elements()) { @@ -139,7 +139,7 @@ public class ExcelReader : DisposeBase // t=DataType, s=SharedString, b=Boolean, n=Number, d=Date var t = col.Attribute("t"); - if (t != null && t.Value == "s") + if (t?.Value == "s") { val = _sharedStrings?[val.ToInt()]; } @@ -153,7 +153,7 @@ public class ExcelReader : DisposeBase if (si < styles.Length) { var st = styles[si]; - if (st != null && st.StartsWith("yy")) + if (st?.StartsWith("yy") == true) { if (val.Contains('.')) { diff --git a/src/Admin/ThingsGateway.NewLife.X/IO/FileSource.cs b/src/Admin/ThingsGateway.NewLife.X/IO/FileSource.cs index bc8b92bd8..1e2c49800 100644 --- a/src/Admin/ThingsGateway.NewLife.X/IO/FileSource.cs +++ b/src/Admin/ThingsGateway.NewLife.X/IO/FileSource.cs @@ -122,7 +122,7 @@ namespace ThingsGateway.NewLife.IO var name = String.Empty; if (asm == null) asm = Assembly.GetCallingAssembly(); var ss = asm.GetManifestResourceNames(); - if (ss != null && ss.Length > 0) + if (ss?.Length > 0) { //找到资源名 name = ss.FirstOrDefault(e => e == filename); diff --git a/src/Admin/ThingsGateway.NewLife.X/IO/IOHelper.cs b/src/Admin/ThingsGateway.NewLife.X/IO/IOHelper.cs index 4691932af..389ab6363 100644 --- a/src/Admin/ThingsGateway.NewLife.X/IO/IOHelper.cs +++ b/src/Admin/ThingsGateway.NewLife.X/IO/IOHelper.cs @@ -155,7 +155,7 @@ public static class IOHelper /// public static Stream Write(this Stream des, params Byte[] src) { - if (src != null && src.Length > 0) des.Write(src, 0, src.Length); + if (src?.Length > 0) des.Write(src, 0, src.Length); return des; } @@ -343,7 +343,7 @@ public static class IOHelper // 可能数据流前面有编码字节序列,需要先去掉 var idx = 0; var preamble = encoding.GetPreamble(); - if (preamble != null && preamble.Length > 0) + if (preamble?.Length > 0) { if (buf.Take(preamble.Length).SequenceEqual(preamble)) idx = preamble.Length; } @@ -368,7 +368,7 @@ public static class IOHelper // 可能数据流前面有编码字节序列,需要先去掉 var idx = 0; var preamble = encoding.GetPreamble(); - if (preamble != null && preamble.Length > 0 && buf.Length >= offset + preamble.Length) + if (preamble?.Length > 0 && buf.Length >= offset + preamble.Length) { if (buf.Skip(offset).Take(preamble.Length).SequenceEqual(preamble)) idx = preamble.Length; } diff --git a/src/Admin/ThingsGateway.NewLife.X/IO/PathHelper.cs b/src/Admin/ThingsGateway.NewLife.X/IO/PathHelper.cs index b4fd4bf7a..026e0b559 100644 --- a/src/Admin/ThingsGateway.NewLife.X/IO/PathHelper.cs +++ b/src/Admin/ThingsGateway.NewLife.X/IO/PathHelper.cs @@ -278,7 +278,7 @@ public static class PathHelper public static Boolean CopyToIfNewer(this FileInfo fi, String destFileName) { // 源文件必须存在 - if (fi == null || !fi.Exists) return false; + if (fi?.Exists != true) return false; var dest = destFileName.AsFile(); // 目标文件必须存在且源文件较新 @@ -425,7 +425,7 @@ public static class PathHelper /// public static IEnumerable GetAllFiles(this DirectoryInfo di, String? exts = null, Boolean allSub = false) { - if (di == null || !di.Exists) yield break; + if (di?.Exists != true) yield break; if (String.IsNullOrEmpty(exts)) exts = "*"; var opt = allSub ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly; @@ -525,7 +525,7 @@ public static class PathHelper Console.WriteLine("\t{1}\t{0}", name, item.CombinePath(name).AsFile().LastWriteTime.ToFullString()); Console.ResetColor(); }); - if (rs != null && rs.Length > 0) list.AddRange(rs); + if (rs?.Length > 0) list.AddRange(rs); } catch (Exception ex) { Console.WriteLine(" " + ex.Message); } } diff --git a/src/Admin/ThingsGateway.NewLife.X/Logger/Logger.cs b/src/Admin/ThingsGateway.NewLife.X/Logger/Logger.cs index 069f326cc..362bbeafc 100644 --- a/src/Admin/ThingsGateway.NewLife.X/Logger/Logger.cs +++ b/src/Admin/ThingsGateway.NewLife.X/Logger/Logger.cs @@ -62,7 +62,7 @@ public abstract class Logger : ILog protected virtual String Format(String format, Object?[]? args) { //处理时间的格式化 - if (args != null && args.Length > 0) + if (args?.Length > 0) { // 特殊处理异常 if (args.Length == 1 && args[0] is Exception ex && (format.IsNullOrEmpty() || format == "{0}")) diff --git a/src/Admin/ThingsGateway.NewLife.X/Logger/TextFileLog.cs b/src/Admin/ThingsGateway.NewLife.X/Logger/TextFileLog.cs index 0acf2500d..4ec1e22bd 100644 --- a/src/Admin/ThingsGateway.NewLife.X/Logger/TextFileLog.cs +++ b/src/Admin/ThingsGateway.NewLife.X/Logger/TextFileLog.cs @@ -229,7 +229,7 @@ public class TextFileLog : Logger, IDisposable try { var dels = di.GetFiles("*.del"); - if (dels != null && dels.Length > 0) + if (dels?.Length > 0) { foreach (var item in dels) { @@ -304,7 +304,7 @@ public class TextFileLog : Logger, IDisposable var e = WriteLogEventArgs.Current.Set(level); // 特殊处理异常对象 - if (args != null && args.Length == 1 && args[0] is Exception ex && (format.IsNullOrEmpty() || format == "{0}")) + if (args?.Length == 1 && args[0] is Exception ex && (format.IsNullOrEmpty() || format == "{0}")) e = e.Set(null, ex); else e = e.Set(Format(format, args), null); diff --git a/src/Admin/ThingsGateway.NewLife.X/Net/IDnsResolver.cs b/src/Admin/ThingsGateway.NewLife.X/Net/IDnsResolver.cs index 7bcc594f8..ac807d32d 100644 --- a/src/Admin/ThingsGateway.NewLife.X/Net/IDnsResolver.cs +++ b/src/Admin/ThingsGateway.NewLife.X/Net/IDnsResolver.cs @@ -54,7 +54,7 @@ public class DnsResolver : IDnsResolver if (!task.Wait(5000)) throw new TaskCanceledException(); var addrs = task.ConfigureAwait(false).GetAwaiter().GetResult(); #endif - if (addrs != null && addrs.Length > 0) + if (addrs?.Length > 0) { // 更新缓存数据 @@ -76,9 +76,9 @@ public class DnsResolver : IDnsResolver } } } - catch (Exception ex) + catch (Exception) { - if (throwError) throw ex; + if (throwError) throw; } return item; diff --git a/src/Admin/ThingsGateway.NewLife.X/Net/NetHelper.cs b/src/Admin/ThingsGateway.NewLife.X/Net/NetHelper.cs index 544ad81f6..283e15b90 100644 --- a/src/Admin/ThingsGateway.NewLife.X/Net/NetHelper.cs +++ b/src/Admin/ThingsGateway.NewLife.X/Net/NetHelper.cs @@ -180,7 +180,7 @@ public static class NetHelper { // 如果不是任意地址,直接返回 var addr = address; - if (addr == null || !addr.IsAny()) return addr; + if (addr?.IsAny() != true) return addr; // 如果是本地环回地址,返回环回地址 if (IPAddress.IsLoopback(remote)) return addr.IsIPv4() ? IPAddress.Loopback : IPAddress.IPv6Loopback; @@ -300,7 +300,7 @@ public static class NetHelper } } #else - if (item != null && item.DhcpServerAddresses.Count > 0) + if (item?.DhcpServerAddresses.Count > 0) { foreach (var elm in item.DhcpServerAddresses) { @@ -321,7 +321,7 @@ public static class NetHelper var list = new List(); foreach (var item in GetActiveInterfaces()) { - if (item != null && item.DnsAddresses.Count > 0) + if (item?.DnsAddresses.Count > 0) { foreach (var elm in item.DnsAddresses) { @@ -341,7 +341,7 @@ public static class NetHelper var list = new List(); foreach (var item in GetActiveInterfaces()) { - if (item != null && item.GatewayAddresses.Count > 0) + if (item?.GatewayAddresses.Count > 0) { foreach (var elm in item.GatewayAddresses) { @@ -365,7 +365,7 @@ public static class NetHelper if (item.NetworkInterfaceType is NetworkInterfaceType.Loopback or NetworkInterfaceType.Tunnel or NetworkInterfaceType.Unknown) continue; var ipp = item.GetIPProperties(); - if (ipp != null && ipp.UnicastAddresses.Count > 0) + if (ipp?.UnicastAddresses.Count > 0) { var gw = 0; @@ -446,7 +446,7 @@ public static class NetHelper var list = new List(); foreach (var item in GetActiveInterfaces()) { - if (item != null && item.MulticastAddresses.Count > 0) + if (item?.MulticastAddresses.Count > 0) { foreach (var elm in item.MulticastAddresses) { @@ -480,7 +480,7 @@ public static class NetHelper if (addrs.Length > 0 && addrs.All(e => IPAddress.IsLoopback(e))) continue; var mac = item.GetPhysicalAddress()?.GetAddressBytes(); - if (mac != null && mac.Length == 6) yield return mac; + if (mac?.Length == 6) yield return mac; } } @@ -508,7 +508,7 @@ public static class NetHelper if (addrs.Length == 0) continue; var mac = item.GetPhysicalAddress()?.GetAddressBytes(); - if (mac != null && mac.Length == 6) return mac; + if (mac?.Length == 6) return mac; } return null; @@ -604,7 +604,7 @@ public static class NetHelper buf = item.GetPhysicalAddress()?.GetAddressBytes(); } - if (buf != null && buf.Length == 6) return buf; + if (buf?.Length == 6) return buf; } } diff --git a/src/Admin/ThingsGateway.NewLife.X/Net/NetUri.cs b/src/Admin/ThingsGateway.NewLife.X/Net/NetUri.cs index 615784509..0d58f78cf 100644 --- a/src/Admin/ThingsGateway.NewLife.X/Net/NetUri.cs +++ b/src/Admin/ThingsGateway.NewLife.X/Net/NetUri.cs @@ -67,7 +67,7 @@ public class NetUri { var ep = _EndPoint; ep ??= _EndPoint = new IPEndPoint(IPAddress.Any, 0); - if ((ep.Address == null || ep.Address.IsAny()) && !Host.IsNullOrEmpty()) ep.Address = ParseAddress(Host)?.FirstOrDefault() ?? IPAddress.Any; + if ((ep.Address?.IsAny() != false) && !Host.IsNullOrEmpty()) ep.Address = ParseAddress(Host)?.FirstOrDefault() ?? IPAddress.Any; return ep; } diff --git a/src/Admin/ThingsGateway.NewLife.X/Net/TcpConnectionInformation2.cs b/src/Admin/ThingsGateway.NewLife.X/Net/TcpConnectionInformation2.cs index daa6ab60f..e42b2a06e 100644 --- a/src/Admin/ThingsGateway.NewLife.X/Net/TcpConnectionInformation2.cs +++ b/src/Admin/ThingsGateway.NewLife.X/Net/TcpConnectionInformation2.cs @@ -179,10 +179,10 @@ public class TcpConnectionInformation2 : TcpConnectionInformation // 各个进程底下的/net/tcp,实际上是所有进程的连接 var rs = ParseTcpsFromFile(processId > 0 ? $"/proc/{processId}/net/tcp" : "/proc/net/tcp"); - if (rs != null && rs.Count > 0) list.AddRange(rs); + if (rs?.Count > 0) list.AddRange(rs); var rs2 = ParseTcpsFromFile(processId > 0 ? $"/proc/{processId}/net/tcp6" : "/proc/net/tcp6"); - if (rs2 != null && rs2.Count > 0) list.AddRange(rs2); + if (rs2?.Count > 0) list.AddRange(rs2); //XTrace.WriteLine("tcps: {0} nodes: {1}", list.Count, nodes?.Length); // 过滤指定进程的连接 diff --git a/src/Admin/ThingsGateway.NewLife.X/Reflection/AssemblyX.cs b/src/Admin/ThingsGateway.NewLife.X/Reflection/AssemblyX.cs index c9628db56..7b90efc82 100644 --- a/src/Admin/ThingsGateway.NewLife.X/Reflection/AssemblyX.cs +++ b/src/Admin/ThingsGateway.NewLife.X/Reflection/AssemblyX.cs @@ -84,7 +84,7 @@ public class AssemblyX { try { - return Asm == null || Asm.IsDynamic ? null : Asm.Location; + return Asm?.IsDynamic != false ? null : Asm.Location; } catch { return null; } } @@ -187,7 +187,7 @@ public class AssemblyX if (item == null) continue; var ts2 = item.GetNestedTypes(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance); - if (ts2 != null && ts2.Length > 0) + if (ts2?.Length > 0) { // 从下一个元素开始插入,让内嵌类紧挨着主类 //Int32 k = i + 1; @@ -332,7 +332,7 @@ public class AssemblyX { foreach (var item in types) { - if (item != null && !item.IsInterface && !item.IsAbstract && !item.IsGenericType && item != baseType && item.As(baseType)) list.Add(item); + if (item?.IsInterface == false && !item.IsAbstract && !item.IsGenericType && item != baseType && item.As(baseType)) list.Add(item); } } @@ -360,7 +360,7 @@ public class AssemblyX foreach (var item in GetAssemblies()) { signs = item.Asm.GetName().GetPublicKey(); - if (hasNotSign && signs != null && signs.Length > 0) continue; + if (hasNotSign && signs?.Length > 0) continue; //// 如果excludeGlobalTypes为true,则指检查来自非GAC引用的程序集 //if (excludeGlobalTypes && item.Asm.GlobalAssemblyCache) continue; @@ -389,7 +389,7 @@ public class AssemblyX if (item.IsSystemAssembly || !IsReferencedFrom(item.Asm, baseAssemblyName)) continue; var ts = item.FindPlugins(baseType); - if (ts != null && ts.Count > 0) + if (ts?.Count > 0) { // 真实加载 if (XTrace.Debug) diff --git a/src/Admin/ThingsGateway.NewLife.X/Reflection/AttributeX.cs b/src/Admin/ThingsGateway.NewLife.X/Reflection/AttributeX.cs index 62af3412b..c19cd43e0 100644 --- a/src/Admin/ThingsGateway.NewLife.X/Reflection/AttributeX.cs +++ b/src/Admin/ThingsGateway.NewLife.X/Reflection/AttributeX.cs @@ -36,7 +36,7 @@ public static class AttributeX public static String? GetDisplayName(this MemberInfo member, Boolean inherit = true) { var att = member.GetCustomAttribute(inherit); - if (att != null && !att.DisplayName.IsNullOrWhiteSpace()) return att.DisplayName; + if (att?.DisplayName.IsNullOrWhiteSpace() == false) return att.DisplayName; return null; } @@ -48,7 +48,7 @@ public static class AttributeX public static String? GetDescription(this MemberInfo member, Boolean inherit = true) { var att2 = member.GetCustomAttribute(inherit); - if (att2 != null && !att2.Description.IsNullOrWhiteSpace()) return att2.Description; + if (att2?.Description.IsNullOrWhiteSpace() == false) return att2.Description; return null; } @@ -72,7 +72,7 @@ public static class AttributeX if (typeof(TAttribute) != item.Constructor.DeclaringType) continue; var args = item.ConstructorArguments; - if (args != null && args.Count > 0) return (TResult?)args[0].Value; + if (args?.Count > 0) return (TResult?)args[0].Value; } } catch { } @@ -93,14 +93,14 @@ public static class AttributeX try { var list = CustomAttributeData.GetCustomAttributes(target); - if (list != null && list.Count > 0) + if (list?.Count > 0) { foreach (var item in list) { if (typeof(TAttribute).FullName != item.Constructor.DeclaringType?.FullName) continue; var args = item.ConstructorArguments; - if (args != null && args.Count > 0) return (TResult?)args[0].Value; + if (args?.Count > 0) return (TResult?)args[0].Value; } } if (inherit && target is Type type && type.BaseType != null) diff --git a/src/Admin/ThingsGateway.NewLife.X/Reflection/IReflect.cs b/src/Admin/ThingsGateway.NewLife.X/Reflection/IReflect.cs index 592c299de..78c364f37 100644 --- a/src/Admin/ThingsGateway.NewLife.X/Reflection/IReflect.cs +++ b/src/Admin/ThingsGateway.NewLife.X/Reflection/IReflect.cs @@ -295,7 +295,7 @@ public class DefaultReflect : IReflect while (type2 != null && type2 != typeof(Object)) { var fs = type2.GetMember(name, ignoreCase ? bfic : bf); - if (fs != null && fs.Length > 0) + if (fs?.Length > 0) { // 得到多个的时候,优先返回精确匹配 if (ignoreCase && fs.Length > 1) @@ -566,7 +566,7 @@ public class DefaultReflect : IReflect foreach (var pi in targetType.GetProperties(true)) { if (!pi.CanWrite) continue; - if (excludes != null && excludes.Contains(pi.Name)) continue; + if (excludes?.Contains(pi.Name) == true) continue; if (sourceProperties.TryGetValue(pi.Name, out var pi2) && pi2.CanRead) SetValue(target, pi, GetValue(source, pi2)); @@ -579,7 +579,7 @@ public class DefaultReflect : IReflect foreach (var pi in sourceProperties.Values) { if (!pi.CanRead) continue; - if (excludes != null && excludes.Contains(pi.Name)) continue; + if (excludes?.Contains(pi.Name) == true) continue; dic[pi.Name] = GetValue(source, pi); } diff --git a/src/Admin/ThingsGateway.NewLife.X/Reflection/Reflect.cs b/src/Admin/ThingsGateway.NewLife.X/Reflection/Reflect.cs index 8fbdd2bfc..1146cac0a 100644 --- a/src/Admin/ThingsGateway.NewLife.X/Reflection/Reflect.cs +++ b/src/Admin/ThingsGateway.NewLife.X/Reflection/Reflect.cs @@ -489,12 +489,12 @@ public static class Reflect /// 是否泛型列表 /// /// - public static Boolean IsList(this Type type) => type != null && type.IsGenericType && type.As(typeof(IList<>)); + public static Boolean IsList(this Type type) => type?.IsGenericType == true && type.As(typeof(IList<>)); /// 是否泛型字典 /// /// - public static Boolean IsDictionary(this Type type) => type != null && type.IsGenericType && type.As(typeof(IDictionary<,>)); + public static Boolean IsDictionary(this Type type) => type?.IsGenericType == true && type.As(typeof(IDictionary<,>)); #endregion #region 插件 diff --git a/src/Admin/ThingsGateway.NewLife.X/Reflection/ScriptEngine.cs b/src/Admin/ThingsGateway.NewLife.X/Reflection/ScriptEngine.cs index dacf9e5bc..43327f604 100644 --- a/src/Admin/ThingsGateway.NewLife.X/Reflection/ScriptEngine.cs +++ b/src/Admin/ThingsGateway.NewLife.X/Reflection/ScriptEngine.cs @@ -305,7 +305,7 @@ public class ScriptEngine if (FinalCode == null) GenerateCode(); var rs = Compile(FinalCode, null); - if (rs.Errors == null || !rs.Errors.HasErrors) + if (rs.Errors?.HasErrors != true) { // 加载外部程序集 foreach (var item in ReferencedAssemblies) diff --git a/src/Admin/ThingsGateway.NewLife.X/Security/Asn1.cs b/src/Admin/ThingsGateway.NewLife.X/Security/Asn1.cs index b3a7f1b96..43f2000cd 100644 --- a/src/Admin/ThingsGateway.NewLife.X/Security/Asn1.cs +++ b/src/Admin/ThingsGateway.NewLife.X/Security/Asn1.cs @@ -55,7 +55,7 @@ public class Asn1 foreach (var item in arr) { var ds = item.GetOids(); - if (ds != null && ds.Length > 0) list.AddRange(ds); + if (ds?.Length > 0) list.AddRange(ds); } } diff --git a/src/Admin/ThingsGateway.NewLife.X/Security/Certificate.cs b/src/Admin/ThingsGateway.NewLife.X/Security/Certificate.cs index 48466b85d..972cb5ccd 100644 --- a/src/Admin/ThingsGateway.NewLife.X/Security/Certificate.cs +++ b/src/Admin/ThingsGateway.NewLife.X/Security/Certificate.cs @@ -7,7 +7,7 @@ namespace ThingsGateway.NewLife.Security { /// 证书 /// http://blogs.msdn.com/b/dcook/archive/2008/11/25/creating-a-self-signed-certificate-in-c.aspx - public class Certificate + public static class Certificate { /// 建立自签名证书 /// @@ -52,7 +52,7 @@ namespace ThingsGateway.NewLife.Security } finally { - if (password != null) password.Dispose(); + password?.Dispose(); } } diff --git a/src/Admin/ThingsGateway.NewLife.X/Security/DSAHelper.cs b/src/Admin/ThingsGateway.NewLife.X/Security/DSAHelper.cs index c15ab8dd8..6b13cc23c 100644 --- a/src/Admin/ThingsGateway.NewLife.X/Security/DSAHelper.cs +++ b/src/Admin/ThingsGateway.NewLife.X/Security/DSAHelper.cs @@ -62,7 +62,7 @@ public static class DSAHelper var xmlDoc = new XmlDocument(); xmlDoc.LoadXml(xmlString); - if (xmlDoc.DocumentElement == null || !xmlDoc.DocumentElement.Name.Equals("DSAKeyValue")) + if (xmlDoc.DocumentElement?.Name.Equals("DSAKeyValue") != true) { throw new Exception("Invalid XML DSA key."); } diff --git a/src/Admin/ThingsGateway.NewLife.X/Security/RSAHelper.cs b/src/Admin/ThingsGateway.NewLife.X/Security/RSAHelper.cs index 6db742a00..6ab81fd53 100644 --- a/src/Admin/ThingsGateway.NewLife.X/Security/RSAHelper.cs +++ b/src/Admin/ThingsGateway.NewLife.X/Security/RSAHelper.cs @@ -56,7 +56,7 @@ public static class RSAHelper ms.WriteArray(p.Modulus); ms.WriteArray(p.Exponent); - if (p.D != null && p.D.Length > 0) + if (p.D?.Length > 0) { if (p.D == null || p.P == null || p.Q == null || p.DP == null || p.DQ == null || p.InverseQ == null) throw new ArgumentNullException(nameof(p)); diff --git a/src/Admin/ThingsGateway.NewLife.X/Security/SecurityHelper.cs b/src/Admin/ThingsGateway.NewLife.X/Security/SecurityHelper.cs index 986e9d473..cdde89d5f 100644 --- a/src/Admin/ThingsGateway.NewLife.X/Security/SecurityHelper.cs +++ b/src/Admin/ThingsGateway.NewLife.X/Security/SecurityHelper.cs @@ -141,9 +141,9 @@ public static class SecurityHelper { if (data == null || data.Length <= 0) throw new ArgumentNullException(nameof(data)); - if (pass != null && pass.Length > 0) + if (pass?.Length > 0) { - if (sa.LegalKeySizes != null && sa.LegalKeySizes.Length > 0) + if (sa.LegalKeySizes?.Length > 0) sa.Key = Pad(pass, sa.LegalKeySizes[0]); else sa.Key = pass; @@ -206,9 +206,9 @@ public static class SecurityHelper { if (data == null || data.Length <= 0) throw new ArgumentNullException(nameof(data)); - if (pass != null && pass.Length > 0) + if (pass?.Length > 0) { - if (sa.LegalKeySizes != null && sa.LegalKeySizes.Length > 0) + if (sa.LegalKeySizes?.Length > 0) sa.Key = Pad(pass, sa.LegalKeySizes[0]); else sa.Key = pass; diff --git a/src/Admin/ThingsGateway.NewLife.X/Serialization/Binary/Binary.cs b/src/Admin/ThingsGateway.NewLife.X/Serialization/Binary/Binary.cs index e2fac634b..f0d3c5b85 100644 --- a/src/Admin/ThingsGateway.NewLife.X/Serialization/Binary/Binary.cs +++ b/src/Admin/ThingsGateway.NewLife.X/Serialization/Binary/Binary.cs @@ -118,7 +118,7 @@ public class Binary : FormatterBase, IBinary type = value.GetType(); // 一般类型为空是顶级调用 - if (Hosts.Count == 0 && Log != null && Log.Enable) WriteLog("BinaryWrite {0} {1}", type.Name, value); + if (Hosts.Count == 0 && Log?.Enable == true) WriteLog("BinaryWrite {0} {1}", type.Name, value); } // 优先 IAccessor 接口 @@ -236,7 +236,7 @@ public class Binary : FormatterBase, IBinary /// public virtual Boolean TryRead(Type type, ref Object? value) { - if (Hosts.Count == 0 && Log != null && Log.Enable) WriteLog("BinaryRead {0} {1}", type.Name, value); + if (Hosts.Count == 0 && Log?.Enable == true) WriteLog("BinaryRead {0} {1}", type.Name, value); // 优先 IAccessor 接口 if (value is IAccessor acc) diff --git a/src/Admin/ThingsGateway.NewLife.X/Serialization/DataMemberResolver.cs b/src/Admin/ThingsGateway.NewLife.X/Serialization/DataMemberResolver.cs index b39fda228..2ba3adecf 100644 --- a/src/Admin/ThingsGateway.NewLife.X/Serialization/DataMemberResolver.cs +++ b/src/Admin/ThingsGateway.NewLife.X/Serialization/DataMemberResolver.cs @@ -50,7 +50,7 @@ public class DataMemberResolver : DefaultJsonTypeInfoResolver else { var attr = provider.GetCustomAttributes(typeof(DataMemberAttribute), false)?.FirstOrDefault() as DataMemberAttribute; - if (attr != null && !attr.Name.IsNullOrEmpty()) + if (attr?.Name.IsNullOrEmpty() == false) propertyInfo.Name = attr.Name; } } diff --git a/src/Admin/ThingsGateway.NewLife.X/Serialization/SerialHelper.cs b/src/Admin/ThingsGateway.NewLife.X/Serialization/SerialHelper.cs index e07cfee60..ed658f04e 100644 --- a/src/Admin/ThingsGateway.NewLife.X/Serialization/SerialHelper.cs +++ b/src/Admin/ThingsGateway.NewLife.X/Serialization/SerialHelper.cs @@ -22,12 +22,12 @@ public static class SerialHelper if (name.IsNullOrEmpty()) { var att = pi.GetCustomAttribute(); - if (att != null && !att.Name.IsNullOrEmpty()) name = att.Name; + if (att?.Name.IsNullOrEmpty() == false) name = att.Name; } if (name.IsNullOrEmpty()) { var att = pi.GetCustomAttribute(); - if (att != null && !att.ElementName.IsNullOrEmpty()) name = att.ElementName; + if (att?.ElementName.IsNullOrEmpty() == false) name = att.ElementName; } if (name.IsNullOrEmpty()) name = pi.Name; diff --git a/src/Admin/ThingsGateway.NewLife.X/Serialization/Xml/Xml.cs b/src/Admin/ThingsGateway.NewLife.X/Serialization/Xml/Xml.cs index 425b3a217..7de4e54f9 100644 --- a/src/Admin/ThingsGateway.NewLife.X/Serialization/Xml/Xml.cs +++ b/src/Admin/ThingsGateway.NewLife.X/Serialization/Xml/Xml.cs @@ -118,7 +118,7 @@ public class Xml : FormatterBase, IXml CurrentName = name; // 一般类型为空是顶级调用 - if (Hosts.Count == 0 && Log != null && Log.Enable) WriteLog("XmlWrite {0} {1}", name ?? type.Name, value); + if (Hosts.Count == 0 && Log?.Enable == true) WriteLog("XmlWrite {0} {1}", name ?? type.Name, value); // 要先写入根 Depth++; @@ -254,7 +254,7 @@ public class Xml : FormatterBase, IXml // 移动到第一个元素 while (reader.NodeType != XmlNodeType.Element) { if (!reader.Read()) return false; } - if (Hosts.Count == 0 && Log != null && Log.Enable) WriteLog("XmlRead {0} {1}", type.Name, value); + if (Hosts.Count == 0 && Log?.Enable == true) WriteLog("XmlRead {0} {1}", type.Name, value); // 要先写入根 Depth++; diff --git a/src/Admin/ThingsGateway.NewLife.X/Stub/ScriptIgnoreAttribute.cs b/src/Admin/ThingsGateway.NewLife.X/Stub/ScriptIgnoreAttribute.cs index c11c0c627..a97a11135 100644 --- a/src/Admin/ThingsGateway.NewLife.X/Stub/ScriptIgnoreAttribute.cs +++ b/src/Admin/ThingsGateway.NewLife.X/Stub/ScriptIgnoreAttribute.cs @@ -1,5 +1,6 @@ namespace System.Web.Script.Serialization { /// 忽略Json序列化 + [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] public sealed class ScriptIgnoreAttribute : Attribute { } } \ No newline at end of file diff --git a/src/Admin/ThingsGateway.NewLife.X/Threading/TimerScheduler.cs b/src/Admin/ThingsGateway.NewLife.X/Threading/TimerScheduler.cs index f8772a049..db6ad4c93 100644 --- a/src/Admin/ThingsGateway.NewLife.X/Threading/TimerScheduler.cs +++ b/src/Admin/ThingsGateway.NewLife.X/Threading/TimerScheduler.cs @@ -129,7 +129,7 @@ public class TimerScheduler : ILogFeature if (e != null) { var swh = e.SafeWaitHandle; - if (swh != null && !swh.IsClosed) e.Set(); + if (swh?.IsClosed == false) e.Set(); } } diff --git a/src/Admin/ThingsGateway.NewLife.X/Threading/TimerX.cs b/src/Admin/ThingsGateway.NewLife.X/Threading/TimerX.cs index c26128c00..2022648c7 100644 --- a/src/Admin/ThingsGateway.NewLife.X/Threading/TimerX.cs +++ b/src/Admin/ThingsGateway.NewLife.X/Threading/TimerX.cs @@ -36,7 +36,7 @@ public class TimerX : ITimer, IDisposable /// 获取/设置 用户数据 public Object? State { - get => _state != null && _state.IsAlive ? _state.Target : null; + get => _state?.IsAlive == true ? _state.Target : null; set { if (_state == null) @@ -105,7 +105,7 @@ public class TimerX : ITimer, IDisposable _nextTick = Runtime.TickCount64; //_baseTime = DateTime.Now.AddMilliseconds(-_nextTick); - Scheduler = (scheduler == null || scheduler.IsNullOrEmpty()) ? TimerScheduler.Default : TimerScheduler.Create(scheduler); + Scheduler = (scheduler?.IsNullOrEmpty() != false) ? TimerScheduler.Default : TimerScheduler.Create(scheduler); //Scheduler.Add(this); _baseTime = Scheduler.GetNow().AddMilliseconds(-_nextTick); @@ -384,7 +384,7 @@ public class TimerX : ITimer, IDisposable public Boolean Change(TimeSpan dueTime, TimeSpan period) { if (Absolutely) return false; - if (Crons != null && Crons.Length > 0) return false; + if (Crons?.Length > 0) return false; if (period.TotalMilliseconds <= 0) { diff --git a/src/Admin/ThingsGateway.NewLife.X/Web/Link.cs b/src/Admin/ThingsGateway.NewLife.X/Web/Link.cs index 9e03eb8c2..49d18eb6c 100644 --- a/src/Admin/ThingsGateway.NewLife.X/Web/Link.cs +++ b/src/Admin/ThingsGateway.NewLife.X/Web/Link.cs @@ -48,7 +48,7 @@ public class Link public static Link[] Parse(String html, String? baseUrl = null, Func? filter = null) { // baseurl必须是/结尾 - if (baseUrl != null && !baseUrl.EndsWith('/')) baseUrl += "/"; + if (baseUrl?.EndsWith('/') == false) baseUrl += "/"; if (baseUrl.StartsWithIgnoreCase("ftp://")) return ParseFTP(html, baseUrl, filter); // 分析所有链接 @@ -218,7 +218,7 @@ public class Link if (Time.Year < 2000) { var fi = file.AsFile(); - if (fi != null && fi.Exists) Time = fi.LastWriteTime; + if (fi?.Exists == true) Time = fi.LastWriteTime; } return this; diff --git a/src/Admin/ThingsGateway.NewLife.X/Xml/SerializableDictionary.cs b/src/Admin/ThingsGateway.NewLife.X/Xml/SerializableDictionary.cs index 5eff3de35..0417d22e8 100644 --- a/src/Admin/ThingsGateway.NewLife.X/Xml/SerializableDictionary.cs +++ b/src/Admin/ThingsGateway.NewLife.X/Xml/SerializableDictionary.cs @@ -11,11 +11,8 @@ namespace ThingsGateway.NewLife.Xml [Serializable] public class SerializableDictionary : Dictionary, IXmlSerializable { - /// public SerializableDictionary() : base() { } - /// - /// public SerializableDictionary(IDictionary dictionary) : base(dictionary) { } #region IXmlSerializable 成员 diff --git a/src/Admin/ThingsGateway.NewLife.X/Xml/XmlConfig.cs b/src/Admin/ThingsGateway.NewLife.X/Xml/XmlConfig.cs index 15533693a..ca7e9de1c 100644 --- a/src/Admin/ThingsGateway.NewLife.X/Xml/XmlConfig.cs +++ b/src/Admin/ThingsGateway.NewLife.X/Xml/XmlConfig.cs @@ -113,7 +113,7 @@ public class XmlConfig : DisposeBase where TConfig : XmlConfig { // 获取XmlConfigFileAttribute特性,那里会指定配置文件名称 var att = typeof(TConfig).GetCustomAttribute(true); - if (att == null || att.FileName.IsNullOrWhiteSpace()) + if (att?.FileName.IsNullOrWhiteSpace() != false) { // 这里不能着急,派生类可能通过静态构造函数指定配置文件路径 //throw new XException("编码错误!请为配置类{0}设置{1}特性,指定配置文件!", typeof(TConfig), typeof(XmlConfigFileAttribute).Name); diff --git a/src/Admin/ThingsGateway.NewLife.X/Xml/XmlHelper.cs b/src/Admin/ThingsGateway.NewLife.X/Xml/XmlHelper.cs index 8e6fa6ac4..a45b66d1c 100644 --- a/src/Admin/ThingsGateway.NewLife.X/Xml/XmlHelper.cs +++ b/src/Admin/ThingsGateway.NewLife.X/Xml/XmlHelper.cs @@ -278,7 +278,7 @@ public static class XmlHelper var dic = new Dictionary(); - if (root.ChildNodes != null && root.ChildNodes.Count > 0) + if (root.ChildNodes?.Count > 0) { foreach (var item in root.ChildNodes) { @@ -311,7 +311,7 @@ public static class XmlHelper var root = doc.CreateElement(rootName); doc.AppendChild(root); - if (dic != null && dic.Count > 0) + if (dic?.Count > 0) { foreach (var item in dic) { diff --git a/src/Admin/ThingsGateway.Razor/Common/RandomHelper.cs b/src/Admin/ThingsGateway.Razor/Common/RandomHelper.cs index fb2dd3fba..52575c0c7 100644 --- a/src/Admin/ThingsGateway.Razor/Common/RandomHelper.cs +++ b/src/Admin/ThingsGateway.Razor/Common/RandomHelper.cs @@ -17,7 +17,7 @@ using System.Text; /// 随机数 /// [ThingsGateway.DependencyInjection.SuppressSniffer] -public class RandomHelper +public static class RandomHelper { /// /// 生成随机纯字母随机数 diff --git a/src/Admin/ThingsGateway.Razor/Const/WebsiteConst.cs b/src/Admin/ThingsGateway.Razor/Const/WebsiteConst.cs index 19de9687c..50327aeb2 100644 --- a/src/Admin/ThingsGateway.Razor/Const/WebsiteConst.cs +++ b/src/Admin/ThingsGateway.Razor/Const/WebsiteConst.cs @@ -14,7 +14,7 @@ namespace ThingsGateway.Razor; /// 网站常量 /// [ThingsGateway.DependencyInjection.SuppressSniffer] -public class WebsiteConst +public static class WebsiteConst { /// /// 默认的资源项目名称 diff --git a/src/Admin/ThingsGateway.Razor/Extensions/ObjectExtensions.cs b/src/Admin/ThingsGateway.Razor/Extensions/ObjectExtensions.cs index feb6ecee2..7199bda53 100644 --- a/src/Admin/ThingsGateway.Razor/Extensions/ObjectExtensions.cs +++ b/src/Admin/ThingsGateway.Razor/Extensions/ObjectExtensions.cs @@ -8,9 +8,6 @@ // QQ群:605534569 //------------------------------------------------------------------------------ -// 版权归百小僧及百签科技(广东)有限公司所有。 - - using System.Collections.Concurrent; using System.ComponentModel; using System.Reflection; @@ -282,7 +279,7 @@ public static class ObjectExtensions foreach (var property in propertys) { var p = oldType.GetProperty(property.Name); - if (property.CanWrite && p != null && p.CanRead) + if (property.CanWrite && p?.CanRead == true) { property.SetValue(o, ChangeType(p.GetValue(obj, null), property.PropertyType), null); } @@ -494,7 +491,7 @@ public static class ObjectExtensions /// 实例,true 表示空集合,false 表示非空集合 internal static bool IsEmpty(this IEnumerable collection) { - return collection == null || !collection.Any(); + return collection?.Any() != true; } /// diff --git a/src/Admin/ThingsGateway.Razor/Util/LocalizerUtil.cs b/src/Admin/ThingsGateway.Razor/Util/LocalizerUtil.cs index d4ff30c15..09c04b875 100644 --- a/src/Admin/ThingsGateway.Razor/Util/LocalizerUtil.cs +++ b/src/Admin/ThingsGateway.Razor/Util/LocalizerUtil.cs @@ -14,7 +14,7 @@ namespace ThingsGateway.Razor; /// [ThingsGateway.DependencyInjection.SuppressSniffer] -public class LocalizerUtil +public static class LocalizerUtil { #region 是否启用 diff --git a/src/Admin/ThingsGateway.SqlSugar/.txt b/src/Admin/ThingsGateway.SqlSugar/.txt new file mode 100644 index 000000000..cf33ff233 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/.txt @@ -0,0 +1,3 @@ +https://gitee.com/dotnetchina/SqlSugar/commit/98ed5e8a756bae6c245838df5ab4e19bf900d0af + +修改大部分CA规则,后期优化减少字符串操作 \ No newline at end of file diff --git a/src/Admin/ThingsGateway.SqlSugar/QuestDb/CsvHelperEnumToIntConverter.cs b/src/Admin/ThingsGateway.SqlSugar/QuestDb/CsvHelperEnumToIntConverter.cs new file mode 100644 index 000000000..9d8f559c8 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/QuestDb/CsvHelperEnumToIntConverter.cs @@ -0,0 +1,31 @@ +using CsvHelper; +using CsvHelper.Configuration; +using CsvHelper.TypeConversion; + +namespace SqlSugar +{ + public class CsvHelperEnumToIntConverter : ITypeConverter + { + public string ConvertToString(object value, IWriterRow row, MemberMapData memberMapData) + { + if (value == null) + { + return "null"; + } + else if (value is Enum enumValue) + { + return (Convert.ToInt32(enumValue)).ToString(); + } + throw new NotSupportedException(); + } + + public object ConvertFromString(string text, IReaderRow row, MemberMapData memberMapData) + { + if (int.TryParse(text, out int intValue)) + { + return text; + } + throw new NotSupportedException(); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/QuestDb/QuestDbPageSizeBulkCopy.cs b/src/Admin/ThingsGateway.SqlSugar/QuestDb/QuestDbPageSizeBulkCopy.cs new file mode 100644 index 000000000..ef379c279 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/QuestDb/QuestDbPageSizeBulkCopy.cs @@ -0,0 +1,33 @@ +namespace SqlSugar +{ + public class QuestDbPageSizeBulkCopy + { + private QuestDbRestAPI questDbRestAPI; + private int pageSize; + private ISqlSugarClient db; + public QuestDbPageSizeBulkCopy(QuestDbRestAPI questDbRestAPI, int pageSize, ISqlSugarClient db) + { + this.questDbRestAPI = questDbRestAPI; + this.pageSize = pageSize; + this.db = db; + } + public int BulkCopy(List insertDatas, string dateFormat = "yyyy/M/d H:mm:ss") where T : class, new() + { + int result = 0; + db.Utilities.PageEach(insertDatas, pageSize, pageItems => + { + result += questDbRestAPI.BulkCopyAsync(pageItems, dateFormat).GetAwaiter().GetResult(); + }); + return result; + } + public async Task BulkCopyAsync(List insertDatas, string dateFormat = "yyyy/M/d H:mm:ss") where T : class, new() + { + int result = 0; + await db.Utilities.PageEachAsync(insertDatas, pageSize, async pageItems => + { + result += await questDbRestAPI.BulkCopyAsync(pageItems, dateFormat).ConfigureAwait(false); + }).ConfigureAwait(false); + return result; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/QuestDb/QuestDbRestAPHelper.cs b/src/Admin/ThingsGateway.SqlSugar/QuestDb/QuestDbRestAPHelper.cs new file mode 100644 index 000000000..7268b8826 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/QuestDb/QuestDbRestAPHelper.cs @@ -0,0 +1,56 @@ +using System.Data.Common; +using System.Text; + +namespace SqlSugar +{ + internal static class QuestDbRestAPHelper + { + + /// + /// 绑定RestAPI需要的信息 + /// + /// + /// + /// + /// + public static void SetRestApiInfo(DbConnectionStringBuilder builder, ref string host, ref string username, ref string password) + { + if (builder.TryGetValue("Host", out object hostValue)) + { + host = Convert.ToString(hostValue); + } + if (builder.TryGetValue("Username", out object usernameValue)) + { + username = Convert.ToString(usernameValue); + } + if (builder.TryGetValue("Password", out object passwordValue)) + { + password = Convert.ToString(passwordValue); + } + } + + /// + /// 逐行读取,包含空行 + /// + /// + /// + public static List SplitByLine(string text) + { + List lines = new List(); + byte[] array = Encoding.UTF8.GetBytes(text); + using (MemoryStream stream = new MemoryStream(array)) + { + using (var sr = new StreamReader(stream)) + { + string line = sr.ReadLine(); + while (line != null) + { + lines.Add(line); + line = sr.ReadLine(); + } + } + } + return lines; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/QuestDb/QuestDbRestAPI.cs b/src/Admin/ThingsGateway.SqlSugar/QuestDb/QuestDbRestAPI.cs new file mode 100644 index 000000000..c01e994c3 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/QuestDb/QuestDbRestAPI.cs @@ -0,0 +1,240 @@ +using CsvHelper; +using CsvHelper.Configuration; +using CsvHelper.TypeConversion; + +using Newtonsoft.Json; + +using System.Collections; +using System.Data.Common; +using System.Globalization; +using System.Net.Http.Headers; +using System.Text; +using System.Web; +namespace SqlSugar +{ + /// + /// QuestDb RestAPI + /// + public class QuestDbRestAPI + { + internal string url = string.Empty; + internal string authorization = string.Empty; + internal static Random random = new Random(); + //can be modified + ISqlSugarClient db; + public QuestDbRestAPI(ISqlSugarClient db) + { + + var builder = new DbConnectionStringBuilder(); + builder.ConnectionString = db.CurrentConnectionConfig.ConnectionString; + this.db = db; + string host = String.Empty; + string username = String.Empty; + string password = String.Empty; + QuestDbRestAPHelper.SetRestApiInfo(builder, ref host, ref username, ref password); + BindHost(host, username, password); + } + /// + /// 执行SQL异步 + /// + /// + /// + public async Task ExecuteCommandAsync(string sql) + { + //HTTP GET 执行SQL + var result = string.Empty; + var url = $"{this.url}/exec?query={HttpUtility.UrlEncode(sql)}"; + if (!string.IsNullOrWhiteSpace(authorization)) + client.DefaultRequestHeaders.Add("Authorization", authorization); + var httpResponseMessage = await client.GetAsync(url).ConfigureAwait(false); + result = await httpResponseMessage.Content.ReadAsStringAsync().ConfigureAwait(false); + return result; + } + /// + /// 执行SQL + /// + /// + /// + public string ExecuteCommand(string sql) + { + return ExecuteCommandAsync(sql).GetAwaiter().GetResult(); + } + + public async Task BulkCopyAsync(T insertData, string dateFormat = "yyyy/M/d H:mm:ss") where T : class, new() + { + if (db.CurrentConnectionConfig.MoreSettings == null) + db.CurrentConnectionConfig.MoreSettings = new ConnMoreSettings(); + db.CurrentConnectionConfig.MoreSettings.DisableNvarchar = true; + var sql = db.Insertable(insertData).ToSqlString(); + var result = await ExecuteCommandAsync(sql).ConfigureAwait(false); + return result.Contains("OK", StringComparison.OrdinalIgnoreCase) ? 1 : 0; + } + + public int BulkCopy(T insertData, string dateFormat = "yyyy/M/d H:mm:ss") where T : class, new() + { + return BulkCopyAsync(insertData, dateFormat).GetAwaiter().GetResult(); + } + + public QuestDbPageSizeBulkCopy PageSize(int pageSize) + { + QuestDbPageSizeBulkCopy result = new QuestDbPageSizeBulkCopy(this, pageSize, db); + return result; + } + private static readonly HttpClient client = new HttpClient(); + /// + /// 批量快速插入异步 + /// + /// + /// + /// 导入时,时间格式 默认:yyyy/M/d H:mm:ss + /// + public async Task BulkCopyAsync(List insertList, string dateFormat = "yyyy/M/d H:mm:ss") where T : class, new() + { + var result = 0; + var fileName = $"{Guid.NewGuid()}.csv"; + var filePath = Path.Combine(AppContext.BaseDirectory, fileName); + try + { + var boundary = "---------------" + DateTime.Now.Ticks.ToString("x"); + var list = new List(); + var name = db.EntityMaintenance.GetEntityInfo().DbTableName; + + var key = "QuestDbBulkCopy" + typeof(T).FullName + typeof(T).GetHashCode(); + var columns = new ReflectionInoCacheService().GetOrCreate(key, () => + db.CopyNew().DbMaintenance.GetColumnInfosByTableName(name)); + columns.ForEach(d => + { + if (d.DataType == "TIMESTAMP") + { + list.Add(new Hashtable() + { + { "name", d.DbColumnName }, + { "type", d.DataType }, + { "pattern", dateFormat} + }); + } + else + { + list.Add(new Hashtable() + { + { "name", d.DbColumnName }, + { "type", d.DataType } + }); + } + }); + var schema = JsonConvert.SerializeObject(list); + //写入CSV文件 + using (var writer = new StreamWriter(filePath)) + using (var csv = new CsvWriter(writer, CultureInfo.CurrentCulture)) + { + var options = new TypeConverterOptions { Formats = new[] { GetDefaultFormat() } }; + csv.Context.TypeConverterOptionsCache.AddOptions(options); + CsvCreating(csv); + await csv.WriteRecordsAsync(insertList).ConfigureAwait(false); + } + + using var httpContent = new MultipartFormDataContent(boundary); + using var fileStream = File.OpenRead(filePath); + if (!string.IsNullOrWhiteSpace(this.authorization)) + client.DefaultRequestHeaders.Add("Authorization", this.authorization); + httpContent.Add(new StringContent(schema), "schema"); + var streamContent = new StreamContent(fileStream); + streamContent.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); + httpContent.Add(streamContent, "data", Path.GetFileName(filePath)); + + //boundary带双引号 可能导致服务器错误情况 + httpContent.Headers.Remove("Content-Type"); + httpContent.Headers.TryAddWithoutValidation("Content-Type", + "multipart/form-data; boundary=" + boundary); + var httpResponseMessage = + await Post(client, name, httpContent).ConfigureAwait(false); + var readAsStringAsync = await httpResponseMessage.Content.ReadAsStringAsync().ConfigureAwait(false); + var splitByLine = QuestDbRestAPHelper.SplitByLine(readAsStringAsync); + foreach (var s in splitByLine) + { + if (s.Contains("Rows")) + { + var strings = s.Split('|'); + if (strings[1].Trim() == "Rows imported") + { + result = Convert.ToInt32(strings[2].Trim()); + } + } + } + } + catch (Exception) + { + throw; + } + finally + { + try + { + File.Delete(filePath); + } + catch + { + // ignored + } + } + return result; + } + + private void CsvCreating(CsvWriter csv) where T : class, new() + { + var entityColumns = db.EntityMaintenance.GetEntityInfo().Columns; + if (entityColumns.Any(it => it.IsIgnore || it.UnderType?.IsEnum == true)) + { + var customMap = new DefaultClassMap(); + foreach (var item in entityColumns.Where(it => !it.IsIgnore)) + { + var memberMap = customMap.Map(typeof(T), item.PropertyInfo).Name(item.PropertyName); + if (item.UnderType?.IsEnum == true + && item.SqlParameterDbType == null + && db.CurrentConnectionConfig?.MoreSettings?.TableEnumIsString != true) + { + memberMap.TypeConverter(); + } + } + csv.Context.RegisterClassMap(customMap); + } + } + + private static string GetDefaultFormat() + { + return "yyyy-MM-ddTHH:mm:ss.fffffff"; + } + + private Task Post(HttpClient client, string name, MultipartFormDataContent httpContent) + { + return client.PostAsync($"{this.url}/imp?name={name}", httpContent); + } + + /// + /// 批量快速插入 + /// + /// + /// + /// 导入时,时间格式 默认:yyyy/M/d H:mm:ss + /// + public int BulkCopy(List insertList, string dateFormat = "yyyy/M/d H:mm:ss") where T : class, new() + { + return BulkCopyAsync(insertList, dateFormat).GetAwaiter().GetResult(); + } + private void BindHost(string host, string username, string password) + { + url = host; + if (url.EndsWith('/')) + url = url.Remove(url.Length - 1); + + if (!url.StartsWith("http", StringComparison.OrdinalIgnoreCase)) + url = $"http://{url}"; + //生成TOKEN + if (!string.IsNullOrWhiteSpace(username) && !string.IsNullOrWhiteSpace(password)) + { + var base64 = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{username}:{password}")); + authorization = $"Basic {base64}"; + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/QuestDb/QuestDbSqlSugarClientExtensions.cs b/src/Admin/ThingsGateway.SqlSugar/QuestDb/QuestDbSqlSugarClientExtensions.cs new file mode 100644 index 000000000..2bb67b9e3 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/QuestDb/QuestDbSqlSugarClientExtensions.cs @@ -0,0 +1,10 @@ +namespace SqlSugar +{ + public static class QuestDbSqlSugarClientExtensions + { + public static QuestDbRestAPI RestApi(this ISqlSugarClient db) + { + return new QuestDbRestAPI(db); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/AdoProvider/AdoAccessory.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/AdoProvider/AdoAccessory.cs new file mode 100644 index 000000000..dfa038b54 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/AdoProvider/AdoAccessory.cs @@ -0,0 +1,82 @@ +using System.Data; +using System.Reflection; +namespace SqlSugar +{ + public partial class AdoAccessory + { + protected IDbBind _DbBind; + protected IDbFirst _DbFirst; + protected ICodeFirst _CodeFirst; + protected IDbMaintenance _DbMaintenance; + protected IDbConnection _DbConnection; + + protected virtual SugarParameter[] GetParameters(object parameters, PropertyInfo[] propertyInfo, string sqlParameterKeyWord) + { + List result = new List(); + if (parameters != null) + { + var entityType = parameters.GetType(); + var isDictionary = entityType.IsIn(UtilConstants.DicArraySO, UtilConstants.DicArraySS); + if (isDictionary) + DictionaryToParameters(parameters, sqlParameterKeyWord, result, entityType); + else if (parameters is List) + { + result = (parameters as List); + } + else if (parameters is SugarParameter[]) + { + result = (parameters as SugarParameter[]).ToList(); + } + else + { + Check.Exception(!entityType.IsAnonymousType(), "The parameter format is wrong. \nUse new{{xx=xx, xx2=xx2}} or \nDictionary or \nSugarParameter [] "); + ProperyToParameter(parameters, propertyInfo, sqlParameterKeyWord, result, entityType); + } + } + return result.ToArray(); + } + protected void ProperyToParameter(object parameters, PropertyInfo[] propertyInfo, string sqlParameterKeyWord, List listParams, Type entityType) + { + PropertyInfo[] properties = null; + if (propertyInfo != null) + properties = propertyInfo; + else + properties = entityType.GetProperties(); + + foreach (PropertyInfo properyty in properties) + { + var value = properyty.GetValue(parameters, null); + if (properyty.PropertyType.IsEnum()) + value = Convert.ToInt64(value); + if (value?.Equals(DateTime.MinValue) != false) value = DBNull.Value; + if (properyty.Name.Contains("hierarchyid", StringComparison.CurrentCultureIgnoreCase)) + { + var parameter = new SugarParameter(sqlParameterKeyWord + properyty.Name, SqlDbType.Udt); + parameter.UdtTypeName = "HIERARCHYID"; + parameter.Value = value; + listParams.Add(parameter); + } + else + { + var parameter = new SugarParameter(sqlParameterKeyWord + properyty.Name, value); + listParams.Add(parameter); + } + } + } + protected void DictionaryToParameters(object parameters, string sqlParameterKeyWord, List listParams, Type entityType) + { + if (entityType == UtilConstants.DicArraySO) + { + var dictionaryParameters = (Dictionary)parameters; + var sugarParameters = dictionaryParameters.Select(it => new SugarParameter(sqlParameterKeyWord + it.Key, it.Value)); + listParams.AddRange(sugarParameters); + } + else + { + var dictionaryParameters = (Dictionary)parameters; + var sugarParameters = dictionaryParameters.Select(it => new SugarParameter(sqlParameterKeyWord + it.Key, it.Value)); + listParams.AddRange(sugarParameters); ; + } + } + } +} \ No newline at end of file diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/AdoProvider/AdoProvider.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/AdoProvider/AdoProvider.cs new file mode 100644 index 000000000..6cd59311d --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/AdoProvider/AdoProvider.cs @@ -0,0 +1,1836 @@ +using System.Collections; +using System.Data; +using System.Data.Common; +using System.Reflection; +using System.Text.RegularExpressions; +namespace SqlSugar +{ + /// + /// ** description:ActiveX Data Objects + /// ** author:sunkaixuan + /// ** date:2017/1/2 + /// ** email:610262374@qq.com + /// + public abstract partial class AdoProvider : AdoAccessory, IAdo + { + #region Constructor + public AdoProvider() + { + this.IsEnableLogEvent = false; + this.CommandType = CommandType.Text; + this.IsClearParameters = true; + this.CommandTimeOut = 300; + } + #endregion + + #region Properties + public virtual bool IsNoSql { get; set; } + internal bool IsOpenAsync { get; set; } + protected List OutputParameters { get; set; } + public virtual string SqlParameterKeyWord { get { return "@"; } } + public IDbTransaction Transaction { get; set; } + public virtual SqlSugarProvider Context { get; set; } + internal CommandType OldCommandType { get; set; } + internal bool OldClearParameters { get; set; } + public IDataParameterCollection DataReaderParameters { get; set; } + public TimeSpan SqlExecutionTime { get { return AfterTime - BeforeTime; } } + public TimeSpan ConnectionExecutionTime { get { return CheckConnectionAfterTime - CheckConnectionBeforeTime; } } + public TimeSpan GetDataExecutionTime { get { return GetDataAfterTime - GetDataBeforeTime; } } + /// + /// Add, delete and modify: the number of affected items; + /// + public int SqlExecuteCount { get; protected set; } = 0; + public SugarActionType SqlExecuteType { get => this.Context.SugarActionType; } + public StackTraceInfo SqlStackTrace { get { return UtilMethods.GetStackTrace(); } } + public bool IsDisableMasterSlaveSeparation { get; set; } + internal DateTime BeforeTime = DateTime.MinValue; + internal DateTime AfterTime = DateTime.MinValue; + internal DateTime GetDataBeforeTime = DateTime.MinValue; + internal DateTime GetDataAfterTime = DateTime.MinValue; + internal DateTime CheckConnectionBeforeTime = DateTime.MinValue; + internal DateTime CheckConnectionAfterTime = DateTime.MinValue; + public virtual IDbBind DbBind + { + get + { + if (base._DbBind == null) + { + IDbBind bind = InstanceFactory.GetDbBind(this.Context.CurrentConnectionConfig); + base._DbBind = bind; + bind.Context = this.Context; + } + return base._DbBind; + } + } + public virtual int CommandTimeOut { get; set; } + public virtual CommandType CommandType { get; set; } + public virtual bool IsEnableLogEvent { get; set; } + public virtual bool IsClearParameters { get; set; } + public virtual Action LogEventStarting => this.Context.CurrentConnectionConfig.AopEvents?.OnLogExecuting; + public virtual Action LogEventCompleted => this.Context.CurrentConnectionConfig.AopEvents?.OnLogExecuted; + public virtual Action CheckConnectionExecuting => this.Context.CurrentConnectionConfig.AopEvents?.CheckConnectionExecuting; + public virtual Action CheckConnectionExecuted => this.Context.CurrentConnectionConfig.AopEvents?.CheckConnectionExecuted; + public virtual Action OnGetDataReadering => this.Context.CurrentConnectionConfig.AopEvents?.OnGetDataReadering; + public virtual Action OnGetDataReadered => this.Context.CurrentConnectionConfig.AopEvents?.OnGetDataReadered; + public virtual Func> ProcessingEventStartingSQL => this.Context.CurrentConnectionConfig.AopEvents?.OnExecutingChangeSql; + protected virtual Func FormatSql { get; set; } + public virtual Action ErrorEvent => this.Context.CurrentConnectionConfig.AopEvents?.OnError; + public virtual Action DiffLogEvent => this.Context.CurrentConnectionConfig.AopEvents?.OnDiffLogEvent; + public virtual List SlaveConnections { get; set; } + public virtual IDbConnection MasterConnection { get; set; } + public virtual string MasterConnectionString { get; set; } + public virtual CancellationToken? CancellationToken { get; set; } + #endregion + + #region Connection + public virtual bool IsValidConnection() + { + try + { + if (this.IsAnyTran()) + { + return true; + } + using (OpenAlways()) + { + return true; + } + } + catch (Exception) + { + return false; + } + } + public virtual bool IsValidConnectionNoClose() + { + try + { + this.Open(); + return true; + } + catch (Exception) + { + return false; + } + } + public virtual void Open() + { + CheckConnection(); + } + public virtual async Task OpenAsync() + { + await CheckConnectionAsync().ConfigureAwait(false); + } + public SugarConnection OpenAlways() + { + SugarConnection result = new SugarConnection(); + result.IsAutoClose = this.Context.CurrentConnectionConfig.IsAutoCloseConnection; + result.conn = this.Connection; + result.Context = this.Context; + this.Context.CurrentConnectionConfig.IsAutoCloseConnection = false; + this.Open(); + return result; + } + public virtual void Close() + { + if (this.Transaction != null) + { + this.Transaction = null; + } + if (this.Connection != null && this.Connection.State == ConnectionState.Open) + { + this.Connection.Close(); + } + if (this.IsMasterSlaveSeparation && this.SlaveConnections.HasValue()) + { + foreach (var slaveConnection in this.SlaveConnections) + { + if (slaveConnection != null && slaveConnection.State == ConnectionState.Open) + { + slaveConnection.Close(); + } + } + } + } + public virtual void Dispose() + { + if (this.Transaction != null) + { + this.Transaction.Rollback(); + this.Transaction = null; + } + //if (this.Connection != null && this.Connection.State != ConnectionState.Open) + //{ + // this.Connection.Close(); + //} + this.Connection?.Dispose(); + this.Connection = null; + + if (this.IsMasterSlaveSeparation) + { + if (this.SlaveConnections != null) + { + foreach (var slaveConnection in this.SlaveConnections) + { + if (slaveConnection != null && slaveConnection.State == ConnectionState.Open) + { + slaveConnection.Dispose(); + } + } + } + } + } + public virtual void CheckConnection() + { + this.CheckConnectionBefore(this.Connection); + if (this.Connection.State != ConnectionState.Open) + { + try + { + this.Connection.Open(); + } + catch (Exception ex) + { + if (this.Context.CurrentConnectionConfig?.DbType == DbType.SqlServer && ex.Message?.Contains("provider: SSL") == true) + { + Check.ExceptionEasy(true, ex.Message, "SSL出错,因为升级了驱动,字符串增加Encrypt=True;TrustServerCertificate=True;即可。详细错误:" + ex.Message); + } + Check.Exception(true, ErrorMessage.ConnnectionOpen, ex.Message + $"DbType=\"{this.Context.CurrentConnectionConfig.DbType}\";ConfigId=\"{this.Context.CurrentConnectionConfig.ConfigId}\""); + } + } + this.CheckConnectionAfter(this.Connection); + } + + public virtual async Task CheckConnectionAsync() + { + this.CheckConnectionBefore(this.Connection); + if (this.Connection.State != ConnectionState.Open) + { + try + { + await (Connection as DbConnection).OpenAsync().ConfigureAwait(false); + } + catch (Exception ex) + { + Check.Exception(true, ErrorMessage.ConnnectionOpen, ex.Message + $"DbType=\"{this.Context.CurrentConnectionConfig.DbType}\";ConfigId=\"{this.Context.CurrentConnectionConfig.ConfigId}\""); + } + } + this.CheckConnectionAfter(this.Connection); + } + public virtual void CheckConnectionBefore(IDbConnection Connection) + { + this.CheckConnectionBeforeTime = DateTime.Now; + if (this.IsEnableLogEvent) + { + Action action = CheckConnectionExecuting; + if (action != null) + { + action(Connection); + } + } + } + public virtual void CheckConnectionAfter(IDbConnection Connection) + { + this.CheckConnectionAfterTime = DateTime.Now; + if (this.IsEnableLogEvent) + { + Action action = CheckConnectionExecuted; + if (action != null) + { + action(Connection, this.ConnectionExecutionTime); + } + } + } + #endregion + + #region Transaction + public virtual bool IsAnyTran() + { + return this.Transaction != null; + } + public virtual bool IsNoTran() + { + return this.Transaction == null; + } + public virtual void BeginTran() + { + CheckConnection(); + if (this.Transaction == null) + this.Transaction = this.Connection.BeginTransaction(); + } + public virtual async Task BeginTranAsync() + { + await CheckConnectionAsync().ConfigureAwait(false); + if (this.Transaction == null) + this.Transaction = await (Connection as DbConnection).BeginTransactionAsync().ConfigureAwait(false); + } + public virtual void BeginTran(IsolationLevel iso) + { + CheckConnection(); + if (this.Transaction == null) + this.Transaction = this.Connection.BeginTransaction(iso); + } + public virtual async Task BeginTranAsync(IsolationLevel iso) + { + await CheckConnectionAsync().ConfigureAwait(false); + if (this.Transaction == null) + this.Transaction = await (Connection as DbConnection).BeginTransactionAsync(iso).ConfigureAwait(false); + } + public virtual void RollbackTran() + { + if (this.Transaction != null) + { + this.Transaction.Rollback(); + this.Transaction = null; + if (this.Context.CurrentConnectionConfig.IsAutoCloseConnection) this.Close(); + } + } + + public virtual async Task RollbackTranAsync() + { + if (this.Transaction != null) + { + await (Transaction as DbTransaction).RollbackAsync().ConfigureAwait(false); + this.Transaction = null; + if (this.Context.CurrentConnectionConfig.IsAutoCloseConnection) await CloseAsync().ConfigureAwait(false); + } + } + public virtual void CommitTran() + { + if (this.Transaction != null) + { + this.Transaction.Commit(); + this.Transaction = null; + if (this.Context.CurrentConnectionConfig.IsAutoCloseConnection) this.Close(); + } + } + public virtual async Task CommitTranAsync() + { + if (this.Transaction != null) + { + await (Transaction as DbTransaction).CommitAsync().ConfigureAwait(false); + this.Transaction = null; + if (this.Context.CurrentConnectionConfig.IsAutoCloseConnection) await CloseAsync().ConfigureAwait(false); + } + } + #endregion + + #region abstract + public abstract IDataParameter[] ToIDbDataParameter(params SugarParameter[] pars); + public abstract void SetCommandToAdapter(IDataAdapter adapter, DbCommand command); + public abstract IDataAdapter GetAdapter(); + public abstract DbCommand GetCommand(string sql, SugarParameter[] pars); + public abstract IDbConnection Connection { get; set; } + public abstract void BeginTran(string transactionName);//Only SqlServer + public abstract void BeginTran(IsolationLevel iso, string transactionName);//Only SqlServer + #endregion + + #region Use + public SqlSugarTransactionAdo UseTran() + { + return new SqlSugarTransactionAdo(this.Context); + } + public DbResult UseTran(Action action, Action errorCallBack = null) + { + var result = new DbResult(); + try + { + this.BeginTran(); + if (action != null) + action(); + this.CommitTran(); + result.Data = result.IsSuccess = true; + } + catch (Exception ex) + { + result.ErrorException = ex; + result.ErrorMessage = ex.Message; + result.IsSuccess = false; + this.RollbackTran(); + if (errorCallBack != null) + { + errorCallBack(ex); + } + } + return result; + } + + public async Task> UseTranAsync(Func action, Action errorCallBack = null) + { + var result = new DbResult(); + try + { + await BeginTranAsync().ConfigureAwait(false); + if (action != null) + await action().ConfigureAwait(false); + await CommitTranAsync().ConfigureAwait(false); + result.Data = result.IsSuccess = true; + } + catch (Exception ex) + { + result.ErrorException = ex; + result.ErrorMessage = ex.Message; + result.IsSuccess = false; + await RollbackTranAsync().ConfigureAwait(false); + if (errorCallBack != null) + { + errorCallBack(ex); + } + } + return result; + } + + public DbResult UseTran(Func action, Action errorCallBack = null) + { + var result = new DbResult(); + try + { + this.BeginTran(); + if (action != null) + result.Data = action(); + this.CommitTran(); + result.IsSuccess = true; + } + catch (Exception ex) + { + result.ErrorException = ex; + result.ErrorMessage = ex.Message; + result.IsSuccess = false; + this.RollbackTran(); + if (errorCallBack != null) + { + errorCallBack(ex); + } + } + return result; + } + + public async Task> UseTranAsync(Func> action, Action errorCallBack = null) + { + var result = new DbResult(); + try + { + this.BeginTran(); + if (action != null) + result.Data = await action().ConfigureAwait(false); + this.CommitTran(); + result.IsSuccess = true; + } + catch (Exception ex) + { + result.ErrorException = ex; + result.ErrorMessage = ex.Message; + result.IsSuccess = false; + this.RollbackTran(); + if (errorCallBack != null) + { + errorCallBack(ex); + } + } + return result; + } + + public IAdo UseStoredProcedure() + { + this.OldCommandType = this.CommandType; + this.OldClearParameters = this.IsClearParameters; + this.CommandType = CommandType.StoredProcedure; + this.IsClearParameters = false; + return this; + } + #endregion + + #region Core + public virtual int ExecuteCommandWithGo(string sql, params SugarParameter[] parameters) + { + if (string.IsNullOrEmpty(sql)) + return 0; + if (!sql.Contains("go", StringComparison.CurrentCultureIgnoreCase)) + { + return ExecuteCommand(sql); + } + System.Collections.ArrayList al = new System.Collections.ArrayList(); + System.Text.RegularExpressions.Regex reg = new System.Text.RegularExpressions.Regex(@"^(\s*)go(\s*)$", System.Text.RegularExpressions.RegexOptions.IgnoreCase | System.Text.RegularExpressions.RegexOptions.Multiline | System.Text.RegularExpressions.RegexOptions.Compiled | System.Text.RegularExpressions.RegexOptions.ExplicitCapture); + al.AddRange(reg.Split(sql)); + int count = 0; + foreach (string item in al) + { + if (item.HasValue()) + { + count += ExecuteCommand(item, parameters); + } + } + return count; + } + public virtual int ExecuteCommand(string sql, params SugarParameter[] parameters) + { + try + { + InitParameters(ref sql, parameters); + if (IsFormat(parameters)) + sql = FormatSql(sql); + if (this.Context.CurrentConnectionConfig?.SqlMiddle?.IsSqlMiddle == true) + return this.Context.CurrentConnectionConfig.SqlMiddle.ExecuteCommand(sql, parameters); + SetConnectionStart(sql); + if (this.ProcessingEventStartingSQL != null) + ExecuteProcessingSQL(ref sql, ref parameters); + ExecuteBefore(sql, parameters); + IDbCommand sqlCommand = GetCommand(sql, parameters); + int count = sqlCommand.ExecuteNonQuery(); + if (this.IsClearParameters) + sqlCommand.Parameters.Clear(); + // 影响条数 + this.SqlExecuteCount = count; + ExecuteAfter(sql, parameters); + sqlCommand.Dispose(); + return count; + } + catch (Exception ex) + { + SugarCatch(ex, sql, parameters); + CommandType = CommandType.Text; + if (ErrorEvent != null) + ExecuteErrorEvent(sql, parameters, ex); + throw; + } + finally + { + if (this.IsAutoClose()) this.Close(); + SetConnectionEnd(sql); + } + } + public virtual IDataReader GetDataReader(string sql, params SugarParameter[] parameters) + { + try + { + InitParameters(ref sql, parameters); + if (IsFormat(parameters)) + sql = FormatSql(sql); + if (this.Context.CurrentConnectionConfig?.SqlMiddle?.IsSqlMiddle == true) + return this.Context.CurrentConnectionConfig.SqlMiddle.GetDataReader(sql, parameters); + SetConnectionStart(sql); + var isSp = this.CommandType == CommandType.StoredProcedure; + if (this.ProcessingEventStartingSQL != null) + ExecuteProcessingSQL(ref sql, ref parameters); + ExecuteBefore(sql, parameters); + IDbCommand sqlCommand = GetCommand(sql, parameters); + IDataReader sqlDataReader = sqlCommand.ExecuteReader(this.IsAutoClose() ? CommandBehavior.CloseConnection : CommandBehavior.Default); + if (isSp) + DataReaderParameters = sqlCommand.Parameters; + if (this.IsClearParameters) + sqlCommand.Parameters.Clear(); + ExecuteAfter(sql, parameters); + SetConnectionEnd(sql); + if (SugarCompatible.IsFramework || this.Context.CurrentConnectionConfig.DbType != DbType.Sqlite) + sqlCommand.Dispose(); + return sqlDataReader; + } + catch (Exception ex) + { + SugarCatch(ex, sql, parameters); + CommandType = CommandType.Text; + if (ErrorEvent != null) + ExecuteErrorEvent(sql, parameters, ex); + throw; + } + } + public virtual DataSet GetDataSetAll(string sql, params SugarParameter[] parameters) + { + try + { + InitParameters(ref sql, parameters); + if (IsFormat(parameters)) + sql = FormatSql(sql); + if (this.Context.CurrentConnectionConfig?.SqlMiddle?.IsSqlMiddle == true) + return this.Context.CurrentConnectionConfig.SqlMiddle.GetDataSetAll(sql, parameters); + SetConnectionStart(sql); + if (this.ProcessingEventStartingSQL != null) + ExecuteProcessingSQL(ref sql, ref parameters); + ExecuteBefore(sql, parameters); + IDataAdapter dataAdapter = this.GetAdapter(); + DbCommand sqlCommand = GetCommand(sql, parameters); + this.SetCommandToAdapter(dataAdapter, sqlCommand); + DataSet ds = new DataSet(); + dataAdapter.Fill(ds); + if (this.IsClearParameters) + sqlCommand.Parameters.Clear(); + ExecuteAfter(sql, parameters); + sqlCommand.Dispose(); + return ds; + } + catch (Exception ex) + { + SugarCatch(ex, sql, parameters); + CommandType = CommandType.Text; + if (ErrorEvent != null) + ExecuteErrorEvent(sql, parameters, ex); + throw; + } + finally + { + if (this.IsAutoClose()) this.Close(); + SetConnectionEnd(sql); + } + } + public virtual object GetScalar(string sql, params SugarParameter[] parameters) + { + try + { + InitParameters(ref sql, parameters); + if (IsFormat(parameters)) + sql = FormatSql(sql); + if (this.Context.CurrentConnectionConfig?.SqlMiddle?.IsSqlMiddle == true) + return this.Context.CurrentConnectionConfig.SqlMiddle.GetScalar(sql, parameters); + SetConnectionStart(sql); + if (this.ProcessingEventStartingSQL != null) + ExecuteProcessingSQL(ref sql, ref parameters); + ExecuteBefore(sql, parameters); + IDbCommand sqlCommand = GetCommand(sql, parameters); + object scalar = sqlCommand.ExecuteScalar(); + //scalar = (scalar == null ? 0 : scalar); + if (this.IsClearParameters) + sqlCommand.Parameters.Clear(); + ExecuteAfter(sql, parameters); + sqlCommand.Dispose(); + return scalar; + } + catch (Exception ex) + { + SugarCatch(ex, sql, parameters); + CommandType = CommandType.Text; + if (ErrorEvent != null) + ExecuteErrorEvent(sql, parameters, ex); + throw; + } + finally + { + if (this.IsAutoClose()) this.Close(); + SetConnectionEnd(sql); + } + } + + public virtual async Task ExecuteCommandAsync(string sql, params SugarParameter[] parameters) + { + try + { + Async(); + InitParameters(ref sql, parameters); + if (IsFormat(parameters)) + sql = FormatSql(sql); + if (this.Context.CurrentConnectionConfig?.SqlMiddle?.IsSqlMiddle == true) + return await Context.CurrentConnectionConfig.SqlMiddle.ExecuteCommandAsync(sql, parameters).ConfigureAwait(false); + SetConnectionStart(sql); + if (this.ProcessingEventStartingSQL != null) + ExecuteProcessingSQL(ref sql, ref parameters); + ExecuteBefore(sql, parameters); + var sqlCommand = IsOpenAsync ? await GetCommandAsync(sql, parameters).ConfigureAwait(false) : GetCommand(sql, parameters); + int count; + if (this.CancellationToken == null) + count = await sqlCommand.ExecuteNonQueryAsync().ConfigureAwait(false); + else + count = await sqlCommand.ExecuteNonQueryAsync(CancellationToken.Value).ConfigureAwait(false); + if (this.IsClearParameters) + sqlCommand.Parameters.Clear(); + this.SqlExecuteCount = count; + ExecuteAfter(sql, parameters); + sqlCommand.Dispose(); + return count; + } + catch (Exception ex) + { + SugarCatch(ex, sql, parameters); + CommandType = CommandType.Text; + if (ErrorEvent != null) + ExecuteErrorEvent(sql, parameters, ex); + throw; + } + finally + { + if (this.IsAutoClose()) this.Close(); + SetConnectionEnd(sql); + } + } + public virtual async Task GetDataReaderAsync(string sql, params SugarParameter[] parameters) + { + try + { + Async(); + InitParameters(ref sql, parameters); + if (IsFormat(parameters)) + sql = FormatSql(sql); + if (this.Context.CurrentConnectionConfig?.SqlMiddle?.IsSqlMiddle == true) + return await Context.CurrentConnectionConfig.SqlMiddle.GetDataReaderAsync(sql, parameters).ConfigureAwait(false); + SetConnectionStart(sql); + var isSp = this.CommandType == CommandType.StoredProcedure; + if (this.ProcessingEventStartingSQL != null) + ExecuteProcessingSQL(ref sql, ref parameters); + ExecuteBefore(sql, parameters); + var sqlCommand = IsOpenAsync ? await GetCommandAsync(sql, parameters).ConfigureAwait(false) : GetCommand(sql, parameters); + DbDataReader sqlDataReader; + if (this.CancellationToken == null) + sqlDataReader = await sqlCommand.ExecuteReaderAsync(IsAutoClose() ? CommandBehavior.CloseConnection : CommandBehavior.Default).ConfigureAwait(false); + else + sqlDataReader = await sqlCommand.ExecuteReaderAsync(IsAutoClose() ? CommandBehavior.CloseConnection : CommandBehavior.Default, CancellationToken.Value).ConfigureAwait(false); + if (isSp) + DataReaderParameters = sqlCommand.Parameters; + if (this.IsClearParameters) + sqlCommand.Parameters.Clear(); + ExecuteAfter(sql, parameters); + SetConnectionEnd(sql); + if (SugarCompatible.IsFramework || this.Context.CurrentConnectionConfig.DbType != DbType.Sqlite) + sqlCommand.Dispose(); + return sqlDataReader; + } + catch (Exception ex) + { + SugarCatch(ex, sql, parameters); + CommandType = CommandType.Text; + if (ErrorEvent != null) + ExecuteErrorEvent(sql, parameters, ex); + throw; + } + } + public virtual async Task GetScalarAsync(string sql, params SugarParameter[] parameters) + { + try + { + Async(); + InitParameters(ref sql, parameters); + if (IsFormat(parameters)) + sql = FormatSql(sql); + if (this.Context.CurrentConnectionConfig?.SqlMiddle?.IsSqlMiddle == true) + return await Context.CurrentConnectionConfig.SqlMiddle.GetScalarAsync(sql, parameters).ConfigureAwait(false); + SetConnectionStart(sql); + if (this.ProcessingEventStartingSQL != null) + ExecuteProcessingSQL(ref sql, ref parameters); + ExecuteBefore(sql, parameters); + var sqlCommand = IsOpenAsync ? await GetCommandAsync(sql, parameters).ConfigureAwait(false) : GetCommand(sql, parameters); + object scalar; + if (CancellationToken == null) + scalar = await sqlCommand.ExecuteScalarAsync().ConfigureAwait(false); + else + scalar = await sqlCommand.ExecuteScalarAsync(CancellationToken.Value).ConfigureAwait(false); + //scalar = (scalar == null ? 0 : scalar); + if (this.IsClearParameters) + sqlCommand.Parameters.Clear(); + ExecuteAfter(sql, parameters); + sqlCommand.Dispose(); + return scalar; + } + catch (Exception ex) + { + SugarCatch(ex, sql, parameters); + CommandType = CommandType.Text; + if (ErrorEvent != null) + ExecuteErrorEvent(sql, parameters, ex); + throw; + } + finally + { + if (this.IsAutoClose()) this.Close(); + SetConnectionEnd(sql); + } + } + public virtual Task GetDataSetAllAsync(string sql, params SugarParameter[] parameters) + { + Async(); + + //False asynchrony . No Support DataSet + if (CancellationToken == null) + { + return Task.Run(() => + { + return GetDataSetAll(sql, parameters); + }); + } + else + { + return Task.Run(() => + { + return GetDataSetAll(sql, parameters); + }, this.CancellationToken.Value); + } + } + #endregion + + #region Methods + + public virtual string GetString(string sql, object parameters) + { + return GetString(sql, this.GetParameters(parameters)); + } + public virtual string GetString(string sql, params SugarParameter[] parameters) + { + return Convert.ToString(GetScalar(sql, parameters)); + } + public virtual string GetString(string sql, List parameters) + { + if (parameters == null) + { + return GetString(sql); + } + else + { + return GetString(sql, parameters.ToArray()); + } + } + + + public virtual Task GetStringAsync(string sql, object parameters) + { + return GetStringAsync(sql, this.GetParameters(parameters)); + } + public virtual async Task GetStringAsync(string sql, params SugarParameter[] parameters) + { + return Convert.ToString(await GetScalarAsync(sql, parameters).ConfigureAwait(false)); + } + public virtual Task GetStringAsync(string sql, List parameters) + { + if (parameters == null) + { + return GetStringAsync(sql); + } + else + { + return GetStringAsync(sql, parameters.ToArray()); + } + } + + + + public virtual long GetLong(string sql, object parameters = null) + { + return Convert.ToInt64(GetScalar(sql, GetParameters(parameters))); + } + public virtual async Task GetLongAsync(string sql, object parameters = null) + { + return Convert.ToInt64(await GetScalarAsync(sql, GetParameters(parameters)).ConfigureAwait(false)); + } + + + public virtual int GetInt(string sql, object parameters) + { + return GetInt(sql, this.GetParameters(parameters)); + } + public virtual int GetInt(string sql, List parameters) + { + if (parameters == null) + { + return GetInt(sql); + } + else + { + return GetInt(sql, parameters.ToArray()); + } + } + public virtual int GetInt(string sql, params SugarParameter[] parameters) + { + return GetScalar(sql, parameters).ObjToInt(); + } + + public virtual Task GetIntAsync(string sql, object parameters) + { + return GetIntAsync(sql, this.GetParameters(parameters)); + } + public virtual Task GetIntAsync(string sql, List parameters) + { + if (parameters == null) + { + return GetIntAsync(sql); + } + else + { + return GetIntAsync(sql, parameters.ToArray()); + } + } + public virtual async Task GetIntAsync(string sql, params SugarParameter[] parameters) + { + var list = await GetScalarAsync(sql, parameters).ConfigureAwait(false); + return list.ObjToInt(); + } + + public virtual Double GetDouble(string sql, object parameters) + { + return GetDouble(sql, this.GetParameters(parameters)); + } + public virtual Double GetDouble(string sql, params SugarParameter[] parameters) + { + return GetScalar(sql, parameters).ObjToMoney(); + } + public virtual Double GetDouble(string sql, List parameters) + { + if (parameters == null) + { + return GetDouble(sql); + } + else + { + return GetDouble(sql, parameters.ToArray()); + } + } + + public virtual Task GetDoubleAsync(string sql, object parameters) + { + return GetDoubleAsync(sql, this.GetParameters(parameters)); + } + public virtual async Task GetDoubleAsync(string sql, params SugarParameter[] parameters) + { + var result = await GetScalarAsync(sql, parameters).ConfigureAwait(false); + return result.ObjToMoney(); + } + public virtual Task GetDoubleAsync(string sql, List parameters) + { + if (parameters == null) + { + return GetDoubleAsync(sql); + } + else + { + return GetDoubleAsync(sql, parameters.ToArray()); + } + } + + + public virtual decimal GetDecimal(string sql, object parameters) + { + return GetDecimal(sql, this.GetParameters(parameters)); + } + public virtual decimal GetDecimal(string sql, params SugarParameter[] parameters) + { + return GetScalar(sql, parameters).ObjToDecimal(); + } + public virtual decimal GetDecimal(string sql, List parameters) + { + if (parameters == null) + { + return GetDecimal(sql); + } + else + { + return GetDecimal(sql, parameters.ToArray()); + } + } + + + public virtual Task GetDecimalAsync(string sql, object parameters) + { + return GetDecimalAsync(sql, this.GetParameters(parameters)); + } + public virtual async Task GetDecimalAsync(string sql, params SugarParameter[] parameters) + { + var result = await GetScalarAsync(sql, parameters).ConfigureAwait(false); + return result.ObjToDecimal(); + } + public virtual Task GetDecimalAsync(string sql, List parameters) + { + if (parameters == null) + { + return GetDecimalAsync(sql); + } + else + { + return GetDecimalAsync(sql, parameters.ToArray()); + } + } + + + + public virtual DateTime GetDateTime(string sql, object parameters) + { + return GetDateTime(sql, this.GetParameters(parameters)); + } + public virtual DateTime GetDateTime(string sql, params SugarParameter[] parameters) + { + return GetScalar(sql, parameters).ObjToDate(); + } + public virtual DateTime GetDateTime(string sql, List parameters) + { + if (parameters == null) + { + return GetDateTime(sql); + } + else + { + return GetDateTime(sql, parameters.ToArray()); + } + } + + + + + public virtual Task GetDateTimeAsync(string sql, object parameters) + { + return GetDateTimeAsync(sql, this.GetParameters(parameters)); + } + public virtual async Task GetDateTimeAsync(string sql, params SugarParameter[] parameters) + { + var list = await GetScalarAsync(sql, parameters).ConfigureAwait(false); + return list.ObjToDate(); + } + public virtual Task GetDateTimeAsync(string sql, List parameters) + { + if (parameters == null) + { + return GetDateTimeAsync(sql); + } + else + { + return GetDateTimeAsync(sql, parameters.ToArray()); + } + } + + + public virtual List SqlQuery(string sql, object parameters = null) + { + var sugarParameters = this.GetParameters(parameters); + return SqlQuery(sql, sugarParameters); + } + public virtual List SqlQuery(string sql, params SugarParameter[] parameters) + { + var result = SqlQuery(sql, parameters); + return result.Item1; + } + public List MasterSqlQuery(string sql, object parameters = null) + { + var oldValue = this.Context.Ado.IsDisableMasterSlaveSeparation; + this.Context.Ado.IsDisableMasterSlaveSeparation = true; + var result = this.Context.Ado.SqlQuery(sql, parameters); + this.Context.Ado.IsDisableMasterSlaveSeparation = oldValue; + return result; + } + public async Task> MasterSqlQueryAasync(string sql, object parameters = null) + { + var oldValue = this.Context.Ado.IsDisableMasterSlaveSeparation; + this.Context.Ado.IsDisableMasterSlaveSeparation = true; + var result = await Context.Ado.SqlQueryAsync(sql, parameters).ConfigureAwait(false); + this.Context.Ado.IsDisableMasterSlaveSeparation = oldValue; + return result; + } + public virtual List SqlQuery(string sql, List parameters) + { + if (parameters != null) + { + return SqlQuery(sql, parameters.ToArray()); + } + else + { + return SqlQuery(sql); + } + } + public Tuple, List> SqlQuery(string sql, object parameters = null) + { + var result = SqlQuery(sql, parameters); + return new Tuple, List>(result.Item1, result.Item2); + } + public Tuple, List, List> SqlQuery(string sql, object parameters = null) + { + var result = SqlQuery(sql, parameters); + return new Tuple, List, List>(result.Item1, result.Item2, result.Item3); + } + public Tuple, List, List, List> SqlQuery(string sql, object parameters = null) + { + var result = SqlQuery(sql, parameters); + return new Tuple, List, List, List>(result.Item1, result.Item2, result.Item3, result.Item4); + } + public Tuple, List, List, List, List> SqlQuery(string sql, object parameters = null) + { + var result = SqlQuery(sql, parameters); + return new Tuple, List, List, List, List>(result.Item1, result.Item2, result.Item3, result.Item4, result.Item5); + } + public Tuple, List, List, List, List, List> SqlQuery(string sql, object parameters = null) + { + var result = SqlQuery(sql, parameters); + return new Tuple, List, List, List, List, List>(result.Item1, result.Item2, result.Item3, result.Item4, result.Item5, result.Item6); + } + public Tuple, List, List, List, List, List, List> SqlQuery(string sql, object parameters = null) + { + var parsmeterArray = this.GetParameters(parameters); + this.Context.InitMappingInfo(); + var builder = InstanceFactory.GetSqlbuilder(this.Context.CurrentConnectionConfig); + builder.SqlQueryBuilder.sql.Append(sql); + if (parsmeterArray != null && parsmeterArray.Length != 0) + builder.SqlQueryBuilder.Parameters.AddRange(parsmeterArray); + string sqlString = builder.SqlQueryBuilder.ToSqlString(); + SugarParameter[] Parameters = builder.SqlQueryBuilder.Parameters.ToArray(); + this.GetDataBefore(sqlString, Parameters); + using (var dataReader = this.GetDataReader(sqlString, Parameters)) + { + DbDataReader DbReader = (DbDataReader)dataReader; + List result = new List(); + if (DbReader.HasRows) + { + result = GetData(typeof(T), dataReader); + } + else + { + dataReader.Read(); + } + List result2 = null; + if (NextResult(dataReader)) + { + this.Context.InitMappingInfo(); + result2 = GetData(typeof(T2), dataReader); + } + List result3 = null; + if (NextResult(dataReader)) + { + this.Context.InitMappingInfo(); + result3 = GetData(typeof(T3), dataReader); + } + List result4 = null; + if (NextResult(dataReader)) + { + this.Context.InitMappingInfo(); + result4 = GetData(typeof(T4), dataReader); + } + List result5 = null; + if (NextResult(dataReader)) + { + this.Context.InitMappingInfo(); + result5 = GetData(typeof(T5), dataReader); + } + List result6 = null; + if (NextResult(dataReader)) + { + this.Context.InitMappingInfo(); + result6 = GetData(typeof(T6), dataReader); + } + List result7 = null; + if (NextResult(dataReader)) + { + this.Context.InitMappingInfo(); + result7 = GetData(typeof(T7), dataReader); + } + builder.SqlQueryBuilder.Clear(); + if (this.Context.Ado.DataReaderParameters != null) + { + foreach (IDataParameter item in this.Context.Ado.DataReaderParameters) + { + var parameter = parsmeterArray.FirstOrDefault(it => item.ParameterName.Substring(1) == it.ParameterName.Substring(1)); + if (parameter != null) + { + parameter.Value = item.Value; + } + } + this.Context.Ado.DataReaderParameters = null; + } + this.GetDataAfter(sqlString, Parameters); + return Tuple.Create, List, List, List, List, List, List>(result, result2, result3, result4, result5, result6, result7); + } + } + + public Task> SqlQueryAsync(string sql, object parameters, CancellationToken token) + { + this.CancellationToken = token; + return SqlQueryAsync(sql, parameters); + } + + public virtual Task> SqlQueryAsync(string sql, object parameters = null) + { + var sugarParameters = this.GetParameters(parameters); + return SqlQueryAsync(sql, sugarParameters); + } + public virtual async Task> SqlQueryAsync(string sql, params SugarParameter[] parameters) + { + var result = await SqlQueryAsync(sql, parameters).ConfigureAwait(false); + return result.Item1; + } + public virtual Task> SqlQueryAsync(string sql, List parameters) + { + if (parameters != null) + { + return SqlQueryAsync(sql, parameters.ToArray()); + } + else + { + return SqlQueryAsync(sql); + } + } + public async Task, List>> SqlQueryAsync(string sql, object parameters = null) + { + var result = await SqlQueryAsync(sql, parameters).ConfigureAwait(false); + return new Tuple, List>(result.Item1, result.Item2); + } + public async Task, List, List>> SqlQueryAsync(string sql, object parameters = null) + { + var result = await SqlQueryAsync(sql, parameters).ConfigureAwait(false); + return new Tuple, List, List>(result.Item1, result.Item2, result.Item3); + } + public async Task, List, List, List>> SqlQueryAsync(string sql, object parameters = null) + { + var result = await SqlQueryAsync(sql, parameters).ConfigureAwait(false); + return new Tuple, List, List, List>(result.Item1, result.Item2, result.Item3, result.Item4); + } + public async Task, List, List, List, List>> SqlQueryAsync(string sql, object parameters = null) + { + var result = await SqlQueryAsync(sql, parameters).ConfigureAwait(false); + return new Tuple, List, List, List, List>(result.Item1, result.Item2, result.Item3, result.Item4, result.Item5); + } + public async Task, List, List, List, List, List>> SqlQueryAsync(string sql, object parameters = null) + { + var result = await SqlQueryAsync(sql, parameters).ConfigureAwait(false); + return new Tuple, List, List, List, List, List>(result.Item1, result.Item2, result.Item3, result.Item4, result.Item5, result.Item6); + } + public async Task, List, List, List, List, List, List>> SqlQueryAsync(string sql, object parameters = null) + { + var parsmeterArray = this.GetParameters(parameters); + this.Context.InitMappingInfo(); + var builder = InstanceFactory.GetSqlbuilder(this.Context.CurrentConnectionConfig); + builder.SqlQueryBuilder.sql.Append(sql); + if (parsmeterArray != null && parsmeterArray.Length != 0) + builder.SqlQueryBuilder.Parameters.AddRange(parsmeterArray); + string sqlString = builder.SqlQueryBuilder.ToSqlString(); + SugarParameter[] Parameters = builder.SqlQueryBuilder.Parameters.ToArray(); + this.GetDataBefore(sqlString, Parameters); + using (var dataReader = await GetDataReaderAsync(sqlString, Parameters).ConfigureAwait(false)) + { + DbDataReader DbReader = (DbDataReader)dataReader; + List result = new List(); + if (DbReader.HasRows) + { + result = await GetDataAsync(typeof(T), dataReader).ConfigureAwait(false); + } + List result2 = null; + if (NextResult(dataReader)) + { + this.Context.InitMappingInfo(); + result2 = await GetDataAsync(typeof(T2), dataReader).ConfigureAwait(false); + } + List result3 = null; + if (NextResult(dataReader)) + { + this.Context.InitMappingInfo(); + result3 = await GetDataAsync(typeof(T3), dataReader).ConfigureAwait(false); + } + List result4 = null; + if (NextResult(dataReader)) + { + this.Context.InitMappingInfo(); + result4 = await GetDataAsync(typeof(T4), dataReader).ConfigureAwait(false); + } + List result5 = null; + if (NextResult(dataReader)) + { + this.Context.InitMappingInfo(); + result5 = await GetDataAsync(typeof(T5), dataReader).ConfigureAwait(false); + } + List result6 = null; + if (NextResult(dataReader)) + { + this.Context.InitMappingInfo(); + result6 = await GetDataAsync(typeof(T6), dataReader).ConfigureAwait(false); + } + List result7 = null; + if (NextResult(dataReader)) + { + this.Context.InitMappingInfo(); + result7 = await GetDataAsync(typeof(T7), dataReader).ConfigureAwait(false); + } + builder.SqlQueryBuilder.Clear(); + if (this.Context.Ado.DataReaderParameters != null) + { + foreach (IDataParameter item in this.Context.Ado.DataReaderParameters) + { + var parameter = parsmeterArray.FirstOrDefault(it => item.ParameterName.Substring(1) == it.ParameterName.Substring(1)); + if (parameter != null) + { + parameter.Value = item.Value; + } + } + this.Context.Ado.DataReaderParameters = null; + } + this.GetDataAfter(sqlString, Parameters); + return Tuple.Create, List, List, List, List, List, List>(result, result2, result3, result4, result5, result6, result7); + } + } + + public virtual T SqlQuerySingle(string sql, object parameters = null) + { + var result = SqlQuery(sql, parameters); + return result == null ? default(T) : result.FirstOrDefault(); + } + public virtual T SqlQuerySingle(string sql, params SugarParameter[] parameters) + { + var result = SqlQuery(sql, parameters); + return result == null ? default(T) : result.FirstOrDefault(); + } + public virtual T SqlQuerySingle(string sql, List parameters) + { + var result = SqlQuery(sql, parameters); + return result == null ? default(T) : result.FirstOrDefault(); + } + + + public virtual async Task SqlQuerySingleAsync(string sql, object parameters = null) + { + var result = await SqlQueryAsync(sql, parameters).ConfigureAwait(false); + return result == null ? default(T) : result.FirstOrDefault(); + } + public virtual async Task SqlQuerySingleAsync(string sql, params SugarParameter[] parameters) + { + var result = await SqlQueryAsync(sql, parameters).ConfigureAwait(false); + return result == null ? default(T) : result.FirstOrDefault(); + } + public virtual async Task SqlQuerySingleAsync(string sql, List parameters) + { + var result = await SqlQueryAsync(sql, parameters).ConfigureAwait(false); + return result == null ? default(T) : result.FirstOrDefault(); + } + + + + public virtual DataTable GetDataTable(string sql, params SugarParameter[] parameters) + { + var ds = GetDataSetAll(sql, parameters); + if (ds.Tables.Count != 0 && ds.Tables.Count > 0) return ds.Tables[0]; + return new DataTable(); + } + public virtual DataTable GetDataTable(string sql, object parameters) + { + return GetDataTable(sql, this.GetParameters(parameters)); + } + public virtual DataTable GetDataTable(string sql, List parameters) + { + if (parameters == null) + { + return GetDataTable(sql); + } + else + { + return GetDataTable(sql, parameters.ToArray()); + } + } + + + public virtual async Task GetDataTableAsync(string sql, params SugarParameter[] parameters) + { + var ds = await GetDataSetAllAsync(sql, parameters).ConfigureAwait(false); + if (ds.Tables.Count != 0 && ds.Tables.Count > 0) return ds.Tables[0]; + return new DataTable(); + } + public virtual Task GetDataTableAsync(string sql, object parameters) + { + return GetDataTableAsync(sql, this.GetParameters(parameters)); + } + public virtual Task GetDataTableAsync(string sql, List parameters) + { + if (parameters == null) + { + return GetDataTableAsync(sql); + } + else + { + return GetDataTableAsync(sql, parameters.ToArray()); + } + } + + + public virtual DataSet GetDataSetAll(string sql, object parameters) + { + return GetDataSetAll(sql, this.GetParameters(parameters)); + } + public virtual DataSet GetDataSetAll(string sql, List parameters) + { + if (parameters == null) + { + return GetDataSetAll(sql); + } + else + { + return GetDataSetAll(sql, parameters.ToArray()); + } + } + + public virtual Task GetDataSetAllAsync(string sql, object parameters) + { + return GetDataSetAllAsync(sql, this.GetParameters(parameters)); + } + public virtual Task GetDataSetAllAsync(string sql, List parameters) + { + if (parameters == null) + { + return GetDataSetAllAsync(sql); + } + else + { + return GetDataSetAllAsync(sql, parameters.ToArray()); + } + } + + + + + public virtual IDataReader GetDataReader(string sql, object parameters) + { + return GetDataReader(sql, this.GetParameters(parameters)); + } + public virtual IDataReader GetDataReader(string sql, List parameters) + { + if (parameters == null) + { + return GetDataReader(sql); + } + else + { + return GetDataReader(sql, parameters.ToArray()); + } + } + public virtual Task GetDataReaderAsync(string sql, object parameters) + { + return GetDataReaderAsync(sql, this.GetParameters(parameters)); + } + public virtual Task GetDataReaderAsync(string sql, List parameters) + { + if (parameters == null) + { + return GetDataReaderAsync(sql); + } + else + { + return GetDataReaderAsync(sql, parameters.ToArray()); + } + } + public virtual object GetScalar(string sql, object parameters) + { + return GetScalar(sql, this.GetParameters(parameters)); + } + public virtual object GetScalar(string sql, List parameters) + { + if (parameters == null) + { + return GetScalar(sql); + } + else + { + return GetScalar(sql, parameters.ToArray()); + } + } + public virtual Task GetScalarAsync(string sql, object parameters) + { + return GetScalarAsync(sql, this.GetParameters(parameters)); + } + public virtual Task GetScalarAsync(string sql, List parameters) + { + if (parameters == null) + { + return GetScalarAsync(sql); + } + else + { + return GetScalarAsync(sql, parameters.ToArray()); + } + } + public virtual int ExecuteCommand(string sql, object parameters) + { + return ExecuteCommand(sql, GetParameters(parameters)); + } + public virtual int ExecuteCommand(string sql, List parameters) + { + if (parameters == null) + { + return ExecuteCommand(sql); + } + else + { + return ExecuteCommand(sql, parameters.ToArray()); + } + } + public Task ExecuteCommandAsync(string sql, object parameters, CancellationToken cancellationToken) + { + this.CancellationToken = CancellationToken; + return ExecuteCommandAsync(sql, parameters); + } + public virtual Task ExecuteCommandAsync(string sql, object parameters) + { + return ExecuteCommandAsync(sql, GetParameters(parameters)); + } + public virtual Task ExecuteCommandAsync(string sql, List parameters) + { + if (parameters == null) + { + return ExecuteCommandAsync(sql); + } + else + { + return ExecuteCommandAsync(sql, parameters.ToArray()); + } + } + #endregion + + #region Helper + public virtual async Task GetCommandAsync(string sql, SugarParameter[] parameters) + { + await Task.FromResult(0).ConfigureAwait(false); + throw new NotImplementedException(); + } + public async Task CloseAsync() + { + if (this.Transaction != null) + { + this.Transaction = null; + } + if (this.Connection != null && this.Connection.State == ConnectionState.Open) + { + await (Connection as DbConnection).CloseAsync().ConfigureAwait(false); + } + if (this.IsMasterSlaveSeparation && this.SlaveConnections.HasValue()) + { + foreach (var slaveConnection in this.SlaveConnections) + { + if (slaveConnection != null && slaveConnection.State == ConnectionState.Open) + { + await (slaveConnection as DbConnection).CloseAsync().ConfigureAwait(false); + } + } + } + } + + protected virtual void SugarCatch(Exception ex, string sql, SugarParameter[] parameters) + { + if (sql?.Contains("{year}{month}{day}") == true) + { + Check.ExceptionEasy("need .SplitTable() message:" + ex.Message, "当前代码是否缺少 .SplitTable() ,可以看文档 [分表] , 详细错误:" + ex.Message); + } + } + public virtual void RemoveCancellationToken() + { + this.CancellationToken = null; + } + protected void Async() + { + if (this.Context.Root != null && this.Context.Root.AsyncId == null) + { + this.Context.Root.AsyncId = Guid.NewGuid(); ; + } + } + private static bool NextResult(IDataReader dataReader) + { + try + { + return dataReader.NextResult(); + } + catch + { + return false; + } + } + + protected void ExecuteProcessingSQL(ref string sql, ref SugarParameter[] parameters) + { + var result = this.ProcessingEventStartingSQL(sql, parameters); + sql = result.Key; + parameters = result.Value; + } + public virtual void ExecuteBefore(string sql, SugarParameter[] parameters) + { + //if (this.Context.CurrentConnectionConfig.Debugger != null && this.Context.CurrentConnectionConfig.Debugger.EnableThreadSecurityValidation == true) + //{ + + // var contextId = this.Context.ContextID.ToString(); + // var processId = Thread.CurrentThread.ManagedThreadId.ToString(); + // var cache = new ReflectionInoCacheService(); + // if (!cache.ContainsKey(contextId)) + // { + // cache.Add(contextId, processId); + // } + // else + // { + // var cacheValue = cache.Get(contextId); + // if (processId != cacheValue) + // { + // throw new SqlSugarException(this.Context, ErrorMessage.GetThrowMessage("Detection of SqlSugarClient cross-threading usage,a thread needs a new one", "检测到声名的SqlSugarClient跨线程使用,请检查是否静态、是否单例、或者IOC配置错误引起的,保证一个线程new出一个对象 ,具本Sql:") + sql, parameters); + // } + // } + //} + this.BeforeTime = DateTime.Now; + if (this.IsEnableLogEvent) + { + Action action = LogEventStarting; + if (action != null) + { + if (parameters == null || parameters.Length == 0) + { + action(sql, Array.Empty()); + } + else + { + action(sql, parameters); + } + } + } + } + public virtual void ExecuteAfter(string sql, SugarParameter[] parameters) + { + this.AfterTime = DateTime.Now; + var hasParameter = parameters.HasValue(); + if (hasParameter) + { + foreach (var outputParameter in parameters.Where(it => it.Direction.IsIn(ParameterDirection.Output, ParameterDirection.InputOutput, ParameterDirection.ReturnValue))) + { + var gobalOutputParamter = this.OutputParameters.FirstOrDefault(it => it.ParameterName == outputParameter.ParameterName); + if (gobalOutputParamter == null) + {//Oracle bug + gobalOutputParamter = this.OutputParameters.FirstOrDefault(it => it.ParameterName == outputParameter.ParameterName.TrimStart(outputParameter.ParameterName.First())); + } + outputParameter.Value = gobalOutputParamter.Value; + this.OutputParameters.Remove(gobalOutputParamter); + } + } + if (this.IsEnableLogEvent) + { + Action action = LogEventCompleted; + if (action != null) + { + if (parameters == null || parameters.Length == 0) + { + action(sql, Array.Empty()); + } + else + { + action(sql, parameters); + } + } + } + if (this.OldCommandType != 0) + { + this.CommandType = this.OldCommandType; + this.IsClearParameters = this.OldClearParameters; + this.OldCommandType = 0; + this.OldClearParameters = false; + } + } + public virtual void GetDataBefore(string sql, SugarParameter[] parameters) + { + this.GetDataBeforeTime = DateTime.Now; + if (this.IsEnableLogEvent) + { + Action action = OnGetDataReadering; + if (action != null) + { + if (parameters == null || parameters.Length == 0) + { + action(sql, Array.Empty()); + } + else + { + action(sql, parameters); + } + } + } + } + public virtual void GetDataAfter(string sql, SugarParameter[] parameters) + { + this.GetDataAfterTime = DateTime.Now; + if (this.IsEnableLogEvent) + { + Action action = OnGetDataReadered; + if (action != null) + { + if (parameters == null || parameters.Length == 0) + { + action(sql, Array.Empty(), GetDataExecutionTime); + } + else + { + action(sql, parameters, GetDataExecutionTime); + } + } + } + } + public virtual SugarParameter[] GetParameters(object parameters, PropertyInfo[] propertyInfo = null) + { + if (parameters == null) return null; + return base.GetParameters(parameters, propertyInfo, this.SqlParameterKeyWord); + } + protected bool IsAutoClose() + { + return this.Context.CurrentConnectionConfig.IsAutoCloseConnection && this.Transaction == null; + } + protected bool IsMasterSlaveSeparation + { + get + { + return this.Context.CurrentConnectionConfig.SlaveConnectionConfigs.HasValue() && this.IsDisableMasterSlaveSeparation == false; + } + } + protected void SetConnectionStart(string sql) + { + if (this.Transaction == null && this.IsMasterSlaveSeparation && IsRead(sql)) + { + if (this.MasterConnection == null) + { + this.MasterConnection = this.Connection; + this.MasterConnectionString = this.MasterConnection.ConnectionString; + } + var saves = this.Context.CurrentConnectionConfig.SlaveConnectionConfigs.Where(it => it.HitRate > 0).ToList(); + var currentIndex = UtilRandom.GetRandomIndex(saves.ToDictionary(it => saves.IndexOf(it), it => it.HitRate)); + var currentSaveConnection = saves[currentIndex]; + this.Connection = null; + this.Context.CurrentConnectionConfig.ConnectionString = currentSaveConnection.ConnectionString; + this.Connection = this.Connection; + if (this.SlaveConnections.IsNullOrEmpty() || !this.SlaveConnections.Any(it => EqualsConnectionString(it.ConnectionString, this.Connection.ConnectionString))) + { + if (this.SlaveConnections == null) this.SlaveConnections = new List(); + this.SlaveConnections.Add(this.Connection); + } + } + else if (this.Transaction == null && this.IsMasterSlaveSeparation && this.MasterConnection == null) + { + this.MasterConnection = this.Connection; + this.MasterConnectionString = this.MasterConnection.ConnectionString; + } + } + + private bool EqualsConnectionString(string connectionString1, string connectionString2) + { + var connectionString1Array = connectionString1.Split(';'); + var connectionString2Array = connectionString2.Split(';'); + var result = connectionString1Array.Except(connectionString2Array); + return !result.Any(); + } + private bool IsFormat(SugarParameter[] parameters) + { + return FormatSql != null && parameters?.Length > 0; + } + + protected void SetConnectionEnd(string sql) + { + if (this.IsMasterSlaveSeparation && IsRead(sql) && this.Transaction == null) + { + this.Connection = this.MasterConnection; + this.Context.CurrentConnectionConfig.ConnectionString = this.MasterConnectionString; + } + this.Context.SugarActionType = SugarActionType.UnKnown; + } + + private bool IsRead(string sql) + { + var sqlLower = sql.ToLower(); + var result = Regex.IsMatch(sqlLower, "[ ]*select[ ]") && !Regex.IsMatch(sqlLower, "[ ]*insert[ ]|[ ]*update[ ]|[ ]*delete[ ]"); + return result; + } + + protected void ExecuteErrorEvent(string sql, SugarParameter[] parameters, Exception ex) + { + this.AfterTime = DateTime.Now; + ErrorEvent(new SqlSugarException(this.Context, ex, sql, parameters)); + } + protected void InitParameters(ref string sql, SugarParameter[] parameters) + { + this.SqlExecuteCount = 0; + this.BeforeTime = DateTime.MinValue; + this.AfterTime = DateTime.MinValue; + if (parameters.HasValue()) + { + foreach (var item in parameters) + { + if (item.Value != null) + { + var type = item.Value.GetType(); + if ((type != UtilConstants.ByteArrayType && type.IsArray && item.IsArray == false) || type.FullName.IsCollectionsList() || type.IsIterator()) + { + var newValues = new List(); + foreach (var inValute in item.Value as IEnumerable) + { + newValues.Add(inValute.ObjToString()); + } + if (newValues.IsNullOrEmpty()) + { + newValues.Add("-1"); + } + if (item.ParameterName.Substring(0, 1) == ":") + { + sql = sql.Replace(string.Concat("@", item.ParameterName.AsSpan(1)), newValues.ToArray().ToJoinSqlInVals()); + } + if (item.ParameterName.Substring(0, 1) != this.SqlParameterKeyWord && sql.ObjToString().Contains(this.SqlParameterKeyWord + item.ParameterName)) + { + sql = sql.Replace(this.SqlParameterKeyWord + item.ParameterName, newValues.ToArray().ToJoinSqlInVals()); + } + else if (item.Value != null && UtilMethods.IsNumberArray(item.Value.GetType())) + { + if (newValues.Any(it => string.IsNullOrEmpty(it))) + { + newValues.RemoveAll(r => string.IsNullOrEmpty(r)); + newValues.Add("null"); + } + sql = sql.Replace(item.ParameterName, string.Join(",", newValues)); + } + else + { + sql = sql.Replace(item.ParameterName, newValues.ToArray().ToJoinSqlInVals()); + } + item.Value = DBNull.Value; + } + } + if (item.ParameterName?.Contains(' ') == true) + { + var oldName = item.ParameterName; + item.ParameterName = item.ParameterName.Replace(" ", ""); + sql = sql.Replace(oldName, item.ParameterName); + } + } + } + } + + + + private List GetData(Type entityType, IDataReader dataReader) + { + List result; + if (entityType == UtilConstants.DynamicType) + { + result = this.Context.Utilities.DataReaderToExpandoObjectListNoUsing(dataReader) as List; + } + else if (entityType == UtilConstants.ObjType) + { + result = this.Context.Utilities.DataReaderToExpandoObjectListNoUsing(dataReader).Select(it => ((TResult)(object)it)).ToList(); + } + else if (entityType.IsAnonymousType() || StaticConfig.EnableAot) + { + if (StaticConfig.EnableAot && entityType == UtilConstants.StringType) + { + result = this.Context.Ado.DbBind.DataReaderToListNoUsing(entityType, dataReader); + } + else + { + result = this.Context.Utilities.DataReaderToListNoUsing(dataReader); + } + } + else + { + result = this.Context.Ado.DbBind.DataReaderToListNoUsing(entityType, dataReader); + } + return result; + } + private async Task> GetDataAsync(Type entityType, IDataReader dataReader) + { + List result; + if (entityType == UtilConstants.DynamicType) + { + result = await Context.Utilities.DataReaderToExpandoObjectListAsyncNoUsing(dataReader).ConfigureAwait(false) as List; + } + else if (entityType == UtilConstants.ObjType) + { + var list = await Context.Utilities.DataReaderToExpandoObjectListAsyncNoUsing(dataReader).ConfigureAwait(false); + result = list.Select(it => ((TResult)(object)it)).ToList(); + } + else if (entityType.IsAnonymousType() || StaticConfig.EnableAot) + { + if (StaticConfig.EnableAot && entityType == UtilConstants.StringType) + { + result = await Context.Ado.DbBind.DataReaderToListNoUsingAsync(entityType, dataReader).ConfigureAwait(false); + } + else + { + result = await Context.Utilities.DataReaderToListAsyncNoUsing(dataReader).ConfigureAwait(false); + } + } + else + { + result = await Context.Ado.DbBind.DataReaderToListNoUsingAsync(entityType, dataReader).ConfigureAwait(false); + } + return result; + } + #endregion + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/AopProvider/AopProvider.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/AopProvider/AopProvider.cs new file mode 100644 index 000000000..358026d4b --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/AopProvider/AopProvider.cs @@ -0,0 +1,27 @@ +using System.Data; + +namespace SqlSugar +{ + public class AopProvider + { + private AopProvider() { } + public AopProvider(SqlSugarProvider context) + { + this.Context = context; + this.Context.Ado.IsEnableLogEvent = true; + } + private SqlSugarProvider Context { get; set; } + public Action OnDiffLogEvent { set { this.Context.CurrentConnectionConfig.AopEvents.OnDiffLogEvent = value; } } + public Action OnError { set { this.Context.CurrentConnectionConfig.AopEvents.OnError = value; } } + public Action OnLogExecuting { set { this.Context.CurrentConnectionConfig.AopEvents.OnLogExecuting = value; } } + public Action OnLogExecuted { set { this.Context.CurrentConnectionConfig.AopEvents.OnLogExecuted = value; } } + public Func> OnExecutingChangeSql { set { this.Context.CurrentConnectionConfig.AopEvents.OnExecutingChangeSql = value; } } + public virtual Action DataExecuting { set { this.Context.CurrentConnectionConfig.AopEvents.DataExecuting = value; } } + public Action DataChangesExecuted { set { this.Context.CurrentConnectionConfig.AopEvents.DataChangesExecuted = value; } } + public virtual Action DataExecuted { set { this.Context.CurrentConnectionConfig.AopEvents.DataExecuted = value; } } + public Action CheckConnectionExecuting { set { this.Context.CurrentConnectionConfig.AopEvents.CheckConnectionExecuting = value; } } + public Action CheckConnectionExecuted { set { this.Context.CurrentConnectionConfig.AopEvents.CheckConnectionExecuted = value; } } + public Action OnGetDataReadering { set { this.Context.CurrentConnectionConfig.AopEvents.OnGetDataReadering = value; } } + public Action OnGetDataReadered { set { this.Context.CurrentConnectionConfig.AopEvents.OnGetDataReadered = value; } } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/CacheProvider/CacheProvider.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/CacheProvider/CacheProvider.cs new file mode 100644 index 000000000..0613ab0e6 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/CacheProvider/CacheProvider.cs @@ -0,0 +1,37 @@ +namespace SqlSugar +{ + public class SugarCacheProvider + { + public ICacheService Servie { get; set; } + + public void RemoveDataCache(string likeString) + { + if (Servie == null) return; + CacheSchemeMain.RemoveCacheByLike(Servie, likeString); + } + + public List GetAllKey() + { + if (Servie == null) return new List(); + return Servie.GetAllKey()?.ToList(); + } + + public void Add(string key, object value) + { + if (Servie == null) return; + Servie.Add(key, value, 60 * 60 * 24 * 100); + } + + public void Add(string key, object value, int seconds) + { + if (Servie == null) return; + Servie.Add(key, value, seconds); + } + + public T Get(string key) + { + if (Servie == null) return default(T); + return Servie.Get(key); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/CodeFirstProvider/CodeFirstProvider.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/CodeFirstProvider/CodeFirstProvider.cs new file mode 100644 index 000000000..2b6ec138d --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/CodeFirstProvider/CodeFirstProvider.cs @@ -0,0 +1,758 @@ +using System.Reflection; +using System.Text; +using System.Text.RegularExpressions; + +namespace SqlSugar +{ + public partial class CodeFirstProvider : ICodeFirst + { + #region Properties + internal static object LockObject = new object(); + public virtual SqlSugarProvider Context { get; set; } + protected bool IsBackupTable { get; set; } + protected int MaxBackupDataRows { get; set; } + protected virtual int DefultLength { get; set; } + protected Dictionary MappingTables = new Dictionary(); + public CodeFirstProvider() + { + if (DefultLength == 0) + { + DefultLength = 255; + } + } + #endregion + + #region Public methods + public SplitCodeFirstProvider SplitTables() + { + var result = new SplitCodeFirstProvider(); + result.Context = this.Context; + result.DefaultLength = this.DefultLength; + return result; + } + + public virtual ICodeFirst BackupTable(int maxBackupDataRows = int.MaxValue) + { + this.IsBackupTable = true; + this.MaxBackupDataRows = maxBackupDataRows; + return this; + } + + public virtual ICodeFirst SetStringDefaultLength(int length) + { + DefultLength = length; + return this; + } + public void InitTablesWithAttr(params Type[] entityTypes) + { + foreach (var item in entityTypes) + { + var attr = item.GetCustomAttribute(); + if (attr == null || this.Context?.Root == null) + { + this.Context.CodeFirst.InitTables(item); + } + else + { + var newDb = this.Context.Root.GetConnectionWithAttr(item); + newDb.CodeFirst.InitTables(item); + } + } + } + public virtual void InitTables(Type entityType) + { + var splitTableAttribute = entityType.GetCustomAttribute(); + if (splitTableAttribute != null) + { + var mappingInfo = this.Context.MappingTables.FirstOrDefault(it => it.EntityName == entityType.Name); + if (mappingInfo == null) + { + UtilMethods.StartCustomSplitTable(this.Context, entityType); + this.Context.CodeFirst.SplitTables().InitTables(entityType); + this.Context.MappingTables.RemoveAll(it => it.EntityName == entityType.Name); + UtilMethods.EndCustomSplitTable(this.Context, entityType); + return; + } + } + //Prevent concurrent requests if used in your program + lock (CodeFirstProvider.LockObject) + { + MappingTableList oldTableList = CopyMappingTalbe(); + //this.Context.Utilities.RemoveCacheAll(); + var entityInfo = this.Context.GetEntityNoCacheInitMappingInfo(entityType); + if (!this.Context.DbMaintenance.IsAnySystemTablePermissions()) + { + Check.Exception(true, "Dbfirst and Codefirst requires system table permissions"); + } + + if (this.Context.Ado.Transaction == null) + { + var executeResult = Context.Ado.UseTran(() => + { + Execute(entityType, entityInfo); + }); + Check.Exception(!executeResult.IsSuccess, executeResult.ErrorMessage); + } + else + { + Execute(entityType, entityInfo); + } + + RestMappingTables(oldTableList); + } + + } + + public void InitTables() + { + InitTables(typeof(T)); + } + public void InitTables() + { + InitTables(typeof(T), typeof(T2)); + } + public void InitTables() + { + InitTables(typeof(T), typeof(T2), typeof(T3)); + } + public void InitTables() + { + InitTables(typeof(T), typeof(T2), typeof(T3), typeof(T4)); + } + public void InitTables() + { + InitTables(typeof(T), typeof(T2), typeof(T3), typeof(T4), typeof(T5)); + } + public virtual void InitTables(params Type[] entityTypes) + { + if (entityTypes.HasValue()) + { + foreach (var item in entityTypes) + { + try + { + InitTables(item); + } + catch (Exception ex) + { + + throw new Exception(item.Name + " 创建失败,请认真检查 1、属性需要get set 2、特殊类型需要加Ignore 具体错误内容: " + ex.Message); + } + } + } + } + public ICodeFirst As(Type type, string newTableName) + { + if (!MappingTables.TryAdd(type, newTableName)) + { + MappingTables[type] = newTableName; + } + return this; + } + public ICodeFirst As(string newTableName) + { + return As(typeof(T), newTableName); + } + + public virtual void InitTables(string entitiesNamespace) + { + var types = Assembly.Load(entitiesNamespace).GetTypes(); + InitTables(types); + } + public virtual void InitTables(params string[] entitiesNamespaces) + { + if (entitiesNamespaces.HasValue()) + { + foreach (var item in entitiesNamespaces) + { + InitTables(item); + } + } + } + public TableDifferenceProvider GetDifferenceTables() + { + var type = typeof(T); + return GetDifferenceTables(type); + } + + public TableDifferenceProvider GetDifferenceTables(params Type[] types) + { + TableDifferenceProvider result = new TableDifferenceProvider(); + foreach (var type in types) + { + GetDifferenceTables(result, type); + } + return result; + } + #endregion + + #region Core Logic + private void GetDifferenceTables(TableDifferenceProvider result, Type type) + { + var tempTableName = "TempDiff" + DateTime.Now.ToString("yyMMssHHmmssfff"); + var oldTableName = this.Context.EntityMaintenance.GetEntityInfo(type).DbTableName; + var db = new SqlSugarProvider(UtilMethods.CopyConfig(this.Context.CurrentConnectionConfig)); + db.CurrentConnectionConfig.ConfigureExternalServices = UtilMethods.IsNullReturnNew(db.CurrentConnectionConfig.ConfigureExternalServices); + db.CurrentConnectionConfig.ConfigureExternalServices.EntityNameService += (x, p) => + { + p.IsDisabledUpdateAll = true;//Disabled update + }; + db.MappingTables = new MappingTableList(); + db.MappingTables.Add(type.Name, tempTableName); + try + { + + var codeFirst = db.CodeFirst; + codeFirst.SetStringDefaultLength(this.DefultLength); + codeFirst.InitTables(type); + var tables = db.DbMaintenance.GetTableInfoList(false); + var oldTableInfo = tables.FirstOrDefault(it => it.Name.EqualCase(oldTableName)); + var newTableInfo = tables.FirstOrDefault(it => it.Name.EqualCase(oldTableName)); + var oldTable = db.DbMaintenance.GetColumnInfosByTableName(oldTableName, false); + var tempTable = db.DbMaintenance.GetColumnInfosByTableName(tempTableName, false); + if (oldTableInfo == null) + { + oldTableInfo = new DbTableInfo() { Name = "还未创建:" + oldTableName }; + newTableInfo = new DbTableInfo() { Name = "还未创建:" + oldTableName }; + } + result.tableInfos.Add(new DiffTableInfo() + { + OldTableInfo = oldTableInfo, + NewTableInfo = newTableInfo, + OldColumnInfos = oldTable, + NewColumnInfos = tempTable + }); + } + catch (Exception) + { + throw; + } + finally + { + db.DbMaintenance.DropTable(tempTableName); + } + } + protected virtual void Execute(Type entityType, EntityInfo entityInfo) + { + //var entityInfo = this.Context.EntityMaintenance.GetEntityInfoNoCache(entityType); + if (entityInfo.Discrimator.HasValue()) + { + Check.ExceptionEasy(!Regex.IsMatch(entityInfo.Discrimator, @"^(?:\w+:\w+)(?:,\w+:\w+)*$"), "The format should be type:cat for this type, and if there are multiple, it can be FieldName:cat,FieldName2:dog ", "格式错误应该是type:cat这种格式,如果是多个可以FieldName:cat,FieldName2:dog,不要有空格"); + var array = entityInfo.Discrimator.Split(','); + foreach (var disItem in array) + { + var name = disItem.Split(':').First(); + var value = disItem.Split(':').Last(); + entityInfo.Columns.Add(new EntityColumnInfo() { PropertyInfo = typeof(DiscriminatorObject).GetProperty(nameof(DiscriminatorObject.FieldName)), IsOnlyIgnoreUpdate = true, DbColumnName = name, UnderType = typeof(string), PropertyName = name, Length = 50 }); + } + } + if (this.MappingTables.TryGetValue(entityType, out string? v)) + { + entityInfo.DbTableName = v; + this.Context.MappingTables.Add(entityInfo.EntityName, entityInfo.DbTableName); + } + if (this.DefultLength > 0) + { + foreach (var item in entityInfo.Columns) + { + if (item.PropertyInfo.PropertyType == UtilConstants.StringType && item.DataType.IsNullOrEmpty() && item.Length == 0) + { + item.Length = DefultLength; + } + if (item.DataType?.Contains(',') == true && !Regex.IsMatch(item.DataType, @"\d\,\d")) + { + var types = item.DataType.Split(',').Select(it => it.ToLower()).ToHashSet(); + var mapingTypes = this.Context.Ado.DbBind.MappingTypes.Select(it => it.Key.ToLower()).ToHashSet(); + var mappingType = types.FirstOrDefault(it => mapingTypes.Contains(it)); + if (mappingType != null) + { + item.DataType = mappingType; + } + if (item.DataType == "varcharmax") + { + item.DataType = "nvarchar(max)"; + } + } + } + } + var tableName = GetTableName(entityInfo); + this.Context.MappingTables.Add(entityInfo.EntityName, tableName); + entityInfo.DbTableName = tableName; + entityInfo.Columns.ForEach(it => + { + it.DbTableName = tableName; + if (it.UnderType?.Name == "DateOnly" && it.DataType == null) + { + it.DataType = "Date"; + } + if (it.UnderType?.Name == "TimeOnly" && it.DataType == null) + { + it.DataType = "Time"; + } + }); + var isAny = this.Context.DbMaintenance.IsAnyTable(tableName, false); + if (isAny && entityInfo.IsDisabledUpdateAll) + { + return; + } + if (isAny) + ExistLogic(entityInfo); + else + NoExistLogic(entityInfo); + + this.Context.DbMaintenance.AddRemark(entityInfo); + this.Context.DbMaintenance.AddIndex(entityInfo); + CreateIndex(entityInfo); + this.Context.DbMaintenance.AddDefaultValue(entityInfo); + } + + private void CreateIndex(EntityInfo entityInfo) + { + if (entityInfo.Indexs.HasValue()) + { + foreach (var item in entityInfo.Indexs) + { + if (entityInfo.Type.GetCustomAttribute() != null) + { + if (item.IndexName?.Contains("{split_table}") == true) + { + item.IndexName = item.IndexName.Replace("{split_table}", entityInfo.DbTableName); + } + else + { + item.IndexName = item.IndexName + entityInfo.DbTableName; + } + } + if (this.Context.CurrentConnectionConfig.IndexSuffix.HasValue()) + { + item.IndexName = (this.Context.CurrentConnectionConfig.IndexSuffix + item.IndexName); + } + var include = ""; + if (item.IndexName != null) + { + var database = "{db}"; + if (item.IndexName.Contains(database)) + { + item.IndexName = item.IndexName.Replace(database, this.Context.Ado.Connection.Database); + } + var table = "{table}"; + if (item.IndexName.Contains(table)) + { + item.IndexName = item.IndexName.Replace(table, entityInfo.DbTableName); + } + if (item.IndexName.Contains("{include:", StringComparison.CurrentCultureIgnoreCase)) + { + include = Regex.Match(item.IndexName, @"\{include\:.+$").Value; + item.IndexName = item.IndexName.Replace(include, ""); + } + if (item.IndexName.Contains('.') && item.IndexName.Contains('[')) + { + item.IndexName = item.IndexName.Replace(".", "_"); + item.IndexName = item.IndexName.Replace("[", "").Replace("]", ""); + } + } + if (!this.Context.DbMaintenance.IsAnyIndex(item.IndexName)) + { + var querybulder = InstanceFactory.GetSqlbuilder(this.Context.CurrentConnectionConfig); + querybulder.Context = this.Context; + var fileds = item.IndexFields + .Select(it => + { + var dbColumn = entityInfo.Columns.FirstOrDefault(z => z.PropertyName == it.Key); + if (dbColumn == null && entityInfo.Discrimator == null) + { + Check.ExceptionEasy($"{entityInfo.EntityName} no SugarIndex[ {it.Key} ] found", $"类{entityInfo.EntityName} 索引特性没找到列 :{it.Key}"); + } + return new KeyValuePair(dbColumn.DbColumnName, it.Value); + }) + .Select(it => querybulder.GetTranslationColumnName(it.Key) + " " + it.Value).ToArray(); + this.Context.DbMaintenance.CreateIndex(entityInfo.DbTableName, fileds, item.IndexName + include, item.IsUnique); + } + } + } + } + + public virtual void NoExistLogic(EntityInfo entityInfo) + { + var tableName = GetTableName(entityInfo); + //Check.Exception(entityInfo.Columns.Where(it => it.IsPrimarykey).Count() > 1, "Use Code First ,The primary key must not exceed 1"); + List columns = new List(); + if (entityInfo.Columns.HasValue()) + { + foreach (var item in entityInfo.Columns.OrderBy(it => it.IsPrimarykey ? 0 : 1).Where(it => it.IsIgnore == false)) + { + DbColumnInfo dbColumnInfo = EntityColumnToDbColumn(entityInfo, tableName, item); + columns.Add(dbColumnInfo); + } + if (entityInfo.IsCreateTableFiledSort) + { + columns = columns.OrderBy(c => c.CreateTableFieldSort).ToList(); + columns = columns.OrderBy(it => it.IsPrimarykey ? 0 : 1).ToList(); + } + } + this.Context.DbMaintenance.CreateTable(tableName, columns, true); + } + public virtual void ExistLogic(EntityInfo entityInfo) + { + if (entityInfo.Columns.HasValue() && entityInfo.IsDisabledUpdateAll == false) + { + //Check.Exception(entityInfo.Columns.Where(it => it.IsPrimarykey).Count() > 1, "Multiple primary keys do not support modifications"); + + var tableName = GetTableName(entityInfo); + var dbColumns = this.Context.DbMaintenance.GetColumnInfosByTableName(tableName, false); + ConvertColumns(dbColumns); + var entityColumns = entityInfo.Columns.Where(it => it.IsIgnore == false).ToList(); + var dropColumns = dbColumns + .Where(dc => !entityColumns.Any(ec => dc.DbColumnName.Equals(ec.OldDbColumnName, StringComparison.CurrentCultureIgnoreCase))) + .Where(dc => !entityColumns.Any(ec => dc.DbColumnName.Equals(ec.DbColumnName, StringComparison.CurrentCultureIgnoreCase))) + .ToList(); + var addColumns = entityColumns + .Where(ec => ec.OldDbColumnName.IsNullOrEmpty() || !dbColumns.Any(dc => dc.DbColumnName.Equals(ec.OldDbColumnName, StringComparison.CurrentCultureIgnoreCase))) + .Where(ec => !dbColumns.Any(dc => ec.DbColumnName.Equals(dc.DbColumnName, StringComparison.CurrentCultureIgnoreCase))).ToList(); + var alterColumns = entityColumns + .Where(it => it.IsDisabledAlterColumn == false) + .Where(ec => !dbColumns.Any(dc => dc.DbColumnName.Equals(ec.OldDbColumnName, StringComparison.CurrentCultureIgnoreCase))) + .Where(ec => + dbColumns.Any(dc => dc.DbColumnName.EqualCase(ec.DbColumnName) + && ((ec.Length != dc.Length && !UtilMethods.GetUnderType(ec.PropertyInfo).IsEnum() && UtilMethods.GetUnderType(ec.PropertyInfo).IsIn(UtilConstants.StringType)) || + ec.IsNullable != dc.IsNullable || + IsNoSamePrecision(ec, dc) || + IsNoSamgeType(ec, dc)))).ToList(); + + alterColumns.RemoveAll(entityColumnInfo => + { + var bigStringArray = StaticConfig.CodeFirst_BigString.Replace("varcharmax", "nvarchar(max)").Split(','); + var dbColumnInfo = dbColumns.FirstOrDefault(dc => dc.DbColumnName.EqualCase(entityColumnInfo.DbColumnName)); + var isMaxString = (dbColumnInfo?.Length == -1 && dbColumnInfo?.DataType?.EqualCase("nvarchar") == true); + var isRemove = + dbColumnInfo != null + && bigStringArray.Contains(entityColumnInfo.DataType) + && isMaxString; + return isRemove; + }); + var renameColumns = entityColumns + .Where(it => !string.IsNullOrEmpty(it.OldDbColumnName)) + .Where(entityColumn => dbColumns.Any(dbColumn => entityColumn.OldDbColumnName.Equals(dbColumn.DbColumnName, StringComparison.CurrentCultureIgnoreCase))) + .ToList(); + + + var isMultiplePrimaryKey = dbColumns.Where(it => it.IsPrimarykey).Count() > 1 || entityColumns.Where(it => it.IsPrimarykey).Count() > 1; + + + var isChange = false; + foreach (var item in addColumns) + { + this.Context.DbMaintenance.AddColumn(tableName, EntityColumnToDbColumn(entityInfo, tableName, item)); + isChange = true; + } + if (entityInfo.IsDisabledDelete == false) + { + foreach (var item in dropColumns) + { + this.Context.DbMaintenance.DropColumn(tableName, item.DbColumnName); + isChange = true; + } + } + foreach (var item in alterColumns) + { + + if (this.Context.CurrentConnectionConfig.DbType == DbType.Oracle) + { + var entityColumnItem = entityColumns.FirstOrDefault(y => y.DbColumnName == item.DbColumnName); + if (entityColumnItem != null && !string.IsNullOrEmpty(entityColumnItem.DataType)) + { + continue; + } + } + + this.Context.DbMaintenance.UpdateColumn(tableName, EntityColumnToDbColumn(entityInfo, tableName, item)); + isChange = true; + } + foreach (var item in renameColumns) + { + this.Context.DbMaintenance.RenameColumn(tableName, item.OldDbColumnName, item.DbColumnName); + isChange = true; + } + var isAddPrimaryKey = false; + foreach (var item in entityColumns) + { + var dbColumn = dbColumns.FirstOrDefault(dc => dc.DbColumnName.Equals(item.DbColumnName, StringComparison.CurrentCultureIgnoreCase)); + if (dbColumn == null) continue; + bool pkDiff, idEntityDiff; + KeyAction(item, dbColumn, out pkDiff, out idEntityDiff); + if (dbColumn != null && pkDiff && !idEntityDiff && isMultiplePrimaryKey == false) + { + var isAdd = item.IsPrimarykey; + if (isAdd) + { + isAddPrimaryKey = true; + this.Context.DbMaintenance.AddPrimaryKey(tableName, item.DbColumnName); + } + else + { + this.Context.DbMaintenance.DropConstraint(tableName, string.Format("PK_{0}_{1}", tableName, item.DbColumnName)); + } + } + else if ((pkDiff || idEntityDiff) && isMultiplePrimaryKey == false) + { + ChangeKey(entityInfo, tableName, item); + } + } + if (isAddPrimaryKey == false && entityColumns.Count(it => it.IsPrimarykey) == 1 && !dbColumns.Any(it => it.IsPrimarykey)) + { + var addPk = entityColumns.First(it => it.IsPrimarykey); + this.Context.DbMaintenance.AddPrimaryKey(tableName, addPk.DbColumnName); + } + if (isMultiplePrimaryKey) + { + var oldPkNames = dbColumns.Where(it => it.IsPrimarykey).Select(it => it.DbColumnName.ToLower()).OrderBy(it => it).ToList(); + var newPkNames = entityColumns.Where(it => it.IsPrimarykey).Select(it => it.DbColumnName.ToLower()).OrderBy(it => it).ToList(); + if (oldPkNames.Count == 0 && newPkNames.Count > 1) + { + try + { + this.Context.DbMaintenance.AddPrimaryKeys(tableName, newPkNames.ToArray()); + } + catch (Exception ex) + { + Check.Exception(true, ErrorMessage.GetThrowMessage("The current database does not support changing multiple primary keys. " + ex.Message, "当前数据库不支持修改多主键," + ex.Message)); + throw; + } + } + else if (!Enumerable.SequenceEqual(oldPkNames, newPkNames)) + { + Check.Exception(true, ErrorMessage.GetThrowMessage("Modification of multiple primary key tables is not supported. Delete tables while creating", "不支持修改多主键表,请删除表在创建")); + } + + } + if (isChange && IsBackupTable) + { + this.Context.DbMaintenance.BackupTable(tableName, tableName + DateTime.Now.ToString("yyyyMMddHHmmss"), MaxBackupDataRows); + } + ExistLogicEnd(entityColumns); + } + } + + private bool IsNoSamePrecision(EntityColumnInfo ec, DbColumnInfo dc) + { + if (this.Context.CurrentConnectionConfig.MoreSettings?.EnableCodeFirstUpdatePrecision == true) + { + return ec.DecimalDigits != dc.DecimalDigits && ec.UnderType.IsIn(UtilConstants.DobType, UtilConstants.DecType); + } + return false; + } + + protected virtual void KeyAction(EntityColumnInfo item, DbColumnInfo dbColumn, out bool pkDiff, out bool idEntityDiff) + { + pkDiff = item.IsPrimarykey != dbColumn.IsPrimarykey; + idEntityDiff = item.IsIdentity != dbColumn.IsIdentity; + } + + protected virtual void ChangeKey(EntityInfo entityInfo, string tableName, EntityColumnInfo item) + { + string constraintName = string.Format("PK_{0}_{1}", tableName, item.DbColumnName); + if (this.Context.DbMaintenance.IsAnyConstraint(constraintName)) + this.Context.DbMaintenance.DropConstraint(tableName, constraintName); + this.Context.DbMaintenance.DropColumn(tableName, item.DbColumnName); + this.Context.DbMaintenance.AddColumn(tableName, EntityColumnToDbColumn(entityInfo, tableName, item)); + if (item.IsPrimarykey) + this.Context.DbMaintenance.AddPrimaryKey(tableName, item.DbColumnName); + } + + protected virtual void ExistLogicEnd(List dbColumns) + { + + } + protected virtual void ConvertColumns(List dbColumns) + { + + } + #endregion + + #region Helper methods + private void RestMappingTables(MappingTableList oldTableList) + { + this.Context.MappingTables.Clear(); + foreach (var table in oldTableList) + { + this.Context.MappingTables.Add(table.EntityName, table.DbTableName); + } + } + private MappingTableList CopyMappingTalbe() + { + MappingTableList oldTableList = new MappingTableList(); + if (this.Context.MappingTables == null) + { + this.Context.MappingTables = new MappingTableList(); + } + foreach (var table in this.Context.MappingTables) + { + oldTableList.Add(table.EntityName, table.DbTableName); + } + return oldTableList; + } + + public virtual string GetCreateTableString(EntityInfo entityInfo) + { + StringBuilder result = new StringBuilder(); + var tableName = GetTableName(entityInfo); + return result.ToString(); + } + public virtual string GetCreateColumnsString(EntityInfo entityInfo) + { + StringBuilder result = new StringBuilder(); + var tableName = GetTableName(entityInfo); + return result.ToString(); + } + protected virtual string GetTableName(EntityInfo entityInfo) + { + return this.Context.EntityMaintenance.GetTableName(entityInfo.EntityName); + } + protected virtual DbColumnInfo EntityColumnToDbColumn(EntityInfo entityInfo, string tableName, EntityColumnInfo item) + { + var propertyType = UtilMethods.GetUnderType(item.PropertyInfo); + var result = new DbColumnInfo() + { + TableId = entityInfo.Columns.IndexOf(item), + DbColumnName = item.DbColumnName.HasValue() ? item.DbColumnName : item.PropertyName, + IsPrimarykey = item.IsPrimarykey, + IsIdentity = item.IsIdentity, + TableName = tableName, + IsNullable = item.IsNullable, + DefaultValue = item.DefaultValue, + ColumnDescription = item.ColumnDescription, + Length = item.Length, + DecimalDigits = item.DecimalDigits, + CreateTableFieldSort = item.CreateTableFieldSort + }; + GetDbType(item, propertyType, result); + return result; + } + + protected virtual void GetDbType(EntityColumnInfo item, Type propertyType, DbColumnInfo result) + { + if (!string.IsNullOrEmpty(item.DataType)) + { + result.DataType = item.DataType; + } + else if (propertyType.IsEnum()) + { + result.DataType = this.Context.Ado.DbBind.GetDbTypeName(item.Length > 9 ? UtilConstants.LongType.Name : UtilConstants.IntType.Name); + } + else + { + var name = GetType(propertyType.Name); + result.DataType = this.Context.Ado.DbBind.GetDbTypeName(name); + } + } + + protected virtual bool IsNoSamgeType(EntityColumnInfo ec, DbColumnInfo dc) + { + if (!string.IsNullOrEmpty(ec.DataType)) + { + if (ec.IsIdentity && dc.IsIdentity) + { + return false; + } + else + { + return ec.DataType != dc.DataType; + } + } + var propertyType = UtilMethods.GetUnderType(ec.PropertyInfo); + var properyTypeName = string.Empty; + if (propertyType.IsEnum()) + { + properyTypeName = this.Context.Ado.DbBind.GetDbTypeName(ec.Length > 9 ? UtilConstants.LongType.Name : UtilConstants.IntType.Name); + } + else + { + var name = GetType(propertyType.Name); + properyTypeName = this.Context.Ado.DbBind.GetDbTypeName(name); + } + var dataType = dc.DataType; + if (properyTypeName == "boolean" && dataType == "bool") + { + return false; + } + if (properyTypeName?.ToLower() == "varchar" && dataType?.ToLower() == "string") + { + return false; + } + if (properyTypeName?.ToLower() == "varchar" && dataType?.ToLower() == "nvarchar") + { + return false; + } + if (properyTypeName?.ToLower() == "number" && dataType?.ToLower() == "decimal") + { + return false; + } + if (this.Context.CurrentConnectionConfig?.MoreSettings?.EnableOracleIdentity == true && properyTypeName?.ToLower() == "int" && dataType?.ToLower() == "decimal") + { + return false; + } + if (properyTypeName?.ToLower() == "int" && dataType?.ToLower() == "decimal" && dc.Length == 22 && dc.Scale == 0 && this.Context.CurrentConnectionConfig.DbType == DbType.Oracle) + { + return false; + } + if (properyTypeName?.ToLower() == "int" && dataType?.ToLower() == "int32") + { + return false; + } + if (properyTypeName?.ToLower() == "date" && dataType?.ToLower() == "datetime") + { + return false; + } + if (properyTypeName?.ToLower() == "bigint" && dataType?.ToLower() == "int64") + { + return false; + } + if (properyTypeName?.ToLower() == "blob" && dataType?.ToLower() == "byte[]") + { + return false; + } + if (properyTypeName == null || dataType == null) + { + return properyTypeName != dataType; + } + else if (this.Context.CurrentConnectionConfig.DbType == DbType.SqlServer && dataType.EqualCase("timestamp") && properyTypeName.EqualCase("varbinary")) + { + return false; + } + else if (properyTypeName.IsIn("int", "long") && dataType.EqualCase("decimal") && dc.Length == 38 && dc.DecimalDigits == 127) + { + return false; + } + else if (dataType.EqualCase("numeric") && properyTypeName.EqualCase("decimal")) + { + return false; + } + else if (ec.UnderType == UtilConstants.BoolType && dc.OracleDataType?.EqualCase("number") == true) + { + return false; + } + else if (ec.UnderType == UtilConstants.LongType && dc.Length == 19 && dc.DecimalDigits == 0 && dc.OracleDataType?.EqualCase("number") == true) + { + return false; + } + else + { + return !properyTypeName.Equals(dataType, StringComparison.CurrentCultureIgnoreCase); + } + } + protected string GetType(string name) + { + if (name.IsContainsIn("UInt32", "UInt16", "UInt64")) + { + name = name.TrimStart('U'); + } + if (name == "char") + { + name = "string"; + } + return name; + } + + #endregion + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/CodeFirstProvider/SplitCodeFirstProvider.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/CodeFirstProvider/SplitCodeFirstProvider.cs new file mode 100644 index 000000000..eab8b23b3 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/CodeFirstProvider/SplitCodeFirstProvider.cs @@ -0,0 +1,66 @@ +using System.Reflection; + +namespace SqlSugar +{ + public class SplitCodeFirstProvider + { + public SqlSugarProvider Context; + + public int DefaultLength { get; set; } + + public void InitTables() + { + var type = typeof(T); + InitTables(type); + } + + public void InitTables(Type type) + { + var isSplitEntity = type.GetCustomAttribute() != null; + if (isSplitEntity) + { + UtilMethods.StartCustomSplitTable(this.Context, type); + _InitTables(type); + UtilMethods.EndCustomSplitTable(this.Context, type); + } + else + { + this.Context.CodeFirst.SetStringDefaultLength(this.DefaultLength).InitTables(type); + } + + } + private void _InitTables(Type type) + { + //var oldMapping = this.Context.Utilities.TranslateCopy(this.Context.MappingTables); + SplitTableContext helper = new SplitTableContext(Context) + { + EntityInfo = this.Context.EntityMaintenance.GetEntityInfo(type) + }; + helper.CheckPrimaryKey(); + var tables = helper.GetTables(); + //var oldMapingTables = this.Context.MappingTables; + if (tables.Count > 0) + { + foreach (var item in tables) + { + this.Context.MappingTables.Add(helper.EntityInfo.EntityName, item.TableName); + this.Context.CodeFirst.SetStringDefaultLength(this.DefaultLength).InitTables(type); + } + } + else + { + this.Context.MappingTables.Add(helper.EntityInfo.EntityName, helper.GetDefaultTableName()); + this.Context.CodeFirst.SetStringDefaultLength(this.DefaultLength).InitTables(type); + } + this.Context.MappingTables.Add(helper.EntityInfo.EntityName, helper.EntityInfo.DbTableName); + } + + public void InitTables(Type[] types) + { + foreach (var type in types) + { + InitTables(type); + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/CodeFirstProvider/TableDifferenceProvider.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/CodeFirstProvider/TableDifferenceProvider.cs new file mode 100644 index 000000000..e0c1d4734 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/CodeFirstProvider/TableDifferenceProvider.cs @@ -0,0 +1,176 @@ +using System.Text; +namespace SqlSugar +{ + public class TableDifferenceProvider + { + internal List tableInfos = new List(); + public string ToDiffString() + { + StringBuilder sb = new StringBuilder(); + sb.AppendLine(); + var diffTables = this.ToDiffList(); + if (diffTables.IsNullOrEmpty()) + { + sb.AppendLine("No change"); + } + else + { + foreach (var item in diffTables) + { + sb.AppendLine($"----Table:{item.TableName}----"); + if (item.AddColums.HasValue()) + { + sb.AppendLine($"Add column: "); + foreach (var addItem in item.AddColums) + { + sb.AppendLine($"{addItem.Message} "); + } + } + if (item.UpdateColums.HasValue()) + { + sb.AppendLine($"Update column: "); + foreach (var addItem in item.UpdateColums) + { + sb.AppendLine($"{addItem.Message} "); + } + } + if (item.DeleteColums.HasValue()) + { + sb.AppendLine($"Delete column: "); + foreach (var addItem in item.DeleteColums) + { + sb.AppendLine($"{addItem.Message} "); + } + } + } + } + sb.AppendLine(); + sb.AppendLine(); + return sb.ToString(); + } + + public List ToDiffList() + { + List result = new List(); + foreach (var tableInfo in tableInfos) + { + TableDifferenceInfo addItem = new TableDifferenceInfo(); + if (tableInfo.OldTableInfo == null) + tableInfo.OldTableInfo = new DbTableInfo(); + addItem.TableName = tableInfo.OldTableInfo.Name; + addItem.AddColums = GetAddColumn(tableInfo); + addItem.UpdateColums = GetUpdateColumn(tableInfo); + addItem.DeleteColums = GetDeleteColumn(tableInfo); + if (addItem.IsDiff) + result.Add(addItem); + } + return result; + } + + private static List GetDeleteColumn(DiffTableInfo tableInfo) + { + List result = new List(); + var columns = tableInfo.OldColumnInfos.Where(z => !tableInfo.NewColumnInfos.Any(y => y.DbColumnName.EqualCase(z.DbColumnName))); + return columns.Select(it => new DiffColumsInfo() { Message = GetColumnString(it) }).ToList(); + } + + private List GetUpdateColumn(DiffTableInfo tableInfo) + { + List result = new List(); + result = tableInfo.NewColumnInfos + .Where(z => tableInfo.OldColumnInfos.Any(y => y.DbColumnName.EqualCase(z.DbColumnName) && ( + z.Length != y.Length || + z.ColumnDescription != y.ColumnDescription || + z.DataType != y.DataType || + z.DecimalDigits != y.DecimalDigits || + z.IsPrimarykey != y.IsPrimarykey || + z.IsIdentity != y.IsIdentity || + z.IsNullable != y.IsNullable + ))).Select(it => new DiffColumsInfo() + { + Message = GetUpdateColumnString(it, tableInfo.OldColumnInfos.FirstOrDefault(y => y.DbColumnName.EqualCase(it.DbColumnName))) + }).ToList(); + return result; + } + + private static List GetAddColumn(DiffTableInfo tableInfo) + { + List result = new List(); + var columns = tableInfo.NewColumnInfos.Where(z => !tableInfo.OldColumnInfos.Any(y => y.DbColumnName.EqualCase(z.DbColumnName))); + return columns.Select(it => new DiffColumsInfo() { Message = GetColumnString(it) }).ToList(); + } + + private static string GetColumnString(DbColumnInfo it) + { + return $"{it.DbColumnName} {it.DataType} {it.Length} {it.Scale} default:{it.DefaultValue} description:{it.ColumnDescription} pk:{it.IsPrimarykey} nullable:{it.IsNullable} identity:{it.IsIdentity} "; + } + + private static string GetUpdateColumnString(DbColumnInfo it, DbColumnInfo old) + { + var result = $"{it.DbColumnName} changes: "; + if (it.DataType != old.DataType) + { + result += $" [DataType:{old.DataType}->{it.DataType}] "; + } + if (it.Length != old.Length) + { + result += $" [Length:{old.Length}->{it.Length}] "; + } + if (it.Scale != old.Scale) + { + result += $" [Scale:{old.Scale}->{it.Scale}] "; + } + if (it.ColumnDescription != old.ColumnDescription) + { + result += $" [Description:{old.ColumnDescription}->{it.ColumnDescription}] "; + } + if (it.IsPrimarykey != old.IsPrimarykey) + { + result += $" [Pk:{old.IsPrimarykey}->{it.IsPrimarykey}] "; + } + if (it.IsNullable != old.IsNullable) + { + result += $" [Nullable:{old.IsNullable}->{it.IsNullable}] "; + } + if (it.IsIdentity != old.IsIdentity) + { + result += $" [Identity:{old.IsIdentity}->{it.IsIdentity}] "; + } + return result; + } + } + public class TableDifferenceInfo + { + public List DeleteColums { get; set; } = new List(); + public List UpdateColums { get; set; } = new List(); + public List AddColums { get; set; } = new List(); + public List UpdateRemark { get; set; } = new List(); + public bool IsDiff + { + get + { + return + (DeleteColums.Count > 0 || + UpdateColums.Count > 0 || + AddColums.Count > 0 || + UpdateRemark.Count > 0); + } + } + + public string TableName { get; set; } + } + + public class DiffColumsInfo + { + public string SqlTemplate { get; set; } + public string Message { get; set; } + } + + public class DiffTableInfo + { + public DbTableInfo OldTableInfo { get; set; } + public DbTableInfo NewTableInfo { get; set; } + public List OldColumnInfos { get; set; } + public List NewColumnInfos { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DbBindProvider/DbBindAccessory.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DbBindProvider/DbBindAccessory.cs new file mode 100644 index 000000000..3187c79e3 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DbBindProvider/DbBindAccessory.cs @@ -0,0 +1,446 @@ +using System.Data; +using System.Data.Common; +using System.Text; + +namespace SqlSugar +{ + public partial class DbBindAccessory + { + public QueryBuilder QueryBuilder { get; set; } + + protected List GetEntityList(SqlSugarProvider context, IDataReader dataReader) + { + Type type = typeof(T); + var entityInfo = context.EntityMaintenance.GetEntityInfo(type); + var isOwnsOne = entityInfo.Columns.Any(it => it.ForOwnsOnePropertyInfo != null); + string types = null; + var fieldNames = GetDataReaderNames(dataReader, ref types); + string cacheKey = GetCacheKey(type, fieldNames) + types; + var dataAfterFunc = context.CurrentConnectionConfig?.AopEvents?.DataExecuted; + IDataReaderEntityBuilder entytyList = context.Utilities.GetReflectionInoCacheInstance().GetOrCreate(cacheKey, () => + { + var cacheResult = new IDataReaderEntityBuilder(context, dataReader, fieldNames).CreateBuilder(type); + return cacheResult; + }); + List result = new List(); + try + { + if (dataReader == null) return result; + while (dataReader.Read()) + { + //try + //{ + var addItem = entytyList.Build(dataReader); + if (this.QueryBuilder?.QueryableFormats?.Count > 0) + { + FormatT(addItem); + } + result.Add(addItem); + //} + //catch (Exception ex) + //{ + // Check.Exception(true, ErrorMessage.EntityMappingErrorCompositeFormat, ex.Message); + //} + SetAppendColumns(dataReader); + SetOwnsOne(addItem, isOwnsOne, entityInfo, dataReader); + } + ExecuteDataAfterFun(context, dataAfterFunc, result); + } + catch (Exception ex) + { + if (ex.Message == "Common Language Runtime detected an invalid program.") + { + Check.Exception(true, ErrorMessage.EntityMappingErrorCompositeFormat, ex.Message); + } + else + { + throw; + } + } + return result; + } + + protected async Task> GetEntityListAsync(SqlSugarProvider context, IDataReader dataReader) + { + Type type = typeof(T); + var entityInfo = context.EntityMaintenance.GetEntityInfo(type); + var isOwnsOne = entityInfo.Columns.Any(it => it.ForOwnsOnePropertyInfo != null); + string types = null; + var fieldNames = GetDataReaderNames(dataReader, ref types); + string cacheKey = GetCacheKey(type, fieldNames) + types; + var dataAfterFunc = context.CurrentConnectionConfig?.AopEvents?.DataExecuted; + IDataReaderEntityBuilder entytyList = context.Utilities.GetReflectionInoCacheInstance().GetOrCreate(cacheKey, () => + { + var cacheResult = new IDataReaderEntityBuilder(context, dataReader, fieldNames).CreateBuilder(type); + return cacheResult; + }); + List result = new List(); + try + { + if (dataReader == null) return result; + while (await GetReadAsync(dataReader, context).ConfigureAwait(false)) + { + //try + //{ + var addItem = entytyList.Build(dataReader); + if (this.QueryBuilder?.QueryableFormats?.Count > 0) + { + FormatT(addItem); + } + result.Add(addItem); + //} + //catch (Exception ex) + //{ + // Check.Exception(true, ErrorMessage.EntityMappingErrorCompositeFormat, ex.Message); + //} + SetAppendColumns(dataReader); + SetOwnsOne(addItem, isOwnsOne, entityInfo, dataReader); + } + ExecuteDataAfterFun(context, dataAfterFunc, result); + } + catch (Exception ex) + { + if (ex.Message == "Common Language Runtime detected an invalid program.") + { + Check.Exception(true, ErrorMessage.EntityMappingErrorCompositeFormat, ex.Message); + } + else + { + throw; + } + } + return result; + } + + private Task GetReadAsync(IDataReader dataReader, SqlSugarProvider context) + { + if (this.QueryBuilder?.Builder?.SupportReadToken == true && context.Ado.CancellationToken != null) + { + return this.QueryBuilder.Builder.GetReaderByToken(dataReader, context.Ado.CancellationToken.Value); + } + else + { + return ((DbDataReader)dataReader).ReadAsync(); + } + } + + private void SetOwnsOne(object addItem, bool isOwnsOne, EntityInfo entityInfo, IDataReader dataReader) + { + if (isOwnsOne) + { + var ownsOneColumnsKv = entityInfo.Columns.Where(it => it.ForOwnsOnePropertyInfo != null) + .GroupBy(it => it.ForOwnsOnePropertyInfo).ToList(); + foreach (var kv in ownsOneColumnsKv) + { + var parentObj = kv.Key.GetValue(addItem); + if (parentObj == null) + { + parentObj = kv.Key.PropertyType.Assembly.CreateInstance(kv.Key.PropertyType.FullName); + kv.Key.SetValue(addItem, parentObj); + } + foreach (var item in kv.ToList()) + { + if (this.QueryBuilder?.SelectValue?.Equals("1") == true) + { + continue; + } + var itemIndex = dataReader.GetOrdinal(item.DbColumnName); + if (item.SqlParameterDbType is Type && item.UnderType.IsEnum && dataReader.GetValue(itemIndex) is string value) + { + item.PropertyInfo.SetValue(parentObj, UtilMethods.ChangeType2(value, item.PropertyInfo.PropertyType)); + } + else if (item.IsJson) + { + item.PropertyInfo.SetValue(parentObj, Newtonsoft.Json.JsonConvert.DeserializeObject(dataReader.GetValue(itemIndex)?.ToString(), item.PropertyInfo.PropertyType)); + } + else + { + var setValue = dataReader.GetValue(itemIndex); + if (setValue == DBNull.Value) + { + setValue = null; + } + if (item.UnderType == UtilConstants.GuidType && setValue is string) + { + if (setValue != null) + { + setValue = Guid.Parse(setValue + ""); + } + } + else if (item.UnderType?.IsEnum == true && setValue != null) + { + setValue = UtilMethods.ChangeType2(setValue, item.UnderType); + } + else if (UtilMethods.IsParameterConverter(item)) + { + setValue = UtilMethods.QueryConverter(itemIndex, null, dataReader, entityInfo, item); + } + item.PropertyInfo.SetValue(parentObj, setValue); + } + } + } + } + } + + private void FormatT(T addItem) + { + var formats = this.QueryBuilder.QueryableFormats; + var columns = this.QueryBuilder.Context.EntityMaintenance.GetEntityInfoWithAttr(typeof(T)) + .Columns.Where(it => formats.Any(y => y.PropertyName == it.PropertyName)).ToList(); + if (columns.Count != 0) + { + foreach (var item in formats) + { + var columnInfo = columns.FirstOrDefault(it => it.PropertyName == item.PropertyName); + var value = columnInfo.PropertyInfo.GetValue(addItem); + value = UtilMethods.GetFormatValue(value, item); + columnInfo.PropertyInfo.SetValue(addItem, value); + } + } + } + + private static void ExecuteDataAfterFun(SqlSugarProvider context, Action dataAfterFunc, List result) + { + if (dataAfterFunc != null) + { + ((AdoProvider)context.Ado).AfterTime = DateTime.Now; + var entity = context.EntityMaintenance.GetEntityInfo(); + foreach (var item in result) + { + dataAfterFunc(item, new DataAfterModel() + { + EntityColumnInfos = entity.Columns, + Entity = entity, + EntityValue = item + }); + } + } + } + + private string GetCacheKey(Type type, List keys) + { + StringBuilder sb = new StringBuilder("DataReaderToList."); + sb.Append(type.FullName); + sb.Append('.'); + foreach (var item in keys) + { + sb.Append(item); + } + return sb.ToString(); + } + + private void SetAppendColumns(IDataReader dataReader) + { + if (QueryBuilder?.AppendColumns != null && QueryBuilder.AppendColumns.Count != 0) + { + if (QueryBuilder.AppendValues == null) + QueryBuilder.AppendValues = new List>(); + List addItems = new List(); + foreach (var item in QueryBuilder.AppendColumns) + { + var vi = dataReader.GetOrdinal(item.AsName); + var value = dataReader.GetValue(vi); + addItems.Add(new QueryableAppendColumn() + { + Name = item.Name, + AsName = item.AsName, + Value = value + }); + } + QueryBuilder.AppendValues.Add(addItems); + } + if (QueryBuilder?.AppendNavInfo != null) + { + var navResult = new AppendNavResult(); + foreach (var item in QueryBuilder?.AppendNavInfo.AppendProperties) + { + var vi = dataReader.GetOrdinal("SugarNav_" + item.Key); + var value = dataReader.GetValue(vi); + navResult.result.Add("SugarNav_" + item.Key, value); + } + QueryBuilder?.AppendNavInfo.Result.Add(navResult); + } + } + + private List GetDataReaderNames(IDataReader dataReader, ref string types) + { + List keys = new List(); + StringBuilder sbTypes = new StringBuilder(); + var count = dataReader.FieldCount; + for (int i = 0; i < count; i++) + { + keys.Add(dataReader.GetName(i)); + var type = dataReader.GetFieldType(i); + if (type != null) + { + sbTypes.Append(type.Name.AsSpan(0, 2)); + } + } + types = sbTypes.ToString(); + if (this.QueryBuilder?.Context?.Ado is AdoProvider adoProvider) + { + if (adoProvider.IsNoSql) + { + types = "NoSql"; + } + } + return keys; + } + + protected List GetKeyValueList(Type type, IDataReader dataReader) + { + List result = new List(); + while (dataReader.Read()) + { + GetKeyValueList(type, dataReader, result); + } + return result; + } + + protected async Task> GetKeyValueListAsync(Type type, IDataReader dataReader) + { + List result = new List(); + while (await ((DbDataReader)dataReader).ReadAsync().ConfigureAwait(false)) + { + GetKeyValueList(type, dataReader, result); + } + return result; + } + + private static void GetKeyValueList(Type type, IDataReader dataReader, List result) + { + if (UtilConstants.DicOO == type) + { + var kv = new KeyValuePair(dataReader.GetValue(0), dataReader.GetValue(1)); + result.Add((T)Convert.ChangeType(kv, typeof(KeyValuePair))); + } + else if (UtilConstants.DicIS == type) + { + var kv = new KeyValuePair(dataReader.GetValue(0).ObjToInt(), dataReader.GetValue(1).ObjToString()); + result.Add((T)Convert.ChangeType(kv, typeof(KeyValuePair))); + } + else if (UtilConstants.Dicii == type) + { + var kv = new KeyValuePair(dataReader.GetValue(0).ObjToInt(), dataReader.GetValue(1).ObjToInt()); + result.Add((T)Convert.ChangeType(kv, typeof(KeyValuePair))); + } + else if (UtilConstants.DicSi == type) + { + var kv = new KeyValuePair(dataReader.GetValue(0).ObjToString(), dataReader.GetValue(1).ObjToInt()); + result.Add((T)Convert.ChangeType(kv, typeof(KeyValuePair))); + } + else if (UtilConstants.DicSo == type) + { + var kv = new KeyValuePair(dataReader.GetValue(0).ObjToString(), dataReader.GetValue(1)); + result.Add((T)Convert.ChangeType(kv, typeof(KeyValuePair))); + } + else if (UtilConstants.DicSS == type) + { + var kv = new KeyValuePair(dataReader.GetValue(0).ObjToString(), dataReader.GetValue(1).ObjToString()); + result.Add((T)Convert.ChangeType(kv, typeof(KeyValuePair))); + } + else + { + Check.Exception(true, ErrorMessage.NotSupportedDictionaryCompositeFormat); + } + } + + + protected async Task> GetArrayListAsync(Type type, IDataReader dataReader) + { + List result = new List(); + int count = dataReader.FieldCount; + var childType = type.GetElementType(); + while (await ((DbDataReader)dataReader).ReadAsync().ConfigureAwait(false)) + { + GetArrayList(type, dataReader, result, count, childType); + } + return result; + } + + protected List GetArrayList(Type type, IDataReader dataReader) + { + List result = new List(); + int count = dataReader.FieldCount; + var childType = type.GetElementType(); + while (dataReader.Read()) + { + GetArrayList(type, dataReader, result, count, childType); + } + return result; + } + + + + private static void GetArrayList(Type type, IDataReader dataReader, List result, int count, Type childType) + { + object[] array = new object[count]; + for (int i = 0; i < count; i++) + { + array[i] = Convert.ChangeType(dataReader.GetValue(i), childType); + } + if (childType == UtilConstants.StringType) + result.Add((T)Convert.ChangeType(array.Select(it => it.ObjToString()).ToArray(), type)); + else if (childType == UtilConstants.ObjType) + result.Add((T)Convert.ChangeType(array.Select(it => it == DBNull.Value ? null : (object)it).ToArray(), type)); + else if (childType == UtilConstants.BoolType) + result.Add((T)Convert.ChangeType(array.Select(it => it.ObjToBool()).ToArray(), type)); + else if (childType == UtilConstants.ByteType) + result.Add((T)Convert.ChangeType(array.Select(it => it == DBNull.Value ? 0 : (byte)it).ToArray(), type)); + else if (childType == UtilConstants.DecType) + result.Add((T)Convert.ChangeType(array.Select(it => it.ObjToDecimal()).ToArray(), type)); + else if (childType == UtilConstants.GuidType) + result.Add((T)Convert.ChangeType(array.Select(it => it == DBNull.Value ? Guid.Empty : (Guid)it).ToArray(), type)); + else if (childType == UtilConstants.DateType) + result.Add((T)Convert.ChangeType(array.Select(it => it == DBNull.Value ? DateTime.MinValue : (DateTime)it).ToArray(), type)); + else if (childType == UtilConstants.IntType) + result.Add((T)Convert.ChangeType(array.Select(it => it.ObjToInt()).ToArray(), type)); + else + Check.Exception(true, ErrorMessage.NotSupportedArrayCompositeFormat); + } + + protected List GetValueTypeList(Type type, IDataReader dataReader) + { + List result = new List(); + while (dataReader.Read()) + { + GetValueTypeList(type, dataReader, result); + } + return result; + } + protected async Task> GetValueTypeListAsync(Type type, IDataReader dataReader) + { + List result = new List(); + while (await ((DbDataReader)dataReader).ReadAsync().ConfigureAwait(false)) + { + GetValueTypeList(type, dataReader, result); + } + return result; + } + + private static void GetValueTypeList(Type type, IDataReader dataReader, List result) + { + var value = dataReader.GetValue(0); + if (type == UtilConstants.GuidType) + { + value = Guid.Parse(value.ToString()); + } + if (value == DBNull.Value) + { + result.Add(default(T)); + } + else if (type.IsEnum) + { + result.Add((T)Enum.Parse(type, value.ObjToString())); + } + else if (value != null && UtilMethods.GetUnderType(type).IsEnum) + { + result.Add((T)Enum.Parse(UtilMethods.GetUnderType(type), value.ObjToString())); + } + else + { + result.Add((T)UtilMethods.ChangeType2(value, type)); + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DbBindProvider/DbBindProvider.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DbBindProvider/DbBindProvider.cs new file mode 100644 index 000000000..c36e679ea --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DbBindProvider/DbBindProvider.cs @@ -0,0 +1,376 @@ +using System.Collections; +using System.Data; +using System.Reflection; + +namespace SqlSugar +{ + public abstract partial class DbBindProvider : DbBindAccessory, IDbBind + { + #region Properties + public virtual SqlSugarProvider Context { get; set; } + public abstract List> MappingTypes { get; } + #endregion + + #region Public methods + public virtual string GetDbTypeName(string csharpTypeName) + { + if (csharpTypeName == UtilConstants.ByteArrayType.Name) + return "varbinary"; + if (csharpTypeName.Equals("int32", StringComparison.CurrentCultureIgnoreCase)) + csharpTypeName = "int"; + if (csharpTypeName.Equals("int16", StringComparison.CurrentCultureIgnoreCase)) + csharpTypeName = "short"; + if (csharpTypeName.Equals("int64", StringComparison.CurrentCultureIgnoreCase)) + csharpTypeName = "long"; + if (csharpTypeName.ToLower().IsIn("boolean", "bool")) + csharpTypeName = "bool"; + if (csharpTypeName == "DateTimeOffset") + csharpTypeName = "DateTime"; + var mappings = this.MappingTypes.Where(it => it.Value.ToString().Equals(csharpTypeName, StringComparison.CurrentCultureIgnoreCase)).ToList(); + if (mappings?.Count > 0) + return mappings.First().Key; + else + return "varchar"; + } + public string GetCsharpTypeName(string dbTypeName) + { + var mappings = this.MappingTypes.Where(it => it.Key == dbTypeName); + return mappings.HasValue() ? mappings.First().Key : "string"; + } + + public string GetCsharpTypeNameByDbTypeName(string dbTypeName) + { + var mappings = this.MappingTypes.Where(it => it.Key == dbTypeName); + if (mappings?.Any() != true) + { + return "string"; + } + var result = mappings.First().Value.ObjToString(); + return result; + } + public virtual string GetConvertString(string dbTypeName) + { + string result = string.Empty; + switch (dbTypeName.ToLower()) + { + #region Int + case "int": + result = "Convert.ToInt32"; + break; + #endregion + + #region String + case "nchar": + case "char": + case "ntext": + case "nvarchar": + case "varchar": + case "text": + result = "Convert.ToString"; + break; + #endregion + + #region Long + case "bigint": + result = "Convert.ToInt64"; + break; + #endregion + + #region Bool + case "bit": + result = "Convert.ToBoolean"; + break; + + #endregion + + #region Datetime + case "timestamp": + case "smalldatetime": + case "datetime": + case "date": + case "datetime2": + result = "Convert.ToDateTime"; + break; + #endregion + + #region Decimal + case "smallmoney": + case "single": + case "numeric": + case "money": + case "decimal": + result = "Convert.ToDecimal"; + break; + #endregion + + #region Double + case "float": + result = "Convert.ToSingle"; + break; + case "double": + result = "Convert.ToDouble"; + break; + #endregion + + #region Byte[] + case "varbinary": + case "binary": + case "image": + result = "byte[]"; + break; + #endregion + + #region Float + case "real": + result = "Convert.ToSingle"; + break; + #endregion + + #region Short + case "smallint": + result = "Convert.ToInt16"; + break; + #endregion + + #region Byte + case "tinyint": + result = "Convert.ToByte"; + break; + + #endregion + + #region Guid + case "uniqueidentifier": + result = "Guid.Parse"; + break; + #endregion + + #region Null + default: + result = null; + break; + #endregion + } + return result; + } + public virtual string GetPropertyTypeName(string dbTypeName) + { + dbTypeName = dbTypeName.ToLower(); + var propertyTypes = MappingTypes.Where(it => it.Key.Equals(dbTypeName, StringComparison.CurrentCultureIgnoreCase)); + if (dbTypeName == "int32") + { + return "int"; + } + else if (dbTypeName == "int64") + { + return "long"; + } + else if (dbTypeName == "int16") + { + return "short"; + } + else if (propertyTypes == null) + { + return "other"; + } + else if (dbTypeName.IsContainsIn("xml", "string", "String")) + { + return "string"; + } + else if (dbTypeName.IsContainsIn("boolean", "bool")) + { + return "bool"; + } + else if (propertyTypes?.Any() != true) + { + return "object"; + } + else if (propertyTypes.First().Value == CSharpDataType.byteArray) + { + return "byte[]"; + } + else + { + return propertyTypes.First().Value.ToString(); + } + } + public virtual List DataReaderToList(Type type, IDataReader dataReader) + { + using (dataReader) + { + if (type.Name.StartsWith("KeyValuePair")) + { + return GetKeyValueList(type, dataReader); + } + else if (type.IsValueType() || type == UtilConstants.StringType || type == UtilConstants.ByteArrayType) + { + return GetValueTypeList(type, dataReader); + } + else if (type.IsArray) + { + return GetArrayList(type, dataReader); + } + else if (typeof(T) != type && typeof(T).IsInterface) + { + //这里是为了解决返回类型是接口的问题 + return GetEntityListByType(type, Context, dataReader); + } + else + { + return GetEntityList(Context, dataReader); + } + } + } + + public virtual async Task> DataReaderToListAsync(Type type, IDataReader dataReader) + { + using (dataReader) + { + if (type.Name.StartsWith("KeyValuePair")) + { + return await GetKeyValueListAsync(type, dataReader).ConfigureAwait(false); + } + else if (type.IsValueType() || type == UtilConstants.StringType || type == UtilConstants.ByteArrayType) + { + return await GetValueTypeListAsync(type, dataReader).ConfigureAwait(false); + } + else if (type.IsArray) + { + return await GetArrayListAsync(type, dataReader).ConfigureAwait(false); + } + else if (typeof(T) != type && typeof(T).IsInterface) + { + //这里是为了解决返回类型是接口的问题 + return await GetEntityListByTypeAsync(type, Context, dataReader).ConfigureAwait(false); + } + else + { + return await GetEntityListAsync(Context, dataReader).ConfigureAwait(false); + } + } + } + public virtual List DataReaderToListNoUsing(Type type, IDataReader dataReader) + { + if (type.Name.StartsWith("KeyValuePair")) + { + return GetKeyValueList(type, dataReader); + } + else if (type.IsValueType() || type == UtilConstants.StringType || type == UtilConstants.ByteArrayType) + { + return GetValueTypeList(type, dataReader); + } + else if (type.IsArray) + { + return GetArrayList(type, dataReader); + } + else + { + return GetEntityList(Context, dataReader); + } + } + public virtual Task> DataReaderToListNoUsingAsync(Type type, IDataReader dataReader) + { + if (type.Name.StartsWith("KeyValuePair")) + { + return GetKeyValueListAsync(type, dataReader); + } + else if (type.IsValueType() || type == UtilConstants.StringType || type == UtilConstants.ByteArrayType) + { + return GetValueTypeListAsync(type, dataReader); + } + else if (type.IsArray) + { + return GetArrayListAsync(type, dataReader); + } + else + { + return GetEntityListAsync(Context, dataReader); + } + } + public virtual List GetEntityListByType(Type entityType, SqlSugarProvider context, IDataReader dataReader) + { + var method = typeof(DbBindProvider).GetMethod("GetEntityList", BindingFlags.Instance | BindingFlags.NonPublic); + var genericMethod = method.MakeGenericMethod(entityType); + var objectValue = genericMethod.Invoke(this, new object[] { context, dataReader }); + List result = new List(); + foreach (var item in objectValue as IEnumerable) + { + result.Add((T)item); + } + return result; + } + public virtual async Task> GetEntityListByTypeAsync(Type entityType, SqlSugarProvider context, IDataReader dataReader) + { + var method = typeof(DbBindProvider).GetMethod("GetEntityListAsync", BindingFlags.Instance | BindingFlags.NonPublic); + var genericMethod = method.MakeGenericMethod(entityType); + Task task = (Task)genericMethod.Invoke(this, new object[] { context, dataReader }); + return await GetTask(task).ConfigureAwait(false); + } + private static async Task> GetTask(Task task) + { + await task.ConfigureAwait(false); // 等待任务完成 + var resultProperty = task.GetType().GetProperty("Result"); + var value = resultProperty.GetValue(task); + List result = new List(); + foreach (var item in value as IEnumerable) + { + result.Add((T)item); + } + return (List)result; + } + #endregion + + #region Throw rule + + public virtual List IntThrow + { + get + { + return new List() { "datetime", "byte" }; + } + } + public virtual List ShortThrow + { + get + { + return new List() { "datetime", "guid" }; + } + } + public virtual List DecimalThrow + { + get + { + return new List() { "datetime", "byte", "guid" }; + } + } + public virtual List DoubleThrow + { + get + { + return new List() { "datetime", "byte", "guid" }; + } + } + public virtual List DateThrow + { + get + { + return new List() { "int32", "decimal", "double", "byte", "guid" }; + } + } + public virtual List GuidThrow + { + get + { + return new List() { "int32", "datetime", "decimal", "double", "byte" }; + } + } + public virtual List StringThrow + { + get + { + return new List() { "int32", "datetime", "decimal", "double", "byte", "int64", "uint32", "uint64" }; + } + } + #endregion + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DbBindProvider/IDataReaderEntityBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DbBindProvider/IDataReaderEntityBuilder.cs new file mode 100644 index 000000000..4cb147f1d --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DbBindProvider/IDataReaderEntityBuilder.cs @@ -0,0 +1,511 @@ +using System.Data; +using System.Reflection; +using System.Reflection.Emit; +using System.Xml.Linq; + +namespace SqlSugar +{ + /// + /// ** description:IDataReader Entity Builder + /// ** author:sunkaixuan + /// ** date:2017/4/2 + /// ** qq:610262374 + /// + public partial class IDataReaderEntityBuilder + { + #region Properies + private List ReaderKeys { get; set; } + #endregion + + #region Fields + private SqlSugarProvider Context = null; + private IDataReaderEntityBuilder DynamicBuilder; + private IDataRecord DataRecord; + private static readonly MethodInfo isDBNullMethod = typeof(IDataRecord).GetMethod("IsDBNull", new Type[] { typeof(int) }); + private static readonly MethodInfo getBoolean = typeof(IDataRecord).GetMethod("GetBoolean", new Type[] { typeof(int) }); + private static readonly MethodInfo getByte = typeof(IDataRecord).GetMethod("GetByte", new Type[] { typeof(int) }); + private static readonly MethodInfo getDateTime = typeof(IDataRecord).GetMethod("GetDateTime", new Type[] { typeof(int) }); + private static readonly MethodInfo getDecimal = typeof(IDataRecord).GetMethod("GetDecimal", new Type[] { typeof(int) }); + private static readonly MethodInfo getDouble = typeof(IDataRecord).GetMethod("GetDouble", new Type[] { typeof(int) }); + private static readonly MethodInfo getFloat = typeof(IDataRecord).GetMethod("GetFloat", new Type[] { typeof(int) }); + private static readonly MethodInfo getGuid = typeof(IDataRecord).GetMethod("GetGuid", new Type[] { typeof(int) }); + private static readonly MethodInfo getInt16 = typeof(IDataRecord).GetMethod("GetInt16", new Type[] { typeof(int) }); + private static readonly MethodInfo getInt32 = typeof(IDataRecord).GetMethod("GetInt32", new Type[] { typeof(int) }); + private static readonly MethodInfo getInt64 = typeof(IDataRecord).GetMethod("GetInt64", new Type[] { typeof(int) }); + private static readonly MethodInfo getString = typeof(IDataRecord).GetMethod("GetString", new Type[] { typeof(int) }); + //private static readonly MethodInfo getConvertValueMethod = typeof(IDataRecordExtensions).GetMethod("GetConvertValue"); + private static readonly MethodInfo getdatetimeoffset = typeof(IDataRecordExtensions).GetMethod("Getdatetimeoffset"); + private static readonly MethodInfo getdatetimeoffsetDate = typeof(IDataRecordExtensions).GetMethod("GetdatetimeoffsetDate"); + private static readonly MethodInfo getStringGuid = typeof(IDataRecordExtensions).GetMethod("GetStringGuid"); + private static readonly MethodInfo getXelement = typeof(IDataRecordExtensions).GetMethod("GetXelement"); + private static readonly MethodInfo getConvertStringGuid = typeof(IDataRecordExtensions).GetMethod("GetConvertStringGuid"); + private static readonly MethodInfo getEnum = typeof(IDataRecordExtensions).GetMethod("GetEnum"); + private static readonly MethodInfo getConvertString = typeof(IDataRecordExtensions).GetMethod("GetConvertString"); + private static readonly MethodInfo getConvertFloat = typeof(IDataRecordExtensions).GetMethod("GetConvertFloat"); + private static readonly MethodInfo getConvertBoolean = typeof(IDataRecordExtensions).GetMethod("GetConvertBoolean"); + private static readonly MethodInfo getConvertByte = typeof(IDataRecordExtensions).GetMethod("GetConvertByte"); + private static readonly MethodInfo getConvertChar = typeof(IDataRecordExtensions).GetMethod("GetConvertChar"); + private static readonly MethodInfo getConvertDateTime = typeof(IDataRecordExtensions).GetMethod("GetConvertDateTime"); + private static readonly MethodInfo getConvertTime = typeof(IDataRecordExtensions).GetMethod("GetConvertTime"); + private static readonly MethodInfo getTime = typeof(IDataRecordExtensions).GetMethod("GetTime"); + private static readonly MethodInfo getConvertDecimal = typeof(IDataRecordExtensions).GetMethod("GetConvertDecimal"); + private static readonly MethodInfo getConvertDouble = typeof(IDataRecordExtensions).GetMethod("GetConvertDouble"); + private static readonly MethodInfo getConvertDoubleToFloat = typeof(IDataRecordExtensions).GetMethod("GetConvertDoubleToFloat"); + private static readonly MethodInfo getConvertGuid = typeof(IDataRecordExtensions).GetMethod("GetConvertGuid"); + private static readonly MethodInfo getConvertInt16 = typeof(IDataRecordExtensions).GetMethod("GetConvertInt16"); + private static readonly MethodInfo getConvertInt32 = typeof(IDataRecordExtensions).GetMethod("GetConvertInt32"); + private static readonly MethodInfo getConvertInt64 = typeof(IDataRecordExtensions).GetMethod("GetConvetInt64"); + private static readonly MethodInfo getConvertEnum_Null = typeof(IDataRecordExtensions).GetMethod("GetConvertEnum_Null"); + private static readonly MethodInfo getConvertdatetimeoffset = typeof(IDataRecordExtensions).GetMethod("GetConvertdatetimeoffset"); + private static readonly MethodInfo getConvertdatetimeoffsetDate = typeof(IDataRecordExtensions).GetMethod("GetConvertdatetimeoffsetDate"); + private static readonly MethodInfo getOtherNull = typeof(IDataRecordExtensions).GetMethod("GetOtherNull"); + private static readonly MethodInfo getOther = typeof(IDataRecordExtensions).GetMethod("GetOther"); + private static readonly MethodInfo getJson = typeof(IDataRecordExtensions).GetMethod("GetJson"); + private static readonly MethodInfo getArray = typeof(IDataRecordExtensions).GetMethod("GetArray"); + private static readonly MethodInfo getEntity = typeof(IDataRecordExtensions).GetMethod("GetEntity", new Type[] { typeof(SqlSugarProvider) }); + private static readonly MethodInfo getMyIntNull = typeof(IDataRecordExtensions).GetMethod("GetMyIntNull"); + private static readonly MethodInfo getMyInt = typeof(IDataRecordExtensions).GetMethod("GetMyInt"); + + private delegate T Load(IDataRecord dataRecord); + private Load handler; + #endregion + + #region Constructor + private IDataReaderEntityBuilder() + { + + } + + public IDataReaderEntityBuilder(SqlSugarProvider context, IDataRecord dataRecord, List fieldNames) + { + this.Context = context; + this.DataRecord = dataRecord; + this.DynamicBuilder = new IDataReaderEntityBuilder(); + this.ReaderKeys = fieldNames; + } + #endregion + + #region Public methods + public T Build(IDataRecord dataRecord) + { + return handler(dataRecord); + } + + public IDataReaderEntityBuilder CreateBuilder(Type type) + { + DynamicMethod method = new DynamicMethod("SqlSugarEntity", type, + new Type[] { typeof(IDataRecord) }, type, true); + ILGenerator generator = method.GetILGenerator(); + LocalBuilder result = generator.DeclareLocal(type); + generator.Emit(OpCodes.Newobj, type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, + null, Type.EmptyTypes, null)); + generator.Emit(OpCodes.Stloc, result); + this.Context.InitMappingInfo(type); + var columnInfos = this.Context.EntityMaintenance.GetEntityInfoWithAttr(type).Columns; + foreach (var columnInfo in columnInfos) + { + string fileName = columnInfo.DbColumnName ?? columnInfo.PropertyName; + if (columnInfo.IsIgnore && !this.ReaderKeys.Any(it => it.Equals(fileName, StringComparison.CurrentCultureIgnoreCase))) + { + continue; + } + else if (columnInfo.ForOwnsOnePropertyInfo != null) + { + continue; + } + if (columnInfo?.PropertyInfo.GetSetMethod(true) != null) + { + var isGemo = columnInfo.PropertyInfo?.PropertyType?.FullName == "NetTopologySuite.Geometries.Geometry"; + if (isGemo == false && columnInfo.PropertyInfo?.PropertyType?.FullName == "Kdbndp.LegacyPostgis.PostgisGeometry") + { + isGemo = true; + } + if (!isGemo && columnInfo.PropertyInfo.PropertyType.IsClass() && columnInfo.PropertyInfo.PropertyType != UtilConstants.ByteArrayType && columnInfo.PropertyInfo.PropertyType != UtilConstants.ObjType) + { + if (this.ReaderKeys.Any(it => it.Equals(fileName, StringComparison.CurrentCultureIgnoreCase))) + { + BindClass(generator, result, columnInfo, ReaderKeys.First(it => it.Equals(fileName, StringComparison.CurrentCultureIgnoreCase))); + } + else if (this.ReaderKeys.Any(it => it.Equals(columnInfo.PropertyName, StringComparison.CurrentCultureIgnoreCase))) + { + BindClass(generator, result, columnInfo, ReaderKeys.First(it => it.Equals(columnInfo.PropertyName, StringComparison.CurrentCultureIgnoreCase))); + } + } + else if (!isGemo && columnInfo.IsJson && columnInfo.PropertyInfo.PropertyType != UtilConstants.StringType) + { //json is struct + if (this.ReaderKeys.Any(it => it.Equals(fileName, StringComparison.CurrentCultureIgnoreCase))) + { + BindClass(generator, result, columnInfo, ReaderKeys.First(it => it.Equals(fileName, StringComparison.CurrentCultureIgnoreCase))); + } + } + else + { + if (this.ReaderKeys.Any(it => it.Equals(fileName, StringComparison.CurrentCultureIgnoreCase))) + { + BindField(generator, result, columnInfo, ReaderKeys.First(it => it.Equals(fileName, StringComparison.CurrentCultureIgnoreCase))); + } + else if (this.ReaderKeys.Any(it => it.Equals(columnInfo.PropertyName, StringComparison.CurrentCultureIgnoreCase))) + { + BindField(generator, result, columnInfo, ReaderKeys.First(it => it.Equals(columnInfo.PropertyName, StringComparison.CurrentCultureIgnoreCase))); + } + } + } + } + generator.Emit(OpCodes.Ldloc, result); + generator.Emit(OpCodes.Ret); + DynamicBuilder.handler = (Load)method.CreateDelegate(typeof(Load)); + return DynamicBuilder; + } + + #endregion + + #region Private methods + private void BindCustomFunc(ILGenerator generator, LocalBuilder result, EntityColumnInfo columnInfo, string fieldName) + { + int i = DataRecord.GetOrdinal(fieldName); + Label endIfLabel = generator.DefineLabel(); + generator.Emit(OpCodes.Ldarg_0); + generator.Emit(OpCodes.Ldc_I4, i); + generator.Emit(OpCodes.Callvirt, isDBNullMethod); + generator.Emit(OpCodes.Brtrue, endIfLabel); + generator.Emit(OpCodes.Ldloc, result); + //generator.Emit(OpCodes.Ldarg_0); + //generator.Emit(OpCodes.Ldc_I4, i); + var method = (columnInfo.SqlParameterDbType as Type).GetMethod("QueryConverter"); + method = method.MakeGenericMethod(new Type[] { columnInfo.PropertyInfo.PropertyType }); + Type type = (columnInfo.SqlParameterDbType as Type); + //ConstructorInfo info = type.GetConstructor(Type.EmptyTypes); + //il.Emit(OpCodes.Newobj, info); + generator.Emit(OpCodes.Newobj, type.GetConstructor(Type.EmptyTypes)); + generator.Emit(OpCodes.Ldarg_0); + generator.Emit(OpCodes.Ldc_I4, i); + //method = (columnInfo.SqlParameterDbType as Type).GetMethod("QueryConverter"); + //method = method.MakeGenericMethod(new Type[] { columnInfo.PropertyInfo.PropertyType }); + if (method.IsVirtual) + generator.Emit(OpCodes.Callvirt, method); + else + generator.Emit(OpCodes.Call, method); + generator.Emit(OpCodes.Callvirt, columnInfo.PropertyInfo.GetSetMethod(true)); + generator.MarkLabel(endIfLabel); + } + private void BindClass(ILGenerator generator, LocalBuilder result, EntityColumnInfo columnInfo, string fieldName) + { + + if (columnInfo.SqlParameterDbType is Type) + { + BindCustomFunc(generator, result, columnInfo, fieldName); + return; + } + + if (columnInfo.IsJson) + { + MethodInfo jsonMethod = getJson.MakeGenericMethod(columnInfo.PropertyInfo.PropertyType); + int i = DataRecord.GetOrdinal(fieldName); + Label endIfLabel = generator.DefineLabel(); + generator.Emit(OpCodes.Ldarg_0); + generator.Emit(OpCodes.Ldc_I4, i); + generator.Emit(OpCodes.Callvirt, isDBNullMethod); + generator.Emit(OpCodes.Brtrue, endIfLabel); + generator.Emit(OpCodes.Ldloc, result); + generator.Emit(OpCodes.Ldarg_0); + generator.Emit(OpCodes.Ldc_I4, i); + generator.Emit(OpCodes.Call, jsonMethod); + generator.Emit(OpCodes.Callvirt, columnInfo.PropertyInfo.GetSetMethod(true)); + generator.MarkLabel(endIfLabel); + } + if (columnInfo.IsArray) + { + MethodInfo arrayMehtod = getArray.MakeGenericMethod(columnInfo.PropertyInfo.PropertyType); + int i = DataRecord.GetOrdinal(fieldName); + Label endIfLabel = generator.DefineLabel(); + generator.Emit(OpCodes.Ldarg_0); + generator.Emit(OpCodes.Ldc_I4, i); + generator.Emit(OpCodes.Callvirt, isDBNullMethod); + generator.Emit(OpCodes.Brtrue, endIfLabel); + generator.Emit(OpCodes.Ldloc, result); + generator.Emit(OpCodes.Ldarg_0); + generator.Emit(OpCodes.Ldc_I4, i); + generator.Emit(OpCodes.Call, arrayMehtod); + generator.Emit(OpCodes.Callvirt, columnInfo.PropertyInfo.GetSetMethod(true)); + generator.MarkLabel(endIfLabel); + } + else if (columnInfo.UnderType == typeof(XElement)) + { + int i = DataRecord.GetOrdinal(fieldName); + Label endIfLabel = generator.DefineLabel(); + generator.Emit(OpCodes.Ldarg_0); + generator.Emit(OpCodes.Ldc_I4, i); + generator.Emit(OpCodes.Callvirt, isDBNullMethod); + generator.Emit(OpCodes.Brtrue, endIfLabel); + generator.Emit(OpCodes.Ldloc, result); + generator.Emit(OpCodes.Ldarg_0); + generator.Emit(OpCodes.Ldc_I4, i); + BindMethod(generator, columnInfo, i); + generator.Emit(OpCodes.Callvirt, columnInfo.PropertyInfo.GetSetMethod(true)); + generator.MarkLabel(endIfLabel); + } + } + private void BindField(ILGenerator generator, LocalBuilder result, EntityColumnInfo columnInfo, string fieldName) + { + if (columnInfo.SqlParameterDbType is Type) + { + BindCustomFunc(generator, result, columnInfo, fieldName); + return; + } + int i = DataRecord.GetOrdinal(fieldName); + Label endIfLabel = generator.DefineLabel(); + + //2023-3-8 + Label tryStart = generator.BeginExceptionBlock();//begin try + //2023-3-8 + + generator.Emit(OpCodes.Ldarg_0); + generator.Emit(OpCodes.Ldc_I4, i); + generator.Emit(OpCodes.Callvirt, isDBNullMethod); + generator.Emit(OpCodes.Brtrue, endIfLabel); + generator.Emit(OpCodes.Ldloc, result); + generator.Emit(OpCodes.Ldarg_0); + generator.Emit(OpCodes.Ldc_I4, i); + BindMethod(generator, columnInfo, i); + generator.Emit(OpCodes.Callvirt, columnInfo.PropertyInfo.GetSetMethod(true)); + generator.MarkLabel(endIfLabel); + + //2023-3-8 + generator.Emit(OpCodes.Leave, tryStart);//eng try + generator.BeginCatchBlock(typeof(Exception));//begin catch + generator.Emit(OpCodes.Ldstr, ErrorMessage.GetThrowMessage($"{columnInfo.EntityName} {columnInfo.PropertyName} bind error", $"{columnInfo.PropertyName}绑定到{columnInfo.EntityName}失败,可以试着换一个类型,或者使用ORM自定义类型实现"));//thow message + generator.Emit(OpCodes.Newobj, typeof(Exception).GetConstructor(new Type[] { typeof(string) })); + generator.Emit(OpCodes.Throw); + generator.EndExceptionBlock(); + //2023-3-8 + } + private void BindMethod(ILGenerator generator, EntityColumnInfo columnInfo, int ordinal) + { + IDbBind bind = Context.Ado.DbBind; + bool isNullableType = false; + MethodInfo method = null; + Type bindPropertyType = UtilMethods.GetUnderType(columnInfo.PropertyInfo, ref isNullableType); + string dbTypeName = UtilMethods.GetParenthesesValue(DataRecord.GetDataTypeName(ordinal)); + if (dbTypeName.IsNullOrEmpty()) + { + dbTypeName = bindPropertyType.Name; + } + string propertyName = columnInfo.PropertyName; + string validPropertyName = bind.GetPropertyTypeName(dbTypeName); + validPropertyName = validPropertyName == "byte[]" ? "byteArray" : validPropertyName; + CSharpDataType validPropertyType = (CSharpDataType)Enum.Parse(typeof(CSharpDataType), validPropertyName); + + #region NoSql + if (this.Context.Ado is AdoProvider provider) + { + if (provider.IsNoSql) + { + method = isNullableType ? getOtherNull.MakeGenericMethod(bindPropertyType) : getOther.MakeGenericMethod(bindPropertyType); + if (method.IsVirtual) + generator.Emit(OpCodes.Callvirt, method); + else + generator.Emit(OpCodes.Call, method); + return; + } + } + #endregion + + #region Sqlite Logic + if (this.Context.CurrentConnectionConfig.DbType == DbType.Sqlite) + { + if (bindPropertyType.IsEnum()) + { + method = isNullableType ? getConvertEnum_Null.MakeGenericMethod(bindPropertyType) : getEnum.MakeGenericMethod(bindPropertyType); + } + else if (bindPropertyType == UtilConstants.IntType) + { + method = isNullableType ? getConvertInt32 : getInt32; + } + else if (bindPropertyType == UtilConstants.DateTimeOffsetType && SugarCompatible.IsFramework) + { + method = isNullableType ? getConvertdatetimeoffset : getdatetimeoffset; + } + else if (bindPropertyType == UtilConstants.ByteType) + { + method = isNullableType ? getConvertByte : getByte; + } + else if (bindPropertyType == UtilConstants.StringType && dbTypeName?.ToLower() == "timestamp") + { + method = isNullableType ? getOtherNull.MakeGenericMethod(bindPropertyType) : getOther.MakeGenericMethod(bindPropertyType); + } + else if (dbTypeName.EqualCase("STRING")) + { + method = isNullableType ? getOtherNull.MakeGenericMethod(bindPropertyType) : getOther.MakeGenericMethod(bindPropertyType); + } + else if (bindPropertyType == UtilConstants.StringType && validPropertyName == "int") + { + method = isNullableType ? getOtherNull.MakeGenericMethod(bindPropertyType) : getOther.MakeGenericMethod(bindPropertyType); + } + else if (bindPropertyType == UtilConstants.StringType) + { + method = getString; + } + else + { + method = isNullableType ? getOtherNull.MakeGenericMethod(bindPropertyType) : getOther.MakeGenericMethod(bindPropertyType); + } + if (method.IsVirtual) + generator.Emit(OpCodes.Callvirt, method); + else + generator.Emit(OpCodes.Call, method); + return; + } + ; + #endregion + + #region Common Database Logic + string bindProperyTypeName = bindPropertyType.Name.ToLower(); + bool isEnum = bindPropertyType.IsEnum(); + if (isEnum) { validPropertyType = CSharpDataType.@enum; } + switch (validPropertyType) + { + case CSharpDataType.@int: + CheckType(bind.IntThrow, bindProperyTypeName, validPropertyName, propertyName); + if (bindProperyTypeName.IsContainsIn("int", "int32")) + method = isNullableType ? getConvertInt32 : getInt32; + if (bindProperyTypeName.IsContainsIn("int64")) + method = null; + if (bindProperyTypeName.IsContainsIn("byte")) + method = isNullableType ? getConvertByte : getByte; + if (bindProperyTypeName.IsContainsIn("int16")) + method = isNullableType ? getConvertInt16 : getInt16; + if (bindProperyTypeName == "uint32" && this.Context.CurrentConnectionConfig.DbType.IsIn(DbType.MySql, DbType.MySqlConnector)) + method = null; + if (bindPropertyType == UtilConstants.IntType && this.Context.CurrentConnectionConfig.DbType == DbType.OceanBaseForOracle) + method = isNullableType ? getMyIntNull : getMyInt; + if (bindProperyTypeName == "int16") + method = null; + break; + case CSharpDataType.@bool: + if (bindProperyTypeName == "bool" || bindProperyTypeName == "boolean") + method = isNullableType ? getConvertBoolean : getBoolean; + break; + case CSharpDataType.@string: + if (this.Context.CurrentConnectionConfig.DbType != DbType.Oracle) + { + CheckType(bind.StringThrow, bindProperyTypeName, validPropertyName, propertyName); + } + method = getString; + if (bindProperyTypeName == "guid") + { + method = isNullableType ? getConvertStringGuid : getStringGuid; + } + else if (bindProperyTypeName == "xelement") + { + method = getXelement; + } + else if (dbTypeName == "CHAR" && DataRecord.GetDataTypeName(ordinal) == "CHAR(36)") + { + method = null; + } + else if (bindPropertyType.Name == "Char") + { + method = null; + } + break; + case CSharpDataType.DateTime: + CheckType(bind.DateThrow, bindProperyTypeName, validPropertyName, propertyName); + if (bindProperyTypeName == "datetime") + method = isNullableType ? getConvertDateTime : getDateTime; + if (bindProperyTypeName == "datetime" && dbTypeName.Equals("time", StringComparison.CurrentCultureIgnoreCase)) + method = isNullableType ? getConvertTime : getTime; + if (bindProperyTypeName == "datetimeoffset") + method = isNullableType ? getConvertdatetimeoffset : getdatetimeoffset; + break; + case CSharpDataType.@decimal: + CheckType(bind.DecimalThrow, bindProperyTypeName, validPropertyName, propertyName); + if (bindProperyTypeName == "decimal") + method = isNullableType ? getConvertDecimal : getDecimal; + break; + case CSharpDataType.@float: + case CSharpDataType.@double: + CheckType(bind.DoubleThrow, bindProperyTypeName, validPropertyName, propertyName); + if (bindProperyTypeName.IsIn("double", "single") && dbTypeName != "real") + method = isNullableType ? getConvertDouble : getDouble; + else + method = isNullableType ? getConvertFloat : getFloat; + if (dbTypeName.Equals("float", StringComparison.CurrentCultureIgnoreCase) && isNullableType && bindProperyTypeName.Equals("single", StringComparison.CurrentCultureIgnoreCase)) + { + method = getConvertDoubleToFloat; + } + if (bindPropertyType == UtilConstants.DecType) + { + method = isNullableType ? getOtherNull.MakeGenericMethod(bindPropertyType) : getOther.MakeGenericMethod(bindPropertyType); + } + if (bindPropertyType == UtilConstants.IntType) + { + method = isNullableType ? getOtherNull.MakeGenericMethod(bindPropertyType) : getOther.MakeGenericMethod(bindPropertyType); + } + if (bindProperyTypeName == "string") + { + method = null; + } + break; + case CSharpDataType.Guid: + CheckType(bind.GuidThrow, bindProperyTypeName, validPropertyName, propertyName); + if (bindProperyTypeName == "guid") + method = isNullableType ? getConvertGuid : getGuid; + break; + case CSharpDataType.@byte: + if (bindProperyTypeName == "byte") + method = isNullableType ? getConvertByte : getByte; + break; + case CSharpDataType.@enum: + method = isNullableType ? getConvertEnum_Null.MakeGenericMethod(bindPropertyType) : getEnum.MakeGenericMethod(bindPropertyType); + break; + case CSharpDataType.@short: + CheckType(bind.ShortThrow, bindProperyTypeName, validPropertyName, propertyName); + if (bindProperyTypeName == "int16" || bindProperyTypeName == "short") + method = isNullableType ? getConvertInt16 : getInt16; + break; + case CSharpDataType.@long: + if (bindProperyTypeName == "int64" || bindProperyTypeName == "long") + method = isNullableType ? getConvertInt64 : getInt64; + break; + case CSharpDataType.DateTimeOffset: + method = isNullableType ? getConvertdatetimeoffset : getdatetimeoffset; + if (bindProperyTypeName == "datetime") + method = isNullableType ? getConvertdatetimeoffsetDate : getdatetimeoffsetDate; + break; + case CSharpDataType.Single: + break; + default: + method = isNullableType ? getOtherNull.MakeGenericMethod(bindPropertyType) : getOther.MakeGenericMethod(bindPropertyType); + break; + } + if (method == null && bindPropertyType == UtilConstants.StringType) + { + method = getConvertString; + } + if (bindPropertyType == UtilConstants.ObjType) + { + method = isNullableType ? getOtherNull.MakeGenericMethod(bindPropertyType) : getOther.MakeGenericMethod(bindPropertyType); + } + if (method == null) + method = isNullableType ? getOtherNull.MakeGenericMethod(bindPropertyType) : getOther.MakeGenericMethod(bindPropertyType); + + + if (method.IsVirtual) + generator.Emit(OpCodes.Callvirt, method); + else + generator.Emit(OpCodes.Call, method); + #endregion + } + + + private void CheckType(List invalidTypes, string bindProperyTypeName, string validPropertyType, string propertyName) + { + var isAny = invalidTypes.Contains(bindProperyTypeName); + if (isAny) + { + throw new SqlSugarException(string.Format("{0} can't convert {1} to {2}", propertyName, validPropertyType, bindProperyTypeName)); + } + } + #endregion + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DbBindProvider/IDataRecordExtensions.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DbBindProvider/IDataRecordExtensions.cs new file mode 100644 index 000000000..f038c8d2e --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DbBindProvider/IDataRecordExtensions.cs @@ -0,0 +1,399 @@ +using System.Data; +using System.Xml.Linq; + +namespace SqlSugar +{ + public static partial class IDataRecordExtensions + { + + #region Common Extensions + public static XElement GetXelement(this IDataRecord dr, int i) + { + var result = XElement.Parse(dr.GetString(i).ToString()); + return result; + } + public static Guid GetStringGuid(this IDataRecord dr, int i) + { + var result = Guid.Parse(dr.GetValue(i).ToString()); + return result; + } + + public static Guid? GetConvertStringGuid(this IDataRecord dr, int i) + { + if (dr.IsDBNull(i)) + { + return Guid.Empty; + } + var result = Guid.Parse(dr.GetValue(i).ToString()); + return result; + } + + public static bool? GetConvertBoolean(this IDataRecord dr, int i) + { + var result = dr.GetBoolean(i); + return result; + } + + public static byte? GetConvertByte(this IDataRecord dr, int i) + { + if (dr.IsDBNull(i)) + { + return null; + } + var result = dr.GetByte(i); + return result; + } + + public static char? GetConvertChar(this IDataRecord dr, int i) + { + if (dr.IsDBNull(i)) + { + return null; + } + var result = dr.GetChar(i); + return result; + } + + public static DateTime? GetConvertDateTime(this IDataRecord dr, int i) + { + var result = dr.GetDateTime(i); + if (result == DateTime.MinValue) + { + return null; ; + } + return result; + } + public static DateTime? GetConvertTime(this IDataRecord dr, int i) + { + var result = dr.GetValue(i); + if (result == DBNull.Value) + { + return null; ; + } + return Convert.ToDateTime(result.ToString()); + } + public static DateTime GetTime(this IDataRecord dr, int i) + { + return Convert.ToDateTime(dr.GetValue(i).ToString()); + } + + public static decimal? GetConvertDecimal(this IDataRecord dr, int i) + { + if (dr.IsDBNull(i)) + { + return null; + } + var result = dr.GetDecimal(i); + return result; + } + + + public static double? GetConvertDouble(this IDataRecord dr, int i) + { + if (dr.IsDBNull(i)) + { + return null; + } + var result = dr.GetDouble(i); + return result; + } + + + public static float? GetConvertDoubleToFloat(this IDataRecord dr, int i) + { + if (dr.IsDBNull(i)) + { + return null; + } + var result = dr.GetDouble(i); + return Convert.ToSingle(result); + } + + + public static Guid? GetConvertGuid(this IDataRecord dr, int i) + { + if (dr.IsDBNull(i)) + { + return null; + } + var result = dr.GetGuid(i); + return result; + } + + public static short? GetConvertInt16(this IDataRecord dr, int i) + { + if (dr.IsDBNull(i)) + { + return null; + } + var result = dr.GetInt16(i); + return result; + } + public static Int32? GetMyIntNull(this IDataRecord dr, int i) + { + if (dr.IsDBNull(i)) + { + return null; + } + if (dr.GetDataTypeName(i) == "NUMBER") + { + return Convert.ToInt32(dr.GetDouble(i)); + } + var result = dr.GetInt32(i); + return result; + } + public static Int32 GetMyInt(this IDataRecord dr, int i) + { + if (dr.GetDataTypeName(i) == "NUMBER") + { + return Convert.ToInt32(dr.GetDouble(i)); + } + var result = dr.GetInt32(i); + return result; + } + + public static Int32? GetConvertInt32(this IDataRecord dr, int i) + { + if (dr.IsDBNull(i)) + { + return null; + } + var result = dr.GetInt32(i); + return result; + } + //public static T GetConvertValue(this IDataRecord dr, int i) + //{ + // try + // { + // if (dr.IsDBNull(i)) + // { + // return default(T); + // } + // var result = dr.GetValue(i); + // return UtilMethods.To(result); + // } + // catch (Exception ex) + // { + // return OtherException(dr, i, ex); + // } + //} + + public static long? GetConvetInt64(this IDataRecord dr, int i) + { + if (dr.IsDBNull(i)) + { + return null; + } + var result = dr.GetInt64(i); + return result; + } + + public static float? GetConvertFloat(this IDataRecord dr, int i) + { + if (dr.IsDBNull(i)) + { + return null; + } + var result = dr.GetFloat(i); + return result; + } + + public static DateTime GetdatetimeoffsetDate(this IDataRecord dr, int i) + { + if (dr.IsDBNull(i)) + { + return DateTime.MinValue; + } + var offsetValue = (DateTimeOffset)dr.GetValue(i); + var result = offsetValue.DateTime; + return result; + } + + public static DateTime? GetConvertdatetimeoffsetDate(this IDataRecord dr, int i) + { + if (dr.IsDBNull(i)) + { + return DateTime.MinValue; + } + var offsetValue = (DateTimeOffset)dr.GetValue(i); + var result = offsetValue.DateTime; + return result; + } + + public static DateTimeOffset Getdatetimeoffset(this IDataRecord dr, int i) + { + if (dr.IsDBNull(i)) + { + return default(DateTimeOffset); + } + var date = dr.GetValue(i); + if (date is DateTime) + { + return new DateTimeOffset((DateTime)(date)); + } + else + { + var result = (DateTimeOffset)date; + return result; + } + } + + public static DateTimeOffset? GetConvertdatetimeoffset(this IDataRecord dr, int i) + { + if (dr.IsDBNull(i)) + { + return default(DateTimeOffset); + } + var date = dr.GetValue(i); + if (date is DateTime) + { + return new DateTimeOffset((DateTime)(date)); + } + else + { + var result = (DateTimeOffset)date; + return result; + } + } + + + public static string GetConvertString(this IDataRecord dr, int i) + { + if (dr.IsDBNull(i)) + { + return null; + } + var result = Convert.ToString(dr.GetValue(i)); + return result; + } + + public static Nullable GetOtherNull(this IDataReader dr, int i) where T : struct + { + if (dr.IsDBNull(i)) + { + return null; + } + return GetOther(dr, i); + + } + + public static T GetOther(this IDataReader dr, int i) + { + try + { + if (dr.IsDBNull(i)) + { + return default(T); + } + var result = dr.GetValue(i); + return UtilMethods.To(result); + } + catch (Exception ex) + { + return OtherException(dr, i, ex); + } + } + + public static T GetJson(this IDataReader dr, int i) + { + var obj = dr.GetValue(i); + if (obj == null) + return default(T); + var value = obj.ObjToString(); + return new SerializeService().DeserializeObject(value); + } + public static T GetArray(this IDataReader dr, int i) + { + //pgsql + var obj = dr.GetValue(i); + if (obj == null) + return default(T); + return (T)obj; + } + + public static Nullable GetConvertEnum_Null(this IDataReader dr, int i) where T : struct + { + if (dr.IsDBNull(i)) + { + return null; + } + object value = dr.GetValue(i); + if (value != null) + { + var valueType = value.GetType(); + if (valueType.IsIn(UtilConstants.FloatType, UtilConstants.DecType, UtilConstants.DobType)) + { + if (Convert.ToDecimal(value) < 0) + { + value = Convert.ToInt32(value); + } + else + { + value = Convert.ToUInt32(value); + } + } + else if (valueType == UtilConstants.StringType) + { + return (T)Enum.Parse(typeof(T), value.ObjToString()); + } + } + T t = (T)Enum.ToObject(typeof(T), value); + return t; + } + + public static T GetEnum(this IDataReader dr, int i) where T : struct + { + object value = dr.GetValue(i); + if (value != null) + { + var valueType = value.GetType(); + if (valueType.IsIn(UtilConstants.FloatType, UtilConstants.DecType, UtilConstants.DobType)) + { + if (Convert.ToDecimal(value) < 0) + { + value = Convert.ToInt32(value); + } + else + { + value = Convert.ToUInt32(value); + } + } + else if (valueType == UtilConstants.StringType) + { + return (T)Enum.Parse(typeof(T), value.ObjToString()); + } + } + T t = (T)Enum.ToObject(typeof(T), value); + return t; + } + + public static object GetEntity(this IDataReader dr, SqlSugarProvider context) + { + return null; + } + + + private static T OtherException(IDataRecord dr, int i, Exception ex) + { + if (dr.GetFieldType(i) == UtilConstants.DateType) + { + return UtilMethods.To(dr.GetConvertDouble(i)); + } + if (dr.GetFieldType(i) == UtilConstants.GuidType) + { + var data = dr.GetString(i); + if (string.IsNullOrEmpty(data)) + { + return UtilMethods.To(default(T)); + } + else + { + return UtilMethods.To(Guid.Parse(data)); + } + } + throw new Exception(ex.Message); + } + + #endregion + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DbFirstProvider/DbFirstProvider.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DbFirstProvider/DbFirstProvider.cs new file mode 100644 index 000000000..cb201a465 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DbFirstProvider/DbFirstProvider.cs @@ -0,0 +1,605 @@ +using System.Text; +using System.Text.RegularExpressions; + +namespace SqlSugar +{ + public abstract partial class DbFirstProvider : IDbFirst + { + public virtual ISqlSugarClient Context { get; set; } + private string ClassTemplate { get; set; } + private string ClassDescriptionTemplate { get; set; } + private string PropertyTemplate { get; set; } + private string PropertyDescriptionTemplate { get; set; } + private string ConstructorTemplate { get; set; } + private string UsingTemplate { get; set; } + private string Namespace { get; set; } + private bool IsAttribute { get; set; } + private bool IsDefaultValue { get; set; } + private Func WhereColumnsfunc; + private Func FormatFileNameFunc { get; set; } + private Func FormatClassNameFunc { get; set; } + private Func FormatPropertyNameFunc { get; set; } + private bool IsStringNullable { get; set; } + private Func PropertyTextTemplateFunc { get; set; } + private Func ReplaceClassStringFunc { get; set; } + private ISqlBuilder SqlBuilder + { + get + { + return InstanceFactory.GetSqlbuilder(this.Context.CurrentConnectionConfig); + } + } + private List TableInfoList { get; set; } + + public DbFirstProvider() + { + this.ClassTemplate = DbFirstTemplate.ClassTemplate; + this.ClassDescriptionTemplate = DbFirstTemplate.ClassDescriptionTemplate; + this.PropertyTemplate = DbFirstTemplate.PropertyTemplate; + this.PropertyDescriptionTemplate = DbFirstTemplate.PropertyDescriptionTemplate; + this.ConstructorTemplate = DbFirstTemplate.ConstructorTemplate; + this.UsingTemplate = DbFirstTemplate.UsingTemplate; + } + + public void Init() + { + this.Context.Utilities.RemoveCacheAll(); + if (!this.Context.DbMaintenance.IsAnySystemTablePermissions()) + { + Check.Exception(true, "Dbfirst and Codefirst requires system table permissions"); + } + this.TableInfoList = this.Context.Utilities.TranslateCopy(this.Context.DbMaintenance.GetTableInfoList()); + var viewList = this.Context.Utilities.TranslateCopy(this.Context.DbMaintenance.GetViewInfoList()); + if (viewList.HasValue()) + { + this.TableInfoList.AddRange(viewList); + } + } + + #region Setting Template + public IDbFirst StringNullable() + { + IsStringNullable = true; + return this; + } + public IDbFirst SettingClassDescriptionTemplate(Func func) + { + this.ClassDescriptionTemplate = func(this.ClassDescriptionTemplate); + return this; + } + + public IDbFirst SettingClassTemplate(Func func) + { + this.ClassTemplate = func(this.ClassTemplate); + return this; + } + + public IDbFirst SettingConstructorTemplate(Func func) + { + this.ConstructorTemplate = func(this.ConstructorTemplate); + return this; + } + + public IDbFirst SettingPropertyDescriptionTemplate(Func func) + { + this.PropertyDescriptionTemplate = func(this.PropertyDescriptionTemplate); + return this; + } + + public IDbFirst SettingNamespaceTemplate(Func func) + { + this.UsingTemplate = func(this.UsingTemplate); + return this; + } + + public IDbFirst SettingPropertyTemplate(Func func) + { + this.PropertyTemplate = func(this.PropertyTemplate); + return this; + } + public IDbFirst SettingPropertyTemplate(Func func) + { + this.PropertyTextTemplateFunc = func; + return this; + } + public RazorFirst UseRazorAnalysis(string razorClassTemplate, string classNamespace = "Models") + { + if (razorClassTemplate == null) + { + razorClassTemplate = ""; + } + razorClassTemplate = razorClassTemplate.Replace("@Model.Namespace", classNamespace); + var result = new RazorFirst(); + if (this.Context.CurrentConnectionConfig.ConfigureExternalServices?.RazorService != null) + { + List razorList = new List(); + var tables = this.TableInfoList; + if (tables.HasValue()) + { + foreach (var item in tables) + { + var columns = this.Context.DbMaintenance.GetColumnInfosByTableName(item.Name, false); + RazorTableInfo table = new RazorTableInfo() + { + Columns = columns.Where(it => WhereColumnsfunc == null || WhereColumnsfunc(it.DbColumnName)).Select(it => new RazorColumnInfo() + { + ColumnDescription = it.ColumnDescription, + DataType = it.DataType, + DbColumnName = it.DbColumnName, + DefaultValue = it.DefaultValue, + IsIdentity = it.IsIdentity, + IsNullable = it.IsNullable, + IsPrimarykey = it.IsPrimarykey, + Length = it.Length + }).ToList(), + Description = item.Description, + DbTableName = item.Name + }; + foreach (var col in table.Columns) + { + col.DataType = GetPropertyTypeName(columns.First(it => it.DbColumnName == col.DbColumnName)); + } + razorList.Add(table); + } + } + result.ClassStringList = this.Context.CurrentConnectionConfig.ConfigureExternalServices.RazorService.GetClassStringList(razorClassTemplate, razorList); + } + else + { + Check.Exception(true, ErrorMessage.GetThrowMessage("Need to achieve ConnectionConfig.ConfigureExternal Services.RazorService", "需要实现 ConnectionConfig.ConfigureExternal Services.RazorService接口")); + } + this.Context.Utilities.RemoveCacheAll(); + result.FormatFileNameFunc = this.FormatFileNameFunc; + return result; + } + #endregion + + #region Setting Content + public IDbFirst IsCreateAttribute(bool isCreateAttribute = true) + { + this.IsAttribute = isCreateAttribute; + return this; + } + public IDbFirst FormatFileName(Func formatFileNameFunc) + { + this.FormatFileNameFunc = formatFileNameFunc; + return this; + } + public IDbFirst FormatClassName(Func formatClassNameFunc) + { + this.FormatClassNameFunc = formatClassNameFunc; + return this; + } + public IDbFirst FormatPropertyName(Func formatPropertyNameFunc) + { + this.FormatPropertyNameFunc = formatPropertyNameFunc; + return this; + } + public IDbFirst CreatedReplaceClassString(Func replaceClassStringFunc) + { + this.ReplaceClassStringFunc = replaceClassStringFunc; + return this; + } + public IDbFirst IsCreateDefaultValue(bool isCreateDefaultValue = true) + { + this.IsDefaultValue = isCreateDefaultValue; + return this; + } + #endregion + + #region Where + public IDbFirst Where(DbObjectType dbObjectType) + { + if (dbObjectType != DbObjectType.All) + this.TableInfoList = this.TableInfoList.Where(it => it.DbObjectType == dbObjectType).ToList(); + return this; + } + + public IDbFirst Where(Func func) + { + this.TableInfoList = this.TableInfoList.Where(it => func(it.Name)).ToList(); + return this; + } + + public IDbFirst WhereColumns(Func func) + { + WhereColumnsfunc = func; + return this; + } + + + public IDbFirst Where(params string[] objectNames) + { + if (objectNames.HasValue()) + { + this.TableInfoList = this.TableInfoList.Where(it => objectNames.Select(x => x.ToLower()).Contains(it.Name.ToLower())).ToList(); + } + return this; + } + #endregion + + #region Core + public Dictionary ToClassStringList(string nameSpace = "Models") + { + this.Namespace = nameSpace; + Dictionary result = new Dictionary(); + if (this.TableInfoList.HasValue()) + { + foreach (var tableInfo in this.TableInfoList) + { + try + { + string classText = null; + string className = tableInfo.Name; + var oldClasName = className; + classText = GetClassString(tableInfo, ref className); + result.Remove(className); + if (this.ReplaceClassStringFunc != null) + { + classText = this.ReplaceClassStringFunc(classText); + } + if (FormatClassNameFunc != null && FormatFileNameFunc != null) + { + className = oldClasName; + } + result.Add(className, classText); + } + catch (Exception ex) + { + Check.Exception(true, "Table '{0}' error,You can filter it with Db.DbFirst.Where(name=>name!=\"{0}\" ) \r\n Error message:{1}", tableInfo.Name, ex.Message); + } + } + } + return result; + } + + internal string GetClassString(DbTableInfo tableInfo, ref string className) + { + string classText; + var columns = this.Context.DbMaintenance.GetColumnInfosByTableName(tableInfo.Name, false); + if (this.Context.IgnoreColumns.HasValue()) + { + var entityName = this.Context.EntityMaintenance.GetEntityName(tableInfo.Name); + columns = columns.Where(c => + !this.Context.IgnoreColumns.Any(ig => ig.EntityName.Equals(entityName, StringComparison.CurrentCultureIgnoreCase) && c.DbColumnName == ig.PropertyName) + ).ToList(); + } + classText = this.ClassTemplate; + string ConstructorText = IsDefaultValue ? this.ConstructorTemplate : null; + if (this.Context.MappingTables.HasValue()) + { + var mappingInfo = this.Context.MappingTables.FirstOrDefault(it => it.DbTableName.Equals(tableInfo.Name, StringComparison.CurrentCultureIgnoreCase)); + if (mappingInfo.HasValue()) + { + className = mappingInfo.EntityName; + } + if (mappingInfo != null) + { + classText = classText.Replace(DbFirstTemplate.KeyClassName, mappingInfo.EntityName); + } + } + if (FormatClassNameFunc != null) + { + className = FormatClassNameFunc(className); + } + classText = classText.Replace(DbFirstTemplate.KeyClassName, className); + classText = classText.Replace(DbFirstTemplate.KeyNamespace, this.Namespace); + classText = classText.Replace(DbFirstTemplate.KeyUsing, IsAttribute ? (this.UsingTemplate + "using " + UtilConstants.AssemblyName + ";\r\n") : this.UsingTemplate); + classText = classText.Replace(DbFirstTemplate.KeyClassDescription, this.ClassDescriptionTemplate.Replace(DbFirstTemplate.KeyClassDescription, tableInfo.Description?.Replace(Environment.NewLine, "\t") + "\r\n")); + classText = classText.Replace(DbFirstTemplate.KeySugarTable, IsAttribute ? string.Format(null, DbFirstTemplate.ValueSugarTable, tableInfo.Name) : null); + if (columns.HasValue()) + { + foreach (var item in columns.Where(it => WhereColumnsfunc == null || WhereColumnsfunc(it.DbColumnName))) + { + var isLast = columns.Last() == item; + var index = columns.IndexOf(item); + string PropertyText = this.PropertyTemplate; + string PropertyDescriptionText = this.PropertyDescriptionTemplate; + string propertyName = GetPropertyName(item); + var oldPropertyName = propertyName; + if (FormatPropertyNameFunc != null) + { + item.DbColumnName = propertyName = FormatPropertyNameFunc(propertyName); + } + string propertyTypeName = GetPropertyTypeName(item); + PropertyText = this.PropertyTextTemplateFunc == null ? GetPropertyText(item, PropertyText) : this.PropertyTextTemplateFunc(item, this.PropertyTemplate, propertyTypeName); + PropertyDescriptionText = GetPropertyDescriptionText(item, PropertyDescriptionText); + if (this.IsAttribute && item.DataType?.StartsWith('_') == true && PropertyText.Contains("[]")) + { + PropertyDescriptionText += "\r\n [SugarColumn(IsArray=true)]"; + } + else if (item?.DataType?.StartsWith("json") == true) + { + PropertyDescriptionText += "\r\n [SugarColumn(IsJson=true)]"; + } + else if (FormatPropertyNameFunc != null) + { + if (PropertyText.Contains("SugarColumn")) + { + PropertyText = PropertyText.Replace(")]", ",ColumnName=\"" + oldPropertyName + "\")]"); + } + else + { + PropertyDescriptionText += "\r\n [SugarColumn(ColumnName=\"" + oldPropertyName + "\")]"; + } + } + PropertyText = PropertyDescriptionText + PropertyText; + classText = classText.Replace(DbFirstTemplate.KeyPropertyName, PropertyText + (isLast ? "" : ("\r\n" + DbFirstTemplate.KeyPropertyName))); + if (ConstructorText.HasValue() && item.DefaultValue != null && item.IsIdentity != true) + { + var hasDefaultValue = columns.Skip(index + 1).Any(it => it.DefaultValue.HasValue()); + if (item.DefaultValue.EqualCase("CURRENT_TIMESTAMP")) + { + item.DefaultValue = "DateTime.Now"; + } + else if (item.DefaultValue == "b'1'") + { + item.DefaultValue = "1"; + } + ConstructorText = ConstructorText.Replace(DbFirstTemplate.KeyPropertyName, propertyName); + ConstructorText = ConstructorText.Replace(DbFirstTemplate.KeyDefaultValue, GetPropertyTypeConvert(item)) + (!hasDefaultValue ? "" : this.ConstructorTemplate); + } + } + } + if (!columns.Any(it => it.DefaultValue != null && it.IsIdentity == false)) + { + ConstructorText = null; + } + classText = classText.Replace(DbFirstTemplate.KeyConstructor, ConstructorText); + classText = classText.Replace(DbFirstTemplate.KeyPropertyName, null); + return classText; + } + + internal string GetClassString(List columns, ref string className) + { + string classText = this.ClassTemplate; + string ConstructorText = IsDefaultValue ? this.ConstructorTemplate : null; + classText = classText.Replace(DbFirstTemplate.KeyClassName, className); + classText = classText.Replace(DbFirstTemplate.KeyNamespace, this.Namespace); + classText = classText.Replace(DbFirstTemplate.KeyUsing, IsAttribute ? (this.UsingTemplate + "using " + UtilConstants.AssemblyName + ";\r\n") : this.UsingTemplate); + classText = classText.Replace(DbFirstTemplate.KeyClassDescription, this.ClassDescriptionTemplate.Replace(DbFirstTemplate.KeyClassDescription, "\r\n")); + classText = classText.Replace(DbFirstTemplate.KeySugarTable, IsAttribute ? string.Format(null, DbFirstTemplate.ValueSugarTable, className) : null); + if (columns.HasValue()) + { + foreach (var item in columns) + { + var isLast = columns.Last() == item; + var index = columns.IndexOf(item); + string PropertyText = this.PropertyTemplate; + string PropertyDescriptionText = this.PropertyDescriptionTemplate; + string propertyName = GetPropertyName(item); + string propertyTypeName = item.PropertyName; + PropertyText = GetPropertyText(item, PropertyText); + PropertyDescriptionText = GetPropertyDescriptionText(item, PropertyDescriptionText); + PropertyText = PropertyDescriptionText + PropertyText; + classText = classText.Replace(DbFirstTemplate.KeyPropertyName, PropertyText + (isLast ? "" : ("\r\n" + DbFirstTemplate.KeyPropertyName))); + if (ConstructorText.HasValue() && item.DefaultValue != null) + { + var hasDefaultValue = columns.Skip(index + 1).Any(it => it.DefaultValue.HasValue()); + ConstructorText = ConstructorText.Replace(DbFirstTemplate.KeyPropertyName, propertyName); + ConstructorText = ConstructorText.Replace(DbFirstTemplate.KeyDefaultValue, GetPropertyTypeConvert(item)) + (!hasDefaultValue ? "" : this.ConstructorTemplate); + } + } + } + if (!columns.Any(it => it.DefaultValue != null)) + { + ConstructorText = null; + } + classText = classText.Replace(DbFirstTemplate.KeyConstructor, ConstructorText); + classText = classText.Replace(DbFirstTemplate.KeyPropertyName, null); + return classText; + } + public void CreateClassFile(string directoryPath, string nameSpace = "Models") + { + var seChar = Path.DirectorySeparatorChar.ToString(); + Check.ArgumentNullException(directoryPath, "directoryPath can't null"); + var classStringList = ToClassStringList(nameSpace); + if (classStringList.IsValuable()) + { + foreach (var item in classStringList) + { + var fileName = item.Key; + if (FormatFileNameFunc != null) + { + fileName = FormatFileNameFunc(fileName); + } + var filePath = directoryPath.TrimEnd('\\').TrimEnd('/') + string.Format(seChar + "{0}.cs", fileName); + FileHelper.CreateFile(filePath, item.Value, Encoding.UTF8); + } + } + } + #endregion + + #region Private methods + private string GetProertypeDefaultValue(DbColumnInfo item) + { + var result = item.DefaultValue; + if (result == null) return null; + if (Regex.IsMatch(result, @"^\(\'(.+)\'\)$")) + { + result = Regex.Match(result, @"^\(\'(.+)\'\)$").Groups[1].Value; + } + if (Regex.IsMatch(result, @"^\(\((.+)\)\)$")) + { + result = Regex.Match(result, @"^\(\((.+)\)\)$").Groups[1].Value; + } + if (Regex.IsMatch(result, @"^\((.+)\)$")) + { + result = Regex.Match(result, @"^\((.+)\)$").Groups[1].Value; + } + if (result.Equals(this.SqlBuilder.SqlDateNow, StringComparison.CurrentCultureIgnoreCase)) + { + result = "DateTime.Now"; + } + if (result.Equals("getdate()", StringComparison.CurrentCultureIgnoreCase)) + { + result = "DateTime.Now"; + } + if (result.Equals("getutcdate()", StringComparison.CurrentCultureIgnoreCase)) + { + result = "DateTime.Now"; + } + if (result.Equals("NOW()", StringComparison.CurrentCultureIgnoreCase)) + { + result = "DateTime.Now"; + } + result = result.Replace("\r", "\t").Replace("\n", "\t"); + result = result.IsIn("''", "\"\"") ? string.Empty : result; + return result; + } + private string GetPropertyText(DbColumnInfo item, string PropertyText) + { + string SugarColumnText = "\r\n [SugarColumn({0})]"; + var propertyName = GetPropertyName(item); + var isMappingColumn = propertyName != item.DbColumnName; + var hasSugarColumn = item.IsPrimarykey == true || item.IsIdentity == true || isMappingColumn; + if (hasSugarColumn && this.IsAttribute) + { + List joinList = new List(); + if (item.IsPrimarykey) + { + joinList.Add("IsPrimaryKey=true"); + } + if (item.IsIdentity) + { + joinList.Add("IsIdentity=true"); + } + if (isMappingColumn) + { + joinList.Add("ColumnName=\"" + item.DbColumnName + '\"'); + } + SugarColumnText = string.Format(SugarColumnText, string.Join(",", joinList)); + } + else + { + SugarColumnText = null; + } + string typeString = GetPropertyTypeName(item); + PropertyText = PropertyText.Replace(DbFirstTemplate.KeySugarColumn, SugarColumnText); + PropertyText = PropertyText.Replace(DbFirstTemplate.KeyPropertyType, typeString); + PropertyText = PropertyText.Replace(DbFirstTemplate.KeyPropertyName, propertyName); + if (typeString == "string" && this.IsStringNullable && item.IsNullable == false && PropertyText.EndsWith("{get;set;}\r\n")) + { + PropertyText = PropertyText.Replace("{get;set;}\r\n", "{get;set;} = null!;\r\n"); + } + return PropertyText; + } + private string GetEnityName(DbColumnInfo item) + { + var mappingInfo = this.Context.MappingTables.FirstOrDefault(it => it.DbTableName.Equals(item.TableName, StringComparison.CurrentCultureIgnoreCase)); + return mappingInfo == null ? item.TableName : mappingInfo.EntityName; + } + private string GetPropertyName(DbColumnInfo item) + { + if (this.Context.MappingColumns.HasValue()) + { + var mappingInfo = this.Context.MappingColumns.SingleOrDefault(it => it.DbColumnName == item.DbColumnName && it.EntityName == GetEnityName(item)); + return mappingInfo == null ? item.DbColumnName : mappingInfo.PropertyName; + } + else + { + return item.DbColumnName; + } + } + protected virtual string GetPropertyTypeName(DbColumnInfo item) + { + string result = item.PropertyType != null ? item.PropertyType.Name : this.Context.Ado.DbBind.GetPropertyTypeName(item.DataType); + if (result != "string" && result != "byte[]" && result != "object" && item.IsNullable) + { + result += "?"; + } + if (result == "Int32") + { + result = item.IsNullable ? "int?" : "int"; + } + if (result == "String") + { + result = "string"; + } + if (result == "string" && item.IsNullable && IsStringNullable) + { + result = result + "?"; + } + if (item.OracleDataType.EqualCase("raw") && item.Length == 16) + { + return "Guid"; + } + if (item.OracleDataType.EqualCase("number") && item.Length == 1 && item.Scale == 0) + { + return "bool"; + } + if (result.EqualCase("char") || result.EqualCase("char?")) + { + return "string"; + } + if (item.DataType == "tinyint unsigned") + { + return "short"; + } + if (item.DataType == "smallint unsigned") + { + return "ushort"; + } + if (item.DataType == "bigint unsigned") + { + return "ulong"; + } + if (item.DataType == "int unsigned") + { + return "uint"; + } + if (item.DataType == "MediumInt") + { + return "int"; + } + if (item.DataType == "MediumInt unsigned") + { + return "uint"; + } + return result; + } + private string GetPropertyTypeConvert(DbColumnInfo item) + { + var convertString = GetProertypeDefaultValue(item); + if (convertString == "DateTime.Now" || convertString == null) + return convertString; + if (convertString.ObjToString() == "newid()") + { + return "Guid.NewGuid()"; + } + if (item.DataType?.ToString()?.EndsWith("unsigned") == true) + { + return convertString; + } + if (item.DataType == "bit") + return (convertString == "1" || convertString.Equals("true", StringComparison.CurrentCultureIgnoreCase)).ToString().ToLower(); + if (convertString.EqualCase("NULL")) + { + return "null"; + } + string result = this.Context.Ado.DbBind.GetConvertString(item.DataType) + "(\"" + convertString + "\")"; + if (this.SqlBuilder.SqlParameterKeyWord == ":" && !string.IsNullOrEmpty(item.OracleDataType)) + { + result = this.Context.Ado.DbBind.GetConvertString(item.OracleDataType) + "(\"" + convertString + "\")"; + } + return result; + } + private string GetPropertyDescriptionText(DbColumnInfo item, string propertyDescriptionText) + { + propertyDescriptionText = propertyDescriptionText.Replace(DbFirstTemplate.KeyPropertyDescription, GetColumnDescription(item.ColumnDescription)); + propertyDescriptionText = propertyDescriptionText.Replace(DbFirstTemplate.KeyDefaultValue, GetProertypeDefaultValue(item)); + propertyDescriptionText = propertyDescriptionText.Replace(DbFirstTemplate.KeyIsNullable, item.IsNullable.ObjToString()); + return propertyDescriptionText; + } + private string GetColumnDescription(string columnDescription) + { + if (columnDescription == null) return columnDescription; + columnDescription = columnDescription.Replace("\r", "\t"); + columnDescription = columnDescription.Replace("\n", "\t"); + columnDescription = columnDescription.Replace(Environment.NewLine, "\t"); + columnDescription = Regex.Replace(columnDescription, "\t{2,}", "\t"); + return columnDescription; + } + #endregion + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DbFirstProvider/DbFirstTemplate.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DbFirstProvider/DbFirstTemplate.cs new file mode 100644 index 000000000..8b7a042d1 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DbFirstProvider/DbFirstTemplate.cs @@ -0,0 +1,73 @@ +using System.Text; + +namespace SqlSugar +{ + public static class DbFirstTemplate + { + #region Template + public static string ClassTemplate = "{using}\r\n" + + "namespace {Namespace}\r\n" + + "{\r\n" + + "{ClassDescription}{SugarTable}\r\n" + + ClassSpace + "public partial class {ClassName}\r\n" + + ClassSpace + "{\r\n" + + PropertySpace + "public {ClassName}(){\r\n\r\n" + + "{Constructor}\r\n" + + PropertySpace + "}\r\n" + + "{PropertyName}\r\n" + + ClassSpace + "}\r\n" + + "}\r\n"; + public static string ClassDescriptionTemplate = + ClassSpace + "///\r\n" + + ClassSpace + "///{ClassDescription}" + + ClassSpace + "///"; + + public static string PropertyTemplate = PropertySpace + "{SugarColumn}\r\n" + + PropertySpace + "public {PropertyType} {PropertyName} {get;set;}\r\n"; + + public static string PropertyDescriptionTemplate = + PropertySpace + "/// \r\n" + + PropertySpace + "/// Desc:{PropertyDescription}\r\n" + + PropertySpace + "/// Default:{DefaultValue}\r\n" + + PropertySpace + "/// Nullable:{IsNullable}\r\n" + + PropertySpace + "/// "; + + public static string ConstructorTemplate = PropertySpace + " this.{PropertyName} ={DefaultValue};\r\n"; + + public static string UsingTemplate = "using System;\r\n" + + "using System.Linq;\r\n" + + "using System.Text;" + "\r\n"; + #endregion + + #region Replace Key + public const string KeyUsing = "{using}"; + public const string KeyNamespace = "{Namespace}"; + public const string KeyClassName = "{ClassName}"; + public const string KeyIsNullable = "{IsNullable}"; + public const string KeySugarTable = "{SugarTable}"; + public const string KeyConstructor = "{Constructor}"; + public const string KeySugarColumn = "{SugarColumn}"; + public const string KeyPropertyType = "{PropertyType}"; + public const string KeyPropertyName = "{PropertyName}"; + public const string KeyDefaultValue = "{DefaultValue}"; + public const string KeyClassDescription = "{ClassDescription}"; + public const string KeyPropertyDescription = "{PropertyDescription}"; + #endregion + + #region Replace Value + + public static readonly CompositeFormat ValueSugarTable = CompositeFormat.Parse(privateValueSugarTable); + + private const string privateValueSugarTable = "\r\n" + ClassSpace + "[SugarTable(\"{0}\")]"; + + public static readonly CompositeFormat ValueSugarCoulmn = CompositeFormat.Parse(privateValueSugarCoulmn); + + private const string privateValueSugarCoulmn = "\r\n" + PropertySpace + "[SugarColumn({0})]"; + #endregion + + #region Space + public const string PropertySpace = " "; + public const string ClassSpace = " "; + #endregion + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DbFirstProvider/DbRazor.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DbFirstProvider/DbRazor.cs new file mode 100644 index 000000000..35492eaff --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DbFirstProvider/DbRazor.cs @@ -0,0 +1,93 @@ +using System.Text; + +namespace SqlSugar +{ + public class RazorFirst + { + internal List> ClassStringList { get; set; } + internal Func FormatFileNameFunc { get; set; } + + public static string DefaultRazorClassTemplate = +@"using System; +using System.Linq; +using System.Text; +using SqlSugar; +namespace @Model.Namespace +{ + /// + /// + /// + public partial class @Model.ClassName + { + public @(Model.ClassName)(){ + + + } + @foreach (var item in @Model.Columns) + { + if(item.IsPrimarykey&&item.IsIdentity){ + @:/// + @:/// Desc:@item.ColumnDescription + @:/// Default:@item.DefaultValue + @:/// Nullable:@item.IsNullable + @:/// + @:[SqlSugar.SugarColumn(IsPrimaryKey = true, IsIdentity = true)] + @:public @item.DataType @item.DbColumnName {get;set;} + } + else if(item.IsPrimarykey) + { + @:/// + @:/// Desc:@item.ColumnDescription + @:/// Default:@item.DefaultValue + @:/// Nullable:@item.IsNullable + @:/// + @:[SqlSugar.SugarColumn(IsPrimaryKey = true)] + @:public @item.DataType @item.DbColumnName {get;set;} + } + else if(item.IsIdentity) + { + @:/// + @:/// Desc:@item.ColumnDescription + @:/// Default:@item.DefaultValue + @:/// Nullable:@item.IsNullable + @:/// + @:[SqlSugar.SugarColumn(IsIdentity = true)] + @:public @item.DataType @item.DbColumnName {get;set;} + } + else + { + @:/// + @:/// Desc:@item.ColumnDescription + @:/// Default:@item.DefaultValue + @:/// Nullable:@item.IsNullable + @:/// + @:public @item.DataType @item.DbColumnName {get;set;} + } + } + + } +}"; + + public void CreateClassFile(string directoryPath) + { + var seChar = Path.DirectorySeparatorChar.ToString(); + if (ClassStringList.HasValue()) + { + foreach (var item in ClassStringList) + { + var fileName = item.Key; + if (this.FormatFileNameFunc != null) + { + fileName = this.FormatFileNameFunc(fileName); + } + var filePath = directoryPath.TrimEnd('\\').TrimEnd('/') + string.Format(seChar + "{0}.cs", fileName); + FileHelper.CreateFile(filePath, item.Value, Encoding.UTF8); + } + } + } + public List> GetClassStringList() + { + return ClassStringList; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DbMaintenanceProvider/Methods.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DbMaintenanceProvider/Methods.cs new file mode 100644 index 000000000..61cd49f6a --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DbMaintenanceProvider/Methods.cs @@ -0,0 +1,908 @@ +using System.Reflection; +using System.Text.RegularExpressions; + +namespace SqlSugar +{ + public abstract partial class DbMaintenanceProvider : IDbMaintenance + { + #region DML + public List GetProcList() + { + return GetProcList(this.Context.Ado.Connection.Database); + } + public virtual List GetProcList(string dbName) + { + return new List(); + } + public virtual List GetDataBaseList(SqlSugarClient db) + { + return db.Ado.SqlQuery(this.GetDataBaseSql); + } + public virtual List GetDataBaseList() + { + return this.Context.Ado.SqlQuery(this.GetDataBaseSql); + } + public virtual List GetViewInfoList(bool isCache = true) + { + string cacheKey = "DbMaintenanceProvider.GetViewInfoList" + this.Context.CurrentConnectionConfig.ConfigId; + cacheKey = GetCacheKey(cacheKey); + var result = new List(); + if (isCache) + result = GetListOrCache(cacheKey, this.GetViewInfoListSql); + else + result = this.Context.Ado.SqlQuery(this.GetViewInfoListSql); + foreach (var item in result) + { + item.DbObjectType = DbObjectType.View; + } + return result; + } + public List GetTableInfoList(Func getChangeSqlFunc) + { + var db = this.Context.CopyNew(); + db.CurrentConnectionConfig.IsAutoCloseConnection = true; + db.Aop.OnExecutingChangeSql = (sql, pars) => + { + sql = getChangeSqlFunc(this.Context.CurrentConnectionConfig.DbType, sql); + return new KeyValuePair(sql, pars); + }; + var result = db.DbMaintenance.GetTableInfoList(false); + return result; + } + public virtual List GetTableInfoList(bool isCache = true) + { + string cacheKey = "DbMaintenanceProvider.GetTableInfoList" + this.Context.CurrentConnectionConfig.ConfigId; + cacheKey = GetCacheKey(cacheKey); + var result = new List(); + if (isCache) + result = GetListOrCache(cacheKey, this.GetTableInfoListSql); + else + result = this.Context.Ado.SqlQuery(this.GetTableInfoListSql); + foreach (var item in result) + { + item.DbObjectType = DbObjectType.Table; + } + return result; + } + public List GetColumnInfosByTableName(string tableName, Func getChangeSqlFunc) + { + var db = this.Context.CopyNew(); + db.CurrentConnectionConfig.IsAutoCloseConnection = true; + db.Aop.OnExecutingChangeSql = (sql, pars) => + { + sql = getChangeSqlFunc(this.Context.CurrentConnectionConfig.DbType, sql); + return new KeyValuePair(sql, pars); + }; + var result = db.DbMaintenance.GetColumnInfosByTableName(tableName, false); + return result; + } + public virtual List GetColumnInfosByTableName(string tableName, bool isCache = true) + { + if (string.IsNullOrEmpty(tableName)) return new List(); + string cacheKey = "DbMaintenanceProvider.GetColumnInfosByTableName." + this.SqlBuilder.GetNoTranslationColumnName(tableName).ToLower() + this.Context.CurrentConnectionConfig.ConfigId; + cacheKey = GetCacheKey(cacheKey); + var sql = string.Format(this.GetColumnInfosByTableNameSql, tableName); + if (isCache) + return GetListOrCache(cacheKey, sql).GroupBy(it => it.DbColumnName).Select(it => it.First()).ToList(); + else + return this.Context.Ado.SqlQuery(sql).GroupBy(it => it.DbColumnName).Select(it => it.First()).ToList(); + + } + public virtual List GetIsIdentities(string tableName) + { + string cacheKey = "DbMaintenanceProvider.GetIsIdentities" + this.SqlBuilder.GetNoTranslationColumnName(tableName).ToLower() + this.Context.CurrentConnectionConfig.ConfigId; + cacheKey = GetCacheKey(cacheKey); + return this.Context.Utilities.GetReflectionInoCacheInstance().GetOrCreate(cacheKey, () => + { + var result = GetColumnInfosByTableName(tableName).Where(it => it.IsIdentity).ToList(); + return result.Select(it => it.DbColumnName).ToList(); + }); + } + public virtual List GetPrimaries(string tableName) + { + string cacheKey = "DbMaintenanceProvider.GetPrimaries" + this.SqlBuilder.GetNoTranslationColumnName(tableName).ToLower() + this.Context.CurrentConnectionConfig.ConfigId; + cacheKey = GetCacheKey(cacheKey); + return this.Context.Utilities.GetReflectionInoCacheInstance().GetOrCreate(cacheKey, () => + { + var result = GetColumnInfosByTableName(tableName).Where(it => it.IsPrimarykey).ToList(); + return result.Select(it => it.DbColumnName).ToList(); + }); + } + public virtual List GetIndexList(string tableName) + { + return new List(); + } + public virtual List GetFuncList() + { + return new List(); + } + public virtual List GetTriggerNames(string tableName) + { + return new List(); + } + public virtual List GetDbTypes() + { + return new List(); + } + #endregion + + #region Check + public virtual bool IsAnyTable() + { + if (typeof(T).GetCustomAttribute() != null) + { + var tables = this.Context.SplitHelper(typeof(T)).GetTables(); + var isAny = false; + foreach (var item in tables) + { + if (this.Context.DbMaintenance.IsAnyTable(item.TableName, false)) + { + isAny = true; + break; + } + } + return isAny; + } + else + { + this.Context.InitMappingInfo(); + return this.IsAnyTable(this.Context.EntityMaintenance.GetEntityInfo().DbTableName, false); + } + } + public virtual bool IsAnyTable(string tableName, bool isCache = true) + { + Check.Exception(string.IsNullOrEmpty(tableName), "IsAnyTable tableName is not null"); + tableName = this.SqlBuilder.GetNoTranslationColumnName(tableName); + var tables = GetTableInfoList(isCache); + if (tables == null) return false; + else return tables.Any(it => it.Name.Equals(tableName, StringComparison.CurrentCultureIgnoreCase)); + } + public virtual bool IsAnyColumn(string tableName, string columnName, bool isCache = true) + { + columnName = this.SqlBuilder.GetNoTranslationColumnName(columnName); + tableName = this.SqlBuilder.GetNoTranslationColumnName(tableName); + var isAny = IsAnyTable(tableName, isCache); + Check.Exception(!isAny, string.Format("Table {0} does not exist", tableName)); + var columns = GetColumnInfosByTableName(tableName, isCache); + if (columns.IsNullOrEmpty()) return false; + return columns.Any(it => it.DbColumnName.Equals(columnName, StringComparison.CurrentCultureIgnoreCase)); + } + public virtual bool IsPrimaryKey(string tableName, string columnName) + { + columnName = this.SqlBuilder.GetNoTranslationColumnName(columnName); + var isAny = IsAnyTable(tableName); + Check.Exception(!isAny, string.Format("Table {0} does not exist", tableName)); + var columns = GetColumnInfosByTableName(tableName); + if (columns.IsNullOrEmpty()) return false; + var result = columns.Any(it => it.IsPrimarykey == true && it.DbColumnName.Equals(columnName, StringComparison.CurrentCultureIgnoreCase)); + return result; + } + public virtual bool IsPrimaryKey(string tableName, string columnName, bool isCache = true) + { + columnName = this.SqlBuilder.GetNoTranslationColumnName(columnName); + var isAny = IsAnyTable(tableName, isCache); + Check.Exception(!isAny, string.Format("Table {0} does not exist", tableName)); + var columns = GetColumnInfosByTableName(tableName, isCache); + if (columns.IsNullOrEmpty()) return false; + var result = columns.Any(it => it.IsPrimarykey == true && it.DbColumnName.Equals(columnName, StringComparison.CurrentCultureIgnoreCase)); + return result; + } + public virtual bool IsIdentity(string tableName, string columnName) + { + columnName = this.SqlBuilder.GetNoTranslationColumnName(columnName); + var isAny = IsAnyTable(tableName); + Check.Exception(!isAny, string.Format("Table {0} does not exist", tableName)); + var columns = GetColumnInfosByTableName(tableName); + if (columns.IsNullOrEmpty()) return false; + return columns.Any(it => it.IsIdentity = true && it.DbColumnName.Equals(columnName, StringComparison.CurrentCultureIgnoreCase)); + } + public virtual bool IsAnyConstraint(string constraintName) + { + return this.Context.Ado.GetInt("select object_id('" + constraintName + "')") > 0; + } + public virtual bool IsAnySystemTablePermissions() + { + this.Context.Ado.CheckConnection(); + string sql = this.CheckSystemTablePermissionsSql; + try + { + var oldIsEnableLog = this.Context.Ado.IsEnableLogEvent; + this.Context.Ado.IsEnableLogEvent = false; + this.Context.Ado.ExecuteCommand(sql); + this.Context.Ado.IsEnableLogEvent = oldIsEnableLog; + return true; + } + catch + { + return false; + } + } + #endregion + + #region DDL + public virtual bool SetAutoIncrementInitialValue(string tableName, int initialValue) + { + Console.WriteLine("no support"); + return true; + } + public virtual bool SetAutoIncrementInitialValue(Type entityType, int initialValue) + { + Console.WriteLine("no support"); + return true; + } + public virtual bool DropIndex(string indexName) + { + indexName = this.SqlBuilder.GetNoTranslationColumnName(indexName); + this.Context.Ado.ExecuteCommand($" DROP INDEX {indexName} "); + return true; + } + public virtual bool DropIndex(string indexName, string tableName) + { + indexName = this.SqlBuilder.GetNoTranslationColumnName(indexName); + tableName = this.SqlBuilder.GetNoTranslationColumnName(tableName); + this.Context.Ado.ExecuteCommand($" DROP INDEX {indexName} "); + return true; + } + public virtual bool DropView(string viewName) + { + viewName = this.SqlBuilder.GetNoTranslationColumnName(viewName); + this.Context.Ado.ExecuteCommand($" DROP VIEW {viewName} "); + return true; + } + public virtual bool DropFunction(string funcName) + { + funcName = this.SqlBuilder.GetNoTranslationColumnName(funcName); + this.Context.Ado.ExecuteCommand($" DROP FUNCTION {funcName} "); + return true; + } + public virtual bool DropProc(string procName) + { + procName = this.SqlBuilder.GetNoTranslationColumnName(procName); + this.Context.Ado.ExecuteCommand($" DROP PROCEDURE {procName} "); + return true; + } + /// + ///by current connection string + /// + /// + /// + public virtual bool CreateDatabase(string databaseDirectory = null) + { + var seChar = Path.DirectorySeparatorChar.ToString(); + if (databaseDirectory.HasValue()) + { + databaseDirectory = databaseDirectory.TrimEnd('\\').TrimEnd('/'); + } + var databaseName = this.Context.Ado.Connection.Database; + return CreateDatabase(databaseName, databaseDirectory); + } + /// + /// by databaseName + /// + /// + /// + /// + public virtual bool CreateDatabase(string databaseName, string databaseDirectory = null) + { + this.Context.Ado.ExecuteCommand(string.Format(CreateDataBaseSql, databaseName, databaseDirectory)); + return true; + } + + public virtual bool AddPrimaryKey(string tableName, string columnName) + { + tableName = this.SqlBuilder.GetTranslationTableName(tableName); + columnName = this.SqlBuilder.GetTranslationTableName(columnName); + var temp = "PK_{0}_{1}"; + if (tableName.IsContainsIn(" ", "-")) + { + temp = SqlBuilder.GetTranslationColumnName(temp); + } + string sql = string.Format(this.AddPrimaryKeySql, tableName, string.Format(temp, this.SqlBuilder.GetNoTranslationColumnName(tableName).Replace("-", "_"), this.SqlBuilder.GetNoTranslationColumnName(columnName)), columnName); + if ((tableName + columnName).Length > 25 && this.Context?.CurrentConnectionConfig?.MoreSettings?.MaxParameterNameLength > 0) + { + sql = string.Format(this.AddPrimaryKeySql, tableName, string.Format(temp, this.SqlBuilder.GetNoTranslationColumnName(tableName).GetNonNegativeHashCodeString(), "Id"), columnName); + } + this.Context.Ado.ExecuteCommand(sql); + return true; + } + + public bool AddPrimaryKeys(string tableName, string[] columnNames) + { + tableName = this.SqlBuilder.GetTranslationTableName(tableName); + var columnName = string.Join(",", columnNames); + var pkName = string.Format("PK_{0}_{1}", this.SqlBuilder.GetNoTranslationColumnName(tableName), columnName.Replace(",", "_")); + if (pkName.Length > 25 && this.Context?.CurrentConnectionConfig?.MoreSettings?.MaxParameterNameLength > 0) + { + pkName = "PK_" + pkName.GetNonNegativeHashCodeString(); + } + columnName = string.Join(",", columnNames.Select(it => SqlBuilder.GetTranslationColumnName(it))); + string sql = string.Format(this.AddPrimaryKeySql, tableName, pkName, columnName); + this.Context.Ado.ExecuteCommand(sql); + return true; + } + public bool AddPrimaryKeys(string tableName, string[] columnNames, string pkName) + { + tableName = this.SqlBuilder.GetTranslationTableName(tableName); + var columnName = string.Join(",", columnNames); + //var pkName = string.Format("PK_{0}_{1}", this.SqlBuilder.GetNoTranslationColumnName(tableName), columnName.Replace(",", "_")); + string sql = string.Format(this.AddPrimaryKeySql, tableName, pkName, columnName); + this.Context.Ado.ExecuteCommand(sql); + return true; + } + public virtual bool AddColumn(string tableName, DbColumnInfo columnInfo) + { + tableName = this.SqlBuilder.GetTranslationTableName(tableName); + var isAddNotNUll = columnInfo.IsNullable == false && columnInfo.DefaultValue.HasValue(); + if (isAddNotNUll) + { + columnInfo = this.Context.Utilities.TranslateCopy(columnInfo); + columnInfo.IsNullable = true; + } + string sql = GetAddColumnSql(tableName, columnInfo); + this.Context.Ado.ExecuteCommand(sql); + if (isAddNotNUll) + { + if (columnInfo.TableName == null) + { + columnInfo.TableName = tableName; + } + var dtColums = this.Context.Queryable().AS(columnInfo.TableName).Where("1=2") + .Select(this.SqlBuilder.GetTranslationColumnName(columnInfo.DbColumnName)).ToDataTable().Columns.Cast(); + var dtColumInfo = dtColums.First(it => it.ColumnName.EqualCase(columnInfo.DbColumnName)); + var type = UtilMethods.GetUnderType(dtColumInfo.DataType); + var value = type == UtilConstants.StringType ? (object)"" : Activator.CreateInstance(type); + if (this.Context.CurrentConnectionConfig.DbType == DbType.Oracle) + { + value = columnInfo.DefaultValue; + if (value.Equals("")) + { + value = "empty"; + } + } + value = GetDefaultValue(columnInfo, value); + var dt = new Dictionary(); + dt.Add(columnInfo.DbColumnName, value); + if (columnInfo.DataType.EqualCase("json") && columnInfo.DefaultValue?.Contains('}') == true) + { + { + dt[columnInfo.DbColumnName] = "{}"; + var sqlobj = this.Context.Updateable(dt) + .AS(tableName) + .Where($"{this.SqlBuilder.GetTranslationColumnName(columnInfo.DbColumnName)} is null ").ToSql(); + sqlobj.Value[0].IsJson = true; + this.Context.Ado.ExecuteCommand(sqlobj.Key, sqlobj.Value); + } + } + else if (columnInfo.DataType.EqualCase("json") && columnInfo.DefaultValue?.Contains(']') == true) + { + { + dt[columnInfo.DbColumnName] = "[]"; + var sqlobj = this.Context.Updateable(dt) + .AS(tableName) + .Where($"{this.SqlBuilder.GetTranslationColumnName(columnInfo.DbColumnName)} is null ").ToSql(); + sqlobj.Value[0].IsJson = true; + this.Context.Ado.ExecuteCommand(sqlobj.Key, sqlobj.Value); + } + } + else + { + this.Context.Updateable(dt) + .AS(tableName) + .Where($"{this.SqlBuilder.GetTranslationColumnName(columnInfo.DbColumnName)} is null ").ExecuteCommand(); + } + columnInfo.IsNullable = false; + UpdateColumn(tableName, columnInfo); + } + return true; + } + public virtual object GetDefaultValue(DbColumnInfo columnInfo, object value) + { + if (columnInfo.DataType.ObjToString().ToLower().IsIn("varchar", "nvarchar", "varchar2", "nvarchar2") && !string.IsNullOrEmpty(columnInfo.DefaultValue) && Regex.IsMatch(columnInfo.DefaultValue, @"^\w+$")) + { + value = columnInfo.DefaultValue; + } + else if (columnInfo.DataType.ObjToString().ToLower().IsIn("float", "double", "decimal", "int", "int4", "bigint", "int8", "int2") && columnInfo.DefaultValue.IsInt()) + { + value = Convert.ToInt32(columnInfo.DefaultValue); + } + return value; + } + public virtual bool UpdateColumn(string tableName, DbColumnInfo column) + { + tableName = this.SqlBuilder.GetTranslationTableName(tableName); + string sql = GetUpdateColumnSql(tableName, column); + this.Context.Ado.ExecuteCommand(sql); + return true; + } + public abstract bool CreateTable(string tableName, List columns, bool isCreatePrimaryKey = true); + public virtual bool DropTable(string tableName) + { + tableName = this.SqlBuilder.GetTranslationTableName(tableName); + this.Context.Ado.ExecuteCommand(string.Format(this.DropTableSql, tableName)); + return true; + } + public virtual bool DropTable(string[] tableName) + { + foreach (var item in tableName) + { + DropTable(item); + } + return true; + } + public virtual bool DropTable(Type[] tableEnittyTypes) + { + foreach (var item in tableEnittyTypes) + { + var tableName = this.Context.EntityMaintenance.GetTableName(item); + DropTable(tableName); + } + return true; + } + public virtual bool DropTable() + { + if (typeof(T).GetCustomAttribute() != null) + { + var tables = this.Context.SplitHelper(typeof(T)).GetTables(); + foreach (var item in tables) + { + this.Context.DbMaintenance.DropTable(SqlBuilder.GetTranslationColumnName(item.TableName)); + } + return true; + } + else + { + var tableName = this.Context.EntityMaintenance.GetTableName(); + return DropTable(tableName); + } + } + public virtual bool DropTable() + { + DropTable(); + DropTable(); + return true; + } + public virtual bool DropTable() + { + DropTable(); + DropTable(); + DropTable(); + return true; + } + public virtual bool DropTable() + { + DropTable(); + DropTable(); + DropTable(); + DropTable(); + return true; + } + public virtual bool TruncateTable() + { + if (typeof(T).GetCustomAttribute() != null) + { + var tables = this.Context.SplitHelper(typeof(T)).GetTables(); + foreach (var item in tables) + { + this.Context.DbMaintenance.TruncateTable(SqlBuilder.GetTranslationColumnName(item.TableName)); + } + return true; + } + else + { + this.Context.InitMappingInfo(); + return this.TruncateTable(this.Context.EntityMaintenance.GetEntityInfo().DbTableName); + } + } + public virtual bool TruncateTable() + { + TruncateTable(); + TruncateTable(); + return true; + } + public virtual bool TruncateTable() + { + TruncateTable(); + TruncateTable(); + TruncateTable(); + return true; + } + public virtual bool TruncateTable() + { + TruncateTable(); + TruncateTable(); + TruncateTable(); + TruncateTable(); + return true; + } + public virtual bool TruncateTable() + { + TruncateTable(); + TruncateTable(); + TruncateTable(); + TruncateTable(); + TruncateTable(); + return true; + } + public virtual bool DropColumn(string tableName, string columnName) + { + columnName = this.SqlBuilder.GetTranslationColumnName(columnName); + tableName = this.SqlBuilder.GetTranslationTableName(tableName); + this.Context.Ado.ExecuteCommand(string.Format(this.DropColumnToTableSql, tableName, columnName)); + return true; + } + public virtual bool DropConstraint(string tableName, string constraintName) + { + tableName = this.SqlBuilder.GetTranslationTableName(tableName); + string sql = string.Format(this.DropConstraintSql, tableName, constraintName); + this.Context.Ado.ExecuteCommand(sql); + return true; + } + public virtual bool TruncateTable(string tableName) + { + tableName = this.SqlBuilder.GetTranslationTableName(tableName); + this.Context.Ado.ExecuteCommand(string.Format(this.TruncateTableSql, tableName)); + return true; + } + public bool TruncateTable(params string[] tableNames) + { + foreach (var item in tableNames) + { + TruncateTable(item); + } + return true; + } + public bool TruncateTable(params Type[] tableEnittyTypes) + { + foreach (var item in tableEnittyTypes) + { + var name = this.Context.EntityMaintenance.GetTableName(item); + TruncateTable(name); + } + return true; + } + public virtual bool BackupDataBase(string databaseName, string fullFileName) + { + var directory = FileHelper.GetDirectoryFromFilePath(fullFileName); + if (!FileHelper.IsExistDirectory(directory)) + { + FileHelper.CreateDirectory(directory); + } + this.Context.Ado.ExecuteCommand(string.Format(this.BackupDataBaseSql, databaseName, fullFileName)); + return true; + } + public virtual bool BackupTable(string oldTableName, string newTableName, int maxBackupDataRows = int.MaxValue) + { + oldTableName = this.SqlBuilder.GetTranslationTableName(oldTableName); + newTableName = this.SqlBuilder.GetTranslationTableName(newTableName); + string sql = string.Format(this.BackupTableSql, maxBackupDataRows, newTableName, oldTableName); + this.Context.Ado.ExecuteCommand(sql); + return true; + } + public virtual bool RenameColumn(string tableName, string oldColumnName, string newColumnName) + { + tableName = this.SqlBuilder.GetTranslationTableName(tableName); + oldColumnName = this.SqlBuilder.GetTranslationColumnName(oldColumnName); + newColumnName = this.SqlBuilder.GetTranslationColumnName(newColumnName); + string sql = string.Format(this.RenameColumnSql, tableName, oldColumnName, newColumnName); + this.Context.Ado.ExecuteCommand(sql); + return true; + } + public virtual bool AddColumnRemark(string columnName, string tableName, string description) + { + string sql = string.Format(this.AddColumnRemarkSql, columnName, tableName, description); + this.Context.Ado.ExecuteCommand(sql); + return true; + } + public virtual bool DeleteColumnRemark(string columnName, string tableName) + { + string sql = string.Format(this.DeleteColumnRemarkSql, columnName, tableName); + this.Context.Ado.ExecuteCommand(sql); + return true; + } + public virtual bool IsAnyColumnRemark(string columnName, string tableName) + { + string sql = string.Format(this.IsAnyColumnRemarkSql, columnName, tableName); + var dt = this.Context.Ado.GetDataTable(sql); + return dt.Rows?.Count > 0; + } + public virtual bool AddTableRemark(string tableName, string description) + { + string sql = string.Format(this.AddTableRemarkSql, tableName, description); + this.Context.Ado.ExecuteCommand(sql); + return true; + } + public virtual bool DeleteTableRemark(string tableName) + { + string sql = string.Format(this.DeleteTableRemarkSql, tableName); + this.Context.Ado.ExecuteCommand(sql); + return true; + } + public virtual bool IsAnyTableRemark(string tableName) + { + string sql = string.Format(this.IsAnyTableRemarkSql, tableName); + var dt = this.Context.Ado.GetDataTable(sql); + return dt.Rows?.Count > 0; + } + public virtual bool AddDefaultValue(string tableName, string columnName, string defaultValue) + { + if (defaultValue == "''") + { + defaultValue = ""; + } + if (defaultValue.IsDate() && !AddDefaultValueSql.Contains("'{2}'")) + { + defaultValue = "'" + defaultValue + "'"; + } + if (defaultValue?.EqualCase("'current_timestamp'") == true) + { + defaultValue = defaultValue.TrimEnd('\'').TrimStart('\''); + } + if (defaultValue?.EqualCase("'current_date'") == true) + { + defaultValue = defaultValue.TrimEnd('\'').TrimStart('\''); + } + string sql = string.Format(AddDefaultValueSql, tableName, columnName, defaultValue); + this.Context.Ado.ExecuteCommand(sql); + return true; + } + public virtual bool CreateIndex(string tableName, string[] columnNames, bool isUnique = false) + { + string sql = string.Format(CreateIndexSql, this.SqlBuilder.GetTranslationTableName(tableName), string.Join(",", columnNames.Select(it => this.SqlBuilder.GetTranslationColumnName(it))), string.Join("_", columnNames) + this.Context.CurrentConnectionConfig.IndexSuffix, isUnique ? "UNIQUE" : ""); + sql = sql.Replace("_" + this.SqlBuilder.SqlTranslationLeft, "_"); + sql = sql.Replace(this.SqlBuilder.SqlTranslationRight + "_", "_"); + sql = sql.Replace(this.SqlBuilder.SqlTranslationLeft + this.SqlBuilder.SqlTranslationLeft, this.SqlBuilder.SqlTranslationLeft); + sql = sql.Replace(this.SqlBuilder.SqlTranslationRight + this.SqlBuilder.SqlTranslationRight, this.SqlBuilder.SqlTranslationRight); + this.Context.Ado.ExecuteCommand(sql); + return true; + } + public virtual bool CreateUniqueIndex(string tableName, string[] columnNames) + { + string sql = string.Format(CreateIndexSql, this.SqlBuilder.GetTranslationTableName(tableName), string.Join(",", columnNames.Select(it => this.SqlBuilder.GetTranslationColumnName(it))), string.Join("_", columnNames) + this.Context.CurrentConnectionConfig.IndexSuffix + "_Unique", "UNIQUE"); + sql = sql.Replace("_" + this.SqlBuilder.SqlTranslationLeft, "_"); + sql = sql.Replace(this.SqlBuilder.SqlTranslationRight + "_", "_"); + sql = sql.Replace(this.SqlBuilder.SqlTranslationLeft + this.SqlBuilder.SqlTranslationLeft, this.SqlBuilder.SqlTranslationLeft); + sql = sql.Replace(this.SqlBuilder.SqlTranslationRight + this.SqlBuilder.SqlTranslationRight, this.SqlBuilder.SqlTranslationRight); + this.Context.Ado.ExecuteCommand(sql); + return true; + } + public virtual bool CreateIndex(string tableName, string[] columnNames, string IndexName, bool isUnique = false) + { + var include = ""; + if (IndexName.Contains("{include:", StringComparison.CurrentCultureIgnoreCase)) + { + include = Regex.Match(IndexName, @"\{include\:.+$").Value; + IndexName = IndexName.Replace(include, ""); + if (include == null) + { + throw new Exception("include format error"); + } + include = include.Replace("{include:", "").Replace("}", ""); + include = $"include({include})"; + } + string sql = string.Format("CREATE {3} INDEX {2} ON {0}({1})" + include, this.SqlBuilder.GetTranslationColumnName(tableName), string.Join(",", columnNames), IndexName, isUnique ? "UNIQUE" : ""); + this.Context.Ado.ExecuteCommand(sql); + return true; + } + public virtual bool IsAnyIndex(string indexName) + { + //string sql = string.Format(this.IsAnyIndexSql, indexName); + string sql = string.Format(this.IsAnyIndexSql, indexName, this.Context.Ado.Connection.Database); + return this.Context.Ado.GetInt(sql) > 0; + } + public virtual bool AddRemark(EntityInfo entity) + { + var db = this.Context; + var columns = entity.Columns.Where(it => it.IsIgnore == false).ToList(); + List dbColumn = new List(); + if (entity.Columns.Any(it => it.ColumnDescription.HasValue())) + { + dbColumn = db.DbMaintenance.GetColumnInfosByTableName(entity.DbTableName, false); + } + foreach (var item in columns) + { + if (item.ColumnDescription != null) + { + //column remak + if (db.DbMaintenance.IsAnyColumnRemark(item.DbColumnName, item.DbTableName)) + { + if (!dbColumn.Any(it => it.DbColumnName == item.DbColumnName && it.ColumnDescription == item.ColumnDescription)) + { + db.DbMaintenance.DeleteColumnRemark(item.DbColumnName, item.DbTableName); + db.DbMaintenance.AddColumnRemark(item.DbColumnName, item.DbTableName, item.ColumnDescription); + } + } + else + { + db.DbMaintenance.AddColumnRemark(item.DbColumnName, item.DbTableName, item.ColumnDescription); + } + } + } + + //table remak + if (entity.TableDescription != null) + { + if (db.DbMaintenance.IsAnyTableRemark(entity.DbTableName)) + { + db.DbMaintenance.DeleteTableRemark(entity.DbTableName); + db.DbMaintenance.AddTableRemark(entity.DbTableName, entity.TableDescription); + } + else + { + db.DbMaintenance.AddTableRemark(entity.DbTableName, entity.TableDescription); + } + } + return true; + } + + public virtual void AddIndex(EntityInfo entityInfo) + { + var db = this.Context; + var columns = entityInfo.Columns.Where(it => it.IsIgnore == false).ToList(); + var indexColumns = columns.Where(it => it.IndexGroupNameList.HasValue()).ToList(); + if (indexColumns.HasValue()) + { + var groups = indexColumns.SelectMany(it => it.IndexGroupNameList).GroupBy(it => it).Select(it => it.Key).ToList(); + foreach (var item in groups) + { + var columnNames = indexColumns.Where(it => it.IndexGroupNameList.Any(i => i.Equals(item, StringComparison.CurrentCultureIgnoreCase))).Select(it => it.DbColumnName).ToArray(); + var indexName = string.Format("Index_{0}_{1}" + this.Context.CurrentConnectionConfig.IndexSuffix, entityInfo.DbTableName, string.Join("_", columnNames)); + if (!IsAnyIndex(indexName)) + { + CreateIndex(entityInfo.DbTableName, columnNames); + } + } + } + + + var uIndexColumns = columns.Where(it => it.UIndexGroupNameList.HasValue()).ToList(); + if (uIndexColumns.HasValue()) + { + var groups = uIndexColumns.SelectMany(it => it.UIndexGroupNameList).GroupBy(it => it).Select(it => it.Key).ToList(); + foreach (var item in groups) + { + var columnNames = uIndexColumns.Where(it => it.UIndexGroupNameList.Any(i => i.Equals(item, StringComparison.CurrentCultureIgnoreCase))).Select(it => it.DbColumnName).ToArray(); + var indexName = string.Format("Index_{0}_{1}_Unique" + this.Context.CurrentConnectionConfig.IndexSuffix, entityInfo.DbTableName, string.Join("_", columnNames)); + if (!IsAnyIndex(indexName)) + { + CreateUniqueIndex(entityInfo.DbTableName, columnNames); + } + } + } + } + + protected virtual bool IsAnyDefaultValue(string tableName, string columnName, List columns) + { + var defaultValue = columns.Where(it => it.DbColumnName.Equals(columnName, StringComparison.CurrentCultureIgnoreCase)).First().DefaultValue; + return defaultValue.HasValue(); + } + + public virtual bool IsAnyDefaultValue(string tableName, string columnName) + { + return IsAnyDefaultValue(tableName, columnName, this.GetColumnInfosByTableName(tableName, false)); + } + + public virtual void AddDefaultValue(EntityInfo entityInfo) + { + var dbColumns = this.GetColumnInfosByTableName(entityInfo.DbTableName, false); + var db = this.Context; + var columns = entityInfo.Columns.Where(it => it.IsIgnore == false).ToList(); + foreach (var item in columns) + { + if (item.DefaultValue != null) + { + if (!IsAnyDefaultValue(entityInfo.DbTableName, item.DbColumnName, dbColumns)) + { + this.AddDefaultValue(entityInfo.DbTableName, item.DbColumnName, item.DefaultValue); + } + } + } + } + + public virtual bool RenameTable(string oldTableName, string newTableName) + { + string sql = string.Format(this.RenameTableSql, oldTableName, newTableName); + this.Context.Ado.ExecuteCommand(sql); + return true; + } + public virtual bool IsAnyProcedure(string procName) + { + string sql = string.Format(this.IsAnyProcedureSql, procName); + return this.Context.Ado.GetInt(sql) > 0; + } + #endregion + + #region Private + public virtual List GetSchemaTables(EntityInfo entityInfo) + { + return null; + } + protected List GetListOrCache(string cacheKey, string sql) + { + return this.Context.Utilities.GetReflectionInoCacheInstance().GetOrCreate(cacheKey, + () => + { + var isEnableLogEvent = this.Context.Ado.IsEnableLogEvent; + this.Context.Ado.IsEnableLogEvent = false; + var result = this.Context.Ado.SqlQuery(sql); + this.Context.Ado.IsEnableLogEvent = isEnableLogEvent; + return result; + }); + } + protected virtual string GetCreateTableSql(string tableName, List columns) + { + List columnArray = new List(); + Check.Exception(columns.IsNullOrEmpty(), "No columns found "); + foreach (var item in columns) + { + string columnName = this.SqlBuilder.GetTranslationTableName(item.DbColumnName); + string dataType = item.DataType; + string dataSize = GetSize(item); + string nullType = item.IsNullable ? this.CreateTableNull : CreateTableNotNull; + string primaryKey = null; + string identity = item.IsIdentity ? this.CreateTableIdentity : null; + string addItem = string.Format(this.CreateTableColumn, columnName, dataType, dataSize, nullType, primaryKey, identity); + columnArray.Add(addItem); + } + string tableString = string.Format(this.CreateTableSql, this.SqlBuilder.GetTranslationTableName(tableName), string.Join(",\r\n", columnArray)); + return tableString; + } + protected virtual string GetAddColumnSql(string tableName, DbColumnInfo columnInfo) + { + string columnName = this.SqlBuilder.GetTranslationColumnName(columnInfo.DbColumnName); + tableName = this.SqlBuilder.GetTranslationTableName(tableName); + string dataType = columnInfo.DataType; + if (dataType.EqualCase("varchar") + && this.Context.CurrentConnectionConfig?.MoreSettings?.SqlServerCodeFirstNvarchar == true + && this.Context.CurrentConnectionConfig?.DbType == DbType.SqlServer) + { + dataType = "nvarchar"; + } + string dataSize = GetSize(columnInfo); + string nullType = columnInfo.IsNullable ? this.CreateTableNull : CreateTableNotNull; + string primaryKey = null; + string identity = null; + string result = string.Format(this.AddColumnToTableSql, tableName, columnName, dataType, dataSize, nullType, primaryKey, identity); + return result; + } + protected virtual string GetUpdateColumnSql(string tableName, DbColumnInfo columnInfo) + { + string columnName = this.SqlBuilder.GetTranslationColumnName(columnInfo.DbColumnName); + tableName = this.SqlBuilder.GetTranslationTableName(tableName); + string dataSize = GetSize(columnInfo); + string dataType = columnInfo.DataType; + string nullType = columnInfo.IsNullable ? this.CreateTableNull : CreateTableNotNull; + string primaryKey = null; + string identity = null; + string result = string.Format(this.AlterColumnToTableSql, tableName, columnName, dataType, dataSize, nullType, primaryKey, identity); + return result; + } + protected virtual string GetCacheKey(string cacheKey) + { + return this.Context.CurrentConnectionConfig.DbType + "." + this.Context.Ado.Connection.Database + "." + cacheKey; + } + protected virtual string GetSize(DbColumnInfo item) + { + string dataSize = null; + var isMax = item.Length > 4000 || item.Length == -1; + if (isMax) + { + dataSize = item.Length > 0 ? string.Format("({0})", "max") : null; + } + else if (item.Length == 0 && item.DecimalDigits > 0) + { + item.Length = 10; + dataSize = string.Format("({0},{1})", item.Length, item.DecimalDigits); + } + else if (item.Length > 0 && item.DecimalDigits == 0) + { + dataSize = item.Length > 0 ? string.Format("({0})", item.Length) : null; + } + else if (item.Length > 0 && item.DecimalDigits > 0) + { + dataSize = item.Length > 0 ? string.Format("({0},{1})", item.Length, item.DecimalDigits) : null; + } + return dataSize; + } + #endregion + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DbMaintenanceProvider/Properties.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DbMaintenanceProvider/Properties.cs new file mode 100644 index 000000000..76ec2f787 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DbMaintenanceProvider/Properties.cs @@ -0,0 +1,67 @@ +namespace SqlSugar +{ + public abstract partial class DbMaintenanceProvider : IDbMaintenance + { + #region Context + private ISqlBuilder _SqlBuilder; + public SqlSugarProvider Context { get; set; } + public ISqlBuilder SqlBuilder + { + get + { + if (_SqlBuilder == null) + { + _SqlBuilder = InstanceFactory.GetSqlbuilder(this.Context.CurrentConnectionConfig); + _SqlBuilder.Context = this.Context; + } + return _SqlBuilder; + } + } + #endregion + + #region DML + protected abstract string GetViewInfoListSql { get; } + protected abstract string GetDataBaseSql { get; } + protected abstract string GetTableInfoListSql { get; } + protected abstract string GetColumnInfosByTableNameSql { get; } + #endregion + + #region DDL + protected abstract string CreateIndexSql { get; } + protected abstract string IsAnyIndexSql { get; } + protected abstract string AddDefaultValueSql { get; } + protected abstract string CreateDataBaseSql { get; } + protected abstract string AddColumnToTableSql { get; } + protected abstract string AlterColumnToTableSql { get; } + protected abstract string BackupDataBaseSql { get; } + protected abstract string CreateTableSql { get; } + protected abstract string CreateTableColumn { get; } + protected abstract string BackupTableSql { get; } + protected abstract string TruncateTableSql { get; } + protected abstract string DropTableSql { get; } + protected abstract string DropColumnToTableSql { get; } + protected abstract string DropConstraintSql { get; } + protected abstract string AddPrimaryKeySql { get; } + protected abstract string RenameColumnSql { get; } + protected abstract string AddColumnRemarkSql { get; } + protected abstract string DeleteColumnRemarkSql { get; } + protected abstract string IsAnyColumnRemarkSql { get; } + protected abstract string AddTableRemarkSql { get; } + protected abstract string DeleteTableRemarkSql { get; } + protected abstract string IsAnyTableRemarkSql { get; } + protected abstract string RenameTableSql { get; } + protected virtual string IsAnyProcedureSql { get; } + #endregion + + #region Check + protected abstract string CheckSystemTablePermissionsSql { get; } + #endregion + + #region Scattered + protected abstract string CreateTableNull { get; } + protected abstract string CreateTableNotNull { get; } + protected abstract string CreateTablePirmaryKey { get; } + protected abstract string CreateTableIdentity { get; } + #endregion + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DeleteProvider/DeleteMethodInfo.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DeleteProvider/DeleteMethodInfo.cs new file mode 100644 index 000000000..155fc94a7 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DeleteProvider/DeleteMethodInfo.cs @@ -0,0 +1,61 @@ +using System.Reflection; + +namespace SqlSugar +{ + public class DeleteMethodInfo + { + internal SqlSugarProvider Context { get; set; } + internal MethodInfo MethodInfo { get; set; } + internal object objectValue { get; set; } + + public int ExecuteCommand() + { + if (Context == null) return 0; + var inertable = MethodInfo.Invoke(Context, new object[] { objectValue }); + var result = inertable.GetType().GetMethod("ExecuteCommand").Invoke(inertable, Array.Empty()); + return (int)result; + } + public async Task ExecuteCommandAsync() + { + if (Context == null) return 0; + var inertable = MethodInfo.Invoke(Context, new object[] { objectValue }); + var result = inertable.GetType().GetMyMethod("ExecuteCommandAsync", 0).Invoke(inertable, Array.Empty()); + return await ((Task)result).ConfigureAwait(false); + } + + public CommonMethodInfo AS(string tableName) + { + var inertable = MethodInfo.Invoke(Context, new object[] { objectValue }); + var newMethod = inertable.GetType().GetMyMethod("AS", 1, typeof(string)); + var result = newMethod.Invoke(inertable, new object[] { tableName }); + return new CommonMethodInfo() + { + Context = result + }; + } + public CommonMethodInfo EnableDiffLogEvent(object businessData = null) + { + if (Context == null) + { + return new CommonMethodInfo(); + } + var inertable = MethodInfo.Invoke(Context, new object[] { objectValue }); + var newMethod = inertable.GetType().GetMyMethod("EnableDiffLogEvent", 1, typeof(object)); + var result = newMethod.Invoke(inertable, new object[] { businessData }); + return new CommonMethodInfo() + { + Context = result + }; + } + public CommonMethodInfo SplitTable() + { + var inertable = MethodInfo.Invoke(Context, new object[] { objectValue }); + var newMethod = inertable.GetType().GetMyMethod("SplitTable", 0); + var result = newMethod.Invoke(inertable, Array.Empty()); + return new CommonMethodInfo() + { + Context = result + }; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DeleteProvider/DeleteNavMethodInfo.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DeleteProvider/DeleteNavMethodInfo.cs new file mode 100644 index 000000000..e7ab7658c --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DeleteProvider/DeleteNavMethodInfo.cs @@ -0,0 +1,49 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public class DeleteNavMethodInfo + { + internal object MethodInfos { get; set; } + internal SqlSugarProvider Context { get; set; } + + public DeleteNavMethodInfo IncludeByNameString(string navMemberName, DeleteNavOptions deleteNavOptions = null) + { + var type = MethodInfos.GetType().GetGenericArguments()[0]; + var entityInfo = this.Context.EntityMaintenance.GetEntityInfo(type); + Type properyItemType; + bool isList; + Expression exp = UtilMethods.GetIncludeExpression(navMemberName, entityInfo, out properyItemType, out isList); + var method = this.MethodInfos.GetType().GetMyMethod("Include", 2, isList) + .MakeGenericMethod(properyItemType); + var obj = method.Invoke(this.MethodInfos, new object[] { exp, deleteNavOptions }); + this.MethodInfos = obj; + return this; + } + public DeleteNavMethodInfo ThenIncludeByNameString(string navMemberName, DeleteNavOptions deleteNavOptions = null) + { + var type = MethodInfos.GetType().GetGenericArguments()[1]; + var entityInfo = this.Context.EntityMaintenance.GetEntityInfo(type); + Type properyItemType; + bool isList; + Expression exp = UtilMethods.GetIncludeExpression(navMemberName, entityInfo, out properyItemType, out isList); + var method = this.MethodInfos.GetType().GetMyMethod("ThenInclude", 2, isList) + .MakeGenericMethod(properyItemType); + var obj = method.Invoke(this.MethodInfos, new object[] { exp, deleteNavOptions }); + this.MethodInfos = obj; + return this; + } + public async Task ExecuteCommandAsync() + { + if (Context == null) return false; + var result = MethodInfos.GetType().GetMethod("ExecuteCommandAsync").Invoke(MethodInfos, Array.Empty()); + return await ((Task)result).ConfigureAwait(false); + } + public bool ExecuteCommand() + { + if (Context == null) return false; + var result = MethodInfos.GetType().GetMethod("ExecuteCommand").Invoke(MethodInfos, Array.Empty()); + return (bool)result; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DeleteProvider/DeleteablePage.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DeleteProvider/DeleteablePage.cs new file mode 100644 index 000000000..08f7b6790 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DeleteProvider/DeleteablePage.cs @@ -0,0 +1,81 @@ +namespace SqlSugar +{ + public class DeleteablePage where T : class, new() + { + public T[] DataList { get; set; } + public ISqlSugarClient Context { get; set; } + public int PageSize { get; internal set; } + public string TableName { get; internal set; } + public bool IsEnableDiffLogEvent { get; internal set; } + public DiffLogModel DiffModel { get; internal set; } + public List UpdateColumns { get; internal set; } + public int ExecuteCommand() + { + if (DataList.Length == 1 && DataList.First() == null) + { + return 0; + } + if (PageSize == 0) { PageSize = 1000; } + var result = 0; + var isNoTran = this.Context.Ado.IsNoTran(); + try + { + if (isNoTran) + { + this.Context.Ado.BeginTran(); + } + this.Context.Utilities.PageEach(DataList, PageSize, pageItem => + { + result += this.Context.Deleteable(pageItem).AS(TableName).EnableDiffLogEventIF(IsEnableDiffLogEvent, DiffModel).ExecuteCommand(); + }); + if (isNoTran) + { + this.Context.Ado.CommitTran(); + } + } + catch (Exception) + { + if (isNoTran) + { + this.Context.Ado.RollbackTran(); + } + throw; + } + return result; + } + public async Task ExecuteCommandAsync() + { + if (DataList.Length == 1 && DataList.First() == null) + { + return 0; + } + if (PageSize == 0) { PageSize = 1000; } + var result = 0; + var isNoTran = this.Context.Ado.IsNoTran(); + try + { + if (isNoTran) + { + await Context.Ado.BeginTranAsync().ConfigureAwait(false); + } + await Context.Utilities.PageEachAsync(DataList, PageSize, async pageItem => + { + result += await Context.Deleteable(pageItem).AS(TableName).EnableDiffLogEventIF(IsEnableDiffLogEvent, DiffModel).ExecuteCommandAsync().ConfigureAwait(false); + }).ConfigureAwait(false); + if (isNoTran) + { + await Context.Ado.CommitTranAsync().ConfigureAwait(false); + } + } + catch (Exception) + { + if (isNoTran) + { + await Context.Ado.RollbackTranAsync().ConfigureAwait(false); + } + throw; + } + return result; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DeleteProvider/DeleteableProvider.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DeleteProvider/DeleteableProvider.cs new file mode 100644 index 000000000..160472003 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DeleteProvider/DeleteableProvider.cs @@ -0,0 +1,781 @@ +using System.Collections; +using System.Data; +using System.Linq.Expressions; +using System.Text; +using System.Text.RegularExpressions; + +namespace SqlSugar +{ + public class DeleteableProvider : IDeleteable where T : class, new() + { + public ISqlSugarClient Context { get; set; } + public IAdo Db { get { return Context.Ado; } } + public ISqlBuilder SqlBuilder { get; set; } + public DeleteBuilder DeleteBuilder { get; set; } + public MappingTableList OldMappingTableList { get; set; } + public bool IsAs { get; set; } + public bool IsEnableDiffLogEvent { get; set; } + public DiffLogModel diffModel { get; set; } + public List tempPrimaryKeys { get; set; } + internal Action RemoveCacheFunc { get; set; } + public List DeleteObjects { get; set; } + public EntityInfo EntityInfo + { + get + { + return this.Context.EntityMaintenance.GetEntityInfo(); + } + } + public void AddQueue() + { + var sqlObj = this.ToSql(); + this.Context.Queues.Add(sqlObj.Key, sqlObj.Value); + } + public int ExecuteCommand() + { + string sql; + SugarParameter[] paramters; + _ExecuteCommand(out sql, out paramters); + var result = Db.ExecuteCommand(sql, paramters); + After(sql); + return result; + } + public bool ExecuteCommandHasChange() + { + return ExecuteCommand() > 0; + } + public Task ExecuteCommandAsync(CancellationToken token) + { + this.Context.Ado.CancellationToken = token; + return ExecuteCommandAsync(); + } + public async Task ExecuteCommandAsync() + { + string sql; + SugarParameter[] paramters; + _ExecuteCommand(out sql, out paramters); + var result = await Db.ExecuteCommandAsync(sql, paramters).ConfigureAwait(false); + After(sql); + return result; + } + public async Task ExecuteCommandHasChangeAsync() + { + return await ExecuteCommandAsync().ConfigureAwait(false) > 0; + } + public IDeleteable AsType(Type tableNameType) + { + return AS(this.Context.EntityMaintenance.GetEntityInfo(tableNameType).DbTableName); + } + public IDeleteable AS(string tableName) + { + if (tableName == null) return this; + //var entityName = typeof(T).Name; + //IsAs = true; + //OldMappingTableList = this.Context.MappingTables; + //this.Context.MappingTables = this.Context.Utilities.TranslateCopy(this.Context.MappingTables); + //if (this.Context.MappingTables.Any(it => it.EntityName == entityName)) + //{ + // this.Context.MappingTables.Add(this.Context.MappingTables.First(it => it.EntityName == entityName).DbTableName, tableName); + //} + //this.Context.MappingTables.Add(entityName, tableName); + this.DeleteBuilder.AsName = tableName; + return this; ; + } + public IDeleteable EnableDiffLogEventIF(bool isEnableDiffLogEvent, object businessData = null) + { + if (isEnableDiffLogEvent) + { + return EnableDiffLogEvent(businessData); + } + else + { + return this; + } + } + public IDeleteable EnableDiffLogEvent(object businessData = null) + { + + diffModel = new DiffLogModel(); + this.IsEnableDiffLogEvent = true; + diffModel.BusinessData = businessData; + diffModel.DiffType = DiffType.delete; + return this; + } + + public IDeleteable Where(List deleteObjs) + { + this.DeleteObjects = deleteObjs; + if (deleteObjs == null || deleteObjs.Count == 0) + { + Where(SqlBuilder.SqlFalse); + return this; + } + DataAop(deleteObjs); + string tableName = this.Context.EntityMaintenance.GetTableName(); + var primaryFields = this.GetPrimaryKeys(); + var isSinglePrimaryKey = primaryFields.Count == 1; + Check.Exception(primaryFields.IsNullOrEmpty(), string.Format("Table {0} with no primarykey", tableName)); + if (isSinglePrimaryKey) + { + List primaryKeyValues = new List(); + var primaryField = primaryFields.Single(); + foreach (var deleteObj in deleteObjs) + { + var entityPropertyName = this.Context.EntityMaintenance.GetPropertyName(primaryField); + var columnInfo = EntityInfo.Columns.Single(it => it.PropertyName.Equals(entityPropertyName, StringComparison.CurrentCultureIgnoreCase)); + var value = columnInfo.PropertyInfo.GetValue(deleteObj, null); + value = UtilMethods.GetConvertValue(value); + if (this.Context.CurrentConnectionConfig?.MoreSettings?.TableEnumIsString != true && + columnInfo.SqlParameterDbType == null && + columnInfo.PropertyInfo.PropertyType.IsEnum()) + { + value = Convert.ToInt64(value); + } + primaryKeyValues.Add(value); + } + if (this.Context.CurrentConnectionConfig.DbType == DbType.Oracle && primaryKeyValues.Count >= 1000) + { + List inItems = new List(); + this.Context.Utilities.PageEach(primaryKeyValues, 999, pageItems => + { + var inValueString = pageItems.ToArray().ToJoinSqlInVals(); + var whereItem = string.Format(DeleteBuilder.WhereInTemplate, SqlBuilder.GetTranslationColumnName(primaryFields.Single()), inValueString); + inItems.Add(whereItem); + }); + Where($"({string.Join(" OR ", inItems)})"); + } + else if (primaryKeyValues.Count < 10000) + { + var inValueString = primaryKeyValues.ToArray().ToJoinSqlInVals(); + Where(string.Format(DeleteBuilder.WhereInTemplate, SqlBuilder.GetTranslationColumnName(primaryFields.Single()), inValueString)); + } + else + { + if (DeleteBuilder.BigDataInValues == null) + DeleteBuilder.BigDataInValues = new List(); + DeleteBuilder.BigDataInValues.AddRange(primaryKeyValues); + DeleteBuilder.BigDataFiled = primaryField; + } + } + else + { + StringBuilder whereInSql = new StringBuilder(); + foreach (var deleteObj in deleteObjs) + { + StringBuilder orString = new StringBuilder(); + var isFirst = deleteObjs.IndexOf(deleteObj) == 0; + if (!isFirst) + { + orString.Append(DeleteBuilder.WhereInOrTemplate + UtilConstants.Space); + } + int i = 0; + StringBuilder andString = new StringBuilder(); + foreach (var primaryField in primaryFields) + { + if (i != 0) + andString.Append(DeleteBuilder.WhereInAndTemplate + UtilConstants.Space); + //var entityPropertyName = this.EntityInfo.Columns.Single(it=>it.PropertyName.EqualCase(primaryField)||it.DbColumnName.EqualCase(primaryField)).PropertyName; + var columnInfo = EntityInfo.Columns.Single(t => t.PropertyName.EqualCase(primaryField) || t.DbColumnName.EqualCase(primaryField)); + var entityValue = columnInfo.PropertyInfo.GetValue(deleteObj, null); + if (this.Context.CurrentConnectionConfig?.MoreSettings?.TableEnumIsString != true && + columnInfo.SqlParameterDbType == null && + columnInfo.PropertyInfo.PropertyType.IsEnum()) + { + entityValue = Convert.ToInt64(entityValue); + } + var tempequals = DeleteBuilder.WhereInEqualTemplate; + if (this.Context.CurrentConnectionConfig.MoreSettings?.DisableNvarchar == true) + { + tempequals = $"{SqlBuilder.SqlTranslationLeft}{{0}}{SqlBuilder.SqlTranslationRight}='{{1}}' "; + } + if (SqlBuilder.SqlParameterKeyWord == ":") + { + var isAutoToUpper = this.Context.CurrentConnectionConfig?.MoreSettings?.IsAutoToUpper ?? true; + if (entityValue != null && UtilMethods.GetUnderType(entityValue.GetType()) == UtilConstants.DateType) + { + andString.AppendFormat("\"{0}\"={1} ", primaryField.ToUpper(isAutoToUpper), "to_date('" + entityValue.ObjToDate().ToString("yyyy-MM-dd HH:mm:ss") + "', 'YYYY-MM-DD HH24:MI:SS') "); + } + else + { + andString.AppendFormat(tempequals.Replace("N", "") + " ", primaryField.ToUpper(isAutoToUpper), entityValue); + } + } + else if (this.Context.CurrentConnectionConfig.DbType == DbType.PostgreSQL && (this.Context.CurrentConnectionConfig.MoreSettings == null || this.Context.CurrentConnectionConfig.MoreSettings?.PgSqlIsAutoToLower == true)) + { + andString.AppendFormat("\"{0}\"={1} ", primaryField.ToLower(), new PostgreSQLExpressionContext().GetValue(entityValue)); + } + else if (entityValue != null && UtilMethods.IsNumber(UtilMethods.GetUnderType(entityValue.GetType()).Name)) + { + andString.AppendFormat("{0}={1} ", this.SqlBuilder.GetTranslationColumnName(primaryField), $"{entityValue}"); + } + else if (entityValue != null && UtilMethods.GetUnderType(entityValue.GetType()) == UtilConstants.DateType) + { + andString.AppendFormat("{0}={1} ", this.SqlBuilder.GetTranslationColumnName(primaryField), this.DeleteBuilder.LambdaExpressions.DbMehtods.ToDate(new MethodCallExpressionModel() + { + Args = new List() + { + new MethodCallExpressionArgs() + { + IsMember=false, + MemberName="'"+entityValue.ObjToDate().ToString("yyyy-MM-dd HH:mm:ss.fff")+"'" + } + } + })); ; + } + else + { + if ((columnInfo.SqlParameterDbType.ObjToString() == System.Data.DbType.AnsiString.ObjToString()) || !(entityValue is string) || this.Context.CurrentConnectionConfig?.MoreSettings?.DisableNvarchar == true) + { + tempequals = tempequals.Replace("=N'", "='"); + } + else + { + tempequals = SqlBuilder.RemoveN(tempequals); + } + entityValue = UtilMethods.GetConvertValue(entityValue); + andString.AppendFormat(tempequals, primaryField, entityValue); + } + ++i; + } + orString.AppendFormat(DeleteBuilder.WhereInAreaTemplate, andString); + whereInSql.Append(orString); + } + Where(string.Format(DeleteBuilder.WhereInAreaTemplate, whereInSql.ToString())); + } + return this; + } + public IDeleteable WhereIF(bool isWhere, Expression> expression) + { + if (DeleteBuilder.WhereInfos.Count != 0 != true) + { + Check.ExceptionEasy(!StaticConfig.EnableAllWhereIF, "Need to program startup configuration StaticConfig. EnableAllWhereIF = true; Tip: This operation is very risky if there are no conditions it is easy to update the entire table", " 需要程序启动时配置StaticConfig.EnableAllWhereIF=true; 提示:该操作存在很大的风险如果没有条件很容易将整个表全部更新"); + } + if (isWhere) + { + return Where(expression); + } + return this; + } + public IDeleteable Where(Expression> expression) + { + var expResult = DeleteBuilder.GetExpressionValue(expression, ResolveExpressType.WhereSingle); + var whereString = expResult.GetResultString(); + if (expression.ToString().Contains("Subqueryable()")) + { + var entityTableName = this.EntityInfo.DbTableName; + if (this.DeleteBuilder.AsName.HasValue()) + { + entityTableName = this.DeleteBuilder.AsName; + } + if (ExpressionTool.GetParameters(expression).First().Type == typeof(T)) + { + var tableName = this.SqlBuilder.GetTranslationColumnName(entityTableName); + whereString = whereString.Replace(tableName, $"( SELECT * FROM {tableName}) "); + } + whereString = whereString.Replace(this.SqlBuilder.GetTranslationColumnName(expression.Parameters.First().Name) + ".", this.SqlBuilder.GetTranslationTableName(entityTableName) + "."); + } + else if (expResult.IsNavicate) + { + var entityTableName2 = this.EntityInfo.DbTableName; + if (this.DeleteBuilder.AsName.HasValue()) + { + entityTableName2 = this.DeleteBuilder.AsName; + } + whereString = whereString.Replace(expression.Parameters.First().Name + ".", this.SqlBuilder.GetTranslationTableName(entityTableName2) + "."); + whereString = whereString.Replace(this.SqlBuilder.GetTranslationColumnName(expression.Parameters.First().Name) + ".", this.SqlBuilder.GetTranslationTableName(entityTableName2) + "."); + + } + DeleteBuilder.WhereInfos.Add(whereString); + return this; + } + + public IDeleteable Where(T deleteObj) + { + Check.Exception(GetPrimaryKeys().IsNullOrEmpty(), "Where(entity) Primary key required"); + Where(new List() { deleteObj }); + return this; + } + + public IDeleteable Where(string whereString, object parameters = null) + { + DeleteBuilder.WhereInfos.Add(whereString); + if (parameters != null) + { + if (DeleteBuilder.Parameters == null) + { + DeleteBuilder.Parameters = new List(); + } + DeleteBuilder.Parameters.AddRange(Context.Ado.GetParameters(parameters)); + } + return this; + } + + public IDeleteable Where(string whereString, SugarParameter parameter) + { + DeleteBuilder.WhereInfos.Add(whereString); + if (DeleteBuilder.Parameters == null) + { + DeleteBuilder.Parameters = new List(); + } + DeleteBuilder.Parameters.Add(parameter); + return this; + } + public IDeleteable Where(string whereString, SugarParameter[] parameters) + { + DeleteBuilder.WhereInfos.Add(whereString); + if (DeleteBuilder.Parameters == null) + { + DeleteBuilder.Parameters = new List(); + } + DeleteBuilder.Parameters.AddRange(parameters); + return this; + } + public IDeleteable Where(string whereString, List parameters) + { + DeleteBuilder.WhereInfos.Add(whereString); + if (DeleteBuilder.Parameters == null) + { + DeleteBuilder.Parameters = new List(); + } + DeleteBuilder.Parameters.AddRange(parameters); + return this; + } + public IDeleteable Where(List conditionalModels, bool isWrap) + { + if (conditionalModels.Count == 0) + { + return Where("1=2"); + } + var sql = this.Context.Queryable().SqlBuilder.ConditionalModelToSql(conditionalModels); + var result = this; + if (isWrap) + { + result.Where($"({sql.Key})", sql.Value); + } + else + { + result.Where(sql.Key, sql.Value); + } + return result; + } + public IDeleteable Where(List conditionalModels) + { + if (conditionalModels.Count == 0) + { + return Where("1=2"); + } + var sql = this.Context.Queryable().SqlBuilder.ConditionalModelToSql(conditionalModels); + var result = this; + result.Where(sql.Key, sql.Value); + return result; + } + public IDeleteable WhereColumns(T data, Expression> columns) + { + return WhereColumns(new List() { data }, columns); + } + public IDeleteable WhereColumns(List list, Expression> columns) + { + if (columns != null) + { + tempPrimaryKeys = DeleteBuilder.GetExpressionValue(columns, ResolveExpressType.ArraySingle).GetResultArray().Select(it => this.SqlBuilder.GetNoTranslationColumnName(it)).ToList(); + } + //else if (columns != null && tempPrimaryKeys.IsNullOrEmpty()) + //{ + // tempPrimaryKeys = DeleteBuilder.GetExpressionValue(columns, ResolveExpressType.ArraySingle).GetResultArray().Select(it => this.SqlBuilder.GetNoTranslationColumnName(it)).ToList(); + //} + this.Where(list); + + return this; + } + public IDeleteable WhereColumns(List> list) + { + List conditionalModels = new List(); + foreach (var model in list) + { + int i = 0; + var clist = new List>(); + foreach (var item in model.Keys) + { + clist.Add(new KeyValuePair(i == 0 ? WhereType.Or : WhereType.And, new ConditionalModel() + { + FieldName = item, + ConditionalType = ConditionalType.Equal, + FieldValue = model[item].ObjToStringNoTrim(), + CSharpTypeName = model[item] == null ? null : model[item].GetType().Name + })); + i++; + } + conditionalModels.Add(new ConditionalCollections() + { + ConditionalList = clist + }); + } + return this.Where(conditionalModels); + } + public IDeleteable RemoveDataCache() + { + this.RemoveCacheFunc = () => + { + var cacheService = this.Context.CurrentConnectionConfig.ConfigureExternalServices.DataInfoCacheService; + CacheSchemeMain.RemoveCache(cacheService, this.Context.EntityMaintenance.GetTableName()); + }; + return this; + } + public IDeleteable EnableQueryFilter() + { + var queryable = this.Context.Queryable(); + queryable.QueryBuilder.LambdaExpressions.ParameterIndex = 1000; + var sqlable = queryable.ToSql(); + var whereInfos = Regex.Split(sqlable.Key, " Where ", RegexOptions.IgnoreCase); + if (whereInfos.Length > 1) + { + this.Where(whereInfos.Last(), sqlable.Value); + } + return this; + } + public IDeleteable EnableQueryFilter(Type type) + { + var queryable = this.Context.Queryable().Filter(type); + queryable.QueryBuilder.LambdaExpressions.ParameterIndex = 1000; + var sqlable = queryable.ToSql(); + var whereInfos = Regex.Split(sqlable.Key, " Where ", RegexOptions.IgnoreCase); + if (whereInfos.Length > 1) + { + this.Where(whereInfos.Last(), sqlable.Value); + } + return this; + } + public SplitTableDeleteProvider SplitTable(Func, IEnumerable> getTableNamesFunc) + { + UtilMethods.StartCustomSplitTable(this.Context, typeof(T)); + this.Context.MappingTables.Add(this.EntityInfo.EntityName, this.EntityInfo.DbTableName); + SplitTableDeleteProvider result = new SplitTableDeleteProvider(); + result.Context = this.Context; + SplitTableContext helper = new SplitTableContext((SqlSugarProvider)Context) + { + EntityInfo = this.EntityInfo + }; + var tables = getTableNamesFunc(helper.GetTables()); + result.Tables = tables; + result.deleteobj = this; + return result; + } + public SplitTableDeleteByObjectProvider SplitTable() + { + UtilMethods.StartCustomSplitTable(this.Context, typeof(T)); + SplitTableDeleteByObjectProvider result = new SplitTableDeleteByObjectProvider(); + result.Context = this.Context; + Check.ExceptionEasy(this.DeleteObjects == null, "SplitTable() +0 only List can be deleted", "SplitTable()无参数重载只支持根据实体集合删除"); + result.deleteObjects = this.DeleteObjects.ToArray(); + SplitTableContext helper = new SplitTableContext((SqlSugarProvider)Context) + { + EntityInfo = this.EntityInfo + }; + result.deleteobj = this; + return result; + } + public LogicDeleteProvider IsLogic() + { + LogicDeleteProvider result = new LogicDeleteProvider(); + result.DeleteBuilder = this.DeleteBuilder; + result.Deleteable = this; + return result; + } + public IDeleteable RemoveDataCache(string likeString) + { + this.RemoveCacheFunc = () => + { + var cacheService = this.Context.CurrentConnectionConfig.ConfigureExternalServices.DataInfoCacheService; + CacheSchemeMain.RemoveCacheByLike(cacheService, likeString); + }; + return this; + } + public IDeleteable In(List primaryKeyValues) + { + if (primaryKeyValues == null || primaryKeyValues.Count == 0) + { + Where(SqlBuilder.SqlFalse); + return this; + } + return In(primaryKeyValues.ToArray()); + } + + public IDeleteable In(PkType[] primaryKeyValues) + { + if (primaryKeyValues == null || primaryKeyValues.Length == 0) + { + Where(SqlBuilder.SqlFalse); + return this; + } + string tableName = this.Context.EntityMaintenance.GetTableName(); + string primaryField = null; + primaryField = GetPrimaryKeys().FirstOrDefault(); + Check.ArgumentNullException(primaryField, "Table " + tableName + " with no primarykey"); + if (primaryKeyValues.Length < 10000) + { + Where(string.Format(DeleteBuilder.WhereInTemplate, SqlBuilder.GetTranslationColumnName(primaryField), primaryKeyValues.ToJoinSqlInVals())); + } + else + { + if (DeleteBuilder.BigDataInValues == null) + DeleteBuilder.BigDataInValues = new List(); + DeleteBuilder.BigDataInValues.AddRange(primaryKeyValues.Select(it => (object)it)); + DeleteBuilder.BigDataFiled = primaryField; + } + return this; + } + + public IDeleteable In(PkType primaryKeyValue) + { + if (typeof(PkType).FullName.IsCollectionsList()) + { + var newValues = new List(); + foreach (var item in primaryKeyValue as IEnumerable) + { + newValues.Add(item); + } + return In(newValues); + } + + + In(new PkType[] { primaryKeyValue }); + return this; + } + + public IDeleteable In(Expression> inField, PkType primaryKeyValue) + { + var lamResult = DeleteBuilder.GetExpressionValue(inField, ResolveExpressType.FieldSingle); + var fieldName = lamResult.GetResultString(); + tempPrimaryKeys = new List() { fieldName }; + var result = In(primaryKeyValue); + tempPrimaryKeys = null; + return this; + } + public IDeleteable In(Expression> inField, PkType[] primaryKeyValues) + { + var lamResult = DeleteBuilder.GetExpressionValue(inField, ResolveExpressType.FieldSingle); + var fieldName = lamResult.GetResultString(); + tempPrimaryKeys = new List() { fieldName }; + var result = In(primaryKeyValues); + tempPrimaryKeys = null; + return this; + } + public IDeleteable In(Expression> inField, List primaryKeyValues) + { + var lamResult = DeleteBuilder.GetExpressionValue(inField, ResolveExpressType.FieldSingle); + var fieldName = lamResult.GetResultString(); + tempPrimaryKeys = new List() { fieldName }; + var result = In(primaryKeyValues); + tempPrimaryKeys = null; + return this; + } + + public IDeleteable In(Expression> inField, ISugarQueryable childQueryExpression) + { + var lamResult = DeleteBuilder.GetExpressionValue(inField, ResolveExpressType.FieldSingle); + var fieldName = lamResult.GetResultString(); + var sql = childQueryExpression.ToSql(); + Where($" {fieldName} IN ( SELECT {fieldName} FROM ( {sql.Key} ) SUBDEL) ", sql.Value); + return this; + } + public IDeleteable In(string inField, List primaryKeyValues) + { + tempPrimaryKeys = new List() { inField }; + var result = In(primaryKeyValues); + tempPrimaryKeys = null; + return this; + } + + public DeleteablePage PageSize(int pageSize) + { + Check.ExceptionEasy(this.DeleteObjects == null, "PageSize can only be deleted as a List entity collection", "Deleteable.PageSize()只能是List实体集合方式删除,并且集合不能为null"); + DeleteablePage result = new DeleteablePage(); + result.DataList = this.DeleteObjects.ToArray(); + result.Context = this.Context; + result.DiffModel = this.diffModel; + result.IsEnableDiffLogEvent = this.IsEnableDiffLogEvent; + result.TableName = this.DeleteBuilder.AsName; + result.PageSize = pageSize; + return result; + } + public IDeleteable With(string lockString) + { + if (this.Context.CurrentConnectionConfig.DbType == DbType.SqlServer) + DeleteBuilder.TableWithString = lockString; + return this; + } + public virtual string ToSqlString() + { + var sqlObj = this.ToSql(); + var result = sqlObj.Key; + if (result == null) return null; + result = UtilMethods.GetSqlString(this.Context.CurrentConnectionConfig, sqlObj); + return result; + } + public KeyValuePair> ToSql() + { + DeleteBuilder.EntityInfo = this.Context.EntityMaintenance.GetEntityInfo(); + string sql = DeleteBuilder.ToSqlString(); + var paramters = DeleteBuilder.Parameters == null ? null : DeleteBuilder.Parameters.ToList(); + RestoreMapping(); + return new KeyValuePair>(sql, paramters); + } + + private List GetPrimaryKeys() + { + if (tempPrimaryKeys.HasValue()) + { + return tempPrimaryKeys; + } + else + { + return this.EntityInfo.Columns.Where(it => it.IsPrimarykey).Select(it => it.DbColumnName).ToList(); + } + } + private void _ExecuteCommand(out string sql, out SugarParameter[] paramters) + { + DeleteBuilder.EntityInfo = this.Context.EntityMaintenance.GetEntityInfo(); + sql = DeleteBuilder.ToSqlString(); + paramters = DeleteBuilder.Parameters == null ? null : DeleteBuilder.Parameters.ToArray(); + RestoreMapping(); + AutoRemoveDataCache(); + Before(sql); + } + + protected virtual List GetIdentityKeys() + { + + return this.EntityInfo.Columns.Where(it => it.IsIdentity).Select(it => it.DbColumnName).ToList(); + } + + private void RestoreMapping() + { + if (IsAs) + { + this.Context.MappingTables = OldMappingTableList; + } + } + + //private void TaskStart(Task result) + //{ + // if (this.Context.CurrentConnectionConfig.IsShardSameThread) + // { + // Check.Exception(true, "IsShardSameThread=true can't be used async method"); + // } + // result.Start(); + //} + + private void AutoRemoveDataCache() + { + var moreSetts = this.Context.CurrentConnectionConfig.MoreSettings; + var extService = this.Context.CurrentConnectionConfig.ConfigureExternalServices; + if (moreSetts?.IsAutoRemoveDataCache == true && extService?.DataInfoCacheService != null) + { + this.RemoveDataCache(); + } + } + + + protected virtual void After(string sql) + { + if (this.IsEnableDiffLogEvent) + { + var isDisableMasterSlaveSeparation = this.Context.Ado.IsDisableMasterSlaveSeparation; + this.Context.Ado.IsDisableMasterSlaveSeparation = true; + var parameters = DeleteBuilder.Parameters; + if (parameters == null) + parameters = new List(); + diffModel.AfterData = null; + diffModel.Time = this.Context.Ado.SqlExecutionTime; + if (this.Context.CurrentConnectionConfig.AopEvents.OnDiffLogEvent != null) + this.Context.CurrentConnectionConfig.AopEvents.OnDiffLogEvent(diffModel); + this.Context.Ado.IsDisableMasterSlaveSeparation = isDisableMasterSlaveSeparation; + } + if (this.RemoveCacheFunc != null) + { + this.RemoveCacheFunc(); + } + DataChangesAop(this.DeleteObjects); + } + + protected virtual void Before(string sql) + { + if (this.IsEnableDiffLogEvent) + { + var isDisableMasterSlaveSeparation = this.Context.Ado.IsDisableMasterSlaveSeparation; + this.Context.Ado.IsDisableMasterSlaveSeparation = true; + var parameters = DeleteBuilder.Parameters; + if (parameters == null) + parameters = new List(); + diffModel.BeforeData = GetDiffTable(sql, parameters); + diffModel.Sql = sql; + diffModel.Parameters = parameters.ToArray(); + this.Context.Ado.IsDisableMasterSlaveSeparation = isDisableMasterSlaveSeparation; + } + } + + protected virtual List GetDiffTable(string sql, List parameters) + { + List result = new List(); + var whereSql = Regex.Replace(sql, ".* WHERE ", "", RegexOptions.Singleline); + var dt = this.Context.Queryable().AS(this.DeleteBuilder.AsName).Filter(null, true).Where(whereSql).AddParameters(parameters).ToDataTable(); + if (dt.Rows?.Count > 0) + { + foreach (DataRow row in dt.Rows) + { + DiffLogTableInfo item = new DiffLogTableInfo(); + item.TableDescription = this.EntityInfo.TableDescription; + item.TableName = this.EntityInfo.DbTableName; + item.Columns = new List(); + foreach (DataColumn col in dt.Columns) + { + var sugarColumn = this.EntityInfo.Columns.Where(it => it.DbColumnName != null).First(it => + it.DbColumnName.Equals(col.ColumnName, StringComparison.CurrentCultureIgnoreCase)); + DiffLogColumnInfo addItem = new DiffLogColumnInfo(); + addItem.Value = row[col.ColumnName]; + addItem.ColumnName = col.ColumnName; + addItem.IsPrimaryKey = sugarColumn.IsPrimarykey; + addItem.ColumnDescription = sugarColumn.ColumnDescription; + item.Columns.Add(addItem); + } + result.Add(item); + } + } + return result; + } + protected virtual void DataAop(object deleteObj) + { + var dataEvent = this.Context.CurrentConnectionConfig.AopEvents?.DataExecuting; + if (deleteObj != null && dataEvent != null) + { + var model = new DataFilterModel() + { + OperationType = DataFilterType.DeleteByObject, + EntityValue = deleteObj, + EntityColumnInfo = this.EntityInfo.Columns.FirstOrDefault() + }; + dataEvent(deleteObj, model); + } + } + protected virtual void DataChangesAop(List deleteObjs) + { + var dataEvent = this.Context.CurrentConnectionConfig.AopEvents?.DataChangesExecuted; + if (dataEvent != null && deleteObjs != null) + { + foreach (var deleteObj in deleteObjs) + { + if (deleteObj != null) + { + var model = new DataFilterModel() + { + OperationType = DataFilterType.DeleteByObject, + EntityValue = deleteObj, + EntityColumnInfo = this.EntityInfo.Columns.FirstOrDefault() + }; + dataEvent(deleteObj, model); + } + } + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DeleteProvider/LogicDeleteProvider.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DeleteProvider/LogicDeleteProvider.cs new file mode 100644 index 000000000..af8f06e75 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DeleteProvider/LogicDeleteProvider.cs @@ -0,0 +1,162 @@ +namespace SqlSugar +{ + public class LogicDeleteProvider where T : class, new() + { + public DeleteableProvider Deleteable { get; set; } + public DeleteBuilder DeleteBuilder { get; set; } + public int ExecuteCommand(string LogicFieldName = null, object deleteValue = null, string deleteTimeFieldName = null) + { + ISqlSugarClient db; + List pars; + string where; + var isAutoDelFilter = + DeleteBuilder.Context?.CurrentConnectionConfig?.MoreSettings?.IsAutoDeleteQueryFilter == true && + DeleteBuilder.Context?.CurrentConnectionConfig?.MoreSettings?.IsAutoUpdateQueryFilter == true; + if (isAutoDelFilter) + { + DeleteBuilder.Context.CurrentConnectionConfig.MoreSettings.IsAutoUpdateQueryFilter = false; + } + LogicFieldName = _ExecuteCommand(LogicFieldName, out db, out where, out pars); + if (deleteValue == null) + { + deleteValue = true; + } + var updateable = db.Updateable().SetColumns(LogicFieldName, deleteValue); + if (deleteTimeFieldName != null) + { + updateable.SetColumns(deleteTimeFieldName, DateTime.Now); + } + if (pars != null) + updateable.UpdateBuilder.Parameters.AddRange(pars); + Convert(updateable as UpdateableProvider); + var result = updateable.Where(where).ExecuteCommand(); + if (isAutoDelFilter) + { + DeleteBuilder.Context.CurrentConnectionConfig.MoreSettings.IsAutoUpdateQueryFilter = true; + } + return result; + } + public int ExecuteCommand(string LogicFieldName, object deleteValue, string deleteTimeFieldName, string userNameFieldName, object userNameValue) + { + ISqlSugarClient db; + List pars; + string where; + var isAutoDelFilter = + DeleteBuilder.Context?.CurrentConnectionConfig?.MoreSettings?.IsAutoDeleteQueryFilter == true && + DeleteBuilder.Context?.CurrentConnectionConfig?.MoreSettings?.IsAutoUpdateQueryFilter == true; + if (isAutoDelFilter) + { + DeleteBuilder.Context.CurrentConnectionConfig.MoreSettings.IsAutoUpdateQueryFilter = false; + } + LogicFieldName = _ExecuteCommand(LogicFieldName, out db, out where, out pars); + var updateable = db.Updateable(); + updateable.UpdateBuilder.LambdaExpressions.ParameterIndex = 1000; + updateable.SetColumns(LogicFieldName, deleteValue); + updateable.SetColumns(deleteTimeFieldName, DateTime.Now); + updateable.SetColumns(userNameFieldName, userNameValue); + if (pars != null) + updateable.UpdateBuilder.Parameters.AddRange(pars); + Convert(updateable as UpdateableProvider); + var result = updateable.Where(where).ExecuteCommand(); + return result; + } + public async Task ExecuteCommandAsync(string LogicFieldName, object deleteValue, string deleteTimeFieldName, string userNameFieldName, object userNameValue) + { + ISqlSugarClient db; + List pars; + string where; + var isAutoDelFilter = + DeleteBuilder.Context?.CurrentConnectionConfig?.MoreSettings?.IsAutoDeleteQueryFilter == true && + DeleteBuilder.Context?.CurrentConnectionConfig?.MoreSettings?.IsAutoUpdateQueryFilter == true; + if (isAutoDelFilter) + { + DeleteBuilder.Context.CurrentConnectionConfig.MoreSettings.IsAutoUpdateQueryFilter = false; + } + LogicFieldName = _ExecuteCommand(LogicFieldName, out db, out where, out pars); + var updateable = db.Updateable(); + updateable.UpdateBuilder.LambdaExpressions.ParameterIndex = 1000; + updateable.SetColumns(LogicFieldName, deleteValue); + updateable.SetColumns(deleteTimeFieldName, DateTime.Now); + updateable.SetColumns(userNameFieldName, userNameValue); + if (pars != null) + updateable.UpdateBuilder.Parameters.AddRange(pars); + Convert(updateable as UpdateableProvider); + var result = await updateable.Where(where).ExecuteCommandAsync().ConfigureAwait(false); + return result; + } + public async Task ExecuteCommandAsync(string LogicFieldName = null, object deleteValue = null, string deleteTimeFieldName = null) + { + ISqlSugarClient db; + List pars; + string where; + var isAutoDelFilter = + DeleteBuilder.Context?.CurrentConnectionConfig?.MoreSettings?.IsAutoDeleteQueryFilter == true && + DeleteBuilder.Context?.CurrentConnectionConfig?.MoreSettings?.IsAutoUpdateQueryFilter == true; + if (isAutoDelFilter) + { + DeleteBuilder.Context.CurrentConnectionConfig.MoreSettings.IsAutoUpdateQueryFilter = false; + } + LogicFieldName = _ExecuteCommand(LogicFieldName, out db, out where, out pars); + if (deleteValue == null) + { + deleteValue = true; + } + var updateable = db.Updateable().SetColumns(LogicFieldName, deleteValue); + if (deleteTimeFieldName != null) + { + updateable.SetColumns(deleteTimeFieldName, DateTime.Now); + } + if (pars != null) + updateable.UpdateBuilder.Parameters.AddRange(pars); + Convert(updateable as UpdateableProvider); + var result = await updateable.Where(where).ExecuteCommandAsync().ConfigureAwait(false); + if (isAutoDelFilter) + { + DeleteBuilder.Context.CurrentConnectionConfig.MoreSettings.IsAutoUpdateQueryFilter = true; + } + return result; + } + + private void Convert(UpdateableProvider updateable) + { + updateable.IsEnableDiffLogEvent = Deleteable.IsEnableDiffLogEvent; + updateable.diffModel = Deleteable.diffModel; + updateable.UpdateBuilder.TableWithString = DeleteBuilder.TableWithString; + updateable.RemoveCacheFunc = Deleteable.RemoveCacheFunc; + } + + private string _ExecuteCommand(string LogicFieldName, out ISqlSugarClient db, out string where, out List pars) + { + var entityInfo = Deleteable.EntityInfo; + db = Deleteable.Context; + if (DeleteBuilder.BigDataInValues?.Count > 0) + { + var sql = db.Queryable().Select("1").AS(nameof(T)).In(DeleteBuilder.BigDataInValues.ToArray()).ToSqlString(); + var whereIndex = sql.IndexOf(" WHERE "); + var whereItem = sql.Substring(whereIndex + 7); + this.DeleteBuilder.WhereInfos.Add(whereItem); + } + + Check.ExceptionEasy(DeleteBuilder.GetWhereString == null, "Logical Delete requires a Where condition", "逻辑删除需要加Where条件"); + + where = DeleteBuilder.GetWhereString.Substring(5); + pars = DeleteBuilder.Parameters; + if (LogicFieldName.IsNullOrEmpty()) + { + var column = entityInfo.Columns.FirstOrDefault(it => + it.PropertyName.EqualCase("isdelete") || + it.PropertyName.EqualCase("isdeleted") || + it.DbColumnName.EqualCase("isdelete") || + it.DbColumnName.EqualCase("isdeleted")); + if (column != null) + { + LogicFieldName = column.DbColumnName; + } + } + Check.Exception(LogicFieldName == null, ErrorMessage.GetThrowMessage( + $"{entityInfo.EntityName} is not isdelete or isdeleted" + , $"{entityInfo.EntityName} 没有IsDelete或者IsDeleted 的属性, 你也可以用 IsLogic().ExecuteCommand(\"列名\")")); + return LogicFieldName; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DeleteProvider/SplitTableDeleteByObjectProvider.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DeleteProvider/SplitTableDeleteByObjectProvider.cs new file mode 100644 index 000000000..18f4c3439 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DeleteProvider/SplitTableDeleteByObjectProvider.cs @@ -0,0 +1,56 @@ +using System.Reflection; + +namespace SqlSugar +{ + public class SplitTableDeleteByObjectProvider where T : class, new() + { + public ISqlSugarClient Context; + public DeleteableProvider deleteobj; + public T[] deleteObjects { get; set; } + + public int ExecuteCommand() + { + List groupModels; + int result; + GroupDataList(deleteObjects, out groupModels, out result); + foreach (var item in groupModels.GroupBy(it => it.GroupName)) + { + var addList = item.Select(it => it.Item).ToList(); + result += this.Context.Deleteable().Where(addList).AS(item.Key).ExecuteCommand(); + } + return result; + } + public async Task ExecuteCommandAsync() + { + List groupModels; + int result; + GroupDataList(deleteObjects, out groupModels, out result); + foreach (var item in groupModels.GroupBy(it => it.GroupName)) + { + var addList = item.Select(it => it.Item).ToList(); + result += await Context.Deleteable().Where(addList).AS(item.Key).ExecuteCommandAsync().ConfigureAwait(false); + } + return result; + } + + private void GroupDataList(T[] datas, out List groupModels, out int result) + { + var attribute = typeof(T).GetCustomAttribute() as SplitTableAttribute; + Check.Exception(attribute == null, $"{typeof(T).Name} need SplitTableAttribute"); + groupModels = new List(); + var db = this.Context; + foreach (var item in datas) + { + var value = db.SplitHelper().GetValue(attribute.SplitType, item); + var tableName = db.SplitHelper().GetTableName(attribute.SplitType, value); + groupModels.Add(new GroupModel() { GroupName = tableName, Item = item }); + } + result = 0; + } + internal class GroupModel + { + public string GroupName { get; set; } + public T Item { get; set; } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DeleteProvider/SplitTableDeleteProvider.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DeleteProvider/SplitTableDeleteProvider.cs new file mode 100644 index 000000000..1bf93a6db --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DeleteProvider/SplitTableDeleteProvider.cs @@ -0,0 +1,93 @@ +using System.Text.RegularExpressions; + +namespace SqlSugar +{ + public class SplitTableDeleteProvider where T : class, new() + { + public ISqlSugarClient Context; + public DeleteableProvider deleteobj; + + public IEnumerable Tables { get; set; } + public int ExecuteCommand() + { + if (this.Context.Ado.Transaction == null) + { + try + { + this.Context.Ado.BeginTran(); + var result = _ExecuteCommand(); + this.Context.Ado.CommitTran(); + return result; + } + catch (Exception) + { + this.Context.Ado.RollbackTran(); + throw; + } + } + else + { + return _ExecuteCommand(); + } + } + public async Task ExecuteCommandAsync() + { + if (this.Context.Ado.Transaction == null) + { + try + { + this.Context.Ado.BeginTran(); + var result = await _ExecuteCommandAsync().ConfigureAwait(false); + this.Context.Ado.CommitTran(); + return result; + } + catch (Exception) + { + this.Context.Ado.RollbackTran(); + throw; + } + } + else + { + return await _ExecuteCommandAsync().ConfigureAwait(false); + } + } + internal int _ExecuteCommand() + { + var result = 0; + var sqlobj = deleteobj.ToSql(); + + foreach (var item in Tables) + { + var newsqlobj = GetSqlObj(sqlobj, item.TableName); + result += this.Context.Ado.ExecuteCommand(newsqlobj.Key, newsqlobj.Value); + } + return result; + } + + internal async Task _ExecuteCommandAsync() + { + var result = 0; + var sqlobj = deleteobj.ToSql(); + foreach (var item in Tables) + { + var newsqlobj = GetSqlObj(sqlobj, item.TableName); + result += await Context.Ado.ExecuteCommandAsync(newsqlobj.Key, newsqlobj.Value).ConfigureAwait(false); + } + return result; + } + + private KeyValuePair> GetSqlObj(KeyValuePair> keyValuePair, string asName) + { + List pars = new List(); + string sql = keyValuePair.Key; + if (keyValuePair.Value != null) + { + pars = keyValuePair.Value.Select(it => new SugarParameter(it.ParameterName, it.Value)).ToList(); + } + sql = Regex.Replace(sql, deleteobj.EntityInfo.DbTableName, asName, RegexOptions.IgnoreCase); + return new KeyValuePair>(sql, pars); + } + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DynamicBuilder/CommonMethodInfo.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DynamicBuilder/CommonMethodInfo.cs new file mode 100644 index 000000000..e359c956b --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DynamicBuilder/CommonMethodInfo.cs @@ -0,0 +1,90 @@ +namespace SqlSugar +{ + public class CommonMethodInfo + { + internal object Context { get; set; } + + public int ExecuteReturnIdentity() + { + if (Context == null) return 0; + var result = Context.GetType().GetMyMethod("ExecuteReturnIdentity", 0).Invoke(Context, Array.Empty()); + return (int)result; + } + public async Task ExecuteReturnIdentityAsync() + { + if (Context == null) return 0; + var result = Context.GetType().GetMyMethod("ExecuteReturnIdentityAsync", 0).Invoke(Context, Array.Empty()); + return await ((Task)result).ConfigureAwait(false); + } + public int ExecuteCommand() + { + if (Context == null) return 0; + var result = Context.GetType().GetMyMethod("ExecuteCommand", 0).Invoke(Context, Array.Empty()); + return (int)result; + } + public async Task ExecuteCommandAsync() + { + if (Context == null) return 0; + var result = Context.GetType().GetMyMethod("ExecuteCommandAsync", 0).Invoke(Context, Array.Empty()); + return await ((Task)result).ConfigureAwait(false); + } + } + public class SplitMethodInfo + { + internal object Context { get; set; } + + public int ExecuteCommand() + { + if (Context == null) return 0; + var result = Context.GetType().GetMyMethod("ExecuteCommand", 0).Invoke(Context, Array.Empty()); + return (int)result; + } + public async Task ExecuteCommandAsync() + { + if (Context == null) return 0; + var result = Context.GetType().GetMyMethod("ExecuteCommandAsync", 0).Invoke(Context, Array.Empty()); + return await ((Task)result).ConfigureAwait(false); + } + } + public class UpdateCommonMethodInfo + { + internal object Context { get; set; } + + public int ExecuteCommand() + { + if (Context == null) return 0; + var result = Context.GetType().GetMyMethod("ExecuteCommand", 0).Invoke(Context, Array.Empty()); + return (int)result; + } + public UpdateCommonMethodInfo WhereColumns(params string[] columns) + { + var result = Context.GetType().GetMyMethod("WhereColumns", 1, typeof(string[])).Invoke(Context, new object[] { columns }); + UpdateCommonMethodInfo updateCommonMethod = new UpdateCommonMethodInfo(); + updateCommonMethod.Context = result; + return updateCommonMethod; + } + public UpdateCommonMethodInfo UpdateColumns(params string[] columns) + { + var result = Context.GetType().GetMyMethod("UpdateColumns", 1, typeof(string[])).Invoke(Context, new object[] { columns }); + UpdateCommonMethodInfo updateCommonMethod = new UpdateCommonMethodInfo(); + updateCommonMethod.Context = result; + return updateCommonMethod; + } + public async Task ExecuteCommandAsync() + { + if (Context == null) return 0; + var result = Context.GetType().GetMyMethod("ExecuteCommandAsync", 0).Invoke(Context, Array.Empty()); + return await ((Task)result).ConfigureAwait(false); + } + public UpdateCommonMethodInfo SplitTable() + { + var newMethod = this.Context.GetType().GetMyMethod("SplitTable", 0); + var result = newMethod.Invoke(Context, Array.Empty()); + return new UpdateCommonMethodInfo() + { + Context = result + }; + } + } + +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DynamicBuilder/DynamicBuilderHelper.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DynamicBuilder/DynamicBuilderHelper.cs new file mode 100644 index 000000000..b571408b7 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DynamicBuilder/DynamicBuilderHelper.cs @@ -0,0 +1,77 @@ +using System.Reflection; +using System.Reflection.Emit; + +namespace SqlSugar +{ + public static class DynamicBuilderHelper + { + public static Type CreateDynamicClass(string className, List properties, TypeAttributes attributes = TypeAttributes.Public, List classCustomAttributes = null, Type baseType = null, Type[] interfaces = null) + { + TypeBuilder typeBuilder = EmitTool.CreateTypeBuilder(className, attributes, baseType, interfaces); + + if (classCustomAttributes != null) + { + foreach (var attributeBuilder in classCustomAttributes) + { + typeBuilder.SetCustomAttribute(attributeBuilder); + } + } + + foreach (PropertyMetadata property in properties) + { + var type = property.Type; + if (type == typeof(DynamicOneselfType)) + { + type = typeBuilder; + } + else if (type == typeof(DynamicOneselfTypeList)) + { + type = typeof(List<>).MakeGenericType(typeBuilder); + } + EmitTool.CreateProperty(typeBuilder, property.Name, type, property.CustomAttributes); + } + + Type dynamicType = typeBuilder.CreateTypeInfo().AsType(); + + return dynamicType; + } + + public static Type CreateDynamicClass(TypeBuilder typeBuilder, TypeBuilder typeBuilderChild, List properties, List classCustomAttributes = null) + { + + if (classCustomAttributes != null) + { + foreach (var attributeBuilder in classCustomAttributes) + { + typeBuilder.SetCustomAttribute(attributeBuilder); + } + } + + foreach (PropertyMetadata property in properties) + { + var type = property.Type; + if (type == typeof(DynamicOneselfType)) + { + type = typeBuilder; + } + else if (type == typeof(DynamicOneselfTypeList)) + { + type = typeof(List<>).MakeGenericType(typeBuilder); + } + else if (type == typeof(NestedObjectType)) + { + type = typeBuilderChild; + } + else if (type == typeof(NestedObjectTypeList)) + { + type = typeof(List<>).MakeGenericType(typeBuilderChild); + } + EmitTool.CreateProperty(typeBuilder, property.Name, type, property.CustomAttributes); + } + + Type dynamicType = typeBuilder.CreateTypeInfo().AsType(); + + return dynamicType; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DynamicBuilder/DynamicOneselfType.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DynamicBuilder/DynamicOneselfType.cs new file mode 100644 index 000000000..4a5c63111 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DynamicBuilder/DynamicOneselfType.cs @@ -0,0 +1,28 @@ +namespace SqlSugar +{ + /// + /// Dynamically construct Type, the property is self, with this type + /// + public class DynamicOneselfType + { + + } + /// + /// Dynamically construct Type, the property is List self , with this type + /// + public class DynamicOneselfTypeList + { + + } + + + public class NestedObjectType + { + + } + + public class NestedObjectTypeList + { + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DynamicBuilder/DynamicProperyBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DynamicBuilder/DynamicProperyBuilder.cs new file mode 100644 index 000000000..4474cd5fb --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DynamicBuilder/DynamicProperyBuilder.cs @@ -0,0 +1,129 @@ +using System.Reflection; +using System.Reflection.Emit; + +namespace SqlSugar +{ + + public class DynamicProperyBuilder + { + private bool IsCache = false; + public static DynamicProperyBuilder CopyNew() + { + return new DynamicProperyBuilder(); + } + public DynamicBuilder baseBuilder; + public DynamicProperyBuilder CreateProperty(string propertyName, Type properyType, SugarColumn column = null, bool isSplitField = false, Navigate navigate = null) + { + if (column == null) + { + column = new SugarColumn() + { + ColumnName = propertyName + }; + } + PropertyMetadata addItem = new PropertyMetadata(); + addItem.Name = propertyName; + addItem.Type = properyType; + addItem.CustomAttributes = new List() { baseBuilder.GetProperty(column) }; + if (navigate != null) + { + addItem.CustomAttributes.Add(BuildNavigateAttribute(navigate)); + } + baseBuilder.propertyAttr.Add(addItem); + if (isSplitField) + { + addItem.CustomAttributes.Add(baseBuilder.GetSplitFieldAttr(new SplitFieldAttribute())); + } + return this; + } + public DynamicProperyBuilder WithCache(bool isCache = true) + { + IsCache = isCache; + return this; + } + public Type BuilderType() + { + if (IsCache) + { + var key = baseBuilder.entityName + string.Join("_", baseBuilder.propertyAttr.Select(it => it.Name + it.Type.Name)); + return new ReflectionInoCacheService().GetOrCreate(key, () => + { + var result = DynamicBuilderHelper.CreateDynamicClass(baseBuilder.entityName, baseBuilder.propertyAttr, TypeAttributes.Public, baseBuilder.entityAttr, baseBuilder.baseType, baseBuilder.interfaces); + return result; + }); + } + else + { + var result = DynamicBuilderHelper.CreateDynamicClass(baseBuilder.entityName, baseBuilder.propertyAttr, TypeAttributes.Public, baseBuilder.entityAttr, baseBuilder.baseType, baseBuilder.interfaces); + return result; + } + } + + #region BuilderTypes + public Tuple BuilderTypes(DynamicProperyBuilder dynamicBuilderB) + { + if (IsCache) + { + var key1 = baseBuilder.entityName + string.Join("_", baseBuilder.propertyAttr.Select(it => it.Name + it.Type.Name)); + var key2 = dynamicBuilderB.baseBuilder.entityName + string.Join("_", dynamicBuilderB.baseBuilder.propertyAttr.Select(it => it.Name + it.Type.Name)); + return new ReflectionInoCacheService().GetOrCreate(key1 + key2, () => + { + Tuple result = GetBuilderTypes(dynamicBuilderB); + return result; + }); + } + else + { + Tuple result = GetBuilderTypes(dynamicBuilderB); + return result; + } + } + private Tuple GetBuilderTypes(DynamicProperyBuilder dynamicBuilderB) + { + DynamicProperyBuilder dynamicBuilderA = this; + TypeBuilder typeBuilderA = EmitTool.CreateTypeBuilder(dynamicBuilderA.baseBuilder.entityName, TypeAttributes.Public, dynamicBuilderA.baseBuilder.baseType, dynamicBuilderA.baseBuilder.interfaces); + TypeBuilder typeBuilderB = EmitTool.CreateTypeBuilder(dynamicBuilderB.baseBuilder.entityName, TypeAttributes.Public, dynamicBuilderB.baseBuilder.baseType, dynamicBuilderB.baseBuilder.interfaces); + DynamicBuilderHelper.CreateDynamicClass(typeBuilderA, typeBuilderB, dynamicBuilderA.baseBuilder.propertyAttr, dynamicBuilderA.baseBuilder.entityAttr); + DynamicBuilderHelper.CreateDynamicClass(typeBuilderB, typeBuilderA, dynamicBuilderB.baseBuilder.propertyAttr, dynamicBuilderB.baseBuilder.entityAttr); + return new Tuple(typeBuilderB.CreateTypeInfo().AsType(), typeBuilderA.CreateTypeInfo().AsType()); + } + #endregion + + public CustomAttributeBuilder BuildNavigateAttribute(Navigate navigate) + { + NavigateType navigatType = navigate.NavigatType; + string name = navigate.Name; + string name2 = navigate.Name2; + string whereSql = navigate.WhereSql; + Type mappingTableType = navigate.MappingType; + string typeAiD = navigate.MappingAId; + string typeBId = navigate.MappingBId; + ConstructorInfo constructor; + object[] constructorArgs; + + if (mappingTableType != null && typeAiD != null && typeBId != null) + { + constructor = typeof(Navigate).GetConstructor(new Type[] { typeof(Type), typeof(string), typeof(string), typeof(string) }); + constructorArgs = new object[] { mappingTableType, typeAiD, typeBId, whereSql }; + } + else if (!string.IsNullOrEmpty(whereSql)) + { + constructor = typeof(Navigate).GetConstructor(new Type[] { typeof(NavigateType), typeof(string), typeof(string), typeof(string) }); + constructorArgs = new object[] { navigatType, name, name2, whereSql }; + } + else if (!string.IsNullOrEmpty(name2)) + { + constructor = typeof(Navigate).GetConstructor(new Type[] { typeof(NavigateType), typeof(string), typeof(string) }); + constructorArgs = new object[] { navigatType, name, name2 }; + } + else + { + constructor = typeof(Navigate).GetConstructor(new Type[] { typeof(NavigateType), typeof(string) }); + constructorArgs = new object[] { navigatType, name }; + } + + return new CustomAttributeBuilder(constructor, constructorArgs); + } + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DynamicBuilder/EmitTool.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DynamicBuilder/EmitTool.cs new file mode 100644 index 000000000..3a36e3c7b --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DynamicBuilder/EmitTool.cs @@ -0,0 +1,62 @@ +using System.Reflection; +using System.Reflection.Emit; + +namespace SqlSugar +{ + internal static class EmitTool + { + internal static ModuleBuilder CreateModuleBuilder() + { + AssemblyBuilder assemblyBuilder = CreateAssembly(); + ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("DynamicModule"); + return moduleBuilder; + } + + internal static AssemblyBuilder CreateAssembly() + { + AssemblyName assemblyName = new AssemblyName($"DynamicAssembly_{Guid.NewGuid():N}"); + AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndCollect); + return assemblyBuilder; + } + + internal static TypeBuilder CreateTypeBuilder(string className, TypeAttributes attributes, Type baseType, Type[] interfaces) + { + ModuleBuilder moduleBuilder = EmitTool.CreateModuleBuilder(); + TypeBuilder typeBuilder = moduleBuilder.DefineType(className, attributes, baseType, interfaces); + return typeBuilder; + } + internal static PropertyBuilder CreateProperty(TypeBuilder typeBuilder, string propertyName, Type propertyType, IEnumerable propertyCustomAttributes = null) + { + FieldBuilder fieldBuilder = typeBuilder.DefineField($"_{propertyName}", propertyType, FieldAttributes.Private); + + PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(propertyName, PropertyAttributes.None, propertyType, null); + + MethodBuilder getterBuilder = typeBuilder.DefineMethod($"get_{propertyName}", MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, propertyType, Type.EmptyTypes); + ILGenerator getterIL = getterBuilder.GetILGenerator(); + getterIL.Emit(OpCodes.Ldarg_0); + getterIL.Emit(OpCodes.Ldfld, fieldBuilder); + getterIL.Emit(OpCodes.Ret); + + MethodBuilder setterBuilder = typeBuilder.DefineMethod($"set_{propertyName}", MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, null, new[] { propertyType }); + ILGenerator setterIL = setterBuilder.GetILGenerator(); + setterIL.Emit(OpCodes.Ldarg_0); + setterIL.Emit(OpCodes.Ldarg_1); + setterIL.Emit(OpCodes.Stfld, fieldBuilder); + setterIL.Emit(OpCodes.Ret); + + propertyBuilder.SetGetMethod(getterBuilder); + propertyBuilder.SetSetMethod(setterBuilder); + + if (propertyCustomAttributes != null) + { + foreach (var attributeBuilder in propertyCustomAttributes) + { + propertyBuilder.SetCustomAttribute(attributeBuilder); + } + } + + return propertyBuilder; + } + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DynamicBuilder/Helper.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DynamicBuilder/Helper.cs new file mode 100644 index 000000000..2b7f0e98e --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DynamicBuilder/Helper.cs @@ -0,0 +1,115 @@ +using System.Reflection; +using System.Reflection.Emit; + +namespace SqlSugar +{ + public partial class DynamicBuilder + { + internal CustomAttributeBuilder GetSplitEntityAttr(SplitTableAttribute sugarTable) + { + Type attributeType = typeof(SplitTableAttribute); + ConstructorInfo attributeCtor = attributeType.GetConstructor(new Type[] { typeof(SplitType) }); + CustomAttributeBuilder attributeBuilder = new CustomAttributeBuilder(attributeCtor, new object[] { sugarTable.SplitType }, + new PropertyInfo[] { + attributeType.GetProperty(nameof(SplitTableAttribute.SplitType)), + } + , new object[] { + sugarTable.SplitType + }); + return attributeBuilder; + } + internal CustomAttributeBuilder GetSplitFieldAttr(SplitFieldAttribute fieldAttribute) + { + Type attributeType = typeof(SplitFieldAttribute); + ConstructorInfo attributeCtor = attributeType.GetConstructor(Array.Empty()); + CustomAttributeBuilder attributeBuilder = new CustomAttributeBuilder(attributeCtor, Array.Empty()); + return attributeBuilder; + } + internal CustomAttributeBuilder GetEntity(SugarTable sugarTable) + { + Type attributeType = typeof(SugarTable); + ConstructorInfo attributeCtor = attributeType.GetConstructor(new Type[] { typeof(string) }); + CustomAttributeBuilder attributeBuilder = new CustomAttributeBuilder(attributeCtor, new object[] { "" }, + new PropertyInfo[] { + attributeType.GetProperty(nameof(SugarTable.TableName)), + attributeType.GetProperty(nameof(SugarTable.TableDescription)) , + attributeType.GetProperty(nameof(SugarTable.IsDisabledUpdateAll)) , + attributeType.GetProperty(nameof(SugarTable.IsDisabledDelete)), + attributeType.GetProperty(nameof(SugarTable.IsCreateTableFiledSort)), + attributeType.GetProperty(nameof(SugarTable.Discrimator)) + } + , new object[] { + sugarTable.TableName, + sugarTable.TableDescription , + sugarTable.IsDisabledUpdateAll, + sugarTable.IsDisabledDelete, + sugarTable.IsCreateTableFiledSort, + sugarTable.Discrimator + }); + return attributeBuilder; + } + internal CustomAttributeBuilder GetProperty(SugarColumn sugarTable) + { + Type attributeType = typeof(SugarColumn); + ConstructorInfo attributeCtor = attributeType.GetConstructor(Array.Empty()); + CustomAttributeBuilder attributeBuilder = new CustomAttributeBuilder(attributeCtor, Array.Empty(), + new PropertyInfo[] { + attributeType.GetProperty(nameof(SugarColumn.IsPrimaryKey)), + attributeType.GetProperty(nameof(SugarColumn.IsIdentity)), + attributeType.GetProperty(nameof(SugarColumn.DefaultValue)), + attributeType.GetProperty(nameof(SugarColumn.Length)), + attributeType.GetProperty(nameof(SugarColumn.DecimalDigits)), + attributeType.GetProperty(nameof(SugarColumn.ColumnDataType)), + attributeType.GetProperty(nameof(SugarColumn.IsNullable)), + attributeType.GetProperty(nameof(SugarColumn.ColumnDescription)), + attributeType.GetProperty(nameof(SugarColumn.OracleSequenceName)), + attributeType.GetProperty(nameof(SugarColumn.IsIgnore)), + attributeType.GetProperty(nameof(SugarColumn.IsJson)), + attributeType.GetProperty(nameof(SugarColumn.IsOnlyIgnoreInsert)), + attributeType.GetProperty(nameof(SugarColumn.IsOnlyIgnoreUpdate)), + attributeType.GetProperty(nameof(SugarColumn.OldColumnName)), + attributeType.GetProperty(nameof(SugarColumn.SqlParameterDbType)), + attributeType.GetProperty(nameof(SugarColumn.SqlParameterSize)), + attributeType.GetProperty(nameof(SugarColumn.IsArray)), + attributeType.GetProperty(nameof(SugarColumn.ColumnName)), + attributeType.GetProperty(nameof(SugarColumn.InsertSql)), + attributeType.GetProperty(nameof(SugarColumn.UpdateSql)), + attributeType.GetProperty(nameof(SugarColumn.ExtendedAttribute)), + attributeType.GetProperty(nameof(SugarColumn.IsDisabledAlterColumn)), + attributeType.GetProperty(nameof(SugarColumn.IsOwnsOne)), + attributeType.GetProperty(nameof(SugarColumn.InsertServerTime)), + attributeType.GetProperty(nameof(SugarColumn.UpdateServerTime)), + attributeType.GetProperty(nameof(SugarColumn.QuerySql)) + } + , new object[] { + sugarTable.IsPrimaryKey, + sugarTable.IsIdentity, + sugarTable.DefaultValue, + sugarTable.Length, + sugarTable.DecimalDigits, + sugarTable.ColumnDataType, + sugarTable.IsNullable, + sugarTable.ColumnDescription, + sugarTable.OracleSequenceName, + sugarTable.IsIgnore, + sugarTable.IsJson, + sugarTable.IsOnlyIgnoreInsert, + sugarTable.IsOnlyIgnoreUpdate, + sugarTable.OldColumnName, + sugarTable.SqlParameterDbType, + sugarTable.SqlParameterSize, + sugarTable.IsArray, + sugarTable.ColumnName, + sugarTable.InsertSql, + sugarTable.UpdateSql, + sugarTable.ExtendedAttribute, + sugarTable.IsDisabledAlterColumn, + sugarTable.IsOwnsOne, + sugarTable.InsertServerTime, + sugarTable.UpdateServerTime, + sugarTable.QuerySql + }); + return attributeBuilder; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DynamicBuilder/Master.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DynamicBuilder/Master.cs new file mode 100644 index 000000000..af5883d00 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/DynamicBuilder/Master.cs @@ -0,0 +1,70 @@ +using System.Reflection; +using System.Reflection.Emit; + +namespace SqlSugar +{ + public partial class DynamicBuilder + { + internal List propertyAttr = new List(); + internal List entityAttr = new List(); + internal string entityName { get; set; } + internal Type baseType = null; + internal Type[] interfaces = null; + internal SqlSugarProvider context; + + public DynamicBuilder(SqlSugarProvider context) + { + this.context = context; + } + + public DynamicProperyBuilder CreateClass(string entityName, SugarTable table = null, Type baseType = null, Type[] interfaces = null, SplitTableAttribute splitTableAttribute = null) + { + this.baseType = baseType; + this.interfaces = interfaces; + this.entityName = entityName; + if (table == null) + { + table = new SugarTable() { TableName = entityName }; + } + this.entityAttr = new List() { GetEntity(table) }; + if (splitTableAttribute != null) + { + this.entityAttr.Add(GetSplitEntityAttr(splitTableAttribute)); + } + return new DynamicProperyBuilder() { baseBuilder = this }; + } + + public object CreateObjectByType(Type type, Dictionary dict) + { + // 创建一个默认的空对象 + object obj = Activator.CreateInstance(type); + + // 遍历字典中的每个 key-value 对 + foreach (KeyValuePair pair in dict) + { + // 获取对象中的属性 + PropertyInfo propertyInfo = type.GetProperty(pair.Key); + + if (propertyInfo == null) + { + propertyInfo = type.GetProperties().FirstOrDefault(it => it.Name.EqualCase(pair.Key)); + } + + propertyInfo?.SetValue(obj, UtilMethods.ChangeType2(pair.Value, propertyInfo.PropertyType)); + } + + // 返回创建的对象 + return obj; + } + + public List CreateObjectByType(Type type, List> dictList) + { + List result = new List(); + foreach (var item in dictList) + { + result.Add(CreateObjectByType(type, item)); + } + return result; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/EntityMaintenance/EntityColumnExtension.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/EntityMaintenance/EntityColumnExtension.cs new file mode 100644 index 000000000..2f34ffb88 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/EntityMaintenance/EntityColumnExtension.cs @@ -0,0 +1,63 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public static class EntityColumnExtension + { + public static EntityColumnable IfTable(this EntityColumnInfo entityColumnInfo) + { + EntityColumnable result = new EntityColumnable(); + result.entityColumnInfo = entityColumnInfo; + result.IsTable = entityColumnInfo.EntityName == typeof(T).Name; + return result; + } + } + + public class EntityColumnable + { + public EntityColumnInfo entityColumnInfo { get; set; } + + public bool IsTable { get; set; } + + public EntityColumnable UpdateProperty(Expression> propertyExpression, Action updateAction) + { + var name = ExpressionTool.GetMemberName(propertyExpression); + if (entityColumnInfo.PropertyName == name && IsTable) + { + updateAction(entityColumnInfo); + } + return this; + } + public EntityColumnable OneToOne(Expression> propertyExpression, string firstName, string lastName = null) + { + var name = ExpressionTool.GetMemberName(propertyExpression); + if (entityColumnInfo.PropertyName == name && IsTable) + { + entityColumnInfo.Navigat = new Navigate(NavigateType.OneToOne, firstName, lastName); + entityColumnInfo.IsIgnore = true; + } + return this; + } + public EntityColumnable OneToMany(Expression> propertyExpression, string firstName, string lastName) + { + var name = ExpressionTool.GetMemberName(propertyExpression); + if (entityColumnInfo.PropertyName == name && IsTable) + { + entityColumnInfo.Navigat = new Navigate(NavigateType.OneToMany, firstName, lastName); + entityColumnInfo.IsIgnore = true; + } + return this; + } + + public EntityColumnable ManyToMany(Expression> propertyExpression, Type mapppingType, string mapppingTypeAid, string mapppingTypeBid) + { + var name = ExpressionTool.GetMemberName(propertyExpression); + if (entityColumnInfo.PropertyName == name && IsTable) + { + entityColumnInfo.Navigat = new Navigate(mapppingType, mapppingTypeAid, mapppingTypeBid); + entityColumnInfo.IsIgnore = true; + } + return this; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/EntityMaintenance/EntityMaintenance.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/EntityMaintenance/EntityMaintenance.cs new file mode 100644 index 000000000..03869db56 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/EntityMaintenance/EntityMaintenance.cs @@ -0,0 +1,483 @@ +using SqlSugar.DbConvert; + +using System.Reflection; +using System.Xml.Linq; + +namespace SqlSugar +{ + public class EntityMaintenance + { + public SqlSugarProvider Context { get; set; } + + public EntityInfo GetEntityInfo() + { + return GetEntityInfo(typeof(T)); + } + public EntityInfo GetEntityInfoWithAttr(Type type) + { + return GetEntityInfo(type); + } + public EntityInfo GetEntityInfo(Type type) + { + var attr = type?.GetCustomAttribute(); + if (attr == null) + { + return _GetEntityInfo(type); + } + else if (attr.configId.ObjToString() == this.Context?.CurrentConnectionConfig?.ConfigId + "") + { + return _GetEntityInfo(type); + } + else if (this.Context.Root == null) + { + return _GetEntityInfo(type); + } + else if (!this.Context.Root.IsAnyConnection(attr.configId)) + { + return _GetEntityInfo(type); + } + else + { + return this.Context.Root.GetConnection(attr.configId).EntityMaintenance._GetEntityInfo(type); + } + } + + private EntityInfo _GetEntityInfo(Type type) + { + string cacheKey = "GetEntityInfo" + type.GetHashCode() + type.FullName + this.Context?.CurrentConnectionConfig?.ConfigId; + return this.Context.Utilities.GetReflectionInoCacheInstance().GetOrCreate(cacheKey, + () => + { + return GetEntityInfoNoCache(type); + }); + } + + public EntityInfo GetEntityInfoNoCache(Type type) + { + EntityInfo result = new EntityInfo(); + var sugarAttributeInfo = type.GetTypeInfo().GetCustomAttributes(typeof(SugarTable), false).Where(it => it is SugarTable).SingleOrDefault(); + if (sugarAttributeInfo.HasValue()) + { + var sugarTable = (SugarTable)sugarAttributeInfo; + result.DbTableName = sugarTable.TableName; + result.TableDescription = sugarTable.TableDescription.ToSqlFilter(); + result.IsDisabledUpdateAll = sugarTable.IsDisabledUpdateAll; + result.IsDisabledDelete = sugarTable.IsDisabledDelete; + result.IsCreateTableFiledSort = sugarTable.IsCreateTableFiledSort; + result.Discrimator = sugarTable.Discrimator; + } + var indexs = type.GetCustomAttributes(typeof(SugarIndexAttribute)); + if (indexs?.Any() == true) + { + result.Indexs = indexs.Select(it => it as SugarIndexAttribute).ToList(); + } + if (result.TableDescription.IsNullOrEmpty()) result.TableDescription = GetTableAnnotation(type); + if (this.Context.CurrentConnectionConfig.ConfigureExternalServices?.EntityNameService != null) + { + if (result.DbTableName == null) + { + result.DbTableName = type.Name; + } + this.Context.CurrentConnectionConfig.ConfigureExternalServices.EntityNameService(type, result); + } + result.Type = type; + result.EntityName = result.Type.Name; + result.Columns = new List(); + SetColumns(result); + return result; + } + + public string GetTableName() + { + return GetTableName(typeof(T)); + } + public string GetTableName(Type entityType) + { + var typeName = entityType.Name; + if (this.Context.MappingTables == null || this.Context.MappingTables.Count == 0 || !this.Context.MappingTables.Any(it => it.EntityName == typeName)) + { + var entity = this.GetEntityInfo(entityType); + if (entity.DbTableName.HasValue()) return entity.DbTableName; + else return entity.EntityName; + } + else + { + var mappingInfo = this.Context.MappingTables.SingleOrDefault(it => it.EntityName == typeName); + return mappingInfo == null ? typeName : mappingInfo.DbTableName; + } + } + public string GetTableName(string entityName) + { + var typeName = entityName; + if (this.Context.MappingTables == null || this.Context.MappingTables.Count == 0) return typeName; + else + { + var mappingInfo = this.Context.MappingTables.SingleOrDefault(it => it.EntityName == typeName); + return mappingInfo == null ? typeName : mappingInfo.DbTableName; + } + } + public string GetEntityName(string tableName) + { + if (this.Context.MappingTables == null || this.Context.MappingTables.Count == 0) return tableName; + else + { + var mappingInfo = this.Context.MappingTables.SingleOrDefault(it => it.DbTableName == tableName); + return mappingInfo == null ? tableName : mappingInfo.EntityName; + } + } + public string GetEntityName() + { + return this.Context.EntityMaintenance.GetEntityInfo().EntityName; + } + public string GetEntityName(Type type) + { + return this.Context.EntityMaintenance.GetEntityInfo(type).EntityName; + } + public string GetDbColumnName(string propertyName) + { + return GetDbColumnName(propertyName, typeof(T)); + } + public string GetDbColumnName(string propertyName, Type entityType) + { + var isAny = this.GetEntityInfo(entityType).Columns.Any(it => it.PropertyName.Equals(propertyName, StringComparison.CurrentCultureIgnoreCase)); + Check.Exception(!isAny, "Property " + propertyName + " is Invalid"); + var typeName = entityType.Name; + if (this.Context.MappingColumns == null || this.Context.MappingColumns.Count == 0 || !this.Context.MappingColumns.Any(it => it.EntityName == typeName && it.PropertyName == propertyName)) + { + var column = this.GetEntityInfo(entityType).Columns.First(it => it.PropertyName.Equals(propertyName, StringComparison.CurrentCultureIgnoreCase)); + if (column.DbColumnName.HasValue()) return column.DbColumnName; + else return column.PropertyName; + } + else + { + var mappingInfo = this.Context.MappingColumns.SingleOrDefault(it => it.EntityName == typeName && it.PropertyName == propertyName); + return mappingInfo == null ? propertyName : mappingInfo.DbColumnName; + } + } + public string GetPropertyName(string dbColumnName) + { + var columnInfo = this.Context.EntityMaintenance.GetEntityInfo().Columns.FirstOrDefault(it => it.DbColumnName.EqualCase(dbColumnName)); + if (columnInfo != null) + { + return columnInfo.PropertyName; + } + var typeName = typeof(T).Name; + if (this.Context.MappingColumns == null || this.Context.MappingColumns.Count == 0) return dbColumnName; + else + { + var mappingInfo = this.Context.MappingColumns.SingleOrDefault(it => it.EntityName == typeName && it.DbColumnName.Equals(dbColumnName, StringComparison.CurrentCultureIgnoreCase)); + return mappingInfo == null ? dbColumnName : mappingInfo.PropertyName; + } + } + public string GetPropertyName(string dbColumnName, Type entityType) + { + var columnInfo = this.Context.EntityMaintenance.GetEntityInfo(entityType).Columns.FirstOrDefault(it => it.DbColumnName.EqualCase(dbColumnName)); + if (columnInfo != null) + { + return columnInfo.PropertyName; + } + var typeName = entityType.Name; + if (this.Context.MappingColumns == null || this.Context.MappingColumns.Count == 0) return dbColumnName; + else + { + var mappingInfo = this.Context.MappingColumns.SingleOrDefault(it => it.EntityName == typeName && it.DbColumnName.Equals(dbColumnName, StringComparison.CurrentCultureIgnoreCase)); + return mappingInfo == null ? dbColumnName : mappingInfo.PropertyName; + } + } + public PropertyInfo GetProperty(string dbColumnName) + { + var propertyName = GetPropertyName(dbColumnName); + return typeof(T).GetProperties().First(it => it.Name == propertyName); + } + /// + /// Gets the text contents of this XML element node + /// + /// entity type + /// The value of the name attribute of the XML node + /// the text contents of this XML element node + public string GetXElementNodeValue(Type entityType, string nodeAttributeName) + { + + try + { + + if (this.Context.CurrentConnectionConfig?.MoreSettings?.IsNoReadXmlDescription == true) + { + return ""; + } + if (entityType.Assembly.IsDynamic && entityType.Assembly.FullName.StartsWith("Dynamic")) + { + return null; + } + if (entityType.Assembly.FullName.StartsWith("System.Linq.Dynamic.Core.DynamicClasses")) + { + return null; + } + var path = entityType.Assembly.Location; + if (string.IsNullOrEmpty(path)) + { + return null; + } + FileInfo file = new FileInfo(path); + string xmlPath = entityType.Assembly.Location.Replace(file.Extension, ".xml"); + if (!File.Exists(xmlPath)) + { + return string.Empty; + } + XElement xe = new ReflectionInoCacheService().GetOrCreate("EntityXml_" + xmlPath, () => XElement.Load(xmlPath)); + if (xe == null) + { + return string.Empty; + } + var xeNode = xe.Element("members").Elements("member").Where(ele => ele.Attribute("name").Value == nodeAttributeName).FirstOrDefault(); + if (xeNode == null) + { + return string.Empty; + } + var summary = xeNode.Element("summary"); + if (summary != null) + { + return summary.Value.ToSqlFilter().Trim(); + } + else + { + var summaryValue = xeNode.Elements().Where(x => x.Name.ToString().EqualCase("summary")).Select(it => it.Value).FirstOrDefault(); + if (summaryValue == null) + return string.Empty; + else + return summaryValue.ToSqlFilter().Trim() ?? ""; + } + + } + catch + { + Check.ExceptionEasy("ORM error reading entity class XML, check entity XML or disable reading XML: MoreSettings IsNoReadXmlDescription set to true (same place to set DbType)", "ORM读取实体类的XML出现错误,检查实体XML或者禁用读取XML: MoreSettings里面的IsNoReadXmlDescription设为true (设置DbType的同一个地方)"); + throw; + } + } + /// + /// Gets the code annotation for the database table + /// + /// entity type + /// the code annotation for the database table + public string GetTableAnnotation(Type entityType) + { + if (entityType.IsClass() == false) + { + return null; + } + var result = GetXElementNodeValue(entityType, $"T:{entityType.FullName}"); + if (string.IsNullOrEmpty(result)) + { + return null; + } + else + { + return result; + } + } + /// + /// Gets the code annotation for the field + /// + /// entity type + /// column name + /// the code annotation for the field + public string GetPropertyAnnotation(Type entityType, string dbColumnName) + { + if (entityType.IsClass() == false || entityType == typeof(object)) + { + return null; + } + + var result = GetXElementNodeValue(entityType, $"P:{entityType.FullName}.{dbColumnName}"); + if (string.IsNullOrEmpty(result)) + { + return GetPropertyAnnotation(entityType.BaseType, dbColumnName); + } + else + { + return result; + } + } + + #region Primary key + private void SetColumns(EntityInfo result) + { + foreach (var property in result.Type.GetProperties()) + { + EntityColumnInfo column = new EntityColumnInfo(); + //var isVirtual = property.GetGetMethod().IsVirtual; + //if (isVirtual) continue; + var navigat = property.GetCustomAttribute(typeof(Navigate)); + if (navigat != null) + { + column.IsIgnore = true; + column.Navigat = navigat as Navigate; + } + var sugarColumn = property.GetCustomAttributes(typeof(SugarColumn), true) + .Where(it => it is SugarColumn) + .Select(it => (SugarColumn)it) + .FirstOrDefault(); + column.ExtendedAttribute = sugarColumn?.ExtendedAttribute; + column.DbTableName = result.DbTableName; + column.EntityName = result.EntityName; + column.PropertyName = property.Name; + column.PropertyInfo = property; + column.UnderType = UtilMethods.GetUnderType(column.PropertyInfo.PropertyType); + if (sugarColumn?.IsOwnsOne == true) + { + SetValueObjectColumns(result, property, column); + } + if (sugarColumn.IsNullOrEmpty()) + { + column.DbColumnName = property.Name; + } + else + { + + if (sugarColumn.IsIgnore == false) + { + column.DbColumnName = sugarColumn.ColumnName.IsNullOrEmpty() ? property.Name : sugarColumn.ColumnName; + column.IsPrimarykey = sugarColumn.IsPrimaryKey; + column.IsIdentity = sugarColumn.IsIdentity; + column.ColumnDescription = sugarColumn.ColumnDescription.ToSqlFilter(); + column.IsNullable = sugarColumn.IsNullable; + column.Length = sugarColumn.Length; + column.OldDbColumnName = sugarColumn.OldColumnName; + column.DataType = sugarColumn.ColumnDataType; + column.DecimalDigits = sugarColumn.DecimalDigits; + column.OracleSequenceName = sugarColumn.OracleSequenceName; + column.IsOnlyIgnoreInsert = sugarColumn.IsOnlyIgnoreInsert; + column.IsEnableUpdateVersionValidation = sugarColumn.IsEnableUpdateVersionValidation; + column.IsTranscoding = sugarColumn.IsTranscoding; + column.SerializeDateTimeFormat = sugarColumn.SerializeDateTimeFormat; + column.IsJson = sugarColumn.IsJson; + column.NoSerialize = sugarColumn.NoSerialize; + column.DefaultValue = sugarColumn.DefaultValue; + column.IndexGroupNameList = sugarColumn.IndexGroupNameList; + column.UIndexGroupNameList = sugarColumn.UniqueGroupNameList; + column.IsOnlyIgnoreUpdate = sugarColumn.IsOnlyIgnoreUpdate; + column.IsArray = sugarColumn.IsArray; + column.IsTreeKey = sugarColumn.IsTreeKey; + column.SqlParameterDbType = sugarColumn.SqlParameterDbType; + column.SqlParameterSize = sugarColumn.SqlParameterSize; + column.CreateTableFieldSort = sugarColumn.CreateTableFieldSort; + column.InsertServerTime = sugarColumn.InsertServerTime; + column.InsertSql = sugarColumn.InsertSql; + column.UpdateServerTime = sugarColumn.UpdateServerTime; + column.UpdateSql = sugarColumn.UpdateSql; + column.IsDisabledAlterColumn = sugarColumn.IsDisabledAlterColumn; + column.QuerySql = sugarColumn.QuerySql; + if (sugarColumn.IsJson && String.IsNullOrEmpty(sugarColumn.ColumnDataType)) + { + if (this.Context.CurrentConnectionConfig.DbType == DbType.PostgreSQL) + { + column.DataType = "json"; + } + else if (column.Length > 0) + { + column.DataType = "varchar"; + } + else + { + column.DataType = "varchar(4000)"; + } + } + else if (typeof(Nvarchar2PropertyConvert).Equals(sugarColumn.SqlParameterDbType) && column.DataType == null) + { + column.DataType = "nvarchar2"; + if (column.Length == 0) + { + column.Length = 200; + } + } + if (column.IsPrimarykey && column.IsOnlyIgnoreUpdate) + { + column.IsOnlyIgnoreUpdate = false; + } + } + else + { + column.IsIgnore = true; + column.NoSerialize = sugarColumn.NoSerialize; + column.ColumnDescription = sugarColumn.ColumnDescription; + column.IsJson = sugarColumn.IsJson; + column.IsArray = sugarColumn.IsArray; + } + } + if (column.ColumnDescription.IsNullOrEmpty()) column.ColumnDescription = GetPropertyAnnotation(result.Type, column.PropertyName); + if (this.Context.MappingColumns.HasValue()) + { + var golbalMappingInfo = this.Context.MappingColumns.FirstOrDefault(it => it.EntityName.Equals(result.EntityName, StringComparison.CurrentCultureIgnoreCase) && it.PropertyName == column.PropertyName); + if (golbalMappingInfo != null) + column.DbColumnName = golbalMappingInfo.DbColumnName; + } + if (this.Context.IgnoreColumns.HasValue()) + { + var golbalMappingInfo = this.Context.IgnoreColumns.FirstOrDefault(it => it.EntityName.Equals(result.EntityName, StringComparison.CurrentCultureIgnoreCase) && it.PropertyName == column.PropertyName); + if (golbalMappingInfo != null) + column.IsIgnore = true; + } + if (this.Context.CurrentConnectionConfig.ConfigureExternalServices?.EntityService != null) + { + if (!column.EntityName.ObjToString().StartsWith("<>f__AnonymousType") + && column.PropertyInfo?.ReflectedType != typeof(DbTableInfo)) + { + var isOldOwnsOne = column.IsOwnsOne; + this.Context.CurrentConnectionConfig.ConfigureExternalServices.EntityService(property, column); + if (column.IsOwnsOne == true && isOldOwnsOne == false) + { + SetValueObjectColumns(result, property, column); + continue; + } + } + } + if (column.PropertyInfo.DeclaringType != null + && column.PropertyInfo.DeclaringType != result.Type + && result.Columns.Any(x => x.PropertyName == column.PropertyName)) + { + continue; + } + if (column.DataType == null && property?.PropertyType.Name.IsIn("TimeOnly") == true) + { + column.DataType = "time"; + } + if (column.DataType == null && property?.PropertyType.Name.IsIn("DateOnly") == true) + { + column.DataType = "date"; + } + if (column.DataType == null && column.UnderType == typeof(TimeSpan)) + { + column.DataType = "time"; + column.Length = 0; + column.DecimalDigits = 0; + } + if (column.OracleSequenceName.HasValue() && + this.Context.CurrentConnectionConfig?.MoreSettings?.EnableOracleIdentity == true) + { + column.OracleSequenceName = null; + } + result.Columns.Add(column); + } + } + + private void SetValueObjectColumns(EntityInfo result, PropertyInfo property, EntityColumnInfo column) + { + column.IsIgnore = true; + column.IsOwnsOne = true; + Check.ExceptionEasy(property.PropertyType.IsClass() == false, column.PropertyName + " IsOwnsOne必须用在类上面", column.PropertyName + "IsOwnsOne must be used on the class"); + Check.ExceptionEasy(property.PropertyType.FullName.IsCollectionsList() == true, column.PropertyName + " IsOwnsOne必须用在类上面", column.PropertyName + "IsOwnsOne must be used on the class"); + var ownsOne = this.GetEntityInfoNoCache(property.PropertyType); + foreach (var item in ownsOne.Columns) + { + if (result.Columns.Any(it => it.PropertyName.EqualCase(item.PropertyName) || it.DbColumnName.EqualCase(item.DbColumnName))) + { + Check.ExceptionEasy($" {result.EntityName} " + item.PropertyName + " 存在重复定义 (IsOwnsOne) ", $" {result.EntityName} " + item.PropertyName + " Duplicate definition exists (IsOwnsOne)"); + } + item.ForOwnsOnePropertyInfo = column.PropertyInfo; + result.Columns.Add(item); + } + } + #endregion + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/DeleteNavManyToMany.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/DeleteNavManyToMany.cs new file mode 100644 index 000000000..f725bd57b --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/DeleteNavManyToMany.cs @@ -0,0 +1,81 @@ +namespace SqlSugar +{ + public partial class DeleteNavProvider where T : class, new() where Root : class, new() + { + private void DeleteManyToMany(string name, EntityColumnInfo nav) where TChild : class, new() + { + var parentEntity = _ParentEntity; + var parentList = _ParentList.Cast().ToList(); + var parentPkColumn = parentEntity.Columns.FirstOrDefault(it => it.IsPrimarykey == true); + var parentNavigateProperty = parentEntity.Columns.FirstOrDefault(it => it.PropertyName == name); + var thisEntity = this._Context.EntityMaintenance.GetEntityInfo(); + var thisPkColumn = thisEntity.Columns.FirstOrDefault(it => it.IsPrimarykey == true); + Check.ExceptionEasy(thisPkColumn == null, $"{thisPkColumn.EntityName} need primary key", $"{thisPkColumn.EntityName}需要主键"); + Check.ExceptionEasy(parentPkColumn == null, $"{parentPkColumn.EntityName} need primary key", $"{parentPkColumn.EntityName}需要主键"); + var mappingType = parentNavigateProperty.Navigat.MappingType; + var mappingEntity = this._Context.EntityMaintenance.GetEntityInfo(mappingType); + var mappingA = mappingEntity.Columns.FirstOrDefault(x => x.PropertyName == parentNavigateProperty.Navigat.MappingAId); + var mappingB = mappingEntity.Columns.FirstOrDefault(x => x.PropertyName == parentNavigateProperty.Navigat.MappingBId); + Check.ExceptionEasy(mappingA == null || mappingB == null, $"Navigate property {name} error ", $"导航属性{name}配置错误"); + var mappingPk = mappingEntity.Columns + .Where(it => it.PropertyName != mappingA.PropertyName) + .Where(it => it.PropertyName != mappingB.PropertyName) + .Where(it => it.IsPrimarykey && !it.IsIdentity && it.OracleSequenceName.IsNullOrEmpty()).FirstOrDefault(); + + if (IsDeleteA()) + { + if (!_IsDeletedParant) + SetContext(() => this._Context.Deleteable(parentList) + .EnableDiffLogEventIF(_RootOptions?.IsDiffLogEvent == true, _RootOptions?.DiffLogBizData) + .ExecuteCommand()); + } + + var aids = _ParentList.Select(it => parentPkColumn.PropertyInfo.GetValue(it)).ToList(); + var bids = _Context.Queryable().Filter(mappingEntity.Type).AS(mappingEntity.DbTableName).In(mappingA.DbColumnName, aids) + .Select(mappingB.DbColumnName).ToDataTable() + .Rows.Cast().Select(it => it[0]).ToList(); + + + var childList = GetChildList().In(thisPkColumn.DbColumnName, bids).ToList(); + if (_WhereList.HasValue()) + { + bids = childList.Select(it => thisPkColumn.PropertyInfo.GetValue(it)).ToList(); + } + + + if (IsDeleteB()) + { + SetContext(() => _Context.Deleteable(childList).ExecuteCommand()); + } + + this._ParentList = childList.Cast().ToList(); + this._ParentPkColumn = thisPkColumn; + this._IsDeletedParant = true; + + + if (_WhereList.HasValue()) + { + SetContext(() => _Context.Deleteable().AS(mappingEntity.DbTableName) + .In(mappingA.DbColumnName, aids) + .In(mappingB.DbColumnName, bids) + .ExecuteCommand()); + } + else + { + SetContext(() => _Context.Deleteable().AS(mappingEntity.DbTableName).In( + mappingA.DbColumnName, aids + ).ExecuteCommand()); + } + + } + + private bool IsDeleteA() + { + return deleteNavOptions?.ManyToManyIsDeleteA == true; + } + private bool IsDeleteB() + { + return deleteNavOptions?.ManyToManyIsDeleteB == true; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/DeleteNavOneToMany.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/DeleteNavOneToMany.cs new file mode 100644 index 000000000..52e0bcd82 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/DeleteNavOneToMany.cs @@ -0,0 +1,106 @@ +namespace SqlSugar +{ + public partial class DeleteNavProvider where T : class, new() where Root : class, new() + { + + private void DeleteOneToMany(string name, EntityColumnInfo nav) where TChild : class, new() + { + var parentEntity = _ParentEntity; + var prentList = _ParentList.Cast().ToList(); + var parentNavigateProperty = parentEntity.Columns.FirstOrDefault(it => it.PropertyName == name); + var thisEntity = this._Context.EntityMaintenance.GetEntityInfo(); + var thisPkColumn = GetPkColumnByNav(thisEntity, nav); + var thisFkColumn = GetFKColumnByNav(thisEntity, nav); + EntityColumnInfo parentPkColumn = GetParentPkColumn(); + EntityColumnInfo parentNavColumn = GetParentPkNavColumn(nav); + if (parentNavColumn != null) + { + parentPkColumn = parentNavColumn; + } + + if (!_IsDeletedParant) + SetContext(() => this._Context.Deleteable(prentList) + .EnableDiffLogEventIF(_RootOptions?.IsDiffLogEvent == true, _RootOptions?.DiffLogBizData) + .ExecuteCommand()); + + var ids = _ParentList.Select(it => parentPkColumn.PropertyInfo.GetValue(it)).ToList(); + var childList = GetChildList().In(thisFkColumn.DbColumnName, ids).ToList(); + + this._ParentList = childList.Cast().ToList(); + this._ParentPkColumn = thisPkColumn; + this._IsDeletedParant = true; + + SetContext(() => this._Context.Deleteable(childList).ExecuteCommand()); + } + + private ISugarQueryable GetChildList() where TChild : class, new() + { + var queryable = this._Context.Queryable(); + if (_WhereList.HasValue()) + { + foreach (var item in _WhereList) + { + queryable.Where(item); + } + queryable.AddParameters(_Parameters); + } + return queryable; + } + + private void SetContext(Action action) + { + var key = "_DeleteNavTask"; + if (this._Context.TempItems == null) + { + this._Context.TempItems = new Dictionary(); + } + if (!this._Context.TempItems.TryGetValue(key, out object? oldTask)) + { + oldTask = null; + this._Context.TempItems.Add(key, oldTask); + } + + var newTask = new List(); + if (oldTask != null) + { + newTask = (List)oldTask; + } + newTask.Add(action); + this._Context.TempItems[key] = newTask; + } + + private EntityColumnInfo GetParentPkColumn() + { + EntityColumnInfo parentPkColumn = _ParentPkColumn; + if (_ParentPkColumn == null) + { + parentPkColumn = _ParentPkColumn = this._ParentEntity.Columns.FirstOrDefault(it => it.IsPrimarykey); + } + return parentPkColumn; + } + private EntityColumnInfo GetParentPkNavColumn(EntityColumnInfo nav) + { + EntityColumnInfo result = null; + if (nav.Navigat.Name2.HasValue()) + { + result = _ParentPkColumn = this._ParentEntity.Columns.FirstOrDefault(it => it.PropertyName == nav.Navigat.Name2); + } + return result; + } + + private EntityColumnInfo GetPkColumnByNav(EntityInfo entity, EntityColumnInfo nav) + { + var pkColumn = entity.Columns.FirstOrDefault(it => it.IsPrimarykey == true); + if (nav.Navigat.Name2.HasValue()) + { + pkColumn = entity.Columns.FirstOrDefault(it => it.PropertyName == nav.Navigat.Name2); + } + return pkColumn; + } + private EntityColumnInfo GetFKColumnByNav(EntityInfo entity, EntityColumnInfo nav) + { + var fkColumn = entity.Columns.FirstOrDefault(it => it.PropertyName == nav.Navigat.Name); + return fkColumn; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/DeleteNavOneToOne.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/DeleteNavOneToOne.cs new file mode 100644 index 000000000..8174a1a78 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/DeleteNavOneToOne.cs @@ -0,0 +1,33 @@ +namespace SqlSugar +{ + public partial class DeleteNavProvider where T : class, new() where Root : class, new() + { + private void DeleteOneToOne(string name, EntityColumnInfo nav) where TChild : class, new() + { + var parentEntity = _ParentEntity; + var parentList = _ParentList.Cast().ToList(); + var parentColumn = parentEntity.Columns.FirstOrDefault(it => it.PropertyName == nav.Navigat.Name); + var parentPkColumn = parentEntity.Columns.FirstOrDefault(it => it.IsPrimarykey); + var thisEntity = this._Context.EntityMaintenance.GetEntityInfo(); + EntityColumnInfo thisPkColumn = GetPkColumnByNav(thisEntity, nav); + Check.Exception(thisPkColumn == null, $" Navigate {parentEntity.EntityName} : {name} is error ", $"导航实体 {parentEntity.EntityName} 属性 {name} 配置错误"); + + + if (!_IsDeletedParant) + SetContext(() => this._Context.Deleteable(parentList) + .EnableDiffLogEventIF(_RootOptions?.IsDiffLogEvent == true, _RootOptions?.DiffLogBizData) + .ExecuteCommand()); + + Check.ExceptionEasy(parentColumn == null, "The one-to-one navigation configuration is incorrect", "一对一导航配置错误"); + var ids = _ParentList.Select(it => parentColumn.PropertyInfo.GetValue(it)).ToList(); + List childList = this._Context.Queryable().In(thisPkColumn.DbColumnName, ids).ToList(); + + this._ParentList = childList.Cast().ToList(); + this._ParentPkColumn = thisPkColumn; + this._IsDeletedParant = true; + + SetContext(() => this._Context.Deleteable(childList).ExecuteCommand()); + } + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/DeleteNavProvider.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/DeleteNavProvider.cs new file mode 100644 index 000000000..f091bf81e --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/DeleteNavProvider.cs @@ -0,0 +1,167 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public partial class DeleteNavProvider where T : class, new() where Root : class, new() + { + internal DeleteNavOptions deleteNavOptions; + public List _Roots { get; set; } + public List _ParentList { get; set; } + public List _RootList { get; set; } + public EntityInfo _ParentEntity { get; set; } + public EntityColumnInfo _ParentPkColumn { get; set; } + public SqlSugarProvider _Context { get; set; } + internal DeleteNavRootOptions _RootOptions { get; set; } + public bool _IsDeletedParant { get; set; } + public List _WhereList = new List(); + public List _Parameters = new List(); + public DeleteNavProvider ThenInclude(Expression> expression) + where TChild : class, new() + { + this._Context.InitMappingInfo(); + InitParentList(); + Expression newExp = GetMamber(expression); + var name = ExpressionTool.GetMemberName(expression); + var nav = this._ParentEntity.Columns.FirstOrDefault(x => x.PropertyName == name); + if (nav.Navigat == null) + { + Check.ExceptionEasy($"{name} no navigate attribute", $"{this._ParentEntity.EntityName}的属性{name}没有导航属性"); + } + if (nav.Navigat.NavigatType == NavigateType.OneToOne || nav.Navigat.NavigatType == NavigateType.ManyToOne) + { + DeleteOneToOne(name, nav); + } + else if (nav.Navigat.NavigatType == NavigateType.OneToMany) + { + DeleteOneToMany(name, nav); + } + else + { + DeleteManyToMany(name, nav); + } + return GetResult(); + } + + public DeleteNavProvider ThenInclude(Expression>> expression) + where TChild : class, new() + { + this._Context.InitMappingInfo(); + InitParentList(); + Expression newExp = GetMamber(expression); + var name = ExpressionTool.GetMemberName(newExp); + var nav = this._ParentEntity.Columns.FirstOrDefault(x => x.PropertyName == name); + if (nav.Navigat == null) + { + Check.ExceptionEasy($"{name} no navigate attribute", $"{this._ParentEntity.EntityName}的属性{name}没有导航属性"); + } + if (nav.Navigat.NavigatType == NavigateType.OneToOne || nav.Navigat.NavigatType == NavigateType.ManyToOne) + { + DeleteOneToOne(name, nav); + } + else if (nav.Navigat.NavigatType == NavigateType.OneToMany) + { + DeleteOneToMany(name, nav); + } + else + { + DeleteManyToMany(name, nav); + } + return GetResult(); + } + + private Expression GetMamber(Expression expression) + { + int i = 0; + Expression newExp = ExpressionTool.GetLambdaExpressionBody(expression); + while (newExp is MethodCallExpression) + { + var callMethod = (newExp as MethodCallExpression); + ActionMethodCallExpression(callMethod); + newExp = callMethod.Arguments[0]; + i++; + Check.Exception(i > 10000, expression + " is error"); + } + return newExp; + } + + + private void ActionMethodCallExpression(MethodCallExpression method) + { + var queryBuilder = GetQueryBuilder(); + NavigatManager navigatManager = new NavigatManager() + { + Context = this._Context + }; + if (method.Method.Name == "ToList") + { + + } + else if (method.Method.Name == "Where") + { + navigatManager.CheckHasRootShortName(method.Arguments[0], method.Arguments[1]); + var exp = method.Arguments[1]; + _WhereList.Add(" " + queryBuilder.GetExpressionValue(exp, ResolveExpressType.WhereSingle).GetString()); + } + else if (method.Method.Name == "WhereIF") + { + var isOk = LambdaExpression.Lambda(method.Arguments[1]).Compile().DynamicInvoke(); + if (isOk.ObjToBool()) + { + var exp = method.Arguments[2]; + navigatManager.CheckHasRootShortName(method.Arguments[1], method.Arguments[2]); + _WhereList.Add(" " + queryBuilder.GetExpressionValue(exp, ResolveExpressType.WhereSingle).GetString()); + } + } + if (queryBuilder.Parameters != null) + { + _Parameters.AddRange(queryBuilder.Parameters); + } + } + + private QueryBuilder GetQueryBuilder() + { + return this._Context.Queryable().QueryBuilder; + } + + private DeleteNavProvider GetResult() where TChild : class, new() + { + return new DeleteNavProvider() + { + _Context = this._Context, + _ParentEntity = this._ParentEntity, + _ParentList = this._ParentList, + _Roots = this._Roots, + _ParentPkColumn = this._ParentPkColumn, + _RootList = this._RootList, + _IsDeletedParant = this._IsDeletedParant + }; + } + public DeleteNavProvider AsNav() + { + return new DeleteNavProvider + { + _Context = _Context, + _ParentEntity = null, + _ParentList = null, + _Roots = _Roots, + _IsDeletedParant = this._IsDeletedParant, + _ParentPkColumn = this._Context.EntityMaintenance.GetEntityInfo().Columns.First(it => it.IsPrimarykey) + }; + } + private void InitParentList() + { + this._ParentEntity = this._Context.EntityMaintenance.GetEntityInfo(); + if (_RootList == null) + { + _RootList = _ParentList = _Roots.Cast().ToList(); + } + else if (_ParentList == null) + { + _ParentList = _RootList; + var pkColumn = this._Context.EntityMaintenance.GetEntityInfo().Columns.FirstOrDefault(it => it.IsPrimarykey); + this._ParentPkColumn = pkColumn; + } + + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/DeleteNavTask.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/DeleteNavTask.cs new file mode 100644 index 000000000..910abaee3 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/DeleteNavTask.cs @@ -0,0 +1,175 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public class DeleteNavTaskInit where T : class, new() where Root : class, new() + { + internal List Roots { get; set; } + internal SqlSugarProvider Context { get; set; } + internal DeleteNavProvider deleteNavProvider { get; set; } + public DeleteNavMethodInfo IncludesAllFirstLayer(params string[] ignoreColumns) + { + if (ignoreColumns == null) + { + ignoreColumns = Array.Empty(); + } + this.Context = deleteNavProvider._Context; + var navColumns = this.Context.EntityMaintenance.GetEntityInfo().Columns.Where(it => !ignoreColumns.Contains(it.PropertyName) || !ignoreColumns.Any(z => z.EqualCase(it.DbColumnName))).Where(it => it.Navigat != null).ToList(); + var updateNavs = this; + DeleteNavMethodInfo methodInfo = updateNavs.IncludeByNameString(navColumns[0].PropertyName); + foreach (var item in navColumns.Skip(1)) + { + methodInfo = methodInfo.IncludeByNameString(item.PropertyName); + } + return methodInfo; + } + public DeleteNavTask Include(Expression> expression) where TChild : class, new() + { + this.Context = deleteNavProvider._Context; + DeleteNavTask result = new DeleteNavTask(); + Func> func = () => deleteNavProvider.ThenInclude(expression); + result.PreFunc = func; + result.Context = this.Context; + return result; + } + public DeleteNavMethodInfo IncludeByNameString(string navMemberName, DeleteNavOptions deleteNavOptions = null) + { + DeleteNavMethodInfo result = new DeleteNavMethodInfo(); + result.Context = deleteNavProvider._Context; + var entityInfo = result.Context.EntityMaintenance.GetEntityInfo(); + Type properyItemType; + bool isList; + Expression exp = UtilMethods.GetIncludeExpression(navMemberName, entityInfo, out properyItemType, out isList); + var method = this.GetType().GetMyMethod("Include", 2, isList) + .MakeGenericMethod(properyItemType); + var obj = method.Invoke(this, new object[] { exp, deleteNavOptions }); + result.MethodInfos = obj; + return result; + } + public DeleteNavTask Include(Expression>> expression) where TChild : class, new() + { + this.Context = deleteNavProvider._Context; + DeleteNavTask result = new DeleteNavTask(); + Func> func = () => deleteNavProvider.ThenInclude(expression); + result.PreFunc = func; + result.Context = this.Context; + return result; + } + public DeleteNavTask Include(Expression>> expression, DeleteNavOptions deleteNavOptions) where TChild : class, new() + { + var result = Include(expression); + deleteNavProvider.deleteNavOptions = deleteNavOptions; + return result; + } + } + public class DeleteNavTask where T : class, new() where Root : class, new() + { + public SqlSugarProvider Context { get; set; } + public Func> PreFunc { get; set; } + public DeleteNavTask ThenInclude(Expression> expression) where TChild : class, new() + { + DeleteNavTask result = new DeleteNavTask(); + Func> func = () => PreFunc().ThenInclude(expression); + result.PreFunc = func; + result.Context = this.Context; + return result; + } + public DeleteNavTask ThenInclude(Expression> expression, DeleteNavOptions deleteNavOptions) where TChild : class, new() + { + DeleteNavTask result = new DeleteNavTask(); + Func> func = () => + { + var dev = PreFunc(); + dev.deleteNavOptions = deleteNavOptions; + return dev.ThenInclude(expression); + }; + result.PreFunc = func; + result.Context = this.Context; + return result; + } + public DeleteNavTask ThenInclude(Expression>> expression) where TChild : class, new() + { + DeleteNavTask result = new DeleteNavTask(); + Func> func = () => PreFunc().ThenInclude(expression); + result.PreFunc = func; + result.Context = this.Context; + return result; + } + public DeleteNavTask ThenInclude(Expression>> expression, DeleteNavOptions deleteNavOptions) where TChild : class, new() + { + DeleteNavTask result = new DeleteNavTask(); + Func> func = () => + { + var dev = PreFunc(); + dev.deleteNavOptions = deleteNavOptions; + return dev.ThenInclude(expression); + }; + result.PreFunc = func; + result.Context = this.Context; + return result; + } + public DeleteNavTask Include(Expression> expression) where TChild : class, new() + { + return AsNav().ThenInclude(expression); + } + public DeleteNavTask Include(Expression> expression, DeleteNavOptions options) where TChild : class, new() + { + return AsNav().ThenInclude(expression, options); + } + public DeleteNavTask Include(Expression>> expression) where TChild : class, new() + { + return AsNav().ThenInclude(expression); + } + public DeleteNavTask Include(Expression>> expression, DeleteNavOptions options) where TChild : class, new() + { + return AsNav().ThenInclude(expression, options); + } + public bool ExecuteCommand() + { + PreFunc(); + + var hasTran = this.Context.Ado.Transaction != null; + if (hasTran) + { + ExecTasks(); + } + else + { + this.Context.Ado.UseTran(() => + { + ExecTasks(); + + }, ex => throw ex); + } + return true; + } + public async Task ExecuteCommandAsync() + { + await Task.Run(async () => + { + ExecuteCommand(); + await Task.Delay(0).ConfigureAwait(false); + }).ConfigureAwait(false); + return true; + } + + private DeleteNavTask AsNav() + { + DeleteNavTask result = new DeleteNavTask(); + Func> func = () => PreFunc().AsNav(); + result.PreFunc = func; + result.Context = this.Context; + return result; + } + private void ExecTasks() + { + var tasks = (List)this.Context.TempItems["_DeleteNavTask"]; + tasks.Reverse(); + foreach (var task in tasks) + { + task(); + } + this.Context.TempItems.Remove("_DeleteNavTask"); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/InsertNavProvider.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/InsertNavProvider.cs new file mode 100644 index 000000000..7c39cde97 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/InsertNavProvider.cs @@ -0,0 +1,145 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public partial class InsertNavProvider where T : class, new() where Root : class, new() + { + public InsertNavRootOptions _RootOptions { get; set; } + public List _Roots { get; set; } + public List _ParentList { get; set; } + public List _RootList { get; set; } + public EntityInfo _ParentEntity { get; set; } + public EntityColumnInfo _ParentPkColumn { get; set; } + public SqlSugarProvider _Context { get; set; } + public NavigateType? _NavigateType { get; set; } + public bool IsFirst { get; set; } + public InsertNavOptions _navOptions { get; set; } + public bool IsNav { get; internal set; } + internal NavContext NavContext { get; set; } + + public InsertNavProvider AsNav() + { + return new InsertNavProvider + { + _Context = _Context, + _ParentEntity = null, + _ParentList = null, + _Roots = _Roots, + _ParentPkColumn = this._Context.EntityMaintenance.GetEntityInfo().Columns.First(it => it.IsPrimarykey) + }; + } + + public InsertNavProvider ThenInclude(Expression> expression, InsertNavOptions options) where TChild : class, new() + { + _navOptions = options; + return _ThenInclude(expression); + } + public InsertNavProvider ThenInclude(Expression>> expression, InsertNavOptions options) where TChild : class, new() + { + _navOptions = options; + return _ThenInclude(expression); + } + + public InsertNavProvider ThenInclude(Expression> expression) where TChild : class, new() + { + return _ThenInclude(expression); + } + public InsertNavProvider ThenInclude(Expression>> expression) where TChild : class, new() + { + return _ThenInclude(expression); + } + + + + private InsertNavProvider _ThenInclude(Expression> expression) where TChild : class, new() + { + var name = ExpressionTool.GetMemberName(expression); + var isRoot = false; + if (this._ParentEntity == null) + { + this._ParentEntity = this._Context.EntityMaintenance.GetEntityInfo(); + this.IsFirst = true; + isRoot = true; + } + var nav = this._ParentEntity.Columns.FirstOrDefault(x => x.PropertyName == name); + if (nav.Navigat == null) + { + Check.ExceptionEasy($"{name} no navigate attribute", $"{this._ParentEntity.EntityName}的属性{name}没有导航属性"); + } + if (nav.Navigat.NavigatType == NavigateType.OneToOne || nav.Navigat.NavigatType == NavigateType.ManyToOne) + { + InitParentList(); + InsertOneToOne(name, nav); + } + else if (nav.Navigat.NavigatType == NavigateType.OneToMany) + { + _NavigateType = NavigateType.OneToMany; + InitParentList(); + InsertOneToMany(name, nav); + } + else + { + InitParentList(); + InsertManyToMany(name, nav); + } + AddContextInfo(name, isRoot); + return GetResult(); + } + + private InsertNavProvider _ThenInclude(Expression>> expression) where TChild : class, new() + { + var name = ExpressionTool.GetMemberName(expression); + if (name == null) + { + name = ExpressionTool.GetMemberNameByMethod(expression, name); + } + var isRoot = false; + if (this._ParentEntity == null) + { + this._ParentEntity = this._Context.EntityMaintenance.GetEntityInfo(); + IsFirst = true; + isRoot = true; + } + var nav = this._ParentEntity.Columns.FirstOrDefault(x => x.PropertyName == name); + if (nav.Navigat == null) + { + Check.ExceptionEasy($"{name} no navigate attribute", $"{this._ParentEntity.EntityName}的属性{name}没有导航属性"); + } + if (nav.Navigat.NavigatType == NavigateType.OneToOne || nav.Navigat.NavigatType == NavigateType.ManyToOne) + { + InitParentList(); + InsertOneToOne(name, nav); + } + else if (nav.Navigat.NavigatType == NavigateType.OneToMany) + { + _NavigateType = NavigateType.OneToMany; + InitParentList(); + InsertOneToMany(name, nav); + } + else + { + InitParentList(); + InsertManyToMany(name, nav); + } + AddContextInfo(name, isRoot); + return GetResult(); + } + + private void AddContextInfo(string name, bool isRoot) + { + if (IsNav || isRoot) + { + if (this.NavContext?.Items != null) + { + this.NavContext.Items.Add(new NavContextItem() { Level = 0, RootName = name }); + } + } + } + private bool NotAny(string name) + { + if (IsFirst) return true; + if (this.NavContext == null) return true; + return this.NavContext?.Items?.Any(it => it.RootName == name) == false; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/InsertNavProviderHelper.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/InsertNavProviderHelper.cs new file mode 100644 index 000000000..b527ffc32 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/InsertNavProviderHelper.cs @@ -0,0 +1,206 @@ +namespace SqlSugar +{ + public partial class InsertNavProvider where T : class, new() where Root : class, new() + { + + private static bool IsDefaultValue(object pvValue) + { + return pvValue?.Equals(UtilMethods.GetDefaultValue(pvValue.GetType())) != false; + } + private void InitParentList() + { + if (_RootList == null) + { + _RootList = _ParentList = GetRootList(_Roots).Cast().ToList(); + } + else if (_ParentList == null) + { + _ParentList = _RootList; + var pkColumn = this._Context.EntityMaintenance.GetEntityInfo().Columns.FirstOrDefault(it => it.IsPrimarykey); + this._ParentPkColumn = pkColumn; + } + IsFirst = false; + } + + private InsertNavProvider GetResult() where TChild : class, new() + { + return new InsertNavProvider() + { + _Context = this._Context, + _ParentEntity = this._ParentEntity, + _ParentList = this._ParentList, + _Roots = this._Roots, + _ParentPkColumn = this._ParentPkColumn, + _RootList = this._RootList + }; + } + + private List GetRootList(List datas) where Type : class, new() + { + List result = new List(); + this._Context.InitMappingInfo(); + var entity = this._Context.EntityMaintenance.GetEntityInfo(); + var pkColumn = entity.Columns.FirstOrDefault(it => it.IsPrimarykey); + InsertDatas(datas, pkColumn); + this._ParentEntity = entity; + result = datas; + return result; + } + + private void InsertIdentity(List datas) where Type : class, new() + { + foreach (var item in datas) + { + if (IsFirst && _RootOptions != null) + { + this._Context.Insertable(item) + .IgnoreColumns(_RootOptions.IgnoreColumns) + .InsertColumns(_RootOptions.InsertColumns) + .EnableDiffLogEventIF(_RootOptions.IsDiffLogEvent, _RootOptions.DiffLogBizData) + .ExecuteCommandIdentityIntoEntity(); + } + else + { + this._Context.Insertable(item).ExecuteCommandIdentityIntoEntity(); + } + } + } + + private EntityColumnInfo GetPkColumnByNav(EntityInfo entity, EntityColumnInfo nav) + { + var pkColumn = entity.Columns.FirstOrDefault(it => it.IsPrimarykey == true); + if (nav.Navigat.Name2.HasValue()) + { + pkColumn = entity.Columns.FirstOrDefault(it => it.PropertyName == nav.Navigat.Name2); + } + return pkColumn; + } + private EntityColumnInfo GetPkColumnByNav2(EntityInfo entity, EntityColumnInfo nav) + { + var pkColumn = entity.Columns.FirstOrDefault(it => it.IsPrimarykey == true); + return pkColumn; + } + private EntityColumnInfo GetFKColumnByNav(EntityInfo entity, EntityColumnInfo nav) + { + var fkColumn = entity.Columns.FirstOrDefault(it => it.PropertyName == nav.Navigat.Name); + return fkColumn; + } + private void InsertDatas(List children, EntityColumnInfo pkColumn, EntityColumnInfo NavColumn = null) where TChild : class, new() + { + children = children.Distinct().ToList(); + if (pkColumn == null) + { + Check.ExceptionEasy($"{typeof(TChild).Name} need primary key ", $"{typeof(TChild).Name}需要主键"); + } + var x = this._Context.Storageable(children).WhereColumns(new string[] { pkColumn.PropertyName }).GetStorageableResult(); + var insertData = children = x.InsertList.Select(it => it.Item).ToList(); + var IsNoExistsNoInsert = _navOptions?.OneToManyIfExistsNoInsert == true; + if (_NavigateType == NavigateType.OneToMany && IsFirst == false && IsNoExistsNoInsert == false) + { + var updateData = x.UpdateList.Select(it => it.Item).ToList(); + ClearPk(updateData, pkColumn); + insertData.AddRange(updateData); + } + else if (_NavigateType == NavigateType.OneToMany && IsNoExistsNoInsert == true) + { + children = new List(); + children.AddRange(x.InsertList.Select(it => it.Item).ToList()); + var updateData = x.UpdateList.Select(it => it.Item).ToList(); + children.AddRange(updateData); + } + Check.ExceptionEasy(pkColumn == null && NavColumn == null, $"The entity is invalid", $"实体错误无法使用导航"); + InitData(pkColumn, insertData); + this._ParentList = children.Cast().ToList(); + } + + private void ClearPk(List updateData, EntityColumnInfo pkColumn) where TChild : class, new() + { + foreach (var child in updateData) + { + var defaultValue = UtilMethods.DefaultForType(pkColumn.PropertyInfo.PropertyType); + pkColumn.PropertyInfo.SetValue(child, defaultValue); + } + } + + private void InitData(EntityColumnInfo pkColumn, List insertData) where TChild : class, new() + { + if (pkColumn.IsIdentity || pkColumn.OracleSequenceName.HasValue()) + { + InsertIdentity(insertData); + } + else if (pkColumn.UnderType == UtilConstants.LongType) + { + SetValue(pkColumn, insertData, () => SnowFlakeSingle.Instance.NextId()); + } + else if (pkColumn.UnderType == UtilConstants.GuidType) + { + SetValue(pkColumn, insertData, () => Guid.NewGuid()); + } + else if (pkColumn.UnderType == UtilConstants.StringType) + { + SetValue(pkColumn, insertData, () => Guid.NewGuid().ToString()); + } + else + { + SetError(pkColumn, insertData); + } + } + + private void SetValue(EntityColumnInfo pkColumn, List insertData, Func value) where TChild : class, new() + { + foreach (var child in insertData) + { + if (IsDefaultValue(pkColumn.PropertyInfo.GetValue(child))) + { + pkColumn.PropertyInfo.SetValue(child, value()); + } + } + if (IsFirst && _RootOptions != null) + { + this._Context.Insertable(insertData) + .IgnoreColumns(_RootOptions.IgnoreColumns) + .InsertColumns(_RootOptions.InsertColumns) + .EnableDiffLogEventIF(_RootOptions.IsDiffLogEvent, _RootOptions.DiffLogBizData) + .ExecuteCommand(); + } + else + { + this._Context.Insertable(insertData).ExecuteCommand(); + } + } + private void SetError(EntityColumnInfo pkColumn, List insertData) where TChild : class, new() + { + foreach (var child in insertData) + { + if (IsDefaultValue(pkColumn.PropertyInfo.GetValue(child))) + { + var name = pkColumn.EntityName + " " + pkColumn.DbColumnName; + Check.ExceptionEasy($"The field {name} is not an autoassignment type and requires an assignment", $"字段{name}不是可自动赋值类型需要赋值(并且不能是已存在值) , 可赋值类型有 自增、long、Guid、string"); + } + } + if (IsFirst && _RootOptions != null) + { + this._Context.Insertable(insertData) + .IgnoreColumns(_RootOptions.IgnoreColumns) + .InsertColumns(_RootOptions.InsertColumns) + .EnableDiffLogEventIF(_RootOptions.IsDiffLogEvent, _RootOptions.DiffLogBizData) + .ExecuteCommand(); + } + else + { + var isIdentity = this._Context.EntityMaintenance.GetEntityInfo(typeof(TChild)).Columns.Any(it => it.IsIdentity); + if (isIdentity) + { + foreach (var item in insertData) + { + this._Context.Insertable(insertData).ExecuteCommandIdentityIntoEntity(); + } + } + else + { + this._Context.Insertable(insertData).ExecuteCommand(); + } + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/InsertNavProviderManyToMany.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/InsertNavProviderManyToMany.cs new file mode 100644 index 000000000..c0cd44460 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/InsertNavProviderManyToMany.cs @@ -0,0 +1,165 @@ +using Newtonsoft.Json; +namespace SqlSugar +{ + public partial class InsertNavProvider where T : class, new() where Root : class, new() + { + private void InsertManyToMany(string name, EntityColumnInfo nav) where TChild : class, new() + { + ; + var parentEntity = _ParentEntity; + var parentList = _ParentList; + var parentPkColumn = parentEntity.Columns.FirstOrDefault(it => it.IsPrimarykey == true); + var parentNavigateProperty = parentEntity.Columns.FirstOrDefault(it => it.PropertyName == name); + var thisEntity = this._Context.EntityMaintenance.GetEntityInfo(); + var thisPkColumn = thisEntity.Columns.FirstOrDefault(it => it.IsPrimarykey == true); + Check.ExceptionEasy(thisPkColumn == null, $"{thisPkColumn.EntityName} need primary key", $"{thisPkColumn.EntityName}需要主键"); + Check.ExceptionEasy(parentPkColumn == null, $"{parentPkColumn.EntityName} need primary key", $"{parentPkColumn.EntityName}需要主键"); + var mappingType = parentNavigateProperty.Navigat.MappingType; + var mappingEntity = this._Context.EntityMaintenance.GetEntityInfo(mappingType); + var mappingA = mappingEntity.Columns.FirstOrDefault(x => x.PropertyName == parentNavigateProperty.Navigat.MappingAId); + var mappingB = mappingEntity.Columns.FirstOrDefault(x => x.PropertyName == parentNavigateProperty.Navigat.MappingBId); + Check.ExceptionEasy(mappingA == null || mappingB == null, $"Navigate property {name} error ", $"导航属性{name}配置错误"); + var mappingPk = mappingEntity.Columns + .Where(it => it.PropertyName != mappingA.PropertyName) + .Where(it => it.PropertyName != mappingB.PropertyName) + .Where(it => it.IsPrimarykey && !it.IsIdentity && it.OracleSequenceName.IsNullOrEmpty()).FirstOrDefault(); + var mappingOthers = mappingEntity.Columns + .Where(it => it.PropertyName != mappingA.PropertyName) + .Where(it => it.PropertyName != mappingB.PropertyName) + .Where(it => !it.IsIdentity) + .Where(it => !it.IsOnlyIgnoreInsert) + .Where(it => !it.IsIgnore) + .Where(it => !it.IsPrimarykey); + List> mappgingTables = new List>(); + foreach (var item in parentList) + { + var items = parentNavigateProperty.PropertyInfo.GetValue(item); + if (items == null) + { + continue; + } + var children = ((List)items); + InsertDatas(children, thisPkColumn); + var parentId = parentPkColumn.PropertyInfo.GetValue(item); + foreach (var child in children) + { + var chidId = thisPkColumn.PropertyInfo.GetValue(child); + Dictionary keyValuePairs = new Dictionary(); + keyValuePairs.Add(mappingA.DbColumnName, parentId); + keyValuePairs.Add(mappingB.DbColumnName, chidId); + if (mappingOthers != null) + { + foreach (var pair in mappingOthers) + { + if (!keyValuePairs.ContainsKey(pair.DbColumnName)) + { + if (pair.UnderType == UtilConstants.DateType) + { + keyValuePairs.Add(pair.DbColumnName, DateTime.Now); + } + else if (pair.UnderType == UtilConstants.StringType) + { + keyValuePairs.Add(pair.DbColumnName, UtilConstants.Space); + } + else + { + keyValuePairs.Add(pair.DbColumnName, UtilMethods.GetDefaultValue(pair.UnderType)); + } + } + } + } + if (mappingPk != null) + { + SetMappingTableDefaultValue(mappingPk, keyValuePairs); + } + mappgingTables.Add(keyValuePairs); + } + } + var ids = mappgingTables.Select(x => x[mappingA.DbColumnName]).ToList(); + if (_navOptions?.ManyToManyNoDeleteMap == true) + { + //The reserved + } + else + { + this._Context.Deleteable().AS(mappingEntity.DbTableName).In(mappingA.DbColumnName, ids).ExecuteCommand(); + } + if (HasMappingTemplate(mappingEntity)) + { + InertMappingWithTemplate(mappingEntity, mappingA, mappingB, mappgingTables); + } + else + { + this._Context.Insertable(mappgingTables).AS(mappingEntity.DbTableName).ExecuteCommand(); + } + SetNewParent(thisEntity, thisPkColumn); + } + + private void InertMappingWithTemplate(EntityInfo mappingEntity, EntityColumnInfo mappingA, EntityColumnInfo mappingB, List> mappgingTables) + { + var template = this._navOptions?.ManyToManySaveMappingTemplate; + List mappingObjects = new List(); + foreach (var item in mappgingTables) + { + // 序列化模板对象 + var serializedTemplate = JsonConvert.SerializeObject(template); + + // 反序列化模板对象,创建新的映射对象 + var mappingObject = JsonConvert.DeserializeObject(serializedTemplate, template.GetType()); + + // 获取映射对象的所有字段 + var fields = mappingEntity.Columns; + + // 遍历字典中的键值对,并将值赋给映射对象的对应字段 + foreach (var kvp in item) + { + var fieldName = kvp.Key; + var fieldValue = kvp.Value; + + // 查找与字段名匹配的字段 + var field = fields.FirstOrDefault(f => f.DbColumnName.EqualCase(fieldName)); + // 如果字段存在且值的类型与字段类型匹配,则赋值给字段 + if (field != null) + { + var isSetValue = field.IsPrimarykey + || field.DbColumnName == mappingA.DbColumnName + || field.DbColumnName == mappingB.DbColumnName; + if (isSetValue) + field.PropertyInfo.SetValue(mappingObject, fieldValue); + } + } + + // 将映射对象添加到列表中 + mappingObjects.Add(mappingObject); + } + this._Context.InsertableByObject(mappingObjects).ExecuteCommand(); + } + + private bool HasMappingTemplate(EntityInfo mappingEntity) + { + return this._navOptions?.ManyToManySaveMappingTemplate?.GetType() == mappingEntity.Type; + } + + private void SetMappingTableDefaultValue(EntityColumnInfo mappingPk, Dictionary keyValuePairs) + { + if (mappingPk.UnderType == UtilConstants.LongType) + { + keyValuePairs.Add(mappingPk.DbColumnName, SnowFlakeSingle.Instance.NextId()); + } + else if (mappingPk.UnderType == UtilConstants.GuidType) + { + keyValuePairs.Add(mappingPk.DbColumnName, Guid.NewGuid()); + } + else if (mappingPk.UnderType == UtilConstants.StringType) + { + keyValuePairs.Add(mappingPk.DbColumnName, Guid.NewGuid() + ""); + } + else + { + var name = mappingPk.EntityName + " " + mappingPk.DbColumnName; + Check.ExceptionEasy($"The field {name} is not an autoassignment type and requires an assignment", + $" 中间表主键字段{name}不是可自动赋值类型, 可赋值类型有 自增、long、Guid、string。你也可以删掉主键 用双主键"); + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/InsertNavProviderOneToMany.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/InsertNavProviderOneToMany.cs new file mode 100644 index 000000000..394383807 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/InsertNavProviderOneToMany.cs @@ -0,0 +1,104 @@ +using System.Collections; +namespace SqlSugar +{ + public partial class InsertNavProvider where T : class, new() where Root : class, new() + { + + private void InsertOneToMany(string name, EntityColumnInfo nav) where TChild : class, new() + { + List children = new List(); + var parentEntity = _ParentEntity; + var parentList = _ParentList; + var parentNavigateProperty = parentEntity.Columns.FirstOrDefault(it => it.PropertyName == name); + var thisEntity = this._Context.EntityMaintenance.GetEntityInfo(); + var thisPkColumn = GetPkColumnByNav2(thisEntity, nav); + var thisFkColumn = GetFKColumnByNav(thisEntity, nav); + EntityColumnInfo parentPkColumn = GetParentPkColumn(); + EntityColumnInfo parentNavColumn = GetParentPkNavColumn(nav); + if (parentNavColumn != null) + { + parentPkColumn = parentNavColumn; + } + if (ParentIsPk(parentNavigateProperty)) + { + parentPkColumn = this._ParentEntity.Columns.FirstOrDefault(it => it.IsPrimarykey); + } + foreach (var item in parentList) + { + var parentValue = parentPkColumn.PropertyInfo.GetValue(item); + var childs = parentNavigateProperty.PropertyInfo.GetValue(item) as List; + if (childs != null) + { + foreach (var child in childs) + { + thisFkColumn.PropertyInfo.SetValue(child, parentValue, null); + } + children.AddRange(childs); + } + else if (childs == null && parentNavigateProperty.PropertyInfo.GetValue(item) is IList ilist && ilist?.Count > 0) + { + childs = GetIChildsBylList(children, thisFkColumn, parentValue, ilist); + } + } + var isTreeChild = GetIsTreeChild(parentEntity, thisEntity); + Check.ExceptionEasy(thisPkColumn == null, $"{thisEntity.EntityName}need primary key", $"实体{thisEntity.EntityName}需要主键"); + if (NotAny(name) || isTreeChild) + { + InsertDatas(children, thisPkColumn); + } + else + { + this._ParentList = children.Cast().ToList(); + } + SetNewParent(thisEntity, thisPkColumn); + } + + private static List GetIChildsBylList(List children, EntityColumnInfo thisFkColumn, object parentValue, IList ilist) where TChild : class, new() + { + List childs = ilist.Cast().ToList(); + foreach (var child in childs) + { + thisFkColumn.PropertyInfo.SetValue(child, parentValue, null); + } + children.AddRange(childs); + return childs; + } + + private bool GetIsTreeChild(EntityInfo parentEntity, EntityInfo thisEntity) + { + return this.NavContext?.Items?.Count > 0 && parentEntity.Type == thisEntity.Type; + } + + private static bool ParentIsPk(EntityColumnInfo parentNavigateProperty) + { + return parentNavigateProperty?.Navigat != null && + parentNavigateProperty.Navigat.NavigatType == NavigateType.OneToMany && + parentNavigateProperty.Navigat.Name2 == null; + } + + private EntityColumnInfo GetParentPkColumn() + { + EntityColumnInfo parentPkColumn = _ParentPkColumn; + if (_ParentPkColumn == null) + { + parentPkColumn = _ParentPkColumn = this._ParentEntity.Columns.FirstOrDefault(it => it.IsPrimarykey); + } + return parentPkColumn; + } + private EntityColumnInfo GetParentPkNavColumn(EntityColumnInfo nav) + { + EntityColumnInfo result = null; + if (nav.Navigat.Name2.HasValue()) + { + result = _ParentPkColumn = this._ParentEntity.Columns.FirstOrDefault(it => it.PropertyName == nav.Navigat.Name2); + } + return result; + } + + private void SetNewParent(EntityInfo entityInfo, EntityColumnInfo entityColumnInfo) where TChild : class, new() + { + this._ParentEntity = entityInfo; + this._ParentPkColumn = entityColumnInfo; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/InsertNavProviderOneToOne.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/InsertNavProviderOneToOne.cs new file mode 100644 index 000000000..08e28905a --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/InsertNavProviderOneToOne.cs @@ -0,0 +1,59 @@ +namespace SqlSugar +{ + public partial class InsertNavProvider where T : class, new() where Root : class, new() + { + private void InsertOneToOne(string name, EntityColumnInfo nav) where TChild : class, new() + { + var parentEntity = _ParentEntity; + var parentList = _ParentList; + var parentColumn = parentEntity.Columns.FirstOrDefault(it => it.PropertyName == nav.Navigat.Name); + var parentPkColumn = parentEntity.Columns.FirstOrDefault(it => it.IsPrimarykey); + var thisEntity = this._Context.EntityMaintenance.GetEntityInfo(); + EntityColumnInfo thisPkColumn = GetPkColumnByNav(thisEntity, nav); + Check.ExceptionEasy(thisPkColumn == null, $" Navigate {parentEntity.EntityName} : {name} is error ", $"导航实体 {parentEntity.EntityName} 属性 {name} 配置错误"); + Check.ExceptionEasy(nav.Navigat.WhereSql.HasValue(), $" {name} Navigate(NavType,WhereSql) no support insert ", $"导航一对一 {name} 配置了 Sql变量 不支持插入"); + List childList = new List(); + foreach (var parent in parentList) + { + var navPropertyValue = parentColumn.PropertyInfo.GetValue(parent); + var childItem = (TChild)nav.PropertyInfo.GetValue(parent); + if (childItem != null) + { + if (IsDefaultValue(navPropertyValue)) + { + var pkValue = thisPkColumn.PropertyInfo.GetValue(childItem); + if (IsDefaultValue(navPropertyValue)) + { + navPropertyValue = pkValue; + } + + } + if (!IsDefaultValue(navPropertyValue) && parentColumn.IsPrimarykey == false) + { + this._Context.Updateable + ().AS(parentEntity.DbTableName) + .SetColumns(parentColumn.DbColumnName, navPropertyValue) + .Where(parentPkColumn.DbColumnName, "=", parentPkColumn.PropertyInfo.GetValue(parent)).ExecuteCommand(); + } + if (IsDefaultValue(navPropertyValue)) + { + InsertDatas(new List() { childItem }, thisPkColumn); + navPropertyValue = thisPkColumn.PropertyInfo.GetValue(childItem); + parentColumn.PropertyInfo.SetValue(parent, navPropertyValue); + this._Context.Updateable + ().AS(parentEntity.DbTableName) + .SetColumns(parentColumn.DbColumnName, navPropertyValue) + .Where(parentPkColumn.DbColumnName, "=", parentPkColumn.PropertyInfo.GetValue(parent)).ExecuteCommand(); + } + + thisPkColumn.PropertyInfo.SetValue(childItem, navPropertyValue); + childList.Add(childItem); + } + } + InsertDatas(childList, thisPkColumn); + this._ParentList = childList.Cast().ToList(); + SetNewParent(thisEntity, thisPkColumn); + } + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/InsertNavTask.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/InsertNavTask.cs new file mode 100644 index 000000000..6c2bccdb6 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/InsertNavTask.cs @@ -0,0 +1,249 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public class InsertNavTaskInit where T : class, new() where Root : class, new() + { + + internal SqlSugarProvider Context { get; set; } + internal InsertNavProvider insertNavProvider { get; set; } + internal NavContext NavContext { get; set; } + public InsertNavMethodInfo IncludeByNameString(string navMemberName, InsertNavOptions insertNavOptions = null) + { + InsertNavMethodInfo result = new InsertNavMethodInfo(); + result.Context = insertNavProvider._Context; + var entityInfo = result.Context.EntityMaintenance.GetEntityInfo(); + Type properyItemType; + bool isList; + Expression exp = UtilMethods.GetIncludeExpression(navMemberName, entityInfo, out properyItemType, out isList); + var method = this.GetType().GetMyMethod("Include", 2, isList) + .MakeGenericMethod(properyItemType); + var obj = method.Invoke(this, new object[] { exp, insertNavOptions }); + result.MethodInfos = obj; + return result; + } + public InsertNavMethodInfo IncludesAllFirstLayer(params string[] ignoreColumns) + { + if (ignoreColumns == null) + { + ignoreColumns = Array.Empty(); + } + this.Context = insertNavProvider._Context; + var navColumns = this.Context.EntityMaintenance.GetEntityInfo().Columns.Where(it => !ignoreColumns.Contains(it.PropertyName) || !ignoreColumns.Any(z => z.EqualCase(it.DbColumnName))).Where(it => it.Navigat != null).ToList(); + var updateNavs = this; + InsertNavMethodInfo methodInfo = updateNavs.IncludeByNameString(navColumns[0].PropertyName); + foreach (var item in navColumns.Skip(1)) + { + methodInfo = methodInfo.IncludeByNameString(item.PropertyName); + } + return methodInfo; + } + public InsertNavMethodInfo IncludesAllFirstLayer(InsertNavOptions insertNavOptions, params string[] ignoreColumns) + { + if (ignoreColumns == null) + { + ignoreColumns = Array.Empty(); + } + this.Context = insertNavProvider._Context; + var navColumns = this.Context.EntityMaintenance.GetEntityInfo().Columns.Where(it => !ignoreColumns.Contains(it.PropertyName) || !ignoreColumns.Any(z => z.EqualCase(it.DbColumnName))).Where(it => it.Navigat != null).ToList(); + var updateNavs = this; + InsertNavMethodInfo methodInfo = updateNavs.IncludeByNameString(navColumns[0].PropertyName); + foreach (var item in navColumns.Skip(1)) + { + methodInfo = methodInfo.IncludeByNameString(item.PropertyName, insertNavOptions); + } + return methodInfo; + } + public InsertNavTask Include(Expression> expression) where TChild : class, new() + { + Check.ExceptionEasy(typeof(TChild).FullName.Contains("System.Collections.Generic.List`"), " need where T: class, new() ", "需要Class,new()约束,并且类属性中不能有required修饰符"); + this.Context = insertNavProvider._Context; + insertNavProvider.NavContext = this.NavContext; + InsertNavTask result = new InsertNavTask(); + Func> func = () => insertNavProvider.ThenInclude(expression); + result.PreFunc = func; + result.Context = this.Context; + result.NavContext = this.NavContext; + return result; + } + public InsertNavTask Include(Expression>> expression) where TChild : class, new() + { + this.Context = insertNavProvider._Context; + insertNavProvider.NavContext = this.NavContext; + InsertNavTask result = new InsertNavTask(); + Func> func = () => insertNavProvider.ThenInclude(expression); + result.PreFunc = func; + result.Context = this.Context; + result.NavContext = this.NavContext; + return result; + } + + public InsertNavTask Include(Expression> expression, InsertNavOptions options) where TChild : class, new() + { + Check.ExceptionEasy(typeof(TChild).FullName.Contains("System.Collections.Generic.List`"), " need where T: class, new() ", "需要Class,new()约束,并且类属性中不能有required修饰符"); + this.Context = insertNavProvider._Context; + insertNavProvider.NavContext = this.NavContext; + InsertNavTask result = new InsertNavTask(); + Func> func = () => insertNavProvider.ThenInclude(expression, options); + result.PreFunc = func; + result.Context = this.Context; + result.NavContext = this.NavContext; + return result; + } + public InsertNavTask Include(Expression>> expression, InsertNavOptions options) where TChild : class, new() + { + this.Context = insertNavProvider._Context; + insertNavProvider.NavContext = this.NavContext; + InsertNavTask result = new InsertNavTask(); + Func> func = () => insertNavProvider.ThenInclude(expression, options); + result.PreFunc = func; + result.Context = this.Context; + result.NavContext = this.NavContext; + return result; + } + } + public class InsertNavTask where T : class, new() where Root : class, new() + { + public SqlSugarProvider Context { get; set; } + public Func> PreFunc { get; set; } + internal NavContext NavContext { get; set; } + + public InsertNavTask ThenInclude(Expression> expression) where TChild : class, new() + { + InsertNavTask result = new InsertNavTask(); + Func> func = () => + { + var nav = PreFunc().ThenInclude(expression); + nav.NavContext = this.NavContext; + return nav; + }; + result.PreFunc = func; + result.Context = this.Context; + result.NavContext = this.NavContext; + return result; + } + public InsertNavTask ThenInclude(Expression>> expression) where TChild : class, new() + { + InsertNavTask result = new InsertNavTask(); + Func> func = () => + { + var nav = PreFunc().ThenInclude(expression); + nav.NavContext = this.NavContext; + return nav; + }; + result.PreFunc = func; + result.Context = this.Context; + result.NavContext = this.NavContext; + return result; + } + public InsertNavTask Include(Expression> expression) where TChild : class, new() + { + return AsNav().ThenInclude(expression); + } + public InsertNavTask Include(Expression>> expression) where TChild : class, new() + { + return AsNav().ThenInclude(expression); + } + + + + public InsertNavTask ThenInclude(Expression> expression, InsertNavOptions options) where TChild : class, new() + { + InsertNavTask result = new InsertNavTask(); + Func> func = () => PreFunc().ThenInclude(expression, options); + result.PreFunc = func; + result.Context = this.Context; + result.NavContext = this.NavContext; + return result; + } + public InsertNavTask ThenInclude(Expression>> expression, InsertNavOptions options) where TChild : class, new() + { + InsertNavTask result = new InsertNavTask(); + Func> func = () => PreFunc().ThenInclude(expression, options); + result.PreFunc = func; + result.Context = this.Context; + result.NavContext = this.NavContext; + return result; + } + public InsertNavTask Include(Expression> expression, InsertNavOptions options) where TChild : class, new() + { + return AsNav().ThenInclude(expression, options); + } + public InsertNavTask Include(Expression>> expression, InsertNavOptions options) where TChild : class, new() + { + return AsNav().ThenInclude(expression, options); + } + + public Root ExecuteReturnEntity() + { + var hasTran = this.Context.Ado.Transaction != null; + if (hasTran) + { + return (Root)PreFunc()?._RootList?.FirstOrDefault(); + } + else + { + Root result = null; + this.Context.Ado.UseTran(() => + { + result = (Root)PreFunc()?._RootList?.FirstOrDefault(); + }, ex => throw ex); + return result; + } + } + public async Task ExecuteReturnEntityAsync() + { + Root result = null; + await Task.Run(async () => + { + result = ExecuteReturnEntity(); + await Task.Delay(0).ConfigureAwait(false); + }).ConfigureAwait(false); + return result; + } + + public bool ExecuteCommand() + { + var hasTran = this.Context.Ado.Transaction != null; + if (hasTran) + { + PreFunc(); + } + else + { + this.Context.Ado.UseTran(() => + { + PreFunc(); + }, ex => throw ex); + } + return true; + } + public async Task ExecuteCommandAsync() + { + await Task.Run(async () => + { + ExecuteCommand(); + await Task.Delay(0).ConfigureAwait(false); + }).ConfigureAwait(false); + return true; + } + + private InsertNavTask AsNav() + { + InsertNavTask result = new InsertNavTask(); + Func> func = () => + { + + var navas = PreFunc().AsNav(); + navas.NavContext = this.NavContext; + navas.IsNav = true; + return navas; + }; + result.PreFunc = func; + result.Context = this.Context; + result.NavContext = this.NavContext; + return result; + } + } + +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/NavContext.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/NavContext.cs new file mode 100644 index 000000000..3d83a582b --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/NavContext.cs @@ -0,0 +1,12 @@ +namespace SqlSugar +{ + internal class NavContext + { + public List Items { get; set; } + } + internal class NavContextItem + { + public int Level { get; set; } + public string RootName { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/UpdateNavManyToMany.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/UpdateNavManyToMany.cs new file mode 100644 index 000000000..4d8f8b47b --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/UpdateNavManyToMany.cs @@ -0,0 +1,194 @@ +using Newtonsoft.Json; +namespace SqlSugar +{ + public partial class UpdateNavProvider where T : class, new() where Root : class, new() + { + private void UpdateManyToMany(string name, EntityColumnInfo nav) where TChild : class, new() + { + var parentEntity = _ParentEntity; + var parentList = _ParentList; + var parentPkColumn = parentEntity.Columns.FirstOrDefault(it => it.IsPrimarykey == true); + var parentNavigateProperty = parentEntity.Columns.FirstOrDefault(it => it.PropertyName == name); + var thisEntity = this._Context.EntityMaintenance.GetEntityInfo(); + var thisPkColumn = thisEntity.Columns.FirstOrDefault(it => it.IsPrimarykey == true); + Check.ExceptionEasy(thisPkColumn == null, $"{thisPkColumn.EntityName} need primary key", $"{thisPkColumn.EntityName}需要主键"); + Check.ExceptionEasy(parentPkColumn == null, $"{parentPkColumn.EntityName} need primary key", $"{parentPkColumn.EntityName}需要主键"); + var mappingType = parentNavigateProperty.Navigat.MappingType; + var mappingEntity = this._Context.EntityMaintenance.GetEntityInfo(mappingType); + var mappingA = mappingEntity.Columns.FirstOrDefault(x => x.PropertyName == parentNavigateProperty.Navigat.MappingAId); + var mappingB = mappingEntity.Columns.FirstOrDefault(x => x.PropertyName == parentNavigateProperty.Navigat.MappingBId); + Check.ExceptionEasy(mappingA == null || mappingB == null, $"Navigate property {name} error ", $"导航属性{name}配置错误"); + var mappingPk = mappingEntity.Columns + .Where(it => it.PropertyName != mappingA.PropertyName) + .Where(it => it.PropertyName != mappingB.PropertyName) + .Where(it => it.IsPrimarykey && !it.IsIdentity && it.OracleSequenceName.IsNullOrEmpty()).FirstOrDefault(); + var mappingOthers = mappingEntity.Columns + .Where(it => it.PropertyName != mappingA.PropertyName) + .Where(it => it.PropertyName != mappingB.PropertyName) + .Where(it => !it.IsIdentity) + .Where(it => !it.IsPrimarykey) + .Where(it => !it.IsOnlyIgnoreInsert) + .Where(it => !it.IsIgnore); + List> mappgingTables = new List>(); + var ids = new List(); + foreach (var item in parentList) + { + var items = parentNavigateProperty.PropertyInfo.GetValue(item); + if (items == null) + { + continue; + } + var children = ((List)items); + if (this._Options?.ManyToManyIsUpdateB == true) + { + InsertDatas(children, thisPkColumn); + } + else + { + _ParentList = children.Cast().ToList(); + } + var parentId = parentPkColumn.PropertyInfo.GetValue(item); + if (!ids.Contains(parentId)) + { + ids.Add(parentId); + } + foreach (var child in children) + { + var chidId = thisPkColumn.PropertyInfo.GetValue(child); + Dictionary keyValuePairs = new Dictionary(); + keyValuePairs.Add(mappingA.DbColumnName, parentId); + keyValuePairs.Add(mappingB.DbColumnName, chidId); + if (mappingOthers != null) + { + foreach (var pair in mappingOthers) + { + if (pair.UnderType == UtilConstants.DateType) + { + keyValuePairs.Add(pair.DbColumnName, DateTime.Now); + } + else if (pair.UnderType == UtilConstants.StringType) + { + keyValuePairs.Add(pair.DbColumnName, UtilConstants.Space); + } + else + { + keyValuePairs.Add(pair.DbColumnName, UtilMethods.GetDefaultValue(pair.UnderType)); + } + } + } + if (mappingPk != null) + { + SetMappingTableDefaultValue(mappingPk, keyValuePairs); + } + mappgingTables.Add(keyValuePairs); + } + } + if (this._Options?.ManyToManyEnableLogicDelete == true) + { + var locgicColumn = thisEntity.Columns.FirstOrDefault(it => it.PropertyName.EqualCase("IsDeleted") || it.PropertyName.EqualCase("IsDelete")); + Check.ExceptionEasy( + locgicColumn == null, + thisEntity.EntityName + "Logical deletion requires the entity to have the IsDeleted property", + thisEntity.EntityName + "假删除需要实体有IsDeleted属性"); + List conditionalModels = new List(); + conditionalModels.Add(new ConditionalModel() + { + FieldName = mappingA.DbColumnName, + FieldValue = string.Join(",", ids.Distinct()), + ConditionalType = ConditionalType.In, + CSharpTypeName = mappingA?.PropertyInfo?.PropertyType?.Name + }); + var sqlObj = _Context.Queryable().SqlBuilder.ConditionalModelToSql(conditionalModels); + this._Context.Updateable() + .AS(mappingEntity.DbTableName) + .Where(sqlObj.Key, sqlObj.Value) + .SetColumns(locgicColumn.DbColumnName, true) + .ExecuteCommand(); + } + else if (_Context?.CurrentConnectionConfig?.MoreSettings?.IsAutoDeleteQueryFilter == true) + { + this._Context.Deleteable().AS(mappingEntity.DbTableName).In(mappingA.DbColumnName, ids).EnableQueryFilter(mappingEntity.Type).ExecuteCommand(); + } + else + { + this._Context.Deleteable().AS(mappingEntity.DbTableName).In(mappingA.DbColumnName, ids).ExecuteCommand(); + } + if (HasMappingTemplate(mappingEntity)) + { + InertMappingWithTemplate(mappingEntity, mappingA, mappingB, mappgingTables); + } + else + { + this._Context.Insertable(mappgingTables).AS(mappingEntity.DbTableName).ExecuteCommand(); + } + _ParentEntity = thisEntity; + } + + private bool HasMappingTemplate(EntityInfo mappingEntity) + { + return this._Options?.ManyToManySaveMappingTemplate?.GetType() == mappingEntity.Type; + } + + private void InertMappingWithTemplate(EntityInfo mappingEntity, EntityColumnInfo mappingA, EntityColumnInfo mappingB, List> mappgingTables) + { + var template = this._Options?.ManyToManySaveMappingTemplate; + List mappingObjects = new List(); + foreach (var item in mappgingTables) + { + // 序列化模板对象 + var serializedTemplate = JsonConvert.SerializeObject(template); + + // 反序列化模板对象,创建新的映射对象 + var mappingObject = JsonConvert.DeserializeObject(serializedTemplate, template.GetType()); + + // 获取映射对象的所有字段 + var fields = mappingEntity.Columns; + + // 遍历字典中的键值对,并将值赋给映射对象的对应字段 + foreach (var kvp in item) + { + var fieldName = kvp.Key; + var fieldValue = kvp.Value; + + // 查找与字段名匹配的字段 + var field = fields.FirstOrDefault(f => f.DbColumnName.EqualCase(fieldName)); + // 如果字段存在且值的类型与字段类型匹配,则赋值给字段 + if (field != null) + { + var isSetValue = field.IsPrimarykey + || field.DbColumnName == mappingA.DbColumnName + || field.DbColumnName == mappingB.DbColumnName; + if (isSetValue) + field.PropertyInfo.SetValue(mappingObject, fieldValue); + } + } + + // 将映射对象添加到列表中 + mappingObjects.Add(mappingObject); + } + this._Context.InsertableByObject(mappingObjects).ExecuteCommand(); + } + + private void SetMappingTableDefaultValue(EntityColumnInfo mappingPk, Dictionary keyValuePairs) + { + if (mappingPk.UnderType == UtilConstants.LongType) + { + keyValuePairs.Add(mappingPk.DbColumnName, SnowFlakeSingle.Instance.NextId()); + } + else if (mappingPk.UnderType == UtilConstants.GuidType) + { + keyValuePairs.Add(mappingPk.DbColumnName, Guid.NewGuid()); + } + else if (mappingPk.UnderType == UtilConstants.StringType) + { + keyValuePairs.Add(mappingPk.DbColumnName, Guid.NewGuid() + ""); + } + else + { + var name = mappingPk.EntityName + " " + mappingPk.DbColumnName; + Check.ExceptionEasy($"The field {name} is not an autoassignment type and requires an assignment", + $" 中间表主键字段{name}不是可自动赋值类型, 可赋值类型有 自增、long、Guid、string。你也可以删掉主键 用双主键"); + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/UpdateNavOneToMany.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/UpdateNavOneToMany.cs new file mode 100644 index 000000000..1920997c1 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/UpdateNavOneToMany.cs @@ -0,0 +1,338 @@ +using System.Collections; +namespace SqlSugar +{ + public partial class UpdateNavProvider where T : class, new() where Root : class, new() + { + public NavigateType? _NavigateType { get; set; } + private void UpdateOneToMany(string name, EntityColumnInfo nav) where TChild : class, new() + { + if (_Options?.OneToManyInsertOrUpdate == true) + { + InsertOrUpdate(name, nav); + } + else + { + DeleteInsert(name, nav); + } + } + private void InsertOrUpdate(string name, EntityColumnInfo nav) where TChild : class, new() + { + List children = new List(); + var parentEntity = _ParentEntity; + var parentList = _ParentList; + var parentNavigateProperty = parentEntity.Columns.FirstOrDefault(it => it.PropertyName == name); + var thisEntity = this._Context.EntityMaintenance.GetEntityInfo(); + var thisPkColumn = GetPkColumnByNav2(thisEntity, nav); + var thisFkColumn = GetFKColumnByNav(thisEntity, nav); + EntityColumnInfo parentPkColumn = GetParentPkColumn(); + EntityColumnInfo parentNavColumn = GetParentPkNavColumn(nav); + if (parentNavColumn != null) + { + parentPkColumn = parentNavColumn; + } + if (ParentIsPk(parentNavigateProperty)) + { + parentPkColumn = this._ParentEntity.Columns.FirstOrDefault(it => it.IsPrimarykey); + } + var ids = new List(); + foreach (var item in parentList) + { + var parentValue = parentPkColumn.PropertyInfo.GetValue(item); + var childs = parentNavigateProperty.PropertyInfo.GetValue(item) as List; + if (childs != null) + { + foreach (var child in childs) + { + thisFkColumn.PropertyInfo.SetValue(child, parentValue, null); + } + children.AddRange(childs); + } + ids.Add(parentValue); + if (_Options?.OneToManyNoDeleteNull == true && childs == null) + { + ids.Remove(parentValue); + } + } + if (NotAny(name)) + { + DeleteMany(thisEntity, ids, thisFkColumn.DbColumnName); + if (this._Options?.OneToManyEnableLogicDelete == true) + { + var locgicColumn = thisEntity.Columns.FirstOrDefault(it => it.PropertyName.EqualCase("IsDeleted") || it.PropertyName.EqualCase("IsDelete")); + Check.ExceptionEasy( + locgicColumn == null, + thisEntity.EntityName + "Logical deletion requires the entity to have the IsDeleted property", + thisEntity.EntityName + "假删除需要实体有IsDeleted属性"); + List conditionalModels = new List(); + conditionalModels.Add(new ConditionalModel() + { + FieldName = thisFkColumn.DbColumnName, + FieldValue = string.Join(",", ids.Distinct()), + ConditionalType = ConditionalType.In, + CSharpTypeName = thisFkColumn?.PropertyInfo?.PropertyType?.Name + }); + var sqlObj = _Context.Queryable().SqlBuilder.ConditionalModelToSql(conditionalModels); + this._Context.Updateable() + .AS(thisEntity.DbTableName) + .Where(sqlObj.Key, sqlObj.Value) + .SetColumns(locgicColumn.DbColumnName, true) + .ExecuteCommand(); + } + else + { + var list = this._Context.Queryable() + .AS(thisEntity.DbTableName) + .In(thisFkColumn.DbColumnName, ids.Distinct().ToList()) + .ToList(); + List result = GetNoExistsId(list, children, thisPkColumn.PropertyName); + if (result.Count != 0) + { + this._Context.Deleteable(result).ExecuteCommand(); + } + } + _NavigateType = NavigateType.OneToMany; + InsertDatas(children, thisPkColumn); + } + else + { + this._ParentList = children.Cast().ToList(); + } + _NavigateType = null; + SetNewParent(thisEntity, thisPkColumn); + } + private void DeleteInsert(string name, EntityColumnInfo nav) where TChild : class, new() + { + List children = new List(); + var parentEntity = _ParentEntity; + var parentList = _ParentList; + var parentNavigateProperty = parentEntity.Columns.FirstOrDefault(it => it.PropertyName == name); + var thisEntity = this._Context.EntityMaintenance.GetEntityInfo(); + var thisPkColumn = GetPkColumnByNav2(thisEntity, nav); + var thisFkColumn = GetFKColumnByNav(thisEntity, nav); + EntityColumnInfo parentPkColumn = GetParentPkColumn(); + EntityColumnInfo parentNavColumn = GetParentPkNavColumn(nav); + if (parentNavColumn != null) + { + parentPkColumn = parentNavColumn; + } + if (ParentIsPk(parentNavigateProperty)) + { + parentPkColumn = this._ParentEntity.Columns.FirstOrDefault(it => it.IsPrimarykey); + } + var ids = new List(); + foreach (var item in parentList) + { + var parentValue = parentPkColumn.PropertyInfo.GetValue(item); + var childs = parentNavigateProperty.PropertyInfo.GetValue(item) as List; + if (childs != null) + { + foreach (var child in childs) + { + thisFkColumn.PropertyInfo.SetValue(child, parentValue, null); + } + children.AddRange(childs); + } + else if (childs == null && parentNavigateProperty.PropertyInfo.GetValue(item) is IList ilist && ilist?.Count > 0) + { + childs = GetIChildsBylList(children, thisFkColumn, parentValue, ilist); + } + ids.Add(parentValue); + if (_Options?.OneToManyNoDeleteNull == true && childs == null) + { + ids.Remove(parentValue); + } + } + if (NotAny(name)) + { + DeleteMany(thisEntity, ids, thisFkColumn.DbColumnName); + if (this._Options?.OneToManyEnableLogicDelete == true) + { + var locgicColumn = thisEntity.Columns.FirstOrDefault(it => it.PropertyName.EqualCase("IsDeleted") || it.PropertyName.EqualCase("IsDelete")); + Check.ExceptionEasy( + locgicColumn == null, + thisEntity.EntityName + "Logical deletion requires the entity to have the IsDeleted property", + thisEntity.EntityName + "假删除需要实体有IsDeleted属性"); + List conditionalModels = new List(); + conditionalModels.Add(new ConditionalModel() + { + FieldName = thisFkColumn.DbColumnName, + FieldValue = string.Join(",", ids.Distinct()), + ConditionalType = ConditionalType.In, + CSharpTypeName = thisFkColumn?.PropertyInfo?.PropertyType?.Name + }); + var sqlObj = _Context.Queryable().SqlBuilder.ConditionalModelToSql(conditionalModels); + this._Context.Updateable() + .AS(thisEntity.DbTableName) + .Where(sqlObj.Key, sqlObj.Value) + .SetColumns(locgicColumn.DbColumnName, true) + .ExecuteCommand(); + } + else + { + if (this._Context?.CurrentConnectionConfig?.MoreSettings?.IsAutoDeleteQueryFilter == true) + { + this._Context.Deleteable() + .AS(thisEntity.DbTableName) + .EnableQueryFilter(thisEntity.Type) + .In(thisFkColumn.DbColumnName, ids.Distinct().ToList()).ExecuteCommand(); + } + else + { + this._Context.Deleteable() + .AS(thisEntity.DbTableName) + .In(thisFkColumn.DbColumnName, ids.Distinct().ToList()).ExecuteCommand(); + } + } + _NavigateType = NavigateType.OneToMany; + InsertDatas(children, thisPkColumn); + } + else + { + this._ParentList = children.Cast().ToList(); + } + _NavigateType = null; + SetNewParent(thisEntity, thisPkColumn); + } + private static List GetIChildsBylList(List children, EntityColumnInfo thisFkColumn, object parentValue, IList ilist) where TChild : class, new() + { + List childs = ilist.Cast().ToList(); + foreach (var child in childs) + { + thisFkColumn.PropertyInfo.SetValue(child, parentValue, null); + } + children.AddRange(childs); + return childs; + } + + private static bool ParentIsPk(EntityColumnInfo parentNavigateProperty) + { + return parentNavigateProperty?.Navigat != null && + parentNavigateProperty.Navigat.NavigatType == NavigateType.OneToMany && + parentNavigateProperty.Navigat.Name2 == null; + } + private void DeleteMany(EntityInfo thisEntity, List ids, string fkName) + { + if (_Options == null || _Options.OneToManyDeleteAll == false) + { + return; + } + var oneToManys = thisEntity.Columns.Where(it => it.Navigat != null && it.Navigat.NavigatType == NavigateType.OneToMany).ToList(); + foreach (var oneToMany in oneToManys) + { + var fkFieldName = oneToMany.Navigat.Name2 ?? thisEntity.Columns.FirstOrDefault(it => it.IsPrimarykey).PropertyName; + var fkDbColumnName = thisEntity.Columns.FirstOrDefault(it => it.PropertyName == fkFieldName).DbColumnName; + var fks = this._Context.Queryable() + .AS(thisEntity.DbTableName) + .In(fkName, ids.Distinct().ToList()).Select(fkDbColumnName).ToDataTable().Rows.Cast().Select(x => x[0]).ToArray(); + + var type = oneToMany.PropertyInfo.PropertyType.GenericTypeArguments[0]; + var entity = this._Context.EntityMaintenance.GetEntityInfo(type); + var id = oneToMany.Navigat.Name; + var column = entity.Columns.FirstOrDefault(it => it.PropertyName == id).DbColumnName; + + DeleteChild(fks, entity, column); + + this._Context.Deleteable() + .AS(entity.DbTableName) + .In(column, fks.Distinct().ToList()).ExecuteCommand(); + } + } + + private void DeleteChild(object[] fks, EntityInfo entity, string column) + { + var childs = entity.Columns.Where(it => it.Navigat != null && it.Navigat?.NavigatType == NavigateType.OneToMany).ToList(); + if (childs.Count != 0) + { + var pkColumn = entity.Columns.First(it => it.IsPrimarykey); + var pkIds = this._Context.Queryable() + .AS(entity.DbTableName) + .In(column, fks.Distinct().ToList()) + .Select(pkColumn.DbColumnName).ToDataTable().Rows + .Cast().Select(it => it[0]).ToList(); + DeleteChildChild(pkIds, childs); + } + } + + int childIndex = 0; + private void DeleteChildChild(List ids, List childs) + { + childIndex++; + if (childIndex > 4) + { + Check.ExceptionEasy("Removing too many levels", "安全机制限制删除脏数据层级不能超过7层"); + } + foreach (var columnInfo in childs) + { + var navigat = columnInfo.Navigat; + var type = columnInfo.PropertyInfo.PropertyType.GenericTypeArguments[0]; + var thisEntity = this._Context.EntityMaintenance.GetEntityInfo(type); + var fkColumn = thisEntity.Columns.FirstOrDefault(it => navigat.Name.EqualCase(it.PropertyName)); + var thisPkColumn = thisEntity.Columns.FirstOrDefault(it => it.IsPrimarykey); + var childs2 = thisEntity.Columns.Where(it => it.Navigat != null && it.Navigat?.NavigatType == NavigateType.OneToMany).ToList(); ; + if (childs2.Count != 0) + { + var pkIds = _Context.Queryable().AS(thisEntity.DbTableName) + .In(fkColumn.DbColumnName, ids) + .Select(thisPkColumn.DbColumnName).ToDataTable().Rows + .Cast().Select(it => it[0]).ToList(); + + DeleteChildChild(pkIds, childs2); + } + _Context.Deleteable().AS(thisEntity.DbTableName).In(fkColumn.DbColumnName, ids).ExecuteCommand(); + } + } + + private EntityColumnInfo GetParentPkColumn() + { + EntityColumnInfo parentPkColumn = _ParentPkColumn; + if (_ParentPkColumn == null) + { + parentPkColumn = _ParentPkColumn = this._ParentEntity.Columns.FirstOrDefault(it => it.IsPrimarykey); + } + return parentPkColumn; + } + private EntityColumnInfo GetParentPkNavColumn(EntityColumnInfo nav) + { + EntityColumnInfo result = null; + if (nav.Navigat.Name2.HasValue()) + { + result = _ParentPkColumn = this._ParentEntity.Columns.FirstOrDefault(it => it.PropertyName == nav.Navigat.Name2); + } + return result; + } + + private void SetNewParent(EntityInfo entityInfo, EntityColumnInfo entityColumnInfo) where TChild : class, new() + { + this._ParentEntity = entityInfo; + this._ParentPkColumn = entityColumnInfo; + } + + public List GetNoExistsId(List old, List newList, string pkName) + { + List result = new List(); + + // 将newList中的主键属性转换为字符串集合 + var newIds = newList.Select(item => GetPropertyValueAsString(item, pkName)).ToList(); + + // 获取在old中但不在newList中的主键属性值 + result = old.Where(item => !newIds.Contains(GetPropertyValueAsString(item, pkName))) + .ToList(); + + return result; + } + + // 获取对象的属性值 + private string GetPropertyValueAsString(TChild item, string propertyName) + { + var property = item.GetType().GetProperty(propertyName); + if (property != null) + { + return property.GetValue(item, null) + ""; + } + else + { + throw new ArgumentException($"Property '{propertyName}' not found on type {item.GetType().Name}"); + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/UpdateNavOneToOne.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/UpdateNavOneToOne.cs new file mode 100644 index 000000000..74ca0750d --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/UpdateNavOneToOne.cs @@ -0,0 +1,68 @@ +namespace SqlSugar +{ + public partial class UpdateNavProvider where T : class, new() where Root : class, new() + { + protected bool IsDeleted { get; set; } + private void UpdateOneToOne(string name, EntityColumnInfo nav) where TChild : class, new() + { + var parentEntity = _ParentEntity; + var parentList = _ParentList; + var isManyPk = parentEntity.Columns.Count(it => it.IsPrimarykey) > 1; + var parentColumn = parentEntity.Columns.FirstOrDefault(it => it.PropertyName == nav.Navigat.Name); + var parentPkColumn = parentEntity.Columns.FirstOrDefault(it => it.IsPrimarykey); + var thisEntity = this._Context.EntityMaintenance.GetEntityInfo(); + IsDeleted = thisEntity.Columns.Any(it => it.PropertyName.EqualCase("isdeleted") || it.PropertyName.EqualCase("isdelete")); + EntityColumnInfo thisPkColumn = GetPkColumnByNav(thisEntity, nav); + Check.ExceptionEasy(thisPkColumn == null, $" Navigate {parentEntity.EntityName} : {name} is error ", $"导航实体 {parentEntity.EntityName} 属性 {name} 配置错误"); + Check.ExceptionEasy(nav.Navigat.WhereSql.HasValue(), $" {name} Navigate(NavType,WhereSql) no support update ", $"导航一对一 {name} 配置了 Sql变量 不支持更新"); + List childList = new List(); + foreach (var parent in parentList) + { + var navPropertyValue = parentColumn.PropertyInfo.GetValue(parent); + var childItem = (TChild)nav.PropertyInfo.GetValue(parent); + if (childItem != null) + { + if (IsDefaultValue(navPropertyValue)) + { + var pkValue = thisPkColumn.PropertyInfo.GetValue(childItem); + if (IsDefaultValue(navPropertyValue)) + { + if (parentColumn.IsPrimarykey == false && isManyPk == false && parentColumn.IsIdentity == false) + { + this._Context.Updateable().AS(parentEntity.DbTableName) + .SetColumns(parentColumn.DbColumnName, pkValue) + .Where(parentPkColumn.DbColumnName, "=", parentPkColumn.PropertyInfo.GetValue(parent)).ExecuteCommand(); + } + navPropertyValue = pkValue; + } + + } + if (!IsDefaultValue(navPropertyValue) && isManyPk == false && parentPkColumn.IsIdentity == false) + { + this._Context.Updateable + ().AS(parentEntity.DbTableName) + .SetColumns(parentColumn.DbColumnName, navPropertyValue) + .Where(parentPkColumn.DbColumnName, "=", parentPkColumn.PropertyInfo.GetValue(parent)).ExecuteCommand(); + } + if (IsDefaultValue(navPropertyValue)) + { + InsertDatas(new List() { childItem }, thisPkColumn); + navPropertyValue = thisPkColumn.PropertyInfo.GetValue(childItem); + parentColumn.PropertyInfo.SetValue(parent, navPropertyValue); + this._Context.Updateable + ().AS(parentEntity.DbTableName) + .SetColumns(parentColumn.DbColumnName, navPropertyValue) + .Where(parentPkColumn.DbColumnName, "=", parentPkColumn.PropertyInfo.GetValue(parent)).ExecuteCommand(); + } + + thisPkColumn.PropertyInfo.SetValue(childItem, navPropertyValue); + childList.Add(childItem); + } + } + InsertDatas(childList, thisPkColumn); + this._ParentList = childList.Cast().ToList(); + SetNewParent(thisEntity, thisPkColumn); + } + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/UpdateNavProvider.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/UpdateNavProvider.cs new file mode 100644 index 000000000..27e82e2cf --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/UpdateNavProvider.cs @@ -0,0 +1,230 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public partial class UpdateNavProvider where T : class, new() where Root : class, new() + { + internal UpdateNavRootOptions _RootOptions { get; set; } + public List _Roots { get; set; } + public List _ParentList { get; set; } + public List _RootList { get; set; } + public EntityInfo _ParentEntity { get; set; } + public EntityColumnInfo _ParentPkColumn { get; set; } + public SqlSugarProvider _Context { get; set; } + + public UpdateNavOptions _Options { get; set; } + public bool IsFirst { get; set; } + public bool IsAsNav { get; set; } + internal NavContext NavContext { get; set; } + + public UpdateNavProvider AsNav() + { + return new UpdateNavProvider + { + _Context = _Context, + _ParentEntity = null, + _ParentList = null, + _Roots = _Roots, + _ParentPkColumn = this._Context.EntityMaintenance.GetEntityInfo().Columns.First(it => it.IsPrimarykey) + }; + } + public UpdateNavProvider ThenInclude(Expression> expression) where TChild : class, new() + { + return _ThenInclude(expression); + } + + public UpdateNavProvider ThenInclude(Expression>> expression) where TChild : class, new() + { + return _ThenInclude(expression); + } + + public UpdateNavProvider ThenInclude(Expression> expression, UpdateNavOptions options) where TChild : class, new() + { + _Options = options; + return _ThenInclude(expression); + } + + public UpdateNavProvider ThenInclude(Expression>> expression, UpdateNavOptions options) where TChild : class, new() + { + _Options = options; + return _ThenInclude(expression); + } + + private UpdateNavProvider _ThenInclude(Expression> expression) where TChild : class, new() + { + var isRoot = _RootList == null; + IsFirst = isRoot && this._ParentList == null; + InitParentList(); + var name = ExpressionTool.GetMemberName(expression); + var nav = this._ParentEntity.Columns.FirstOrDefault(x => x.PropertyName == name); + if (nav.Navigat == null) + { + Check.ExceptionEasy($"{name} no navigate attribute", $"{this._ParentEntity.EntityName}的属性{name}没有导航属性"); + } + if (_RootOptions?.IsDisableUpdateRoot == true) + { + //Future + } + else + { + + UpdateRoot(isRoot, nav); + } + IsFirst = false; + if (nav.Navigat.NavigatType == NavigateType.OneToOne || nav.Navigat.NavigatType == NavigateType.ManyToOne) + { + UpdateOneToOne(name, nav); + } + else if (nav.Navigat.NavigatType == NavigateType.OneToMany) + { + UpdateOneToMany(name, nav); + } + else + { + UpdateManyToMany(name, nav); + } + AddContextInfo(name, isRoot); + return GetResult(); + } + private UpdateNavProvider _ThenInclude(Expression>> expression) where TChild : class, new() + { + var isRoot = _RootList == null; + IsFirst = isRoot && this._ParentList == null; + InitParentList(); + var name = ExpressionTool.GetMemberName(expression); + if (name == null) + { + name = ExpressionTool.GetMemberNameByMethod(expression, name); + } + var nav = this._ParentEntity.Columns.FirstOrDefault(x => x.PropertyName == name); + if (nav.Navigat == null) + { + Check.ExceptionEasy($"{name} no navigate attribute", $"{this._ParentEntity.EntityName}的属性{name}没有导航属性"); + } + UpdateRoot(isRoot, nav); + IsFirst = false; + if (nav.Navigat.NavigatType == NavigateType.OneToOne || nav.Navigat.NavigatType == NavigateType.ManyToOne) + { + UpdateOneToOne(name, nav); + } + else if (nav.Navigat.NavigatType == NavigateType.OneToMany) + { + UpdateOneToMany(name, nav); + } + else + { + UpdateManyToMany(name, nav); + } + AddContextInfo(name, isRoot); + return GetResult(); + } + private void UpdateRoot(bool isRoot, EntityColumnInfo nav) + { + if (isRoot && nav.Navigat.NavigatType != NavigateType.ManyToMany && _RootOptions?.IsDisableUpdateRoot != true) + { + UpdateRoot(); + } + else if (isRoot && _RootOptions?.IsInsertRoot == true && nav.Navigat.NavigatType == NavigateType.ManyToMany) + { + UpdateRoot(); + } + else + { + if (_Options?.ManyToManyIsUpdateA == true) + { + UpdateRoot(); + } + } + } + + private void UpdateRoot() + { + if (IsAsNav) + { + return; + } + if (_Options?.RootFunc != null) + { + var updateable = this._Context.Updateable(_Roots); + var exp = _Options.RootFunc as Expression>>; + Check.ExceptionEasy(exp == null, "UpdateOptions.RootFunc is error", "UpdateOptions.RootFunc"); + var com = exp.Compile(); + com(updateable); + updateable.ExecuteCommand(); + } + else if (IsFirst && _RootOptions != null) + { + var isInsert = _RootOptions.IsInsertRoot; + if (isInsert) + { + var newRoots = new List(); + foreach (var item in _Roots) + { + var x = this._Context.Storageable(item).ToStorage(); + if (x.InsertList.HasValue()) + { + newRoots.Add(x.AsInsertable.IgnoreColumns(_RootOptions.IgnoreInsertColumns).EnableDiffLogEventIF(_RootOptions.IsDiffLogEvent, _RootOptions.DiffLogBizData).ExecuteReturnEntity()); + } + else + { + x.AsUpdateable + .EnableDiffLogEventIF(_RootOptions.IsDiffLogEvent, _RootOptions.DiffLogBizData) + .UpdateColumns(_RootOptions.UpdateColumns) + .IgnoreColumns(_RootOptions.IgnoreColumns) + .IgnoreNullColumns(_RootOptions.IsIgnoreAllNullColumns) + .ExecuteCommandWithOptLockIF(_RootOptions?.IsOptLock, _RootOptions?.IsOptLock); + newRoots.Add(item); + } + } + _ParentList = _RootList = newRoots.Cast().ToList(); + } + else + { + if (_Roots.Count == 1 && _RootOptions?.IsOptLock == true) + { + this._Context.Updateable(_Roots.First()) + .EnableDiffLogEventIF(_RootOptions.IsDiffLogEvent, _RootOptions.DiffLogBizData) + .UpdateColumns(_RootOptions.UpdateColumns) + .IgnoreColumns(_RootOptions.IgnoreColumns) + .IgnoreNullColumns(_RootOptions.IsIgnoreAllNullColumns) + .ExecuteCommandWithOptLockIF(_RootOptions?.IsOptLock, _RootOptions?.IsOptLock); + } + else + { + this._Context.Updateable(_Roots) + .EnableDiffLogEventIF(_RootOptions.IsDiffLogEvent, _RootOptions.DiffLogBizData) + .UpdateColumns(_RootOptions.UpdateColumns) + .IgnoreColumns(_RootOptions.IgnoreColumns) + .IgnoreNullColumns(_RootOptions.IsIgnoreAllNullColumns) + .ExecuteCommandWithOptLockIF(_RootOptions?.IsOptLock, _RootOptions?.IsOptLock); + } + } + } + else if (_RootOptions != null && _RootOptions?.IsDiffLogEvent == true) + { + this._Context.Updateable(_Roots).EnableDiffLogEvent(_RootOptions.DiffLogBizData).ExecuteCommand(); + } + else + { + this._Context.Updateable(_Roots).ExecuteCommand(); + } + } + + private void AddContextInfo(string name, bool isRoot) + { + if (IsAsNav || isRoot) + { + if (this.NavContext?.Items != null) + { + this.NavContext.Items.Add(new NavContextItem() { Level = 0, RootName = name }); + } + } + } + private bool NotAny(string name) + { + if (IsFirst) return true; + if (this.NavContext == null) return true; + return this.NavContext?.Items?.Any(it => it.RootName == name) == false; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/UpdateNavProviderHelper.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/UpdateNavProviderHelper.cs new file mode 100644 index 000000000..a12f57594 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/UpdateNavProviderHelper.cs @@ -0,0 +1,198 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public partial class UpdateNavProvider where T : class, new() where Root : class, new() + { + + private static bool IsDefaultValue(object pvValue) + { + return pvValue?.Equals(UtilMethods.GetDefaultValue(pvValue.GetType())) != false; + } + private void InitParentList() + { + if (_RootList == null) + { + _RootList = _ParentList = _Roots.Cast().ToList(); + _ParentEntity = this._Context.EntityMaintenance.GetEntityInfo(); + } + else if (_ParentList == null) + { + _ParentList = _RootList; + var pkColumn = this._Context.EntityMaintenance.GetEntityInfo().Columns.FirstOrDefault(it => it.IsPrimarykey); + this._ParentPkColumn = pkColumn; + } + } + + private UpdateNavProvider GetResult() where TChild : class, new() + { + return new UpdateNavProvider() + { + _Context = this._Context, + _ParentEntity = this._ParentEntity, + _ParentList = this._ParentList, + _Roots = this._Roots, + _ParentPkColumn = this._ParentPkColumn, + _RootList = this._RootList + }; + } + + + private void InsertIdentity(List datas) where Type : class, new() + { + foreach (var item in datas) + { + this._Context.Insertable(item).ExecuteCommandIdentityIntoEntity(); + } + } + + private EntityColumnInfo GetPkColumnByNav(EntityInfo entity, EntityColumnInfo nav) + { + var pkColumn = entity.Columns.FirstOrDefault(it => it.IsPrimarykey == true); + if (nav.Navigat.Name2.HasValue()) + { + pkColumn = entity.Columns.FirstOrDefault(it => it.PropertyName == nav.Navigat.Name2); + } + return pkColumn; + } + private EntityColumnInfo GetPkColumnByNav2(EntityInfo entity, EntityColumnInfo nav) + { + var pkColumn = entity.Columns.FirstOrDefault(it => it.IsPrimarykey == true); + return pkColumn; + } + private EntityColumnInfo GetFKColumnByNav(EntityInfo entity, EntityColumnInfo nav) + { + var fkColumn = entity.Columns.FirstOrDefault(it => it.PropertyName == nav.Navigat.Name); + return fkColumn; + } + private void InsertDatas(List children, EntityColumnInfo pkColumn, EntityColumnInfo NavColumn = null) where TChild : class, new() + { + children = children.Distinct().ToList(); + Check.ExceptionEasy(pkColumn == null, typeof(TChild).Name + " has no primary key", typeof(TChild).Name + "没有主键"); + var whereName = pkColumn.PropertyName; + if (_Options?.OneToOneSaveByPrimaryKey == true && pkColumn.IsPrimarykey == false) + { + var newPkColumn = this._Context.EntityMaintenance.GetEntityInfo().Columns.FirstOrDefault(it => it.IsPrimarykey); + if (newPkColumn != null) + { + whereName = newPkColumn.PropertyName; + } + } +; var x = this._Context.Storageable(children).WhereColumns(new string[] { whereName }).ToStorage(); + var insertData = x.InsertList.Select(it => it.Item).ToList(); + var updateData = x.UpdateList.Select(it => it.Item).ToList(); + Check.ExceptionEasy(pkColumn == null && NavColumn == null, $"The entity is invalid", $"实体错误无法使用导航"); + if (_Options?.CurrentFunc != null) + { + var updateable = x.AsUpdateable; + var exp = _Options.CurrentFunc as Expression>>; + Check.ExceptionEasy(exp == null, "UpdateOptions.CurrentFunc is error", "UpdateOptions.CurrentFunc参数设置错误"); + var com = exp.Compile(); + com(updateable); + if (IsDeleted) + { + updateable.PageSize(1).EnableQueryFilter().ExecuteCommand(); + } + else + { + updateable.ExecuteCommand(); + } + } + else if (pkColumn.IsPrimarykey == false) + { + var pk = this._Context.EntityMaintenance.GetEntityInfo().Columns.Where(it => it.IsPrimarykey); + List ignoreColumns = new List(); + if (_Options?.IgnoreColumns != null) + { + ignoreColumns.AddRange(_Options.IgnoreColumns); + } + if (pk.Any()) + { + ignoreColumns.AddRange(pk.Select(it => it.PropertyName)); + } + if (_Options?.OneToOneSaveByPrimaryKey == true) + { + ignoreColumns = ignoreColumns.Where(it => it != whereName).ToList(); + } + if (IsDeleted) + { + x.AsUpdateable.IgnoreColumns(ignoreColumns.ToArray()).PageSize(1).EnableQueryFilter().ExecuteCommand(); + } + else + { + x.AsUpdateable.IgnoreColumns(ignoreColumns.ToArray()).ExecuteCommand(); + } + } + else + { + var ignoreColumns = _Options?.IgnoreColumns; + var isIgnoreNull = _Options?.IgnoreNullColumns == true; + if (IsDeleted) + { + x.AsUpdateable.IgnoreNullColumns(isIgnoreNull).IgnoreColumns(ignoreColumns?.ToArray()).PageSize(1).EnableQueryFilter().ExecuteCommand(); + } + else + { + x.AsUpdateable.IgnoreNullColumns(isIgnoreNull).IgnoreColumns(ignoreColumns?.ToArray()).ExecuteCommand(); + } + } + InitData(pkColumn, insertData); + if (_NavigateType == NavigateType.OneToMany) + { + this._ParentList = children.Cast().ToList(); + } + else + { + this._ParentList = insertData.Union(updateData).Cast().ToList(); + } + } + + private void InitData(EntityColumnInfo pkColumn, List UpdateData) where TChild : class, new() + { + if (pkColumn.IsIdentity || pkColumn.OracleSequenceName.HasValue()) + { + InsertIdentity(UpdateData); + } + else if (pkColumn.UnderType == UtilConstants.LongType) + { + SetValue(pkColumn, UpdateData, () => SnowFlakeSingle.Instance.NextId()); + } + else if (pkColumn.UnderType == UtilConstants.GuidType) + { + SetValue(pkColumn, UpdateData, () => Guid.NewGuid()); + } + else if (pkColumn.UnderType == UtilConstants.StringType) + { + SetValue(pkColumn, UpdateData, () => Guid.NewGuid().ToString()); + } + else + { + SetError(pkColumn, UpdateData); + } + } + + private void SetValue(EntityColumnInfo pkColumn, List UpdateData, Func value) where TChild : class, new() + { + foreach (var child in UpdateData) + { + if (IsDefaultValue(pkColumn.PropertyInfo.GetValue(child))) + { + pkColumn.PropertyInfo.SetValue(child, value()); + } + } + this._Context.Insertable(UpdateData).ExecuteCommand(); + } + private void SetError(EntityColumnInfo pkColumn, List UpdateData) where TChild : class, new() + { + foreach (var child in UpdateData) + { + if (IsDefaultValue(pkColumn.PropertyInfo.GetValue(child))) + { + var name = pkColumn.EntityName + " " + pkColumn.DbColumnName; + Check.ExceptionEasy($"The field {name} is not an autoassignment type and requires an assignment", $"字段{name}不是可自动赋值类型,需要赋值 , 可赋值类型有 自增、long、Guid、string"); + } + } + this._Context.Insertable(UpdateData).ExecuteCommand(); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/UpdateNavTask.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/UpdateNavTask.cs new file mode 100644 index 000000000..751600b3d --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExecuteNavProvider/UpdateNavTask.cs @@ -0,0 +1,226 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public class UpdateNavTaskInit where T : class, new() where Root : class, new() + { + + internal SqlSugarProvider Context { get; set; } + internal UpdateNavProvider UpdateNavProvider { get; set; } + internal NavContext NavContext { get; set; } + + public UpdateNavTask Include(Expression> expression) where TChild : class, new() + { + this.Context = UpdateNavProvider._Context; + UpdateNavProvider.NavContext = this.NavContext; + UpdateNavTask result = new UpdateNavTask(); + Func> func = () => UpdateNavProvider.ThenInclude(expression); + result.PreFunc = func; + result.Context = this.Context; + result.NavContext = this.NavContext; + return result; + } + public UpdateNavTask Include(Expression>> expression) where TChild : class, new() + { + this.Context = UpdateNavProvider._Context; + UpdateNavProvider.NavContext = this.NavContext; + UpdateNavTask result = new UpdateNavTask(); + Func> func = () => UpdateNavProvider.ThenInclude(expression); + result.PreFunc = func; + result.Context = this.Context; + result.NavContext = UpdateNavProvider.NavContext; + return result; + } + + public UpdateNavTask Include(Expression> expression, UpdateNavOptions options) where TChild : class, new() + { + this.Context = UpdateNavProvider._Context; + UpdateNavProvider.NavContext = this.NavContext; + UpdateNavTask result = new UpdateNavTask(); + Func> func = () => UpdateNavProvider.ThenInclude(expression, options); + result.PreFunc = func; + result.Context = this.Context; + result.NavContext = UpdateNavProvider.NavContext; + return result; + } + public UpdateNavTask Include(Expression>> expression, UpdateNavOptions options) where TChild : class, new() + { + this.Context = UpdateNavProvider._Context; + UpdateNavProvider.NavContext = this.NavContext; + UpdateNavTask result = new UpdateNavTask(); + Func> func = () => UpdateNavProvider.ThenInclude(expression, options); + result.PreFunc = func; + result.Context = this.Context; + result.NavContext = UpdateNavProvider.NavContext; + return result; + } + public UpdateNavMethodInfo IncludesAllFirstLayer(params string[] ignoreColumns) + { + if (ignoreColumns == null) + { + ignoreColumns = Array.Empty(); + } + this.Context = UpdateNavProvider._Context; + var navColumns = this.Context.EntityMaintenance.GetEntityInfo().Columns.Where(it => !ignoreColumns.Contains(it.PropertyName) || !ignoreColumns.Any(z => z.EqualCase(it.DbColumnName))).Where(it => it.Navigat != null).ToList(); + var updateNavs = this; + UpdateNavMethodInfo methodInfo = updateNavs.IncludeByNameString(navColumns[0].PropertyName); + foreach (var item in navColumns.Skip(1)) + { + methodInfo = methodInfo.IncludeByNameString(item.PropertyName); + } + return methodInfo; + } + + public UpdateNavMethodInfo IncludesAllFirstLayer(UpdateNavOptions updateNavOptions, params string[] ignoreColumns) + { + if (ignoreColumns == null) + { + ignoreColumns = Array.Empty(); + } + this.Context = UpdateNavProvider._Context; + var navColumns = this.Context.EntityMaintenance.GetEntityInfo().Columns.Where(it => !ignoreColumns.Contains(it.PropertyName) || !ignoreColumns.Any(z => z.EqualCase(it.DbColumnName))).Where(it => it.Navigat != null).ToList(); + var updateNavs = this; + UpdateNavMethodInfo methodInfo = updateNavs.IncludeByNameString(navColumns[0].PropertyName); + foreach (var item in navColumns.Skip(1)) + { + methodInfo = methodInfo.IncludeByNameString(item.PropertyName, updateNavOptions); + } + return methodInfo; + } + public UpdateNavMethodInfo IncludeByNameString(string navMemberName, UpdateNavOptions updateNavOptions = null) + { + UpdateNavMethodInfo result = new UpdateNavMethodInfo(); + result.Context = UpdateNavProvider._Context; + var entityInfo = result.Context.EntityMaintenance.GetEntityInfo(); + Type properyItemType; + bool isList; + Expression exp = UtilMethods.GetIncludeExpression(navMemberName, entityInfo, out properyItemType, out isList); + var method = this.GetType().GetMyMethod("Include", 2, isList) + .MakeGenericMethod(properyItemType); + var obj = method.Invoke(this, new object[] { exp, updateNavOptions }); + result.MethodInfos = obj; + return result; + } + + + } + public class UpdateNavTask where T : class, new() where Root : class, new() + { + public SqlSugarProvider Context { get; set; } + public Func> PreFunc { get; set; } + internal NavContext NavContext { get; set; } + + + #region +1 + public UpdateNavTask ThenInclude(Expression> expression) where TChild : class, new() + { + UpdateNavTask result = new UpdateNavTask(); + Func> func = () => PreFunc().ThenInclude(expression); + result.PreFunc = func; + result.Context = this.Context; + result.NavContext = this.NavContext; + return result; + } + public UpdateNavTask ThenInclude(Expression>> expression) where TChild : class, new() + { + UpdateNavTask result = new UpdateNavTask(); + Func> func = () => PreFunc().ThenInclude(expression); + result.PreFunc = func; + result.Context = this.Context; + result.NavContext = this.NavContext; + return result; + } + public UpdateNavTask Include(Expression> expression) where TChild : class, new() + { + return AsNav().ThenInclude(expression); + } + public UpdateNavTask Include(Expression>> expression) where TChild : class, new() + { + return AsNav().ThenInclude(expression); + } + + #endregion + + + #region +2 + public UpdateNavTask ThenInclude(Expression> expression, UpdateNavOptions options) where TChild : class, new() + { + UpdateNavTask result = new UpdateNavTask(); + Func> func = () => + { + var nav = PreFunc().ThenInclude(expression, options); + nav.NavContext = this.NavContext; + return nav; + }; + result.PreFunc = func; + result.Context = this.Context; + return result; + } + public UpdateNavTask ThenInclude(Expression>> expression, UpdateNavOptions options) where TChild : class, new() + { + UpdateNavTask result = new UpdateNavTask(); + Func> func = () => + { + var nav = PreFunc().ThenInclude(expression, options); + result.NavContext = this.NavContext; + return nav; + }; + result.PreFunc = func; + result.Context = this.Context; + return result; + } + public UpdateNavTask Include(Expression> expression, UpdateNavOptions options) where TChild : class, new() + { + return AsNav().ThenInclude(expression, options); + } + public UpdateNavTask Include(Expression>> expression, UpdateNavOptions options) where TChild : class, new() + { + return AsNav().ThenInclude(expression, options); + } + #endregion + + + public bool ExecuteCommand() + { + var hasTran = this.Context.Ado.Transaction != null; + if (hasTran) + { + PreFunc(); + } + else + { + this.Context.Ado.UseTran(() => + { + PreFunc(); + }, ex => throw ex); + } + return true; + } + public async Task ExecuteCommandAsync() + { + await Task.Run(async () => + { + ExecuteCommand(); + await Task.Delay(0).ConfigureAwait(false); + }).ConfigureAwait(false); + return true; + } + + private UpdateNavTask AsNav() + { + UpdateNavTask result = new UpdateNavTask(); + Func> func = () => + { + var navres = PreFunc().AsNav(); + navres.IsAsNav = true; + navres.NavContext = this.NavContext; + return navres; + }; + result.PreFunc = func; + result.Context = this.Context; + result.NavContext = this.NavContext; + return result; + } + } + +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExpressionableProvider/Expressionable.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExpressionableProvider/Expressionable.cs new file mode 100644 index 000000000..3fafadf50 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/ExpressionableProvider/Expressionable.cs @@ -0,0 +1,585 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public class Expressionable where T : class, new() + { + Expression> _exp = null; + + public Expressionable And(Expression> exp) + { + if (_exp == null) + _exp = exp; + else + _exp = Expression.Lambda>(Expression.AndAlso(_exp.Body, exp.Body), _exp.Parameters); + return this; + } + + public Expressionable AndIF(bool isAnd, Expression> exp) + { + if (isAnd) + And(exp); + return this; + } + + public Expressionable Or(Expression> exp) + { + if (_exp == null) + _exp = exp; + else + _exp = Expression.Lambda>(Expression.OrElse(_exp.Body, exp.Body), _exp.Parameters); + return this; + } + + public Expressionable OrIF(bool isOr, Expression> exp) + { + if (isOr) + Or(exp); + return this; + } + + + public Expression> ToExpression() + { + if (_exp == null) + _exp = it => true; + return _exp; + } + } + public class Expressionable where T : class, new() where T2 : class, new() + { + Expression> _exp = null; + + public Expressionable And(Expression> exp) + { + if (_exp == null) + _exp = exp; + else + _exp = Expression.Lambda>(Expression.AndAlso(_exp.Body, exp.Body), _exp.Parameters); + return this; + } + + public Expressionable AndIF(bool isAnd, Expression> exp) + { + if (isAnd) + And(exp); + return this; + } + + public Expressionable Or(Expression> exp) + { + if (_exp == null) + _exp = exp; + else + _exp = Expression.Lambda>(Expression.OrElse(_exp.Body, exp.Body), _exp.Parameters); + return this; + } + + public Expressionable OrIF(bool isOr, Expression> exp) + { + if (isOr) + Or(exp); + return this; + } + + + public Expression> ToExpression() + { + if (_exp == null) + _exp = (it, t2) => true; + return _exp; + } + } + public class Expressionable where T : class, new() where T2 : class, new() where T3 : class, new() + { + Expression> _exp = null; + + public Expressionable And(Expression> exp) + { + if (_exp == null) + _exp = exp; + else + _exp = Expression.Lambda>(Expression.AndAlso(_exp.Body, exp.Body), _exp.Parameters); + return this; + } + + public Expressionable AndIF(bool isAnd, Expression> exp) + { + if (isAnd) + And(exp); + return this; + } + + public Expressionable Or(Expression> exp) + { + if (_exp == null) + _exp = exp; + else + _exp = Expression.Lambda>(Expression.OrElse(_exp.Body, exp.Body), _exp.Parameters); + return this; + } + + public Expressionable OrIF(bool isOr, Expression> exp) + { + if (isOr) + Or(exp); + return this; + } + + + public Expression> ToExpression() + { + if (_exp == null) + _exp = (it, t2, t3) => true; + return _exp; + } + } + public class Expressionable where T : class, new() where T2 : class, new() where T3 : class, new() where T4 : class, new() + { + Expression> _exp = null; + + public Expressionable And(Expression> exp) + { + if (_exp == null) + _exp = exp; + else + _exp = Expression.Lambda>(Expression.AndAlso(_exp.Body, exp.Body), _exp.Parameters); + return this; + } + + public Expressionable AndIF(bool isAnd, Expression> exp) + { + if (isAnd) + And(exp); + return this; + } + + public Expressionable Or(Expression> exp) + { + if (_exp == null) + _exp = exp; + else + _exp = Expression.Lambda>(Expression.OrElse(_exp.Body, exp.Body), _exp.Parameters); + return this; + } + + public Expressionable OrIF(bool isOr, Expression> exp) + { + if (isOr) + Or(exp); + return this; + } + + + public Expression> ToExpression() + { + if (_exp == null) + _exp = (it, t2, t3, t4) => true; + return _exp; + } + } + public class Expressionable where T : class, new() where T2 : class, new() where T3 : class, new() where T4 : class, new() where T5 : class, new() + { + Expression> _exp = null; + + public Expressionable And(Expression> exp) + { + if (_exp == null) + _exp = exp; + else + _exp = Expression.Lambda>(Expression.AndAlso(_exp.Body, exp.Body), _exp.Parameters); + return this; + } + + public Expressionable AndIF(bool isAnd, Expression> exp) + { + if (isAnd) + And(exp); + return this; + } + + public Expressionable Or(Expression> exp) + { + if (_exp == null) + _exp = exp; + else + _exp = Expression.Lambda>(Expression.OrElse(_exp.Body, exp.Body), _exp.Parameters); + return this; + } + + public Expressionable OrIF(bool isOr, Expression> exp) + { + if (isOr) + Or(exp); + return this; + } + + + public Expression> ToExpression() + { + if (_exp == null) + _exp = (it, t2, t3, t4, T5) => true; + return _exp; + } + } + + public class Expressionable where T : class, new() where T2 : class, new() where T3 : class, new() where T4 : class, new() where T5 : class, new() where T6 : class, new() + { + Expression> _exp = null; + + public Expressionable And(Expression> exp) + { + if (_exp == null) + _exp = exp; + else + _exp = Expression.Lambda>(Expression.AndAlso(_exp.Body, exp.Body), _exp.Parameters); + return this; + } + + public Expressionable AndIF(bool isAnd, Expression> exp) + { + if (isAnd) + And(exp); + return this; + } + + public Expressionable Or(Expression> exp) + { + if (_exp == null) + _exp = exp; + else + _exp = Expression.Lambda>(Expression.OrElse(_exp.Body, exp.Body), _exp.Parameters); + return this; + } + + public Expressionable OrIF(bool isOr, Expression> exp) + { + if (isOr) + Or(exp); + return this; + } + + + public Expression> ToExpression() + { + if (_exp == null) + _exp = (it, t2, t3, t4, T5, t6) => true; + return _exp; + } + } + public class Expressionable where T : class, new() where T2 : class, new() where T3 : class, new() where T4 : class, new() where T5 : class, new() where T6 : class, new() where T7 : class, new() + { + Expression> _exp = null; + + public Expressionable And(Expression> exp) + { + if (_exp == null) + _exp = exp; + else + _exp = Expression.Lambda>(Expression.AndAlso(_exp.Body, exp.Body), _exp.Parameters); + return this; + } + + public Expressionable AndIF(bool isAnd, Expression> exp) + { + if (isAnd) + And(exp); + return this; + } + + public Expressionable Or(Expression> exp) + { + if (_exp == null) + _exp = exp; + else + _exp = Expression.Lambda>(Expression.OrElse(_exp.Body, exp.Body), _exp.Parameters); + return this; + } + + public Expressionable OrIF(bool isOr, Expression> exp) + { + if (isOr) + Or(exp); + return this; + } + + + public Expression> ToExpression() + { + if (_exp == null) + _exp = (it, t2, t3, t4, T5, t6, t7) => true; + return _exp; + } + } + public class Expressionable where T : class, new() where T2 : class, new() where T3 : class, new() where T4 : class, new() where T5 : class, new() where T6 : class, new() where T7 : class, new() where T8 : class, new() + { + Expression> _exp = null; + + public Expressionable And(Expression> exp) + { + if (_exp == null) + _exp = exp; + else + _exp = Expression.Lambda>(Expression.AndAlso(_exp.Body, exp.Body), _exp.Parameters); + return this; + } + + public Expressionable AndIF(bool isAnd, Expression> exp) + { + if (isAnd) + And(exp); + return this; + } + + public Expressionable Or(Expression> exp) + { + if (_exp == null) + _exp = exp; + else + _exp = Expression.Lambda>(Expression.OrElse(_exp.Body, exp.Body), _exp.Parameters); + return this; + } + + public Expressionable OrIF(bool isOr, Expression> exp) + { + if (isOr) + Or(exp); + return this; + } + + + public Expression> ToExpression() + { + if (_exp == null) + _exp = (it, t2, t3, t4, T5, t6, t7, t8) => true; + return _exp; + } + } + public class Expressionable where T : class, new() where T2 : class, new() where T3 : class, new() where T4 : class, new() where T5 : class, new() where T6 : class, new() where T7 : class, new() where T8 : class, new() where T9 : class, new() + { + Expression> _exp = null; + + public Expressionable And(Expression> exp) + { + if (_exp == null) + _exp = exp; + else + _exp = Expression.Lambda>(Expression.AndAlso(_exp.Body, exp.Body), _exp.Parameters); + return this; + } + + public Expressionable AndIF(bool isAnd, Expression> exp) + { + if (isAnd) + And(exp); + return this; + } + + public Expressionable Or(Expression> exp) + { + if (_exp == null) + _exp = exp; + else + _exp = Expression.Lambda>(Expression.OrElse(_exp.Body, exp.Body), _exp.Parameters); + return this; + } + + public Expressionable OrIF(bool isOr, Expression> exp) + { + if (isOr) + Or(exp); + return this; + } + + + public Expression> ToExpression() + { + if (_exp == null) + _exp = (it, t2, t3, t4, T5, t6, t7, t8, t9) => true; + return _exp; + } + } + public class Expressionable where T : class, new() where T2 : class, new() where T3 : class, new() where T4 : class, new() where T5 : class, new() where T6 : class, new() where T7 : class, new() where T8 : class, new() where T9 : class, new() + { + Expression> _exp = null; + + public Expressionable And(Expression> exp) + { + if (_exp == null) + _exp = exp; + else + _exp = Expression.Lambda>(Expression.AndAlso(_exp.Body, exp.Body), _exp.Parameters); + return this; + } + + public Expressionable AndIF(bool isAnd, Expression> exp) + { + if (isAnd) + And(exp); + return this; + } + + public Expressionable Or(Expression> exp) + { + if (_exp == null) + _exp = exp; + else + _exp = Expression.Lambda>(Expression.OrElse(_exp.Body, exp.Body), _exp.Parameters); + return this; + } + + public Expressionable OrIF(bool isOr, Expression> exp) + { + if (isOr) + Or(exp); + return this; + } + + + public Expression> ToExpression() + { + if (_exp == null) + _exp = (it, t2, t3, t4, T5, t6, t7, t8, t9, t10) => true; + return _exp; + } + } + public class Expressionable where T : class, new() where T2 : class, new() where T3 : class, new() where T4 : class, new() where T5 : class, new() where T6 : class, new() where T7 : class, new() where T8 : class, new() where T9 : class, new() + { + Expression> _exp = null; + + public Expressionable And(Expression> exp) + { + if (_exp == null) + _exp = exp; + else + _exp = Expression.Lambda>(Expression.AndAlso(_exp.Body, exp.Body), _exp.Parameters); + return this; + } + + public Expressionable AndIF(bool isAnd, Expression> exp) + { + if (isAnd) + And(exp); + return this; + } + + public Expressionable Or(Expression> exp) + { + if (_exp == null) + _exp = exp; + else + _exp = Expression.Lambda>(Expression.OrElse(_exp.Body, exp.Body), _exp.Parameters); + return this; + } + + public Expressionable OrIF(bool isOr, Expression> exp) + { + if (isOr) + Or(exp); + return this; + } + + + public Expression> ToExpression() + { + if (_exp == null) + _exp = (it, t2, t3, t4, T5, t6, t7, t8, t9, t10, t11) => true; + return _exp; + } + } + public class Expressionable where T : class, new() where T2 : class, new() where T3 : class, new() where T4 : class, new() where T5 : class, new() where T6 : class, new() where T7 : class, new() where T8 : class, new() where T9 : class, new() + { + Expression> _exp = null; + + public Expressionable And(Expression> exp) + { + if (_exp == null) + _exp = exp; + else + _exp = Expression.Lambda>(Expression.AndAlso(_exp.Body, exp.Body), _exp.Parameters); + return this; + } + + public Expressionable AndIF(bool isAnd, Expression> exp) + { + if (isAnd) + And(exp); + return this; + } + + public Expressionable Or(Expression> exp) + { + if (_exp == null) + _exp = exp; + else + _exp = Expression.Lambda>(Expression.OrElse(_exp.Body, exp.Body), _exp.Parameters); + return this; + } + + public Expressionable OrIF(bool isOr, Expression> exp) + { + if (isOr) + Or(exp); + return this; + } + + + public Expression> ToExpression() + { + if (_exp == null) + _exp = (it, t2, t3, t4, T5, t6, t7, t8, t9, t10, t11, t12) => true; + return _exp; + } + } + public static class Expressionable + { + public static Expressionable Create() where T : class, new() + { + return new Expressionable(); + } + public static Expressionable Create() where T : class, new() where T2 : class, new() + { + return new Expressionable(); + } + public static Expressionable Create() where T : class, new() where T2 : class, new() where T3 : class, new() + { + return new Expressionable(); + } + public static Expressionable Create() where T : class, new() where T2 : class, new() where T3 : class, new() where T4 : class, new() + { + return new Expressionable(); + } + public static Expressionable Create() where T : class, new() where T2 : class, new() where T3 : class, new() where T4 : class, new() where T5 : class, new() + { + return new Expressionable(); + } + public static Expressionable Create() where T : class, new() where T2 : class, new() where T3 : class, new() where T4 : class, new() where T5 : class, new() where T6 : class, new() + { + return new Expressionable(); + } + public static Expressionable Create() where T : class, new() where T2 : class, new() where T3 : class, new() where T4 : class, new() where T5 : class, new() where T6 : class, new() where T7 : class, new() + { + return new Expressionable(); + } + public static Expressionable Create() where T : class, new() where T2 : class, new() where T3 : class, new() where T4 : class, new() where T5 : class, new() where T6 : class, new() where T7 : class, new() where T8 : class, new() + { + return new Expressionable(); + } + public static Expressionable Create() where T : class, new() where T2 : class, new() where T3 : class, new() where T4 : class, new() where T5 : class, new() where T6 : class, new() where T7 : class, new() where T8 : class, new() where T9 : class, new() + { + return new Expressionable(); + } + public static Expressionable Create() where T : class, new() where T2 : class, new() where T3 : class, new() where T4 : class, new() where T5 : class, new() where T6 : class, new() where T7 : class, new() where T8 : class, new() where T9 : class, new() where T10 : class, new() + { + return new Expressionable(); + } + public static Expressionable Create() where T : class, new() where T2 : class, new() where T3 : class, new() where T4 : class, new() where T5 : class, new() where T6 : class, new() where T7 : class, new() where T8 : class, new() where T9 : class, new() where T10 : class, new() where T11 : class, new() + { + return new Expressionable(); + } + public static Expressionable Create() where T : class, new() where T2 : class, new() where T3 : class, new() where T4 : class, new() where T5 : class, new() where T6 : class, new() where T7 : class, new() where T8 : class, new() where T9 : class, new() where T10 : class, new() where T11 : class, new() where T12 : class, new() + { + return new Expressionable(); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/FastestProvider/FastBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/FastestProvider/FastBuilder.cs new file mode 100644 index 000000000..43e8d30ec --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/FastestProvider/FastBuilder.cs @@ -0,0 +1,72 @@ +using System.Data; + +namespace SqlSugar +{ + public class FastBuilder + { + public EntityInfo FastEntityInfo { get; set; } + public virtual bool IsActionUpdateColumns { get; set; } + public virtual DbFastestProperties DbFastestProperties { get; set; } + public SqlSugarProvider Context { get; set; } + public virtual string CharacterSet { get; set; } + public virtual string UpdateSql { get; set; } = @"UPDATE TM + SET {0} + FROM {1} TM + INNER JOIN {2} TE ON {3} "; + + + public virtual void CloseDb() + { + if (this.Context.CurrentConnectionConfig.IsAutoCloseConnection && this.Context.Ado.Transaction == null) + { + this.Context.Ado.Connection.Close(); + } + } + + public virtual async Task UpdateByTempAsync(string tableName, string tempName, string[] updateColumns, string[] whereColumns) + { + var sqlbuilder = this.Context.Queryable().SqlBuilder; + Check.ArgumentNullException(updateColumns.Length == 0, "update columns count is 0"); + Check.ArgumentNullException(whereColumns.Length == 0, "where columns count is 0"); + var sets = string.Join(",", updateColumns.Select(it => $"TM.{sqlbuilder.GetTranslationColumnName(it)}=TE.{sqlbuilder.GetTranslationColumnName(it)}")); + var wheres = string.Join(" AND ", whereColumns.Select(it => $"TM.{sqlbuilder.GetTranslationColumnName(it)}=TE.{sqlbuilder.GetTranslationColumnName(it)}")); + string sql = string.Format(UpdateSql, sets, tableName, tempName, wheres); + return await Context.Ado.ExecuteCommandAsync(sql).ConfigureAwait(false); + } + + public virtual async Task CreateTempAsync(DataTable dt) where T : class, new() + { + var sqlbuilder = this.Context.Queryable().SqlBuilder; + await Context.UnionAll( + Context.Queryable().Filter(null, true).Select(string.Join(",", dt.Columns.Cast().Select(it => sqlbuilder.GetTranslationColumnName(it.ColumnName)))).Where(it => false).AS(dt.TableName), + Context.Queryable().Filter(null, true).Select(string.Join(",", dt.Columns.Cast().Select(it => sqlbuilder.GetTranslationColumnName(it.ColumnName)))).Where(it => false).AS(dt.TableName)).Select("top 1 * into #temp").ToListAsync().ConfigureAwait(false); + dt.TableName = "#temp"; + } + + public async virtual Task Merge(string tableName, DataTable dt, EntityInfo entityInfo, string[] whereColumns, string[] updateColumns, List datas) where T : class, new() + { + var result = 0; + var pageSize = 2000; + if (dt.Columns.Count > 100) + { + pageSize = 100; + } + else if (dt.Columns.Count > 50) + { + pageSize = 300; + } + else if (dt.Columns.Count > 30) + { + pageSize = 500; + } + await Context.Utilities.PageEachAsync(datas, pageSize, async pageItems => + { + var x = await Context.Storageable(pageItems).As(tableName).WhereColumns(whereColumns).ToStorageAsync().ConfigureAwait(false); + result += await x.BulkCopyAsync().ConfigureAwait(false); + result += await x.BulkUpdateAsync(updateColumns).ConfigureAwait(false); + return result; + }).ConfigureAwait(false); + return result; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/FastestProvider/FastestProvider.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/FastestProvider/FastestProvider.cs new file mode 100644 index 000000000..0c6ec9804 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/FastestProvider/FastestProvider.cs @@ -0,0 +1,540 @@ +using System.Data; +using System.Reflection; +namespace SqlSugar +{ + public partial class FastestProvider : IFastest where T : class, new() + { + internal SqlSugarProvider context; + private ISugarQueryable queryable; + private EntityInfo entityInfo { get; set; } + public bool isLog; + public FastestProvider(SqlSugarProvider sqlSugarProvider) + { + this.context = sqlSugarProvider; + this.queryable = this.context.Queryable(); + entityInfo = this.context.EntityMaintenance.GetEntityInfo(); + } + + #region BulkCopy + public int BulkCopy(string tableName, DataTable dt) + { + return BulkCopyAsync(tableName, dt).ConfigureAwait(true).GetAwaiter().GetResult(); + } + public int BulkCopy(DataTable dt) + { + Check.ExceptionEasy(this.AsName.IsNullOrEmpty(), "need .AS(tablaeName) ", "需要 .AS(tablaeName) 设置表名"); + return BulkCopyAsync(this.AsName, dt).ConfigureAwait(true).GetAwaiter().GetResult(); + } + public Task BulkCopyAsync(DataTable dt) + { + Check.ExceptionEasy(this.AsName.IsNullOrEmpty(), "need .AS(tablaeName) ", "需要 .AS(tablaeName) 设置表名"); + return BulkCopyAsync(this.AsName, dt); + } + public async Task BulkCopyAsync(string tableName, DataTable dt) + { + if (Size > 0) + { + int resul = 0; + await context.Utilities.PageEachAsync(dt.Rows.Cast().ToList(), Size, async item => + { + resul += await _BulkCopy(tableName, item.CopyToDataTable()).ConfigureAwait(false); + }).ConfigureAwait(false); + return resul; + } + else + { + return await _BulkCopy(tableName, dt).ConfigureAwait(false); + } + } + public int BulkCopy(List datas) + { + return BulkCopyAsync(datas).ConfigureAwait(true).GetAwaiter().GetResult(); + } + public async Task BulkCopyAsync(List datas) + { + if (Size > 0) + { + if (this.GetBuider()?.DbFastestProperties?.NoPage == true) + { + Size = int.MaxValue / 2; + } + int resul = 0; + await context.Utilities.PageEachAsync(datas, Size, async item => + { + resul += await _BulkCopy(item).ConfigureAwait(false); + }).ConfigureAwait(false); + return resul; + } + else + { + return await _BulkCopy(datas).ConfigureAwait(false); + } + } + #endregion + + #region BulkUpdate + public int BulkUpdate(List datas) + { + return BulkUpdateAsync(datas).ConfigureAwait(true).GetAwaiter().GetResult(); + } + public async Task BulkUpdateAsync(List datas) + { + var whereColumns = entityInfo.Columns.Where(it => it.IsPrimarykey).Select(it => it.DbColumnName ?? it.PropertyName).ToArray(); + var updateColumns = entityInfo.Columns.Where(it => !it.IsPrimarykey && !it.IsIdentity && !it.IsOnlyIgnoreUpdate && !it.IsIgnore).Select(it => it.DbColumnName ?? it.PropertyName).ToArray(); + return await BulkUpdateAsync(datas, whereColumns, updateColumns).ConfigureAwait(false); + } + public int BulkUpdate(List datas, string[] whereColumns, string[] updateColumns) + { + whereColumns = whereColumns.Select(x => this.entityInfo.Columns.FirstOrDefault(it => it.PropertyName.EqualCase(x) || it.DbColumnName.EqualCase(x))?.DbColumnName ?? x).ToArray(); + updateColumns = updateColumns.Select(x => this.entityInfo.Columns.FirstOrDefault(it => it.PropertyName.EqualCase(x) || it.DbColumnName.EqualCase(x))?.DbColumnName ?? x).ToArray(); + return BulkUpdateAsync(datas, whereColumns, updateColumns).ConfigureAwait(true).GetAwaiter().GetResult(); + } + + public int BulkUpdate(List datas, string[] whereColumns) + { + return BulkUpdateAsync(datas, whereColumns).GetAwaiter().GetResult(); + } + + public async Task BulkUpdateAsync(List datas, string[] whereColumns) + { + whereColumns = whereColumns.Select(x => this.entityInfo.Columns.FirstOrDefault(it => it.PropertyName.EqualCase(x) || it.DbColumnName.EqualCase(x))?.DbColumnName ?? x).ToArray(); + var updateColumns = this.entityInfo.Columns + .Where(it => !whereColumns.Any(z => z.EqualCase(it.DbColumnName))) + .Where(it => !it.IsIdentity) + .Where(it => !it.IsPrimarykey) + .Where(it => !it.IsOnlyIgnoreUpdate) + .Where(it => !it.IsIgnore) + .Select(it => it.DbColumnName) + .ToArray(); + return await BulkUpdateAsync(datas, whereColumns, updateColumns).ConfigureAwait(true); + } + public async Task BulkUpdateAsync(List datas, string[] whereColumns, string[] updateColumns) + { + + if (Size > 0) + { + int resul = 0; + await context.Utilities.PageEachAsync(datas, Size, async item => + { + resul += await _BulkUpdate(item, whereColumns, updateColumns).ConfigureAwait(false); + }).ConfigureAwait(false); + return resul; + } + else + { + return await _BulkUpdate(datas, whereColumns, updateColumns).ConfigureAwait(false); + } + } + + public int BulkUpdate(string tableName, DataTable dataTable, string[] whereColumns, string[] updateColumns) + { + return BulkUpdateAsync(tableName, dataTable, whereColumns, updateColumns).ConfigureAwait(true).GetAwaiter().GetResult(); + } + public int BulkUpdate(DataTable dataTable, string[] whereColumns, string[] updateColumns) + { + Check.ExceptionEasy(this.AsName.IsNullOrEmpty(), "need .AS(tablaeName) ", "需要 .AS(tablaeName) 设置表名"); + return BulkUpdateAsync(this.AsName, dataTable, whereColumns, updateColumns).ConfigureAwait(true).GetAwaiter().GetResult(); + } + public int BulkUpdate(DataTable dataTable, string[] whereColumns) + { + string[] updateColumns = dataTable.Columns.Cast().Select(it => it.ColumnName).Where(it => !whereColumns.Any(z => z.EqualCase(it))).ToArray(); + whereColumns = dataTable.Columns.Cast().Select(it => it.ColumnName).Where(it => whereColumns.Any(z => z.EqualCase(it))).ToArray(); + Check.ExceptionEasy(this.AsName.IsNullOrEmpty(), "need .AS(tablaeName) ", "需要 .AS(tablaeName) 设置表名"); + return BulkUpdateAsync(this.AsName, dataTable, whereColumns, updateColumns).ConfigureAwait(true).GetAwaiter().GetResult(); + } + public Task BulkUpdateAsync(DataTable dataTable, string[] whereColumns) + { + string[] updateColumns = dataTable.Columns.Cast().Select(it => it.ColumnName).Where(it => !whereColumns.Any(z => z.EqualCase(it))).ToArray(); + whereColumns = dataTable.Columns.Cast().Select(it => it.ColumnName).Where(it => whereColumns.Any(z => z.EqualCase(it))).ToArray(); + Check.ExceptionEasy(this.AsName.IsNullOrEmpty(), "need .AS(tablaeName) ", "需要 .AS(tablaeName) 设置表名"); + return BulkUpdateAsync(this.AsName, dataTable, whereColumns, updateColumns); + } + public async Task BulkUpdateAsync(string tableName, DataTable dataTable, string[] whereColumns, string[] updateColumns) + { + + if (Size > 0) + { + int resul = 0; + await context.Utilities.PageEachAsync(dataTable.Rows.Cast().ToList(), Size, async item => + { + resul += await _BulkUpdate(tableName, item.CopyToDataTable(), whereColumns, updateColumns).ConfigureAwait(false); + }).ConfigureAwait(false); + return resul; + } + else + { + return await _BulkUpdate(tableName, dataTable, whereColumns, updateColumns).ConfigureAwait(false); + } + } + #endregion + + #region BulkMerge + public Task BulkMergeAsync(List datas) + { + var updateColumns = entityInfo.Columns.Where(it => !it.IsPrimarykey && !it.IsIdentity && !it.IsOnlyIgnoreUpdate && !it.IsIgnore).Select(it => it.DbColumnName ?? it.PropertyName).ToArray(); + var whereColumns = entityInfo.Columns.Where(it => it.IsPrimarykey).Select(it => it.DbColumnName ?? it.PropertyName).ToArray(); ; + return BulkMergeAsync(datas, whereColumns, updateColumns); + } + public int BulkMerge(List datas) + { + return BulkMergeAsync(datas).GetAwaiter().GetResult(); + } + public int BulkMerge(DataTable dataTable, string[] whereColumns, bool isIdentity) + { + object newValue, fastestMethod; + MethodInfo bulkCopyMethod; + _BulkMerge(dataTable, whereColumns, out newValue, out fastestMethod, out bulkCopyMethod, false, isIdentity); + var result = (int)bulkCopyMethod.Invoke(fastestMethod, new object[] { newValue }); + return result; + } + public Task BulkMergeAsync(DataTable dataTable, string[] whereColumns, bool isIdentity) + { + object newValue, fastestMethod; + MethodInfo bulkCopyMethod; + _BulkMerge(dataTable, whereColumns, out newValue, out fastestMethod, out bulkCopyMethod, true, isIdentity); + var result = (Task)bulkCopyMethod.Invoke(fastestMethod, new object[] { newValue }); + return result; + } + public int BulkMerge(DataTable dataTable, string[] whereColumns, string[] updateColumns, bool isIdentity) + { + return BulkMergeAsync(dataTable, whereColumns, updateColumns, isIdentity).GetAwaiter().GetResult(); + } + public Task BulkMergeAsync(DataTable dataTable, string[] whereColumns, string[] updateColumns, bool isIdentity) + { + object newValue, fastestMethod; + MethodInfo bulkCopyMethod; + _BulkMerge(dataTable, whereColumns, updateColumns, out newValue, out fastestMethod, out bulkCopyMethod, true, isIdentity); + var result = (Task)bulkCopyMethod.Invoke(fastestMethod, new object[] { newValue, whereColumns, updateColumns }); + return result; + } + public Task BulkMergeAsync(List datas, string[] whereColumns) + { + var updateColumns = entityInfo.Columns.Where(it => !it.IsPrimarykey && !it.IsIdentity && !it.IsOnlyIgnoreUpdate && !it.IsIgnore).Select(it => it.DbColumnName ?? it.PropertyName).ToArray(); + return BulkMergeAsync(datas, whereColumns, updateColumns); + } + public int BulkMerge(List datas, string[] whereColumns) + { + return BulkMergeAsync(datas, whereColumns).GetAwaiter().GetResult(); + } + public async Task BulkMergeAsync(List datas, string[] whereColumns, string[] updateColumns) + { + if (Size > 0) + { + int resul = 0; + await context.Utilities.PageEachAsync(datas, Size, async item => + { + resul += await _BulkMerge(item, updateColumns, whereColumns).ConfigureAwait(false); + }).ConfigureAwait(false); + return resul; + } + else + { + return await _BulkMerge(datas, updateColumns, whereColumns).ConfigureAwait(false); + } + } + public int BulkMerge(List datas, string[] whereColumns, string[] updateColumns) + { + return BulkMergeAsync(datas, whereColumns, updateColumns).GetAwaiter().GetResult(); + } + + private async Task _BulkMerge(List datas, string[] updateColumns, string[] whereColumns) + { + try + { + Begin(datas, false, true); + Check.Exception(whereColumns == null || whereColumns.Length == 0, "where columns count=0 or need primary key"); + Check.Exception(whereColumns == null || whereColumns.Length == 0, "where columns count=0 or need primary key"); + var isAuto = this.context.CurrentConnectionConfig.IsAutoCloseConnection; + this.context.CurrentConnectionConfig.IsAutoCloseConnection = false; + var old = this.context.Ado.IsDisableMasterSlaveSeparation; + this.context.Ado.IsDisableMasterSlaveSeparation = true; + DataTable dt = ToDdateTable(datas); + IFastBuilder buider = GetBuider(); + buider.Context = context; + if (buider?.DbFastestProperties?.IsMerge == true) + { + await buider.CreateTempAsync(dt).ConfigureAwait(false); + await buider.ExecuteBulkCopyAsync(dt).ConfigureAwait(false); + } + var result = await buider.Merge(GetTableName(), dt, entityInfo, whereColumns, updateColumns, datas).ConfigureAwait(false); + //var queryTemp = this.context.Queryable().AS(dt.TableName).ToList();//test + //var result = await buider.UpdateByTempAsync(GetTableName(), dt.TableName, updateColumns, whereColumns); + if (buider?.DbFastestProperties?.IsMerge == true && this.context.CurrentConnectionConfig.DbType != DbType.Sqlite) + { + this.context.DbMaintenance.DropTable(dt.TableName); + } + this.context.CurrentConnectionConfig.IsAutoCloseConnection = isAuto; + buider.CloseDb(); + End(datas, false, true); + this.context.Ado.IsDisableMasterSlaveSeparation = old; + return result; + } + catch (Exception) + { + this.context.Close(); + throw; + } + } + #endregion + + #region Core + private void _BulkMerge(DataTable dataTable, string[] whereColumns, out object newValue, out object fastestMethod, out MethodInfo bulkCopyMethod, bool isAsync, bool isIdentity) + { + Check.ExceptionEasy(this.AsName.IsNullOrEmpty(), "need .AS(tablaeName) ", "需要 .AS(tablaeName) 设置表名"); + var className = "BulkMerge_" + isIdentity + this.AsName.GetNonNegativeHashCodeString(); + var builder = this.context.DynamicBuilder().CreateClass(className, new SugarTable() + { + TableName = this.AsName + }); + foreach (DataColumn item in dataTable.Columns) + { + var isPrimaryKey = whereColumns.Any(it => it.EqualCase(item.ColumnName)); + var propertyType = item.DataType; + if (!propertyType.IsClass() && propertyType != typeof(string) && propertyType != typeof(byte[])) + { + propertyType = typeof(Nullable<>).MakeGenericType(UtilMethods.GetUnderType(item.DataType)); + } + builder.CreateProperty(item.ColumnName, propertyType, new SugarColumn() + { + IsPrimaryKey = isPrimaryKey, + IsIdentity = isIdentity && isPrimaryKey, + IsNullable = true, + + }); + } + var dicList = this.context.Utilities.DataTableToDictionaryList(dataTable); + var type = builder.WithCache().BuilderType(); + var value = this.context.DynamicBuilder().CreateObjectByType(type, dicList); + newValue = UtilMethods.ConvertToObjectList(type, value); + fastestMethod = this.context.GetType() + .GetMethod("Fastest") + .MakeGenericMethod(type) + .Invoke(this.context, null); + bulkCopyMethod = fastestMethod.GetType().GetMyMethod(isAsync ? "BulkMergeAsync" : "BulkMerge", 1); + } + private void _BulkMerge(DataTable dataTable, string[] whereColumns, string[] updateColumns, out object newValue, out object fastestMethod, out MethodInfo bulkCopyMethod, bool isAsync, bool isIdentity) + { + Check.ExceptionEasy(this.AsName.IsNullOrEmpty(), "need .AS(tablaeName) ", "需要 .AS(tablaeName) 设置表名"); + var className = "BulkMerge_" + isIdentity + this.AsName.GetNonNegativeHashCodeString(); + var builder = this.context.DynamicBuilder().CreateClass(className, new SugarTable() + { + TableName = this.AsName + }); + foreach (DataColumn item in dataTable.Columns) + { + var isPrimaryKey = whereColumns.Any(it => it.EqualCase(item.ColumnName)); + var propertyType = item.DataType; + if (!propertyType.IsClass() && propertyType != typeof(string) && propertyType != typeof(byte[])) + { + propertyType = typeof(Nullable<>).MakeGenericType(UtilMethods.GetUnderType(item.DataType)); + } + builder.CreateProperty(item.ColumnName, propertyType, new SugarColumn() + { + IsPrimaryKey = isPrimaryKey, + IsIdentity = isIdentity && isPrimaryKey, + IsNullable = true, + + }); + } + var dicList = this.context.Utilities.DataTableToDictionaryList(dataTable); + var type = builder.WithCache().BuilderType(); + var value = this.context.DynamicBuilder().CreateObjectByType(type, dicList); + newValue = UtilMethods.ConvertToObjectList(type, value); + fastestMethod = this.context.GetType() + .GetMethod("Fastest") + .MakeGenericMethod(type) + .Invoke(this.context, null); + bulkCopyMethod = fastestMethod.GetType().GetMyMethod(isAsync ? "BulkMergeAsync" : "BulkMerge", 3, newValue.GetType(), typeof(string[]), typeof(string[])); + } + + private async Task _BulkUpdate(List datas, string[] whereColumns, string[] updateColumns) + { + try + { + Begin(datas, false); + Check.Exception(whereColumns == null || whereColumns.Length == 0, "where columns count=0 or need primary key"); + Check.Exception(updateColumns == null || updateColumns.Length == 0, "set columns count=0"); + var isAuto = this.context.CurrentConnectionConfig.IsAutoCloseConnection; + this.context.CurrentConnectionConfig.IsAutoCloseConnection = false; + var old = this.context.Ado.IsDisableMasterSlaveSeparation; + this.context.Ado.IsDisableMasterSlaveSeparation = true; + DataTable dt = ToDdateTable(datas); + IFastBuilder buider = GetBuider(); + ActionIgnoreColums(whereColumns, updateColumns, dt, buider.IsActionUpdateColumns); + buider.Context = context; + await buider.CreateTempAsync(dt).ConfigureAwait(false); + await buider.ExecuteBulkCopyAsync(dt).ConfigureAwait(false); + //var queryTemp = this.context.Queryable().AS(dt.TableName).ToList();//test + var result = await buider.UpdateByTempAsync(GetTableName(), dt.TableName, updateColumns, whereColumns).ConfigureAwait(false); + if (this.context.CurrentConnectionConfig.DbType != DbType.Sqlite) + { + this.context.DbMaintenance.DropTable(dt.TableName); + } + this.context.CurrentConnectionConfig.IsAutoCloseConnection = isAuto; + buider.CloseDb(); + this.context.Ado.IsDisableMasterSlaveSeparation = old; + End(datas, false); + return result; + } + catch (Exception) + { + this.context.Close(); + throw; + } + } + + private void ActionIgnoreColums(string[] whereColumns, string[] updateColumns, DataTable dt, bool IsActionUpdateColumns) + { + if (entityInfo.Columns.Where(it => it.IsIgnore == false).Count() > whereColumns.Length + updateColumns.Length && IsActionUpdateColumns) + { + var ignoreColums = dt.Columns.Cast() + .Where(it => !whereColumns.Any(y => y.EqualCase(it.ColumnName))) + .Where(it => !updateColumns.Any(y => y.EqualCase(it.ColumnName))).ToList(); + foreach (DataRow item in dt.Rows) + { + foreach (var col in ignoreColums) + { + if (item[col.ColumnName].IsNullOrEmpty()) + { + if (col.DataType == UtilConstants.StringType) + { + item[col.ColumnName] = string.Empty; + if (this.queryable?.SqlBuilder?.SqlParameterKeyWord == ":") + { + item[col.ColumnName] = " "; + } + ; + } + else if (col.DataType == UtilConstants.DateType) + { + item[col.ColumnName] = UtilMethods.GetMinDate(this.context.CurrentConnectionConfig); + } + else + { + item[col.ColumnName] = Activator.CreateInstance(col.DataType); + } + } + } + } + } + } + + private async Task _BulkUpdate(string tableName, DataTable dataTable, string[] whereColumns, string[] updateColumns) + { + var datas = new string[dataTable.Rows.Count].ToList(); + Begin(datas, false); + Check.Exception(whereColumns == null || whereColumns.Length == 0, "where columns count=0 or need primary key"); + Check.Exception(updateColumns == null || updateColumns.Length == 0, "set columns count=0"); + var isAuto = this.context.CurrentConnectionConfig.IsAutoCloseConnection; + this.context.CurrentConnectionConfig.IsAutoCloseConnection = false; + var old = this.context.Ado.IsDisableMasterSlaveSeparation; + this.context.Ado.IsDisableMasterSlaveSeparation = true; + dataTable.TableName = this.queryable.SqlBuilder.GetTranslationTableName(tableName); + DataTable dt = GetCopyWriteDataTableUpdate(dataTable); + IFastBuilder buider = GetBuider(); + if (dt.Columns.Count != dataTable.Columns.Count) + { + ActionIgnoreColums(whereColumns, updateColumns, dt, buider.IsActionUpdateColumns); + } + buider.Context = context; + if (buider.DbFastestProperties == null) + { + buider.DbFastestProperties = new DbFastestProperties(); + } + buider.DbFastestProperties.WhereColumns = whereColumns; + await buider.CreateTempAsync(dt).ConfigureAwait(false); + await buider.ExecuteBulkCopyAsync(dt).ConfigureAwait(false); + //var queryTemp = this.context.Queryable().AS(dt.TableName).ToList();//test + var result = await buider.UpdateByTempAsync(GetTableName(), dt.TableName, updateColumns, whereColumns).ConfigureAwait(false); + if (this.context.CurrentConnectionConfig.DbType != DbType.Sqlite) + { + this.context.DbMaintenance.DropTable(dt.TableName); + } + this.context.CurrentConnectionConfig.IsAutoCloseConnection = isAuto; + buider.CloseDb(); + this.context.Ado.IsDisableMasterSlaveSeparation = old; + End(datas, false); + return result; + } + private async Task _BulkCopy(List datas) + { + Begin(datas, true); + DataTable dt = ToDdateTable(datas); + IFastBuilder buider = GetBuider(); + buider.Context = context; + var result = await buider.ExecuteBulkCopyAsync(dt).ConfigureAwait(false); + End(datas, true); + return result; + } + private async Task _BulkCopy(string tableName, DataTable dataTable) + { + var datas = new string[dataTable.Rows.Count].ToList(); + Begin(datas, true); + DataTable dt = dataTable; + dt.TableName = this.queryable.SqlBuilder.GetTranslationTableName(tableName); + dt = GetCopyWriteDataTable(dt); + IFastBuilder buider = GetBuider(); + buider.Context = context; + var result = await buider.ExecuteBulkCopyAsync(dt).ConfigureAwait(false); + End(datas, true); + return result; + } + #endregion + + #region AOP + private void End(List datas, bool isAdd, bool isMerge = false) + { + var title = isAdd ? "BulkCopy" : "BulkUpdate"; + if (isMerge) + { + title = "BulkMerge"; + } + this.context.Ado.IsEnableLogEvent = isLog; + if (this.context.CurrentConnectionConfig?.AopEvents?.OnLogExecuted != null) + { + this.context.CurrentConnectionConfig?.AopEvents?.OnLogExecuted($"End {title} name:{GetTableName()} ,count: {datas.Count},current time: {DateTime.Now}", Array.Empty()); + } + RemoveCache(); + } + private void Begin(List datas, bool isAdd, bool isMerge = false) + { + var title = isAdd ? "BulkCopy" : "BulkUpdate"; + if (isMerge) + { + title = "BulkMerge"; + } + isLog = this.context.Ado.IsEnableLogEvent; + this.context.Ado.IsEnableLogEvent = false; + if (this.context.CurrentConnectionConfig?.AopEvents?.OnLogExecuting != null) + { + this.context.CurrentConnectionConfig?.AopEvents?.OnLogExecuting($"Begin {title} name:{GetTableName()} ,count: {datas.Count},current time: {DateTime.Now} ", Array.Empty()); + } + var dataEvent = this.context.CurrentConnectionConfig.AopEvents?.DataExecuting; + if (IsDataAop && dataEvent != null) + { + var entity = this.context.EntityMaintenance.GetEntityInfo(typeof(Type)); + foreach (var item in datas) + { + DataAop(item, isAdd + ? + DataFilterType.InsertByObject : + DataFilterType.UpdateByObject + , entity); + } + } + } + private void DataAop(Type item, DataFilterType type, EntityInfo entity) + { + var dataEvent = this.context.CurrentConnectionConfig.AopEvents?.DataExecuting; + if (dataEvent != null && item != null) + { + foreach (var columnInfo in entity.Columns) + { + dataEvent(columnInfo.PropertyInfo.GetValue(item, null), new DataFilterModel() { OperationType = type, EntityValue = item, EntityColumnInfo = columnInfo }); + } + } + } + #endregion + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/FastestProvider/Private.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/FastestProvider/Private.cs new file mode 100644 index 000000000..3f8da4d43 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/FastestProvider/Private.cs @@ -0,0 +1,350 @@ +using System.Data; + +namespace SqlSugar +{ + public partial class FastestProvider : IFastest where T : class, new() + { + private IFastBuilder GetBuider() + { + var className = string.Empty; + switch (this.context.CurrentConnectionConfig.DbType) + { + case DbType.MySql: + var result = new MySqlFastBuilder(); + result.CharacterSet = this.CharacterSet; + return result; + case DbType.SqlServer: + var result2 = new SqlServerFastBuilder(); + result2.DbFastestProperties.IsOffIdentity = this.IsOffIdentity; + return result2; + case DbType.Sqlite: + return new SqliteFastBuilder(this.entityInfo); + case DbType.Oracle: + return new OracleFastBuilder(this.entityInfo); + case DbType.PostgreSQL: + return new PostgreSQLFastBuilder(this.entityInfo); + case DbType.MySqlConnector: + var resultConnector = InstanceFactory.CreateInstance("SqlSugar.MySqlConnector.MySqlFastBuilder"); + resultConnector.CharacterSet = this.CharacterSet; + return resultConnector; + case DbType.Dm: + var result3 = new DmFastBuilder(); + result3.DbFastestProperties.IsOffIdentity = this.IsOffIdentity; + return result3; + case DbType.ClickHouse: + var resultConnectorClickHouse = InstanceFactory.CreateInstance("SqlSugar.ClickHouse.ClickHouseFastBuilder"); + resultConnectorClickHouse.CharacterSet = this.CharacterSet; + return resultConnectorClickHouse; + //case DbType.Kdbndp: + // break; + //case DbType.Oscar: + // break; + case DbType.QuestDB: + return new QuestDBFastBuilder(this.entityInfo); + case DbType.Custom: + className = InstanceFactory.CustomNamespace + "." + InstanceFactory.CustomDbName + "FastBuilder"; + break; + case DbType.GaussDBNative: + className = "SqlSugar.GaussDB.GaussDBFastBuilder"; + break; + default: + className = $"SqlSugar.{this.context.CurrentConnectionConfig.DbType.ToString().Replace("Native", "")}FastBuilder"; + break; + } + var reslut = InstanceFactory.CreateInstance(className); + reslut.CharacterSet = this.CharacterSet; + reslut.FastEntityInfo = this.entityInfo; + return reslut; + } + private DataTable ToDdateTable(List datas) + { + var builder = GetBuider(); + DataTable tempDataTable = ReflectionInoCore.GetInstance().GetOrCreate("BulkCopyAsync" + typeof(T).GetHashCode(), + () => + { + if (AsName == null) + { + return queryable.Where(it => false).Select("*").ToDataTable(); + } + else + { + return queryable.AS(AsName).Where(it => false).Select("*").ToDataTable(); + } + } + ); + var dt = new DataTable(); + List uInt64TypeName = new List(); + foreach (DataColumn item in tempDataTable.Columns) + { + if (item.DataType == typeof(UInt64)) + { + uInt64TypeName.Add(item.ColumnName); + } + if (item.DataType.Name == "ClickHouseDecimal") + { + dt.Columns.Add(item.ColumnName, typeof(decimal)); + } + else + { + dt.Columns.Add(item.ColumnName, item.DataType); + } + } + dt.TableName = GetTableName(); + var columns = entityInfo.Columns; + if (columns.Where(it => !it.IsIgnore).Count() > tempDataTable.Columns.Count) + { + var tempColumns = tempDataTable.Columns.Cast().Select(it => it.ColumnName); + columns = columns.Where(it => tempColumns.Any(s => s.EqualCase(it.DbColumnName))).ToList(); + } + var isMySql = this.context.CurrentConnectionConfig.DbType.IsIn(DbType.MySql, DbType.MySqlConnector); + var isSqliteCore = SugarCompatible.IsFramework == false && this.context.CurrentConnectionConfig.DbType.IsIn(DbType.Sqlite); + foreach (var item in datas) + { + var dr = dt.NewRow(); + foreach (var column in columns) + { + if (column.IsIgnore) + { + continue; + } + var name = column.DbColumnName; + if (name == null) + { + name = column.PropertyName; + } + var value = ValueConverter(column, GetValue(item, column)); + if (column.SqlParameterDbType != null && column.SqlParameterDbType is Type && UtilMethods.HasInterface((Type)column.SqlParameterDbType, typeof(ISugarDataConverter))) + { + var columnInfo = column; + var type = columnInfo.SqlParameterDbType as Type; + var ParameterConverter = type.GetMethod("ParameterConverter").MakeGenericMethod(columnInfo.PropertyInfo.PropertyType); + var obj = Activator.CreateInstance(type); + var p = ParameterConverter.Invoke(obj, new object[] { value, 100 }) as SugarParameter; + value = p.Value; + } + else if (isMySql && column.UnderType == UtilConstants.BoolType) + { + + if (value.ObjToBool() == false && uInt64TypeName.Any(z => z.EqualCase(column.DbColumnName))) + { + value = DBNull.Value; + } + } + else if (isSqliteCore && column.UnderType == UtilConstants.StringType && value is bool) + { + value = "isSqliteCore_" + value.ObjToString(); + } + else if (isSqliteCore && column.UnderType == UtilConstants.BoolType && value is bool) + { + value = Convert.ToBoolean(value) ? 1 : 0; + } + else if (column.UnderType == UtilConstants.DateTimeOffsetType && value != null && value != DBNull.Value) + { + if (builder.DbFastestProperties?.HasOffsetTime == true) + { + //Don't need to deal with + } + else + { + value = UtilMethods.ConvertFromDateTimeOffset((DateTimeOffset)value); + } + } + else if (value != DBNull.Value && value != null && column.UnderType?.FullName == "System.TimeOnly") + { + value = UtilMethods.TimeOnlyToTimeSpan(value); + } + else if (value != DBNull.Value && value != null && column.UnderType?.FullName == "System.DateOnly") + { + value = UtilMethods.DateOnlyToDateTime(value); + } + dr[name] = value; + } + dt.Rows.Add(dr); + } + + return dt; + } + private static object GetValue(T item, EntityColumnInfo column) + { + if (StaticConfig.EnableAot) + { + return column.PropertyInfo.GetValue(item); + } + else + { + return PropertyCallAdapterProvider.GetInstance(column.PropertyName).InvokeGet(item); + } + } + + private string GetTableName() + { + if (this.AsName.HasValue()) + { + return queryable.SqlBuilder.GetTranslationTableName(AsName); + } + else + { + return queryable.SqlBuilder.GetTranslationTableName(this.context.EntityMaintenance.GetTableName()); + } + } + private object ValueConverter(EntityColumnInfo columnInfo, object value) + { + if (value == null) + return DBNull.Value; + if (value is DateTime && (DateTime)value == DateTime.MinValue) + { + value = Convert.ToDateTime("1900-01-01"); + } + //else if (columnInfo.IsJson) + //{ + // columnInfo.IsJson = true; + //} + //else if (columnInfo.IsArray) + //{ + // columnInfo.IsArray = true; + //} + else if (columnInfo.UnderType.IsEnum()) + { + value = Convert.ToInt64(value); + } + else if (columnInfo.IsJson && value != null) + { + value = this.context.Utilities.SerializeObject(value); + } + else if (columnInfo.IsTranscoding && value.HasValue()) + { + value = UtilMethods.EncodeBase64(value.ToString()); + } + return value; + } + private DataTable GetCopyWriteDataTable(DataTable dt) + { + var builder = GetBuider(); + if (builder.DbFastestProperties?.IsConvertDateTimeOffsetToDateTime == true) + { + dt = UtilMethods.ConvertDateTimeOffsetToDateTime(dt); + } + if (builder.DbFastestProperties?.IsNoCopyDataTable == true) + { + return dt; + } + DataTable tempDataTable = null; + if (AsName == null) + { + tempDataTable = queryable.Clone().Where(it => false).Select("*").ToDataTable(); + } + else + { + tempDataTable = queryable.Clone().AS(AsName).Where(it => false).Select("*").ToDataTable(); + } + ; + List uInt64TypeName = new List(); + foreach (DataColumn item in tempDataTable.Columns) + { + if (item.DataType == typeof(UInt64)) + { + uInt64TypeName.Add(item.ColumnName); + } + } + var temColumnsList = tempDataTable.Columns.Cast().Select(it => it.ColumnName.ToLower()).ToList(); + var columns = dt.Columns.Cast().Where(it => temColumnsList.Contains(it.ColumnName.ToLower())).ToList(); + foreach (DataRow item in dt.Rows) + { + DataRow dr = tempDataTable.NewRow(); + foreach (DataColumn column in columns) + { + + dr[column.ColumnName] = item[column.ColumnName]; + if (dr[column.ColumnName] == null || dr[column.ColumnName] == DBNull.Value) + { + dr[column.ColumnName] = DBNull.Value; + } + else if (column.DataType == UtilConstants.BoolType && this.context.CurrentConnectionConfig.DbType.IsIn(DbType.MySql, DbType.MySqlConnector)) + { + if (Convert.ToBoolean(dr[column.ColumnName]) == false && uInt64TypeName.Any(z => z.EqualCase(column.ColumnName))) + { + dr[column.ColumnName] = DBNull.Value; + } + } + } + tempDataTable.Rows.Add(dr); + } + tempDataTable.TableName = dt.TableName; + return tempDataTable; + } + private DataTable GetCopyWriteDataTableUpdate(DataTable dt) + { + var sqlBuilder = this.context.Queryable().SqlBuilder; + var dts = dt.Columns.Cast().Select(it => sqlBuilder.GetTranslationColumnName(it.ColumnName)).ToList(); + DataTable tempDataTable = null; + if (AsName == null) + { + tempDataTable = queryable.Clone().Where(it => false).Select(string.Join(",", dts)).ToDataTable(); + } + else + { + tempDataTable = queryable.Clone().AS(AsName).Where(it => false).Select(string.Join(",", dts)).ToDataTable(); + } + ; + List uInt64TypeName = new List(); + foreach (DataColumn item in tempDataTable.Columns) + { + if (item.DataType == typeof(UInt64)) + { + uInt64TypeName.Add(item.ColumnName); + } + } + var temColumnsList = tempDataTable.Columns.Cast().Select(it => it.ColumnName.ToLower()).ToList(); + var columns = dt.Columns.Cast().Where(it => temColumnsList.Contains(it.ColumnName.ToLower())).ToList(); + foreach (DataRow item in dt.Rows) + { + DataRow dr = tempDataTable.NewRow(); + foreach (DataColumn column in columns) + { + + dr[column.ColumnName] = item[column.ColumnName]; + if (dr[column.ColumnName] == null || dr[column.ColumnName] == DBNull.Value) + { + dr[column.ColumnName] = DBNull.Value; + } + else if (column.DataType == UtilConstants.BoolType && this.context.CurrentConnectionConfig.DbType.IsIn(DbType.MySql, DbType.MySqlConnector)) + { + if (Convert.ToBoolean(dr[column.ColumnName]) == false && uInt64TypeName.Any(z => z.EqualCase(column.ColumnName))) + { + dr[column.ColumnName] = DBNull.Value; + } + } + } + tempDataTable.Rows.Add(dr); + } + tempDataTable.TableName = dt.TableName; + return tempDataTable; + } + + private void RemoveCache() + { + if (!string.IsNullOrEmpty(CacheKey) || !string.IsNullOrEmpty(CacheKeyLike)) + { + Check.Exception(this.context.CurrentConnectionConfig.ConfigureExternalServices?.DataInfoCacheService == null, "ConnectionConfig.ConfigureExternalServices.DataInfoCacheService is null"); + var service = this.context.CurrentConnectionConfig.ConfigureExternalServices?.DataInfoCacheService; + if (!string.IsNullOrEmpty(CacheKey)) + { + CacheSchemeMain.RemoveCache(service, CacheKey); + } + if (!string.IsNullOrEmpty(CacheKeyLike)) + { + CacheSchemeMain.RemoveCacheByLike(service, CacheKeyLike); + } + } + if (this.context.CurrentConnectionConfig?.MoreSettings?.IsAutoRemoveDataCache == true) + { + var cacheService = this.context.CurrentConnectionConfig?.ConfigureExternalServices?.DataInfoCacheService; + if (cacheService != null) + { + CacheSchemeMain.RemoveCache(cacheService, this.context.EntityMaintenance.GetTableName()); + } + } + } + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/FastestProvider/Setting.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/FastestProvider/Setting.cs new file mode 100644 index 000000000..a64f363a5 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/FastestProvider/Setting.cs @@ -0,0 +1,54 @@ +namespace SqlSugar +{ + public partial class FastestProvider : IFastest where T : class, new() + { + private string AsName { get; set; } + private int Size { get; set; } + private string CacheKey { get; set; } + private string CacheKeyLike { get; set; } + private string CharacterSet { get; set; } + private bool IsDataAop { get; set; } + private bool IsOffIdentity { get; set; } + public IFastest SetCharacterSet(string CharacterSet) + { + this.CharacterSet = CharacterSet; + return this; + } + public IFastest EnableDataAop() + { + this.IsDataAop = true; + return this; + } + public IFastest RemoveDataCache() + { + CacheKey = typeof(T).FullName; + return this; + } + public IFastest RemoveDataCache(string cacheKey) + { + CacheKeyLike = this.context.EntityMaintenance.GetTableName(); + return this; + } + public IFastest AS(string tableName) + { + this.AsName = tableName; + return this; + } + public IFastest PageSize(int size) + { + this.Size = size; + return this; + } + public IFastest OffIdentity() + { + this.IsOffIdentity = true; + return this; + } + public SplitFastest SplitTable() + { + SplitFastest result = new SplitFastest(); + result.FastestProvider = this; + return result; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/FastestProvider/SplitFastest.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/FastestProvider/SplitFastest.cs new file mode 100644 index 000000000..60a54f47d --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/FastestProvider/SplitFastest.cs @@ -0,0 +1,141 @@ +using System.Reflection; + +namespace SqlSugar +{ + public class SplitFastest where T : class, new() + { + public FastestProvider FastestProvider { get; set; } + public SqlSugarProvider Context { get { return this.FastestProvider.context; } } + public EntityInfo EntityInfo { get { return this.Context.EntityMaintenance.GetEntityInfo(); } } + public int BulkCopy(List datas) + { + if (StaticConfig.SplitTableCreateTableFunc != null) + { + StaticConfig.SplitTableCreateTableFunc(typeof(T), datas?.Cast()?.ToArray()); + } + List groupModels; + int result; + GroupDataList(datas, out groupModels, out result); + foreach (var item in groupModels.GroupBy(it => it.GroupName)) + { + CreateTable(item.Key); + var addList = item.Select(it => it.Item).ToList(); + result += FastestProvider.AS(item.Key).BulkCopy(addList); + this.Context.MappingTables.Add(EntityInfo.EntityName, EntityInfo.DbTableName); + } + return result; + } + public async Task BulkCopyAsync(List datas) + { + if (StaticConfig.SplitTableCreateTableFunc != null) + { + StaticConfig.SplitTableCreateTableFunc(typeof(T), datas?.Cast()?.ToArray()); + } + List groupModels; + int result; + GroupDataList(datas, out groupModels, out result); + foreach (var item in groupModels.GroupBy(it => it.GroupName)) + { + CreateTable(item.Key); + var addList = item.Select(it => it.Item).ToList(); + result += await FastestProvider.AS(item.Key).BulkCopyAsync(addList).ConfigureAwait(false); + this.Context.MappingTables.Add(EntityInfo.EntityName, EntityInfo.DbTableName); + } + return result; + } + + + public int BulkUpdate(List datas) + { + List groupModels; + int result; + GroupDataList(datas, out groupModels, out result); + foreach (var item in groupModels.GroupBy(it => it.GroupName)) + { + CreateTable(item.Key); + var addList = item.Select(it => it.Item).ToList(); + result += FastestProvider.AS(item.Key).BulkUpdate(addList); + this.Context.MappingTables.Add(EntityInfo.EntityName, EntityInfo.DbTableName); + } + return result; + } + public async Task BulkUpdateAsync(List datas) + { + List groupModels; + int result; + GroupDataList(datas, out groupModels, out result); + foreach (var item in groupModels.GroupBy(it => it.GroupName)) + { + CreateTable(item.Key); + var addList = item.Select(it => it.Item).ToList(); + result += await FastestProvider.AS(item.Key).BulkUpdateAsync(addList).ConfigureAwait(false); + this.Context.MappingTables.Add(EntityInfo.EntityName, EntityInfo.DbTableName); + } + return result; + } + + + public int BulkUpdate(List datas, string[] wherColumns, string[] updateColumns) + { + List groupModels; + int result; + GroupDataList(datas, out groupModels, out result); + foreach (var item in groupModels.GroupBy(it => it.GroupName)) + { + var addList = item.Select(it => it.Item).ToList(); + result += FastestProvider.AS(item.Key).BulkUpdate(addList, wherColumns, updateColumns); ; + } + return result; + } + public async Task BulkUpdateAsync(List datas, string[] wherColumns, string[] updateColumns) + { + List groupModels; + int result; + GroupDataList(datas, out groupModels, out result); + foreach (var item in groupModels.GroupBy(it => it.GroupName)) + { + var addList = item.Select(it => it.Item).ToList(); + result += await FastestProvider.AS(item.Key).BulkUpdateAsync(addList, wherColumns, updateColumns).ConfigureAwait(false); ; + } + return result; + } + private void CreateTable(string tableName) + { + var isLog = this.Context.Ado.IsEnableLogEvent; + this.Context.Ado.IsEnableLogEvent = false; + if (!this.Context.DbMaintenance.IsAnyTable(tableName, false)) + { + this.Context.MappingTables.Add(EntityInfo.EntityName, tableName); + this.Context.CodeFirst.InitTables(); + } + this.Context.Ado.IsEnableLogEvent = isLog; + } + + private void GroupDataList(List datas, out List groupModels, out int result) + { + var attribute = typeof(T).GetCustomAttribute() as SplitTableAttribute; + Check.Exception(attribute == null, $"{typeof(T).Name} need SplitTableAttribute"); + groupModels = new List(); + var db = FastestProvider.context; + var hasSplitField = typeof(T).GetProperties().Any(it => it.GetCustomAttribute() != null); + foreach (var item in datas) + { + if (groupModels.Count > 0 && !hasSplitField) + groupModels.Add(new GroupModel() { GroupName = groupModels[0].GroupName, Item = item }); + else + { + var value = db.SplitHelper().GetValue(attribute.SplitType, item); + var tableName = db.SplitHelper().GetTableName(attribute.SplitType, value); + groupModels.Add(new GroupModel() { GroupName = tableName, Item = item }); + } + } + result = 0; + } + internal class GroupModel + { + public string GroupName { get; set; } + public T Item { get; set; } + } + } + +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/FilterProvider/FilterProvider.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/FilterProvider/FilterProvider.cs new file mode 100644 index 000000000..48dd89c70 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/FilterProvider/FilterProvider.cs @@ -0,0 +1,146 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public class QueryFilterProvider : IFilter + { + internal SqlSugarProvider Context { get; set; } + private List _Filters { get; set; } + private List _BackUpFilters { get; set; } + public bool Any() + { + return _Filters != null && _Filters.Count != 0; + } + public IFilter Add(SqlFilterItem filter) + { + if (_Filters == null) + _Filters = new List(); + //if (this.Context.CurrentConnectionConfig.IsShardSameThread) + //{ + // if (!_Filters.Select(it => it.FilterValue(this.Context).Sql).Contains(filter.FilterValue(this.Context).Sql)) + // _Filters.Add(filter); + //} + //else + //{ + _Filters.Add(filter); + //} + return this; + } + + public void Remove(string filterName) + { + if (_Filters == null) + _Filters = new List(); + _Filters.RemoveAll(it => it.FilterName == filterName); + } + + public List GetFilterList + { + get + { + if (_Filters == null) + _Filters = new List(); + return _Filters; + } + } + public void Clear() + { + _Filters = new List(); + } + public void Clear() + { + _Filters = _Filters.Where(it => !(it is TableFilterItem)).ToList(); + } + public void Clear(params Type[] types) + { + _Filters = _Filters.Where(it => !types.Contains(it.type)).ToList(); + } + public void Clear() + { + _Filters = _Filters.Where(it => !(it is TableFilterItem) && !(it is TableFilterItem)).ToList(); + } + public void Clear() + { + _Filters = _Filters.Where(it => !(it is TableFilterItem) && !(it is TableFilterItem) && !(it is TableFilterItem)).ToList(); + } + public void ClearAndBackup() + { + _BackUpFilters = _Filters; + _Filters = new List(); + } + + public void ClearAndBackup() + { + _BackUpFilters = _Filters; + _Filters = _BackUpFilters.Where(it => !(it is TableFilterItem)).ToList(); + } + + public void ClearAndBackup() + { + _BackUpFilters = _Filters; + _Filters = _BackUpFilters.Where(it => !(it is TableFilterItem) && !(it is TableFilterItem)).ToList(); + } + + public void ClearAndBackup() + { + _BackUpFilters = _Filters; + _Filters = _BackUpFilters.Where(it => !(it is TableFilterItem) && !(it is TableFilterItem) && !(it is TableFilterItem)).ToList(); + } + + public void ClearAndBackup(params Type[] types) + { + _BackUpFilters = _Filters; + _Filters = _BackUpFilters.Where(it => !types.Contains(it.type)).ToList(); + } + + public void Restore() + { + _Filters = _BackUpFilters; + if (_Filters == null) + { + _Filters = new List(); + } + } + + public QueryFilterProvider AddTableFilter(Expression> expression, FilterJoinPosition filterJoinType = FilterJoinPosition.On) + { + var isOn = filterJoinType == FilterJoinPosition.On; + var tableFilter = new TableFilterItem(expression, isOn); + this.Add(tableFilter); + return this; + } + public QueryFilterProvider AddTableFilterIF(bool isAppendFilter, Expression> expression, FilterJoinPosition filterJoinType = FilterJoinPosition.On) + { + if (isAppendFilter) + { + AddTableFilter(expression, filterJoinType); + } + return this; + } + public QueryFilterProvider AddTableFilter(Type type, string shortName, FormattableString expString, FilterJoinPosition filterJoinType = FilterJoinPosition.On) + { + var exp = DynamicCoreHelper.GetWhere(type, shortName, expString); + return AddTableFilter(type, exp, filterJoinType); + } + public QueryFilterProvider AddTableFilter(Type type, Expression expression, FilterJoinPosition filterJoinType = FilterJoinPosition.On) + { + var isOn = filterJoinType == FilterJoinPosition.On; + this.Add(new TableFilterItem(type, expression, isOn)); + return this; + } + + public QueryFilterProvider AddTableFilterIF(bool isAppendFilter, Type type, Expression expression, FilterJoinPosition posType = FilterJoinPosition.On) + { + if (isAppendFilter) + { + AddTableFilter(type, expression, posType); + } + return this; + } + public enum FilterJoinPosition + { + On = 0, + Where = 1 + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/GridSave/GridSaveProvider.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/GridSave/GridSaveProvider.cs new file mode 100644 index 000000000..a314f5973 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/GridSave/GridSaveProvider.cs @@ -0,0 +1,90 @@ +namespace SqlSugar +{ + public class GridSaveProvider where T : class, new() + { + internal SqlSugarProvider Context { get; set; } + internal List OldList { get; set; } + internal List SaveList { get; set; } + internal bool IsIncluesFirstAll { get; set; } + internal string[] IgnoreColumnsSaveInclues { get; set; } + public bool ExecuteCommand() + { + var deleteList = GetDeleteList(); + this.Context.Deleteable(deleteList).PageSize(1000).ExecuteCommand(); + if (IsIncludesSave()) + { + this.Context.Utilities.PageEach(SaveList, 1000, pageList => + { + var options = new UpdateNavRootOptions() { IsInsertRoot = true }; + this.Context.UpdateNav(pageList, options) + .IncludesAllFirstLayer(IgnoreColumnsSaveInclues).ExecuteCommand(); + }); + } + else + { + this.Context.Storageable(SaveList).PageSize(1000).ExecuteCommand(); + } + return true; + } + + public async Task ExecuteCommandAsync() + { + var deleteList = GetDeleteList(); + await Context.Deleteable(deleteList).PageSize(1000).ExecuteCommandAsync().ConfigureAwait(false); + if (IsIncludesSave()) + { + await Context.Utilities.PageEachAsync(SaveList, 1000, async pageList => + { + var options = new UpdateNavRootOptions() { IsInsertRoot = true }; + await Context.UpdateNav(pageList, options) + .IncludesAllFirstLayer(IgnoreColumnsSaveInclues).ExecuteCommandAsync().ConfigureAwait(false); + }).ConfigureAwait(false); + } + else + { + await Context.Storageable(SaveList).PageSize(1000).ExecuteCommandAsync().ConfigureAwait(false); + } + return true; + } + + public List GetDeleteList() + { + //下面代码ToDictionary会有重复错请修改 + string[] primaryKeys = this.Context.EntityMaintenance.GetEntityInfo().Columns.Where(it => it.IsPrimarykey).Select(it => it.PropertyName).ToArray(); + var saveListDictionary = this.SaveList.Select(item => + new { Key = CreateCompositeKey(primaryKeys, item), Value = item }); + var deleteList = this.OldList.Where(oldItem => + { + var compositeKey = CreateCompositeKey(primaryKeys, oldItem); + return !saveListDictionary.Any(it => it.Key == compositeKey); + }).ToList(); + return deleteList; + } + + public GridSaveProvider IncludesAllFirstLayer(params string[] ignoreColumns) + { + this.IsIncluesFirstAll = true; + IgnoreColumnsSaveInclues = ignoreColumns; + return this; + } + + + private bool IsIncludesSave() + { + return IsIncluesFirstAll && this.Context.EntityMaintenance.GetEntityInfo().Columns.Any(it => it.Navigat != null); + } + + + private string CreateCompositeKey(string[] propertyNames, object obj) + { + var keyValues = propertyNames.Select(propertyName => GetPropertyValue(obj, propertyName)?.ToString() ?? ""); + return string.Join("|", keyValues); + } + + private object GetPropertyValue(object obj, string propertyName) + { + var property = obj.GetType().GetProperty(propertyName); + return property != null ? property.GetValue(obj) : null; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/InsertableProvider/InsertMethodInfo.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/InsertableProvider/InsertMethodInfo.cs new file mode 100644 index 000000000..ee5dbcc69 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/InsertableProvider/InsertMethodInfo.cs @@ -0,0 +1,125 @@ +using System.Reflection; + +namespace SqlSugar +{ + public class InsertMethodInfo + { + internal SqlSugarProvider Context { get; set; } + internal MethodInfo MethodInfo { get; set; } + internal object objectValue { get; set; } + internal int pageSize { get; set; } + public int ExecuteCommand() + { + if (Context == null) return 0; + var inertable = MethodInfo.Invoke(Context, new object[] { objectValue }); + inertable = GetPageInsertable(inertable); + var result = inertable.GetType().GetMethod("ExecuteCommand").Invoke(inertable, Array.Empty()); + return (int)result; + } + + public InsertMethodInfo PageSize(int pageSize) + { + this.pageSize = pageSize; + return this; + } + public async Task ExecuteCommandAsync() + { + if (Context == null) return 0; + var inertable = MethodInfo.Invoke(Context, new object[] { objectValue }); + inertable = GetPageInsertable(inertable); + var result = inertable.GetType().GetMyMethod("ExecuteCommandAsync", 0).Invoke(inertable, Array.Empty()); + return await ((Task)result).ConfigureAwait(false); + } + public int ExecuteReturnIdentity() + { + if (Context == null) return 0; + var inertable = MethodInfo.Invoke(Context, new object[] { objectValue }); + var result = inertable.GetType().GetMethod("ExecuteReturnIdentity").Invoke(inertable, Array.Empty()); + return (int)result; + } + public async Task ExecuteReturnIdentityAsync() + { + if (Context == null) return 0; + var inertable = MethodInfo.Invoke(Context, new object[] { objectValue }); + var result = inertable.GetType().GetMyMethod("ExecuteReturnIdentityAsync", 0).Invoke(inertable, Array.Empty()); + return await ((Task)result).ConfigureAwait(false); + } + + public CommonMethodInfo AS(string tableName) + { + var inertable = MethodInfo.Invoke(Context, new object[] { objectValue }); + var newMethod = inertable.GetType().GetMyMethod("AS", 1, typeof(string)); + var result = newMethod.Invoke(inertable, new object[] { tableName }); + return new CommonMethodInfo() + { + Context = result + }; + } + public CommonMethodInfo EnableDiffLogEvent(object businessData = null) + { + var inertable = MethodInfo.Invoke(Context, new object[] { objectValue }); + var newMethod = inertable.GetType().GetMyMethod("EnableDiffLogEvent", 1, typeof(object)); + var result = newMethod.Invoke(inertable, new object[] { businessData }); + return new CommonMethodInfo() + { + Context = result + }; + } + public CommonMethodInfo IgnoreColumns(params string[] ignoreColumns) + { + var inertable = MethodInfo.Invoke(Context, new object[] { objectValue }); + var newMethod = inertable.GetType().GetMyMethod("IgnoreColumns", 1, typeof(string[])); + var result = newMethod.Invoke(inertable, new object[] { ignoreColumns }); + return new CommonMethodInfo() + { + Context = result + }; + } + public CommonMethodInfo IgnoreColumns(bool ignoreNullColumn) + { + var inertable = MethodInfo.Invoke(Context, new object[] { objectValue }); + var newMethod = inertable.GetType().GetMyMethod("IgnoreColumns", 2, typeof(bool), typeof(bool)); + var result = newMethod.Invoke(inertable, new object[] { ignoreNullColumn, true }); + return new CommonMethodInfo() + { + Context = result + }; + } + + public SplitMethodInfo SplitTable() + { + var inertable = MethodInfo.Invoke(Context, new object[] { objectValue }); + var newMethod = inertable.GetType().GetMyMethod("SplitTable", 0); + var result = newMethod.Invoke(inertable, Array.Empty()); + return new SplitMethodInfo() + { + Context = result + }; + } + + public long ExecuteReturnSnowflakeId() + { + if (Context == null) return 0; + var inertable = MethodInfo.Invoke(Context, new object[] { objectValue }); + var result = inertable.GetType().GetMethod("ExecuteReturnSnowflakeId").Invoke(inertable, Array.Empty()); + return (long)result; + } + public async Task ExecuteReturnSnowflakeIdAsync() + { + if (Context == null) return 0; + var inertable = MethodInfo.Invoke(Context, new object[] { objectValue }); + var result = inertable.GetType().GetMyMethod("ExecuteReturnSnowflakeIdAsync", 0).Invoke(inertable, Array.Empty()); + return await ((Task)result).ConfigureAwait(false); + } + + private object GetPageInsertable(object inertable) + { + if (pageSize > 0) + { + inertable = inertable.GetType().GetMethod("PageSize").Invoke(inertable, new object[] { pageSize }); + } + return inertable; + } + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/InsertableProvider/InsertNavMethodInfo.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/InsertableProvider/InsertNavMethodInfo.cs new file mode 100644 index 000000000..ba45351d8 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/InsertableProvider/InsertNavMethodInfo.cs @@ -0,0 +1,49 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public class InsertNavMethodInfo + { + internal object MethodInfos { get; set; } + internal SqlSugarProvider Context { get; set; } + + public InsertNavMethodInfo IncludeByNameString(string navMemberName, InsertNavOptions insertNavOptions = null) + { + var type = MethodInfos.GetType().GetGenericArguments()[0]; + var entityInfo = this.Context.EntityMaintenance.GetEntityInfo(type); + Type properyItemType; + bool isList; + Expression exp = UtilMethods.GetIncludeExpression(navMemberName, entityInfo, out properyItemType, out isList); + var method = this.MethodInfos.GetType().GetMyMethod("Include", 2, isList) + .MakeGenericMethod(properyItemType); + var obj = method.Invoke(this.MethodInfos, new object[] { exp, insertNavOptions }); + this.MethodInfos = obj; + return this; + } + public InsertNavMethodInfo ThenIncludeByNameString(string navMemberName, InsertNavOptions insertNavOptions = null) + { + var type = MethodInfos.GetType().GetGenericArguments()[1]; + var entityInfo = this.Context.EntityMaintenance.GetEntityInfo(type); + Type properyItemType; + bool isList; + Expression exp = UtilMethods.GetIncludeExpression(navMemberName, entityInfo, out properyItemType, out isList); + var method = this.MethodInfos.GetType().GetMyMethod("ThenInclude", 2, isList) + .MakeGenericMethod(properyItemType); + var obj = method.Invoke(this.MethodInfos, new object[] { exp, insertNavOptions }); + this.MethodInfos = obj; + return this; + } + public async Task ExecuteCommandAsync() + { + if (Context == null) return false; + var result = MethodInfos.GetType().GetMethod("ExecuteCommandAsync").Invoke(MethodInfos, Array.Empty()); + return await ((Task)result).ConfigureAwait(false); + } + public bool ExecuteCommand() + { + if (Context == null) return false; + var result = MethodInfos.GetType().GetMethod("ExecuteCommand").Invoke(MethodInfos, Array.Empty()); + return (bool)result; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/InsertableProvider/InsertableHelper.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/InsertableProvider/InsertableHelper.cs new file mode 100644 index 000000000..a2b3a237f --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/InsertableProvider/InsertableHelper.cs @@ -0,0 +1,744 @@ +using System.Data; +using System.Linq.Expressions; +using System.Text.RegularExpressions; + +namespace SqlSugar +{ + public partial class InsertableProvider : IInsertable where T : class, new() + { + #region Protected Methods + private string _ExecuteReturnBigIdentity() + { + InsertBuilder.IsReturnIdentity = true; + PreToSql(); + AutoRemoveDataCache(); + string sql = InsertBuilder.ToSqlString(); + RestoreMapping(); + Before(sql); + return sql; + } + private string _ExecuteReturnIdentity() + { + InsertBuilder.IsReturnIdentity = true; + PreToSql(); + AutoRemoveDataCache(); + string sql = InsertBuilder.ToSqlString(); + RestoreMapping(); + Before(sql); + return sql; + } + + private string _ExecuteCommand() + { + if (InsertBuilder.DbColumnInfoList.HasValue()) + { + var pks = GetPrimaryKeys(); + foreach (var item in InsertBuilder.DbColumnInfoList) + { + var isPk = pks.Any(y => y.Equals(item.DbColumnName, StringComparison.CurrentCultureIgnoreCase)) || item.IsPrimarykey; + if (isPk && item.PropertyType == UtilConstants.GuidType && item.Value is Guid guid && guid == Guid.Empty) + { + if (StaticConfig.CustomGuidFunc != null) + { + item.Value = StaticConfig.CustomGuidFunc(); + } + else if (StaticConfig.CustomGuidByValueFunc != null && item.Value is Guid guidValue) + { + item.Value = StaticConfig.CustomGuidByValueFunc(guidValue); + } + else + { + item.Value = Guid.NewGuid(); + } + if (InsertObjs.First().GetType().GetProperties().Any(it => it.Name == item.PropertyName)) + InsertObjs.First().GetType().GetProperties().First(it => it.Name == item.PropertyName).SetValue(InsertObjs.First(), item.Value, null); + } + } + } + InsertBuilder.IsReturnIdentity = false; + PreToSql(); + AutoRemoveDataCache(); + string sql = InsertBuilder.ToSqlString(); + RestoreMapping(); + Before(sql); + return sql; + } + protected void AutoRemoveDataCache() + { + var moreSetts = this.Context.CurrentConnectionConfig.MoreSettings; + var extService = this.Context.CurrentConnectionConfig.ConfigureExternalServices; + if (moreSetts?.IsAutoRemoveDataCache == true && extService?.DataInfoCacheService != null) + { + this.RemoveDataCache(); + } + } + protected virtual void PreToSql() + { + #region Identities + if (!IsOffIdentity) + { + List identities = GetIdentityKeys(); + if (identities != null && identities.Count != 0) + { + this.InsertBuilder.DbColumnInfoList = this.InsertBuilder.DbColumnInfoList.Where(it => + { + return !identities.Any(i => it.DbColumnName.Equals(i, StringComparison.CurrentCultureIgnoreCase)); + }).ToList(); + } + } + #endregion + + #region IgnoreColumns + if (this.Context.IgnoreColumns?.Any() == true) + { + var currentIgnoreColumns = this.Context.IgnoreColumns.Where(it => it.EntityName == this.EntityInfo.EntityName).ToList(); + this.InsertBuilder.DbColumnInfoList = this.InsertBuilder.DbColumnInfoList.Where(it => + { + return !currentIgnoreColumns.Any(i => it.PropertyName.Equals(i.PropertyName, StringComparison.CurrentCulture)); + }).ToList(); + } + + if (this.Context.IgnoreInsertColumns?.Any() == true) + { + var currentIgnoreColumns = this.Context.IgnoreInsertColumns.Where(it => it.EntityName == this.EntityInfo.EntityName).ToList(); + this.InsertBuilder.DbColumnInfoList = this.InsertBuilder.DbColumnInfoList.Where(it => + { + return !currentIgnoreColumns.Any(i => it.PropertyName.Equals(i.PropertyName, StringComparison.CurrentCulture)); + }).ToList(); + } + #endregion + if (this.IsSingle) + { + var isDic = this.EntityInfo.DbTableName.StartsWith("Dictionary`"); + foreach (var item in this.InsertBuilder.DbColumnInfoList) + { + if (this.InsertBuilder.Parameters == null) this.InsertBuilder.Parameters = new List(); + var paramters = new SugarParameter(this.SqlBuilder.SqlParameterKeyWord + item.DbColumnName, item.Value, item.PropertyType); + if (InsertBuilder.IsNoInsertNull && paramters.Value == null) + { + continue; + } + if (item.SqlParameterDbType is Type) + { + continue; + } + if (item.IsJson) + { + paramters.IsJson = true; + SqlBuilder.ChangeJsonType(paramters); + } + if (item.IsArray) + { + paramters.IsArray = true; + if (item.Value == null || item.Value == DBNull.Value) + { + ArrayNull(item, paramters); + } + + } + if (item.Value == null && isDic) + { + var type = this.SqlBuilder.GetNullType(this.InsertBuilder.GetTableNameString, item.DbColumnName); + if (type != null) + { + paramters = new SugarParameter(this.SqlBuilder.SqlParameterKeyWord + item.DbColumnName, item.Value, type); + + } + } + this.InsertBuilder.Parameters.Add(paramters); + } + } + } + + private static void ArrayNull(DbColumnInfo item, SugarParameter parameter) + { + if (item.PropertyType.IsIn(typeof(Guid[]), typeof(Guid?[]))) + { + parameter.DbType = System.Data.DbType.Guid; + } + else if (item.PropertyType.IsIn(typeof(int[]), typeof(int?[]))) + { + parameter.DbType = System.Data.DbType.Int32; + } + else if (item.PropertyType.IsIn(typeof(long[]), typeof(long?[]))) + { + parameter.DbType = System.Data.DbType.Int64; + } + else if (item.PropertyType.IsIn(typeof(short[]), typeof(short?[]))) + { + parameter.DbType = System.Data.DbType.Int16; + } + } + internal void Init() + { + InsertBuilder.EntityInfo = this.EntityInfo; + Check.Exception(InsertObjs == null || InsertObjs.Length == 0, "InsertObjs is null"); + int i = 0; + foreach (var item in InsertObjs) + { + List insertItem = new List(); + if (item is Dictionary) + { + Check.ExceptionEasy("To use Insertable dictionary, use string or object", "Insertable字典请使用string,object类型"); + } + if (item is Dictionary) + { + SetInsertItemByDic(i, item, insertItem); + } + else + { + DataAop(item); + SetInsertItemByEntity(i, item, insertItem); + } + this.InsertBuilder.DbColumnInfoList.AddRange(insertItem); + ++i; + } + } + + private void DataAop(T item) + { + var dataEvent = this.Context.CurrentConnectionConfig.AopEvents?.DataExecuting; + if (dataEvent != null && item != null) + { + foreach (var columnInfo in this.EntityInfo.Columns) + { + if (columnInfo.ForOwnsOnePropertyInfo != null) + { + var data = columnInfo.ForOwnsOnePropertyInfo.GetValue(item, null); + if (data != null) + { + dataEvent(columnInfo.PropertyInfo.GetValue(data, null), new DataFilterModel() { OperationType = DataFilterType.InsertByObject, EntityValue = item, EntityColumnInfo = columnInfo }); + } + } + else if (columnInfo.PropertyInfo.Name == "Item" && columnInfo.IsIgnore) + { + //class index + } + else + { + dataEvent(columnInfo.PropertyInfo.GetValue(item, null), new DataFilterModel() { OperationType = DataFilterType.InsertByObject, EntityValue = item, EntityColumnInfo = columnInfo }); + } + } + } + } + + private void DataChangeAop(T[] items) + { + + var dataEvent = this.Context.CurrentConnectionConfig.AopEvents?.DataChangesExecuted; + if (dataEvent != null) + { + foreach (var item in items) + { + if (item != null && !(item is Dictionary)) + { + foreach (var columnInfo in this.EntityInfo.Columns) + { + if (columnInfo.ForOwnsOnePropertyInfo != null) + { + var data = columnInfo.ForOwnsOnePropertyInfo.GetValue(item, null); + if (data != null) + { + dataEvent(columnInfo.PropertyInfo.GetValue(data, null), new DataFilterModel() { OperationType = DataFilterType.InsertByObject, EntityValue = item, EntityColumnInfo = columnInfo }); + } + } + else if (columnInfo.PropertyInfo.Name == "Item" && columnInfo.IsIgnore) + { + //class index + } + else + { + dataEvent(columnInfo.PropertyInfo.GetValue(item, null), new DataFilterModel() { OperationType = DataFilterType.InsertByObject, EntityValue = item, EntityColumnInfo = columnInfo }); + } + } + } + } + } + } + + private void SetInsertItemByDic(int i, T item, List insertItem) + { + foreach (var column in (item as Dictionary).OrderBy(it => it.Key)) + { + var columnInfo = new DbColumnInfo() + { + Value = column.Value, + DbColumnName = column.Key, + PropertyName = column.Key, + PropertyType = column.Value == null ? DBNull.Value.GetType() : UtilMethods.GetUnderType(column.Value.GetType()), + TableId = i + }; + if (columnInfo.PropertyType?.FullName == "System.Text.Json.JsonElement") + { + columnInfo.Value = column.Value.ObjToString(); + } + if (columnInfo.PropertyType.IsEnum()) + { + if (this.Context.CurrentConnectionConfig.MoreSettings?.TableEnumIsString == true) + { + columnInfo.Value = columnInfo.Value.ToString(); + columnInfo.PropertyType = UtilConstants.StringType; + } + else + { + columnInfo.Value = Convert.ToInt64(columnInfo.Value); + } + } + insertItem.Add(columnInfo); + } + } + private void SetInsertItemByEntity(int i, T item, List insertItem) + { + if (item == null) + { + return; + } + foreach (var column in EntityInfo.Columns) + { + if (column.IsIgnore || column.IsOnlyIgnoreInsert) continue; + var isMapping = IsMappingColumns; + var columnInfo = new DbColumnInfo() + { + Value = GetValue(item, column), + DbColumnName = column.DbColumnName, + PropertyName = column.PropertyName, + PropertyType = UtilMethods.GetUnderType(column.PropertyInfo), + TableId = i, + InsertSql = column.InsertSql, + InsertServerTime = column.InsertServerTime, + DataType = column.DataType, + SqlParameterDbType = column.SqlParameterDbType, + IsIdentity = column.IsIdentity + + }; + if (column.DbColumnName == null) + { + column.DbColumnName = column.PropertyName; + } + if (isMapping && column.ForOwnsOnePropertyInfo == null) + { + columnInfo.DbColumnName = GetDbColumnName(column.PropertyName); + } + if (column.IsJson) + { + columnInfo.IsJson = true; + } + if (column.IsArray) + { + columnInfo.IsArray = true; + } + if (columnInfo.PropertyType.IsEnum() && columnInfo.Value != null) + { + if (this.Context.CurrentConnectionConfig.MoreSettings?.TableEnumIsString == true) + { + columnInfo.Value = columnInfo.Value.ToString(); + columnInfo.PropertyType = UtilConstants.StringType; + } + else + { + columnInfo.Value = Convert.ToInt64(columnInfo.Value); + } + } + if (column.IsJson && columnInfo.Value != null) + { + if (columnInfo.Value != null) + columnInfo.Value = this.Context.Utilities.SerializeObject(columnInfo.Value); + } + //var tranColumn=EntityInfo.Columns.FirstOrDefault(it => it.IsTranscoding && it.DbColumnName.Equals(column.DbColumnName, StringComparison.CurrentCultureIgnoreCase)); + if (column.IsTranscoding && columnInfo.Value.HasValue()) + { + columnInfo.Value = UtilMethods.EncodeBase64(columnInfo.Value.ToString()); + } + insertItem.Add(columnInfo); + } + if (EntityInfo.Discrimator.HasValue()) + { + Check.ExceptionEasy(!Regex.IsMatch(EntityInfo.Discrimator, @"^(?:\w+:\w+)(?:,\w+:\w+)*$"), "The format should be type:cat for this type, and if there are multiple, it can be FieldName:cat,FieldName2:dog ", "格式错误应该是type:cat这种格式,如果是多个可以FieldName:cat,FieldName2:dog,不要有空格"); + var array = EntityInfo.Discrimator.Split(','); + foreach (var disItem in array) + { + var name = disItem.Split(':').First(); + var value = disItem.Split(':').Last(); + insertItem.Add(new DbColumnInfo() { DbColumnName = name, PropertyName = name, PropertyType = typeof(string), Value = value }); + } + } + } + private static object GetValue(T item, EntityColumnInfo column) + { + if (column.ForOwnsOnePropertyInfo != null) + { + var owsPropertyValue = column.ForOwnsOnePropertyInfo.GetValue(item, null); + return column.PropertyInfo.GetValue(owsPropertyValue, null); + } + if (StaticConfig.EnableAot) + { + return column.PropertyInfo.GetValue(item, null); + } + else + { + return PropertyCallAdapterProvider.GetInstance(column.PropertyName).InvokeGet(item); + } + } + + private string GetDbColumnName(string propertyName) + { + if (!IsMappingColumns) + { + return propertyName; + } + if (this.Context.MappingColumns.Any(it => it.EntityName.Equals(EntityInfo.EntityName, StringComparison.CurrentCultureIgnoreCase))) + { + this.MappingColumnList = this.Context.MappingColumns.Where(it => it.EntityName.Equals(EntityInfo.EntityName, StringComparison.CurrentCultureIgnoreCase)).ToList(); + } + if (MappingColumnList == null || MappingColumnList.Count == 0) + { + return propertyName; + } + else + { + var mappInfo = this.MappingColumnList.FirstOrDefault(it => it.PropertyName.Equals(propertyName, StringComparison.CurrentCultureIgnoreCase)); + return mappInfo == null ? propertyName : mappInfo.DbColumnName; + } + } + + protected virtual List GetPrimaryKeys() + { + return this.EntityInfo.Columns.Where(it => it.IsPrimarykey).Select(it => it.DbColumnName).ToList(); + } + protected virtual List GetIdentityKeys() + { + return this.EntityInfo.Columns.Where(it => + { + + if (StaticConfig.Check_StringIdentity) + { + Check.ExceptionEasy(it.IsIdentity && it.UnderType == typeof(string), "Auto-incremented is not a string, how can I use a executable startup configuration: StaticConfig.Check_StringIdentity=false ", "自增不是能string,如何非要用可以程序启动配置:StaticConfig.Check_StringIdentity=false"); + } + return it.IsIdentity; + + }).Select(it => it.DbColumnName).ToList(); + } + //private void TaskStart(Task result) + //{ + // if (this.Context.CurrentConnectionConfig.IsShardSameThread) + // { + // Check.Exception(true, "IsShardSameThread=true can't be used async method"); + // } + // result.Start(); + //} + protected void RestoreMapping() + { + if (IsAs) + { + this.Context.MappingTables = OldMappingTableList; + } + } + //protected IInsertable CopyInsertable() + //{ + // var asyncContext = this.Context.Utilities.CopyContext(true); + // asyncContext.CurrentConnectionConfig.IsAutoCloseConnection = true; + // asyncContext.IsAsyncMethod = true; + // var asyncInsertable = asyncContext.Insertable(this.InsertObjs); + // var asyncInsertableBuilder = asyncInsertable.InsertBuilder; + // asyncInsertableBuilder.DbColumnInfoList = this.InsertBuilder.DbColumnInfoList; + // asyncInsertableBuilder.EntityInfo = this.InsertBuilder.EntityInfo; + // asyncInsertableBuilder.Parameters = this.InsertBuilder.Parameters; + // asyncInsertableBuilder.sql = this.InsertBuilder.sql; + // asyncInsertableBuilder.IsNoInsertNull = this.InsertBuilder.IsNoInsertNull; + // asyncInsertableBuilder.IsReturnIdentity = this.InsertBuilder.IsReturnIdentity; + // asyncInsertableBuilder.EntityInfo = this.InsertBuilder.EntityInfo; + // asyncInsertableBuilder.TableWithString = this.InsertBuilder.TableWithString; + // if (this.RemoveCacheFunc != null) + // { + // asyncInsertable.RemoveDataCache(); + // } + // return asyncInsertable; + //} + + protected void After(string sql, long? result) + { + if (this.IsEnableDiffLogEvent) + { + var isDisableMasterSlaveSeparation = this.Ado.IsDisableMasterSlaveSeparation; + this.Ado.IsDisableMasterSlaveSeparation = true; + var parameters = InsertBuilder.Parameters; + if (parameters == null) + parameters = new List(); + diffModel.AfterData = GetDiffTable(sql, result); + diffModel.Time = this.Context.Ado.SqlExecutionTime; + if (this.Context.CurrentConnectionConfig.AopEvents.OnDiffLogEvent != null) + this.Context.CurrentConnectionConfig.AopEvents.OnDiffLogEvent(diffModel); + this.Ado.IsDisableMasterSlaveSeparation = isDisableMasterSlaveSeparation; + } + if (this.RemoveCacheFunc != null) + { + this.RemoveCacheFunc(); + } + DataChangeAop(this.InsertObjs); + } + protected void Before(string sql) + { + if (this.IsEnableDiffLogEvent) + { + var isDisableMasterSlaveSeparation = this.Ado.IsDisableMasterSlaveSeparation; + this.Ado.IsDisableMasterSlaveSeparation = true; + var parameters = InsertBuilder.Parameters; + if (parameters == null) + parameters = new List(); + diffModel.BeforeData = null; + diffModel.Sql = sql; + diffModel.Parameters = parameters.ToArray(); + this.Ado.IsDisableMasterSlaveSeparation = isDisableMasterSlaveSeparation; + } + } + private List GetDiffTable(string sql, long? identity) + { + + if (GetIdentityKeys().HasValue() && this.InsertObjs.Length > 1) + { + return GetDiffTableByEntity(); + } + else if (GetIdentityKeys().IsNullOrEmpty()) + { + return GetDiffTableByEntity(); + } + else + { + return GetDiffTableBySql(identity); + } + + } + + private List GetDiffTableByEntity() + { + List parameters = new List(); + List result = new List(); + var dt2 = this.Context.Utilities.ListToDataTable(this.InsertObjs.ToList()); + foreach (DataRow row in dt2.Rows) + { + DiffLogTableInfo item = new DiffLogTableInfo(); + item.TableDescription = this.EntityInfo.TableDescription; + item.TableName = this.EntityInfo.DbTableName; + item.Columns = new List(); + foreach (DataColumn col in dt2.Columns) + { + var sugarColumn = this.EntityInfo.Columns.Where(it => it.DbColumnName != null).FirstOrDefault(it => + it.PropertyName.Equals(col.ColumnName, StringComparison.CurrentCultureIgnoreCase)); + DiffLogColumnInfo addItem = new DiffLogColumnInfo(); + addItem.Value = row[col.ColumnName]; + addItem.ColumnName = sugarColumn?.DbColumnName ?? col.ColumnName; + addItem.IsPrimaryKey = sugarColumn?.IsPrimarykey ?? false; + addItem.ColumnDescription = sugarColumn?.ColumnDescription; + item.Columns.Add(addItem); + } + result.Add(item); + } + return result; + } + + private List GetDiffTableBySql(long? identity) + { + List parameters = new List(); + List result = new List(); + var whereSql = string.Empty; + List cons = new List(); + if (identity != null && identity > 0 && GetIdentityKeys().HasValue()) + { + var fieldName = GetIdentityKeys().Last(); + + if (this.Context.CurrentConnectionConfig.DbType == DbType.PostgreSQL) + { + var fieldObjectType = this.EntityInfo.Columns.FirstOrDefault(x => x.DbColumnName == fieldName) + .PropertyInfo.PropertyType; + cons.Add(new ConditionalModel() + { + ConditionalType = ConditionalType.Equal, + FieldName = fieldName, + FieldValue = identity.ToString(), + FieldValueConvertFunc = it => UtilMethods.ChangeType2(it, fieldObjectType) + }); + } + else + cons.Add(new ConditionalModel() { ConditionalType = ConditionalType.Equal, FieldName = fieldName, FieldValue = identity.ToString() }); + } + else + { + foreach (var item in this.EntityInfo.Columns.Where(it => it.IsIgnore == false && GetPrimaryKeys().Any(pk => pk.Equals(it.DbColumnName, StringComparison.CurrentCultureIgnoreCase)))) + { + var fielddName = item.DbColumnName; + var filedObject = this.EntityInfo.Columns.FirstOrDefault(it => it.PropertyName == item.PropertyName).PropertyInfo.GetValue(this.InsertObjs.Last(), null); + var fieldValue = filedObject.ObjToString(); + if (filedObject != null && filedObject.GetType() != typeof(string) && this.Context.CurrentConnectionConfig.DbType == DbType.PostgreSQL) + { + cons.Add(new ConditionalModel() { ConditionalType = ConditionalType.Equal, FieldName = fielddName, FieldValue = fieldValue, FieldValueConvertFunc = it => UtilMethods.ChangeType2(it, filedObject.GetType()) }); + } + else + { + cons.Add(new ConditionalModel() { ConditionalType = ConditionalType.Equal, FieldName = fielddName, FieldValue = fieldValue }); + } + } + } + Check.Exception(cons.IsNullOrEmpty(), "Insertable.EnableDiffLogEvent need primary key"); + var sqlable = this.SqlBuilder.ConditionalModelToSql(cons); + whereSql = sqlable.Key; + parameters.AddRange(sqlable.Value); + var dt = this.Context.Queryable().Where(whereSql).AddParameters(parameters).ToDataTable(); + if (dt.Rows?.Count > 0) + { + foreach (DataRow row in dt.Rows) + { + DiffLogTableInfo item = new DiffLogTableInfo(); + item.TableDescription = this.EntityInfo.TableDescription; + item.TableName = this.EntityInfo.DbTableName; + item.Columns = new List(); + foreach (DataColumn col in dt.Columns) + { + var sugarColumn = this.EntityInfo.Columns.Where(it => it.DbColumnName != null).First(it => + it.DbColumnName.Equals(col.ColumnName, StringComparison.CurrentCultureIgnoreCase)); + DiffLogColumnInfo addItem = new DiffLogColumnInfo(); + addItem.Value = row[col.ColumnName]; + addItem.ColumnName = col.ColumnName; + addItem.IsPrimaryKey = sugarColumn.IsPrimarykey; + addItem.ColumnDescription = sugarColumn.ColumnDescription; + item.Columns.Add(addItem); + } + result.Add(item); + } + return result; + } + else + { + DiffLogTableInfo diffTable = new DiffLogTableInfo(); + diffTable.TableName = this.EntityInfo.DbTableName; + diffTable.TableDescription = this.EntityInfo.TableDescription; + diffTable.Columns = this.EntityInfo.Columns.Where(it => it.IsIgnore == false).Select(it => new DiffLogColumnInfo() + { + ColumnDescription = it.ColumnDescription, + ColumnName = it.DbColumnName, + Value = it.PropertyInfo.GetValue(this.InsertObjs.Last(), null), + IsPrimaryKey = it.IsPrimarykey + }).ToList(); + return new List() { diffTable }; + } + } + + public IInsertable CallEntityMethod(Expression> method) + { + if (this.InsertObjs.HasValue()) + { + var oldColumns = this.InsertBuilder.DbColumnInfoList.Select(it => it.PropertyName).ToHashSet(); + var expression = (LambdaExpression.Lambda(method).Body as LambdaExpression).Body; + Check.Exception(!(expression is MethodCallExpression), method.ToString() + " is not method"); + var callExpresion = expression as MethodCallExpression; + UtilMethods.DataInoveByExpresson(this.InsertObjs, callExpresion); + this.InsertBuilder.DbColumnInfoList = new List(); + Init(); + this.InsertBuilder.DbColumnInfoList = this.InsertBuilder.DbColumnInfoList.Where(it => oldColumns.Contains(it.PropertyName)).ToList(); + } + return this; + } + + #endregion + + #region Insert PkList + + private List InsertPkListWithFunc(EntityColumnInfo pkInfo) + { + InsertBuilder.IsReturnPkList = true; + InsertBuilder.IsNoPage = true; + string sql = _ExecuteCommand(); + sql = this.InsertBuilder.ConvertInsertReturnIdFunc(SqlBuilder.GetTranslationColumnName(pkInfo.DbColumnName), sql); + var result = Ado.SqlQuery(sql, InsertBuilder.Parameters == null ? null : InsertBuilder.Parameters.ToArray()); + After(sql, null); + return result; + } + + private List InsertPkListNoFunc(EntityColumnInfo pkInfo) + { + if (this.Ado.Transaction != null) + { + return ReturnDefaultIdentity(pkInfo); + } + else + { + try + { + this.Context.Ado.BeginTran(); + var result = ReturnDefaultIdentity(pkInfo); + this.Context.Ado.CommitTran(); + return result; + } + catch (Exception) + { + this.Context.Ado.RollbackTran(); + throw; + } + } + } + + private List InsertPkListIdentityCount1(EntityColumnInfo pkInfo) + { + if (pkInfo.UnderType == UtilConstants.IntType) + { + return new List { (Type)(object)this.ExecuteReturnIdentity() }; + } + else + { + return new List { (Type)(object)this.ExecuteReturnBigIdentity() }; + } + } + + private List InsertPkListLong() + { + var list = this.ExecuteReturnSnowflakeIdList(); + try + { + return list.Cast().ToList(); + } + catch + { + Check.ExceptionEasy($"long to ExecuteReturnPkList<{typeof(Type).Name}> error ", $" long 转换成ExecuteReturnPkList<{typeof(Type).Name}>失败"); + return null; + } + } + + private List InsertPkListGuid(EntityColumnInfo pkInfo) + { + Check.ExceptionEasy(pkInfo.UnderType.Name != typeof(Type).Name, $"{pkInfo.UnderType.Name} to ExecuteReturnPkList<{typeof(Type).Name}> error ", $" {pkInfo.UnderType.Name} 转换成ExecuteReturnPkList<{typeof(Type).Name}>失败"); + this.ExecuteCommand(); + List result = new List(); + if (InsertBuilder.DbColumnInfoList.HasValue()) + { + foreach (var item in InsertBuilder.DbColumnInfoList) + { + var isPk = item.DbColumnName.EqualCase(pkInfo.DbColumnName); + if (isPk) + { + result.Add((Type)item.Value); + } + } + } + return result; + } + private List ReturnDefaultIdentity(EntityColumnInfo pkInfo) + { + List result = new List(); + foreach (var item in this.InsertObjs) + { + var insertable = this.Context.Insertable(item) + .AS(this.InsertBuilder.AsName) + .InsertColumns(this.InsertBuilder.DbColumnInfoList.Select(it => it.DbColumnName).Distinct().ToArray()); + if (pkInfo.UnderType == UtilConstants.IntType) + { + result.Add((Type)(object)insertable.ExecuteReturnIdentity()); + } + else + { + result.Add((Type)(object)insertable.ExecuteReturnBigIdentity()); + } + } + return result; + } + #endregion + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/InsertableProvider/InsertablePage.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/InsertableProvider/InsertablePage.cs new file mode 100644 index 000000000..238d6f96a --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/InsertableProvider/InsertablePage.cs @@ -0,0 +1,160 @@ +namespace SqlSugar +{ + public class InsertablePage where T : class, new() + { + public int PageSize { get; set; } + public SqlSugarProvider Context { get; set; } + public T[] DataList { get; set; } + public string TableName { get; internal set; } + public List InsertColumns { get; internal set; } + public bool IsEnableDiffLogEvent { get; internal set; } + public DiffLogModel DiffModel { get; internal set; } + public bool IsOffIdentity { get; internal set; } + public bool IsInsertColumnsNull { get; internal set; } + public bool IsMySqlIgnore { get; internal set; } + public int ExecuteCommand() + { + if (DataList.Length == 1 && DataList.First() == null) + { + return 0; + } + if (PageSize == 0) { PageSize = 1000; } + var result = 0; + var isNoTran = this.Context.Ado.IsNoTran(); + try + { + if (isNoTran) + { + this.Context.Ado.BeginTran(); + } + this.Context.Utilities.PageEach(DataList, PageSize, pageItem => + { + result += this.Context.Insertable(pageItem).AS(TableName).MySqlIgnore(IsMySqlIgnore).IgnoreColumnsNull(this.IsInsertColumnsNull).OffIdentity(IsOffIdentity).EnableDiffLogEventIF(IsEnableDiffLogEvent, DiffModel).InsertColumns(InsertColumns.ToArray()).ExecuteCommand(); + }); + if (isNoTran) + { + this.Context.Ado.CommitTran(); + } + } + catch (Exception) + { + if (isNoTran) + { + this.Context.Ado.RollbackTran(); + } + throw; + } + return result; + } + public async Task ExecuteCommandAsync() + { + if (DataList.Length == 1 && DataList.First() == null) + { + return 0; + } + if (PageSize == 0) { PageSize = 1000; } + var result = 0; + var isNoTran = this.Context.Ado.IsNoTran(); + try + { + if (isNoTran) + { + await Context.Ado.BeginTranAsync().ConfigureAwait(false); + } + await Context.Utilities.PageEachAsync(DataList, PageSize, async pageItem => + { + result += await Context.Insertable(pageItem).AS(TableName).IgnoreColumnsNull(IsInsertColumnsNull).OffIdentity(IsOffIdentity).EnableDiffLogEventIF(IsEnableDiffLogEvent, DiffModel).InsertColumns(InsertColumns.ToArray()).ExecuteCommandAsync().ConfigureAwait(false); + }).ConfigureAwait(false); + if (isNoTran) + { + await Context.Ado.CommitTranAsync().ConfigureAwait(false); + } + } + catch (Exception) + { + if (isNoTran) + { + await Context.Ado.RollbackTranAsync().ConfigureAwait(false); + } + throw; + } + return result; + } + + public List ExecuteReturnSnowflakeIdList() + { + if (DataList.Length == 1 && DataList.First() == null) + { + return new List(); + } + if (PageSize == 0) { PageSize = 1000; } + var result = new List(); + var isNoTran = this.Context.Ado.IsNoTran(); + try + { + if (isNoTran) + { + this.Context.Ado.BeginTran(); + } + this.Context.Utilities.PageEach(DataList, PageSize, pageItem => + { + result.AddRange(this.Context.Insertable(pageItem).AS(TableName).OffIdentity(IsOffIdentity).EnableDiffLogEventIF(IsEnableDiffLogEvent, DiffModel).InsertColumns(InsertColumns.ToArray()).ExecuteReturnSnowflakeIdList()); + }); + if (isNoTran) + { + this.Context.Ado.CommitTran(); + } + } + catch (Exception) + { + if (isNoTran) + { + this.Context.Ado.RollbackTran(); + } + throw; + } + return result; + } + public async Task> ExecuteReturnSnowflakeIdListAsync() + { + if (DataList.Length == 1 && DataList.First() == null) + { + return new List(); + } + if (PageSize == 0) { PageSize = 1000; } + var result = new List(); + var isNoTran = this.Context.Ado.IsNoTran(); + try + { + if (isNoTran) + { + await Context.Ado.BeginTranAsync().ConfigureAwait(false); + } + await Context.Utilities.PageEachAsync(DataList, PageSize, async pageItem => + { + result.AddRange(await Context.Insertable(pageItem).AS(TableName).OffIdentity(IsOffIdentity).EnableDiffLogEventIF(IsEnableDiffLogEvent, DiffModel).InsertColumns(InsertColumns.ToArray()).ExecuteReturnSnowflakeIdListAsync().ConfigureAwait(false)); + }).ConfigureAwait(false); + if (isNoTran) + { + await Context.Ado.CommitTranAsync().ConfigureAwait(false); + } + } + catch (Exception) + { + if (isNoTran) + { + await Context.Ado.RollbackTranAsync().ConfigureAwait(false); + } + throw; + } + return result; + } + + public InsertablePage IgnoreColumnsNull(bool isIgnoreNull = true) + { + this.PageSize = 1; + this.IsInsertColumnsNull = isIgnoreNull; + return this; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/InsertableProvider/InsertableProvider.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/InsertableProvider/InsertableProvider.cs new file mode 100644 index 000000000..a36e5e91b --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/InsertableProvider/InsertableProvider.cs @@ -0,0 +1,793 @@ +using System.Data; +using System.Linq.Expressions; +using System.Reflection; + +namespace SqlSugar +{ + public partial class InsertableProvider : IInsertable where T : class, new() + { + public SqlSugarProvider Context { get; set; } + public IAdo Ado { get { return Context.Ado; } } + public ISqlBuilder SqlBuilder { get; set; } + public InsertBuilder InsertBuilder { get; set; } + + public bool IsMappingTable { get { return this.Context.MappingTables?.Any() == true; } } + public bool IsMappingColumns { get { return this.Context.MappingColumns?.Any() == true; } } + public bool IsSingle { get { return this.InsertObjs.Length == 1; } } + + public EntityInfo EntityInfo { get; set; } + public List MappingColumnList { get; set; } + private List IgnoreColumnNameList { get; set; } + internal bool IsOffIdentity { get; set; } + public T[] InsertObjs { get; set; } + + public MappingTableList OldMappingTableList { get; set; } + public bool IsAs { get; set; } + public bool IsEnableDiffLogEvent { get; set; } + public DiffLogModel diffModel { get; set; } + internal Action RemoveCacheFunc { get; set; } + + + #region Core + public void AddQueue() + { + if (this.InsertObjs?.Length > 0 && this.InsertObjs[0] != null) + { + var sqlObj = this.ToSql(); + this.Context.Queues.Add(sqlObj.Key, sqlObj.Value); + } + } + public virtual int ExecuteCommand() + { + if (this.InsertObjs.Length == 1 && this.InsertObjs.First() == null) + { + return 0; + } + string sql = _ExecuteCommand(); + var result = Ado.ExecuteCommand(sql, InsertBuilder.Parameters == null ? null : InsertBuilder.Parameters.ToArray()); + After(sql, null); + if (result == -1) return this.InsertObjs.Length; + return result; + } + public virtual string ToSqlString() + { + var sqlObj = this.ToSql(); + var result = sqlObj.Key; + if (result == null) return null; + result = UtilMethods.GetSqlString(this.Context.CurrentConnectionConfig, sqlObj); + return result; + } + public virtual KeyValuePair> ToSql() + { + InsertBuilder.IsReturnIdentity = true; + if (this.SqlBuilder.SqlParameterKeyWord == ":" && !this.EntityInfo.Columns.Any(it => it.IsIdentity)) + { + InsertBuilder.IsReturnIdentity = false; + } + PreToSql(); + AutoRemoveDataCache(); + string sql = InsertBuilder.ToSqlString(); + RestoreMapping(); + return new KeyValuePair>(sql, InsertBuilder.Parameters); + } + public async Task> ExecuteReturnPkListAsync() + { + return await Task.Run(() => ExecuteReturnPkList()).ConfigureAwait(false); + } + public virtual List ExecuteReturnPkList() + { + var pkInfo = this.EntityInfo.Columns.FirstOrDefault(it => it.IsPrimarykey == true); + Check.ExceptionEasy(pkInfo == null, "ExecuteReturnPkList need primary key", "ExecuteReturnPkList需要主键"); + Check.ExceptionEasy(this.EntityInfo.Columns.Count(it => it.IsPrimarykey == true) > 1, "ExecuteReturnPkList ,Only support technology single primary key", "ExecuteReturnPkList只支技单主键"); + var isIdEntity = pkInfo.IsIdentity || (pkInfo.OracleSequenceName.HasValue() && this.Context.CurrentConnectionConfig.DbType == DbType.Oracle); + if (isIdEntity && this.InsertObjs.Length == 1) + { + return InsertPkListIdentityCount1(pkInfo); + } + else if (isIdEntity && this.InsertBuilder.ConvertInsertReturnIdFunc == null) + { + return InsertPkListNoFunc(pkInfo); + } + else if (isIdEntity && this.InsertBuilder.ConvertInsertReturnIdFunc != null) + { + return InsertPkListWithFunc(pkInfo); + } + else if (pkInfo.UnderType == UtilConstants.LongType) + { + return InsertPkListLong(); + } + else + { + return InsertPkListGuid(pkInfo); + } + } + + public virtual int ExecuteReturnIdentity() + { + if (this.InsertObjs.Length == 1 && this.InsertObjs.First() == null) + { + return 0; + } + string sql = _ExecuteReturnIdentity(); + var result = 0; + if (InsertBuilder.IsOleDb) + { + var isAuto = false; + if (this.Context.Ado.IsAnyTran() == false && this.Context.CurrentConnectionConfig.IsAutoCloseConnection) + { + isAuto = this.Context.CurrentConnectionConfig.IsAutoCloseConnection; + this.Context.CurrentConnectionConfig.IsAutoCloseConnection = false; + } + result = Ado.GetInt(sql.Split(';').First(), InsertBuilder.Parameters == null ? null : InsertBuilder.Parameters.ToArray()); + result = Ado.GetInt(sql.Split(';').Last(), InsertBuilder.Parameters == null ? null : InsertBuilder.Parameters.ToArray()); + if (this.Context.Ado.IsAnyTran() == false && isAuto) + { + this.Ado.Close(); + this.Context.CurrentConnectionConfig.IsAutoCloseConnection = isAuto; + } + } + else + { + result = Ado.GetInt(sql, InsertBuilder.Parameters == null ? null : InsertBuilder.Parameters.ToArray()); + } + After(sql, result); + return result; + } + + public virtual long ExecuteReturnBigIdentity() + { + if (this.InsertObjs.Length == 1 && this.InsertObjs.First() == null) + { + return 0; + } + string sql = _ExecuteReturnBigIdentity(); + long result = 0; + if (InsertBuilder.IsOleDb) + { + var isAuto = false; + if (this.Context.Ado.IsAnyTran() == false && this.Context.CurrentConnectionConfig.IsAutoCloseConnection) + { + isAuto = this.Context.CurrentConnectionConfig.IsAutoCloseConnection; + this.Context.CurrentConnectionConfig.IsAutoCloseConnection = false; + } + result = Convert.ToInt64(Ado.GetScalar(sql.Split(';').First(), InsertBuilder.Parameters == null ? null : InsertBuilder.Parameters.ToArray())); + result = Convert.ToInt64(Ado.GetScalar(sql.Split(';').Last(), InsertBuilder.Parameters == null ? null : InsertBuilder.Parameters.ToArray())); + if (this.Context.Ado.IsAnyTran() == false && isAuto) + { + this.Ado.Close(); + this.Context.CurrentConnectionConfig.IsAutoCloseConnection = isAuto; + } + } + else + { + result = (Ado.GetScalar(sql, InsertBuilder.Parameters == null ? null : InsertBuilder.Parameters.ToArray())).ObjToLong(); + } + After(sql, result); + return result; + } + + public virtual long ExecuteReturnSnowflakeId() + { + if (this.InsertObjs.Length > 1) + { + return this.ExecuteReturnSnowflakeIdList().First(); + } + + var id = SnowFlakeSingle.instance.getID(); + var entity = this.Context.EntityMaintenance.GetEntityInfo(); + var snowProperty = entity.Columns.FirstOrDefault(it => it.IsPrimarykey && it.PropertyInfo.PropertyType == UtilConstants.LongType); + Check.Exception(snowProperty == null, "The entity sets the primary key and is long"); + Check.Exception(snowProperty.IsIdentity == true, "SnowflakeId IsIdentity can't true"); + foreach (var item in this.InsertBuilder.DbColumnInfoList.Where(it => it.PropertyName == snowProperty.PropertyName)) + { + item.Value = id; + snowProperty?.PropertyInfo.SetValue(this.InsertObjs.First(), id); + } + this.ExecuteCommand(); + return id; + } + public List ExecuteReturnSnowflakeIdList() + { + List result = new List(); + var entity = this.Context.EntityMaintenance.GetEntityInfo(); + var snowProperty = entity.Columns.FirstOrDefault(it => it.IsPrimarykey && it.PropertyInfo.PropertyType == UtilConstants.LongType); + Check.Exception(snowProperty == null, "The entity sets the primary key and is long"); + Check.Exception(snowProperty.IsIdentity == true, "SnowflakeId IsIdentity can't true"); + foreach (var item in this.InsertBuilder.DbColumnInfoList.Where(it => it.PropertyName == snowProperty.PropertyName)) + { + var id = SnowFlakeSingle.instance.getID(); + item.Value = id; + result.Add(id); + var obj = this.InsertObjs.ElementAtOrDefault(item.TableId); + if (obj != null) + { + snowProperty?.PropertyInfo.SetValue(obj, id); + } + } + this.ExecuteCommand(); + return result; + } + public Task ExecuteReturnSnowflakeIdAsync(CancellationToken token) + { + this.Context.Ado.CancellationToken = token; + return ExecuteReturnSnowflakeIdAsync(); + } + public async Task ExecuteReturnSnowflakeIdAsync() + { + var id = SnowFlakeSingle.instance.getID(); + var entity = this.Context.EntityMaintenance.GetEntityInfo(); + var snowProperty = entity.Columns.FirstOrDefault(it => it.IsPrimarykey && it.PropertyInfo.PropertyType == UtilConstants.LongType); + Check.Exception(snowProperty == null, "The entity sets the primary key and is long"); + Check.Exception(snowProperty.IsIdentity == true, "SnowflakeId IsIdentity can't true"); + foreach (var item in this.InsertBuilder.DbColumnInfoList.Where(it => it.PropertyName == snowProperty.PropertyName)) + { + item.Value = id; + snowProperty?.PropertyInfo.SetValue(this.InsertObjs.First(), id); + } + await ExecuteCommandAsync().ConfigureAwait(false); + return id; + } + public async Task> ExecuteReturnSnowflakeIdListAsync() + { + List result = new List(); + var entity = this.Context.EntityMaintenance.GetEntityInfo(); + var snowProperty = entity.Columns.FirstOrDefault(it => it.IsPrimarykey && it.PropertyInfo.PropertyType == UtilConstants.LongType); + Check.Exception(snowProperty == null, "The entity sets the primary key and is long"); + Check.Exception(snowProperty.IsIdentity == true, "SnowflakeId IsIdentity can't true"); + foreach (var item in this.InsertBuilder.DbColumnInfoList.Where(it => it.PropertyName == snowProperty.PropertyName)) + { + var id = SnowFlakeSingle.instance.getID(); + item.Value = id; + result.Add(id); + var obj = this.InsertObjs.ElementAtOrDefault(item.TableId); + if (obj != null) + { + snowProperty?.PropertyInfo.SetValue(obj, id); + } + } + await ExecuteCommandAsync().ConfigureAwait(false); + return result; + } + + public Task> ExecuteReturnSnowflakeIdListAsync(CancellationToken token) + { + this.Ado.CancellationToken = token; + return ExecuteReturnSnowflakeIdListAsync(); + } + + public virtual T ExecuteReturnEntity() + { + ExecuteCommandIdentityIntoEntity(); + return InsertObjs.First(); + } + public virtual bool ExecuteCommandIdentityIntoEntity() + { + var result = InsertObjs.First(); + var identityKeys = GetIdentityKeys(); + if (this.Context?.CurrentConnectionConfig?.MoreSettings?.EnableOracleIdentity == true) + { + var identity = this.EntityInfo.Columns.FirstOrDefault(it => it.IsIdentity); + if (identity != null) + { + identityKeys = new List() { identity.DbColumnName }; + } + } + if (identityKeys.Count == 0) + { + var snowColumn = this.EntityInfo.Columns.FirstOrDefault(it => it.IsPrimarykey && it.UnderType == UtilConstants.LongType); + if (snowColumn != null) + { + if (Convert.ToInt64(snowColumn.PropertyInfo.GetValue(result)) == 0) + { + var id = this.ExecuteReturnSnowflakeId(); + snowColumn.PropertyInfo.SetValue(result, id); + } + else + { + ExecuteCommand(); + } + return true; + } + else + { + return this.ExecuteCommand() > 0; + } + } + var idValue = ExecuteReturnBigIdentity(); + Check.Exception(identityKeys.Count > 1, "ExecuteCommandIdentityIntoEntity does not support multiple identity keys"); + var identityKey = identityKeys.First(); + object setValue = 0; + if (idValue > int.MaxValue) + setValue = idValue; + else if (this.EntityInfo.Columns.Any(it => it.IsIdentity && it.PropertyInfo.PropertyType == typeof(uint))) + { + setValue = Convert.ToUInt32(idValue); + } + else if (this.EntityInfo.Columns.Any(it => it.IsIdentity && it.PropertyInfo.PropertyType == typeof(ulong))) + { + setValue = Convert.ToUInt64(idValue); + } + else if (this.EntityInfo.Columns.Any(it => it.IsIdentity && it.PropertyInfo.PropertyType == typeof(ushort))) + { + setValue = Convert.ToUInt16(idValue); + } + else if (this.EntityInfo.Columns.Any(it => it.IsIdentity && it.PropertyInfo.PropertyType == typeof(short))) + { + setValue = Convert.ToInt16(idValue); + } + else + setValue = Convert.ToInt32(idValue); + this.Context.EntityMaintenance.GetProperty(identityKey).SetValue(result, setValue, null); + return idValue > 0; + } + public Task ExecuteCommandAsync(CancellationToken token) + { + this.Context.Ado.CancellationToken = token; + return ExecuteCommandAsync(); + } + public async Task ExecuteCommandAsync() + { + if (this.InsertObjs.Length == 1 && this.InsertObjs.First() == null) + { + return 0; + } + string sql = _ExecuteCommand(); + var result = await Ado.ExecuteCommandAsync(sql, InsertBuilder.Parameters == null ? null : InsertBuilder.Parameters.ToArray()).ConfigureAwait(false); + After(sql, null); + if (result == -1) return this.InsertObjs.Length; + return result; + } + public Task ExecuteReturnIdentityAsync(CancellationToken token) + { + this.Ado.CancellationToken = token; + return ExecuteReturnIdentityAsync(); + } + public virtual async Task ExecuteReturnIdentityAsync() + { + if (this.InsertObjs.Length == 1 && this.InsertObjs.First() == null) + { + return 0; + } + string sql = _ExecuteReturnIdentity(); + var result = 0; + if (InsertBuilder.IsOleDb) + { + var isAuto = false; + if (this.Context.Ado.IsAnyTran() == false && this.Context.CurrentConnectionConfig.IsAutoCloseConnection) + { + isAuto = this.Context.CurrentConnectionConfig.IsAutoCloseConnection; + this.Context.CurrentConnectionConfig.IsAutoCloseConnection = false; + } + result = Ado.GetInt(sql.Split(';').First(), InsertBuilder.Parameters == null ? null : InsertBuilder.Parameters.ToArray()); + result = Ado.GetInt(sql.Split(';').Last(), InsertBuilder.Parameters == null ? null : InsertBuilder.Parameters.ToArray()); + if (this.Context.Ado.IsAnyTran() == false && isAuto) + { + this.Ado.Close(); + this.Context.CurrentConnectionConfig.IsAutoCloseConnection = isAuto; + } + } + else + { + result = await Ado.GetIntAsync(sql, InsertBuilder.Parameters == null ? null : InsertBuilder.Parameters.ToArray()).ConfigureAwait(false); + } + After(sql, result); + return result; + } + public T ExecuteReturnEntity(bool isIncludesAllFirstLayer) + { + var data = ExecuteReturnEntity(); + if (this.InsertBuilder.IsWithAttr) + { + return this.Context.Root.QueryableWithAttr().WhereClassByPrimaryKey(data).IncludesAllFirstLayer().First(); + } + else + { + return this.Context.Queryable().WhereClassByPrimaryKey(data).IncludesAllFirstLayer().First(); + } + } + public async Task ExecuteReturnEntityAsync() + { + await ExecuteCommandIdentityIntoEntityAsync().ConfigureAwait(false); + return InsertObjs.First(); + } + public async Task ExecuteReturnEntityAsync(bool isIncludesAllFirstLayer) + { + var data = await ExecuteReturnEntityAsync().ConfigureAwait(false); + if (this.InsertBuilder.IsWithAttr) + { + return await Context.Root.QueryableWithAttr().WhereClassByPrimaryKey(data).IncludesAllFirstLayer().FirstAsync().ConfigureAwait(false); + } + else + { + return await Context.Queryable().WhereClassByPrimaryKey(data).IncludesAllFirstLayer().FirstAsync().ConfigureAwait(false); + } + } + public async Task ExecuteCommandIdentityIntoEntityAsync() + { + var result = InsertObjs.First(); + var identityKeys = GetIdentityKeys(); + if (identityKeys.Count == 0) + { + var snowColumn = this.EntityInfo.Columns.FirstOrDefault(it => it.IsPrimarykey && it.UnderType == UtilConstants.LongType); + if (snowColumn != null) + { + + if (Convert.ToInt64(snowColumn.PropertyInfo.GetValue(result)) == 0) + { + var id = await ExecuteReturnSnowflakeIdAsync().ConfigureAwait(false); + snowColumn.PropertyInfo.SetValue(result, id); + } + else + { + await ExecuteCommandAsync().ConfigureAwait(false); + } + return true; + } + else + { + return await ExecuteCommandAsync().ConfigureAwait(false) > 0; + } + } + var idValue = await ExecuteReturnBigIdentityAsync().ConfigureAwait(false); + Check.Exception(identityKeys.Count > 1, "ExecuteCommandIdentityIntoEntity does not support multiple identity keys"); + var identityKey = identityKeys.First(); + object setValue = 0; + if (idValue > int.MaxValue) + setValue = idValue; + else if (this.EntityInfo.Columns.Any(it => it.IsIdentity && it.PropertyInfo.PropertyType == typeof(uint))) + { + setValue = Convert.ToUInt32(idValue); + } + else if (this.EntityInfo.Columns.Any(it => it.IsIdentity && it.PropertyInfo.PropertyType == typeof(ulong))) + { + setValue = Convert.ToUInt64(idValue); + } + else if (this.EntityInfo.Columns.Any(it => it.IsIdentity && it.PropertyInfo.PropertyType == typeof(ushort))) + { + setValue = Convert.ToUInt16(idValue); + } + else if (this.EntityInfo.Columns.Any(it => it.IsIdentity && it.PropertyInfo.PropertyType == typeof(short))) + { + setValue = Convert.ToInt16(idValue); + } + else + setValue = Convert.ToInt32(idValue); + this.Context.EntityMaintenance.GetProperty(identityKey).SetValue(result, setValue, null); + return idValue > 0; + } + public Task ExecuteReturnBigIdentityAsync(CancellationToken token) + { + this.Context.Ado.CancellationToken = token; + return ExecuteReturnBigIdentityAsync(); + } + public virtual async Task ExecuteReturnBigIdentityAsync() + { + if (this.InsertObjs.Length == 1 && this.InsertObjs.First() == null) + { + return 0; + } + string sql = _ExecuteReturnBigIdentity(); + long result = 0; + if (InsertBuilder.IsOleDb) + { + var isAuto = false; + if (this.Context.Ado.IsAnyTran() == false && this.Context.CurrentConnectionConfig.IsAutoCloseConnection) + { + isAuto = this.Context.CurrentConnectionConfig.IsAutoCloseConnection; + this.Context.CurrentConnectionConfig.IsAutoCloseConnection = false; + } + result = Ado.GetInt(sql.Split(';').First(), InsertBuilder.Parameters == null ? null : InsertBuilder.Parameters.ToArray()); + result = Ado.GetInt(sql.Split(';').Last(), InsertBuilder.Parameters == null ? null : InsertBuilder.Parameters.ToArray()); + if (this.Context.Ado.IsAnyTran() == false && isAuto) + { + this.Ado.Close(); + this.Context.CurrentConnectionConfig.IsAutoCloseConnection = isAuto; + } + } + else + { + result = (await Ado.GetScalarAsync(sql, InsertBuilder.Parameters == null ? null : InsertBuilder.Parameters.ToArray()).ConfigureAwait(false)).ObjToLong(); + } + After(sql, result); + return result; + } + + #endregion + + #region Setting + public InsertablePage PageSize(int pageSize) + { + InsertablePage result = new InsertablePage(); + result.PageSize = pageSize; + result.Context = this.Context; + result.DataList = this.InsertObjs; + result.TableName = this.InsertBuilder.AsName; + result.IsEnableDiffLogEvent = this.IsEnableDiffLogEvent; + result.DiffModel = this.diffModel; + result.IsMySqlIgnore = this.InsertBuilder.MySqlIgnore; + result.IsOffIdentity = this.InsertBuilder.IsOffIdentity; + if (this.InsertBuilder.DbColumnInfoList.Count != 0) + result.InsertColumns = this.InsertBuilder.DbColumnInfoList.GroupBy(it => it.TableId).First().Select(it => it.DbColumnName).ToList(); + return result; + } + public IParameterInsertable UseParameter() + { + var result = new ParameterInsertable(); + result.Context = this.Context; + result.Inserable = this; + return result; + } + public IInsertable AsType(Type tableNameType) + { + return AS(this.Context.EntityMaintenance.GetEntityInfo(tableNameType).DbTableName); + } + public IInsertable AS(string tableName) + { + this.InsertBuilder.AsName = tableName; + return this; ; + } + public IInsertable IgnoreColumns(Expression> columns) + { + if (columns == null) return this; + var ignoreColumns = InsertBuilder.GetExpressionValue(columns, ResolveExpressType.ArraySingle).GetResultArray().Select(it => this.SqlBuilder.GetNoTranslationColumnName(it)).ToList(); + this.InsertBuilder.DbColumnInfoList = this.InsertBuilder.DbColumnInfoList.Where(it => !ignoreColumns.Any(ig => ig.Equals(it.PropertyName, StringComparison.CurrentCultureIgnoreCase))).ToList(); + this.InsertBuilder.DbColumnInfoList = this.InsertBuilder.DbColumnInfoList.Where(it => !ignoreColumns.Any(ig => ig.Equals(it.DbColumnName, StringComparison.CurrentCultureIgnoreCase))).ToList(); + return this; + } + public IInsertable IgnoreColumns(params string[] columns) + { + if (columns == null) + columns = Array.Empty(); + this.InsertBuilder.DbColumnInfoList = this.InsertBuilder.DbColumnInfoList.Where(it => !columns.Any(ig => ig.Equals(it.PropertyName, StringComparison.CurrentCultureIgnoreCase))).ToList(); + this.InsertBuilder.DbColumnInfoList = this.InsertBuilder.DbColumnInfoList.Where(it => !columns.Any(ig => ig.Equals(it.DbColumnName, StringComparison.CurrentCultureIgnoreCase))).ToList(); + return this; + } + + public IInsertable IgnoreColumnsNull(bool isIgnoreNull = true) + { + if (isIgnoreNull) + { + Check.Exception(this.InsertObjs.Length > 1, ErrorMessage.GetThrowMessage("ignoreNullColumn NoSupport batch insert, use .PageSize(1).IgnoreColumnsNull().ExecuteCommand()", "ignoreNullColumn 不支持批量操作,你可以用PageSzie(1).IgnoreColumnsNull().ExecuteCommand()")); + this.InsertBuilder.IsNoInsertNull = true; + } + return this; + } + public IInsertable PostgreSQLConflictNothing(string[] columns) + { + this.InsertBuilder.ConflictNothing = columns; + return this; + } + public IInsertable MySqlIgnore() + { + this.InsertBuilder.MySqlIgnore = true; + return this; + } + public IInsertable MySqlIgnore(bool isIgnore) + { + if (isIgnore) + { + return MySqlIgnore(); + } + else + { + return this; + } + } + public IInsertable InsertColumns(Expression> columns) + { + if (columns == null) return this; + var ignoreColumns = InsertBuilder.GetExpressionValue(columns, ResolveExpressType.ArraySingle).GetResultArray().Select(it => this.SqlBuilder.GetNoTranslationColumnName(it)).ToList(); + this.InsertBuilder.DbColumnInfoList = this.InsertBuilder.DbColumnInfoList.Where(it => ignoreColumns.Any(ig => ig.Equals(it.PropertyName, StringComparison.CurrentCultureIgnoreCase)) || ignoreColumns.Any(ig => ig.Equals(it.DbColumnName, StringComparison.CurrentCultureIgnoreCase))).ToList(); + return this; + } + + public IInsertable InsertColumns(string[] columns) + { + if (columns == null) return this; + this.InsertBuilder.DbColumnInfoList = this.InsertBuilder.DbColumnInfoList.Where(it => columns.Any(ig => ig.Equals(it.PropertyName, StringComparison.CurrentCultureIgnoreCase)) || columns.Any(ig => ig.Equals(it.DbColumnName, StringComparison.CurrentCultureIgnoreCase))).ToList(); + return this; + } + + public IInsertable With(string lockString) + { + if (this.Context.CurrentConnectionConfig.DbType == DbType.SqlServer) + this.InsertBuilder.TableWithString = lockString; + return this; + } + public IInsertable OffIdentity(bool isSetOn) + { + if (isSetOn) + { + return this.OffIdentity(); + } + else + { + return this; + } + } + public IInsertable OffIdentity() + { + this.IsOffIdentity = true; + this.InsertBuilder.IsOffIdentity = true; + return this; + } + public IInsertable IgnoreColumns(bool ignoreNullColumn, bool isOffIdentity = false) + { + Check.Exception(this.InsertObjs.Length > 1 && ignoreNullColumn, ErrorMessage.GetThrowMessage("ignoreNullColumn NoSupport batch insert, use .PageSize(1).IgnoreColumnsNull().ExecuteCommand()", "ignoreNullColumn 不支持批量操作, 你可以使用 .PageSize(1).IgnoreColumnsNull().ExecuteCommand()")); + this.IsOffIdentity = isOffIdentity; + this.InsertBuilder.IsOffIdentity = isOffIdentity; + if (this.InsertBuilder.LambdaExpressions == null) + this.InsertBuilder.LambdaExpressions = InstanceFactory.GetLambdaExpressions(this.Context.CurrentConnectionConfig); + this.InsertBuilder.IsNoInsertNull = ignoreNullColumn; + return this; + } + + public IInsertable RemoveDataCache() + { + this.RemoveCacheFunc = () => + { + var cacheService = this.Context.CurrentConnectionConfig.ConfigureExternalServices.DataInfoCacheService; + CacheSchemeMain.RemoveCache(cacheService, this.Context.EntityMaintenance.GetTableName()); + }; + return this; + } + public IInsertable RemoveDataCache(string likeString) + { + this.RemoveCacheFunc = () => + { + var cacheService = this.Context.CurrentConnectionConfig.ConfigureExternalServices.DataInfoCacheService; + CacheSchemeMain.RemoveCacheByLike(cacheService, likeString); + }; + return this; + } + public MySqlBlukCopy UseMySql() + { + return new MySqlBlukCopy(this.Context, this.SqlBuilder, InsertObjs); + } + public SqlServerBlukCopy UseSqlServer() + { + PreToSql(); + var currentType = this.Context.CurrentConnectionConfig.DbType; + Check.Exception(currentType != DbType.SqlServer, "UseSqlServer no support " + currentType); + SqlServerBlukCopy result = new SqlServerBlukCopy(); + result.DbColumnInfoList = this.InsertBuilder.DbColumnInfoList.GroupBy(it => it.TableId).ToList(); + result.InsertBuilder = this.InsertBuilder; + result.Builder = this.SqlBuilder; + result.Context = this.Context; + result.Inserts = this.InsertObjs; + return result; + } + public OracleBlukCopy UseOracle() + + { + + PreToSql(); + + var currentType = this.Context.CurrentConnectionConfig.DbType; + + Check.Exception(currentType != DbType.Oracle, "UseSqlServer no support " + currentType); + + OracleBlukCopy result = new OracleBlukCopy(); + + result.DbColumnInfoList = this.InsertBuilder.DbColumnInfoList.GroupBy(it => it.TableId).ToList(); + + result.InsertBuilder = this.InsertBuilder; + + result.Builder = this.SqlBuilder; + + result.Context = this.Context; + + result.Inserts = this.InsertObjs; + InsertBuilder.IsBlukCopy = true; + + return result; + + } + + + public IInsertable EnableDiffLogEventIF(bool isDiffLogEvent, object diffLogBizData) + { + if (isDiffLogEvent) + { + return EnableDiffLogEvent(diffLogBizData); + } + return this; + } + public IInsertable EnableDiffLogEvent(object businessData = null) + { + //Check.Exception(this.InsertObjs.HasValue() && this.InsertObjs.Count() > 1, "DiffLog does not support batch operations"); + diffModel = new DiffLogModel(); + this.IsEnableDiffLogEvent = true; + diffModel.BusinessData = businessData; + diffModel.DiffType = DiffType.insert; + return this; + } + + public ISubInsertable AddSubList(Expression> items) + { + Check.Exception(GetPrimaryKeys().Count == 0, typeof(T).Name + " need Primary key"); + Check.Exception(GetPrimaryKeys().Count > 1, typeof(T).Name + "Multiple primary keys are not supported"); + //Check.Exception(this.InsertObjs.Count() > 1, "SubInserable No Support Insertable(List)"); + //Check.Exception(items.ToString().Contains(".First().")==false, items.ToString()+ " not supported "); + if (this.InsertObjs == null || this.InsertObjs.Length == 0) + { + return new SubInsertable(); + } + SubInsertable result = new SubInsertable(); + result.InsertObjects = this.InsertObjs; + result.Context = this.Context; + result.SubList = new List(); + result.SubList.Add(new SubInsertTreeExpression() { Expression = items }); + result.InsertBuilder = this.InsertBuilder; + result.Pk = GetPrimaryKeys().First(); + result.Entity = this.EntityInfo; + return result; + } + public ISubInsertable AddSubList(Expression> tree) + { + Check.Exception(GetPrimaryKeys().Count == 0, typeof(T).Name + " need Primary key"); + Check.Exception(GetPrimaryKeys().Count > 1, typeof(T).Name + "Multiple primary keys are not supported"); + //Check.Exception(this.InsertObjs.Count() > 1, "SubInserable No Support Insertable(List)"); + //Check.Exception(items.ToString().Contains(".First().")==false, items.ToString()+ " not supported "); + if (this.InsertObjs == null || this.InsertObjs.Length == 0) + { + return new SubInsertable(); + } + SubInsertable result = new SubInsertable(); + result.InsertObjects = this.InsertObjs; + result.Context = this.Context; + result.SubList = new List(); + result.InsertBuilder = this.InsertBuilder; + result.Pk = GetPrimaryKeys().First(); + result.Entity = this.EntityInfo; + result.AddSubList(tree); + return result; + } + public SplitInsertable SplitTable(SplitType splitType) + { + UtilMethods.StartCustomSplitTable(this.Context, typeof(T)); + SplitTableContext helper = new SplitTableContext(Context) + { + EntityInfo = this.EntityInfo + }; + helper.CheckPrimaryKey(); + SplitInsertable result = new SplitInsertable(); + result.Context = this.Context; + result.EntityInfo = this.EntityInfo; + result.Helper = helper; + result.SplitType = splitType; + result.TableNames = new List>(); + result.MySqlIgnore = this.InsertBuilder.MySqlIgnore; + foreach (var item in this.InsertObjs) + { + var splitFieldValue = helper.GetValue(splitType, item); + var tableName = helper.GetTableName(splitType, splitFieldValue); + result.TableNames.Add(new KeyValuePair(tableName, item)); + } + result.Inserable = this; + return result; + } + + public SplitInsertable SplitTable() + { + if (StaticConfig.SplitTableCreateTableFunc != null) + { + StaticConfig.SplitTableCreateTableFunc(typeof(T), this.InsertObjs); + } + UtilMethods.StartCustomSplitTable(this.Context, typeof(T)); + var splitTableAttribute = typeof(T).GetCustomAttribute(); + if (splitTableAttribute != null) + { + return SplitTable((splitTableAttribute as SplitTableAttribute).SplitType); + } + else + { + Check.Exception(true, $" {typeof(T).Name} need SplitTableAttribute"); + return null; + } + } + + #endregion + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/InsertableProvider/ParameterInsertable.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/InsertableProvider/ParameterInsertable.cs new file mode 100644 index 000000000..ebe07f496 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/InsertableProvider/ParameterInsertable.cs @@ -0,0 +1,209 @@ +using System.Text; + +namespace SqlSugar +{ + public class ParameterInsertable : IParameterInsertable where T : class, new() + { + internal IInsertable Inserable { get; set; } + internal SqlSugarProvider Context { get; set; } + public int ExecuteCommand() + { + if (this.Context.CurrentConnectionConfig.DbType.IsIn(DbType.Oracle, DbType.Dm)) + { + return DefaultExecuteCommand(); + } + else + { + return ValuesExecuteCommand(); + } + } + public async Task ExecuteCommandAsync() + { + if (this.Context.CurrentConnectionConfig.DbType.IsIn(DbType.Oracle, DbType.Dm)) + { + return await DefaultExecuteCommandAsync().ConfigureAwait(false); + } + else + { + return await ValuesExecuteCommandAsync().ConfigureAwait(false); + } + } + public int DefaultExecuteCommand() + { + int result = 0; + var inserable = Inserable as InsertableProvider; + var columns = inserable.InsertBuilder.DbColumnInfoList.GroupBy(it => it.DbColumnName).Select(it => it.Key).Distinct().ToList(); + var tableWithString = inserable.InsertBuilder.TableWithString; + var removeCacheFunc = inserable.RemoveCacheFunc; + var objects = inserable.InsertObjs; + this.Context.Utilities.PageEach(objects, 60, pagelist => + { + if (this.Context.CurrentConnectionConfig.DbType == DbType.Oracle) + this.Context.AddQueue("begin"); + foreach (var item in pagelist) + { + var itemable = this.Context.Insertable(item); + itemable.InsertBuilder.DbColumnInfoList = itemable.InsertBuilder.DbColumnInfoList.Where(it => columns.Contains(it.DbColumnName)).ToList(); + itemable.InsertBuilder.TableWithString = tableWithString; + itemable.InsertBuilder.AsName = Inserable.InsertBuilder.AsName; + (itemable as InsertableProvider).RemoveCacheFunc = removeCacheFunc; + itemable.AddQueue(); + } + if (this.Context.CurrentConnectionConfig.DbType == DbType.Oracle) + this.Context.AddQueue("end \r\n"); + result += this.Context.SaveQueues(false); + }); + //if (this.Context.CurrentConnectionConfig.DbType == DbType.Oracle) + // result=objects.Length; + if (result == -1) + { + result = objects.Length; + } + return result; + } + public async Task DefaultExecuteCommandAsync() + { + int result = 0; + var inserable = Inserable as InsertableProvider; + var columns = inserable.InsertBuilder.DbColumnInfoList.GroupBy(it => it.DbColumnName).Select(it => it.Key).Distinct().ToList(); + var tableWithString = inserable.InsertBuilder.TableWithString; + var removeCacheFunc = inserable.RemoveCacheFunc; + var objects = inserable.InsertObjs; + await Context.Utilities.PageEachAsync(objects, 60, async pagelist => + { + if (Context.CurrentConnectionConfig.DbType == DbType.Oracle) + Context.AddQueue("begin"); + foreach (var item in pagelist) + { + var itemable = Context.Insertable(item); + itemable.InsertBuilder.DbColumnInfoList = itemable.InsertBuilder.DbColumnInfoList.Where(it => columns.Contains(it.DbColumnName)).ToList(); + itemable.InsertBuilder.TableWithString = tableWithString; + itemable.InsertBuilder.AsName = Inserable.InsertBuilder.AsName; + (itemable as InsertableProvider).RemoveCacheFunc = removeCacheFunc; + itemable.AddQueue(); + } + if (Context.CurrentConnectionConfig.DbType == DbType.Oracle) + Context.AddQueue("end"); + result += await Context.SaveQueuesAsync(false).ConfigureAwait(false); + if (Context.CurrentConnectionConfig.DbType == DbType.Oracle) + result = objects.Length; + return result; + }).ConfigureAwait(false); + return result; + } + public int ValuesExecuteCommand() + { + + int result = 0; + var inserable = Inserable as InsertableProvider; + var columns = inserable.InsertBuilder.DbColumnInfoList.GroupBy(it => it.DbColumnName).Select(it => it.Key).Distinct().ToList(); + var tableWithString = inserable.InsertBuilder.AsName; + var removeCacheFunc = inserable.RemoveCacheFunc; + var objects = inserable.InsertObjs; + if (objects == null || objects.Length == 0 || (objects.Length == 1 && objects.First() == null)) + { + return result; + } + var identityList = inserable.EntityInfo.Columns.Where(it => it.IsIdentity).Select(it => it.PropertyName).ToArray(); + if (inserable.IsOffIdentity) + { + identityList = Array.Empty(); + } + var pageSize = 100; + var count = inserable.EntityInfo.Columns.Count; + pageSize = GetPageSize(pageSize, count); + this.Context.Utilities.PageEach(objects, pageSize, pagelist => + { + + StringBuilder batchInsetrSql; + List allParamter = new List(); + GetInsertValues(identityList, columns, tableWithString, removeCacheFunc, pagelist, out batchInsetrSql, allParamter); + result += this.Context.Ado.ExecuteCommand(batchInsetrSql.ToString(), allParamter); + + }); + return result; + + } + + public async Task ValuesExecuteCommandAsync() + { + int result = 0; + var inserable = Inserable as InsertableProvider; + var columns = inserable.InsertBuilder.DbColumnInfoList.GroupBy(it => it.DbColumnName).Select(it => it.Key).Distinct().ToList(); + var tableWithString = inserable.InsertBuilder.AsName; + var removeCacheFunc = inserable.RemoveCacheFunc; + var objects = inserable.InsertObjs; + var identityList = inserable.EntityInfo.Columns.Where(it => it.IsIdentity).Select(it => it.PropertyName).ToArray(); + if (inserable.IsOffIdentity) + { + identityList = Array.Empty(); + } + await Context.Utilities.PageEachAsync(objects, 100, async pagelist => + { + + StringBuilder batchInsetrSql; + List allParamter = new List(); + GetInsertValues(identityList, columns, tableWithString, removeCacheFunc, pagelist, out batchInsetrSql, allParamter); + result += await Context.Ado.ExecuteCommandAsync(batchInsetrSql.ToString(), allParamter).ConfigureAwait(false); + + }).ConfigureAwait(false); + return result; + } + #region Values Helper + + private static int GetPageSize(int pageSize, int count) + { + if (pageSize * count > 2100) + { + pageSize = 50; + } + if (pageSize * count > 2100) + { + pageSize = 20; + } + if (pageSize * count > 2100) + { + pageSize = 10; + } + + return pageSize; + } + private void GetInsertValues(string[] identitys, List columns, string tableWithString, Action removeCacheFunc, List items, out StringBuilder batchInsetrSql, List allParamter) + { + var itemable = this.Context.Insertable(items); + itemable.InsertBuilder.DbColumnInfoList = itemable.InsertBuilder.DbColumnInfoList.Where(it => columns.Contains(it.DbColumnName)).ToList(); + itemable.InsertBuilder.TableWithString = tableWithString; + (itemable as InsertableProvider).RemoveCacheFunc = removeCacheFunc; + batchInsetrSql = new StringBuilder(); + batchInsetrSql.Append("INSERT INTO " + itemable.InsertBuilder.GetTableNameString + " "); + batchInsetrSql.Append('('); + var groupList = itemable.InsertBuilder.DbColumnInfoList.Where(it => !identitys.Contains(it.PropertyName)).GroupBy(it => it.TableId).ToList(); + string columnsString = string.Join(",", groupList.First().Select(it => itemable.InsertBuilder.Builder.GetTranslationColumnName(it.DbColumnName))); + batchInsetrSql.Append(columnsString); + batchInsetrSql.Append(") VALUES"); + string insertColumns = ""; + foreach (var gitem in groupList) + { + batchInsetrSql.Append('('); + insertColumns = string.Join(",", gitem.Select(it => FormatValue(it.PropertyType, it.DbColumnName, it.Value, allParamter, itemable.InsertBuilder.Builder.SqlParameterKeyWord))); + batchInsetrSql.Append(insertColumns); + if (groupList.Last() == gitem) + { + batchInsetrSql.Append(") "); + } + else + { + batchInsetrSql.Append("), "); + } + } + } + private string FormatValue(Type type, string name, object value, List allParamter, string keyword) + { + var result = keyword + name + allParamter.Count; + var addParameter = new SugarParameter(result, value, type); + allParamter.Add(addParameter); + return result; + } + #endregion + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/InsertableProvider/SplitInsertable.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/InsertableProvider/SplitInsertable.cs new file mode 100644 index 000000000..a78b84bb9 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/InsertableProvider/SplitInsertable.cs @@ -0,0 +1,245 @@ +namespace SqlSugar +{ + public class SplitInsertable where T : class, new() + { + private static readonly object SplitLockObj = new object(); + public SqlSugarProvider Context; + internal SplitTableContext Helper; + public EntityInfo EntityInfo; + public SplitType SplitType; + internal IInsertable Inserable { get; set; } + internal List> TableNames { get; set; } + internal bool MySqlIgnore { get; set; } + + public int ExecuteCommand() + { + if (this.Context.Ado.Transaction == null) + { + try + { + this.Context.Ado.BeginTran(); + var result = _ExecuteCommand(); + this.Context.Ado.CommitTran(); + return result; + } + catch (Exception) + { + this.Context.Ado.RollbackTran(); + throw; + } + } + else + { + return _ExecuteCommand(); + } + } + public async Task ExecuteCommandAsync() + { + if (this.Context.Ado.Transaction == null) + { + try + { + this.Context.Ado.BeginTran(); + var result = await _ExecuteCommandAsync().ConfigureAwait(false); + this.Context.Ado.CommitTran(); + return result; + } + catch (Exception) + { + this.Context.Ado.RollbackTran(); + throw; + } + } + else + { + return await _ExecuteCommandAsync().ConfigureAwait(false); + } + } + + + public List ExecuteReturnSnowflakeIdList() + { + if (this.Context.Ado.Transaction == null) + { + try + { + this.Context.Ado.BeginTran(); + var result = _ExecuteReturnSnowflakeIdList(); + this.Context.Ado.CommitTran(); + return result; + } + catch (Exception) + { + this.Context.Ado.RollbackTran(); + throw; + } + } + else + { + return _ExecuteReturnSnowflakeIdList(); + } + } + public async Task> ExecuteReturnSnowflakeIdListAsync() + { + if (this.Context.Ado.Transaction == null) + { + try + { + this.Context.Ado.BeginTran(); + var result = await _ExecuteReturnSnowflakeIdListAsync().ConfigureAwait(false); + this.Context.Ado.CommitTran(); + return result; + } + catch (Exception) + { + this.Context.Ado.RollbackTran(); + throw; + } + } + else + { + return await _ExecuteReturnSnowflakeIdListAsync().ConfigureAwait(false); + } + } + + + public long ExecuteReturnSnowflakeId() + { + return ExecuteReturnSnowflakeIdList().FirstOrDefault(); + } + public async Task ExecuteReturnSnowflakeIdAsync() + { + var list = await ExecuteReturnSnowflakeIdListAsync().ConfigureAwait(false); + return list.FirstOrDefault(); + } + + + internal int _ExecuteCommand() + { + CreateTable(); + var result = 0; + var groups = TableNames.GroupBy(it => it.Key).ToList(); + var parent = ((InsertableProvider)Inserable); + var names = parent.InsertBuilder.DbColumnInfoList.GroupBy(it => it.DbColumnName).Select(i => i.Key).ToList(); + foreach (var item in groups) + { + var list = item.Select(it => it.Value as T).ToList(); + var dataEvent = this.Context.CurrentConnectionConfig.AopEvents?.DataExecuting; + this.Context.Aop.DataExecuting = null; + var groupInserable = (InsertableProvider)this.Context.Insertable(list); + this.Context.Aop.DataExecuting = dataEvent; + groupInserable.InsertBuilder.TableWithString = parent.InsertBuilder.TableWithString; + groupInserable.RemoveCacheFunc = parent.RemoveCacheFunc; + groupInserable.diffModel = parent.diffModel; + groupInserable.IsEnableDiffLogEvent = parent.IsEnableDiffLogEvent; + groupInserable.InsertBuilder.IsNoInsertNull = parent.InsertBuilder.IsNoInsertNull; + groupInserable.IsOffIdentity = parent.IsOffIdentity; + groupInserable.InsertBuilder.MySqlIgnore = this.MySqlIgnore; + result += groupInserable.AS(item.Key).InsertColumns(names.ToArray()).ExecuteCommand(); + } + return result; + } + internal async Task _ExecuteCommandAsync() + { + CreateTable(); + var result = 0; + var groups = TableNames.GroupBy(it => it.Key).ToList(); + var parent = ((InsertableProvider)Inserable); + var names = parent.InsertBuilder.DbColumnInfoList.GroupBy(it => it.DbColumnName).Select(i => i.Key).ToList(); + foreach (var item in groups) + { + var list = item.Select(it => it.Value as T).ToList(); + var dataEvent = this.Context.CurrentConnectionConfig.AopEvents?.DataExecuting; + this.Context.Aop.DataExecuting = null; + var groupInserable = (InsertableProvider)this.Context.Insertable(list); + this.Context.Aop.DataExecuting = dataEvent; + groupInserable.InsertBuilder.TableWithString = parent.InsertBuilder.TableWithString; + groupInserable.RemoveCacheFunc = parent.RemoveCacheFunc; + groupInserable.diffModel = parent.diffModel; + groupInserable.IsEnableDiffLogEvent = parent.IsEnableDiffLogEvent; + groupInserable.InsertBuilder.IsNoInsertNull = parent.InsertBuilder.IsNoInsertNull; + groupInserable.IsOffIdentity = parent.IsOffIdentity; + groupInserable.InsertBuilder.MySqlIgnore = this.MySqlIgnore; + result += await groupInserable.AS(item.Key).InsertColumns(names.ToArray()).ExecuteCommandAsync().ConfigureAwait(false); + } + return result; + } + + internal List _ExecuteReturnSnowflakeIdList() + { + CreateTable(); + var result = new List(); + var groups = TableNames.GroupBy(it => it.Key).ToList(); + var parent = ((InsertableProvider)Inserable); + var names = parent.InsertBuilder.DbColumnInfoList.GroupBy(it => it.DbColumnName).Select(i => i.Key).ToList(); + foreach (var item in groups) + { + var list = item.Select(it => it.Value as T).ToList(); + var groupInserable = (InsertableProvider)this.Context.Insertable(list); + groupInserable.InsertBuilder.TableWithString = parent.InsertBuilder.TableWithString; + groupInserable.RemoveCacheFunc = parent.RemoveCacheFunc; + groupInserable.diffModel = parent.diffModel; + groupInserable.IsEnableDiffLogEvent = parent.IsEnableDiffLogEvent; + groupInserable.InsertBuilder.IsNoInsertNull = parent.InsertBuilder.IsNoInsertNull; + groupInserable.IsOffIdentity = parent.IsOffIdentity; + groupInserable.InsertBuilder.MySqlIgnore = this.MySqlIgnore; + var idList = groupInserable.AS(item.Key).InsertColumns(names.ToArray()).ExecuteReturnSnowflakeIdList(); + result.AddRange(idList); + } + return result; + } + internal async Task> _ExecuteReturnSnowflakeIdListAsync() + { + CreateTable(); + var result = new List(); + var groups = TableNames.GroupBy(it => it.Key).ToList(); + var parent = ((InsertableProvider)Inserable); + var names = parent.InsertBuilder.DbColumnInfoList.GroupBy(it => it.DbColumnName).Select(i => i.Key).ToList(); + foreach (var item in groups) + { + var list = item.Select(it => it.Value as T).ToList(); + var groupInserable = (InsertableProvider)this.Context.Insertable(list); + groupInserable.InsertBuilder.TableWithString = parent.InsertBuilder.TableWithString; + groupInserable.RemoveCacheFunc = parent.RemoveCacheFunc; + groupInserable.diffModel = parent.diffModel; + groupInserable.IsEnableDiffLogEvent = parent.IsEnableDiffLogEvent; + groupInserable.InsertBuilder.IsNoInsertNull = parent.InsertBuilder.IsNoInsertNull; + groupInserable.IsOffIdentity = parent.IsOffIdentity; + groupInserable.InsertBuilder.MySqlIgnore = this.MySqlIgnore; + var idList = await groupInserable.AS(item.Key).InsertColumns(names.ToArray()).ExecuteReturnSnowflakeIdListAsync().ConfigureAwait(false); + result.AddRange(idList); + } + return result; + } + + + private void CreateTable() + { + var isLog = this.Context.Ado.IsEnableLogEvent; + this.Context.Ado.IsEnableLogEvent = false; + foreach (var item in TableNames.GroupBy(it => it.Key).Select(it => it).ToDictionary(it => it.Key, it => it.First().Value)) + { + var newDb = this.Context.CopyNew(); + newDb.CurrentConnectionConfig.IsAutoCloseConnection = true; + if (!newDb.DbMaintenance.IsAnyTable(item.Key, false)) + { + lock (SplitLockObj) + { + var newDb2 = this.Context.CopyNew(); + newDb2.CurrentConnectionConfig.IsAutoCloseConnection = true; + if (!newDb2.DbMaintenance.IsAnyTable(item.Key, false)) + { + if (item.Value != null) + { + this.Context.MappingTables.Add(EntityInfo.EntityName, item.Key); + this.Context.CodeFirst.InitTables(); + } + } + } + } + } + this.Context.Ado.IsEnableLogEvent = isLog; + this.Context.MappingTables.Add(EntityInfo.EntityName, EntityInfo.DbTableName); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/InsertableProvider/SubInserable.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/InsertableProvider/SubInserable.cs new file mode 100644 index 000000000..197ed9cfb --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/InsertableProvider/SubInserable.cs @@ -0,0 +1,323 @@ +using System.Data.SqlTypes; +using System.Linq.Expressions; + +namespace SqlSugar +{ + public class SubInsertable : ISubInsertable where T : class, new() + { + internal EntityInfo Entity { get; set; } + internal List SubList { get; set; } + internal SqlSugarProvider Context { get; set; } + internal T[] InsertObjects { get; set; } + internal InsertBuilder InsertBuilder { get; set; } + internal string Pk { get; set; } + + public ISubInsertable AddSubList(Expression> items) + { + if (this.SubList == null) + this.SubList = new List(); + this.SubList.Add(new SubInsertTreeExpression() { Expression = items }); + return this; + } + public ISubInsertable AddSubList(Expression> tree) + { + try + { + var lamda = (tree as LambdaExpression); + var memInit = lamda.Body as MemberInitExpression; + if (memInit.Bindings != null) + { + + MemberAssignment memberAssignment = (MemberAssignment)memInit.Bindings[0]; + SubList.Add(new SubInsertTreeExpression() + { + Expression = memberAssignment.Expression, + Childs = GetSubInsertTree(((MemberAssignment)memInit.Bindings[1]).Expression) + }); + } + } + catch + { + Check.Exception(true, tree.ToString() + " format error "); + } + return this; + } + + private List GetSubInsertTree(Expression expression) + { + List resul = new List(); + + if (expression is ListInitExpression) + { + ListInitExpression exp = expression as ListInitExpression; + foreach (var item in exp.Initializers) + { + SubInsertTreeExpression tree = new SubInsertTreeExpression(); + var memInit = item.Arguments[0] as MemberInitExpression; + if (memInit.Bindings != null) + { + MemberAssignment memberAssignment = (MemberAssignment)memInit.Bindings[0]; + tree.Expression = memberAssignment.Expression; + if (memInit.Bindings.Count > 1) + { + tree.Childs = GetSubInsertTree(((MemberAssignment)memInit.Bindings[1]).Expression); + } + } + resul.Add(tree); + } + } + else + { + + } + return resul; + } + + [Obsolete("use ExecuteCommand")] + public object ExecuteReturnPrimaryKey() + { + return ExecuteCommand(); + } + + public async Task ExecuteCommandAsync() + { + object resut = 0; + await Task.Run(() => + { + resut = ExecuteCommand(); + }).ConfigureAwait(false); + return resut; + } + public object ExecuteCommand() + { + var isNoTrean = this.Context.Ado.Transaction == null; + try + { + if (isNoTrean) + this.Context.Ado.BeginTran(); + + var result = Execute(); + + if (isNoTrean) + this.Context.Ado.CommitTran(); + return result; + } + catch (Exception) + { + if (isNoTrean) + this.Context.Ado.RollbackTran(); + throw; + } + } + + private int Execute() + { + if (InsertObjects?.Length > 0) + { + var isIdEntity = IsIdEntity(this.Entity); + if (!isIdEntity) + { + this.Context.Insertable(InsertObjects).ExecuteCommand(); + } + foreach (var InsertObject in InsertObjects) + { + int id = 0; + if (isIdEntity) + { + id = this.Context.Insertable(InsertObject).ExecuteReturnIdentity(); + this.Entity.Columns.First(it => it.IsIdentity || it.OracleSequenceName.HasValue()).PropertyInfo.SetValue(InsertObject, id); + } + var pk = GetPrimaryKey(this.Entity, InsertObject, id); + AddChildList(this.SubList, InsertObject, pk); + } + return InsertObjects.Length; + } + else + { + return 0; + } + } + [Obsolete("use ExecuteCommandAsync")] + public Task ExecuteReturnPrimaryKeyAsync() + { + return Task.FromResult(ExecuteReturnPrimaryKey()); + } + + private bool IsIdEntity(EntityInfo entity) + { + return entity.Columns.Where(it => it.IsIdentity || it.OracleSequenceName.HasValue()).Any(); + } + + private void AddChildList(List items, object insertObject, object pkValue) + { + if (items != null) + { + foreach (var item in items) + { + MemberExpression subMemberException; + string subMemberName = GetMemberName(item, out subMemberException); + string childName = GetChildName(item, subMemberException); + var childListProperty = insertObject.GetType().GetProperty(childName); + if (childListProperty == null) + { + childName = subMemberName; + childListProperty = insertObject.GetType().GetProperty(childName); + } + var childList = childListProperty.GetValue(insertObject); + if (childList != null) + { + if (!(childList is IEnumerable)) + { + childList = new List() { childList }; + } + if (!string.IsNullOrEmpty(subMemberName) && subMemberName != childName) + { + foreach (var child in childList as IEnumerable) + { + child.GetType().GetProperty(subMemberName).SetValue(child, pkValue); + } + } + if (!(childList as IEnumerable).Any()) + { + continue; + } + var type = (childList as IEnumerable).First().GetType(); + this.Context.InitMappingInfo(type); + var entityInfo = this.Context.EntityMaintenance.GetEntityInfo(type); + var isIdentity = IsIdEntity(entityInfo); + var tableName = entityInfo.DbTableName; + List> insertList = new List>(); + var entityList = (childList as IEnumerable).ToList(); + foreach (var child in entityList) + { + insertList.Add(GetInsertDictionary(child, entityInfo)); + } + if (!isIdentity) + { + this.Context.Insertable(insertList).AS(tableName).ExecuteCommand(); + } + int i = 0; + foreach (var insert in insertList) + { + int id = 0; + if (isIdentity) + { + if (this.Context.CurrentConnectionConfig.DbType == DbType.PostgreSQL) + { + var sqlobj = this.Context.Insertable(insert).AS(tableName).ToSql(); + id = this.Context.Ado.GetInt(sqlobj.Key + " returning " + this.InsertBuilder.Builder.GetTranslationColumnName(entityInfo.Columns.First(it => isIdentity).DbColumnName), sqlobj.Value); + } + else + { + id = this.Context.Insertable(insert).AS(tableName).ExecuteReturnIdentity(); + } + if (this.Context.CurrentConnectionConfig.DbType == DbType.Oracle && id == 0) + { + var seqName = entityInfo.Columns.First(it => it.OracleSequenceName.HasValue())?.OracleSequenceName; + id = this.Context.Ado.GetInt("select " + seqName + ".currval from dual"); + } + } + var entity = entityList[i]; + var pk = GetPrimaryKey(entityInfo, entity, id); + AddChildList(item.Childs, entity, pk); + ++i; + } + } + } + } + } + private Dictionary GetInsertDictionary(object insetObject, EntityInfo subEntity) + { + Dictionary insertDictionary = new Dictionary(); + foreach (var item in subEntity.Columns) + { + if (item.IsIdentity || item.IsIgnore) + { + + } + else if (!string.IsNullOrEmpty(item.OracleSequenceName) && this.Context.CurrentConnectionConfig.DbType == DbType.Oracle) + { + var value = "{SugarSeq:=}" + item.OracleSequenceName + ".nextval{SugarSeq:=}"; + insertDictionary.Add(item.DbColumnName, value); + continue; + } + else + { + var value = item.PropertyInfo.GetValue(insetObject); + if (value == null && this.Context.CurrentConnectionConfig.DbType == DbType.PostgreSQL) + { + var underType = UtilMethods.GetUnderType(item.PropertyInfo.PropertyType); + if (underType == UtilConstants.DateType) + { + value = SqlDateTime.Null; + } + } + insertDictionary.Add(item.DbColumnName, value); + } + } + return insertDictionary; + } + private static string GetChildName(SubInsertTreeExpression item, MemberExpression subMemberException) + { + string childName; + MemberExpression listMember = null; + if (subMemberException.Expression is MethodCallExpression) + { + listMember = (subMemberException.Expression as MethodCallExpression).Arguments.First() as MemberExpression; + + } + else + { + listMember = (subMemberException.Expression as MemberExpression); + } + if (listMember == null && item.Expression is LambdaExpression) + { + listMember = (item.Expression as LambdaExpression).Body as MemberExpression; + } + if (listMember == null && item.Expression is MemberExpression) + { + listMember = item.Expression as MemberExpression; + } + childName = listMember.Member.Name; + return childName; + } + + private static string GetMemberName(SubInsertTreeExpression item, out MemberExpression subMemberException) + { + string subMemberName = null; + Expression lambdaExpression; + if (item.Expression is LambdaExpression) + { + lambdaExpression = (item.Expression as LambdaExpression).Body; + } + else + { + lambdaExpression = item.Expression; + } + if (lambdaExpression is UnaryExpression) + { + lambdaExpression = (lambdaExpression as UnaryExpression).Operand; + } + subMemberException = lambdaExpression as MemberExpression; + subMemberName = subMemberException.Member.Name; + return subMemberName; + } + + private object GetPrimaryKey(EntityInfo entityInfo, object InsertObject, int id) + { + object pkValue; + if (id.ObjToInt() == 0) + { + var primaryProperty = entityInfo.Columns.FirstOrDefault(it => it.IsPrimarykey); + Check.Exception(primaryProperty == null, entityInfo.EntityName + " no primarykey"); + pkValue = primaryProperty.PropertyInfo.GetValue(InsertObject); + } + else + { + pkValue = id; + } + + return pkValue; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/Entities/AppendNavInfoList.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/Entities/AppendNavInfoList.cs new file mode 100644 index 000000000..5c368d523 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/Entities/AppendNavInfoList.cs @@ -0,0 +1,21 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + internal class AppendNavInfo + { + public Dictionary MappingNavProperties { get; set; } = new Dictionary(); + public Dictionary AppendProperties { get; set; } = new Dictionary(); + public List Result { get; set; } = new List(); + } + internal class AppendNavResult + { + public Dictionary result = new Dictionary(); + } + internal class MappingNavColumnInfo + { + public List ExpressionList { get; set; } + public string Name { get; set; } + public string ParentName { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/Entities/QueryableAppendColumn.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/Entities/QueryableAppendColumn.cs new file mode 100644 index 000000000..7dd386870 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/Entities/QueryableAppendColumn.cs @@ -0,0 +1,10 @@ +namespace SqlSugar +{ + internal class QueryableAppendColumn + { + public string AsName { get; set; } + public int Index { get; set; } + public object Value { get; set; } + public string Name { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/Entities/QueryableFormat.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/Entities/QueryableFormat.cs new file mode 100644 index 000000000..f0b6a5a98 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/Entities/QueryableFormat.cs @@ -0,0 +1,14 @@ +using System.Reflection; + +namespace SqlSugar +{ + internal class QueryableFormat + { + public Type Type { get; set; } + public string TypeString { get; set; } + public string Format { get; set; } + public string PropertyName { get; set; } + public string MethodName { get; set; } + public MethodInfo MethodInfo { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/Entities/SqlInfo.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/Entities/SqlInfo.cs new file mode 100644 index 000000000..35c4e561d --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/Entities/SqlInfo.cs @@ -0,0 +1,17 @@ +namespace SqlSugar +{ + + internal class SqlInfo + { + public bool IsSelectNav { get; set; } + public int? Take { get; set; } + public int? Skip { get; set; } + public string WhereString { get; set; } + public string OrderByString { get; set; } + public string SelectString { get; set; } + public List Parameters { get; set; } + public List MappingExpressions { get; set; } + public string TableShortName { get; set; } + public Func, IEnumerable> SplitTable { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/Entities/SubQueryToListDefaultT.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/Entities/SubQueryToListDefaultT.cs new file mode 100644 index 000000000..191c5e490 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/Entities/SubQueryToListDefaultT.cs @@ -0,0 +1,8 @@ +namespace SqlSugar +{ + internal class SubQueryToListDefaultT + { + public object id { get; set; } + public object sugarIndex { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/Includes.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/Includes.cs new file mode 100644 index 000000000..7eb44e596 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/Includes.cs @@ -0,0 +1,501 @@ +using System.Data; +using System.Linq.Expressions; +using System.Reflection; + +namespace SqlSugar +{ + public partial class QueryableProvider : QueryableAccessory, ISugarQueryable + { + public ISugarQueryable IncludesByExpression2(Expression include1, Expression include2) + { + _Includes(this.Context, include1, include2); + return this; + } + public ISugarQueryable IncludesByExpression3(Expression include1, Expression include2, Expression include3) + { + _Includes(this.Context, include1, include2, include3); + return this; + } + public ISugarQueryable IncludesByExpression4(Expression include1, Expression include2, Expression include3, Expression include4) + { + _Includes(this.Context, include1, include2, include3, include4); + return this; + } + public ISugarQueryable IncludesByExpression5(Expression include1, Expression include2, Expression include3, Expression include4, Expression include5) + { + _Includes(this.Context, include1, include2, include3, include4, include5); + return this; + } + public ISugarQueryable IncludesByExpression(Expression include1) + { + _Includes(this.Context, include1); + return this; + } + public ISugarQueryable IncludesByNameString(string navMemberName, string thenNavMemberName2) + { + var method = this.GetType().GetMethods().Where(it => it.Name == "IncludesByExpression2") + .First(); + List parametres = new List(); + List types = new List(); + var entityInfo = this.EntityInfo; + method = GetIncludesByNameStringMethod(types, navMemberName, method, parametres, entityInfo); + //var navFirst = GetNavColumnInfo(navMemberName, entityInfo); + var entityInfo2 = this.Context.EntityMaintenance.GetEntityInfo(types.Last()); + method = GetIncludesByNameStringMethod(types, thenNavMemberName2, method, parametres, entityInfo2); + method.MakeGenericMethod(types.ToArray()).Invoke(this, parametres.Cast().ToArray()); + return this; + } + public ISugarQueryable IncludesByNameString(string navMemberName, string thenNavMemberName2, string thenNavMemberName3) + { + var method = this.GetType().GetMethods().Where(it => it.Name == "IncludesByExpression3") + .First(); + List parametres = new List(); + List types = new List(); + var entityInfo = this.EntityInfo; + method = GetIncludesByNameStringMethod(types, navMemberName, method, parametres, entityInfo); + //var navFirst = GetNavColumnInfo(navMemberName, entityInfo); + var entityInfo2 = this.Context.EntityMaintenance.GetEntityInfo(types.Last()); + method = GetIncludesByNameStringMethod(types, thenNavMemberName2, method, parametres, entityInfo2); + var entityInfo3 = this.Context.EntityMaintenance.GetEntityInfo(types.Last()); + method = GetIncludesByNameStringMethod(types, thenNavMemberName3, method, parametres, entityInfo3); + method.MakeGenericMethod(types.ToArray()).Invoke(this, parametres.Cast().ToArray()); + return this; + } + public ISugarQueryable IncludesByNameString(string navMemberName, string thenNavMemberName2, string thenNavMemberName3, string thenNavMemberName4) + { + var method = this.GetType().GetMethods().Where(it => it.Name == "IncludesByExpression4") + .First(); + List parametres = new List(); + List types = new List(); + var entityInfo = this.EntityInfo; + method = GetIncludesByNameStringMethod(types, navMemberName, method, parametres, entityInfo); + //var navFirst = GetNavColumnInfo(navMemberName, entityInfo); + var entityInfo2 = this.Context.EntityMaintenance.GetEntityInfo(types.Last()); + method = GetIncludesByNameStringMethod(types, thenNavMemberName2, method, parametres, entityInfo2); + var entityInfo3 = this.Context.EntityMaintenance.GetEntityInfo(types.Last()); + method = GetIncludesByNameStringMethod(types, thenNavMemberName3, method, parametres, entityInfo3); + var entityInfo4 = this.Context.EntityMaintenance.GetEntityInfo(types.Last()); + method = GetIncludesByNameStringMethod(types, thenNavMemberName4, method, parametres, entityInfo4); + method.MakeGenericMethod(types.ToArray()).Invoke(this, parametres.Cast().ToArray()); + return this; + } + public ISugarQueryable IncludesByNameString(string navMemberName, string thenNavMemberName2, string thenNavMemberName3, string thenNavMemberName4, string thenNavMemberName5) + { + var method = this.GetType().GetMethods().Where(it => it.Name == "IncludesByExpression5") + .First(); + List parametres = new List(); + List types = new List(); + var entityInfo = this.EntityInfo; + method = GetIncludesByNameStringMethod(types, navMemberName, method, parametres, entityInfo); + //var navFirst = GetNavColumnInfo(navMemberName, entityInfo); + var entityInfo2 = this.Context.EntityMaintenance.GetEntityInfo(types.Last()); + method = GetIncludesByNameStringMethod(types, thenNavMemberName2, method, parametres, entityInfo2); + var entityInfo3 = this.Context.EntityMaintenance.GetEntityInfo(types.Last()); + method = GetIncludesByNameStringMethod(types, thenNavMemberName3, method, parametres, entityInfo3); + var entityInfo4 = this.Context.EntityMaintenance.GetEntityInfo(types.Last()); + method = GetIncludesByNameStringMethod(types, thenNavMemberName4, method, parametres, entityInfo4); + var entityInfo5 = this.Context.EntityMaintenance.GetEntityInfo(types.Last()); + method = GetIncludesByNameStringMethod(types, thenNavMemberName5, method, parametres, entityInfo5); + method.MakeGenericMethod(types.ToArray()).Invoke(this, parametres.Cast().ToArray()); + return this; + } + private static MethodInfo GetIncludesByNameStringMethod(List types, string navMemberName, MethodInfo method, List parametres, EntityInfo entityInfo) + { + var navFirst = GetNavColumnInfo(navMemberName, entityInfo); + parametres.AddRange(GetIncludesByNameStringParameters(entityInfo.Type, navFirst)); + if (navFirst.PropertyInfo.PropertyType.FullName.IsCollectionsList()) + { + types.Add(navFirst.PropertyInfo.PropertyType.GetGenericArguments()[0]); + } + else + { + types.Add(navFirst.PropertyInfo.PropertyType); + } + return method; + } + + private static EntityColumnInfo GetNavColumnInfo(string navMemberName, EntityInfo entityInfo) + { + return entityInfo.Columns.Where(it => it.Navigat != null && it.PropertyName.EqualCase(navMemberName)).FirstOrDefault(); + } + + private static List GetIncludesByNameStringParameters(Type type, EntityColumnInfo item) + { + var parametres = new List { }; + Check.ExceptionEasy(item == null, "\r\nThe \"IncludesByNameString\" method encountered an error. The navigation object does not exist. Please check the parameters and navigation configuration.", "IncludesByNameString方法出错,导航对象不存在,请检查参数和导航配置"); + var properyType = item.PropertyInfo.PropertyType; + var properyItemType = properyType; + if (properyType.FullName.IsCollectionsList()) + { + properyItemType = properyType.GetGenericArguments()[0]; + } + var exp = ExpressionBuilderHelper.CreateExpressionSelectField(type, item.PropertyName, properyType); + parametres.Add(exp); + return parametres; + } + + public ISugarQueryable IncludesByNameString(string navMemberName) + { + var navs = this.EntityInfo.Columns.Where(it => it.Navigat != null && it.PropertyName.EqualCase(navMemberName)).ToList(); + foreach (var item in navs) + { + var properyType = item.PropertyInfo.PropertyType; + var properyItemType = properyType; + if (properyType.FullName.IsCollectionsList()) + { + properyItemType = properyType.GetGenericArguments()[0]; + } + var exp = ExpressionBuilderHelper.CreateExpressionSelectField(typeof(T), item.PropertyName, properyType); + var method = this.GetType().GetMethods().Where(it => it.Name == "IncludesByExpression") + .First() + .MakeGenericMethod(properyItemType); + method.Invoke(this, new object[] { exp }); + } + return this; + } + public ISugarQueryable IncludesAllFirstLayer(params string[] ignoreProperyNameList) + { + var navs = this.EntityInfo.Columns.Where(it => it.Navigat != null).ToList(); + foreach (var item in navs) + { + if (ignoreProperyNameList?.Any(z => z.EqualCase(item.PropertyName)) == true) + { + //future + } + else + { + var properyType = item.PropertyInfo.PropertyType; + var properyItemType = properyType; + if (properyType.FullName.IsCollectionsList()) + { + properyItemType = properyType.GetGenericArguments()[0]; + } + var shortName = "it"; + if (this.QueryBuilder.TableShortName.HasValue()) + { + shortName = this.QueryBuilder.TableShortName; + } + var exp = ExpressionBuilderHelper.CreateExpressionSelectField(typeof(T), item.PropertyName, properyType, shortName); + var method = this.GetType().GetMethods().Where(it => it.Name == "IncludesByExpression") + .First() + .MakeGenericMethod(properyItemType); + method.Invoke(this, new object[] { exp }); + } + } + return this; + } + public ISugarQueryable IncludesAllSecondLayer(Expression> expression, params string[] ignoreProperyNameList) + { + this.Includes(expression); + var type = typeof(TReturn1); + if (type.FullName.IsCollectionsList()) + { + type = type.GetGenericArguments()[0]; + } + var navs = this.Context.EntityMaintenance.GetEntityInfo(type).Columns.Where(it => it.Navigat != null).ToList(); + foreach (var item in navs) + { + if (ignoreProperyNameList?.Any(z => z.EqualCase(item.PropertyName)) == true) + { + //future + } + else + { + var properyType = item.PropertyInfo.PropertyType; + var properyItemType = properyType; + if (properyType.FullName.IsCollectionsList()) + { + properyItemType = properyType.GetGenericArguments()[0]; + } + var exp = ExpressionBuilderHelper.CreateExpressionSelectField(type, item.PropertyName, properyType); + var method = this.GetType().GetMethods().Where(it => it.Name == "IncludesByExpression2") + .First() + .MakeGenericMethod(type, properyItemType); + method.Invoke(this, new object[] { expression, exp }); + } + } + return this; + } + public ISugarQueryable Includes(Expression>> include1) + { + var result = GetManyQueryable(include1); + if (result != null) + { + return result; + } + _Includes(this.Context, include1); + return this; + } + public ISugarQueryable Includes(Expression> include1) + { + var result = GetManyQueryable(include1); + if (result != null) + { + return result; + } + _Includes(this.Context, include1); + return this; + } + public ISugarQueryable Includes(Expression> include1, Expression>> include2) { _Includes(this.Context, include1, include2); return this; } + public ISugarQueryable Includes(Expression> include1, Expression> include2) { _Includes(this.Context, include1, include2); return this; } + public ISugarQueryable Includes(Expression>> include1, Expression>> include2) { _Includes(this.Context, include1, include2); return this; } + public ISugarQueryable Includes(Expression>> include1, Expression> include2) { _Includes(this.Context, include1, include2); return this; } + public NavISugarQueryable Includes(Expression>> include1, Expression>> include2, Expression>> include3) { _Includes(this.Context, include1, include2, include3); return GetNavSugarQueryable(); } + public NavISugarQueryable Includes(Expression>> include1, Expression> include2, Expression>> include3) { _Includes(this.Context, include1, include2, include3); return GetNavSugarQueryable(); } + public NavISugarQueryable Includes(Expression>> include1, Expression>> include2, Expression> include3) { _Includes(this.Context, include1, include2, include3); return GetNavSugarQueryable(); } + public NavISugarQueryable Includes(Expression> include1, Expression>> include2, Expression>> include3) { _Includes(this.Context, include1, include2, include3); return GetNavSugarQueryable(); } + public NavISugarQueryable Includes(Expression> include1, Expression> include2, Expression> include3) { _Includes(this.Context, include1, include2, include3); return GetNavSugarQueryable(); } + public NavISugarQueryable Includes(Expression> include1, Expression> include2, Expression>> include3) { _Includes(this.Context, include1, include2, include3); return GetNavSugarQueryable(); } + public NavISugarQueryable Includes(Expression> include1, Expression>> include2, Expression> include3) { _Includes(this.Context, include1, include2, include3); return GetNavSugarQueryable(); } + public NavISugarQueryable Includes(Expression>> include1, Expression> include2, Expression> include3) { _Includes(this.Context, include1, include2, include3); return GetNavSugarQueryable(); } + + + } + + public partial class NavQueryableProvider : QueryableProvider, NavISugarQueryable + { + + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression> include4) { _Includes(this.Context, include1, include2, include3, include4); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression> include4) { _Includes(this.Context, include1, include2, include3, include4); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression>> include4) { _Includes(this.Context, include1, include2, include3, include4); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression> include4) { _Includes(this.Context, include1, include2, include3, include4); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression>> include4) { _Includes(this.Context, include1, include2, include3, include4); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression> include4) { _Includes(this.Context, include1, include2, include3, include4); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression> include4) { _Includes(this.Context, include1, include2, include3, include4); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression>> include4) { _Includes(this.Context, include1, include2, include3, include4); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression> include4) { _Includes(this.Context, include1, include2, include3, include4); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression> include4) { _Includes(this.Context, include1, include2, include3, include4); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression>> include4) { _Includes(this.Context, include1, include2, include3, include4); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression> include4) { _Includes(this.Context, include1, include2, include3, include4); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression>> include4) { _Includes(this.Context, include1, include2, include3, include4); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression>> include4) { _Includes(this.Context, include1, include2, include3, include4); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression>> include4) { _Includes(this.Context, include1, include2, include3, include4); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression>> include4) { _Includes(this.Context, include1, include2, include3, include4); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression> include4, Expression> include5) { _Includes(this.Context, include1, include2, include3, include4, include5); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression>> include4, Expression> include5) { _Includes(this.Context, include1, include2, include3, include4, include5); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression>> include4, Expression>> include5) { _Includes(this.Context, include1, include2, include3, include4, include5); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression> include4, Expression>> include5) { _Includes(this.Context, include1, include2, include3, include4, include5); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression> include4, Expression>> include5) { _Includes(this.Context, include1, include2, include3, include4, include5); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression>> include4, Expression> include5) { _Includes(this.Context, include1, include2, include3, include4, include5); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression> include4, Expression>> include5) { _Includes(this.Context, include1, include2, include3, include4, include5); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression> include4, Expression> include5) { _Includes(this.Context, include1, include2, include3, include4, include5); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression>> include4, Expression>> include5) { _Includes(this.Context, include1, include2, include3, include4, include5); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression> include4, Expression> include5) { _Includes(this.Context, include1, include2, include3, include4, include5); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression> include4, Expression> include5) { _Includes(this.Context, include1, include2, include3, include4, include5); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression>> include4, Expression> include5) { _Includes(this.Context, include1, include2, include3, include4, include5); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression>> include4, Expression>> include5) { _Includes(this.Context, include1, include2, include3, include4, include5); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression> include4, Expression>> include5) { _Includes(this.Context, include1, include2, include3, include4, include5); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression> include4, Expression>> include5) { _Includes(this.Context, include1, include2, include3, include4, include5); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression> include4, Expression>> include5) { _Includes(this.Context, include1, include2, include3, include4, include5); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression>> include4, Expression> include5) { _Includes(this.Context, include1, include2, include3, include4, include5); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression> include4, Expression> include5) { _Includes(this.Context, include1, include2, include3, include4, include5); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression> include4, Expression>> include5) { _Includes(this.Context, include1, include2, include3, include4, include5); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression> include4, Expression>> include5) { _Includes(this.Context, include1, include2, include3, include4, include5); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression>> include4, Expression>> include5) { _Includes(this.Context, include1, include2, include3, include4, include5); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression> include4, Expression> include5) { _Includes(this.Context, include1, include2, include3, include4, include5); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression> include4, Expression> include5) { _Includes(this.Context, include1, include2, include3, include4, include5); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression>> include4, Expression>> include5) { _Includes(this.Context, include1, include2, include3, include4, include5); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression>> include4, Expression> include5) { _Includes(this.Context, include1, include2, include3, include4, include5); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression>> include4, Expression>> include5) { _Includes(this.Context, include1, include2, include3, include4, include5); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression>> include4, Expression> include5) { _Includes(this.Context, include1, include2, include3, include4, include5); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression>> include4, Expression>> include5) { _Includes(this.Context, include1, include2, include3, include4, include5); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression>> include4, Expression> include5) { _Includes(this.Context, include1, include2, include3, include4, include5); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression>> include4, Expression>> include5) { _Includes(this.Context, include1, include2, include3, include4, include5); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression>> include4, Expression> include5) { _Includes(this.Context, include1, include2, include3, include4, include5); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression> include4, Expression> include5) { _Includes(this.Context, include1, include2, include3, include4, include5); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression>> include4, Expression> include5, Expression> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression> include4, Expression>> include5, Expression> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression> include4, Expression> include5, Expression>> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression>> include4, Expression> include5, Expression>> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression>> include4, Expression> include5, Expression>> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression> include4, Expression>> include5, Expression>> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression> include4, Expression> include5, Expression> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression>> include4, Expression> include5, Expression>> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression> include4, Expression>> include5, Expression> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression>> include4, Expression> include5, Expression> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression> include4, Expression> include5, Expression> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression>> include4, Expression>> include5, Expression>> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression> include4, Expression> include5, Expression>> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression> include4, Expression> include5, Expression>> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression>> include4, Expression>> include5, Expression>> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression> include4, Expression>> include5, Expression> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression>> include4, Expression>> include5, Expression> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression>> include4, Expression> include5, Expression> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression> include4, Expression>> include5, Expression> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression>> include4, Expression> include5, Expression>> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression> include4, Expression> include5, Expression> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression> include4, Expression>> include5, Expression>> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression>> include4, Expression> include5, Expression>> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression>> include4, Expression>> include5, Expression>> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression>> include4, Expression>> include5, Expression>> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression>> include4, Expression>> include5, Expression>> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression> include4, Expression> include5, Expression> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression>> include4, Expression> include5, Expression>> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression>> include4, Expression>> include5, Expression> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression>> include4, Expression>> include5, Expression>> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression>> include4, Expression>> include5, Expression> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression>> include4, Expression>> include5, Expression>> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression> include4, Expression>> include5, Expression> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression>> include4, Expression>> include5, Expression> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression>> include4, Expression> include5, Expression>> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression> include4, Expression>> include5, Expression>> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression> include4, Expression> include5, Expression> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression>> include4, Expression> include5, Expression> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression>> include4, Expression> include5, Expression> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression>> include4, Expression> include5, Expression> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression> include4, Expression>> include5, Expression>> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression> include4, Expression>> include5, Expression>> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression>> include4, Expression>> include5, Expression>> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression> include4, Expression> include5, Expression>> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression>> include4, Expression>> include5, Expression> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression> include4, Expression> include5, Expression> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression>> include4, Expression>> include5, Expression> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression> include4, Expression>> include5, Expression>> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression>> include4, Expression>> include5, Expression> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression> include4, Expression>> include5, Expression>> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression>> include4, Expression> include5, Expression>> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression> include4, Expression>> include5, Expression>> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression> include4, Expression>> include5, Expression> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression> include4, Expression>> include5, Expression> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression>> include4, Expression>> include5, Expression> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression> include4, Expression>> include5, Expression> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression>> include4, Expression> include5, Expression> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression>> include4, Expression> include5, Expression> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression> include4, Expression> include5, Expression> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression> include4, Expression> include5, Expression>> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression> include4, Expression> include5, Expression>> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression> include4, Expression> include5, Expression>> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression> include4, Expression> include5, Expression> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression> include4, Expression> include5, Expression>> include6) { _Includes(this.Context, include1, include2, include3, include4, include5, include6); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression>> include4, Expression> include5, Expression> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression> include4, Expression> include5, Expression> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression> include4, Expression> include5, Expression>> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression> include4, Expression>> include5, Expression>> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression> include4, Expression>> include5, Expression>> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression> include4, Expression>> include5, Expression>> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression>> include4, Expression>> include5, Expression>> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression>> include4, Expression>> include5, Expression>> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression> include4, Expression>> include5, Expression> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression> include4, Expression>> include5, Expression>> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression>> include4, Expression>> include5, Expression> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression>> include4, Expression>> include5, Expression>> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression>> include4, Expression> include5, Expression> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression>> include4, Expression>> include5, Expression> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression> include4, Expression>> include5, Expression> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression> include4, Expression>> include5, Expression>> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression> include4, Expression> include5, Expression>> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression> include4, Expression>> include5, Expression>> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression> include4, Expression> include5, Expression> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression>> include4, Expression> include5, Expression>> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression>> include4, Expression>> include5, Expression> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression>> include4, Expression> include5, Expression>> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression> include4, Expression>> include5, Expression>> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression> include4, Expression> include5, Expression> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression>> include4, Expression> include5, Expression>> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression>> include4, Expression> include5, Expression> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression> include4, Expression> include5, Expression>> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression>> include4, Expression> include5, Expression> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression> include4, Expression> include5, Expression> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression> include4, Expression>> include5, Expression>> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression>> include4, Expression> include5, Expression>> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression> include4, Expression>> include5, Expression> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression> include4, Expression>> include5, Expression> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression>> include4, Expression> include5, Expression>> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression>> include4, Expression>> include5, Expression>> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression>> include4, Expression>> include5, Expression> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression> include4, Expression>> include5, Expression>> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression>> include4, Expression> include5, Expression> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression>> include4, Expression> include5, Expression>> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression> include4, Expression> include5, Expression> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression>> include4, Expression>> include5, Expression>> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression>> include4, Expression> include5, Expression>> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression> include4, Expression> include5, Expression> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression> include4, Expression> include5, Expression>> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression> include4, Expression> include5, Expression>> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression>> include4, Expression> include5, Expression> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression>> include4, Expression>> include5, Expression> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression>> include4, Expression> include5, Expression> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression> include4, Expression>> include5, Expression> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression> include4, Expression>> include5, Expression>> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression>> include4, Expression> include5, Expression> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression>> include4, Expression>> include5, Expression> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression>> include4, Expression> include5, Expression> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression>> include4, Expression>> include5, Expression>> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression>> include4, Expression>> include5, Expression> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression> include4, Expression> include5, Expression>> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression> include4, Expression> include5, Expression> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression> include4, Expression> include5, Expression> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression>> include4, Expression>> include5, Expression> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression> include4, Expression>> include5, Expression> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression> include4, Expression>> include5, Expression> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression>> include4, Expression>> include5, Expression>> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression> include4, Expression>> include5, Expression> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression> include4, Expression>> include5, Expression> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression> include4, Expression>> include5, Expression>> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression>> include4, Expression>> include5, Expression> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression>> include4, Expression>> include5, Expression> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression>> include4, Expression> include5, Expression>> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression>> include4, Expression>> include5, Expression>> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression> include4, Expression> include5, Expression>> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression> include4, Expression>> include5, Expression> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression>> include4, Expression>> include5, Expression>> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression>> include4, Expression>> include5, Expression>> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression> include4, Expression> include5, Expression>> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression>> include4, Expression>> include5, Expression> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression> include4, Expression> include5, Expression> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression>> include4, Expression>> include5, Expression>> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression>> include4, Expression> include5, Expression>> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression>> include4, Expression>> include5, Expression>> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression> include4, Expression>> include5, Expression> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression>> include4, Expression> include5, Expression> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression> include4, Expression>> include5, Expression>> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression> include4, Expression> include5, Expression> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression>> include4, Expression> include5, Expression>> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression> include4, Expression> include5, Expression>> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression> include4, Expression> include5, Expression> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression>> include4, Expression>> include5, Expression>> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression> include4, Expression>> include5, Expression>> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression> include4, Expression> include5, Expression>> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression>> include4, Expression>> include5, Expression> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression>> include4, Expression>> include5, Expression> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression>> include4, Expression>> include5, Expression> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression> include4, Expression> include5, Expression> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression>> include4, Expression> include5, Expression> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression> include4, Expression> include5, Expression> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression> include4, Expression>> include5, Expression>> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression> include4, Expression>> include5, Expression> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression>> include4, Expression> include5, Expression>> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression>> include4, Expression> include5, Expression>> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression>> include4, Expression>> include5, Expression> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression> include4, Expression>> include5, Expression>> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression>> include4, Expression> include5, Expression>> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression> include4, Expression>> include5, Expression> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression>> include4, Expression>> include5, Expression> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression>> include4, Expression>> include5, Expression>> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression>> include4, Expression> include5, Expression>> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression>> include4, Expression>> include5, Expression>> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression> include4, Expression> include5, Expression> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression> include4, Expression>> include5, Expression> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression> include4, Expression> include5, Expression>> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression> include4, Expression>> include5, Expression> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression>> include4, Expression>> include5, Expression>> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression>> include4, Expression> include5, Expression>> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression> include4, Expression> include5, Expression> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression> include4, Expression>> include5, Expression> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression>> include4, Expression> include5, Expression> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression> include4, Expression> include5, Expression>> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression>> include4, Expression> include5, Expression> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression> include4, Expression> include5, Expression>> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression> include4, Expression> include5, Expression>> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression> include4, Expression>> include5, Expression>> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression> include4, Expression> include5, Expression>> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression>> include4, Expression> include5, Expression> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression> include4, Expression> include5, Expression>> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression> include4, Expression> include5, Expression> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression>> include4, Expression> include5, Expression> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression>> include4, Expression> include5, Expression>> include6, Expression>> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + public NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression>> include4, Expression> include5, Expression> include6, Expression> include7) { _Includes(this.Context, include1, include2, include3, include4, include5, include6, include7); return this; } + + } +} + diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/Includes.txt b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/Includes.txt new file mode 100644 index 000000000..4f50f7fa9 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/Includes.txt @@ -0,0 +1,105 @@ + +List sourceNumbers = new List { 2, 4, 8, 16, 32, 64, 128 }; +var ram = new Random(); +foreach (var source in sourceNumbers) +{ + var index = sourceNumbers.IndexOf(source) + 1; + //Console.WriteLine("index=" + index); + + List> result = new List>(); + + string sb = ""; + List sb2 = new List(); + for (int i = 1; i <= source; i++) + { + + List test = new List(); + + List test2 = new List(); + for (int j = 1; j <= index; j++) + { + + test2.Add(new Test() { i = j, b = ram.Next(1, 222) % 2 == 0 }); + sb += $"[{test2.Last().b}:{test2.Last().i}]"; + //sb2 += $"[{test2.Last().b}:{test2.Last().i}]"; + if (j == index) + { + if (sb2.Count > 0 && sb2.Contains(sb)) + { + j = 0; + sb = ""; + test2 = new List(); + } + else + { + test.AddRange(test2.Select(x => new Test { b = x.b, i = x.i })); + sb2.Add(sb); + sb = ""; + } + } + + } + + test = test.OrderBy(it => it.i).ToList(); + result.Add(test); + + } + foreach (var test in result) + { + List res = new List(); + List res2 = new List(); + List res3 = new List(); + foreach (var item in test) + { + res3.Add($"include{ item.i}"); + if (item.i == 1) + { + if (item.b == false) + { + res.Add($"Expression>> include{item.i}"); + } + else + { + res.Add($"Expression> include{item.i}"); + } + + } + else + { + if (item.b == false) + { + res.Add($"Expression>> include{item.i}"); + } + else + { + res.Add($"Expression> include{item.i}"); + } + } + res2.Add("TReturn" + (item.i)); + } + + Console.WriteLine($"public ISugarQueryable Includes<{string.Join(",", res2)}>({string.Join(",", res)}){{ _Includes(this.Context,{string.Join(",", res3)}); return this; }}"); + } + //Console.WriteLine("--"); +} + +Console.ReadKey(); + +public class Test +{ + public bool b { get; set; } + public int i { get; set; } +} + + +//1:1 true + +//2: +// 1 true , 2 true +// 1 false , 2 true + +//3: +// 1 true , 2 true 3 true +// 1 true , 2 false 3 true +// 1 false , 2 false 3 true +// 1 false , 2 true 3 true diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/IncludesHelper.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/IncludesHelper.cs new file mode 100644 index 000000000..fe651156f --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/IncludesHelper.cs @@ -0,0 +1,293 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public partial class QueryableProvider : QueryableAccessory, ISugarQueryable + { + private void _Includes(SqlSugarProvider context, params Expression[] expressions) + { + Func, List> SelectR1 = it => it.Select().ToList().Select(x => x as object).ToList(); + var navigat = new NavigatManager(); + navigat.SelectR1 = SelectR1; + navigat.Expressions = expressions; + navigat.Context = this.Context; + navigat.IsCrossQueryWithAttr = this.QueryBuilder.IsCrossQueryWithAttr; + navigat.CrossQueryItems = this.QueryBuilder.CrossQueryItems; + navigat.QueryBuilder = this.QueryBuilder; + if (this.QueryBuilder.Includes == null) this.QueryBuilder.Includes = new List(); + this.QueryBuilder.Includes.Add(navigat); + } + private void _Includes(SqlSugarProvider context, params Expression[] expressions) + { + Func, List> SelectR1 = it => it.Select().ToList().Select(x => x as object).ToList(); + Func, List> SelectR2 = it => it.Select().ToList().Select(x => x as object).ToList(); + var navigat = new NavigatManager(); + navigat.SelectR1 = SelectR1; + navigat.SelectR2 = SelectR2; + navigat.IsCrossQueryWithAttr = this.QueryBuilder.IsCrossQueryWithAttr; + navigat.CrossQueryItems = this.QueryBuilder.CrossQueryItems; + navigat.Expressions = expressions; + navigat.Context = this.Context; + navigat.QueryBuilder = this.QueryBuilder; + if (this.QueryBuilder.Includes == null) this.QueryBuilder.Includes = new List(); + this.QueryBuilder.Includes.Add(navigat); + } + private void _Includes(SqlSugarProvider context, params Expression[] expressions) + { + Func, List> SelectR1 = it => it.Select().ToList().Select(x => x as object).ToList(); + Func, List> SelectR2 = it => it.Select().ToList().Select(x => x as object).ToList(); + Func, List> SelectR3 = it => it.Select().ToList().Select(x => x as object).ToList(); + var navigat = new NavigatManager(); + navigat.SelectR1 = SelectR1; + navigat.SelectR2 = SelectR2; + navigat.SelectR3 = SelectR3; + navigat.IsCrossQueryWithAttr = this.QueryBuilder.IsCrossQueryWithAttr; + navigat.CrossQueryItems = this.QueryBuilder.CrossQueryItems; + navigat.Expressions = expressions; + navigat.Context = this.Context; + navigat.QueryBuilder = this.QueryBuilder; + if (this.QueryBuilder.Includes == null) this.QueryBuilder.Includes = new List(); + this.QueryBuilder.Includes.Add(navigat); + } + private void _Includes(SqlSugarProvider context, params Expression[] expressions) + { + Func, List> SelectR1 = it => it.Select().ToList().Select(x => x as object).ToList(); + Func, List> SelectR2 = it => it.Select().ToList().Select(x => x as object).ToList(); + Func, List> SelectR3 = it => it.Select().ToList().Select(x => x as object).ToList(); + Func, List> SelectR4 = it => it.Select().ToList().Select(x => x as object).ToList(); + var navigat = new NavigatManager(); + navigat.SelectR1 = SelectR1; + navigat.SelectR2 = SelectR2; + navigat.SelectR3 = SelectR3; + navigat.SelectR4 = SelectR4; + navigat.Expressions = expressions; + navigat.IsCrossQueryWithAttr = this.QueryBuilder.IsCrossQueryWithAttr; + navigat.CrossQueryItems = this.QueryBuilder.CrossQueryItems; + navigat.Context = this.Context; + navigat.QueryBuilder = this.QueryBuilder; + if (this.QueryBuilder.Includes == null) this.QueryBuilder.Includes = new List(); + this.QueryBuilder.Includes.Add(navigat); + } + private void _Includes(SqlSugarProvider context, params Expression[] expressions) + { + Func, List> SelectR1 = it => it.Select().ToList().Select(x => x as object).ToList(); + Func, List> SelectR2 = it => it.Select().ToList().Select(x => x as object).ToList(); + Func, List> SelectR3 = it => it.Select().ToList().Select(x => x as object).ToList(); + Func, List> SelectR4 = it => it.Select().ToList().Select(x => x as object).ToList(); + Func, List> SelectR5 = it => it.Select().ToList().Select(x => x as object).ToList(); + var navigat = new NavigatManager(); + navigat.SelectR1 = SelectR1; + navigat.SelectR2 = SelectR2; + navigat.SelectR3 = SelectR3; + navigat.SelectR4 = SelectR4; + navigat.SelectR5 = SelectR5; + navigat.Expressions = expressions; + navigat.IsCrossQueryWithAttr = this.QueryBuilder.IsCrossQueryWithAttr; + navigat.CrossQueryItems = this.QueryBuilder.CrossQueryItems; + navigat.Context = this.Context; + navigat.QueryBuilder = this.QueryBuilder; + if (this.QueryBuilder.Includes == null) this.QueryBuilder.Includes = new List(); + this.QueryBuilder.Includes.Add(navigat); + } + public NavISugarQueryable AsNavQueryable() + { + return GetNavSugarQueryable(); + } + private NavQueryableProvider GetNavSugarQueryable() + { + var result = new NavQueryableProvider(); + result.Context = this.Context; + var clone = this.Clone(); + result.SqlBuilder = clone.SqlBuilder; + result.QueryBuilder = clone.QueryBuilder; + return result; + } + private ISugarQueryable GetManyQueryable(Expression> include1) + { + ISugarQueryable result = null; + var isManyMembers = IsMembers(include1); + if (isManyMembers) + { + var array = ExpressionTool.ExtractMemberNames(include1); + if (array.Count > 1) + { + + if (array.Count == 2) + { + result = this.IncludesByNameString(array[0], array[1]); + } + else if (array.Count == 3) + { + result = this.IncludesByNameString(array[0], array[1], array[2]); + } + else if (array.Count == 4) + { + result = this.IncludesByNameString(array[0], array[1], array[2], array[3]); + } + else if (array.Count == 5) + { + result = this.IncludesByNameString(array[0], array[1], array[2], array[3], array[4]); + } + else if (array.Count == 6) + { + throw new Exception("Multiple levels of expression exceeded the upper limit"); + } + } + } + return result; + } + private static bool IsMembers(Expression> include1) + { + var isManyMembers = false; + var x = ((include1 as LambdaExpression).Body as MemberExpression)?.Expression; + if (x is MemberExpression) + { + var exp = (x as MemberExpression)?.Expression; + if (exp != null) + { + isManyMembers = true; + } + } + return isManyMembers; + } + + } + public partial class NavQueryableProvider : QueryableProvider, NavISugarQueryable + { + private void _Includes(SqlSugarProvider context, params Expression[] expressions) + { + Func, List> SelectR1 = it => it.Select().ToList().Select(x => x as object).ToList(); + var navigat = new NavigatManager(); + navigat.SelectR1 = SelectR1; + navigat.Expressions = expressions; + navigat.Context = this.Context; + navigat.IsCrossQueryWithAttr = this.QueryBuilder.IsCrossQueryWithAttr; + navigat.CrossQueryItems = this.QueryBuilder.CrossQueryItems; + navigat.QueryBuilder = this.QueryBuilder; + if (this.QueryBuilder.Includes == null) this.QueryBuilder.Includes = new List(); + this.QueryBuilder.Includes.Add(navigat); + } + private void _Includes(SqlSugarProvider context, params Expression[] expressions) + { + Func, List> SelectR1 = it => it.Select().ToList().Select(x => x as object).ToList(); + Func, List> SelectR2 = it => it.Select().ToList().Select(x => x as object).ToList(); + var navigat = new NavigatManager(); + navigat.SelectR1 = SelectR1; + navigat.SelectR2 = SelectR2; + navigat.Expressions = expressions; + navigat.IsCrossQueryWithAttr = this.QueryBuilder.IsCrossQueryWithAttr; + navigat.CrossQueryItems = this.QueryBuilder.CrossQueryItems; + navigat.Context = this.Context; + navigat.QueryBuilder = this.QueryBuilder; + if (this.QueryBuilder.Includes == null) this.QueryBuilder.Includes = new List(); + this.QueryBuilder.Includes.Add(navigat); + } + private void _Includes(SqlSugarProvider context, params Expression[] expressions) + { + Func, List> SelectR1 = it => it.Select().ToList().Select(x => x as object).ToList(); + Func, List> SelectR2 = it => it.Select().ToList().Select(x => x as object).ToList(); + Func, List> SelectR3 = it => it.Select().ToList().Select(x => x as object).ToList(); + var navigat = new NavigatManager(); + navigat.SelectR1 = SelectR1; + navigat.SelectR2 = SelectR2; + navigat.SelectR3 = SelectR3; + navigat.Expressions = expressions; + navigat.IsCrossQueryWithAttr = this.QueryBuilder.IsCrossQueryWithAttr; + navigat.CrossQueryItems = this.QueryBuilder.CrossQueryItems; + navigat.Context = this.Context; + navigat.QueryBuilder = this.QueryBuilder; + if (this.QueryBuilder.Includes == null) this.QueryBuilder.Includes = new List(); + this.QueryBuilder.Includes.Add(navigat); + } + private void _Includes(SqlSugarProvider context, params Expression[] expressions) + { + Func, List> SelectR1 = it => it.Select().ToList().Select(x => x as object).ToList(); + Func, List> SelectR2 = it => it.Select().ToList().Select(x => x as object).ToList(); + Func, List> SelectR3 = it => it.Select().ToList().Select(x => x as object).ToList(); + Func, List> SelectR4 = it => it.Select().ToList().Select(x => x as object).ToList(); + var navigat = new NavigatManager(); + navigat.SelectR1 = SelectR1; + navigat.SelectR2 = SelectR2; + navigat.SelectR3 = SelectR3; + navigat.SelectR4 = SelectR4; + navigat.Expressions = expressions; + navigat.IsCrossQueryWithAttr = this.QueryBuilder.IsCrossQueryWithAttr; + navigat.CrossQueryItems = this.QueryBuilder.CrossQueryItems; + navigat.Context = this.Context; + navigat.QueryBuilder = this.QueryBuilder; + if (this.QueryBuilder.Includes == null) this.QueryBuilder.Includes = new List(); + this.QueryBuilder.Includes.Add(navigat); + } + private void _Includes(SqlSugarProvider context, params Expression[] expressions) + { + Func, List> SelectR1 = it => it.Select().ToList().Select(x => x as object).ToList(); + Func, List> SelectR2 = it => it.Select().ToList().Select(x => x as object).ToList(); + Func, List> SelectR3 = it => it.Select().ToList().Select(x => x as object).ToList(); + Func, List> SelectR4 = it => it.Select().ToList().Select(x => x as object).ToList(); + Func, List> SelectR5 = it => it.Select().ToList().Select(x => x as object).ToList(); + var navigat = new NavigatManager(); + navigat.SelectR1 = SelectR1; + navigat.SelectR2 = SelectR2; + navigat.SelectR3 = SelectR3; + navigat.SelectR4 = SelectR4; + navigat.SelectR5 = SelectR5; + navigat.Expressions = expressions; + navigat.IsCrossQueryWithAttr = this.QueryBuilder.IsCrossQueryWithAttr; + navigat.CrossQueryItems = this.QueryBuilder.CrossQueryItems; + navigat.Context = this.Context; + navigat.QueryBuilder = this.QueryBuilder; + if (this.QueryBuilder.Includes == null) this.QueryBuilder.Includes = new List(); + this.QueryBuilder.Includes.Add(navigat); + } + private void _Includes(SqlSugarProvider context, params Expression[] expressions) + { + Func, List> SelectR1 = it => it.Select().ToList().Select(x => x as object).ToList(); + Func, List> SelectR2 = it => it.Select().ToList().Select(x => x as object).ToList(); + Func, List> SelectR3 = it => it.Select().ToList().Select(x => x as object).ToList(); + Func, List> SelectR4 = it => it.Select().ToList().Select(x => x as object).ToList(); + Func, List> SelectR5 = it => it.Select().ToList().Select(x => x as object).ToList(); + Func, List> SelectR6 = it => it.Select().ToList().Select(x => x as object).ToList(); + var navigat = new NavigatManager(); + navigat.SelectR1 = SelectR1; + navigat.SelectR2 = SelectR2; + navigat.SelectR3 = SelectR3; + navigat.SelectR4 = SelectR4; + navigat.SelectR5 = SelectR5; + navigat.SelectR6 = SelectR6; + navigat.Expressions = expressions; + navigat.IsCrossQueryWithAttr = this.QueryBuilder.IsCrossQueryWithAttr; + navigat.CrossQueryItems = this.QueryBuilder.CrossQueryItems; + navigat.Context = this.Context; + navigat.QueryBuilder = this.QueryBuilder; + if (this.QueryBuilder.Includes == null) this.QueryBuilder.Includes = new List(); + this.QueryBuilder.Includes.Add(navigat); + } + private void _Includes(SqlSugarProvider context, params Expression[] expressions) + { + Func, List> SelectR1 = it => it.Select().ToList().Select(x => x as object).ToList(); + Func, List> SelectR2 = it => it.Select().ToList().Select(x => x as object).ToList(); + Func, List> SelectR3 = it => it.Select().ToList().Select(x => x as object).ToList(); + Func, List> SelectR4 = it => it.Select().ToList().Select(x => x as object).ToList(); + Func, List> SelectR5 = it => it.Select().ToList().Select(x => x as object).ToList(); + Func, List> SelectR6 = it => it.Select().ToList().Select(x => x as object).ToList(); + Func, List> SelectR7 = it => it.Select().ToList().Select(x => x as object).ToList(); + var navigat = new NavigatManager(); + navigat.SelectR1 = SelectR1; + navigat.SelectR2 = SelectR2; + navigat.SelectR3 = SelectR3; + navigat.SelectR4 = SelectR4; + navigat.SelectR5 = SelectR5; + navigat.SelectR6 = SelectR6; + navigat.SelectR7 = SelectR7; + navigat.Expressions = expressions; + navigat.QueryBuilder = this.QueryBuilder; + navigat.IsCrossQueryWithAttr = this.QueryBuilder.IsCrossQueryWithAttr; + navigat.CrossQueryItems = this.QueryBuilder.CrossQueryItems; + navigat.Context = this.Context; + if (this.QueryBuilder.Includes == null) this.QueryBuilder.Includes = new List(); + this.QueryBuilder.Includes.Add(navigat); + } + + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/MappingFieldsHelper.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/MappingFieldsHelper.cs new file mode 100644 index 000000000..2447bebcb --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/MappingFieldsHelper.cs @@ -0,0 +1,216 @@ +using System.Collections; +using System.Linq.Expressions; + +namespace SqlSugar +{ + internal class MappingFieldsHelper + { + public SqlSugarProvider Context { get; set; } + public EntityInfo NavEntity { get; set; } + public EntityInfo RootEntity { get; set; } + + public MappingFieldsInfo GetMappings(Expression thisFiled, Expression mappingFiled) + { + MappingFieldsInfo mappingFields = new MappingFieldsInfo(); + var pkName = ""; + if ((mappingFiled as LambdaExpression).Body is UnaryExpression) + { + pkName = (((mappingFiled as LambdaExpression).Body as UnaryExpression).Operand as MemberExpression).Member.Name; + } + else + { + pkName = ((mappingFiled as LambdaExpression).Body as MemberExpression).Member.Name; + } + return mappingFields; + } + + public List GetMppingSql(List list, List mappingFieldsExpressions) + { + List conditionalModels = new List(); + foreach (var model in list) + { + var clist = new List>(); + var i = 0; + foreach (var item in mappingFieldsExpressions) + { + InitMappingFieldsExpression(item); + clist.Add(new KeyValuePair(i == 0 ? WhereType.Or : WhereType.And, new ConditionalModel() + { + FieldName = item.LeftEntityColumn.DbColumnName, + ConditionalType = ConditionalType.Equal, + FieldValue = item.RightEntityColumn.PropertyInfo.GetValue(model).ObjToString(), + CSharpTypeName = UtilMethods.GetUnderType(item.RightEntityColumn.PropertyInfo.PropertyType).Name + })); + i++; + } + conditionalModels.Add(new ConditionalCollections() + { + ConditionalList = clist + }); + } + return conditionalModels; + } + + public void SetChildList(EntityColumnInfo navColumnInfo, object item, List list, List mappingFieldsExpressions) + { + if (item != null) + { + //var expable =Expressionable.Create(); + List setList = GetSetList(item, list, mappingFieldsExpressions); + //navColumnInfo.PropertyInfo.SetValue(); + var instance = Activator.CreateInstance(navColumnInfo.PropertyInfo.PropertyType, true); + var ilist = instance as IList; + foreach (var value in setList) + { + ilist.Add(value); + } + navColumnInfo.PropertyInfo.SetValue(item, ilist); + } + } + + public void SetChildItem(EntityColumnInfo navColumnInfo, object item, List list, List mappingFieldsExpressions) + { + if (item != null) + { + //var expable =Expressionable.Create(); + List setList = GetSetList(item, list, mappingFieldsExpressions); + //navColumnInfo.PropertyInfo.SetValue(); + var instance = Activator.CreateInstance(navColumnInfo.PropertyInfo.PropertyType, true); + var ilist = instance as IList; + foreach (var value in setList) + { + navColumnInfo.PropertyInfo.SetValue(item, value); + } + + } + } + public List GetSetList(object item, List list, List mappingFieldsExpressions) + { + foreach (var field in mappingFieldsExpressions) + { + InitMappingFieldsExpression(field); + } + var setList = new List(); + var count = mappingFieldsExpressions.Count; + if (count == 1) + { + setList = list.Where(it => GetWhereByIndex(item, mappingFieldsExpressions, it, 0)).ToList(); + } + else if (count == 2) + { + setList = list.Where(it => + GetWhereByIndex(item, mappingFieldsExpressions, it, 0) && + GetWhereByIndex(item, mappingFieldsExpressions, it, 1) + ).ToList(); + } + else if (count == 3) + { + setList = list.Where(it => + GetWhereByIndex(item, mappingFieldsExpressions, it, 0) && + GetWhereByIndex(item, mappingFieldsExpressions, it, 1) && + GetWhereByIndex(item, mappingFieldsExpressions, it, 2) + ).ToList(); + } + else if (count == 4) + { + setList = list.Where(it => + GetWhereByIndex(item, mappingFieldsExpressions, it, 0) && + GetWhereByIndex(item, mappingFieldsExpressions, it, 1) && + GetWhereByIndex(item, mappingFieldsExpressions, it, 2) && + GetWhereByIndex(item, mappingFieldsExpressions, it, 3) + ).ToList(); + } + else if (count == 5) + { + setList = list.Where(it => + GetWhereByIndex(item, mappingFieldsExpressions, it, 0) && + GetWhereByIndex(item, mappingFieldsExpressions, it, 1) && + GetWhereByIndex(item, mappingFieldsExpressions, it, 2) && + GetWhereByIndex(item, mappingFieldsExpressions, it, 3) && + GetWhereByIndex(item, mappingFieldsExpressions, it, 4) + ).ToList(); + } + else + { + Check.ExceptionEasy("MappingField max value is 5", "MappingField最大数量不能超过5"); + } + + return setList; + } + + private static bool GetWhereByIndex(object item, List mappingFieldsExpressions, object it, int index) + { + var left = mappingFieldsExpressions[index].LeftEntityColumn.PropertyInfo.GetValue(it).ObjToString(); + var right = mappingFieldsExpressions[index].RightEntityColumn.PropertyInfo.GetValue(item).ObjToString(); ; + return left == right; + } + + private void InitMappingFieldsExpression(MappingFieldsExpression item) + { + var leftName = item.LeftName; + var rightName = item.RightName; + if (item.LeftEntityColumn == null) + { + item.LeftEntityColumn = this.NavEntity.Columns.FirstOrDefault(it => it.PropertyName == leftName); + } + if (item.RightEntityColumn == null && this.Context != null) + { + if (item.RightColumnExpression is LambdaExpression) + { + var body = (item.RightColumnExpression as LambdaExpression).Body; + if (body is UnaryExpression) + { + body = ((UnaryExpression)body).Operand; + } + if (body is MemberExpression) + { + var exp = (body as MemberExpression).Expression; + if (exp.NodeType == ExpressionType.Parameter) + { + item.RightEntityColumn = this.Context.EntityMaintenance.GetEntityInfo(exp.Type).Columns.FirstOrDefault(it => it.PropertyName == rightName); + } + } + } + if (item.RightEntityColumn == null) + item.RightEntityColumn = this.RootEntity.Columns.FirstOrDefault(it => it.PropertyName == rightName); + } + } + + } + public class MappingFieldsInfo + { + public DbColumnInfo LeftColumn { get; set; } + public DbColumnInfo RightColumn { get; set; } + } + public class MappingFieldsExpression + { + public Expression LeftColumnExpression { get; set; } + public Expression RightColumnExpression { get; set; } + public EntityColumnInfo LeftEntityColumn { get; set; } + public EntityColumnInfo RightEntityColumn { get; set; } + private string _LeftName; + public string LeftName + { + get + { + if (_LeftName == null) + { + _LeftName = ExpressionTool.GetMemberName(this.LeftColumnExpression); + } + return _LeftName; + } + } + private string _RightName; + public string RightName + { + get + { + if (_RightName == null) + { + _RightName = ExpressionTool.GetMemberName(this.RightColumnExpression); + } + return _RightName; + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/NavSelectHelper.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/NavSelectHelper.cs new file mode 100644 index 000000000..9ecabf9a1 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/NavSelectHelper.cs @@ -0,0 +1,352 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + internal static class NavSelectHelper + { + internal static List GetList(Expression> expression, QueryableProvider queryableProvider) + { + + List result = new List(); + var isSqlFunc = IsSqlFunc(expression, queryableProvider); + var isClass = IsClass(expression, queryableProvider); + if (isGroup(expression, queryableProvider)) + { + var sqlfuncQueryable = queryableProvider.Clone(); + sqlfuncQueryable.QueryBuilder.Includes = null; + result = sqlfuncQueryable + .Select(expression) + .ToList(); + var includeQueryable = queryableProvider.Clone(); + includeQueryable.Select(GetGroupSelect(typeof(T), queryableProvider.Context, queryableProvider.QueryBuilder)); + includeQueryable.QueryBuilder.NoCheckInclude = true; + var mappingColumn = GetMappingColumn(expression); + MegerList(result, includeQueryable.ToList(), sqlfuncQueryable.Context, mappingColumn); + } + else if (isSqlFunc) + { + result = SqlFunc(expression, queryableProvider); + } + else if (typeof(TResult).IsAnonymousType() && isClass == false) + { + result = SqlFunc(expression, queryableProvider); + } + else if (typeof(TResult).IsAnonymousType() && isClass == true) + { + result = Action(expression, queryableProvider); + } + else if (expression.ToString().Contains("FirstOrDefault()")) + { + result = Action(expression, queryableProvider); + } + else + { + try + { + result = SqlFunc(expression, queryableProvider); + } + catch (Exception ex) + { + try + { + Console.WriteLine("Select DTO error . Warning:" + ex.Message); + result = Action(expression, queryableProvider); + } + catch + { + + throw; + } + } + } + return result; + } + + private static List Action(Expression> expression, QueryableProvider queryableProvider) + { + List result; + var entity = queryableProvider.Context.EntityMaintenance.GetEntityInfo(); + var list = queryableProvider.Clone().ToList(); + var dt = queryableProvider.Context.Utilities.ListToDataTable(list); + foreach (System.Data.DataRow item in dt.Rows) + { + foreach (System.Data.DataColumn columnInfo in dt.Columns) + { + if (columnInfo.DataType.IsClass()) + { + if (item[columnInfo.ColumnName] == null || item[columnInfo.ColumnName] == DBNull.Value) + { + item[columnInfo.ColumnName] = Activator.CreateInstance(columnInfo.DataType, true); + } + } + } + } + list = queryableProvider.Context.Utilities.DataTableToList(dt); + result = list.Select(expression.Compile()).ToList(); + return result; + } + + private static List SqlFunc(Expression> expression, QueryableProvider queryableProvider) + { + var mappingColumn = GetMappingColumn(expression); + if (mappingColumn.Any(it => it.IsError)) + { + return Action(expression, queryableProvider); + } + List result; + var sqlfuncQueryable = queryableProvider.Clone(); + var dtoEntity = sqlfuncQueryable.Context.EntityMaintenance.GetEntityInfo().Columns; + var tableEntity = sqlfuncQueryable.Context.EntityMaintenance.GetEntityInfo().Columns; + var ignoreColumns = GetIgnoreColumns(dtoEntity, tableEntity); + sqlfuncQueryable.QueryBuilder.Includes = null; + result = sqlfuncQueryable + .IgnoreColumns(ignoreColumns) + .Select(expression) + .ToList(); + var selector = GetDefaultSelector(queryableProvider.Context.EntityMaintenance.GetEntityInfo(), queryableProvider.QueryBuilder); + var queryable = queryableProvider.Select(selector).Clone(); + queryable.QueryBuilder.NoCheckInclude = true; + var includeList = queryable.ToList(); + MegerList(result, includeList, sqlfuncQueryable.Context, mappingColumn); + return result; + } + + private static string[] GetIgnoreColumns(List dtoEntity, List tableEntity) + { + var column = (from dto in dtoEntity + join tab in tableEntity on dto.PropertyInfo.PropertyType equals tab.PropertyInfo.PropertyType + where tab.Navigat != null + select tab.PropertyName).Distinct().ToArray(); + return column; + } + + internal static async Task> GetListAsync(Expression> expression, QueryableProvider queryableProvider) + { + return await Task.Run(() => { return GetList(expression, queryableProvider); }).ConfigureAwait(false); + } + + private static string GetGroupSelect(Type type, SqlSugarProvider context, QueryBuilder queryBuilder) + { + var entity = context.EntityMaintenance.GetEntityInfo(type); + List selector = new List(); + List columns = GetListNavColumns(entity); + foreach (var item in columns) + { + if (queryBuilder.TableShortName.HasValue()) + { + selector.Add($" min({queryBuilder.TableShortName}.{item.DbColumnName}) as {item.DbColumnName}"); + } + else + { + selector.Add($" min({item.DbColumnName}) as {item.DbColumnName}"); + } + } + return string.Join(",", selector); + } + + private static string GetDefaultSelector(EntityInfo entityInfo, QueryBuilder queryBuilder) + { + List columns = GetListNavColumns(entityInfo); + var selector = new List(); + if (columns.Count == 0) return null; + foreach (var item in columns) + { + if (queryBuilder.TableShortName.HasValue()) + { + selector.Add($" {queryBuilder.TableShortName}.{item.DbColumnName} as {item.DbColumnName}"); + } + else + { + selector.Add($" {item.DbColumnName} as {item.DbColumnName}"); + } + } + return string.Join(",", selector); + } + + private static List GetListNavColumns(EntityInfo entityInfo) + { + var list = entityInfo.Columns.Where(it => it.Navigat != null).Select( + it => it.Navigat.Name + ).ToArray(); + var list2 = entityInfo.Columns.Where(it => it.Navigat?.Name2 != null).Select( + it => it.Navigat.Name2 + ).ToArray(); + var columns = entityInfo.Columns.Where(it => it.IsPrimarykey || + list.Contains(it.PropertyName) || + list2.Contains(it.PropertyName) + ).ToList(); + return columns; + } + + private static void MegerList(List result, List includeList, SqlSugarProvider context, List navMappingColumns) + { + if (result.Count != includeList.Count) return; + var columns = context.EntityMaintenance.GetEntityInfo().Columns; + var resColumns = context.EntityMaintenance.GetEntityInfo().Columns; + var i = 0; + foreach (var item in includeList) + { + foreach (var column in columns) + { + if (column.Navigat != null) + { + var value = column.PropertyInfo.GetValue(item); + var resColumn = resColumns + .FirstOrDefault(z => + z.PropertyName.Equals(column.PropertyName) && + z.PropertyInfo.PropertyType == column.PropertyInfo.PropertyType + ); + if (resColumn == null && navMappingColumns.Any(z => z.Value == column.PropertyName)) + { + var mappingColumn = navMappingColumns.First(z => z.Value == column.PropertyName); + resColumn = resColumns + .FirstOrDefault(z => + z.PropertyName.Equals(mappingColumn.Key) && + z.PropertyInfo.PropertyType == column.PropertyInfo.PropertyType + ); + } + if (resColumn != null) + { + var resItem = result[i]; + resColumn.PropertyInfo.SetValue(resItem, value); + } + } + } + i++; + } + } + private static bool IsClass(Expression> expression, QueryableProvider queryableProvider) + { + var body = ExpressionTool.GetLambdaExpressionBody(expression); + if (body is NewExpression) + { + var newExp = ((NewExpression)body); + foreach (var item in newExp.Arguments) + { + if (item is MemberExpression) + { + var member = (MemberExpression)item; + if (member.Type.IsClass()) + { + return true; + } + } + } + } + return false; + } + + private static bool IsSqlFunc(Expression> expression, QueryableProvider queryableProvider) + { + var body = ExpressionTool.GetLambdaExpressionBody(expression); + if (body is NewExpression) + { + var newExp = ((NewExpression)body); + foreach (var item in newExp.Arguments) + { + if (item is MethodCallExpression) + { + var method = ((MethodCallExpression)item).Method; + if (method.DeclaringType?.Name == "SqlFunc") + { + return true; + } + } + } + } + if (body is MemberInitExpression) + { + var newExp = ((MemberInitExpression)body); + foreach (var item in newExp.Bindings) + { + MemberAssignment memberAssignment = (MemberAssignment)item; + if (memberAssignment.Expression is MethodCallExpression) + { + var method = ((MethodCallExpression)memberAssignment.Expression).Method; + if (method.DeclaringType?.Name == "SqlFunc") + { + return true; + } + } + } + } + return false; + } + private static List GetMappingColumn(Expression expression) + { + var body = ExpressionTool.GetLambdaExpressionBody(expression); + var parameterName = (expression as LambdaExpression).Parameters.FirstOrDefault().Name; + List result = new List(); + if (body is NewExpression) + { + var index = 0; + var arg = ((NewExpression)body).Arguments; + var members = ((NewExpression)body).Members; + foreach (var item in arg) + { + var name = members[index].Name; + if (item is MethodCallExpression) + { + AddCallError(result, item, parameterName); + } + index++; + } + } + else if (body is MemberInitExpression) + { + foreach (var item in ((MemberInitExpression)body).Bindings) + { + MemberAssignment memberAssignment = (MemberAssignment)item; + var key = memberAssignment.Member.Name; + var value = memberAssignment.Expression; + if (memberAssignment.Expression is MemberExpression) + { + result.Add(new NavMappingColumn() { Key = key, Value = ExpressionTool.GetMemberName(memberAssignment.Expression) }); + } + else if (memberAssignment.Expression is MethodCallExpression) + { + AddCallError(result, memberAssignment.Expression, parameterName); + } + } + } + return result; + } + + private static void AddCallError(List result, Expression item, string parameterName) + { + var method = (item as MethodCallExpression); + if (method.Method.Name == "ToList" && method.Arguments.Count > 0 && method.Arguments[0] is MethodCallExpression) + { + method = (MethodCallExpression)method.Arguments[0]; + } + if (method.Method.Name == "Select") + { + if (!item.ToString().Contains("Subqueryable")) + { + result.Add(new NavMappingColumn() { IsError = true }); + } + } + else if (method.Method.Name == "Join") + { + + if (item.ToString().Contains($" {parameterName}.")) + { + result.Add(new NavMappingColumn() { IsError = true }); + } + } + } + + private static bool isGroup(Expression> expression, QueryableProvider queryableProvider) + { + var isGroup = queryableProvider.QueryBuilder.GetGroupByString.HasValue(); + return isGroup; + } + + internal class NavMappingColumn + { + public string Key { get; set; } + public string Value { get; set; } + public bool IsError { get; set; } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/NavigatManager.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/NavigatManager.cs new file mode 100644 index 000000000..645110163 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/NavigatManager.cs @@ -0,0 +1,1093 @@ +using System.Collections; +using System.Linq.Expressions; +using System.Reflection; + +namespace SqlSugar +{ + public class NavigatManager + { + public SqlSugarProvider Context { get; set; } + public bool IsCrossQueryWithAttr { get; set; } + public Dictionary CrossQueryItems { get; set; } + public Func, List> SelectR1 { get; set; } + public Func, List> SelectR2 { get; set; } + public Func, List> SelectR3 { get; set; } + public Func, List> SelectR4 { get; set; } + public Func, List> SelectR5 { get; set; } + public Func, List> SelectR6 { get; set; } + public Func, List> SelectR7 { get; set; } + public Func, List> SelectR8 { get; set; } + public Expression[] Expressions { get; set; } + public List RootList { get; set; } + public QueryBuilder QueryBuilder { get; set; } + + //public QueryableProvider Queryable { get; set; } + + private List _preExpressionList = new List(); + private List _preList = new List(); + private List _ListCallFunc; + //private Expression[] _expressions; + //private List _list; + //private EntityInfo _entityInfo; + public void Execute() + { + var i = 1; + foreach (var item in Expressions) + { + ExecuteByLay(i, item); + i++; + } + } + + private void ExecuteByLay(int i, Expression item) + { + _ListCallFunc = GetWhereExpression(ref item); + if (i == 1) + { + ExecuteByLay(item, RootList.Select(it => it as object).ToList(), SelectR1); + } + else if (i == 2) + { + var currentList = RootList; + if (RootList == null || currentList.Count == 0) return; + var memberExpression = ((_preExpressionList.Last() as LambdaExpression).Body as MemberExpression); + var navObjectName = memberExpression.Member.Name; + var navType = currentList[0].GetType().GetProperty(navObjectName).PropertyType.Name; + var isList = navType.StartsWith("List`"); + List list = new List(); + if (isList) + { + list = currentList.SelectMany(it => (it.GetType().GetProperty(navObjectName).GetValue(it) as IList)?.Cast() ?? new List { }).ToList(); + + } + else + { + list = currentList.Select(it => (it.GetType().GetProperty(navObjectName).GetValue(it))).ToList(); + } + ExecuteByLay(item, list, SelectR2); + _preList = list; + } + else if (i == 3) + { + var currentList = _preList.Where(it => it != null).ToList(); + if (RootList == null || currentList.Count == 0) return; + List list = ExecuteByLay(currentList); + ExecuteByLay(item, list, SelectR3); + _preList = list.ToList(); + } + else if (i == 4) + { + var currentList = _preList.Where(it => it != null).ToList(); + if (RootList == null || currentList.Count == 0) return; + List list = ExecuteByLay(currentList); + ExecuteByLay(item, list, SelectR4); + _preList = list.ToList(); + } + else if (i == 5) + { + var currentList = _preList.Where(it => it != null).ToList(); + if (RootList == null || currentList.Count == 0) return; + List list = ExecuteByLay(currentList); + ExecuteByLay(item, list, SelectR5); + _preList = list.ToList(); + } + else if (i == 6) + { + var currentList = _preList.Where(it => it != null).ToList(); + if (RootList == null || currentList.Count == 0) return; + List list = ExecuteByLay(currentList); + ExecuteByLay(item, list, SelectR6); + _preList = list.ToList(); + } + else if (i == 7) + { + var currentList = _preList.Where(it => it != null).ToList(); + if (RootList == null || currentList.Count == 0) return; + List list = ExecuteByLay(currentList); + ExecuteByLay(item, list, SelectR7); + _preList = list.ToList(); + } + _preExpressionList.Add(item); + _ListCallFunc = new List(); + } + + private List ExecuteByLay(List currentList) + { + var memberExpression = ((_preExpressionList.Last() as LambdaExpression).Body as MemberExpression); + var navObjectName = memberExpression.Member.Name; + var navType = currentList[0].GetType().GetProperty(navObjectName).PropertyType.Name; + var isList = navType.StartsWith("List`"); + List list = new List(); + if (isList) + { + list = currentList.Where(it => it.GetType().GetProperty(navObjectName).GetValue(it) != null).SelectMany(it => (it.GetType().GetProperty(navObjectName).GetValue(it) as IList).Cast()).ToList(); + + } + else + { + list = currentList.Where(it => it.GetType().GetProperty(navObjectName).GetValue(it) != null).Select(it => (it.GetType().GetProperty(navObjectName).GetValue(it))).ToList(); + } + + return list; + } + + private void ExecuteByLay(Expression expression, List list, Func, List> selector) + { + if (list == null || list.Count == 0) return; + list = list.Where(it => it != null).ToList(); + var memberExpression = ((expression as LambdaExpression).Body as MemberExpression); + + var listItemType = list.Where(it => it != null).FirstOrDefault()?.GetType(); + if (listItemType == null) + { + return; + } + if (listItemType.Name.StartsWith("List`")) + { + listItemType = listItemType.GetGenericArguments()[0]; + } + //if (listItemType == null) return; + + var listItemEntity = this.Context.EntityMaintenance.GetEntityInfo(listItemType); + var listPkColumn = listItemEntity.Columns.Where(it => it.IsPrimarykey).FirstOrDefault(); + var navObjectName = memberExpression.Member.Name; + var navObjectNamePropety = listItemType.GetProperty(navObjectName); + var navObjectNameColumnInfo = listItemEntity.Columns.First(it => it.PropertyName == navObjectName); + Check.ExceptionEasy(navObjectNameColumnInfo.Navigat == null, $"{navObjectName} not [Navigat(..)] ", $"{navObjectName} 没有导航特性 [Navigat(..)] "); + + + + if (navObjectNameColumnInfo.Navigat.NavigatType == NavigateType.OneToOne) + { + this.Context.Utilities.PageEach(list, 5000, pageList => + { + OneToOne(pageList, selector, listItemEntity, navObjectNamePropety, navObjectNameColumnInfo); + }); + } + else if (navObjectNameColumnInfo.Navigat.NavigatType == NavigateType.OneToMany) + { + this.Context.Utilities.PageEach(list, 5000, pageList => + { + OneToMany(pageList, selector, listItemEntity, navObjectNamePropety, navObjectNameColumnInfo); + }); + } + else if (navObjectNameColumnInfo.Navigat.NavigatType == NavigateType.ManyToOne) + { + this.Context.Utilities.PageEach(list, 5000, pageList => + { + OneToOne(pageList, selector, listItemEntity, navObjectNamePropety, navObjectNameColumnInfo); + }); + } + else if (navObjectNameColumnInfo.Navigat.NavigatType == NavigateType.Dynamic) + { + this.Context.Utilities.PageEach(list, 100, pageList => + { + Dynamic(pageList, selector, listItemEntity, navObjectNamePropety, navObjectNameColumnInfo, expression); + }); + } + else + { + this.Context.Utilities.PageEach(list, 100, pageList => + { + ManyToMany(pageList, selector, listItemEntity, navObjectNamePropety, navObjectNameColumnInfo); + }); + } + } + + private List GetWhereExpression(ref Expression expression) + { + List expressions = new List(); + var isCall = (expression as LambdaExpression).Body is MethodCallExpression; + if (isCall) + { + var newexp = (expression as LambdaExpression).Body; + while (newexp is MethodCallExpression) + { + expressions.Add(newexp); + newexp = (newexp as MethodCallExpression).Arguments[0]; + } + expression = LambdaExpression.Lambda(newexp); + } + return expressions; + } + + private void ManyToMany(List list, Func, List> selector, EntityInfo listItemEntity, System.Reflection.PropertyInfo navObjectNamePropety, EntityColumnInfo navObjectNameColumnInfo) + { + var bEntity = navObjectNameColumnInfo.PropertyInfo.PropertyType.GetGenericArguments()[0]; + var bEntityInfo = this.Context.EntityMaintenance.GetEntityInfo(bEntity); + var bPkColumn = bEntityInfo.Columns.FirstOrDefault(it => it.IsPrimarykey); + if (navObjectNameColumnInfo.Navigat.BClassId.HasValue()) + { + bPkColumn = bEntityInfo.Columns.FirstOrDefault(it => it.PropertyName == navObjectNameColumnInfo.Navigat.BClassId); + } + Check.ExceptionEasy(bPkColumn == null, $"{bEntityInfo.EntityName} need primary key", $"{bEntityInfo.EntityName} 实体需要配置主键"); + var bDb = this.Context; + bDb = GetCrossDatabase(bDb, bEntity); + bDb.InitMappingInfo(bEntity); + var listItemPkColumn = listItemEntity.Columns.Where(it => it.IsPrimarykey).FirstOrDefault(); + if (navObjectNameColumnInfo.Navigat.AClassId.HasValue()) + { + listItemPkColumn = listItemEntity.Columns.FirstOrDefault(it => it.PropertyName == navObjectNameColumnInfo.Navigat.AClassId); + } + Check.ExceptionEasy(listItemPkColumn == null, $"{listItemEntity.EntityName} need primary key", $"{listItemEntity.EntityName} 实体需要配置主键"); + var ids = list.Select(it => it.GetType().GetProperty(listItemPkColumn.PropertyName).GetValue(it)).Select(it => it == null ? "null" : it).Distinct().ToList(); + var mappingEntity = this.Context.EntityMaintenance.GetEntityInfo(navObjectNameColumnInfo.Navigat.MappingType); + var aColumn = mappingEntity.Columns.First(it => it.PropertyName == navObjectNameColumnInfo.Navigat.MappingAId); + var bColumn = mappingEntity.Columns.First(it => it.PropertyName == navObjectNameColumnInfo.Navigat.MappingBId); + List conditionalModels = new List(); + conditionalModels.Add((new ConditionalModel() + { + ConditionalType = ConditionalType.In, + FieldName = aColumn.DbColumnName, + FieldValue = String.Join(",", ids), + CSharpTypeName = aColumn.PropertyInfo.PropertyType.Name + })); + var abDb = this.Context; + abDb = GetCrossDatabase(abDb, mappingEntity.Type); + var queryable = abDb.Queryable(); + var abids = queryable.AS(mappingEntity.DbTableName).WhereIF(navObjectNameColumnInfo?.Navigat?.WhereSql != null, navObjectNameColumnInfo?.Navigat?.WhereSql).ClearFilter(QueryBuilder.RemoveFilters).Filter(this.QueryBuilder?.IsDisabledGobalFilter == true ? null : mappingEntity.Type).Where(conditionalModels).Select($"{queryable.SqlBuilder.GetTranslationColumnName(aColumn.DbColumnName)} as aid,{queryable.SqlBuilder.GetTranslationColumnName(bColumn.DbColumnName)} as bid").ToList(); + + List conditionalModels2 = new List(); + conditionalModels2.Add((new ConditionalModel() + { + ConditionalType = ConditionalType.In, + FieldName = bPkColumn.DbColumnName, + FieldValue = String.Join(",", abids.Select(it => it.Bid).ToArray()), + CSharpTypeName = bColumn.PropertyInfo.PropertyType.Name + })); + var sql = GetWhereSql(GetCrossDatabase(abDb, bEntity)); + if (sql.SelectString == null) + { + var columns = bEntityInfo.Columns.Where(it => !it.IsIgnore) + .Select(it => GetOneToManySelectByColumnInfo(it, abDb)).ToList(); + sql.SelectString = String.Join(",", columns); + } + else + { + var bid = InstanceFactory.GetQueryBuilderWithContext(abDb).Builder.GetTranslationColumnName(bPkColumn.DbColumnName); + if (!sql.SelectString.Contains(bid?.ToLower(), StringComparison.CurrentCultureIgnoreCase) && !sql.SelectString.Contains('*')) + { + sql.SelectString += ("," + bid + " AS " + bid); + } + } + var bList = selector(bDb.Queryable().AS(bEntityInfo.DbTableName).ClearFilter(QueryBuilder.RemoveFilters).Filter(this.QueryBuilder?.IsDisabledGobalFilter == true ? null : bEntityInfo.Type).AddParameters(sql.Parameters).Where(conditionalModels2).WhereIF(sql.WhereString.HasValue(), sql.WhereString).Select(sql.SelectString).OrderByIF(sql.OrderByString.HasValue(), sql.OrderByString)); + if (bList.HasValue()) + { + foreach (var listItem in list) + { + if (navObjectNamePropety.GetValue(listItem) == null) + { + var instance = Activator.CreateInstance(navObjectNamePropety.PropertyType, true); + var ilist = instance as IList; + foreach (var bInfo in bList) + { + var pk = listItemPkColumn.PropertyInfo.GetValue(listItem).ObjToString(); + var bid = bPkColumn.PropertyInfo.GetValue(bInfo).ObjToString(); + if (abids.Any(x => x.Aid == pk && x.Bid == bid)) + { + ilist.Add(bInfo); + } + } + if (sql.MappingExpressions.HasValue()) + { + MappingFieldsHelper helper = new MappingFieldsHelper(); + helper.Context = this.Context; + helper.NavEntity = bEntityInfo; + helper.RootEntity = this.Context.EntityMaintenance.GetEntityInfo(); + helper.SetChildList(navObjectNameColumnInfo, listItem, ilist.Cast().ToList(), sql.MappingExpressions); + } + else + { + if (sql.Skip != null || sql.Take != null) + { + var instanceCast = (instance as IList); + var newinstance = Activator.CreateInstance(navObjectNamePropety.PropertyType, true) as IList; + SkipTakeIList(sql, instanceCast, newinstance); + navObjectNamePropety.SetValue(listItem, newinstance); + } + else + { + navObjectNamePropety.SetValue(listItem, instance); + } + } + } + } + } + else + { + foreach (var listItem in list) + { + if (navObjectNamePropety.GetValue(listItem) == null) + { + var newinstance = Activator.CreateInstance(navObjectNamePropety.PropertyType, true) as IList; + navObjectNamePropety.SetValue(listItem, newinstance); + } + } + } + } + + private static void SkipTakeIList(SqlInfo sql, IList instanceCast, IList newinstance) + { + var intArray = Enumerable.Range(0, instanceCast.Count); + if (sql.Skip != null) + { + intArray = intArray + .Skip(sql.Skip.Value); + } + if (sql.Take != null) + { + intArray = intArray + .Take(sql.Take.Value); + } + foreach (var i in intArray) + { + newinstance.Add(instanceCast[i]); + } + } + + private void OneToOne(List list, Func, List> selector, EntityInfo listItemEntity, System.Reflection.PropertyInfo navObjectNamePropety, EntityColumnInfo navObjectNameColumnInfo) + { + var navColumn = listItemEntity.Columns.FirstOrDefault(it => it.PropertyName == navObjectNameColumnInfo.Navigat.Name); + Check.ExceptionEasy(navColumn == null, "OneToOne navigation configuration error", $"OneToOne导航配置错误: 实体{listItemEntity.EntityName} 不存在{navObjectNameColumnInfo.Navigat.Name}"); + var navType = navObjectNamePropety.PropertyType; + var db = this.Context; + db = GetCrossDatabase(db, navType); + var navEntityInfo = db.EntityMaintenance.GetEntityInfo(navType); + db.InitMappingInfo(navEntityInfo.Type); + var navPkColumn = navEntityInfo.Columns.Where(it => it.IsPrimarykey).FirstOrDefault(); + var navPkCount = navEntityInfo.Columns.Where(it => it.IsPrimarykey).Count(); + Check.ExceptionEasy(navPkColumn == null && navObjectNameColumnInfo.Navigat.Name2 == null, navEntityInfo.EntityName + "need primarykey", navEntityInfo.EntityName + " 需要主键"); + if (navObjectNameColumnInfo.Navigat.Name2.HasValue()) + { + navPkColumn = navEntityInfo.Columns.Where(it => it.PropertyName == navObjectNameColumnInfo.Navigat.Name2).FirstOrDefault(); + } + if (navPkColumn == null && navType.FullName.IsCollectionsList()) + { + Check.ExceptionEasy($"{navObjectNamePropety.Name} type error ", $"一对一不能是List对象 {navObjectNamePropety.Name} "); + } + List ids = null; + var isOwnsOneProperty = IsOwnsOneProperty(listItemEntity, navObjectNameColumnInfo); + if (isOwnsOneProperty) + { + var data = listItemEntity.Columns.FirstOrDefault(it => it.PropertyName == navObjectNameColumnInfo.Navigat.Name); + ids = list.Select(it => + { + var ownsObj = data.ForOwnsOnePropertyInfo.GetValue(it); + return ownsObj.GetType().GetProperty(navObjectNameColumnInfo.Navigat.Name).GetValue(ownsObj); + }).Select(it => it == null ? "null" : it).Distinct().ToList(); + } + else + { + ids = list.Select(it => it.GetType().GetProperty(navObjectNameColumnInfo.Navigat.Name).GetValue(it)).Select(it => it == null ? "null" : it).Distinct().ToList(); + } + List conditionalModels = new List(); + if (IsEnumNumber(navColumn)) + { + ids = ids.Select(it => Convert.ToInt64(it)).Cast().ToList(); + } + if (navPkColumn?.UnderType?.Name == UtilConstants.StringType.Name) + { + ids = ids.Select(it => it?.ToString()?.Replace(",", "[comma]")).Cast().ToList(); + } + if (navPkColumn?.UnderType?.Name == UtilConstants.DateType.Name) + { + ids = ids.Select(it => it == null ? null : it.ObjToDate().ToString("yyyy-MM-dd HH:mm:ss.fff")).Cast().ToList(); + } + conditionalModels.Add((new ConditionalModel() + { + ConditionalType = ConditionalType.In, + FieldName = navPkColumn.DbColumnName, + FieldValue = String.Join(",", ids), + CSharpTypeName = navPkColumn?.UnderType?.Name + })); + if (OneToOneGlobalInstanceRegistry.IsAny()) + { + foreach (var item in list) + { + var firstObj = navObjectNamePropety.GetValue(item); + if (OneToOneGlobalInstanceRegistry.IsNavigationInitializerCreated(firstObj)) + { + navObjectNamePropety.SetValue(item, null); + } + } + } + if (list.Count != 0 && navObjectNamePropety.GetValue(list.First()) == null) + { + var sqlObj = GetWhereSql(db, navObjectNameColumnInfo.Navigat.Name); + if (sqlObj.SelectString == null) + { + var columns = navEntityInfo.Columns.Where(it => !it.IsIgnore) + .Select(it => GetOneToOneSelectByColumnInfo(it, db)).ToList(); + sqlObj.SelectString = String.Join(",", columns); + } + var navList = selector(db.Queryable().ClearFilter(QueryBuilder.RemoveFilters).Filter((navPkColumn.IsPrimarykey && navPkCount == 1) ? null : this.QueryBuilder?.IsDisabledGobalFilter == true ? null : navEntityInfo.Type).AS(GetDbTableName(navEntityInfo, sqlObj)) + .WhereIF(navObjectNameColumnInfo.Navigat.WhereSql.HasValue(), navObjectNameColumnInfo.Navigat.WhereSql) + .WhereIF(sqlObj.WhereString.HasValue(), sqlObj.WhereString) + .AddParameters(sqlObj.Parameters).Where(conditionalModels) + .Select(sqlObj.SelectString)); + + if (isOwnsOneProperty) + { + { + // 有 OwnsOne 的情况 + var data = listItemEntity.Columns.FirstOrDefault(it => it.PropertyName == navObjectNameColumnInfo.Navigat.Name); + + var groupQuery = (from l in list + let ownsObj = data.ForOwnsOnePropertyInfo.GetValue(l) + join n in navList + on ownsObj.GetType() + .GetProperty(navObjectNameColumnInfo.Navigat.Name) + .GetValue(ownsObj) + .ObjToString() + equals navPkColumn.PropertyInfo.GetValue(n).ObjToString() + select new + { + l, + n + }).ToList(); + + foreach (var item in groupQuery) + { + + // var setValue = navList.FirstOrDefault(x => navPkColumn.PropertyInfo.GetValue(x).ObjToString() == navColumn.PropertyInfo.GetValue(item).ObjToString()); + + if (navObjectNamePropety.GetValue(item.l) == null) + { + navObjectNamePropety.SetValue(item.l, item.n); + } + else + { + //The reserved + } + + } + } + } + else + { + var groupQuery = (from l in list + join n in navList + on navColumn.PropertyInfo.GetValue(l).ObjToString() + equals navPkColumn.PropertyInfo.GetValue(n).ObjToString() + select new + { + l, + n + }).ToList(); + foreach (var item in groupQuery) + { + + // var setValue = navList.FirstOrDefault(x => navPkColumn.PropertyInfo.GetValue(x).ObjToString() == navColumn.PropertyInfo.GetValue(item).ObjToString()); + + if (navObjectNamePropety.GetValue(item.l) == null) + { + navObjectNamePropety.SetValue(item.l, item.n); + } + else + { + //The reserved + } + + } + } + } + } + + private void OneToMany(List list, Func, List> selector, EntityInfo listItemEntity, System.Reflection.PropertyInfo navObjectNamePropety, EntityColumnInfo navObjectNameColumnInfo) + { + Check.ExceptionEasy(navObjectNameColumnInfo.PropertyInfo.PropertyType.GetGenericArguments().Length == 0, navObjectNamePropety?.Name + "Navigation configuration error one to many should be List", navObjectNamePropety?.Name + "导航配置错误一对多应该是List"); + + var navEntity = navObjectNameColumnInfo.PropertyInfo.PropertyType.GetGenericArguments()[0]; + var navEntityInfo = this.Context.EntityMaintenance.GetEntityInfo(navEntity); + var childDb = this.Context; + childDb = GetCrossDatabase(childDb, navEntityInfo.Type); + childDb.InitMappingInfo(navEntityInfo.Type); + var navColumn = navEntityInfo.Columns.FirstOrDefault(it => it.PropertyName == navObjectNameColumnInfo.Navigat.Name); + Check.ExceptionEasy(navColumn == null, $"{navEntityInfo.EntityName} not found {navObjectNameColumnInfo.Navigat.Name} ", $"实体 {navEntityInfo.EntityName} 未找到导航配置列 {navObjectNameColumnInfo.Navigat.Name} "); + //var navType = navObjectNamePropety.PropertyType; + var listItemPkColumn = listItemEntity.Columns.Where(it => it.IsPrimarykey).FirstOrDefault(); + Check.ExceptionEasy(listItemPkColumn == null && navObjectNameColumnInfo.Navigat.Name2 == null, listItemEntity.EntityName + " not primary key", listItemEntity.EntityName + "没有主键"); + if (navObjectNameColumnInfo.Navigat.Name2.HasValue()) + { + listItemPkColumn = listItemEntity.Columns.Where(it => it.PropertyName == navObjectNameColumnInfo.Navigat.Name2).FirstOrDefault(); + Check.ExceptionEasy(listItemPkColumn == null, $"{navObjectNameColumnInfo.PropertyName} Navigate is error ", $"{navObjectNameColumnInfo.PropertyName}导航配置错误,可能顺序反了。"); + } + var ids = list.Select(it => it.GetType().GetProperty(listItemPkColumn.PropertyName).GetValue(it)).Select(it => it == null ? "null" : it).Distinct().ToList(); + List conditionalModels = new List(); + if (IsEnumNumber(navColumn)) + { + ids = ids.Select(it => Convert.ToInt64(it)).Cast().ToList(); + } + if (navColumn?.UnderType?.Name == UtilConstants.StringType.Name) + { + ids = ids.Select(it => it?.ToString()?.Replace(",", "[comma]")).Cast().ToList(); + } + conditionalModels.Add((new ConditionalModel() + { + ConditionalType = ConditionalType.In, + FieldName = navColumn.DbColumnName, + FieldValue = String.Join(",", ids), + CSharpTypeName = listItemPkColumn?.UnderType?.Name + })); + var sqlObj = GetWhereSql(childDb, navObjectNameColumnInfo.Navigat.Name); + + if (list.Count != 0 && navObjectNamePropety.GetValue(list.First()) == null) + { + if (sqlObj.SelectString == null) + { + var columns = navEntityInfo.Columns.Where(it => !it.IsIgnore) + .Select(it => GetOneToManySelectByColumnInfo(it, childDb)).ToList(); + sqlObj.SelectString = String.Join(",", columns); + } + var navList = selector(childDb.Queryable(sqlObj.TableShortName).AS(GetDbTableName(navEntityInfo, sqlObj)).ClearFilter(QueryBuilder.RemoveFilters).Filter(this.QueryBuilder?.IsDisabledGobalFilter == true ? null : navEntityInfo.Type).AddParameters(sqlObj.Parameters).Where(conditionalModels).WhereIF(sqlObj.WhereString.HasValue(), sqlObj.WhereString).WhereIF(navObjectNameColumnInfo?.Navigat?.WhereSql != null, navObjectNameColumnInfo?.Navigat?.WhereSql).Select(sqlObj.SelectString).OrderByIF(sqlObj.OrderByString.HasValue(), sqlObj.OrderByString)); + if (navList.HasValue()) + { + //var setValue = navList + // .Where(x => navColumn.PropertyInfo.GetValue(x).ObjToString() == listItemPkColumn.PropertyInfo.GetValue(item).ObjToString()).ToList(); + var groupQuery = (from l in list.Distinct() + join n in navList + on listItemPkColumn.PropertyInfo.GetValue(l).ObjToString() + equals navColumn.PropertyInfo.GetValue(n).ObjToString() + select new + { + l, + n + }).GroupBy(it => it.l).ToList(); + foreach (var item in groupQuery) + { + var itemSelectList = item.Select(it => it.n); + if (sqlObj.Skip != null) + { + itemSelectList = itemSelectList + .Skip(sqlObj.Skip.Value); + } + if (sqlObj.Take != null) + { + itemSelectList = itemSelectList + .Take(sqlObj.Take.Value); + } + if (sqlObj.MappingExpressions.HasValue()) + { + MappingFieldsHelper helper = new MappingFieldsHelper(); + helper.NavEntity = navEntityInfo; + helper.Context = this.Context; + helper.RootEntity = this.Context.EntityMaintenance.GetEntityInfo(); + helper.SetChildList(navObjectNameColumnInfo, item.Key, itemSelectList.ToList(), sqlObj.MappingExpressions); + } + else + { + + var instance = Activator.CreateInstance(navObjectNamePropety.PropertyType, true); + var ilist = instance as IList; + foreach (var value in itemSelectList.ToList()) + { + ilist.Add(value); + } + navObjectNamePropety.SetValue(item.Key, instance); + } + } + foreach (var item in list) + { + if (navObjectNamePropety.GetValue(item) == null) + { + var instance = Activator.CreateInstance(navObjectNamePropety.PropertyType, true); + navObjectNamePropety.SetValue(item, instance); + } + } + } + else + { + //No navigation data set new List() + foreach (var item in list) + { + var instance = Activator.CreateInstance(navObjectNamePropety.PropertyType, true); + navObjectNamePropety.SetValue(item, instance); + } + } + } + } + + private void Dynamic(List list, Func, List> selector, EntityInfo listItemEntity, System.Reflection.PropertyInfo navObjectNamePropety, EntityColumnInfo navObjectNameColumnInfo, Expression expression) + { + var args = navObjectNameColumnInfo.PropertyInfo.PropertyType.GetGenericArguments(); + if (args.Length == 0) + { + DynamicOneToOne(list, selector, listItemEntity, navObjectNamePropety, navObjectNameColumnInfo, expression); + return; + } + var navEntity = args[0]; + + var childDb = this.Context; + childDb = GetCrossDatabase(childDb, navEntity); + + childDb.InitMappingInfo(navEntity); + var navEntityInfo = childDb.EntityMaintenance.GetEntityInfo(navEntity); + var sqlObj = GetWhereSql(childDb, navObjectNameColumnInfo.Navigat.Name); + if (IsJsonMapping(navObjectNameColumnInfo, sqlObj)) + { + CreateDynamicMappingExpression(sqlObj, navObjectNameColumnInfo.Navigat.Name, navEntityInfo, listItemEntity); + if (sqlObj.WhereString == null) + { + sqlObj.WhereString = navObjectNameColumnInfo?.Navigat?.Name2; + } + } + Check.ExceptionEasy(sqlObj.MappingExpressions.IsNullOrEmpty(), $"{expression} error,dynamic need MappingField ,Demo: Includes(it => it.Books.MappingField(z=>z.studenId,()=>it.StudentId).ToList())", $"{expression} 解析出错,自定义映射需要 MappingField ,例子: Includes(it => it.Books.MappingField(z=>z.studenId,()=>it.StudentId).ToList())"); + if (list.Count != 0 && navObjectNamePropety.GetValue(list.First()) == null) + { + MappingFieldsHelper helper = new MappingFieldsHelper(); + helper.Context = childDb; + helper.NavEntity = navEntityInfo; + helper.RootEntity = childDb.EntityMaintenance.GetEntityInfo(); + var whereSql = helper.GetMppingSql(list, sqlObj.MappingExpressions); + var navList = selector(childDb.Queryable().AS(GetDbTableName(navEntityInfo, sqlObj)).AddParameters(sqlObj.Parameters).Where(whereSql, true).WhereIF(sqlObj.WhereString.HasValue(), sqlObj.WhereString).Select(sqlObj.SelectString).OrderByIF(sqlObj.OrderByString.HasValue(), sqlObj.OrderByString)); + if (navList.HasValue()) + { + foreach (var item in list) + { + helper.SetChildList(navObjectNameColumnInfo, item, navList, sqlObj.MappingExpressions); + } + } + } + + } + + private void DynamicOneToOne(List list, Func, List> selector, EntityInfo listItemEntity, System.Reflection.PropertyInfo navObjectNamePropety, EntityColumnInfo navObjectNameColumnInfo, Expression expression) + { + var navEntity = navObjectNameColumnInfo.PropertyInfo.PropertyType; + var navEntityInfo = this.Context.EntityMaintenance.GetEntityInfo(navEntity); + var childDb = this.Context; + childDb = GetCrossDatabase(childDb, navEntity); + this.Context.InitMappingInfo(navEntity); + var sqlObj = GetWhereSql(childDb, navObjectNameColumnInfo.Navigat.Name); + if (IsJsonMapping(navObjectNameColumnInfo, sqlObj)) + { + CreateDynamicMappingExpression(sqlObj, navObjectNameColumnInfo.Navigat.Name, navEntityInfo, listItemEntity); + if (sqlObj.WhereString == null) + { + sqlObj.WhereString = navObjectNameColumnInfo?.Navigat?.Name2; + } + } + Check.ExceptionEasy(sqlObj.MappingExpressions.IsNullOrEmpty(), $"{expression} error,dynamic need MappingField ,Demo: Includes(it => it.Books.MappingField(z=>z.studenId,()=>it.StudentId).ToList())", $"{expression}解析出错, 自定义映射需要 MappingField ,例子: Includes(it => it.Books.MappingField(z=>z.studenId,()=>it.StudentId).ToList())"); + if (list.Count != 0 && navObjectNamePropety.GetValue(list.First()) == null) + { + MappingFieldsHelper helper = new MappingFieldsHelper(); + helper.Context = this.Context; + helper.NavEntity = navEntityInfo; + helper.RootEntity = this.Context.EntityMaintenance.GetEntityInfo(); + var whereSql = helper.GetMppingSql(list, sqlObj.MappingExpressions); + var navList = selector(childDb.Queryable().AS(GetDbTableName(navEntityInfo, sqlObj)).AddParameters(sqlObj.Parameters).Where(whereSql, true).WhereIF(sqlObj.WhereString.HasValue(), sqlObj.WhereString).Select(sqlObj.SelectString).OrderByIF(sqlObj.OrderByString.HasValue(), sqlObj.OrderByString)); + if (navList.HasValue()) + { + foreach (var item in list) + { + helper.SetChildItem(navObjectNameColumnInfo, item, navList, sqlObj.MappingExpressions); + } + } + } + } + + private SqlInfo GetWhereSql(SqlSugarProvider db, string properyName = null) + { + if (_ListCallFunc == null || _ListCallFunc.Count == 0) return new SqlInfo(); + List where = new List(); + List oredrBy = new List(); + _ListCallFunc.Reverse(); + SqlInfo result = new SqlInfo(); + result.Parameters = new List(); + var isList = false; + int parameterIndex = 100; + foreach (var item in _ListCallFunc) + { + var method = item as MethodCallExpression; + var queryable = db.Queryable(); + if (method.Method.Name == "Where") + { + if (method.Arguments[1].Type == typeof(List)) + { + //var x=method.Arguments[1]; + var conditionals = ExpressionTool.GetExpressionValue(method.Arguments[1]) as List; + if (conditionals.Count > 0) + { + var whereObj = queryable.QueryBuilder.Builder.ConditionalModelToSql(conditionals); + where.Add(whereObj.Key); + if (whereObj.Value != null) + result.Parameters.AddRange(whereObj.Value); + } + } + else + { + queryable.QueryBuilder.LambdaExpressions.ParameterIndex = parameterIndex; + CheckHasRootShortName(method.Arguments[0], method.Arguments[1]); + var exp = method.Arguments[1]; + InitMappingtType(exp); + where.Add(" " + queryable.QueryBuilder.GetExpressionValue(exp, ResolveExpressType.WhereSingle).GetString()); + SetTableShortName(result, queryable); + parameterIndex = queryable.QueryBuilder.LambdaExpressions.ParameterIndex; + } + } + else if (method.Method.Name == "WhereIF") + { + var isOk = LambdaExpression.Lambda(method.Arguments[1]).Compile().DynamicInvoke(); + if (isOk.ObjToBool()) + { + queryable.QueryBuilder.LambdaExpressions.ParameterIndex = parameterIndex; + var exp = method.Arguments[2]; + InitMappingtType(exp); + CheckHasRootShortName(method.Arguments[1], method.Arguments[2]); + where.Add(" " + queryable.QueryBuilder.GetExpressionValue(exp, ResolveExpressType.WhereSingle).GetString()); + SetTableShortName(result, queryable); + parameterIndex = queryable.QueryBuilder.LambdaExpressions.ParameterIndex; + } + } + else if (method.Method.Name.IsIn("OrderBy", "ThenBy")) + { + var exp = method.Arguments[1]; + oredrBy.Add(" " + queryable.QueryBuilder.GetExpressionValue(exp, ResolveExpressType.WhereSingle).GetString()); + SetTableShortName(result, queryable); + } + else if (method.Method.Name == "MappingField") + { + if (result.MappingExpressions == null) + result.MappingExpressions = new List(); + result.MappingExpressions.Add(new MappingFieldsExpression() + { + LeftColumnExpression = method.Arguments[1], + RightColumnExpression = method.Arguments[2] + }); + } + else if (method.Method.Name == "Select") + { + Select(properyName, result, method, queryable); + } + else if (method.Method.Name.IsIn("OrderByDescending", "ThenByDescending")) + { + var exp = method.Arguments[1]; + oredrBy.Add(" " + queryable.QueryBuilder.GetExpressionValue(exp, ResolveExpressType.WhereSingle).GetString() + " DESC"); + } + else if (method.Method.Name == "Skip") + { + var exp = method.Arguments[1]; + if (exp is BinaryExpression) + { + result.Skip = (int)ExpressionTool.DynamicInvoke(exp); + } + else + { + result.Skip = (int)ExpressionTool.GetExpressionValue(exp); + } + } + else if (method.Method.Name == "Take") + { + var exp = method.Arguments[1]; + if (exp is BinaryExpression) + { + result.Take = (int)ExpressionTool.DynamicInvoke(exp); + } + else + { + result.Take = (int)ExpressionTool.GetExpressionValue(exp); + } + } + else if (method.Method.Name == "ToList") + { + if (method.Arguments.Count > 1) + { + Select(properyName, result, method, queryable); + } + isList = true; + } + else if (method.Method.Name == "SplitTable") + { + var exp = (Expression, IEnumerable>>)(item as MethodCallExpression).Arguments[1]; + result.SplitTable = exp.Compile(); + } + else + { + Check.ExceptionEasy($"no support {item}", $"不支持表达式{item} 不支持方法{method.Method.Name}"); + } + if (queryable.QueryBuilder.Parameters != null) + result.Parameters.AddRange(queryable.QueryBuilder.Parameters); + } + if (where.Count != 0) + { + Check.ExceptionEasy(isList == false, $"{_ListCallFunc.First()} need is ToList()", $"{_ListCallFunc.First()} 需要ToList"); + result.WhereString = String.Join(" AND ", where); + } + if (oredrBy.Count != 0) + { + Check.ExceptionEasy(isList == false, $"{_ListCallFunc.First()} need is ToList()", $"{_ListCallFunc.First()} 需要ToList"); + result.OrderByString = String.Join(" , ", oredrBy); + } + if (result.SelectString.HasValue()) + { + Check.ExceptionEasy(isList == false, $"{_ListCallFunc.First()} need is ToList()", $"{_ListCallFunc.First()} 需要ToList"); + result.OrderByString = String.Join(" , ", oredrBy); + } + return result; + } + + private void Select(string properyName, SqlInfo result, MethodCallExpression method, ISugarQueryable queryable) + { + var exp = method.Arguments[1]; + var newExp = (exp as LambdaExpression).Body; + var types = exp.Type.GetGenericArguments(); + var ps = ExpressionTool.GetParameters(exp).Select(it => it.Name).Distinct().ToList(); + if (ps.Count > 1 && result.TableShortName.IsNullOrEmpty()) + { + result.TableShortName = ps[0]; + } + if (types?.Length > 0) + { + var type = types[0]; + var entityInfo = this.Context.EntityMaintenance.GetEntityInfo(type); + this.Context.InitMappingInfo(type); + Check.ExceptionEasy(newExp.Type != entityInfo.Type, $" new {newExp.Type.Name}is error ,use Select(it=>new {entityInfo.Type.Name})", $"new {newExp.Type.Name}是错误的,请使用Select(it=>new {entityInfo.Type.Name})"); + if (!entityInfo.Columns.Any(x => x.Navigat != null)) + { + result.SelectString = (" " + queryable.QueryBuilder.GetExpressionValue(exp, ResolveExpressType.SelectSingle).GetString()); + } + else + { + var pkInfo = entityInfo.Columns.FirstOrDefault(x => x.IsPrimarykey); + result.SelectString = (" " + queryable.QueryBuilder.GetExpressionValue(exp, ResolveExpressType.SelectSingle).GetString()); + if (ExpressionTool.ContainsTwoLevelAccess(exp)) + { + var shortName = ExpressionTool.GetParameters(exp).FirstOrDefault()?.Name; + if (shortName.HasValue()) + { + if (result.TableShortName == null) + { + result.TableShortName = shortName; + result.IsSelectNav = true; + } + } + } + if (pkInfo != null) + { + var pkName = pkInfo.DbColumnName; + AppColumns(result, queryable, pkName); + } + foreach (var nav in entityInfo.Columns.Where(x => x.Navigat != null && x.Navigat.NavigatType == NavigateType.OneToOne)) + { + var navColumn = entityInfo.Columns.FirstOrDefault(it => it.PropertyName == nav.Navigat.Name); + if (navColumn != null) + { + AppColumns(result, queryable, navColumn.DbColumnName); + } + } + foreach (var nav in entityInfo.Columns.Where(x => x.Navigat != null && x.Navigat.NavigatType == NavigateType.OneToMany && x.Navigat.Name2 != null)) + { + var navColumn = entityInfo.Columns.FirstOrDefault(it => it.PropertyName == nav.Navigat.Name2); + if (navColumn != null) + { + AppColumns(result, queryable, navColumn.DbColumnName); + } + } + result.SelectString = result.SelectString.TrimStart(','); + if (string.IsNullOrEmpty(result.SelectString)) + { + result.SelectString = null; + } + } + if (properyName != null) + { + var fkColumnsInfo = entityInfo.Columns.FirstOrDefault(x => x.PropertyName == properyName); + var pkColumnsInfo = entityInfo.Columns.FirstOrDefault(x => x.IsPrimarykey); + if (fkColumnsInfo != null) + { + var fkName = fkColumnsInfo.DbColumnName; + AppColumns(result, queryable, fkName); + } + if (pkColumnsInfo != null && fkColumnsInfo == null && result.SelectString?.Contains(queryable.SqlBuilder.GetTranslationColumnName(pkColumnsInfo.DbColumnName)) == false) + { + AppColumns(result, queryable, pkColumnsInfo.DbColumnName); + } + } + } + } + + private static void SetTableShortName(SqlInfo result, ISugarQueryable queryable) + { + if (queryable.QueryBuilder.TableShortName.HasValue() && result.TableShortName.IsNullOrEmpty()) + { + result.TableShortName = queryable.QueryBuilder.TableShortName; + } + } + + + private SqlSugarProvider GetCrossDatabase(SqlSugarProvider db, Type type) + { + if (IsCrossQueryWithAttr == false && this.CrossQueryItems == null) + { + return db; + } + else if (IsCrossQueryWithAttr) + { + var tenant = type.GetCustomAttribute(); + if (tenant != null) + { + return db.Root.GetConnection(tenant.configId); + } + else + { + return db; + } + } + else if (this.CrossQueryItems?.Count > 0 && this.CrossQueryItems.TryGetValue(type.FullName, out string? value)) + { + var result = db.Root.GetConnection(value); + return result; + } + else + { + return db; + } + } + + private static void AppColumns(SqlInfo result, ISugarQueryable queryable, string columnName) + { + var selectPkName = queryable.SqlBuilder.GetTranslationColumnName(columnName); + if (result.IsSelectNav) + { + if (result.SelectString?.Contains($" {selectPkName.ToLower()} AS {selectPkName.ToLower()}", StringComparison.CurrentCultureIgnoreCase) == false) + { + result.SelectString = result.SelectString + "," + (selectPkName + " AS " + selectPkName); + } + } + if (result.SelectString?.Contains(selectPkName, StringComparison.CurrentCultureIgnoreCase) == false) + { + result.SelectString = result.SelectString + "," + (selectPkName + " AS " + selectPkName); + } + } + public void CheckHasRootShortName(Expression rootExpression, Expression childExpression) + { + var rootShortName = GetShortName(rootExpression); + if (rootShortName.HasValue() && childExpression.ToString().Contains($" {rootShortName}.")) + { + Check.ExceptionEasy($".Where({childExpression}) no support {rootShortName}.Field, Use .MappingField", $".Where({childExpression})禁止出{rootShortName}.字段 , 你可以使用.MappingField(z=>z.字段,()=>{rootShortName}.字段) 与主表字段进行过滤"); + } + else if (rootShortName.HasValue() && childExpression.ToString().Contains($"({rootShortName}.")) + { + Check.ExceptionEasy($".Where({childExpression}) no support {rootShortName}.Field, Use .MappingField", $".Where({childExpression})禁止出{rootShortName}.字段 , 你可以使用.MappingField(z=>z.字段,()=>{rootShortName}.字段) 与主表字段进行过滤"); + } + } + + private static string GetShortName(Expression expression1) + { + string shortName = null; + if (expression1 is MemberExpression) + { + var shortNameExpression = (expression1 as MemberExpression).Expression; + if (shortNameExpression != null && shortNameExpression.Type == typeof(T)) + { + if (shortNameExpression is ParameterExpression) + { + shortName = (shortNameExpression as ParameterExpression).Name; + } + } + } + return shortName; + } + + + private string GetDbTableName(EntityInfo navEntityInfo, SqlInfo sqlInfo) + { + if (navEntityInfo.Type.GetCustomAttribute() != null && sqlInfo.SplitTable != null) + { + return "(" + this.Context.QueryableByObject(navEntityInfo.Type).SplitTable(sqlInfo.SplitTable).ToSqlString() + ") split_table"; + } + else if (navEntityInfo.Type.GetCustomAttribute() != null) + { + return "(" + this.Context.QueryableByObject(navEntityInfo.Type).SplitTable().ToSqlString() + ") split_table"; + } + else + { + return navEntityInfo.DbTableName; + } + } + + private void InitMappingtType(Expression exp) + { + if (exp is LambdaExpression) + { + var pars = (exp as LambdaExpression).Parameters; + if (pars != null) + { + foreach (var item in pars) + { + this.Context.InitMappingInfo(item.Type); + } + } + } + } + + + private bool IsEnumNumber(EntityColumnInfo navPkColumn) + { + return + navPkColumn?.UnderType?.IsEnum() == true && + navPkColumn?.SqlParameterDbType == null && + this.Context?.CurrentConnectionConfig?.MoreSettings?.TableEnumIsString != true; + } + private static bool IsOwnsOneProperty(EntityInfo listItemEntity, EntityColumnInfo navObjectNameColumnInfo) + { + return listItemEntity.Columns.Any(it => it.IsOwnsOne) && !listItemEntity.Type.GetProperties().Any(it => it.PropertyType.Name == navObjectNameColumnInfo.Navigat.Name); + } + + private static bool IsJsonMapping(EntityColumnInfo navObjectNameColumnInfo, SqlInfo sqlObj) + { + return sqlObj.MappingExpressions == null && navObjectNameColumnInfo.Navigat.Name.HasValue(); + } + + private void CreateDynamicMappingExpression(SqlInfo sqlObj, string name, EntityInfo navEntityInfo, EntityInfo listItemEntity) + { + var json = Newtonsoft.Json.Linq.JArray.Parse(name); + sqlObj.MappingExpressions = new List(); + foreach (var item in json) + { + string m = item["m"] + ""; + string c = item["c"] + ""; + Check.ExceptionEasy(m.IsNullOrEmpty() || c.IsNullOrEmpty(), $"{name} Navigation json format error, see documentation", $"{name}导航json格式错误,请看文档"); + var cColumn = navEntityInfo.Columns.FirstOrDefault(it => it.PropertyName.EqualCase(c)); + Check.ExceptionEasy(cColumn == null, $"{c} does not exist in {navEntityInfo.EntityName}", $"{c}不存在于{navEntityInfo.EntityName}"); + var mColumn = listItemEntity.Columns.FirstOrDefault(it => it.PropertyName.EqualCase(m)); + Check.ExceptionEasy(cColumn == null, $"{m} does not exist in {listItemEntity.EntityName}", $"{m}不存在于{listItemEntity.EntityName}"); + sqlObj.MappingExpressions.Add(new MappingFieldsExpression() + { + + LeftEntityColumn = cColumn, + RightEntityColumn = mColumn, + }); + } + } + + private string GetOneToManySelectByColumnInfo(EntityColumnInfo it, ISqlSugarClient db) + { + QueryBuilder QueryBuilder = InstanceFactory.GetQueryBuilderWithContext(db); + if (it.QuerySql.HasValue()) + { + return it.QuerySql + " AS " + QueryBuilder.Builder.GetTranslationColumnName(it.PropertyName); + } + if (it.ForOwnsOnePropertyInfo != null) + { + return QueryBuilder.Builder.GetTranslationColumnName(it.DbColumnName); + } + return QueryBuilder.Builder.GetTranslationColumnName(it.DbColumnName) + " AS " + QueryBuilder.Builder.GetTranslationColumnName(it.PropertyName); + } + private string GetOneToOneSelectByColumnInfo(EntityColumnInfo it, ISqlSugarClient db) + { + QueryBuilder QueryBuilder = InstanceFactory.GetQueryBuilderWithContext(db); + if (it.QuerySql.HasValue()) + { + return it.QuerySql + " AS " + QueryBuilder.Builder.GetTranslationColumnName(it.PropertyName); + } + if (it.ForOwnsOnePropertyInfo != null) + { + + return QueryBuilder.Builder.GetTranslationColumnName(it.DbColumnName); + } + return QueryBuilder.Builder.GetTranslationColumnName(it.DbColumnName) + " AS " + QueryBuilder.Builder.GetTranslationColumnName(it.PropertyName); + } + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/QueryMethodInfo.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/QueryMethodInfo.cs new file mode 100644 index 000000000..abbbaf491 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/QueryMethodInfo.cs @@ -0,0 +1,428 @@ +using System.Data; +using System.Text.RegularExpressions; + + +namespace SqlSugar +{ + public class QueryMethodInfo + { + public object QueryableObj { get; internal set; } + public SqlSugarProvider Context { get; internal set; } + public Type EntityType { get; set; } + + + #region Json 2 sql api + #endregion + + #region Sql API + + public QueryMethodInfo MergeTable() + { + var method = QueryableObj.GetType().GetMethod("MergeTable"); + this.QueryableObj = method.Invoke(QueryableObj, Array.Empty()); + return this; + } + public QueryMethodInfo AS(string tableName) + { + string shortName = $"{tableName}_1"; + if (!Regex.IsMatch(shortName, @"^\w+$")) + { + shortName = "maintable"; + } + var method = QueryableObj.GetType().GetMyMethod("AS", 2, typeof(string), typeof(string)); + this.QueryableObj = method.Invoke(QueryableObj, new object[] { tableName, shortName }); + return this; + } + public QueryMethodInfo AS(string tableName, string shortName) + { + var method = QueryableObj.GetType().GetMyMethod("AS", 2, typeof(string), typeof(string)); + this.QueryableObj = method.Invoke(QueryableObj, new object[] { tableName, shortName }); + return this; + } + public QueryMethodInfo OrderBy(List models) + { + var method = QueryableObj.GetType().GetMyMethod("OrderBy", 1, typeof(List)); + this.QueryableObj = method.Invoke(QueryableObj, new object[] { models }); + return this; + } + public QueryMethodInfo OrderBy(string orderBySql) + { + var method = QueryableObj.GetType().GetMyMethod("OrderBy", 1, typeof(string)); + this.QueryableObj = method.Invoke(QueryableObj, new object[] { orderBySql }); + return this; + } + public QueryMethodInfo AddJoinInfo(string tableName, string shortName, string onWhere, JoinType type = JoinType.Left) + { + var method = QueryableObj.GetType().GetMyMethod("AddJoinInfo", 4, typeof(string), typeof(string), typeof(string), typeof(JoinType)); + this.QueryableObj = method.Invoke(QueryableObj, new object[] { tableName, shortName, onWhere, type }); + return this; + } + public QueryMethodInfo AddJoinInfo(string tableName, string shortName, IFuncModel onFunc, JoinType type = JoinType.Left) + { + var method = QueryableObj.GetType().GetMyMethod("AddJoinInfo", 4, typeof(string), typeof(string), typeof(IFuncModel), typeof(JoinType)); + this.QueryableObj = method.Invoke(QueryableObj, new object[] { tableName, shortName, onFunc, type }); + return this; + } + public QueryMethodInfo AddJoinInfo(List joinInfoParameters) + { + foreach (var item in joinInfoParameters) + { + AddJoinInfo(item.TableName, item.ShortName, item.Models, item.Type); + } + return this; + } + public QueryMethodInfo AddJoinInfo(Type joinEntityType, Dictionary keyIsShortName_ValueIsType_Dictionary, FormattableString expOnWhere, JoinType type = JoinType.Left) + { + var method = QueryableObj.GetType().GetMyMethod("AddJoinInfo", 4, typeof(Type), typeof(Dictionary), typeof(FormattableString), typeof(JoinType)); + this.QueryableObj = method.Invoke(QueryableObj, new object[] { joinEntityType, keyIsShortName_ValueIsType_Dictionary, expOnWhere, type }); + return this; + } + public QueryMethodInfo AddJoinInfo(Type joinEntityType, string shortName, string onWhere, JoinType type = JoinType.Left) + { + var method = QueryableObj.GetType().GetMyMethod("AddJoinInfo", 4, typeof(Type), typeof(string), typeof(string), typeof(JoinType)); + this.QueryableObj = method.Invoke(QueryableObj, new object[] { joinEntityType, shortName, onWhere, type }); + return this; + } + public QueryMethodInfo GroupBy(List models) + { + var method = QueryableObj.GetType().GetMyMethod("GroupBy", 1, typeof(List)); + this.QueryableObj = method.Invoke(QueryableObj, new object[] { models }); + return this; + } + public QueryMethodInfo GroupBy(string groupBySql) + { + var method = QueryableObj.GetType().GetMyMethod("GroupBy", 1, typeof(string)); + this.QueryableObj = method.Invoke(QueryableObj, new object[] { groupBySql }); + return this; + } + public QueryMethodInfo Where(string expShortName, FormattableString expressionString) + { + var method = QueryableObj.GetType().GetMyMethod("Where", 2, typeof(string), typeof(FormattableString)); + this.QueryableObj = method.Invoke(QueryableObj, new object[] { expShortName, expressionString }); + return this; + } + public QueryMethodInfo Where(Dictionary keyIsShortName_ValueIsType_Dictionary, FormattableString expressionString) + { + var method = QueryableObj.GetType().GetMyMethod("Where", 2, typeof(Dictionary), typeof(FormattableString)); + this.QueryableObj = method.Invoke(QueryableObj, new object[] { keyIsShortName_ValueIsType_Dictionary, expressionString }); + return this; + } + public QueryMethodInfo Where(List conditionalModels) + { + var method = QueryableObj.GetType().GetMyMethod("Where", 1, typeof(List)); + this.QueryableObj = method.Invoke(QueryableObj, new object[] { conditionalModels }); + return this; + } + + public QueryMethodInfo Where(IFuncModel model) + { + var method = QueryableObj.GetType().GetMyMethod("Where", 1, typeof(IFuncModel)); + this.QueryableObj = method.Invoke(QueryableObj, new object[] { model }); + return this; + } + + public QueryMethodInfo Where(List conditionalModels, bool isWrap) + { + var method = QueryableObj.GetType().GetMyMethod("Where", 2, typeof(List), typeof(bool)); + this.QueryableObj = method.Invoke(QueryableObj, new object[] { conditionalModels, isWrap }); + return this; + } + public QueryMethodInfo Where(string sql, object parameters = null) + { + var method = QueryableObj.GetType().GetMyMethodNoGen("Where", 2, typeof(string), typeof(object)); + this.QueryableObj = method.Invoke(QueryableObj, new object[] { sql, parameters }); + return this; + } + public QueryMethodInfo Having(IFuncModel model) + { + var method = QueryableObj.GetType().GetMyMethod("Having", 1, typeof(IFuncModel)); + this.QueryableObj = method.Invoke(QueryableObj, new object[] { model }); + return this; + } + public QueryMethodInfo Having(string sql, object parameters = null) + { + var method = QueryableObj.GetType().GetMyMethod("Having", 2, typeof(string), typeof(object)); + this.QueryableObj = method.Invoke(QueryableObj, new object[] { sql, parameters }); + return this; + } + public QueryMethodInfo SplitTable(Func, IEnumerable> getTableNamesFunc) + { + var method = QueryableObj.GetType().GetMyMethod("SplitTable", 1, typeof(Func, IEnumerable>)); + this.QueryableObj = method.Invoke(QueryableObj, new object[] { getTableNamesFunc }); + return this; + } + public QueryMethodInfo SplitTable(DateTime begintTime, DateTime endTime) + { + var method = QueryableObj.GetType().GetMyMethod("SplitTable", 2, typeof(DateTime), typeof(DateTime)); + this.QueryableObj = method.Invoke(QueryableObj, new object[] { begintTime, endTime }); + return this; + } + public QueryMethodInfo SplitTable() + { + var method = QueryableObj.GetType().GetMyMethod("SplitTable", 0); + this.QueryableObj = method.Invoke(QueryableObj, Array.Empty()); + return this; + } + public QueryMethodInfo Select(string expShortName, List columns, params object[] args) + { + var method = QueryableObj.GetType().GetMyMethod("Select", 3, typeof(string), typeof(List), typeof(object[])); + this.QueryableObj = method.Invoke(QueryableObj, new object[] { expShortName, columns, args }); + return this; + } + public QueryMethodInfo Select(List models) + { + var method = QueryableObj.GetType().GetMyMethod("Select", 1, typeof(List)); + this.QueryableObj = method.Invoke(QueryableObj, new object[] { models }); + return this; + } + public QueryMethodInfo Select(string expShortName, FormattableString expSelect, Type resultType) + { + var method = QueryableObj.GetType().GetMyMethod("Select", 3, typeof(string), typeof(FormattableString), typeof(Type)); + method = method.MakeGenericMethod(resultType); + this.QueryableObj = method.Invoke(QueryableObj, new object[] { expShortName, expSelect, resultType }); + return this; + } + public QueryMethodInfo Select(Dictionary keyIsShortName_ValueIsType_Dictionary, FormattableString expSelect, Type resultType) + { + var method = QueryableObj.GetType().GetMyMethod("Select", 3, typeof(Dictionary), typeof(FormattableString), typeof(Type)); + method = method.MakeGenericMethod(resultType); + this.QueryableObj = method.Invoke(QueryableObj, new object[] { keyIsShortName_ValueIsType_Dictionary, expSelect, resultType }); + return this; + } + public QueryMethodInfo Select(string selectorSql) + { + var method = QueryableObj.GetType().GetMyMethod("Select", 1, typeof(string)) + .MakeGenericMethod(EntityType); + this.QueryableObj = method.Invoke(QueryableObj, new object[] { selectorSql }); + return this; + } + + public QueryMethodInfo Select(string selectorSql, Type selectType) + { + var method = QueryableObj.GetType().GetMyMethod("Select", 1, typeof(string)) + .MakeGenericMethod(selectType); + this.QueryableObj = method.Invoke(QueryableObj, new object[] { selectorSql }); + return this; + } + + #endregion + + #region Nav + + public QueryMethodInfo IncludesAllFirstLayer(params string[] ignoreNavPropertyNames) + { + var method = QueryableObj.GetType().GetMyMethod("IncludesAllFirstLayer", 1, typeof(string[])); + this.QueryableObj = method.Invoke(QueryableObj, new object[] { ignoreNavPropertyNames }); + return this; + } + public QueryMethodInfo Includes(string navProperyName) + { + var method = QueryableObj.GetType().GetMyMethod("IncludesByNameString", 1, typeof(string)); + this.QueryableObj = method.Invoke(QueryableObj, new object[] { navProperyName }); + return this; + } + public QueryMethodInfo IgnoreColumns(params string[] ignoreColumns) + { + var method = QueryableObj.GetType().GetMyMethod("IgnoreColumns", 1, typeof(string[])); + this.QueryableObj = method.Invoke(QueryableObj, new object[] { ignoreColumns }); + return this; + } + public QueryMethodInfo Includes(string navProperyName, string thenNavProperyName2) + { + var method = QueryableObj.GetType().GetMyMethod("IncludesByNameString", 2, typeof(string), typeof(string)); + this.QueryableObj = method.Invoke(QueryableObj, new object[] { navProperyName, thenNavProperyName2 }); + return this; + } + public QueryMethodInfo Includes(string navProperyName, string thenNavProperyName2, string thenNavProperyName3) + { + var method = QueryableObj.GetType().GetMyMethod("IncludesByNameString", 3, typeof(string), typeof(string), typeof(string)); + this.QueryableObj = method.Invoke(QueryableObj, new object[] { navProperyName, thenNavProperyName2, thenNavProperyName3 }); + return this; + } + #endregion + + #region Result + + public void IntoTable(Type type, string tableName) + { + var method = QueryableObj.GetType().GetMyMethod("IntoTable", 2, typeof(Type), typeof(string)); + var reslt = method.Invoke(QueryableObj, new object[] { type, tableName }); + } + public object ToPageList(int pageNumber, int pageSize) + { + var method = QueryableObj.GetType().GetMyMethod("ToPageList", 2, typeof(int), typeof(int)); + var reslt = method.Invoke(QueryableObj, new object[] { pageNumber, pageSize }); + return reslt; + } + public object ToPageList(int pageNumber, int pageSize, ref int count) + { + var method = QueryableObj.GetType().GetMyMethod("ToPageList", 3, typeof(int), typeof(int), typeof(int).MakeByRefType()); + var parameters = new object[] { pageNumber, pageSize, count }; + var reslt = method.Invoke(QueryableObj, parameters); + count = parameters.Last().ObjToInt(); + return reslt; + } + public object ToList() + { + var method = QueryableObj.GetType().GetMyMethod("ToList", 0); + var reslt = method.Invoke(QueryableObj, Array.Empty()); + return reslt; + } + public DataTable ToDataTablePage(int pageNumber, int pageSize, ref int count) + { + var method = QueryableObj.GetType().GetMyMethod("ToDataTablePage", 3, typeof(int), typeof(int), typeof(int).MakeByRefType()); + var parameters = new object[] { pageNumber, pageSize, count }; + var reslt = (DataTable)method.Invoke(QueryableObj, parameters); + count = parameters.Last().ObjToInt(); + return reslt; + } + public DataTable ToDataTable() + { + var method = QueryableObj.GetType().GetMyMethod("ToDataTable", 0); + var reslt = (DataTable)method.Invoke(QueryableObj, Array.Empty()); + return reslt; + } + public string ToSqlString() + { + var method = QueryableObj.GetType().GetMyMethod("ToSqlString", 0); + var reslt = method.Invoke(QueryableObj, Array.Empty()); + return (string)reslt; + } + public KeyValuePair> ToSql() + { + var method = QueryableObj.GetType().GetMyMethod("ToSql", 0); + var reslt = method.Invoke(QueryableObj, Array.Empty()); + return (KeyValuePair>)reslt; + } + public object InSingle(object pkValue) + { + var method = QueryableObj.GetType().GetMyMethod("InSingle", 1); + var reslt = method.Invoke(QueryableObj, new object[] { pkValue }); + return reslt; + } + public bool CreateView(string viewNameFomat) + { + if (viewNameFomat?.Contains("{0}") != true) + { + Check.ExceptionEasy("need{0}", "需要{0}表名的占位符"); + } + var entityInfo = this.Context.EntityMaintenance.GetEntityInfo(EntityType); + var viewName = string.Format(viewNameFomat, entityInfo.DbTableName); + if (!this.Context.DbMaintenance.GetViewInfoList().Any(it => it.Name.EqualCase(viewName))) + { + var method = QueryableObj.GetType().GetMyMethod("ToSqlString", 0); + var reslt = (string)method.Invoke(QueryableObj, Array.Empty()); + var sql = $"CREATE VIEW {viewName} AS {Environment.NewLine} {reslt}"; + this.Context.Ado.ExecuteCommand(sql); + return true; + } + else + { + return false; + } + } + public object First() + { + var method = QueryableObj.GetType().GetMyMethod("First", 0); + var reslt = method.Invoke(QueryableObj, Array.Empty()); + return reslt; + } + public bool Any() + { + var method = QueryableObj.GetType().GetMyMethod("Any", 0); + var reslt = method.Invoke(QueryableObj, Array.Empty()); + return Convert.ToBoolean(reslt); + } + public int Count() + { + var method = QueryableObj.GetType().GetMyMethod("Count", 0); + var reslt = method.Invoke(QueryableObj, Array.Empty()); + return Convert.ToInt32(reslt); + } + public object ToTree(string childPropertyName, string parentIdPropertyName, object rootValue, string primaryKeyPropertyName) + { + var method = QueryableObj.GetType().GetMyMethod("ToTree", 4, typeof(string), typeof(string), typeof(object), typeof(string)); + var reslt = method.Invoke(QueryableObj, new object[] { childPropertyName, parentIdPropertyName, rootValue, primaryKeyPropertyName }); + return reslt; + } + + #endregion + + #region Result Async + public async Task ToPageListAsync(int pageNumber, int pageSize) + { + var method = QueryableObj.GetType().GetMyMethod("ToPageListAsync", 2, typeof(int), typeof(int)); + Task task = (Task)method.Invoke(QueryableObj, new object[] { pageNumber, pageSize }); + return await GetTask(task).ConfigureAwait(false); + } + public async Task ToPageListAsync(int pageNumber, int pageSize, RefAsync count) + { + var method = QueryableObj.GetType().GetMyMethod("ToPageListAsync", 3, typeof(int), typeof(int), typeof(RefAsync)); + var parameters = new object[] { pageNumber, pageSize, count }; + var task = (Task)method.Invoke(QueryableObj, parameters); + return await GetTask(task).ConfigureAwait(false); + } + public async Task ToListAsync() + { + var method = QueryableObj.GetType().GetMyMethod("ToListAsync", 0); + var task = (Task)method.Invoke(QueryableObj, Array.Empty()); + return await GetTask(task).ConfigureAwait(false); + } + public async Task ToDataTablePageAsync(int pageNumber, int pageSize, RefAsync count) + { + var method = QueryableObj.GetType().GetMyMethod("ToDataTablePageAsync", 3, typeof(int), typeof(int), typeof(RefAsync)); + var parameters = new object[] { pageNumber, pageSize, count }; + var task = (Task)method.Invoke(QueryableObj, parameters); + count = parameters.Last().ObjToInt(); + return await GetTask(task).ConfigureAwait(false); + } + public async Task ToDataTableAsync() + { + var method = QueryableObj.GetType().GetMyMethod("ToDataTableAsync", 0); + var task = (Task)method.Invoke(QueryableObj, Array.Empty()); + return await GetTask(task).ConfigureAwait(false); + } + public async Task FirstAsync() + { + var method = QueryableObj.GetType().GetMyMethod("FirstAsync", 0); + var task = (Task)method.Invoke(QueryableObj, Array.Empty()); + return await GetTask(task).ConfigureAwait(false); + } + public async Task AnyAsync() + { + var method = QueryableObj.GetType().GetMyMethod("AnyAsync", 0); + var reslt = method.Invoke(QueryableObj, Array.Empty()); + return await ((Task)reslt).ConfigureAwait(false); + } + public async Task CountAsync() + { + var method = QueryableObj.GetType().GetMyMethod("CountAsync", 0); + var reslt = method.Invoke(QueryableObj, Array.Empty()); + return await ((Task)reslt).ConfigureAwait(false); + } + public async Task InSingleAsync(object pkValue) + { + var method = QueryableObj.GetType().GetMyMethod("InSingleAsync", 1); + var task = (Task)method.Invoke(QueryableObj, new object[] { pkValue }); + return await GetTask(task).ConfigureAwait(false); + } + + public async Task ToTreeAsync(string childPropertyName, string parentIdPropertyName, object rootValue, string primaryKeyPropertyName) + { + var method = QueryableObj.GetType().GetMyMethod("ToTreeAsync", 4, typeof(string), typeof(string), typeof(object), typeof(string)); + var task = (Task)method.Invoke(QueryableObj, new object[] { childPropertyName, parentIdPropertyName, rootValue, primaryKeyPropertyName }); + return await GetTask(task).ConfigureAwait(false); + } + #endregion + + #region Helper + private static async Task GetTask(Task task) + { + await task.ConfigureAwait(false); // 等待任务完成 + var resultProperty = task.GetType().GetProperty("Result"); + var result = resultProperty.GetValue(task); + return result; + } + #endregion + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/QueryableAccessory.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/QueryableAccessory.cs new file mode 100644 index 000000000..2c7a31235 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/QueryableAccessory.cs @@ -0,0 +1,9 @@ +namespace SqlSugar +{ + public class QueryableAccessory + { + protected ILambdaExpressions _LambdaExpressions; + protected bool _RestoreMapping = true; + protected int _InQueryableIndex = 100; + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/QueryableContext.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/QueryableContext.cs new file mode 100644 index 000000000..6fcd602a7 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/QueryableContext.cs @@ -0,0 +1,10 @@ +namespace SqlSugar +{ + public class MapperContext + { + public ISqlSugarClient context { get; set; } + + public List list { get; set; } + public Dictionary TempChildLists { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/QueryableExecuteSql.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/QueryableExecuteSql.cs new file mode 100644 index 000000000..c7d66fffd --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/QueryableExecuteSql.cs @@ -0,0 +1,948 @@ +using System.Data; +using System.Linq.Expressions; + +namespace SqlSugar +{ + + public partial class QueryableProvider : QueryableAccessory, ISugarQueryable + { + public virtual T Single() + { + if (QueryBuilder.OrderByValue.IsNullOrEmpty()) + { + QueryBuilder.OrderByValue = QueryBuilder.DefaultOrderByTemplate; + } + var oldSkip = QueryBuilder.Skip; + var oldTake = QueryBuilder.Take; + var oldOrderBy = QueryBuilder.OrderByValue; + QueryBuilder.Skip = null; + QueryBuilder.Take = null; + QueryBuilder.OrderByValue = null; + var result = this.ToList(); + QueryBuilder.Skip = oldSkip; + QueryBuilder.Take = oldTake; + QueryBuilder.OrderByValue = oldOrderBy; + if (result == null || result.Count == 0) + { + return default(T); + } + else if (result.Count >= 2) + { + Check.Exception(true, ErrorMessage.GetThrowMessage(".Single() result must not exceed one . You can use.First()", "使用single查询结果集不能大于1,适合主键查询,如果大于1你可以使用Queryable.First")); + return default(T); + } + else + { + return result.SingleOrDefault(); + } + } + public virtual T Single(Expression> expression) + { + _Where(expression); + var result = Single(); + this.QueryBuilder.WhereInfos.Remove(this.QueryBuilder.WhereInfos.Last()); + return result; + } + + public virtual T First() + { + if (QueryBuilder.OrderByValue.IsNullOrEmpty()) + { + QueryBuilder.OrderByValue = QueryBuilder.DefaultOrderByTemplate; + } + if (QueryBuilder.Skip.HasValue) + { + QueryBuilder.Take = 1; + return this.ToList().FirstOrDefault(); + } + else + { + QueryBuilder.Skip = 0; + QueryBuilder.Take = 1; + var result = this.ToList(); + if (result.HasValue()) + return result.FirstOrDefault(); + else + return default(T); + } + } + public virtual T First(Expression> expression) + { + _Where(expression); + var result = First(); + this.QueryBuilder.WhereInfos.Remove(this.QueryBuilder.WhereInfos.Last()); + return result; + } + + public virtual bool Any(Expression> expression) + { + _Where(expression); + var result = Any(); + this.QueryBuilder.WhereInfos.Remove(this.QueryBuilder.WhereInfos.Last()); + return result; + } + public virtual bool Any() + { + return this.Clone().Take(1).Select("1").ToList().Count > 0; + } + + public virtual List ToList(Expression> expression) + { + if (this.QueryBuilder.Includes?.Count > 0) + { + return NavSelectHelper.GetList(expression, this); + // var list = this.ToList().Select(expression.Compile()).ToList(); + // return list; + } + else + { + var list = this.Select(expression).ToList(); + return list; + } + } + + public virtual int Count() + { + if (this.QueryBuilder.Skip == null && + this.QueryBuilder.Take == null && + this.QueryBuilder.OrderByValue == null && + this.QueryBuilder.PartitionByValue == null && + this.QueryBuilder.SelectValue == null && + this.QueryBuilder.Includes == null && + this.QueryBuilder.IsDistinct == false) + { + if (StaticConfig.EnableAot) + { + var sqlobj = this.Clone().Select(" COUNT(1) ").ToSql(); + return this.Context.Ado.GetInt(sqlobj.Key, sqlobj.Value); + } + return this.Clone().Select(" COUNT(1) ").ToList().FirstOrDefault(); + } + MappingTableList expMapping; + int result; + _CountBegin(out expMapping, out result); + if (IsCache) + { + var cacheService = this.Context.CurrentConnectionConfig.ConfigureExternalServices.DataInfoCacheService; + result = CacheSchemeMain.GetOrCreate(cacheService, this.QueryBuilder, () => { return GetCount(); }, CacheTime, this.Context, CacheKey); + } + else + { + result = GetCount(); + } + _CountEnd(expMapping); + return result; + } + public virtual int Count(Expression> expression) + { + _Where(expression); + var result = Count(); + this.QueryBuilder.WhereInfos.Remove(this.QueryBuilder.WhereInfos.Last()); + return result; + } + + public virtual TResult Max(string maxField) + { + this.Select(string.Format(QueryBuilder.MaxTemplate, maxField)); + var result = this._ToList().SingleOrDefault(); + return result; + } + public virtual TResult Max(Expression> expression) + { + return _Max(expression); + } + + public virtual TResult Min(string minField) + { + this.Select(string.Format(QueryBuilder.MinTemplate, minField)); + var result = this._ToList().SingleOrDefault(); + return result; + } + public virtual TResult Min(Expression> expression) + { + return _Min(expression); + } + + public virtual TResult Sum(string sumField) + { + this.Select(string.Format(QueryBuilder.SumTemplate, sumField)); + var result = this._ToList().SingleOrDefault(); + return result; + } + public virtual TResult Sum(Expression> expression) + { + return _Sum(expression); + } + + public virtual TResult Avg(string avgField) + { + this.Select(string.Format(QueryBuilder.AvgTemplate, avgField)); + var result = this._ToList().SingleOrDefault(); + return result; + } + public virtual TResult Avg(Expression> expression) + { + return _Avg(expression); + } + public virtual T[] ToArray() + { + + var result = this.ToList(); + if (result.HasValue()) + return result.ToArray(); + else + return null; + } + + public virtual string ToJson() + { + if (IsCache) + { + var cacheService = this.Context.CurrentConnectionConfig.ConfigureExternalServices.DataInfoCacheService; + var result = CacheSchemeMain.GetOrCreate(cacheService, this.QueryBuilder, () => + { + return this.Context.Utilities.SerializeObject(this.ToList(), typeof(T)); + }, CacheTime, this.Context, CacheKey); + return result; + } + else + { + return this.Context.Utilities.SerializeObject(this.ToList(), typeof(T)); + } + } + public virtual string ToJsonPage(int pageIndex, int pageSize) + { + return this.Context.Utilities.SerializeObject(this.ToPageList(pageIndex, pageSize), typeof(T)); + } + public virtual string ToJsonPage(int pageIndex, int pageSize, ref int totalNumber) + { + return this.Context.Utilities.SerializeObject(this.ToPageList(pageIndex, pageSize, ref totalNumber), typeof(T)); + } + + #region 内存行转列 + + #region 同步 + public virtual DataTable ToPivotTable(Func columnSelector, Expression> rowSelector, Func, TData> dataSelector) + { + return this.ToList().ToPivotTable(columnSelector, rowSelector, dataSelector); + } + public virtual List ToPivotList(Func columnSelector, Expression> rowSelector, Func, TData> dataSelector) + { + return ToPivotEnumerable(columnSelector, rowSelector, dataSelector).ToList(); + } + public virtual IEnumerable ToPivotEnumerable(Func columnSelector, Expression> rowSelector, Func, TData> dataSelector) + { + return this.ToList().ToPivotList(columnSelector, rowSelector, dataSelector); + } + public virtual string ToPivotJson(Func columnSelector, Expression> rowSelector, Func, TData> dataSelector) + { + var list = ToPivotEnumerable(columnSelector, rowSelector, dataSelector).ToList(); + return this.Context.Utilities.SerializeObject(list); + } + #endregion + + #region 异步 + public virtual async Task ToPivotTableAsync(Func columnSelector, Expression> rowSelector, Func, TData> dataSelector) + { + return (await ToListAsync().ConfigureAwait(false)).ToPivotTable(columnSelector, rowSelector, dataSelector); + } + public virtual async Task> ToPivotListAsync(Func columnSelector, Expression> rowSelector, Func, TData> dataSelector) + { + return (await ToPivotEnumerableAsync(columnSelector, rowSelector, dataSelector).ConfigureAwait(false)).ToList(); + } + public virtual async Task> ToPivotEnumerableAsync(Func columnSelector, Expression> rowSelector, Func, TData> dataSelector) + { + return (await ToListAsync().ConfigureAwait(false)).ToPivotList(columnSelector, rowSelector, dataSelector); + } + public virtual async Task ToPivotJsonAsync(Func columnSelector, Expression> rowSelector, Func, TData> dataSelector) + { + var list = (await ToPivotEnumerableAsync(columnSelector, rowSelector, dataSelector).ConfigureAwait(false)).ToList(); + return this.Context.Utilities.SerializeObject(list); + } + #endregion + + #endregion + + public List ToChildList(Expression> parentIdExpression, object primaryKeyValue, bool isContainOneself = true) + { + var entity = this.Context.EntityMaintenance.GetEntityInfo(); + var pk = GetTreeKey(entity); + var list = this.ToList(); + return GetChildList(parentIdExpression, pk, list, primaryKeyValue, isContainOneself); + } + public List ToChildList(Expression> parentIdExpression, object[] primaryKeyValues, bool isContainOneself = true) + { + var entity = this.Context.EntityMaintenance.GetEntityInfo(); + var pk = GetTreeKey(entity); + var list = this.ToList(); + List result = new List(); + foreach (var item in primaryKeyValues) + { + result.AddRange(GetChildList(parentIdExpression, pk, list, item, isContainOneself)); + } + return result; + } + public List ToParentList(Expression> parentIdExpression, object primaryKeyValue) + { + var entity = this.Context.EntityMaintenance.GetEntityInfo(); + var isTreeKey = entity.Columns.Any(it => it.IsTreeKey); + if (isTreeKey) + { + return _ToParentListByTreeKey(parentIdExpression, primaryKeyValue); + } + List result = new List() { }; + Check.Exception(entity.Columns.Where(it => it.IsPrimarykey).Any(), "No Primary key"); + var parentIdName = UtilConvert.ToMemberExpression((parentIdExpression as LambdaExpression).Body).Member.Name; + var ParentInfo = entity.Columns.First(it => it.PropertyName == parentIdName); + var parentPropertyName = ParentInfo.DbColumnName; + var tableName = this.QueryBuilder.GetTableNameString; + if (this.QueryBuilder.IsSingle() == false) + { + if (this.QueryBuilder.JoinQueryInfos.Count > 0) + { + tableName = this.QueryBuilder.JoinQueryInfos.First().TableName; + } + if (this.QueryBuilder.EasyJoinInfos.Count > 0) + { + tableName = this.QueryBuilder.JoinQueryInfos.First().TableName; + } + } + var current = this.Context.Queryable().AS(tableName).WithCacheIF(this.IsCache, this.CacheTime).Filter(null, this.QueryBuilder.IsDisabledGobalFilter).ClearFilter(this.QueryBuilder.RemoveFilters).InSingle(primaryKeyValue); + if (current != null) + { + result.Add(current); + object parentId = ParentInfo.PropertyInfo.GetValue(current, null); + int i = 0; + while (parentId != null && this.Context.Queryable().AS(tableName).WithCacheIF(this.IsCache, this.CacheTime).Filter(null, this.QueryBuilder.IsDisabledGobalFilter).ClearFilter(this.QueryBuilder.RemoveFilters).In(parentId).Any()) + { + Check.Exception(i > 200, ErrorMessage.GetThrowMessage("Dead cycle", "出现死循环或超出循环上限(200),检查最顶层的ParentId是否是null或者0")); + var parent = this.Context.Queryable().AS(tableName).WithCacheIF(this.IsCache, this.CacheTime).Filter(null, this.QueryBuilder.IsDisabledGobalFilter).InSingle(parentId); + result.Add(parent); + parentId = ParentInfo.PropertyInfo.GetValue(parent, null); + ++i; + } + } + return result; + } + public List ToParentList(Expression> parentIdExpression, object primaryKeyValue, Expression> parentWhereExpression) + { + var entity = this.Context.EntityMaintenance.GetEntityInfo(); + var isTreeKey = entity.Columns.Any(it => it.IsTreeKey); + if (isTreeKey) + { + return _ToParentListByTreeKey(parentIdExpression, primaryKeyValue, parentWhereExpression); + } + List result = new List() { }; + Check.Exception(entity.Columns.Where(it => it.IsPrimarykey).Any(), "No Primary key"); + var parentIdName = UtilConvert.ToMemberExpression((parentIdExpression as LambdaExpression).Body).Member.Name; + var ParentInfo = entity.Columns.First(it => it.PropertyName == parentIdName); + var parentPropertyName = ParentInfo.DbColumnName; + var tableName = this.QueryBuilder.GetTableNameString; + if (this.QueryBuilder.IsSingle() == false) + { + if (this.QueryBuilder.JoinQueryInfos.Count > 0) + { + tableName = this.QueryBuilder.JoinQueryInfos.First().TableName; + } + if (this.QueryBuilder.EasyJoinInfos.Count > 0) + { + tableName = this.QueryBuilder.JoinQueryInfos.First().TableName; + } + } + var current = this.Context.Queryable().AS(tableName).WhereIF(parentWhereExpression != default, parentWhereExpression).Filter(null, this.QueryBuilder.IsDisabledGobalFilter).InSingle(primaryKeyValue); + if (current != null) + { + result.Add(current); + object parentId = ParentInfo.PropertyInfo.GetValue(current, null); + int i = 0; + while (parentId != null && this.Context.Queryable().AS(tableName).WhereIF(parentWhereExpression != default, parentWhereExpression).Filter(null, this.QueryBuilder.IsDisabledGobalFilter).In(parentId).Any()) + { + Check.Exception(i > 200, ErrorMessage.GetThrowMessage("Dead cycle", "出现死循环或超出循环上限(200),检查最顶层的ParentId是否是null或者0")); + var parent = this.Context.Queryable().AS(tableName).WhereIF(parentWhereExpression != default, parentWhereExpression).Filter(null, this.QueryBuilder.IsDisabledGobalFilter).InSingle(parentId); + result.Add(parent); + parentId = ParentInfo.PropertyInfo.GetValue(parent, null); + ++i; + } + } + return result; + } + + public List ToTree(string childPropertyName, string parentIdPropertyName, object rootValue, string primaryKeyPropertyName) + { + var entity = this.Context.EntityMaintenance.GetEntityInfo(); + var pk = primaryKeyPropertyName; + var list = this.ToList(); + Expression>> childListExpression = (Expression>>)ExpressionBuilderHelper.CreateExpressionSelectField(typeof(T), childPropertyName, typeof(IEnumerable)); + Expression> parentIdExpression = (Expression>)ExpressionBuilderHelper.CreateExpressionSelectFieldObject(typeof(T), parentIdPropertyName); + return GetTreeRoot(childListExpression, parentIdExpression, pk, list, rootValue) ?? new List(); + } + public List ToTree(Expression>> childListExpression, Expression> parentIdExpression, object rootValue, Expression> primaryKeyExpression) + { + var entity = this.Context.EntityMaintenance.GetEntityInfo(); + var pk = ExpressionTool.GetMemberName(primaryKeyExpression); + var list = this.ToList(); + return GetTreeRoot(childListExpression, parentIdExpression, pk, list, rootValue) ?? new List(); + } + public List ToTree(Expression>> childListExpression, Expression> parentIdExpression, object rootValue) + { + var entity = this.Context.EntityMaintenance.GetEntityInfo(); + var pk = GetTreeKey(entity); + var list = this.ToList(); + return GetTreeRoot(childListExpression, parentIdExpression, pk, list, rootValue) ?? new List(); + } + public List ToTree(Expression>> childListExpression, Expression> parentIdExpression, object rootValue, object[] childIds) + { + var list = this.ToList(); + return TreeAndFilterIds(childListExpression, parentIdExpression, rootValue, childIds, ref list) ?? new List(); + } + public List ToTree(Expression>> childListExpression, Expression> parentIdExpression, object rootValue, object[] childIds, Expression> primaryKeyExpression) + { + var list = this.ToList(); + return TreeAndFilterIds(childListExpression, parentIdExpression, primaryKeyExpression, rootValue, childIds, ref list) ?? new List(); + } + public virtual DataTable ToDataTableByEntity() + { + var list = this.ToList(); + return this.Context.Utilities.ListToDataTable(list); + } + public virtual DataTable ToDataTable() + { + QueryBuilder.ResultType = typeof(SugarCacheDataTable); + InitMapping(); + var sqlObj = this.ToSql(); + RestoreMapping(); + DataTable result = null; + bool isChangeQueryableMasterSlave = GetIsMasterQuery(); + bool isChangeQueryableSlave = GetIsSlaveQuery(); + if (IsCache) + { + var cacheService = this.Context.CurrentConnectionConfig.ConfigureExternalServices.DataInfoCacheService; + result = CacheSchemeMain.GetOrCreate(cacheService, this.QueryBuilder, () => { return this.Db.GetDataTable(sqlObj.Key, sqlObj.Value.ToArray()); }, CacheTime, this.Context, CacheKey); + } + else + { + result = this.Db.GetDataTable(sqlObj.Key, sqlObj.Value.ToArray()); + } + RestChangeMasterQuery(isChangeQueryableMasterSlave); + RestChangeSlaveQuery(isChangeQueryableSlave); + return result; + } + public virtual DataTable ToDataTablePage(int pageIndex, int pageSize) + { + if (pageIndex == 0) + pageIndex = 1; + if (QueryBuilder.PartitionByValue.HasValue()) + { + QueryBuilder.ExternalPageIndex = pageIndex; + QueryBuilder.ExternalPageSize = pageSize; + } + else + { + QueryBuilder.Skip = (pageIndex - 1) * pageSize; + QueryBuilder.Take = pageSize; + } + return ToDataTable(); + } + + public DataTable ToOffsetDataTablePage(int pageNumber, int pageSize) + { + if (this.Context.CurrentConnectionConfig.DbType != DbType.SqlServer) + { + this.QueryBuilder.Offset = "true"; + return this.ToDataTablePage(pageNumber, pageSize); + } + else + { + _ToOffsetPage(pageNumber, pageSize); + return this.ToDataTable(); + } + } + public DataTable ToOffsetDataTablePage(int pageNumber, int pageSize, ref int totalNumber) + { + if (this.Context.CurrentConnectionConfig.DbType != DbType.SqlServer) + { + this.QueryBuilder.Offset = "true"; + return this.ToDataTablePage(pageNumber, pageSize, ref totalNumber); + } + else + { + totalNumber = this.Clone().Count(); + _ToOffsetPage(pageNumber, pageSize); + return this.Clone().ToDataTable(); + } + } + public DataTable ToOffsetDataTableByEntityPage(int pageNumber, int pageSize, ref int totalNumber) + { + return this.Context.Utilities.ListToDataTable(this.ToOffsetPage(pageNumber, pageSize, ref totalNumber)); + } + public DataTable ToOffsetDataTablePage(int pageNumber, int pageSize, ref int totalNumber, ref int totalPage) + { + return this.Context.Utilities.ListToDataTable(this.ToOffsetPage(pageNumber, pageSize, ref totalNumber, ref totalPage)); + } + + public DataTable ToDataTableByEntityPage(int pageNumber, int pageSize, ref int totalNumber) + { + var list = this.ToPageList(pageNumber, pageSize, ref totalNumber); + return this.Context.Utilities.ListToDataTable(list); + } + public virtual DataTable ToDataTablePage(int pageIndex, int pageSize, ref int totalNumber) + { + _RestoreMapping = false; + totalNumber = this.Clone().Count(); + _RestoreMapping = true; + var result = this.Clone().ToDataTablePage(pageIndex, pageSize); + return result; + } + public virtual DataTable ToDataTablePage(int pageIndex, int pageSize, ref int totalNumber, ref int totalPage) + { + var result = ToDataTablePage(pageIndex, pageSize, ref totalNumber); + totalPage = (totalNumber + pageSize - 1) / pageSize; + return result; + } + public Dictionary ToDictionary(Expression> key, Expression> value) + { + return this.ToDictionary(key, value).ToDictionary(it => it.Key, it => (ValueType)UtilMethods.ChangeType2(it.Value, typeof(ValueType))); + } + public Dictionary ToDictionary(Expression> key, Expression> value) + { + if (this.QueryBuilder.IsSingle() == false && (this.QueryBuilder.AsTables == null || this.QueryBuilder.AsTables.Count == 0)) + { + return this.MergeTable().ToDictionary(key, value); + } + this.QueryBuilder.ResultType = typeof(SugarCacheDictionary); + var keyName = QueryBuilder.GetExpressionValue(key, ResolveExpressType.FieldSingle).GetResultString(); + var valueName = QueryBuilder.GetExpressionValue(value, ResolveExpressType.FieldSingle).GetResultString(); + if (this.QueryBuilder.IsSingle() == false) + { + keyName = this.QueryBuilder.TableShortName + "." + keyName; + valueName = this.QueryBuilder.TableShortName + "." + valueName; + } + var isJson = this.Context.EntityMaintenance.GetEntityInfo().Columns.Where(it => it.IsJson && it.PropertyName == ExpressionTool.GetMemberName(value)).Any(); + if (isJson) + { + var result = this.Select(keyName + "," + valueName).ToList().ToDictionary(ExpressionTool.GetMemberName(key), ExpressionTool.GetMemberName(value)); + return result; + } + else if (valueName == null) + { + // 编译key和value的表达式树为委托 + var keySelector = key.Compile(); + var valueSelector = value.Compile(); + Dictionary objDic = this.ToList().ToDictionary(keySelector, valueSelector); + return objDic.ToDictionary(it => it.Key?.ToString(), it => it.Value); + } + else + { + var result = this.Select>(keyName + "," + valueName).ToList().ToDictionary(it => it.Key.ObjToString(), it => it.Value); + return result; + } + } + + public List> ToDictionaryList() + { + var list = this.ToList(); + if (list == null) + return null; + else + return this.Context.Utilities.DeserializeObject>>(this.Context.Utilities.SerializeObject(list)); + } + public async Task>> ToDictionaryListAsync() + { + var list = await ToListAsync().ConfigureAwait(false); + if (list == null) + return null; + else + return this.Context.Utilities.DeserializeObject>>(this.Context.Utilities.SerializeObject(list)); + } + + public virtual List ToList() + { + InitMapping(); + return _ToList(); + } + public List SetContext(Expression> whereExpression, ParameterT parameter) + { + var queryableContext = this.Context.TempItems["Queryable_To_Context"] as MapperContext; + var rootList = queryableContext.list; + List> queryableList = new List>(); + var index = rootList.IndexOf(parameter); + var selector = this.Clone().QueryBuilder.GetSelectValue + $",{index} as "; + var sqlObj = this.Clone().Where(whereExpression).Select(selector + "") + .Select(it => (object)new { it, sql_sugar_index = index }); + queryableList.Add(sqlObj); + + var allList = this.Context.Union(queryableList) + .Select(it => new { it = default(T), sql_sugar_index = 0 }) + .Select("*").ToList(); + var result = new List(); + throw new Exception("开发中"); + } + public List SetContext(Expression> thisFiled, Expression> mappingFiled, ParameterT parameter) + { + if (parameter == null) + { + return new List(); + } + List result = new List(); + var entity = this.Context.EntityMaintenance.GetEntityInfo(); + var queryableContext = this.Context.TempItems["Queryable_To_Context"] as MapperContext; + var list = queryableContext.list; + var pkName = ""; + if ((mappingFiled as LambdaExpression).Body is UnaryExpression) + { + pkName = (((mappingFiled as LambdaExpression).Body as UnaryExpression).Operand as MemberExpression).Member.Name; + } + else + { + pkName = ((mappingFiled as LambdaExpression).Body as MemberExpression).Member.Name; + } + var key = thisFiled.ToString() + mappingFiled.ToString() + typeof(ParameterT).FullName + typeof(T).FullName; + var ids = list.Where(it => it != null).Select(it => it.GetType().GetProperty(pkName).GetValue(it)).Distinct().ToArray(); + if (queryableContext.TempChildLists == null) + queryableContext.TempChildLists = new Dictionary(); + if (list != null && queryableContext.TempChildLists.TryGetValue(key, out object? value)) + { + result = (List)value; + } + else + { + if (queryableContext.TempChildLists == null) + queryableContext.TempChildLists = new Dictionary(); + this.Context.Utilities.PageEach(ids, 200, pageIds => + { + result.AddRange(this.Clone().In(thisFiled, pageIds).ToList()); + }); + queryableContext.TempChildLists[key] = result; + } + var name = ""; + if ((thisFiled as LambdaExpression).Body is UnaryExpression) + { + name = (((thisFiled as LambdaExpression).Body as UnaryExpression).Operand as MemberExpression).Member.Name; + } + else + { + name = ((thisFiled as LambdaExpression).Body as MemberExpression).Member.Name; + } + var pkValue = parameter.GetType().GetProperty(pkName).GetValue(parameter); + result = result.Where(it => it.GetType().GetProperty(name).GetValue(it).ObjToString() == pkValue.ObjToString()).ToList(); + return result; + } + public List SetContext(Expression> thisFiled1, Expression> mappingFiled1, + Expression> thisFiled2, Expression> mappingFiled2, + ParameterT parameter) + { + if (parameter == null) + { + return new List(); + } + var rightEntity = this.Context.EntityMaintenance.GetEntityInfo(); + var leftEntity = this.Context.EntityMaintenance.GetEntityInfo(); + List result = new List(); + var queryableContext = this.Context.TempItems["Queryable_To_Context"] as MapperContext; + var list = queryableContext.list; + var key = thisFiled1.ToString() + mappingFiled1.ToString() + + thisFiled2.ToString() + mappingFiled2.ToString() + + typeof(ParameterT).FullName + typeof(T).FullName; + MappingFieldsHelper fieldsHelper = new MappingFieldsHelper(); + var mappings = new List() { + new MappingFieldsExpression(){ + LeftColumnExpression=thisFiled1, + LeftEntityColumn=leftEntity.Columns.First(it=>it.PropertyName==ExpressionTool.GetMemberName(thisFiled1)), + RightColumnExpression=mappingFiled1, + RightEntityColumn=rightEntity.Columns.First(it=>it.PropertyName==ExpressionTool.GetMemberName(mappingFiled1)) + }, + new MappingFieldsExpression(){ + LeftColumnExpression=thisFiled2, + LeftEntityColumn=leftEntity.Columns.First(it=>it.PropertyName==ExpressionTool.GetMemberName(thisFiled2)), + RightColumnExpression=mappingFiled2, + RightEntityColumn=rightEntity.Columns.First(it=>it.PropertyName==ExpressionTool.GetMemberName(mappingFiled2)) + } + }; + var conditionals = fieldsHelper.GetMppingSql(list.Cast().ToList(), mappings); + if (queryableContext.TempChildLists == null) + queryableContext.TempChildLists = new Dictionary(); + if (list != null && queryableContext.TempChildLists.TryGetValue(key, out object? value)) + { + result = (List)value; + } + else + { + result = this.Clone().Where(conditionals, true).ToList(); + queryableContext.TempChildLists[key] = result; + } + List listObj = result.Select(it => (object)it).ToList(); + object obj = (object)parameter; + var newResult = fieldsHelper.GetSetList(obj, listObj, mappings).Select(it => (T)it).ToList(); + return newResult; + } + public void ForEachDataReader(Action action) + { + var queryable = this.Clone(); + var sql = queryable.ToSql(); + var dr = this.Context.Ado.GetDataReader(sql.Key, sql.Value); + var entityInfo = this.Context.EntityMaintenance.GetEntityInfo(); + var columns = UtilMethods.GetColumnInfo(dr); + var cacheKey = "ForEachDataReader" + typeof(T).GetHashCode() + string.Join(",", columns.Select(it => it.Item1 + it.Item2.Name + "_")); + IDataReaderEntityBuilder entytyList = this.Context.Utilities.GetReflectionInoCacheInstance().GetOrCreate("cacheKey", () => + { + var cacheResult = new IDataReaderEntityBuilder(this.Context, dr, + columns.Select(it => it.Item1).ToList()).CreateBuilder(typeof(T)); + return cacheResult; + }); + using (dr) + { + while (dr.Read()) + { + + var order = entytyList.Build(dr); + action(order); + } + } + if (this.Context.CurrentConnectionConfig.IsAutoCloseConnection) + { + this.Context.Ado.Close(); + } + } + public async Task ForEachDataReaderAsync(Action action) + { + var queryable = this.Clone(); + var sql = queryable.ToSql(); + var dr = await Context.Ado.GetDataReaderAsync(sql.Key, sql.Value).ConfigureAwait(false); + var entityInfo = this.Context.EntityMaintenance.GetEntityInfo(); + var columns = UtilMethods.GetColumnInfo(dr); + var cacheKey = "ForEachDataReader" + typeof(T).GetHashCode() + string.Join(",", columns.Select(it => it.Item1 + it.Item2.Name + "_")); + IDataReaderEntityBuilder entytyList = this.Context.Utilities.GetReflectionInoCacheInstance().GetOrCreate("cacheKey", () => + { + var cacheResult = new IDataReaderEntityBuilder(this.Context, dr, + columns.Select(it => it.Item1).ToList()).CreateBuilder(typeof(T)); + return cacheResult; + }); + using (dr) + { + while (dr.Read()) + { + + var order = entytyList.Build(dr); + action(order); + } + } + if (this.Context.CurrentConnectionConfig.IsAutoCloseConnection) + { + this.Context.Ado.Close(); + } + } + public virtual void ForEach(Action action, int singleMaxReads = 300, System.Threading.CancellationTokenSource cancellationTokenSource = null) + { + Check.Exception(this.QueryBuilder.Skip > 0 || this.QueryBuilder.Take > 0, ErrorMessage.GetThrowMessage("no support Skip take, use PageForEach", "不支持Skip Take,请使用 Queryale.PageForEach")); + var totalNumber = 0; + var totalPage = 1; + for (int i = 1; i <= totalPage; i++) + { + if (cancellationTokenSource?.IsCancellationRequested == true) return; + var queryable = this.Clone(); + var page = + totalPage == 1 ? + queryable.ToPageList(i, singleMaxReads, ref totalNumber, ref totalPage) : + queryable.ToPageList(i, singleMaxReads); + foreach (var item in page) + { + if (cancellationTokenSource?.IsCancellationRequested == true) return; + action.Invoke(item); + } + } + } + + public virtual void ForEachByPage(Action action, int pageIndex, int pageSize, ref int totalNumber, int singleMaxReads = 300, System.Threading.CancellationTokenSource cancellationTokenSource = null) + { + int count = this.Clone().Count(); + if (count > 0) + { + if (pageSize > singleMaxReads && count - ((pageIndex - 1) * pageSize) > singleMaxReads) + { + Int32 Skip = (pageIndex - 1) * pageSize; + Int32 NowCount = count - Skip; + Int32 number = 0; + if (NowCount > pageSize) NowCount = pageSize; + while (NowCount > 0) + { + if (cancellationTokenSource?.IsCancellationRequested == true) return; + if (number + singleMaxReads > pageSize) singleMaxReads = NowCount; + foreach (var item in this.Clone().Skip(Skip).Take(singleMaxReads).ToList()) + { + if (cancellationTokenSource?.IsCancellationRequested == true) return; + action.Invoke(item); + } + NowCount -= singleMaxReads; + Skip += singleMaxReads; + number += singleMaxReads; + } + } + else + { + if (cancellationTokenSource?.IsCancellationRequested == true) return; + foreach (var item in this.Clone().ToPageList(pageIndex, pageSize)) + { + if (cancellationTokenSource?.IsCancellationRequested == true) return; + action.Invoke(item); + } + } + } + totalNumber = count; + } + + public List ToOffsetPage(int pageIndex, int pageSize) + { + if (this.Context.CurrentConnectionConfig.DbType != DbType.SqlServer) + { + this.QueryBuilder.Offset = "true"; + return this.ToPageList(pageIndex, pageSize); + } + else + { + _ToOffsetPage(pageIndex, pageSize); + return this.ToList(); + } + } + public virtual List ToOffsetPage(int pageIndex, int pageSize, ref int totalNumber, ref int totalPage) + { + var result = ToOffsetPage(pageIndex, pageSize, ref totalNumber); + totalPage = (totalNumber + pageSize - 1) / pageSize; + return result; + } + public List ToOffsetPage(int pageIndex, int pageSize, ref int totalNumber) + { + if (this.Context.CurrentConnectionConfig.DbType != DbType.SqlServer) + { + this.QueryBuilder.Offset = "true"; + return this.ToPageList(pageIndex, pageSize, ref totalNumber); + } + else + { + totalNumber = this.Clone().Count(); + _ToOffsetPage(pageIndex, pageSize); + return this.Clone().ToList(); + } + } + public Task> ToOffsetPageAsync(int pageIndex, int pageSize) + { + if (this.Context.CurrentConnectionConfig.DbType != DbType.SqlServer) + { + this.QueryBuilder.Offset = "true"; + return this.ToPageListAsync(pageIndex, pageSize); + } + else + { + _ToOffsetPage(pageIndex, pageSize); + return this.ToListAsync(); + } + } + + public virtual List ToPageList(int pageIndex, int pageSize) + { + pageIndex = _PageList(pageIndex, pageSize); + return ToList(); + } + public virtual List ToPageList(int pageIndex, int pageSize, ref int totalNumber, Expression> expression) + { + if (this.QueryBuilder.Includes?.Count > 0) + { + if (pageIndex == 0) + pageIndex = 1; + var list = this.Clone().Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList(expression); + var countQueryable = this.Clone(); + countQueryable.QueryBuilder.Includes = null; + totalNumber = countQueryable.Count(); + return list; + } + else + { + var list = this.Select(expression).ToPageList(pageIndex, pageSize, ref totalNumber).ToList(); + return list; + } + } + public virtual List ToPageList(int pageIndex, int pageSize, ref int totalNumber) + { + var oldMapping = this.Context.MappingTables; + var countQueryable = this.Clone(); + if (countQueryable.QueryBuilder.Offset == "true") + { + countQueryable.QueryBuilder.Offset = null; + } + totalNumber = countQueryable.Count(); + this.Context.MappingTables = oldMapping; + return this.Clone().ToPageList(pageIndex, pageSize); + } + public virtual List ToPageList(int pageIndex, int pageSize, ref int totalNumber, ref int totalPage) + { + var result = ToPageList(pageIndex, pageSize, ref totalNumber); + totalPage = (totalNumber + pageSize - 1) / pageSize; + return result; + } + public virtual string ToSqlString() + { + if (this.EntityInfo?.Type?.IsInterface == true) + { + this.QueryBuilder.SelectValue = " * "; + this.AsType(this.EntityInfo.Type); + } + var sqlObj = this.Clone().ToSql(); + var result = sqlObj.Key; + if (result == null) return null; + result = UtilMethods.GetSqlString(this.Context.CurrentConnectionConfig, sqlObj); + return result; + } + + + public virtual KeyValuePair> ToSql() + { + if (!QueryBuilder.IsClone) + { + var newQueryable = this.Clone(); + newQueryable.QueryBuilder.IsClone = true; + return newQueryable.ToSql(); + } + else + { + return _ToSql(); + } + } + public string ToClassString(string className) + { + List columns = new List(); + var properties = typeof(T).GetProperties(); + foreach (var item in properties) + { + columns.Add(new DbColumnInfo() + { + DbColumnName = item.Name, + PropertyName = UtilMethods.GetUnderType(item.PropertyType).Name, + PropertyType = UtilMethods.GetUnderType(item.PropertyType) + }); + } + var result = ((this.Context.DbFirst) as DbFirstProvider).GetClassString(columns, ref className); + return result; + } + + public int IntoTable() + { + return IntoTable(typeof(TableEntityType)); + } + public int IntoTable(string TableName) + { + return IntoTable(typeof(TableEntityType), TableName); + } + public int IntoTable(Type TableEntityType) + { + var entityInfo = this.Context.EntityMaintenance.GetEntityInfo(TableEntityType); + var name = this.SqlBuilder.GetTranslationTableName(entityInfo.DbTableName); + return IntoTable(TableEntityType, name); + } + public int IntoTable(Type TableEntityType, string TableName) + { + KeyValuePair> sqlInfo; + string sql; + OutIntoTableSql(TableName, out sqlInfo, out sql, TableEntityType); + return this.Context.Ado.ExecuteCommand(sql, sqlInfo.Value); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/QueryableExecuteSqlAsync.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/QueryableExecuteSqlAsync.cs new file mode 100644 index 000000000..7ffaf3652 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/QueryableExecuteSqlAsync.cs @@ -0,0 +1,785 @@ +using System.Data; +using System.Linq.Expressions; + +namespace SqlSugar +{ + + public partial class QueryableProvider : QueryableAccessory, ISugarQueryable + { + public async virtual Task ToArrayAsync() + { + + var result = await ToListAsync().ConfigureAwait(false); + if (result.HasValue()) + return result.ToArray(); + else + return null; + } + + public virtual async Task InSingleAsync(object pkValue) + { + if (pkValue == null) + { + return default(T); + } + Check.Exception(this.QueryBuilder.SelectValue.HasValue(), "'InSingle' and' Select' can't be used together,You can use .Select(it=>...).Single(it.id==1)"); + var list = await In(pkValue).ToListAsync().ConfigureAwait(false); + if (list == null) return default(T); + else return list.SingleOrDefault(); + } + public async Task SingleAsync() + { + if (QueryBuilder.OrderByValue.IsNullOrEmpty()) + { + QueryBuilder.OrderByValue = QueryBuilder.DefaultOrderByTemplate; + } + var oldSkip = QueryBuilder.Skip; + var oldTake = QueryBuilder.Take; + var oldOrderBy = QueryBuilder.OrderByValue; + QueryBuilder.Skip = null; + QueryBuilder.Take = null; + QueryBuilder.OrderByValue = null; + var result = await ToListAsync().ConfigureAwait(false); + QueryBuilder.Skip = oldSkip; + QueryBuilder.Take = oldTake; + QueryBuilder.OrderByValue = oldOrderBy; + if (result == null || result.Count == 0) + { + return default(T); + } + else if (result.Count == 2) + { + Check.Exception(true, ErrorMessage.GetThrowMessage(".Single() result must not exceed one . You can use.First()", "使用single查询结果集不能大于1,适合主键查询,如果大于1你可以使用Queryable.First")); + return default(T); + } + else + { + return result.SingleOrDefault(); + } + } + public async Task SingleAsync(Expression> expression) + { + _Where(expression); + var result = await SingleAsync().ConfigureAwait(false); + this.QueryBuilder.WhereInfos.Remove(this.QueryBuilder.WhereInfos.Last()); + return result; + } + public Task FirstAsync(CancellationToken token) + { + this.Context.Ado.CancellationToken = token; + return FirstAsync(); + } + public async Task FirstAsync() + { + if (QueryBuilder.OrderByValue.IsNullOrEmpty()) + { + QueryBuilder.OrderByValue = QueryBuilder.DefaultOrderByTemplate; + } + if (QueryBuilder.Skip.HasValue) + { + QueryBuilder.Take = 1; + var list = await ToListAsync().ConfigureAwait(false); + return list.FirstOrDefault(); + } + else + { + QueryBuilder.Skip = 0; + QueryBuilder.Take = 1; + var result = await ToListAsync().ConfigureAwait(false); + if (result.HasValue()) + return result.FirstOrDefault(); + else + return default(T); + } + } + public Task FirstAsync(Expression> expression, CancellationToken token) + { + this.Context.Ado.CancellationToken = token; + return FirstAsync(expression); + } + public async Task FirstAsync(Expression> expression) + { + _Where(expression); + var result = await FirstAsync().ConfigureAwait(false); + this.QueryBuilder.WhereInfos.Remove(this.QueryBuilder.WhereInfos.Last()); + return result; + } + + public async Task AnyAsync(Expression> expression) + { + _Where(expression); + var result = await AnyAsync().ConfigureAwait(false); + this.QueryBuilder.WhereInfos.Remove(this.QueryBuilder.WhereInfos.Last()); + return result; + } + + public Task AnyAsync(Expression> expression, CancellationToken token) + { + this.Context.Ado.CancellationToken = token; + return AnyAsync(expression); + } + + public async Task AnyAsync() + { + return (await Clone().Take(1).Select("1").ToListAsync().ConfigureAwait(false)).Count > 0; ; + } + + public Task CountAsync(CancellationToken token) + { + this.Context.Ado.CancellationToken = token; + return CountAsync(); + } + public async Task CountAsync() + { + if (this.QueryBuilder.Skip == null && + this.QueryBuilder.Take == null && + this.QueryBuilder.OrderByValue == null && + this.QueryBuilder.PartitionByValue == null && + this.QueryBuilder.SelectValue == null && + this.QueryBuilder.Includes == null && + this.QueryBuilder.IsDistinct == false) + { + var list = await Clone().Select(" COUNT(1) ").ToListAsync().ConfigureAwait(false); + return list.FirstOrDefault(); + } + MappingTableList expMapping; + int result; + _CountBegin(out expMapping, out result); + if (IsCache) + { + var cacheService = this.Context.CurrentConnectionConfig.ConfigureExternalServices.DataInfoCacheService; + result = CacheSchemeMain.GetOrCreate(cacheService, this.QueryBuilder, () => { return GetCount(); }, CacheTime, this.Context, CacheKey); + } + else + { + result = await GetCountAsync().ConfigureAwait(false); + } + _CountEnd(expMapping); + return result; + } + public async Task CountAsync(Expression> expression) + { + _Where(expression); + var result = await CountAsync().ConfigureAwait(false); + this.QueryBuilder.WhereInfos.Remove(this.QueryBuilder.WhereInfos.Last()); + return result; + } + + public Task CountAsync(Expression> expression, CancellationToken token) + { + this.Context.Ado.CancellationToken = token; + return CountAsync(expression); + } + + public async Task MaxAsync(string maxField) + { + this.Select(string.Format(QueryBuilder.MaxTemplate, maxField)); + var list = await _ToListAsync().ConfigureAwait(false); + var result = list.SingleOrDefault(); + return result; + } + + public Task MaxAsync(string maxField, CancellationToken token) + { + this.Context.Ado.CancellationToken = token; + return MaxAsync(maxField); + } + + public Task MaxAsync(Expression> expression) + { + return _MaxAsync(expression); + } + + public Task MaxAsync(Expression> expression, CancellationToken token) + { + this.Context.Ado.CancellationToken = token; + return MaxAsync(expression); + } + + public async Task MinAsync(string minField) + { + this.Select(string.Format(QueryBuilder.MinTemplate, minField)); + var list = await _ToListAsync().ConfigureAwait(false); + var result = list.SingleOrDefault(); + return result; + } + public Task MinAsync(Expression> expression) + { + return _MinAsync(expression); + } + + public async Task SumAsync(string sumField) + { + this.Select(string.Format(QueryBuilder.SumTemplate, sumField)); + var list = await _ToListAsync().ConfigureAwait(false); + var result = list.SingleOrDefault(); + return result; + } + public Task SumAsync(Expression> expression) + { + return _SumAsync(expression); + } + + public async Task AvgAsync(string avgField) + { + this.Select(string.Format(QueryBuilder.AvgTemplate, avgField)); + var list = await _ToListAsync().ConfigureAwait(false); + var result = list.SingleOrDefault(); + return result; + } + public Task AvgAsync(Expression> expression) + { + return _AvgAsync(expression); + } + + public async virtual Task> ToListAsync(Expression> expression) + { + if (this.QueryBuilder.Includes?.Count > 0) + { + return await NavSelectHelper.GetListAsync(expression, this).ConfigureAwait(false); + } + else + { + var list = await Select(expression).ToListAsync().ConfigureAwait(false); + return list; + } + } + public Task> ToListAsync() + { + InitMapping(); + return _ToListAsync(); + } + + public Task> ToListAsync(CancellationToken token) + { + this.Context.Ado.CancellationToken = token; + return ToListAsync(); + } + public Task> ToPageListAsync(int pageNumber, int pageSize, CancellationToken token) + { + this.Context.Ado.CancellationToken = token; + return ToPageListAsync(pageNumber, pageSize); + } + public Task> ToPageListAsync(int pageIndex, int pageSize) + { + pageIndex = _PageList(pageIndex, pageSize); + return ToListAsync(); + } + public async virtual Task> ToPageListAsync(int pageIndex, int pageSize, RefAsync totalNumber, Expression> expression) + { + if (this.QueryBuilder.Includes?.Count > 0) + { + if (pageIndex == 0) + pageIndex = 1; + var list = await Clone().Skip((pageIndex - 1) * pageSize).Take(pageSize).ToListAsync(expression).ConfigureAwait(false); + var countQueryable = this.Clone(); + countQueryable.QueryBuilder.Includes = null; + totalNumber.Value = await countQueryable.CountAsync().ConfigureAwait(false); + return list; + } + else + { + var list = await Select(expression).ToPageListAsync(pageIndex, pageSize, totalNumber).ConfigureAwait(false); + return list; + } + } + public Task> ToPageListAsync(int pageNumber, int pageSize, RefAsync totalNumber, CancellationToken token) + { + this.Context.Ado.CancellationToken = token; + return ToPageListAsync(pageNumber, pageSize, totalNumber); + } + public async Task> ToPageListAsync(int pageIndex, int pageSize, RefAsync totalNumber) + { + var oldMapping = this.Context.MappingTables; + var countQueryable = this.Clone(); + if (countQueryable.QueryBuilder.Offset == "true") + { + countQueryable.QueryBuilder.Offset = null; + } + totalNumber.Value = await countQueryable.CountAsync().ConfigureAwait(false); + this.Context.MappingTables = oldMapping; + return await Clone().ToPageListAsync(pageIndex, pageSize).ConfigureAwait(false); + } + public async Task> ToPageListAsync(int pageNumber, int pageSize, RefAsync totalNumber, RefAsync totalPage) + { + var result = await ToPageListAsync(pageNumber, pageSize, totalNumber).ConfigureAwait(false); + totalPage.Value = (totalNumber.Value + pageSize - 1) / pageSize; + return result; + } + + public Task> ToPageListAsync(int pageNumber, int pageSize, RefAsync totalNumber, RefAsync totalPage, CancellationToken token) + { + this.Context.Ado.CancellationToken = token; + return ToPageListAsync(pageNumber, pageSize, totalNumber, totalPage); + } + + public async Task ToJsonAsync() + { + if (IsCache) + { + var cacheService = this.Context.CurrentConnectionConfig.ConfigureExternalServices.DataInfoCacheService; + var result = CacheSchemeMain.GetOrCreate(cacheService, this.QueryBuilder, () => + { + return this.Context.Utilities.SerializeObject(this.ToList(), typeof(T)); + }, CacheTime, this.Context, CacheKey); + return result; + } + else + { + return this.Context.Utilities.SerializeObject(await ToListAsync().ConfigureAwait(false), typeof(T)); + } + } + public async Task ToJsonPageAsync(int pageIndex, int pageSize) + { + return this.Context.Utilities.SerializeObject(await ToPageListAsync(pageIndex, pageSize).ConfigureAwait(false), typeof(T)); + } + public async Task ToJsonPageAsync(int pageIndex, int pageSize, RefAsync totalNumber) + { + var oldMapping = this.Context.MappingTables; + totalNumber.Value = await Clone().CountAsync().ConfigureAwait(false); + this.Context.MappingTables = oldMapping; + return await Clone().ToJsonPageAsync(pageIndex, pageSize).ConfigureAwait(false); + } + public async virtual Task ToDataTableByEntityAsync() + { + var list = await ToListAsync().ConfigureAwait(false); + return this.Context.Utilities.ListToDataTable(list); + } + + public Task ToOffsetDataTablePageAsync(int pageNumber, int pageSize) + { + if (this.Context.CurrentConnectionConfig.DbType != DbType.SqlServer) + { + this.QueryBuilder.Offset = "true"; + return this.ToDataTablePageAsync(pageNumber, pageSize); + } + else + { + _ToOffsetPage(pageNumber, pageSize); + return this.ToDataTableAsync(); + } + } + public async Task ToOffsetDataTablePageAsync(int pageNumber, int pageSize, RefAsync totalNumber) + { + if (this.Context.CurrentConnectionConfig.DbType != DbType.SqlServer) + { + this.QueryBuilder.Offset = "true"; + return await ToDataTablePageAsync(pageNumber, pageSize, totalNumber).ConfigureAwait(false); + } + else + { + totalNumber.Value = await Clone().CountAsync().ConfigureAwait(false); + _ToOffsetPage(pageNumber, pageSize); + return await Clone().ToDataTableAsync().ConfigureAwait(false); + } + } + public async Task ToOffsetDataTableByEntityPageAsync(int pageNumber, int pageSize, RefAsync totalNumber) + { + return this.Context.Utilities.ListToDataTable(await ToOffsetPageAsync(pageNumber, pageSize, totalNumber).ConfigureAwait(false)); + } + + public async Task ToDataTableAsync() + { + QueryBuilder.ResultType = typeof(SugarCacheDataTable); + InitMapping(); + var sqlObj = this._ToSql(); + RestoreMapping(); + DataTable result = null; + if (IsCache) + { + var cacheService = this.Context.CurrentConnectionConfig.ConfigureExternalServices.DataInfoCacheService; + result = CacheSchemeMain.GetOrCreate(cacheService, this.QueryBuilder, () => { return this.Db.GetDataTable(sqlObj.Key, sqlObj.Value.ToArray()); }, CacheTime, this.Context, CacheKey); + } + else + { + result = await Db.GetDataTableAsync(sqlObj.Key, sqlObj.Value.ToArray()).ConfigureAwait(false); + } + return result; + } + public Task ToDataTablePageAsync(int pageIndex, int pageSize) + { + pageIndex = _PageList(pageIndex, pageSize); + return ToDataTableAsync(); + } + public async Task ToDataTablePageAsync(int pageIndex, int pageSize, RefAsync totalNumber) + { + var oldMapping = this.Context.MappingTables; + totalNumber.Value = await Clone().CountAsync().ConfigureAwait(false); + this.Context.MappingTables = oldMapping; + return await Clone().ToDataTablePageAsync(pageIndex, pageSize).ConfigureAwait(false); + } + public async Task ToDataTableByEntityPageAsync(int pageNumber, int pageSize, RefAsync totalNumber) + { + var list = await ToPageListAsync(pageNumber, pageSize, totalNumber).ConfigureAwait(false); + return this.Context.Utilities.ListToDataTable(list); + } + public Task> ToOffsetPageAsync(int pageNumber, int pageSize, RefAsync totalNumber, CancellationToken token) + { + this.Context.Ado.CancellationToken = token; + return ToOffsetPageAsync(pageNumber, pageSize, totalNumber); + } + public Task> ToOffsetPageAsync(int pageNumber, int pageSize, RefAsync totalNumber, RefAsync totalPage, CancellationToken token) + { + this.Context.Ado.CancellationToken = token; + return ToOffsetPageAsync(pageNumber, pageSize, totalNumber, totalPage); + } + public async Task> ToOffsetPageAsync(int pageNumber, int pageSize, RefAsync totalNumber, RefAsync totalPage) + { + var result = await ToOffsetPageAsync(pageNumber, pageSize, totalNumber).ConfigureAwait(false); + totalPage.Value = (totalNumber.Value + pageSize - 1) / pageSize; + return result; + } + public async Task> ToOffsetPageAsync(int pageIndex, int pageSize, RefAsync totalNumber) + { + if (this.Context.CurrentConnectionConfig.DbType != DbType.SqlServer) + { + this.QueryBuilder.Offset = "true"; + return await ToPageListAsync(pageIndex, pageSize, totalNumber).ConfigureAwait(false); + } + else + { + totalNumber.Value = await Clone().CountAsync().ConfigureAwait(false); + _ToOffsetPage(pageIndex, pageSize); + return await Clone().ToListAsync().ConfigureAwait(false); + } + } + + public virtual async Task ForEachAsync(Action action, int singleMaxReads = 300, System.Threading.CancellationTokenSource cancellationTokenSource = null) + { + Check.Exception(this.QueryBuilder.Skip > 0 || this.QueryBuilder.Take > 0, ErrorMessage.GetThrowMessage("no support Skip take, use PageForEach", "不支持Skip Take,请使用 Queryale.PageForEach")); + RefAsync totalNumber = 0; + RefAsync totalPage = 1; + for (int i = 1; i <= totalPage; i++) + { + if (cancellationTokenSource?.IsCancellationRequested == true) return; + var queryable = this.Clone(); + var page = + totalPage == 1 ? + await queryable.ToPageListAsync(i, singleMaxReads, totalNumber, totalPage).ConfigureAwait(false) : + await queryable.ToPageListAsync(i, singleMaxReads).ConfigureAwait(false); + foreach (var item in page) + { + if (cancellationTokenSource?.IsCancellationRequested == true) return; + action.Invoke(item); + } + } + } + public virtual async Task ForEachByPageAsync(Action action, int pageIndex, int pageSize, RefAsync totalNumber, int singleMaxReads = 300, System.Threading.CancellationTokenSource cancellationTokenSource = null) + { + int count = this.Clone().Count(); + if (count > 0) + { + if (pageSize > singleMaxReads && count - ((pageIndex - 1) * pageSize) > singleMaxReads) + { + Int32 Skip = (pageIndex - 1) * pageSize; + Int32 NowCount = count - Skip; + Int32 number = 0; + if (NowCount > pageSize) NowCount = pageSize; + while (NowCount > 0) + { + if (cancellationTokenSource?.IsCancellationRequested == true) return; + if (number + singleMaxReads > pageSize) singleMaxReads = NowCount; + foreach (var item in await Clone().Skip(Skip).Take(singleMaxReads).ToListAsync().ConfigureAwait(false)) + { + if (cancellationTokenSource?.IsCancellationRequested == true) return; + action.Invoke(item); + } + NowCount -= singleMaxReads; + Skip += singleMaxReads; + number += singleMaxReads; + } + } + else + { + if (cancellationTokenSource?.IsCancellationRequested == true) return; + foreach (var item in this.Clone().ToPageList(pageIndex, pageSize)) + { + if (cancellationTokenSource?.IsCancellationRequested == true) return; + action.Invoke(item); + } + } + } + totalNumber.Value = count; + } + + public async Task> SetContextAsync(Expression> thisFiled1, Expression> mappingFiled1, +Expression> thisFiled2, Expression> mappingFiled2, +ParameterT parameter) + { + if (parameter == null) + { + return new List(); + } + var rightEntity = this.Context.EntityMaintenance.GetEntityInfo(); + var leftEntity = this.Context.EntityMaintenance.GetEntityInfo(); + List result = new List(); + var queryableContext = this.Context.TempItems["Queryable_To_Context"] as MapperContext; + var list = queryableContext.list; + var key = thisFiled1.ToString() + mappingFiled1.ToString() + + thisFiled2.ToString() + mappingFiled2.ToString() + + typeof(ParameterT).FullName + typeof(T).FullName; + MappingFieldsHelper fieldsHelper = new MappingFieldsHelper(); + var mappings = new List() { + new MappingFieldsExpression(){ + LeftColumnExpression=thisFiled1, + LeftEntityColumn=leftEntity.Columns.First(it=>it.PropertyName==ExpressionTool.GetMemberName(thisFiled1)), + RightColumnExpression=mappingFiled1, + RightEntityColumn=rightEntity.Columns.First(it=>it.PropertyName==ExpressionTool.GetMemberName(mappingFiled1)) + }, + new MappingFieldsExpression(){ + LeftColumnExpression=thisFiled2, + LeftEntityColumn=leftEntity.Columns.First(it=>it.PropertyName==ExpressionTool.GetMemberName(thisFiled2)), + RightColumnExpression=mappingFiled2, + RightEntityColumn=rightEntity.Columns.First(it=>it.PropertyName==ExpressionTool.GetMemberName(mappingFiled2)) + } + }; + var conditionals = fieldsHelper.GetMppingSql(list.Cast().ToList(), mappings); + if (queryableContext.TempChildLists == null) + queryableContext.TempChildLists = new Dictionary(); + if (list != null && queryableContext.TempChildLists.TryGetValue(key, out object? value)) + { + result = (List)value; + } + else + { + result = await Clone().Where(conditionals, true).ToListAsync().ConfigureAwait(false); + queryableContext.TempChildLists[key] = result; + } + List listObj = result.Select(it => (object)it).ToList(); + object obj = (object)parameter; + var newResult = fieldsHelper.GetSetList(obj, listObj, mappings).Select(it => (T)it).ToList(); + return newResult; + } + public async Task> SetContextAsync(Expression> thisFiled, Expression> mappingFiled, ParameterT parameter) + { + List result = new List(); + var entity = this.Context.EntityMaintenance.GetEntityInfo(); + var queryableContext = this.Context.TempItems["Queryable_To_Context"] as MapperContext; + var list = queryableContext.list; + var pkName = ""; + if ((mappingFiled as LambdaExpression).Body is UnaryExpression) + { + pkName = (((mappingFiled as LambdaExpression).Body as UnaryExpression).Operand as MemberExpression).Member.Name; + } + else + { + pkName = ((mappingFiled as LambdaExpression).Body as MemberExpression).Member.Name; + } + var key = thisFiled.ToString() + mappingFiled.ToString() + typeof(ParameterT).FullName + typeof(T).FullName; + var ids = list.Select(it => it.GetType().GetProperty(pkName).GetValue(it)).ToArray(); + if (queryableContext.TempChildLists == null) + queryableContext.TempChildLists = new Dictionary(); + if (list != null && queryableContext.TempChildLists.TryGetValue(key, out object? value)) + { + result = (List)value; + } + else + { + if (queryableContext.TempChildLists == null) + queryableContext.TempChildLists = new Dictionary(); + await Context.Utilities.PageEachAsync(ids, 200, async pageIds => + { + result.AddRange(await Clone().In(thisFiled, pageIds).ToListAsync().ConfigureAwait(false)); + }).ConfigureAwait(false); + queryableContext.TempChildLists[key] = result; + } + var name = ""; + if ((thisFiled as LambdaExpression).Body is UnaryExpression) + { + name = (((thisFiled as LambdaExpression).Body as UnaryExpression).Operand as MemberExpression).Member.Name; + } + else + { + name = ((thisFiled as LambdaExpression).Body as MemberExpression).Member.Name; + } + var pkValue = parameter.GetType().GetProperty(pkName).GetValue(parameter); + result = result.Where(it => it.GetType().GetProperty(name).GetValue(it).ObjToString() == pkValue.ObjToString()).ToList(); + return result; + } + public async Task> ToDictionaryAsync(Expression> key, Expression> value) + { + return (await ToDictionaryAsync(key, value).ConfigureAwait(false)).ToDictionary(it => it.Key, it => (ValueType)UtilMethods.ChangeType2(it.Value, typeof(ValueType))); + } + public async Task> ToDictionaryAsync(Expression> key, Expression> value) + { + if (this.QueryBuilder.IsSingle() == false && (this.QueryBuilder.AsTables == null || this.QueryBuilder.AsTables.Count == 0)) + { + return await MergeTable().ToDictionaryAsync(key, value).ConfigureAwait(false); + } + this.QueryBuilder.ResultType = typeof(SugarCacheDictionary); + var keyName = QueryBuilder.GetExpressionValue(key, ResolveExpressType.FieldSingle).GetResultString(); + var valueName = QueryBuilder.GetExpressionValue(value, ResolveExpressType.FieldSingle).GetResultString(); + var list = await Select>(keyName + "," + valueName).ToListAsync().ConfigureAwait(false); + var isJson = this.Context.EntityMaintenance.GetEntityInfo().Columns.Where(it => it.IsJson && it.PropertyName == ExpressionTool.GetMemberName(value)).Any(); + if (isJson) + { + var result = this.Select(keyName + "," + valueName).ToList().ToDictionary(ExpressionTool.GetMemberName(key), ExpressionTool.GetMemberName(value)); + return result; + } + else + { + var result = list.ToDictionary(it => it.Key.ObjToString(), it => it.Value); + return result; + } + } + public async Task> ToTreeAsync(string childPropertyName, string parentIdPropertyName, object rootValue, string primaryKeyPropertyName) + { + var entity = this.Context.EntityMaintenance.GetEntityInfo(); + var pk = primaryKeyPropertyName; + var list = await ToListAsync().ConfigureAwait(false); + Expression>> childListExpression = (Expression>>)ExpressionBuilderHelper.CreateExpressionSelectField(typeof(T), childPropertyName, typeof(IEnumerable)); + Expression> parentIdExpression = (Expression>)ExpressionBuilderHelper.CreateExpressionSelectFieldObject(typeof(T), parentIdPropertyName); + return GetTreeRoot(childListExpression, parentIdExpression, pk, list, rootValue) ?? new List(); + } + public async Task> ToTreeAsync(Expression>> childListExpression, Expression> parentIdExpression, object rootValue, object[] childIds) + { + var list = await ToListAsync().ConfigureAwait(false); + return TreeAndFilterIds(childListExpression, parentIdExpression, rootValue, childIds, ref list) ?? new List(); + } + public async Task> ToTreeAsync(Expression>> childListExpression, Expression> parentIdExpression, object rootValue, object[] childIds, Expression> primaryKeyExpression) + { + var list = await ToListAsync().ConfigureAwait(false); + return TreeAndFilterIds(childListExpression, parentIdExpression, primaryKeyExpression, rootValue, childIds, ref list) ?? new List(); + } + public async Task> ToTreeAsync(Expression>> childListExpression, Expression> parentIdExpression, object rootValue) + { + var entity = this.Context.EntityMaintenance.GetEntityInfo(); + var pk = GetTreeKey(entity); ; + var list = await ToListAsync().ConfigureAwait(false); + return GetTreeRoot(childListExpression, parentIdExpression, pk, list, rootValue) ?? new List(); + } + public async Task> ToTreeAsync(Expression>> childListExpression, Expression> parentIdExpression, object rootValue, Expression> primaryKeyExpression) + { + var entity = this.Context.EntityMaintenance.GetEntityInfo(); + var pk = ExpressionTool.GetMemberName(primaryKeyExpression); ; + var list = await ToListAsync().ConfigureAwait(false); + return GetTreeRoot(childListExpression, parentIdExpression, pk, list, rootValue) ?? new List(); + } + public async Task> ToParentListAsync(Expression> parentIdExpression, object primaryKeyValue) + { + List result = new List() { }; + var entity = this.Context.EntityMaintenance.GetEntityInfo(); + var isTreeKey = entity.Columns.Any(it => it.IsTreeKey); + if (isTreeKey) + { + return await _ToParentListByTreeKeyAsync(parentIdExpression, primaryKeyValue).ConfigureAwait(false); + } + Check.Exception(entity.Columns.Where(it => it.IsPrimarykey).Any(), "No Primary key"); + var parentIdName = UtilConvert.ToMemberExpression((parentIdExpression as LambdaExpression).Body).Member.Name; + var ParentInfo = entity.Columns.First(it => it.PropertyName == parentIdName); + var parentPropertyName = ParentInfo.DbColumnName; + var tableName = this.QueryBuilder.GetTableNameString; + if (this.QueryBuilder.IsSingle() == false) + { + if (this.QueryBuilder.JoinQueryInfos.Count > 0) + { + tableName = this.QueryBuilder.JoinQueryInfos.First().TableName; + } + if (this.QueryBuilder.EasyJoinInfos.Count > 0) + { + tableName = this.QueryBuilder.JoinQueryInfos.First().TableName; + } + } + var current = await Context.Queryable().AS(tableName).WithCacheIF(IsCache, CacheTime).Filter(null, QueryBuilder.IsDisabledGobalFilter).InSingleAsync(primaryKeyValue).ConfigureAwait(false); + if (current != null) + { + result.Add(current); + object parentId = ParentInfo.PropertyInfo.GetValue(current, null); + int i = 0; + while (parentId != null && await Context.Queryable().AS(tableName).Filter(null, QueryBuilder.IsDisabledGobalFilter).In(parentId).AnyAsync().ConfigureAwait(false)) + { + Check.Exception(i > 100, ErrorMessage.GetThrowMessage("Dead cycle", "出现死循环或超出循环上限(100),检查最顶层的ParentId是否是null或者0")); + var parent = await Context.Queryable().AS(tableName).WithCacheIF(IsCache, CacheTime).Filter(null, QueryBuilder.IsDisabledGobalFilter).InSingleAsync(parentId).ConfigureAwait(false); + result.Add(parent); + parentId = ParentInfo.PropertyInfo.GetValue(parent, null); + ++i; + } + } + return result; + } + public async Task> ToParentListAsync(Expression> parentIdExpression, object primaryKeyValue, Expression> parentWhereExpression) + { + List result = new List() { }; + var entity = this.Context.EntityMaintenance.GetEntityInfo(); + var isTreeKey = entity.Columns.Any(it => it.IsTreeKey); + if (isTreeKey) + { + return await _ToParentListByTreeKeyAsync(parentIdExpression, primaryKeyValue, parentWhereExpression).ConfigureAwait(false); + } + Check.Exception(entity.Columns.Where(it => it.IsPrimarykey).Any(), "No Primary key"); + var parentIdName = UtilConvert.ToMemberExpression((parentIdExpression as LambdaExpression).Body).Member.Name; + var ParentInfo = entity.Columns.First(it => it.PropertyName == parentIdName); + var parentPropertyName = ParentInfo.DbColumnName; + var tableName = this.QueryBuilder.GetTableNameString; + if (this.QueryBuilder.IsSingle() == false) + { + if (this.QueryBuilder.JoinQueryInfos.Count > 0) + { + tableName = this.QueryBuilder.JoinQueryInfos.First().TableName; + } + if (this.QueryBuilder.EasyJoinInfos.Count > 0) + { + tableName = this.QueryBuilder.JoinQueryInfos.First().TableName; + } + } + var current = await Context.Queryable().AS(tableName).WhereIF(parentWhereExpression != default, parentWhereExpression).Filter(null, QueryBuilder.IsDisabledGobalFilter).InSingleAsync(primaryKeyValue).ConfigureAwait(false); + if (current != null) + { + result.Add(current); + object parentId = ParentInfo.PropertyInfo.GetValue(current, null); + int i = 0; + while (parentId != null && await Context.Queryable().AS(tableName).WhereIF(parentWhereExpression != default, parentWhereExpression).Filter(null, QueryBuilder.IsDisabledGobalFilter).In(parentId).AnyAsync().ConfigureAwait(false)) + { + Check.Exception(i > 100, ErrorMessage.GetThrowMessage("Dead cycle", "出现死循环或超出循环上限(100),检查最顶层的ParentId是否是null或者0")); + var parent = await Context.Queryable().AS(tableName).WhereIF(parentWhereExpression != default, parentWhereExpression).Filter(null, QueryBuilder.IsDisabledGobalFilter).InSingleAsync(parentId).ConfigureAwait(false); + result.Add(parent); + parentId = ParentInfo.PropertyInfo.GetValue(parent, null); + ++i; + } + } + return result; + } + public async Task> ToChildListAsync(Expression> parentIdExpression, object primaryKeyValue, bool isContainOneself = true) + { + var entity = this.Context.EntityMaintenance.GetEntityInfo(); + var pk = GetTreeKey(entity); + var list = await ToListAsync().ConfigureAwait(false); + return GetChildList(parentIdExpression, pk, list, primaryKeyValue, isContainOneself); + } + + public async Task> ToChildListAsync(Expression> parentIdExpression, object[] primaryKeyValues, bool isContainOneself = true) + { + var entity = this.Context.EntityMaintenance.GetEntityInfo(); + var pk = GetTreeKey(entity); + var list = await ToListAsync().ConfigureAwait(false); + List result = new List(); + foreach (var item in primaryKeyValues) + { + result.AddRange(GetChildList(parentIdExpression, pk, list, item, isContainOneself)); + } + return result; + } + public Task IntoTableAsync(CancellationToken cancellationToken = default) + { + return IntoTableAsync(typeof(TableEntityType), cancellationToken); + } + public Task IntoTableAsync(string TableName, CancellationToken cancellationToken = default) + { + return IntoTableAsync(typeof(TableEntityType), TableName, cancellationToken); + } + public Task IntoTableAsync(Type TableEntityType, CancellationToken cancellationToken = default) + { + var entityInfo = this.Context.EntityMaintenance.GetEntityInfo(TableEntityType); + var name = this.SqlBuilder.GetTranslationTableName(entityInfo.DbTableName); + return IntoTableAsync(TableEntityType, name, cancellationToken); + } + public async Task IntoTableAsync(Type TableEntityType, string TableName, CancellationToken cancellationToken = default) + { + this.Context.Ado.CancellationToken = cancellationToken; + KeyValuePair> sqlInfo; + string sql; + OutIntoTableSql(TableName, out sqlInfo, out sql, TableEntityType); + return await Context.Ado.ExecuteCommandAsync(sql, sqlInfo.Value).ConfigureAwait(false); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/QueryableHelper.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/QueryableHelper.cs new file mode 100644 index 000000000..a80bb963e --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/QueryableHelper.cs @@ -0,0 +1,2544 @@ +using Newtonsoft.Json.Linq; + +using System.Collections; +using System.Collections.ObjectModel; +using System.Data; +using System.Linq.Expressions; +using System.Reflection; +using System.Text.RegularExpressions; + + +namespace SqlSugar +{ + public partial class QueryableProvider : QueryableAccessory, ISugarQueryable + { + #region Tree + private List _ToParentListByTreeKey(Expression> parentIdExpression, object primaryKeyValue) + { + var entity = this.Context.EntityMaintenance.GetEntityInfo(); + var treeKey = entity.Columns.FirstOrDefault(it => it.IsTreeKey); + List result = new List() { }; + var parentIdName = UtilConvert.ToMemberExpression((parentIdExpression as LambdaExpression).Body).Member.Name; + var ParentInfo = entity.Columns.First(it => it.PropertyName == parentIdName); + var parentPropertyName = ParentInfo.DbColumnName; + var tableName = this.QueryBuilder.GetTableNameString; + if (this.QueryBuilder.IsSingle() == false) + { + if (this.QueryBuilder.JoinQueryInfos.Count > 0) + { + tableName = this.QueryBuilder.JoinQueryInfos.First().TableName; + } + if (this.QueryBuilder.EasyJoinInfos.Count > 0) + { + tableName = this.QueryBuilder.JoinQueryInfos.First().TableName; + } + } + var current = this.Context.Queryable().AS(tableName).Filter(null, this.QueryBuilder.IsDisabledGobalFilter).Where(new List() { + new ConditionalModel() + { + ConditionalType = ConditionalType.Equal, + CSharpTypeName = treeKey.PropertyInfo.PropertyType.Name, + FieldValue = primaryKeyValue + "", + FieldName = treeKey.DbColumnName + } }).First(); + if (current != null) + { + result.Add(current); + object parentId = ParentInfo.PropertyInfo.GetValue(current, null); + int i = 0; + while (parentId != null && this.Context.Queryable().AS(tableName).Filter(null, this.QueryBuilder.IsDisabledGobalFilter).Where(new List() { + new ConditionalModel() + { + ConditionalType = ConditionalType.Equal, + CSharpTypeName = treeKey.PropertyInfo.PropertyType.Name, + FieldValue = parentId + "", + FieldName = treeKey.DbColumnName + } }).Any()) + { + Check.Exception(i > 100, ErrorMessage.GetThrowMessage("Dead cycle", "出现死循环或超出循环上限(100),检查最顶层的ParentId是否是null或者0")); + var parent = this.Context.Queryable().AS(tableName).Filter(null, this.QueryBuilder.IsDisabledGobalFilter).Where(new List() { + new ConditionalModel() + { + ConditionalType = ConditionalType.Equal, + CSharpTypeName = treeKey.PropertyInfo.PropertyType.Name, + FieldValue = parentId + "", + FieldName = treeKey.DbColumnName + } }).First(); + result.Add(parent); + parentId = ParentInfo.PropertyInfo.GetValue(parent, null); + ++i; + } + } + return result; + } + private List _ToParentListByTreeKey(Expression> parentIdExpression, object primaryKeyValue, Expression> parentWhereExpression) + { + var entity = this.Context.EntityMaintenance.GetEntityInfo(); + var treeKey = entity.Columns.FirstOrDefault(it => it.IsTreeKey); + List result = new List() { }; + var parentIdName = UtilConvert.ToMemberExpression((parentIdExpression as LambdaExpression).Body).Member.Name; + var ParentInfo = entity.Columns.First(it => it.PropertyName == parentIdName); + var parentPropertyName = ParentInfo.DbColumnName; + var tableName = this.QueryBuilder.GetTableNameString; + if (this.QueryBuilder.IsSingle() == false) + { + if (this.QueryBuilder.JoinQueryInfos.Count > 0) + { + tableName = this.QueryBuilder.JoinQueryInfos.First().TableName; + } + if (this.QueryBuilder.EasyJoinInfos.Count > 0) + { + tableName = this.QueryBuilder.JoinQueryInfos.First().TableName; + } + } + var current = this.Context.Queryable().AS(tableName).WhereIF(parentWhereExpression != default, parentWhereExpression).Filter(null, this.QueryBuilder.IsDisabledGobalFilter).Where(new List() { + new ConditionalModel() + { + ConditionalType = ConditionalType.Equal, + CSharpTypeName = treeKey.PropertyInfo.PropertyType.Name, + FieldValue = primaryKeyValue + "", + FieldName = treeKey.DbColumnName + } }).First(); + if (current != null) + { + result.Add(current); + object parentId = ParentInfo.PropertyInfo.GetValue(current, null); + int i = 0; + while (parentId != null && this.Context.Queryable().AS(tableName).WhereIF(parentWhereExpression != default, parentWhereExpression).Filter(null, this.QueryBuilder.IsDisabledGobalFilter).Where(new List() { + new ConditionalModel() + { + ConditionalType = ConditionalType.Equal, + CSharpTypeName = treeKey.PropertyInfo.PropertyType.Name, + FieldValue = parentId + "", + FieldName = treeKey.DbColumnName + } }).Any()) + { + Check.Exception(i > 100, ErrorMessage.GetThrowMessage("Dead cycle", "出现死循环或超出循环上限(100),检查最顶层的ParentId是否是null或者0")); + var parent = this.Context.Queryable().AS(tableName).WhereIF(parentWhereExpression != default, parentWhereExpression).Filter(null, this.QueryBuilder.IsDisabledGobalFilter).Where(new List() { + new ConditionalModel() + { + ConditionalType = ConditionalType.Equal, + CSharpTypeName = treeKey.PropertyInfo.PropertyType.Name, + FieldValue = parentId + "", + FieldName = treeKey.DbColumnName + } }).First(); + result.Add(parent); + parentId = ParentInfo.PropertyInfo.GetValue(parent, null); + ++i; + } + } + return result; + } + + private async Task> _ToParentListByTreeKeyAsync(Expression> parentIdExpression, object primaryKeyValue) + { + var entity = this.Context.EntityMaintenance.GetEntityInfo(); + var treeKey = entity.Columns.FirstOrDefault(it => it.IsTreeKey); + List result = new List() { }; + var parentIdName = UtilConvert.ToMemberExpression((parentIdExpression as LambdaExpression).Body).Member.Name; + var ParentInfo = entity.Columns.First(it => it.PropertyName == parentIdName); + var parentPropertyName = ParentInfo.DbColumnName; + var tableName = this.QueryBuilder.GetTableNameString; + if (this.QueryBuilder.IsSingle() == false) + { + if (this.QueryBuilder.JoinQueryInfos.Count > 0) + { + tableName = this.QueryBuilder.JoinQueryInfos.First().TableName; + } + if (this.QueryBuilder.EasyJoinInfos.Count > 0) + { + tableName = this.QueryBuilder.JoinQueryInfos.First().TableName; + } + } + var current = await Context.Queryable().AS(tableName).Filter(null, QueryBuilder.IsDisabledGobalFilter).Where(new List() { + new ConditionalModel() + { + ConditionalType = ConditionalType.Equal, + CSharpTypeName = treeKey.PropertyInfo.PropertyType.Name, + FieldValue = primaryKeyValue + "", + FieldName = treeKey.DbColumnName + } }).FirstAsync().ConfigureAwait(false); + if (current != null) + { + result.Add(current); + object parentId = ParentInfo.PropertyInfo.GetValue(current, null); + int i = 0; + while (parentId != null && await Context.Queryable().AS(tableName).Filter(null, QueryBuilder.IsDisabledGobalFilter).Where(new List() { + new ConditionalModel() + { + ConditionalType = ConditionalType.Equal, + CSharpTypeName = treeKey.PropertyInfo.PropertyType.Name, + FieldValue = parentId + "", + FieldName = treeKey.DbColumnName + } }).AnyAsync().ConfigureAwait(false)) + { + Check.Exception(i > 100, ErrorMessage.GetThrowMessage("Dead cycle", "出现死循环或超出循环上限(100),检查最顶层的ParentId是否是null或者0")); + var parent = await Context.Queryable().AS(tableName).Filter(null, QueryBuilder.IsDisabledGobalFilter).Where(new List() { + new ConditionalModel() + { + ConditionalType = ConditionalType.Equal, + CSharpTypeName = treeKey.PropertyInfo.PropertyType.Name, + FieldValue = parentId + "", + FieldName = treeKey.DbColumnName + } }).FirstAsync().ConfigureAwait(false); + result.Add(parent); + parentId = ParentInfo.PropertyInfo.GetValue(parent, null); + ++i; + } + } + return result; + } + + private async Task> _ToParentListByTreeKeyAsync(Expression> parentIdExpression, object primaryKeyValue, Expression> parentWhereExpression) + { + var entity = this.Context.EntityMaintenance.GetEntityInfo(); + var treeKey = entity.Columns.FirstOrDefault(it => it.IsTreeKey); + List result = new List() { }; + var parentIdName = UtilConvert.ToMemberExpression((parentIdExpression as LambdaExpression).Body).Member.Name; + var ParentInfo = entity.Columns.First(it => it.PropertyName == parentIdName); + var parentPropertyName = ParentInfo.DbColumnName; + var tableName = this.QueryBuilder.GetTableNameString; + if (this.QueryBuilder.IsSingle() == false) + { + if (this.QueryBuilder.JoinQueryInfos.Count > 0) + { + tableName = this.QueryBuilder.JoinQueryInfos.First().TableName; + } + if (this.QueryBuilder.EasyJoinInfos.Count > 0) + { + tableName = this.QueryBuilder.JoinQueryInfos.First().TableName; + } + } + var current = await Context.Queryable().AS(tableName).WhereIF(parentWhereExpression != default, parentWhereExpression).Filter(null, QueryBuilder.IsDisabledGobalFilter).Where(new List() { + new ConditionalModel() + { + ConditionalType = ConditionalType.Equal, + CSharpTypeName = treeKey.PropertyInfo.PropertyType.Name, + FieldValue = primaryKeyValue + "", + FieldName = treeKey.DbColumnName + } }).FirstAsync().ConfigureAwait(false); + if (current != null) + { + result.Add(current); + object parentId = ParentInfo.PropertyInfo.GetValue(current, null); + int i = 0; + while (parentId != null && await Context.Queryable().AS(tableName).WhereIF(parentWhereExpression != default, parentWhereExpression).Filter(null, QueryBuilder.IsDisabledGobalFilter).Where(new List() { + new ConditionalModel() + { + ConditionalType = ConditionalType.Equal, + CSharpTypeName = treeKey.PropertyInfo.PropertyType.Name, + FieldValue = parentId + "", + FieldName = treeKey.DbColumnName + } }).AnyAsync().ConfigureAwait(false)) + { + Check.Exception(i > 100, ErrorMessage.GetThrowMessage("Dead cycle", "出现死循环或超出循环上限(100),检查最顶层的ParentId是否是null或者0")); + var parent = await Context.Queryable().AS(tableName).WhereIF(parentWhereExpression != default, parentWhereExpression).Filter(null, QueryBuilder.IsDisabledGobalFilter).Where(new List() { + new ConditionalModel() + { + ConditionalType = ConditionalType.Equal, + CSharpTypeName = treeKey.PropertyInfo.PropertyType.Name, + FieldValue = parentId + "", + FieldName = treeKey.DbColumnName + } }).FirstAsync().ConfigureAwait(false); + result.Add(parent); + parentId = ParentInfo.PropertyInfo.GetValue(parent, null); + ++i; + } + } + return result; + } + + private List GetChildList(Expression> parentIdExpression, string pkName, List list, object rootValue, bool isContainOneself) + { + var exp = (parentIdExpression as LambdaExpression).Body; + if (exp is UnaryExpression) + { + exp = (exp as UnaryExpression).Operand; + } + var parentIdName = (exp as MemberExpression).Member.Name; + var result = BuildChildList(list, pkName, parentIdName, rootValue, isContainOneself); + return result; + } + + private static List BuildChildList(List list, string idName, string pIdName, object rootValue, bool isContainOneself) + { + var type = typeof(T); + var idProp = type.GetProperty(idName); + var pIdProp = type.GetProperty(pIdName); + + var kvpList = list.ToDictionary(x => x, v => idProp.GetValue(v).ObjToString()); + var groupKv = list.GroupBy(x => pIdProp.GetValue(x).ObjToString()).ToDictionary(k => k.Key, v => v.ToList()); + + Func> fc = null; + fc = (rootVal) => + { + var finalList = new List(); + if (groupKv.TryGetValue(rootVal, out var nextChildList)) + { + finalList.AddRange(nextChildList); + foreach (var child in nextChildList) + { + finalList.AddRange(fc(kvpList[child])); + } + } + return finalList; + }; + + var result = fc(rootValue.ObjToString()); + + if (isContainOneself) + { + var root = kvpList.FirstOrDefault(x => x.Value == rootValue.ObjToString()).Key; + if (root != null) + { + result.Insert(0, root); + } + } + + return result; + } + private List GetPrentIds(List list, object id, EntityColumnInfo pkName, EntityColumnInfo parentName) + { + var currentId = id; + List result = new List(); + result.Add(id); + while (list.Any(it => pkName.PropertyInfo.GetValue(it).ObjToString() == currentId.ObjToString())) + { + var data = list.First(it => pkName.PropertyInfo.GetValue(it).ObjToString() == currentId.ObjToString()); + currentId = parentName.PropertyInfo.GetValue(data); + result.Add(currentId); + } + return result; + } + private List TreeAndFilterIds(Expression>> childListExpression, Expression> parentIdExpression, object rootValue, object[] childIds, ref List list) + { + var entity = this.Context.EntityMaintenance.GetEntityInfo(); + var pk = GetTreeKey(entity); + var pkColumn = entity.Columns.FirstOrDefault(z => z.PropertyName == pk); + var newIds = new List(); + string parentIdName = GetParentName(parentIdExpression); + var parentColumn = entity.Columns.FirstOrDefault(z => z.PropertyName == parentIdName); + foreach (var id in childIds) + { + newIds.AddRange(GetPrentIds(list, id, pkColumn, parentColumn)); + } + list = list.Where(z => newIds.Any(it => it.ObjToString() == pkColumn.PropertyInfo.GetValue(z).ObjToString())).ToList(); + return GetTreeRoot(childListExpression, parentIdExpression, pk, list, rootValue); + } + private List TreeAndFilterIds(Expression>> childListExpression, Expression> parentIdExpression, Expression> primaryKeyExpresion, object rootValue, object[] childIds, ref List list) + { + var entity = this.Context.EntityMaintenance.GetEntityInfo(); + var pk = ExpressionTool.GetMemberName(primaryKeyExpresion); + var pkColumn = entity.Columns.FirstOrDefault(z => z.PropertyName == pk); + var newIds = new List(); + string parentIdName = GetParentName(parentIdExpression); + var parentColumn = entity.Columns.FirstOrDefault(z => z.PropertyName == parentIdName); + foreach (var id in childIds) + { + newIds.AddRange(GetPrentIds(list, id, pkColumn, parentColumn)); + } + list = list.Where(z => newIds.Any(it => it.ObjToString() == pkColumn.PropertyInfo.GetValue(z).ObjToString())).ToList(); + return GetTreeRoot(childListExpression, parentIdExpression, pk, list, rootValue); + } + + internal List GetTreeRoot(Expression>> childListExpression, Expression> parentIdExpression, string pk, List list, object rootValue) + { + var childName = ((childListExpression as LambdaExpression).Body as MemberExpression).Member.Name; + string parentIdName = GetParentName(parentIdExpression); + return UtilMethods.BuildTree(this.Context, list, pk, parentIdName, childName, rootValue)?.ToList() ?? default; + } + + private static string GetParentName(Expression> parentIdExpression) + { + var exp = (parentIdExpression as LambdaExpression).Body; + if (exp is UnaryExpression) + { + exp = (exp as UnaryExpression).Operand; + } + var parentIdName = (exp as MemberExpression).Member.Name; + return parentIdName; + } + + public List GetTreeChildList(List alllist, object pkValue, string pkName, string childName, string parentIdName) + { + var result = alllist.Where(it => + { + + var value = it.GetType().GetProperty(parentIdName).GetValue(it); + return value.ObjToString() == pkValue.ObjToString(); + + }).ToList(); + if (result?.Count > 0) + { + foreach (var item in result) + { + var itemPkValue = item.GetType().GetProperty(pkName).GetValue(item); + item.GetType().GetProperty(childName).SetValue(item, GetTreeChildList(alllist, itemPkValue, pkName, childName, parentIdName)); + } + } + return result; + } + private static string GetTreeKey(EntityInfo entity) + { + Check.Exception(entity.Columns.Where(it => it.IsPrimarykey || it.IsTreeKey).Any(), "need IsPrimary=true Or IsTreeKey=true"); + string pk = entity.Columns.Where(it => it.IsTreeKey).FirstOrDefault()?.PropertyName; + if (pk == null) + pk = entity.Columns.Where(it => it.IsPrimarykey).FirstOrDefault()?.PropertyName; + return pk; + } + #endregion + + #region Count + protected int GetCount() + { + var sql = string.Empty; + ToSqlBefore(); + sql = QueryBuilder.ToSqlString(); + sql = QueryBuilder.ToCountSql(sql); + var result = Context.Ado.GetInt(sql, QueryBuilder.Parameters.ToArray()); + return result; + } + protected async Task GetCountAsync() + { + var sql = string.Empty; + ToSqlBefore(); + sql = QueryBuilder.ToSqlString(); + sql = QueryBuilder.ToCountSql(sql); + var result = Convert.ToInt32(await Context.Ado.GetScalarAsync(sql, QueryBuilder.Parameters.ToArray()).ConfigureAwait(false)); + return result; + } + private void _CountEnd(MappingTableList expMapping) + { + RestoreMapping(); + QueryBuilder.IsCount = false; + if (expMapping.Count > 0) + { + if (this.QueryableMappingTableList == null) + { + this.QueryableMappingTableList = new MappingTableList(); + } + this.QueryableMappingTableList.Add(expMapping.First()); + } + } + private void _CountBegin(out MappingTableList expMapping, out int result) + { + expMapping = new MappingTableList(); + if (QueryBuilder.EntityName == "ExpandoObject" && this.Context.MappingTables.Any(it => it.EntityName == "ExpandoObject")) + { + expMapping.Add("ExpandoObject", this.Context.MappingTables.First(it => it.EntityName == "ExpandoObject").DbTableName); + } + InitMapping(); + QueryBuilder.IsCount = true; + result = 0; + } + #endregion + + #region Min Max Sum Gvg + protected TResult _Min(Expression expression) + { + if (this.QueryBuilder.IsSqlQuery) + { + this.QueryBuilder.IsSqlQuery = false; + } + QueryBuilder.CheckExpression(expression, "Main"); + var isSingle = QueryBuilder.IsSingle(); + var lamResult = QueryBuilder.GetExpressionValue(expression, isSingle ? ResolveExpressType.FieldSingle : ResolveExpressType.FieldMultiple); + var result = Min(lamResult.GetResultString()); + QueryBuilder.SelectValue = null; + return result; + } + protected async Task _MinAsync(Expression expression) + { + if (this.QueryBuilder.IsSqlQuery) + { + this.QueryBuilder.IsSqlQuery = false; + } + QueryBuilder.CheckExpression(expression, "Main"); + var isSingle = QueryBuilder.IsSingle(); + var lamResult = QueryBuilder.GetExpressionValue(expression, isSingle ? ResolveExpressType.FieldSingle : ResolveExpressType.FieldMultiple); + var result = await MinAsync(lamResult.GetResultString()).ConfigureAwait(false); + QueryBuilder.SelectValue = null; + return result; + } + protected TResult _Avg(Expression expression) + { + if (this.QueryBuilder.IsSqlQuery) + { + this.QueryBuilder.IsSqlQuery = false; + } + QueryBuilder.CheckExpression(expression, "Avg"); + var isSingle = QueryBuilder.IsSingle(); + var lamResult = QueryBuilder.GetExpressionValue(expression, isSingle ? ResolveExpressType.FieldSingle : ResolveExpressType.FieldMultiple); + return Avg(lamResult.GetResultString()); + } + protected async Task _AvgAsync(Expression expression) + { + if (this.QueryBuilder.IsSqlQuery) + { + this.QueryBuilder.IsSqlQuery = false; + } + QueryBuilder.CheckExpression(expression, "Avg"); + var isSingle = QueryBuilder.IsSingle(); + var lamResult = QueryBuilder.GetExpressionValue(expression, isSingle ? ResolveExpressType.FieldSingle : ResolveExpressType.FieldMultiple); + return await AvgAsync(lamResult.GetResultString()).ConfigureAwait(false); + } + protected TResult _Max(Expression expression) + { + if (this.QueryBuilder.IsSqlQuery) + { + this.QueryBuilder.IsSqlQuery = false; + } + QueryBuilder.CheckExpression(expression, "Max"); + var isSingle = QueryBuilder.IsSingle(); + var lamResult = QueryBuilder.GetExpressionValue(expression, isSingle ? ResolveExpressType.FieldSingle : ResolveExpressType.FieldMultiple); + var reslut = Max(lamResult.GetResultString()); + QueryBuilder.SelectValue = null; + return reslut; + } + protected async Task _MaxAsync(Expression expression) + { + if (this.QueryBuilder.IsSqlQuery) + { + this.QueryBuilder.IsSqlQuery = false; + } + QueryBuilder.CheckExpression(expression, "Max"); + var isSingle = QueryBuilder.IsSingle(); + var lamResult = QueryBuilder.GetExpressionValue(expression, isSingle ? ResolveExpressType.FieldSingle : ResolveExpressType.FieldMultiple); + var reslut = await MaxAsync(lamResult.GetResultString()).ConfigureAwait(false); + QueryBuilder.SelectValue = null; + return reslut; + } + protected TResult _Sum(Expression expression) + { + if (this.QueryBuilder.IsSqlQuery) + { + this.QueryBuilder.IsSqlQuery = false; + } + QueryBuilder.CheckExpression(expression, "Sum"); + var isSingle = QueryBuilder.IsSingle(); + var lamResult = QueryBuilder.GetExpressionValue(expression, isSingle ? ResolveExpressType.FieldSingle : ResolveExpressType.FieldMultiple); + var reslut = Sum(lamResult.GetResultString()); + QueryBuilder.SelectValue = null; + return reslut; + } + protected async Task _SumAsync(Expression expression) + { + if (this.QueryBuilder.IsSqlQuery) + { + this.QueryBuilder.IsSqlQuery = false; + } + QueryBuilder.CheckExpression(expression, "Sum"); + var isSingle = QueryBuilder.IsSingle(); + var lamResult = QueryBuilder.GetExpressionValue(expression, isSingle ? ResolveExpressType.FieldSingle : ResolveExpressType.FieldMultiple); + var reslut = await SumAsync(lamResult.GetResultString()).ConfigureAwait(false); + QueryBuilder.SelectValue = null; + return reslut; + } + #endregion + + #region Master Slave + private void RestChangeMasterQuery(bool isChangeQueryableMasterSlave) + { + if (isChangeQueryableMasterSlave) + this.Context.Ado.IsDisableMasterSlaveSeparation = false; + } + private bool GetIsMasterQuery() + { + var isChangeQueryableMasterSlave = + this.QueryBuilder.IsDisableMasterSlaveSeparation == true && + this.Context.Ado.IsDisableMasterSlaveSeparation == false && + this.Context.Ado.Transaction == null; + if (isChangeQueryableMasterSlave) + this.Context.Ado.IsDisableMasterSlaveSeparation = true; + return isChangeQueryableMasterSlave; + } + private void RestChangeSlaveQuery(bool isChangeQueryableSlaveSlave) + { + if (isChangeQueryableSlaveSlave) + this.Context.Ado.IsDisableMasterSlaveSeparation = true; + } + private bool GetIsSlaveQuery() + { + var isChangeQueryableMasterSlave = + this.QueryBuilder.IsEnableMasterSlaveSeparation == true && + this.Context.Ado.IsDisableMasterSlaveSeparation == true && + this.Context.Ado.Transaction == null; + if (isChangeQueryableMasterSlave) + this.Context.Ado.IsDisableMasterSlaveSeparation = false; + return isChangeQueryableMasterSlave; + } + #endregion + + #region Navigate + internal bool IsAppendNavColumns() + { + return this.QueryBuilder.Includes.HasValue() && this.QueryBuilder.AppendNavInfo == null; + } + + internal void SetAppendNavColumns(Expression expression) + { + var tResultType = expression.Type; + var dic = ExpressionTool.GetNewExpressionItemList(expression); + var navs = this.QueryBuilder.Includes; + var navManages = navs.Cast>(); + if (navManages.FirstOrDefault() == null) return; + this.QueryBuilder.AppendNavInfo = new AppendNavInfo(); + var navInfo = this.QueryBuilder.AppendNavInfo; + var entityColumns = this.EntityInfo.Columns; + var pkColumns = entityColumns.Where(it => it.IsPrimarykey); + AddAppendProperties(navManages, navInfo, entityColumns, pkColumns); + AddMappingNavProperties(dic, navInfo, entityColumns); + } + + private void AddMappingNavProperties(Dictionary dic, AppendNavInfo navInfo, List entityColumns) + { + foreach (var item in dic) + { + var value = item.Value; + var expressionTree = new ExpressionTreeVisitor().GetExpressions(value); + var isSqlMethod = expressionTree.Count != 0 && ExpressionTool.GetMethodName(expressionTree.Last()).IsIn("Any", "Count"); + if (expressionTree.Count != 0 && isSqlMethod == false) + { + + var name = ExpressionTool.GetMemberName(expressionTree.First()); + if (name != null && entityColumns.Any(it => it.Navigat != null && it.PropertyName == name)) + { + var mappingNavColumnInfo = new MappingNavColumnInfo() + { + ExpressionList = expressionTree, + Name = name + }; + navInfo.MappingNavProperties.Add(item.Key, mappingNavColumnInfo); + } + else if (ExpressionTool.IsNavMember(this.Context, value)) + { + var mappingNavColumnInfo = new MappingNavColumnInfo() + { + ExpressionList = expressionTree, + Name = name + }; + if (value is MemberExpression exp) + { + if (exp.Expression is MemberExpression expChild) + { + mappingNavColumnInfo.ParentName = expChild.Member.Name; + } + } + navInfo.MappingNavProperties.Add(item.Key, mappingNavColumnInfo); + } + } + + } + } + + private static void AddAppendProperties(IEnumerable> navManages, AppendNavInfo navInfo, List entityColumns, IEnumerable pkColumns) + { + foreach (var item in pkColumns) + { + navInfo.AppendProperties.Add(item.PropertyName, item.DbColumnName); + } + foreach (var item in navManages) + { + var FirstExp = item.Expressions.First(); + var navName = ExpressionTool.GetMemberName(FirstExp); + if (FirstExp is LambdaExpression && ExpressionTool.GetMethodName((FirstExp as LambdaExpression).Body) == "ToList") + { + navName = ExpressionTool.GetFirstTypeNameFromExpression(FirstExp); + } + var navColumn = entityColumns.Where(it => it.IsPrimarykey == false).Where(it => it.Navigat != null).FirstOrDefault(it => it.PropertyName == navName); + if (navColumn != null && navColumn.Navigat.NavigatType != NavigateType.ManyToMany) + { + var name1 = navColumn.Navigat.Name; + var name2 = navColumn.Navigat.Name2; + var name1Column = entityColumns.FirstOrDefault(it => it.PropertyName == name1); + var name2Column = entityColumns.FirstOrDefault(it => it.PropertyName == name2); + if (name1Column != null) + { + if (!navInfo.AppendProperties.ContainsKey(name1Column.PropertyName)) + navInfo.AppendProperties.Add(name1Column.PropertyName, name1Column.DbColumnName); + } + if (name2Column != null) + { + if (!navInfo.AppendProperties.ContainsKey(name2Column.PropertyName)) + navInfo.AppendProperties.Add(name2Column.PropertyName, name2Column.DbColumnName); + } + if (navColumn.Navigat.NavigatType == NavigateType.Dynamic && name1.HasValue()) + { + var jarray = JArray.Parse(name1); + foreach (var jitem in jarray) + { + var columnInfo = entityColumns.FirstOrDefault(it => + it.PropertyName.EqualCase(jitem["m"].ToString()) || + it.DbColumnName.EqualCase(jitem["m"].ToString())); + if (columnInfo != null) + { + if (!navInfo.AppendProperties.ContainsKey(columnInfo.PropertyName)) + navInfo.AppendProperties.Add(columnInfo.PropertyName, columnInfo.DbColumnName); + } + } + } + } + } + } + + private async Task _InitNavigatAsync(List result) + { + if (this.QueryBuilder.Includes != null) + { + await Task.Run(() => { _InitNavigat(result); }).ConfigureAwait(false); + } + } + private void _InitNavigat(List result) + { + if (this.QueryBuilder.Includes != null) + { + var managers = (this.QueryBuilder.Includes as List); + if (this.QueryBuilder.AppendNavInfo?.AppendProperties?.Count > 0) + { + if (result.HasValue()) + { + SelectNavQuery(result, managers); + } + } + else + { + foreach (var it in managers) + { + var manager = it as NavigatManager; + if (manager != null) + { + manager.RootList = result; + manager.Execute(); + } + } + } + } + } + + private void SelectNavQuery(List result, List managers) + { + if (result.Count != 0) + { + IList outList = null; + foreach (var it in managers) + { + var manager = it; + var p = it.GetType().GetProperty("RootList"); + var tType = it.GetType().GenericTypeArguments[0]; + var allColumns = this.Context.EntityMaintenance.GetEntityInfo(tType) + .Columns; + var columns = allColumns + .Where(a => this.QueryBuilder.AppendNavInfo.Result.First().result.ContainsKey("SugarNav_" + a.PropertyName)) + .ToList(); + var listType = typeof(List<>).MakeGenericType(tType); + if (outList == null) + { + outList = SelectNavQuery_SetList(result, it, p, tType, columns, listType); + } + else + { + p.SetValue(it, outList); + } + it.GetType().GetMethod("Execute").Invoke(it, null); + SelectNavQuery_MappingList(it, result, outList, allColumns.Where(a => a.Navigat != null).ToList()); + } + } + } + + private void SelectNavQuery_MappingList(object it, List result, IList outList, List columnInfos) + { + for (int i = 0; i < result.Count; i++) + { + var leftObject = result[i]; + var rightObject = outList[i]; + foreach (var item in this.QueryBuilder.AppendNavInfo.MappingNavProperties) + { + var rightName = item.Value.Name; + var rightColumnInfo = columnInfos.FirstOrDefault(a => a.PropertyName == rightName); + var anyParent = item.Value.ParentName.HasValue(); + if (anyParent) + { + rightColumnInfo = columnInfos.FirstOrDefault(a => a.PropertyName == item.Value.ParentName); + } + var rightValue = rightColumnInfo.PropertyInfo.GetValue(rightObject); + var leftName = item.Key; + if (anyParent) + { + if (rightValue == null) + { + rightValue = UtilMethods.GetDefaultValue(rightColumnInfo.PropertyInfo.PropertyType); + } + rightValue = rightValue.GetType().GetProperty(item.Value.Name).GetValue(rightValue); + } + //// var rightColumn=col + // object value = item; + if (item.Value.ExpressionList.Count > 1 && rightValue != null) + { + + //foreach (var callExp in item.Value.ExpressionList.Skip(1)) + //{ + try + { + MethodCallExpression meExp = (MethodCallExpression)item.Value.ExpressionList.Last(); + ParameterExpression ps = ExpressionTool.GetParameters(meExp).First(); + var comExp = Expression.Lambda(meExp, ps); + var obj = comExp.Compile(); + // 传递参数值 + var leftValue = obj.DynamicInvoke(rightObject); + UtilMethods.SetAnonymousObjectPropertyValue(leftObject, leftName, leftValue); + } + catch (Exception ex) + { + var errorExp = item.Value.ExpressionList.Last().ToString(); + Check.ExceptionEasy($"{errorExp} no support,{ex.Message}", $"{errorExp}语法不支持,请查SqlSugar文档询导航DTO用法,{ex.Message}"); + } + // // 重新构造Lambda表达式,将参数替换为新的参数,方法调用替换为新的方法调用 + // var newExpression = Expression.Lambda>>(newMethodCallExpr, paramExpr); + // Expression.Call(callExp, (callExp as MethodCallExpression).Method,new ) + // var propertyExpr = Expression.Property(paramExpr, rightName); + // } + } + else if (rightValue != null) + { + //leftObject.GetType().GetProperty(leftName).SetValue(leftObject, rightValue); + UtilMethods.SetAnonymousObjectPropertyValue(leftObject, leftName, rightValue); + } + } + } + } + + private IList SelectNavQuery_SetList(List result, object it, PropertyInfo p, Type tType, List columns, Type listType) + { + var outList = Activator.CreateInstance(listType); + p.SetValue(it, outList); + var index = 0; + foreach (var item in result) + { + var addItem = Activator.CreateInstance(tType); + var appendResult = this.QueryBuilder.AppendNavInfo.Result[index]; + foreach (var kv in appendResult.result) + { + var propertyName = kv.Key.Replace("SugarNav_", ""); + var propertyInfo = columns.First(i => i.PropertyName == propertyName).PropertyInfo; + if (kv.Value is decimal && UtilMethods.GetUnderType(propertyInfo.PropertyType).IsIn(typeof(int), typeof(long))) + { + + var changeValue = UtilMethods.ChangeType2(kv.Value, propertyInfo.PropertyType); + propertyInfo.SetValue(addItem, changeValue); + } + else if (kv.Value == DBNull.Value && UtilMethods.GetUnderType(propertyInfo.PropertyType).IsIn(typeof(int), typeof(long))) + { + + var changeValue = UtilMethods.ChangeType2(0, propertyInfo.PropertyType); + propertyInfo.SetValue(addItem, changeValue); + } + else if (kv.Value == DBNull.Value) + { + propertyInfo.SetValue(addItem, null); + } + else if (UtilMethods.GetUnderType(propertyInfo.PropertyType) == typeof(Guid) && kv.Value is string) + { + propertyInfo.SetValue(addItem, new Guid(kv.Value.ToString())); + } + else if (UtilMethods.GetUnderType(propertyInfo.PropertyType) == typeof(int) && kv.Value is long) + { + propertyInfo.SetValue(addItem, Convert.ToInt32(kv.Value)); + } + else if (propertyInfo.PropertyType.FullName == "System.Ulid") + { + propertyInfo.SetValue(addItem, UtilMethods.To(kv.Value, propertyInfo.PropertyType)); + } + else + { + propertyInfo.SetValue(addItem, kv.Value); + } + } + (outList as IList).Add(addItem); + index++; + } + return outList as IList; + } + + private bool IsSelectNavQuery() + { + return this.QueryBuilder.SelectValue.HasValue() && this.QueryBuilder.NoCheckInclude == false; + } + + protected void _Mapper(List result) + { + if (this.EntityInfo.Columns.Any(it => it.IsTranscoding)) + { + foreach (var item in result) + { + foreach (var column in this.EntityInfo.Columns.Where(it => it.IsTranscoding)) + { + var value = column.PropertyInfo.GetValue(item, null); + if (value != null) + { + column.PropertyInfo.SetValue(item, UtilMethods.DecodeBase64(value.ToString()), null); + } + } + } + } + if (this.Mappers.HasValue()) + { + foreach (var mapper in this.Mappers) + { + if (typeof(TResult) == typeof(T)) + { + mapper(result.Select(it => (T)Convert.ChangeType(it, typeof(T))).ToList()); + } + else + { + Check.Exception(true, "{0} and {1} are not a type, Try .select().mapper().ToList", typeof(TResult).FullName, typeof(T).FullName); + } + } + } + if (this.MapperAction != null) + { + foreach (TResult item in result) + { + if (typeof(TResult) == typeof(T)) + { + foreach (var mapper in this.MapperAction) + { + mapper((T)(item as object)); + } + } + else + { + Check.Exception(true, "{0} and {1} are not a type, Try .select().mapper().ToList", typeof(TResult).FullName, typeof(T).FullName); + } + } + } + if (this.MapperActionWithCache != null) + { + if (typeof(TResult) == typeof(T)) + { + var list = (List)Convert.ChangeType(result, typeof(List)); + var mapperCache = new MapperCache(list, this.Context); + foreach (T item in list) + { + mapperCache.GetIndex = 0; + this.MapperActionWithCache(item, mapperCache); + } + } + else + { + Check.Exception(true, "{0} and {1} are not a type, Try .select().mapper().ToList", typeof(TResult).FullName, typeof(T).FullName); + } + } + } + private QueryableProvider _Mapper(Expression mapperObject, Expression mapperField) + { + if ((mapperObject as LambdaExpression).Body is UnaryExpression) + { + mapperObject = ((mapperObject as LambdaExpression).Body as UnaryExpression).Operand; + } + else + { + mapperObject = (mapperObject as LambdaExpression).Body; + } + if ((mapperField as LambdaExpression).Body is UnaryExpression) + { + mapperField = ((mapperField as LambdaExpression).Body as UnaryExpression).Operand; + } + else + { + mapperField = (mapperField as LambdaExpression).Body; + } + Check.Exception(mapperObject is MemberExpression == false || mapperField is MemberExpression == false, ".Mapper() parameter error"); + var mapperObjectExp = mapperObject as MemberExpression; + var mapperFieldExp = mapperField as MemberExpression; + Check.Exception(mapperFieldExp.Type.IsClass(), ".Mapper() parameter error"); + var objType = mapperObjectExp.Type; + var filedType = mapperFieldExp.Expression.Type; + Check.Exception(objType != typeof(TObject) && objType != typeof(List), ".Mapper() parameter error"); + if (objType == typeof(List)) + { + objType = typeof(TObject); + } + var filedName = mapperFieldExp.Member.Name; + var objName = mapperObjectExp.Member.Name; + var filedEntity = this.Context.EntityMaintenance.GetEntityInfo(objType); + var objEntity = this.Context.EntityMaintenance.GetEntityInfo(filedType); + var isSelf = filedType == typeof(T); + if (Mappers == null) + Mappers = new List>>(); + if (isSelf) + { + Action> mapper = (entitys) => + { + if (entitys.IsNullOrEmpty() || entitys.Count == 0) return; + var entity = entitys.First(); + var whereCol = filedEntity.Columns.FirstOrDefault(it => it.PropertyName.Equals(filedName, StringComparison.CurrentCultureIgnoreCase)); + if (whereCol == null) + { + whereCol = filedEntity.Columns.FirstOrDefault(it => it.IsPrimarykey == true); + } + if (whereCol == null) + { + whereCol = filedEntity.Columns.FirstOrDefault(it => GetPrimaryKeys().Any(pk => pk.Equals(it.DbColumnName, StringComparison.CurrentCultureIgnoreCase))); + } + if (whereCol == null) + { + whereCol = filedEntity.Columns.FirstOrDefault(it => it.PropertyName.Equals("id", StringComparison.CurrentCultureIgnoreCase)); + } + if (whereCol == null) + { + whereCol = filedEntity.Columns.FirstOrDefault(it => (it.PropertyName).Equals(it.EntityName + "id", StringComparison.CurrentCultureIgnoreCase)); + } + if (whereCol == null) + { + Check.Exception(true, ".Mapper() parameter error"); + } + List inValues = entitys.Select(it => it.GetType().GetProperty(filedName).GetValue(it, null).ObjToString()).ToList(); + if (inValues != null && inValues.Count != 0 && UtilMethods.GetUnderType(entitys.First().GetType().GetProperty(filedName).PropertyType) == UtilConstants.GuidType) + { + inValues = inValues.Select(x => string.IsNullOrEmpty(x) ? "null" : x).Distinct().ToList(); + } + List wheres = new List() + { + new ConditionalModel() + { + FieldName=this.SqlBuilder.GetTranslationColumnName(whereCol.DbColumnName), + ConditionalType= ConditionalType.In, + FieldValue=string.Join(",",inValues.Distinct()), + CSharpTypeName=whereCol.PropertyInfo.PropertyType.Name + } + }; + var list = this.Context.Queryable().Where(wheres).ToList(); + foreach (var item in entitys) + { + var whereValue = item.GetType().GetProperty(filedName).GetValue(item, null); + var setValue = list.Where(x => x.GetType().GetProperty(whereCol.PropertyName).GetValue(x, null).ObjToString() == whereValue.ObjToString()).ToList(); + var setObject = item.GetType().GetProperty(objName); + if (setObject.PropertyType.FullName.IsCollectionsList()) + { + setObject.SetValue(item, setValue.ToList(), null); + } + else + { + setObject.SetValue(item, setValue.FirstOrDefault(), null); + } + } + }; + Mappers.Add(mapper); + } + else + { + Action> mapper = (entitys) => + { + if (entitys.IsNullOrEmpty() || entitys.Count == 0) return; + var entity = entitys.First(); + var tEntity = this.Context.EntityMaintenance.GetEntityInfo(); + var whereCol = tEntity.Columns.FirstOrDefault(it => it.PropertyName.Equals(filedName, StringComparison.CurrentCultureIgnoreCase)); + if (whereCol == null) + { + whereCol = tEntity.Columns.FirstOrDefault(it => it.IsPrimarykey == true); + } + if (whereCol == null) + { + whereCol = tEntity.Columns.FirstOrDefault(it => GetPrimaryKeys().Any(pk => pk.Equals(it.DbColumnName, StringComparison.CurrentCultureIgnoreCase))); + } + if (whereCol == null) + { + whereCol = tEntity.Columns.FirstOrDefault(it => it.PropertyName.Equals("id", StringComparison.CurrentCultureIgnoreCase)); + } + if (whereCol == null) + { + whereCol = tEntity.Columns.FirstOrDefault(it => (it.PropertyName).Equals(it.EntityName + "id", StringComparison.CurrentCultureIgnoreCase)); + } + if (whereCol == null) + { + Check.Exception(true, ".Mapper() parameter error"); + } + List inValues = entitys.Select(it => it.GetType().GetProperty(whereCol.PropertyName).GetValue(it, null).ObjToString()).ToList(); + var dbColumnName = filedEntity.Columns.FirstOrDefault(it => it.PropertyName == filedName).DbColumnName; + List wheres = new List() + { + new ConditionalModel() + { + FieldName=dbColumnName, + ConditionalType= ConditionalType.In, + FieldValue=string.Join(",",inValues) + } + }; + var list = this.Context.Queryable().Where(wheres).ToList(); + foreach (var item in entitys) + { + var whereValue = item.GetType().GetProperty(whereCol.PropertyName).GetValue(item, null); + var setValue = list.Where(x => x.GetType().GetProperty(filedName).GetValue(x, null).ObjToString() == whereValue.ObjToString()).ToList(); + var setObject = item.GetType().GetProperty(objName); + if (setObject.PropertyType.FullName.IsCollectionsList()) + { + setObject.SetValue(item, setValue.ToList(), null); + } + else + { + setObject.SetValue(item, setValue.FirstOrDefault(), null); + } + } + }; + Mappers.Add(mapper); + } + + return this; + } + private QueryableProvider _Mapper(Expression mapperObject, Expression mainField, Expression childField) + { + if ((mapperObject as LambdaExpression).Body is UnaryExpression) + { + mapperObject = ((mapperObject as LambdaExpression).Body as UnaryExpression).Operand; + } + else + { + mapperObject = (mapperObject as LambdaExpression).Body; + } + if ((mainField as LambdaExpression).Body is UnaryExpression) + { + mainField = ((mainField as LambdaExpression).Body as UnaryExpression).Operand; + } + else + { + mainField = (mainField as LambdaExpression).Body; + } + if ((childField as LambdaExpression).Body is UnaryExpression) + { + childField = ((childField as LambdaExpression).Body as UnaryExpression).Operand; + } + else + { + childField = (childField as LambdaExpression).Body; + } + Check.Exception(mapperObject is MemberExpression == false || mainField is MemberExpression == false, ".Mapper() parameter error"); + var mapperObjectExp = mapperObject as MemberExpression; + var mainFieldExp = mainField as MemberExpression; + var childFieldExp = childField as MemberExpression; + Check.Exception(mainFieldExp.Type.IsClass(), ".Mapper() parameter error"); + Check.Exception(childFieldExp.Type.IsClass(), ".Mapper() parameter error"); + var objType = mapperObjectExp.Type; + var filedType = mainFieldExp.Expression.Type; + Check.Exception(objType != typeof(TObject) && objType != typeof(List), ".Mapper() parameter error"); + if (objType == typeof(List)) + { + objType = typeof(TObject); + } + var mainFiledName = mainFieldExp.Member.Name; + var childFiledName = childFieldExp.Member.Name; + var objName = mapperObjectExp.Member.Name; + var filedEntity = this.Context.EntityMaintenance.GetEntityInfo(objType); + var objEntity = this.Context.EntityMaintenance.GetEntityInfo(filedType); + var isSelf = filedType == typeof(T); + if (Mappers == null) + Mappers = new List>>(); + if (isSelf) + { + Action> mapper = (entitys) => + { + if (entitys.IsNullOrEmpty() || entitys.Count == 0) return; + var entity = entitys.First(); + var whereCol = filedEntity.Columns.FirstOrDefault(it => it.PropertyName.Equals(childFiledName, StringComparison.CurrentCultureIgnoreCase)); + if (whereCol == null) + { + whereCol = filedEntity.Columns.FirstOrDefault(it => it.IsPrimarykey == true); + } + if (whereCol == null) + { + whereCol = filedEntity.Columns.FirstOrDefault(it => GetPrimaryKeys().Any(pk => pk.Equals(it.DbColumnName, StringComparison.CurrentCultureIgnoreCase))); + } + if (whereCol == null) + { + whereCol = filedEntity.Columns.FirstOrDefault(it => it.PropertyName.Equals("id", StringComparison.CurrentCultureIgnoreCase)); + } + if (whereCol == null) + { + whereCol = filedEntity.Columns.FirstOrDefault(it => (it.PropertyName).Equals(it.EntityName + "id", StringComparison.CurrentCultureIgnoreCase)); + } + if (whereCol == null) + { + Check.Exception(true, ".Mapper() parameter error"); + } + List inValues = entitys.Select(it => it.GetType().GetProperty(mainFiledName).GetValue(it, null).ObjToString()).ToList(); + List wheres = new List() + { + new ConditionalModel() + { + FieldName=whereCol.DbColumnName, + ConditionalType= ConditionalType.In, + FieldValue=string.Join(",",inValues.Distinct()) + } + }; + var list = this.Context.Queryable().Where(wheres).ToList(); + foreach (var item in entitys) + { + var whereValue = item.GetType().GetProperty(mainFiledName).GetValue(item, null); + var setValue = list.Where(x => x.GetType().GetProperty(whereCol.PropertyName).GetValue(x, null).ObjToString() == whereValue.ObjToString()).ToList(); + var setObject = item.GetType().GetProperty(objName); + if (setObject.PropertyType.FullName.IsCollectionsList()) + { + setObject.SetValue(item, setValue.ToList(), null); + } + else + { + setObject.SetValue(item, setValue.FirstOrDefault(), null); + } + } + }; + Mappers.Add(mapper); + } + else + { + Action> mapper = (entitys) => + { + if (entitys.IsNullOrEmpty() || entitys.Count == 0) return; + var entity = entitys.First(); + var tEntity = this.Context.EntityMaintenance.GetEntityInfo(); + var whereCol = tEntity.Columns.FirstOrDefault(it => it.PropertyName.Equals(childFiledName, StringComparison.CurrentCultureIgnoreCase)); + if (whereCol == null) + { + whereCol = tEntity.Columns.FirstOrDefault(it => it.IsPrimarykey == true); + } + if (whereCol == null) + { + whereCol = tEntity.Columns.FirstOrDefault(it => GetPrimaryKeys().Any(pk => pk.Equals(it.DbColumnName, StringComparison.CurrentCultureIgnoreCase))); + } + if (whereCol == null) + { + whereCol = tEntity.Columns.FirstOrDefault(it => it.PropertyName.Equals("id", StringComparison.CurrentCultureIgnoreCase)); + } + if (whereCol == null) + { + whereCol = tEntity.Columns.FirstOrDefault(it => (it.PropertyName).Equals(it.EntityName + "id", StringComparison.CurrentCultureIgnoreCase)); + } + if (whereCol == null) + { + Check.Exception(true, ".Mapper() parameter error"); + } + List inValues = entitys.Select(it => it.GetType().GetProperty(whereCol.PropertyName).GetValue(it, null).ObjToString()).ToList(); + var dbColumnName = filedEntity.Columns.FirstOrDefault(it => it.PropertyName == mainFiledName).DbColumnName; + List wheres = new List() + { + new ConditionalModel() + { + FieldName=dbColumnName, + ConditionalType= ConditionalType.In, + FieldValue=string.Join(",",inValues) + } + }; + var list = this.Context.Queryable().Where(wheres).ToList(); + foreach (var item in entitys) + { + var whereValue = item.GetType().GetProperty(whereCol.PropertyName).GetValue(item, null); + var setValue = list.Where(x => x.GetType().GetProperty(mainFiledName).GetValue(x, null).ObjToString() == whereValue.ObjToString()).ToList(); + var setObject = item.GetType().GetProperty(objName); + if (setObject.PropertyType.FullName.IsCollectionsList()) + { + setObject.SetValue(item, setValue.ToList(), null); + } + else + { + setObject.SetValue(item, setValue.FirstOrDefault(), null); + } + } + }; + Mappers.Add(mapper); + } + + return this; + } + private void SetContextModel(List result, Type entityType) + { + if (result.HasValue()) + { + if (UtilMethods.GetRootBaseType(entityType).HasValue() && UtilMethods.GetRootBaseType(entityType) == UtilConstants.ModelType) + { + foreach (var item in result) + { + var contextProperty = item.GetType().GetProperty("Context"); + SqlSugarProvider newClient = this.Context.Utilities.CopyContext(); + newClient.Ado.IsDisableMasterSlaveSeparation = true; + if (newClient.CurrentConnectionConfig.AopEvents == null) + newClient.CurrentConnectionConfig.AopEvents = new AopEvents(); + contextProperty.SetValue(item, newClient, null); + } + } + } + } + #endregion + + #region Mapping Type + protected void RestoreMapping() + { + if (IsAs && _RestoreMapping) + { + this.Context.MappingTables = OldMappingTableList == null ? new MappingTableList() : OldMappingTableList; + } + } + protected void InitMapping() + { + if (this.QueryableMappingTableList != null) + this.Context.MappingTables = this.QueryableMappingTableList; + } + #endregion + + #region Other + + private bool IsSingleWithChildTableQuery() + { + return this.QueryBuilder.IsSingle() && this.QueryBuilder.TableShortName.HasValue(); + } + + private Expression> ReplaceMasterTableParameters(Expression> expression) + { + var parameterName = (expression as LambdaExpression)?.Parameters?.FirstOrDefault()?.Name; + if (parameterName != null && parameterName != this.QueryBuilder.TableShortName) + { + expression = ExpressionTool.ChangeLambdaExpression(expression, parameterName, this.QueryBuilder.TableShortName); + } + + return expression; + } + private void orderPropertyNameByJoin(string orderPropertyName, OrderByType? orderByType) + { + var shortName = orderPropertyName.Split('.').FirstOrDefault(); + orderPropertyName = orderPropertyName.Split('.').Last(); + var entityType = this.QueryBuilder.JoinQueryInfos.FirstOrDefault(it => it.ShortName.EqualCase(shortName))?.EntityType; + if (entityType == null) + { + entityType = this.EntityInfo.Type; + } + if (this.Context.EntityMaintenance.GetEntityInfoWithAttr(entityType).Columns.Any(it => + it.DbColumnName?.EqualCase(orderPropertyName) == true + || it.PropertyName?.EqualCase(orderPropertyName) == true)) + { + var name = this.Context.EntityMaintenance.GetEntityInfoWithAttr(entityType).Columns.FirstOrDefault(it => + it.DbColumnName?.EqualCase(orderPropertyName) == true + || it.PropertyName?.EqualCase(orderPropertyName) == true)?.DbColumnName; + this.OrderBy(this.SqlBuilder.GetTranslationColumnName(shortName) + "." + this.SqlBuilder.GetTranslationColumnName(name) + " " + orderByType); + } + else + { + Check.ExceptionEasy($"OrderByPropertyName error.{orderPropertyName} does not exist in the entity class", $"OrderByPropertyName出错实体类中不存在{orderPropertyName}"); + } + } + + private void OutIntoTableSql(string TableName, out KeyValuePair> sqlInfo, out string sql, Type tableInfo) + { + var columnList = this.Context.EntityMaintenance.GetEntityInfo(tableInfo).Columns; + //var entityInfo = this.Context.EntityMaintenance.GetEntityInfo(TableEntityType); + sqlInfo = this.ToSql(); + var name = this.SqlBuilder.GetTranslationTableName(TableName); + var columns = ""; + sql = ""; + var isSqlFunc = this.QueryBuilder.SelectValue is Expression; + if (isSqlFunc) + { + columns = "("; + foreach (var item in ExpressionTool.GetNewExpressionItemList((Expression)this.QueryBuilder.SelectValue)) + { + var column = item.Key; + var columnInfo = columnList.FirstOrDefault(it => it.PropertyName == item.Key); + if (columnInfo != null) + { + column = this.SqlBuilder.GetTranslationColumnName(columnInfo.DbColumnName); + } + columns += $"{column},"; + } + columns = columns.TrimEnd(',') + ")"; + sql = $" INSERT INTO {name} {columns} " + sqlInfo.Key; + } + else + { + //if (this.QueryBuilder.GetSelectValue != null && this.QueryBuilder.GetSelectValue.Contains(",")) ; + //{ + columns = "("; + foreach (var item in this.QueryBuilder.GetSelectValue.Split(',')) + { + var column = Regex.Split(item, " AS ").Last().Trim(); + var columnInfo = columnList.FirstOrDefault(it => SqlBuilder.GetTranslationColumnName(it.PropertyName) == column); + if (columnInfo != null) + { + column = SqlBuilder.GetTranslationColumnName(columnInfo.DbColumnName); + } + columns += $"{column},"; + } + columns = columns.TrimEnd(',') + ")"; + //} + sql = $" INSERT INTO {name} {columns} " + sqlInfo.Key; + } + } + + internal string GetTableName(EntityInfo entity, string tableName) + { + var oldTableName = tableName; + var attr = entity?.Type?.GetCustomAttribute(); + var configId = ((object)this.Context.CurrentConnectionConfig.ConfigId).ObjToString(); + if (attr != null && configId != attr.configId.ObjToString()) + { + var dbName = this.Context.Root.GetConnection(attr.configId).Ado.Connection.Database; + var guidPoint = Guid.NewGuid().ObjToString(); + dbName = dbName.Replace(".", guidPoint); + tableName = this.Context.Root.GetConnection(attr.configId).EntityMaintenance.GetEntityInfo(entity.Type).DbTableName; + oldTableName = tableName; + tableName = this.QueryBuilder.LambdaExpressions.DbMehtods.GetTableWithDataBase + (this.QueryBuilder.Builder.GetTranslationColumnName(dbName), this.QueryBuilder.Builder.GetTranslationColumnName(tableName)); + tableName = tableName.Replace(guidPoint, "."); + } + + if (attr != null && configId != attr.configId.ObjToString()) + { + var dbLinkName = this.Context.Root.GetConnection(attr.configId).CurrentConnectionConfig.DbLinkName; + if (dbLinkName != null) + { + if (dbLinkName.First() == '@') + { + tableName = this.QueryBuilder.Builder.GetTranslationColumnName(oldTableName) + dbLinkName; + } + else if (dbLinkName.Last() == '_') + { + tableName = dbLinkName + oldTableName; + } + else + { + tableName = dbLinkName + "." + this.QueryBuilder.Builder.GetTranslationColumnName(oldTableName); + } + } + } + if (tableName == null) + { + tableName = entity.DbTableName; + } + return tableName; + } + protected string AppendSelect(List entityColumnInfos, string sql, ReadOnlyCollection parameters, List columnsResult, int parameterIndex1) + { + //var lowerSql = sql.ToLower(); + //var isSubquery = lowerSql.Contains("select ") && ExpressionTool.IsMemberInit(this.QueryBuilder.SelectValue); + //if (isSubquery) + //{ + return AppendSelectWithSubQuery(entityColumnInfos, sql, parameters, columnsResult, parameterIndex1); + //} + //var columns = entityColumnInfos; + //var parameterName = parameters[parameterIndex1]; + //foreach (var item in columns) + //{ + // if (item.IsIgnore == false && columnsResult.Any(it => it.PropertyName.EqualCase(item.PropertyName)) && !lowerSql.Contains(SqlBuilder.GetTranslationColumnName(item.PropertyName.ToLower()))) + // { + // if (item.EntityName == this.QueryBuilder.EntityName&&sql.StartsWith("*,")) + // { + // continue; + // } + // sql = $" {sql},{SqlBuilder.GetTranslationColumnName(item.DbColumnName)} AS {SqlBuilder.GetTranslationColumnName(item.PropertyName)} "; + // } + //} + + //return sql; + } + private string AppendSelectWithSubQuery(List entityColumnInfos, string sql, ReadOnlyCollection parameters, List columnsResult, int parameterIndex1, string parameterName) + { + var list = ExpressionTool.GetMemberInit(this.QueryBuilder.SelectValue)?.Bindings?.Cast() + ?.Select(it => it.Member.Name)?.ToList() ?? new List(); + var columns = entityColumnInfos; + //var parameterName = parameters[parameterIndex1]; + if (this.QueryBuilder.AutoAppendedColumns == null) + { + this.QueryBuilder.AutoAppendedColumns = new List(); + } + foreach (var item in columns) + { + if (item.IsIgnore == false && !this.QueryBuilder.AutoAppendedColumns.Contains(item.PropertyName) && columnsResult.Any(it => it.PropertyName.EqualCase(item.PropertyName)) && !list.Any(it => it.EqualCase(item.PropertyName))) + { + if (!sql.Contains($"{SqlBuilder.GetTranslationColumnName(parameterName)}.{SqlBuilder.GetTranslationColumnName(item.DbColumnName)} AS {SqlBuilder.GetTranslationColumnName(item.PropertyName)}") && + !sql.Contains($"{SqlBuilder.GetTranslationColumnName(parameterName)}.{SqlBuilder.GetTranslationColumnName(item.DbColumnName)} AS {SqlBuilder.GetTranslationColumnName(item.PropertyName)}")) + { + sql = $" {sql},{SqlBuilder.GetTranslationColumnName(parameterName)}.{SqlBuilder.GetTranslationColumnName(item.DbColumnName)} AS {SqlBuilder.GetTranslationColumnName(item.PropertyName)} "; + this.QueryBuilder.AutoAppendedColumns.Add(item.PropertyName); + } + } + } + return sql; + } + + private string AppendSelectWithSubQuery(List entityColumnInfos, string sql, ReadOnlyCollection parameters, List columnsResult, int parameterIndex1) + { + var list = ExpressionTool.GetMemberInit(this.QueryBuilder.SelectValue).Bindings.Cast() + .Select(it => it.Member.Name).ToList(); + var columns = entityColumnInfos; + var parameterName = parameters[parameterIndex1]; + if (this.QueryBuilder.AutoAppendedColumns == null) + { + this.QueryBuilder.AutoAppendedColumns = new List(); + } + foreach (var item in columns) + { + if (item.IsIgnore == false && !this.QueryBuilder.AutoAppendedColumns.Contains(item.PropertyName) && columnsResult.Any(it => it.PropertyName.EqualCase(item.PropertyName)) && !list.Any(it => it.EqualCase(item.PropertyName))) + { + if (!sql.Contains($"{SqlBuilder.GetTranslationColumnName(item.DbColumnName)} AS {SqlBuilder.GetTranslationColumnName(item.PropertyName)}") && + !sql.Contains($"{SqlBuilder.GetTranslationColumnName(item.DbColumnName)} AS {SqlBuilder.GetTranslationColumnName(item.PropertyName)}")) + { + + if (parameterIndex1 == 0 && this.QueryBuilder.JoinQueryInfos.Count != 0) + { + sql = $" {sql},{SqlBuilder.GetTranslationColumnName(this.QueryBuilder.TableShortName)}.{SqlBuilder.GetTranslationColumnName(item.DbColumnName)} AS {SqlBuilder.GetTranslationColumnName(item.PropertyName)} "; + this.QueryBuilder.AutoAppendedColumns.Add(item.PropertyName); + } + else + { + sql = $" {sql},{SqlBuilder.GetTranslationColumnName(item.DbColumnName)} AS {SqlBuilder.GetTranslationColumnName(item.PropertyName)} "; + this.QueryBuilder.AutoAppendedColumns.Add(item.PropertyName); + } + } + } + } + return sql; + } + + protected string AppendSelect(string sql, ReadOnlyCollection parameters, List columnsResult, int parameterIndex1) + { + var columns = this.Context.EntityMaintenance.GetEntityInfoWithAttr(typeof(EntityType)).Columns; + //var lowerSql = sql.ToLower(); + //var isSubquery = lowerSql.Contains("select ") && ExpressionTool.IsMemberInit(this.QueryBuilder.SelectValue); + //if (isSubquery) + //{ + return AppendSelectWithSubQuery(columns, sql, parameters, columnsResult, parameterIndex1, parameters[parameterIndex1].Name); + //} + //var parameterName = parameters[parameterIndex1].Name; + //if (parameterName.HasValue()) + //{ + // parameterName = this.SqlBuilder.GetTranslationColumnName(parameterName); + //} + //foreach (var item in columns) + //{ + // if (item.IsIgnore == false && columnsResult.Any(it => it.PropertyName.EqualCase(item.PropertyName)) && !sql.ToLower().Contains(SqlBuilder.GetTranslationColumnName(item.PropertyName).ToLower())) + // { + // if (!sql.Contains($"{parameterName}.{SqlBuilder.GetTranslationColumnName(item.DbColumnName)} AS {SqlBuilder.GetTranslationColumnName(item.PropertyName)}")&& + // !sql.Contains($"{parameterName}.{SqlBuilder.GetTranslationColumnName(item.DbColumnName)} AS {SqlBuilder.GetTranslationColumnName(item.PropertyName)}")) + // { + // sql = $" {sql},{parameterName}.{SqlBuilder.GetTranslationColumnName(item.DbColumnName)} AS {SqlBuilder.GetTranslationColumnName(item.PropertyName)} "; + // } + // } + //} + + //return sql; + } + + internal JoinQueryInfo GetJoinInfo(Expression joinExpression, JoinType joinType) + { + QueryBuilder.CheckExpressionNew(joinExpression, "Join"); + QueryBuilder.JoinExpression = joinExpression; + var express = LambdaExpression.Lambda(joinExpression).Body; + var lastPareamter = (express as LambdaExpression).Parameters.Last(); + var expResult = this.QueryBuilder.GetExpressionValue(joinExpression, ResolveExpressType.WhereMultiple); + this.Context.InitMappingInfo(lastPareamter.Type); + var result = new JoinQueryInfo() + { + JoinIndex = QueryBuilder.JoinQueryInfos.Count, + JoinType = joinType, + JoinWhere = expResult.GetResultString(), + ShortName = lastPareamter.Name, + EntityType = lastPareamter.Type, + TableName = null + }; + if (QueryBuilder.IsCrossQueryWithAttr) + { + result.TableName = GetTableName(this.Context.EntityMaintenance.GetEntityInfoWithAttr(lastPareamter.Type), result.TableName); + } + else + { + result.TableName = this.Context.EntityMaintenance.GetTableName(lastPareamter.Type); + } + if (this.Context.CurrentConnectionConfig?.MoreSettings?.PgSqlIsAutoToLower == false) + { + result.ShortName = this.SqlBuilder.GetTranslationColumnName(result.ShortName); + } + if (this.EntityInfo.Type == result.EntityType && this.QueryBuilder?.AsTables?.Count == 1) + { + var tableName = this.QueryBuilder.AsTables.First().Value; + if (tableName.EndsWith(" MergeTable ") && tableName?.Trim() == this.QueryBuilder.GetTableNameString?.Trim()) + { + this.QueryBuilder.MasterDbTableName = " (SELECT * FROM " + tableName + ")"; + this.QueryBuilder.AsTables?.Clear(); + } + } + if (result.JoinIndex == 0) + { + var firstPareamter = (express as LambdaExpression).Parameters.First(); + this.QueryBuilder.TableShortName = firstPareamter.Name; + if (this.QueryBuilder.AsTables?.Count == 1) + { + var tableinfo = this.QueryBuilder.AsTables.First(); + if (this.QueryBuilder.TableWithString != SqlWith.Null && this.Context.CurrentConnectionConfig?.MoreSettings?.IsWithNoLockQuery == true && this.QueryBuilder.AsTables.First().Value.ObjToString().Contains(SqlWith.NoLock) == false) + { + if (this.QueryBuilder.AsTables.First().Value.EndsWith(") unionTable ")) + { + this.QueryBuilder.AsTables[tableinfo.Key] = " (SELECT * FROM " + this.QueryBuilder.AsTables.First().Value + ")"; + } + else if (this.QueryBuilder.AsTables.First().Value.EndsWith(") MergeTable ")) + { + this.QueryBuilder.AsTables[tableinfo.Key] = " (SELECT * FROM " + this.QueryBuilder.AsTables.First().Value + ")"; + } + else + { + this.QueryBuilder.AsTables[tableinfo.Key] = " (SELECT * FROM " + this.QueryBuilder.AsTables.First().Value + $" {SqlWith.NoLock} )"; + } + } + else if (this.QueryBuilder.IsSqlQuery && this.QueryBuilder.AsTables.First().Value.ObjToString().StartsWith('(')) + { + var tableName = this.QueryBuilder.AsTables.First().Value; + this.QueryBuilder.AsTables[tableinfo.Key] = " (SELECT * FROM " + tableName + ")" + this.QueryBuilder.TableShortName; + } + else + { + var tableName = this.QueryBuilder.AsTables.First().Value; + if (tableName != null && Regex.IsMatch(tableName.Replace(".", "").Replace("-", ""), @"^\w+$")) + { + tableName = SqlBuilder.GetTranslationTableName(tableName); + } + this.QueryBuilder.AsTables[tableinfo.Key] = " (SELECT * FROM " + tableName + ")"; + } + this.QueryBuilder.SelectValue = this.SqlBuilder.GetTranslationColumnName(this.QueryBuilder.TableShortName) + ".*"; + } + } + Check.Exception(result.JoinIndex > 10, ErrorMessage.GetThrowMessage("只支持12个表", "Only 12 tables are supported")); + return result; + } + protected ISugarQueryable _Select(Expression expression) + { + QueryBuilder.CheckExpression(expression, "Select"); + this.Context.InitMappingInfo(typeof(TResult)); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.Context = this.Context; + result.SqlBuilder = this.SqlBuilder; + result.SqlBuilder.QueryBuilder.Parameters = QueryBuilder.Parameters; + result.SqlBuilder.QueryBuilder.SelectValue = expression; + result.SqlBuilder.QueryBuilder.IsSelectSingleFiledJson = UtilMethods.IsJsonMember(expression, this.Context); + result.SqlBuilder.QueryBuilder.IsSelectSingleFiledArray = UtilMethods.IsArrayMember(expression, this.Context); + if (this.IsCache) + { + result.WithCache(this.CacheTime); + } + if (this.QueryBuilder.IsSqlQuery) + { + this.QueryBuilder.IsSqlQuerySelect = true; + } + return result; + } + protected void _Where(Expression expression) + { + QueryBuilder.CheckExpression(expression, "Where"); + var isSingle = QueryBuilder.IsSingle(); + var result = QueryBuilder.GetExpressionValue(expression, isSingle ? ResolveExpressType.WhereSingle : ResolveExpressType.WhereMultiple); + QueryBuilder.WhereInfos.Add(SqlBuilder.AppendWhereOrAnd(QueryBuilder.WhereInfos.IsNullOrEmpty(), result.GetResultString())); + } + protected ISugarQueryable _OrderBy(Expression expression, OrderByType type = OrderByType.Asc) + { + QueryBuilder.CheckExpression(expression, "OrderBy"); + var isSingle = QueryBuilder.IsSingle(); + var member = ExpressionTool.RemoveConvert(ExpressionTool.GetLambdaExpressionBody(expression)) is MemberExpression; + if (member == false && expression.ToString().IsContainsIn("Desc(", "Asc(")) + { + var orderValue = ""; + var newExp = (expression as LambdaExpression).Body as NewExpression; + if (newExp == null) + { + orderValue = QueryBuilder.GetExpressionValue(expression, isSingle ? ResolveExpressType.FieldSingle : ResolveExpressType.FieldMultiple).GetResultString(); + return OrderBy(orderValue); + } + foreach (var item in newExp.Arguments) + { + if (item is MemberExpression) + { + orderValue += + QueryBuilder.GetExpressionValue(item, isSingle ? ResolveExpressType.FieldSingle : ResolveExpressType.FieldMultiple).GetResultString() + ","; + } + else + { + orderValue += + QueryBuilder.GetExpressionValue(item, isSingle ? ResolveExpressType.WhereSingle : ResolveExpressType.WhereMultiple).GetResultString() + ","; + } + } + orderValue = orderValue.TrimEnd(','); + OrderBy(orderValue); + return this; + } + else if ((expression as LambdaExpression).Body is NewExpression) + { + var newExp = (expression as LambdaExpression).Body as NewExpression; + var result = ""; + foreach (var item in newExp.Arguments) + { + if (item is MemberExpression && type == OrderByType.Asc) + { + result += + QueryBuilder.GetExpressionValue(item, isSingle ? ResolveExpressType.FieldSingle : ResolveExpressType.FieldMultiple).GetResultString() + ","; + } + else if (item is MemberExpression && type == OrderByType.Desc) + { + result += + QueryBuilder.GetExpressionValue(item, isSingle ? ResolveExpressType.FieldSingle : ResolveExpressType.FieldMultiple).GetResultString() + " Desc,"; + } + else if (type == OrderByType.Desc) + { + result += + QueryBuilder.GetExpressionValue(item, isSingle ? ResolveExpressType.WhereSingle : ResolveExpressType.WhereMultiple).GetResultString() + " Desc ,"; + } + else + { + result += + QueryBuilder.GetExpressionValue(item, isSingle ? ResolveExpressType.WhereSingle : ResolveExpressType.WhereMultiple).GetResultString() + ","; + } + } + result = result.TrimEnd(','); + OrderBy(result); + return this; + } + else + { + var lamResult = QueryBuilder.GetExpressionValue(expression, isSingle ? ResolveExpressType.FieldSingle : ResolveExpressType.FieldMultiple); + OrderBy(lamResult.GetResultString() + UtilConstants.Space + type.ToString().ToUpper()); + return this; + } + } + private void _ToOffsetPage(int pageIndex, int pageSize) + { + QueryBuilder.Offset = $" OFFSET {(pageIndex - 1) * pageSize} ROWS FETCH NEXT {pageSize} ROWS ONLY"; + } + private int _PageList(int pageIndex, int pageSize) + { + if (pageIndex == 0) + pageIndex = 1; + if (QueryBuilder.PartitionByValue.HasValue()) + { + QueryBuilder.ExternalPageIndex = pageIndex; + QueryBuilder.ExternalPageSize = pageSize; + } + else + { + QueryBuilder.Skip = (pageIndex - 1) * pageSize; + QueryBuilder.Take = pageSize; + } + + return pageIndex; + } + protected ISugarQueryable _GroupBy(Expression expression) + { + var oldParameterNames = this.QueryBuilder.Parameters?.Select(it => it.ParameterName) + ?.ToList(); + QueryBuilder.CheckExpression(expression, "GroupBy"); + LambdaExpression lambda = expression as LambdaExpression; + expression = lambda.Body; + var isSingle = QueryBuilder.IsSingle(); + ExpressionResult lamResult = null; + string result = null; + if (expression is NewExpression) + { + var newExp = expression as NewExpression; + foreach (var item in newExp.Arguments) + { + if (item is MemberExpression) + { + result += + QueryBuilder.GetExpressionValue(item, isSingle ? ResolveExpressType.FieldSingle : ResolveExpressType.FieldMultiple).GetResultString() + ","; + } + else + { + result += + QueryBuilder.GetExpressionValue(item, isSingle ? ResolveExpressType.WhereSingle : ResolveExpressType.WhereMultiple).GetResultString() + ","; + } + } + result = result.TrimEnd(','); + } + else + { + expression = ExpressionTool.RemoveConvert(expression); + lamResult = QueryBuilder.GetExpressionValue(expression, isSingle ? ResolveExpressType.FieldSingle : ResolveExpressType.FieldMultiple); + result = lamResult.GetResultString(); + } + if (oldParameterNames != null && this.Context.CurrentConnectionConfig.DbType == DbType.SqlServer) + { + var newParas = this.QueryBuilder.Parameters.Where(it => !oldParameterNames.Contains(it.ParameterName)).ToList(); + this.QueryBuilder.GroupParameters = newParas; + var groupBySql = UtilMethods.GetSqlString(DbType.SqlServer, result, newParas.ToArray()); + this.QueryBuilder.GroupBySql = groupBySql; + this.QueryBuilder.GroupBySqlOld = result; + GroupBy(result); + } + else + { + GroupBy(result); + } + return this; + } + protected ISugarQueryable _As(string tableName, string entityName) + { + if (this.QueryBuilder.AsTables?.Any(it => it.Key == entityName) == true) + { + if (this.QueryBuilder.JoinQueryInfos.Count(it => it.TableName.EqualCase(tableName)) > 1) + { + Check.ExceptionEasy($"if same entity name ,.LeftJoin(db.Queryable<{entityName}>,(x,y..)=>....).As(\"{tableName}\")", $"存在相同实体,请使用.LeftJoin(db.Queryable<{entityName}>).As(\"{tableName}\",(x,y..)=>...)"); + } + Check.Exception(true, ErrorMessage.GetThrowMessage($"use As<{tableName}>(\"{tableName}\")", $"请把 As(\"{tableName}\"), 改成 As<{tableName}实体>(\"{tableName}\")")); + } + else + { + this.QueryBuilder.AsTables.Add(entityName, tableName); + } + return this; + } + protected void _Filter(string FilterName, bool isDisabledGobalFilter) + { + QueryBuilder.IsDisabledGobalFilter = isDisabledGobalFilter; + if (this.Context.QueryFilter.GetFilterList.HasValue() && FilterName.HasValue()) + { + var list = this.Context.QueryFilter.GetFilterList.Where(it => it.FilterName == FilterName && it.IsJoinQuery == !QueryBuilder.IsSingle()); + foreach (var item in list) + { + var filterResult = item.FilterValue(this.Context); + Where(filterResult.Sql + UtilConstants.Space, filterResult.Parameters); + } + } + } + public ISugarQueryable _PartitionBy(Expression expression) + { + QueryBuilder.CheckExpression(expression, "PartitionBy"); + LambdaExpression lambda = expression as LambdaExpression; + expression = lambda.Body; + var isSingle = QueryBuilder.IsSingle(); + ExpressionResult lamResult = null; + string result = null; + if (expression is NewExpression) + { + lamResult = QueryBuilder.GetExpressionValue(expression, isSingle ? ResolveExpressType.ArraySingle : ResolveExpressType.ArrayMultiple); + result = string.Join(",", lamResult.GetResultArray()); + } + else + { + lamResult = QueryBuilder.GetExpressionValue(expression, isSingle ? ResolveExpressType.FieldSingle : ResolveExpressType.FieldMultiple); + result = lamResult.GetResultString(); + } + PartitionBy(result); + return this; + } + protected ISugarQueryable _Having(Expression expression) + { + QueryBuilder.CheckExpression(expression, "Having"); + var isSingle = QueryBuilder.IsSingle(); + var lamResult = QueryBuilder.GetExpressionValue(expression, isSingle ? ResolveExpressType.WhereSingle : ResolveExpressType.WhereMultiple); + Having(lamResult.GetResultString()); + return this; + } + protected List _ToList() + { + List result = null; + var sqlObj = this._ToSql(); + if (IsCache) + { + var cacheService = this.Context.CurrentConnectionConfig.ConfigureExternalServices.DataInfoCacheService; + result = CacheSchemeMain.GetOrCreate>(cacheService, this.QueryBuilder, () => { return GetData(sqlObj); }, CacheTime, this.Context, CacheKey); + } + else + { + result = GetData(sqlObj); + } + RestoreMapping(); + _InitNavigat(result); + _SubQuery(result); + _Mapper(result); + return result; + } + + + + protected async Task> _ToListAsync() + { + List result = null; + var sqlObj = this._ToSql(); + if (IsCache) + { + var cacheService = this.Context.CurrentConnectionConfig.ConfigureExternalServices.DataInfoCacheService; + result = CacheSchemeMain.GetOrCreate>(cacheService, this.QueryBuilder, () => { return GetData(sqlObj); }, CacheTime, this.Context, CacheKey); + } + else + { + result = await GetDataAsync(sqlObj).ConfigureAwait(false); + } + RestoreMapping(); + await _InitNavigatAsync(result).ConfigureAwait(false); + await _SubQueryAsync(result).ConfigureAwait(false); + _Mapper(result); + return result; + } + private void ToSqlBefore() + { + var moreSetts = this.Context.CurrentConnectionConfig.MoreSettings; + if (moreSetts?.IsWithNoLockQuery == true && string.IsNullOrEmpty(QueryBuilder.TableWithString)) + { + if (moreSetts.DisableWithNoLockWithTran && this.Context.Ado.IsAnyTran()) + { + //No With(nolock) + } + else + { + this.With(SqlWith.NoLock); + } + } + } + protected List GetData(KeyValuePair> sqlObj) + { + List result; + var isComplexModel = QueryBuilder.IsComplexModel(sqlObj.Key); + var entityType = typeof(TResult); + bool isChangeQueryableSlave = GetIsSlaveQuery(); + bool isChangeQueryableMasterSlave = GetIsMasterQuery(); + string sqlString = sqlObj.Key; + SugarParameter[] parameters = sqlObj.Value.ToArray(); + var dataReader = this.Db.GetDataReader(sqlString, parameters); + this.Db.GetDataBefore(sqlString, parameters); + if (entityType.IsInterface) + { + result = GetData(isComplexModel, this.QueryBuilder.AsType, dataReader); + } + else + { + result = GetData(isComplexModel, entityType, dataReader); + } + this.Db.GetDataAfter(sqlString, parameters); + RestChangeMasterQuery(isChangeQueryableMasterSlave); + RestChangeSlaveQuery(isChangeQueryableSlave); + return result; + } + protected async Task> GetDataAsync(KeyValuePair> sqlObj) + { + List result; + var isComplexModel = QueryBuilder.IsComplexModel(sqlObj.Key); + var entityType = typeof(TResult); + bool isChangeQueryableSlave = GetIsSlaveQuery(); + bool isChangeQueryableMasterSlave = GetIsMasterQuery(); + string sqlString = sqlObj.Key; + SugarParameter[] parameters = sqlObj.Value.ToArray(); + var dataReader = await Db.GetDataReaderAsync(sqlString, parameters).ConfigureAwait(false); + this.Db.GetDataBefore(sqlString, parameters); + if (entityType.IsInterface) + { + result = await GetDataAsync(isComplexModel, QueryBuilder.AsType, dataReader).ConfigureAwait(false); + } + else + { + result = await GetDataAsync(isComplexModel, entityType, dataReader).ConfigureAwait(false); + } + this.Db.GetDataAfter(sqlString, parameters); + RestChangeMasterQuery(isChangeQueryableMasterSlave); + RestChangeSlaveQuery(isChangeQueryableSlave); + return result; + } + private List GetData(bool isComplexModel, Type entityType, IDataReader dataReader) + { + List result; + this.Bind.QueryBuilder = this.QueryBuilder; + this.Context.Utilities.QueryBuilder = this.QueryBuilder; + if (entityType == UtilConstants.DynamicType) + { + result = this.Context.Utilities.DataReaderToExpandoObjectList(dataReader) as List; + } + else if (entityType.Name?.StartsWith("ValueTuple`") == true) + { + result = Db.Context.Utilities.DataReaderToValueTupleType(dataReader); + } + else if (entityType == UtilConstants.ObjType) + { + result = this.Context.Utilities.DataReaderToExpandoObjectList(dataReader).Select(it => ((TResult)(object)it)).ToList(); + } + else if (QueryBuilder.IsSelectSingleFiledJson) + { + result = this.Context.Utilities.DataReaderToSelectJsonList(dataReader); + } + else if (QueryBuilder.IsSelectSingleFiledArray) + { + result = this.Context.Utilities.DataReaderToSelectArrayList(dataReader); + } + else if (QueryBuilder.IsParameterizedConstructor || entityType.IsAnonymousType() || isComplexModel || StaticConfig.EnableAot) + { + if (entityType.IsClass() == false && StaticConfig.EnableAot) + { + result = this.Bind.DataReaderToList(entityType, dataReader); + } + else + { + result = this.Context.Utilities.DataReaderToList(dataReader); + if (StaticConfig.EnableAot) + ResetNavigationPropertiesForAot(result); + } + } + else + { + result = this.Bind.DataReaderToList(entityType, dataReader); + } + SetContextModel(result, entityType); + return result; + } + private async Task> GetDataAsync(bool isComplexModel, Type entityType, IDataReader dataReader) + { + List result; + this.Bind.QueryBuilder = this.QueryBuilder; + this.Context.Utilities.QueryBuilder = this.QueryBuilder; + if (entityType == UtilConstants.DynamicType) + { + result = await Context.Utilities.DataReaderToExpandoObjectListAsync(dataReader).ConfigureAwait(false) as List; + } + else if (entityType.Name?.StartsWith("ValueTuple`") == true) + { + result = await Db.Context.Utilities.DataReaderToValueTupleTypeAsync(dataReader).ConfigureAwait(false); + } + else if (entityType == UtilConstants.ObjType) + { + var expObj = await Context.Utilities.DataReaderToExpandoObjectListAsync(dataReader).ConfigureAwait(false); + result = expObj.Select(it => ((TResult)(object)it)).ToList(); + } + else if (QueryBuilder.IsSelectSingleFiledJson) + { + result = await Context.Utilities.DataReaderToSelectJsonListAsync(dataReader).ConfigureAwait(false); + } + else if (QueryBuilder.IsSelectSingleFiledArray) + { + result = await Context.Utilities.DataReaderToSelectArrayListAsync(dataReader).ConfigureAwait(false); + } + else if (entityType.IsAnonymousType() || isComplexModel || StaticConfig.EnableAot) + { + if (entityType.IsClass() == false && StaticConfig.EnableAot) + { + result = await Bind.DataReaderToListAsync(entityType, dataReader).ConfigureAwait(false); + } + else + { + result = await Context.Utilities.DataReaderToListAsync(dataReader).ConfigureAwait(false); + if (StaticConfig.EnableAot) + ResetNavigationPropertiesForAot(result); + } + } + else + { + result = await Bind.DataReaderToListAsync(entityType, dataReader).ConfigureAwait(false); + } + SetContextModel(result, entityType); + return result; + } + private void ResetNavigationPropertiesForAot(List result) + { + if (StaticConfig.EnableAot) + { + var entityInfo = this.Context.EntityMaintenance.GetEntityInfo(); + var navColumnList = entityInfo.Columns.Where(it => it.Navigat != null); + if (navColumnList.Any()) + { + foreach (var item in result) + { + foreach (var columnInfo in navColumnList) + { + columnInfo.PropertyInfo.SetValue(item, null); + } + } + } + } + } + protected void _InQueryable(Expression expression, KeyValuePair> sqlObj) + { + QueryBuilder.CheckExpression(expression, "In"); + string sql = sqlObj.Key; + if (sqlObj.Value.HasValue()) + { + this.SqlBuilder.RepairReplicationParameters(ref sql, sqlObj.Value.ToArray(), 100); + this.QueryBuilder.Parameters.AddRange(sqlObj.Value); + } + var isSingle = QueryBuilder.IsSingle(); + var lamResult = QueryBuilder.GetExpressionValue(expression, isSingle ? ResolveExpressType.FieldSingle : ResolveExpressType.FieldMultiple); + var fieldName = lamResult.GetResultString(); + var whereSql = string.Format(this.QueryBuilder.InTemplate, fieldName, sql); + this.QueryBuilder.WhereInfos.Add(SqlBuilder.AppendWhereOrAnd(this.QueryBuilder.WhereInfos.IsNullOrEmpty(), whereSql)); + base._InQueryableIndex += 100; + } + protected List GetPrimaryKeys() + { + + return this.EntityInfo.Columns.Where(it => it.IsPrimarykey).Select(it => it.DbColumnName).ToList(); + } + protected virtual List GetIdentityKeys() + { + return this.EntityInfo.Columns.Where(it => it.IsIdentity).Select(it => it.DbColumnName).ToList(); + } + protected void CopyQueryBuilder(QueryBuilder asyncQueryableBuilder) + { + var pars = new List(); + if (this.QueryBuilder.Parameters != null) + { + pars = this.QueryBuilder.Parameters.Select(it => new SugarParameter(it.ParameterName, it.Value) + { + DbType = it.DbType, + Value = it.Value, + ParameterName = it.ParameterName, + Direction = it.Direction, + IsArray = it.IsArray, + IsJson = it.IsJson, + IsNullable = it.IsNullable, + IsRefCursor = it.IsRefCursor, + Size = it.Size, + SourceColumn = it.SourceColumn, + SourceColumnNullMapping = it.SourceColumnNullMapping, + SourceVersion = it.SourceVersion, + TempDate = it.TempDate, + TypeName = it.TypeName, + UdtTypeName = it.UdtTypeName, + IsNvarchar2 = it.IsNvarchar2, + _Size = it._Size + }).ToList(); + } + asyncQueryableBuilder.IsEnableMasterSlaveSeparation = this.QueryBuilder.IsEnableMasterSlaveSeparation; + asyncQueryableBuilder.TranLock = this.QueryBuilder.TranLock; + asyncQueryableBuilder.IsDisableMasterSlaveSeparation = this.QueryBuilder.IsDisableMasterSlaveSeparation; + asyncQueryableBuilder.IsQueryInQuery = this.QueryBuilder.IsQueryInQuery; + asyncQueryableBuilder.Includes = this.QueryBuilder.Includes; + asyncQueryableBuilder.Take = this.QueryBuilder.Take; + asyncQueryableBuilder.Skip = this.QueryBuilder.Skip; + asyncQueryableBuilder.SelectValue = this.QueryBuilder.SelectValue; + asyncQueryableBuilder.WhereInfos = this.Context.Utilities.TranslateCopy(this.QueryBuilder.WhereInfos); + asyncQueryableBuilder.EasyJoinInfos = this.Context.Utilities.TranslateCopy(this.QueryBuilder.EasyJoinInfos); + asyncQueryableBuilder.JoinQueryInfos = QueryBuilder.JoinQueryInfos.Select(it => CopyJoinInfo(it)).ToList(); + asyncQueryableBuilder.WhereIndex = this.QueryBuilder.WhereIndex; + asyncQueryableBuilder.EntityType = this.QueryBuilder.EntityType; + asyncQueryableBuilder.EntityName = this.QueryBuilder.EntityName; + asyncQueryableBuilder.Parameters = pars; + asyncQueryableBuilder.TableShortName = this.QueryBuilder.TableShortName; + asyncQueryableBuilder.TableWithString = this.QueryBuilder.TableWithString; + asyncQueryableBuilder.GroupByValue = this.QueryBuilder.GroupByValue; + asyncQueryableBuilder.IsDistinct = this.QueryBuilder.IsDistinct; + asyncQueryableBuilder.OrderByValue = this.QueryBuilder.OrderByValue; + asyncQueryableBuilder.IsDisabledGobalFilter = this.QueryBuilder.IsDisabledGobalFilter; + asyncQueryableBuilder.PartitionByValue = this.QueryBuilder.PartitionByValue; + asyncQueryableBuilder.JoinExpression = this.QueryBuilder.JoinExpression; + asyncQueryableBuilder.WhereIndex = this.QueryBuilder.WhereIndex; + asyncQueryableBuilder.HavingInfos = this.QueryBuilder.HavingInfos; + asyncQueryableBuilder.AsType = this.QueryBuilder.AsType; + asyncQueryableBuilder.LambdaExpressions.ParameterIndex = this.QueryBuilder.LambdaExpressions.ParameterIndex; + asyncQueryableBuilder.IgnoreColumns = this.Context.Utilities.TranslateCopy(this.QueryBuilder.IgnoreColumns); + asyncQueryableBuilder.AsTables = this.Context.Utilities.TranslateCopy(this.QueryBuilder.AsTables); + asyncQueryableBuilder.DisableTop = this.QueryBuilder.DisableTop; + asyncQueryableBuilder.Offset = this.QueryBuilder.Offset; + asyncQueryableBuilder.IsSqlQuery = this.QueryBuilder.IsSqlQuery; + asyncQueryableBuilder.IsSqlQuerySelect = this.QueryBuilder.IsSqlQuerySelect; + asyncQueryableBuilder.OldSql = this.QueryBuilder.OldSql; + asyncQueryableBuilder.IsCrossQueryWithAttr = this.QueryBuilder.IsCrossQueryWithAttr; + asyncQueryableBuilder.CrossQueryItems = this.QueryBuilder.CrossQueryItems; + asyncQueryableBuilder.SubToListParameters = this.Context.Utilities.TranslateCopy(this.QueryBuilder.SubToListParameters); + asyncQueryableBuilder.AppendColumns = this.Context.Utilities.TranslateCopy(this.QueryBuilder.AppendColumns); + asyncQueryableBuilder.AppendValues = this.Context.Utilities.TranslateCopy(this.QueryBuilder.AppendValues); + asyncQueryableBuilder.RemoveFilters = this.QueryBuilder.RemoveFilters?.ToArray(); + asyncQueryableBuilder.Hints = this.QueryBuilder.Hints; + asyncQueryableBuilder.MasterDbTableName = this.QueryBuilder.MasterDbTableName; + asyncQueryableBuilder.IsParameterizedConstructor = this.QueryBuilder.IsParameterizedConstructor; + asyncQueryableBuilder.GroupParameters = this.QueryBuilder.GroupParameters; + asyncQueryableBuilder.GroupBySql = this.QueryBuilder.GroupBySql; + asyncQueryableBuilder.GroupBySqlOld = this.QueryBuilder.GroupBySqlOld; + if (this.QueryBuilder.AppendNavInfo != null) + { + asyncQueryableBuilder.AppendNavInfo = new AppendNavInfo() + { + AppendProperties = this.QueryBuilder.AppendNavInfo.AppendProperties.ToDictionary(it => it.Key, it => it.Value), + MappingNavProperties = this.QueryBuilder.AppendNavInfo.MappingNavProperties.ToDictionary(it => it.Key, it => it.Value) + }; + } + } + + private static JoinQueryInfo CopyJoinInfo(JoinQueryInfo it) + { + return new JoinQueryInfo() + { + EntityType = it.EntityType, + JoinIndex = it.JoinIndex, + JoinType = it.JoinType, + JoinWhere = it.JoinWhere, + ShortName = it.ShortName, + TableName = it.TableName + }; + } + + protected int SetCacheTime(int cacheDurationInSeconds) + { + if (cacheDurationInSeconds == int.MaxValue && this.Context.CurrentConnectionConfig.MoreSettings?.DefaultCacheDurationInSeconds > 0) + { + cacheDurationInSeconds = this.Context.CurrentConnectionConfig.MoreSettings.DefaultCacheDurationInSeconds; + } + + return cacheDurationInSeconds; + } + public virtual KeyValuePair> _ToSql() + { + InitMapping(); + ToSqlBefore(); + string sql = QueryBuilder.ToSqlString(); + RestoreMapping(); + return new KeyValuePair>(sql, QueryBuilder.Parameters); + } + #endregion + + #region Subquery + private bool IsSubToList() + { + return this.QueryBuilder.SubToListParameters?.Count > 0; + } + + public virtual ISugarQueryable MergeTableWithSubToListJoin() + { + //_ToSql(); + var clone = this.Clone(); + + clone.QueryBuilder.AppendValues = null; + clone.QueryBuilder.SubToListParameters = null; + clone.QueryBuilder.AppendColumns = null; + Check.Exception(this.MapperAction != null || this.MapperActionWithCache != null, ErrorMessage.GetThrowMessage("'Mapper’ needs to be written after ‘MergeTable’ ", "Mapper 只能在 MergeTable 之后使用")); + //Check.Exception(this.QueryBuilder.SelectValue.IsNullOrEmpty(),ErrorMessage.GetThrowMessage( "MergeTable need to use Queryable.Select Method .", "使用MergeTable之前必须要有Queryable.Select方法")); + //Check.Exception(this.QueryBuilder.Skip > 0 || this.QueryBuilder.Take > 0 || this.QueryBuilder.OrderByValue.HasValue(),ErrorMessage.GetThrowMessage( "MergeTable Queryable cannot Take Skip OrderBy PageToList ", "使用 MergeTable不能有 Take Skip OrderBy PageToList 等操作,你可以在Mergetable之后操作")); + var sqlobj = clone.ToSql(); + var index = QueryBuilder.WhereIndex + 1; + var result = this.Context.Queryable().AS(SqlBuilder.GetPackTable(sqlobj.Key, "MergeTable")).AddParameters(sqlobj.Value).Select("*").With(SqlWith.Null); + result.QueryBuilder.WhereIndex = index; + result.QueryBuilder.LambdaExpressions.ParameterIndex = QueryBuilder.LambdaExpressions.ParameterIndex++; + result.QueryBuilder.LambdaExpressions.Index = QueryBuilder.LambdaExpressions.Index++; + if (this.Context.CurrentConnectionConfig.DbType == DbType.Oracle) + { + result.Select("MergeTable.*"); + } + result.QueryBuilder.AppendValues = this.QueryBuilder.AppendValues; + result.QueryBuilder.SubToListParameters = this.QueryBuilder.SubToListParameters; + result.QueryBuilder.AppendColumns = this.QueryBuilder.AppendColumns; + return result; + } + public virtual ISugarQueryable MergeTableWithSubToList() + { + _ToSql(); + var clone = this.Clone(); + + clone.QueryBuilder.AppendValues = null; + clone.QueryBuilder.SubToListParameters = null; + clone.QueryBuilder.AppendColumns = null; + Check.Exception(this.MapperAction != null || this.MapperActionWithCache != null, ErrorMessage.GetThrowMessage("'Mapper’ needs to be written after ‘MergeTable’ ", "Mapper 只能在 MergeTable 之后使用")); + //Check.Exception(this.QueryBuilder.SelectValue.IsNullOrEmpty(),ErrorMessage.GetThrowMessage( "MergeTable need to use Queryable.Select Method .", "使用MergeTable之前必须要有Queryable.Select方法")); + //Check.Exception(this.QueryBuilder.Skip > 0 || this.QueryBuilder.Take > 0 || this.QueryBuilder.OrderByValue.HasValue(),ErrorMessage.GetThrowMessage( "MergeTable Queryable cannot Take Skip OrderBy PageToList ", "使用 MergeTable不能有 Take Skip OrderBy PageToList 等操作,你可以在Mergetable之后操作")); + var sqlobj = clone.ToSql(); + var index = QueryBuilder.WhereIndex + 1; + var result = this.Context.Queryable().AS(SqlBuilder.GetPackTable(sqlobj.Key, "MergeTable")).AddParameters(sqlobj.Value).Select("*").With(SqlWith.Null); + result.QueryBuilder.WhereIndex = index; + result.QueryBuilder.LambdaExpressions.ParameterIndex = QueryBuilder.LambdaExpressions.ParameterIndex++; + result.QueryBuilder.LambdaExpressions.Index = QueryBuilder.LambdaExpressions.Index++; + if (this.Context.CurrentConnectionConfig.DbType == DbType.Oracle) + { + result.Select("MergeTable.*"); + } + result.QueryBuilder.AppendValues = this.QueryBuilder.AppendValues; + result.QueryBuilder.SubToListParameters = this.QueryBuilder.SubToListParameters; + result.QueryBuilder.AppendColumns = this.QueryBuilder.AppendColumns; + return result; + } + + private void _SubQuery(List result) + { + if (result == null || result.Count == 0) + { + return; + } + var isSubToList = this.QueryBuilder.SubToListParameters != null && this.QueryBuilder.SubToListParameters.Count != 0; + if (!isSubToList) + { + return; + } + var appendValues = this.QueryBuilder.AppendValues; + var subParamters = this.QueryBuilder.SubToListParameters; + foreach (var subPara in subParamters) + { + if (appendValues != null) + AppendSubWhereToList(result, appendValues, subPara); + else + AppendSubToList(result, appendValues, subPara); + } + + } + + private void AppendSubToList(List result, List> appendValues, KeyValuePair subPara) + { + var ps = this.QueryBuilder.Parameters; + var itemProperty = typeof(TResult).GetProperty(subPara.Key); + var callType = itemProperty.PropertyType.GetGenericArguments().FirstOrDefault(); + var isFirst = false; + if (callType == null) + { + callType = itemProperty.PropertyType; + isFirst = true; + } + var sql = subPara.Value.ObjToString().Replace("@sugarIndex", "0"); + sql = SqlBuilder.RemoveParentheses(sql); + var methodParamters = new object[] { sql, ps }; + var subList = ExpressionBuilderHelper.CallFunc(callType, methodParamters, this.Clone(), "SubQueryList"); + for (var i = 0; i < result.Count; i++) + { + var item = result[i]; + var setValue = Activator.CreateInstance(itemProperty.PropertyType, true) as IList; + if (typeof(TResult).IsAnonymousType()) + { + if (isFirst) + { + var jobj = JObject.FromObject(item); + var prop = jobj.Property(itemProperty.Name); + prop.Value = JObject.FromObject((subList as IList).Cast().FirstOrDefault()); + result[i] = jobj.ToObject(); + } + else + { + var jobj = JObject.FromObject(item); + var prop = jobj.Property(itemProperty.Name); + prop.Value = JArray.FromObject(subList); + result[i] = jobj.ToObject(); + } + } + else + { + if (isFirst) + { + if ((subList as IList).Count > 0) + { + itemProperty.SetValue(item, (subList as IList)[0]); + } + } + else + { + itemProperty.SetValue(item, subList); + } + } + } + } + + private void AppendSubWhereToList(List result, List> appendValues, KeyValuePair subPara) + { + var ps = this.QueryBuilder.Parameters; + var index = 0; + List sqls = new List(); + foreach (var item in result) + { + + var sql = subPara.Value.ObjToString(); + var replaceValues = appendValues[index]; + foreach (var re in replaceValues) + { + var config = this.Context.CurrentConnectionConfig; + var p = new SugarParameter[] { + new SugarParameter("@p",re.Value) + }; + var isNvarchar = true; + if (this.Context.CurrentConnectionConfig?.DbType == DbType.SqlServer + && this.Context.CurrentConnectionConfig?.MoreSettings?.DisableNvarchar != true) + { + isNvarchar = false; + } + var value = UtilMethods.GetSqlString(config.DbType, "@p", p, isNvarchar); + sql = sql.Replace(re.Name, value); + + } + sql = SqlBuilder.RemoveParentheses(sql); + sql = sql.Replace("@sugarIndex", index + ""); + sqls.Add(sql); + + index++; + } + var itemProperty = typeof(TResult).GetProperty(subPara.Key); + var callType = itemProperty.PropertyType.GetGenericArguments().FirstOrDefault(); + var isFirst = false; + if (callType == null) + { + callType = itemProperty.PropertyType; + isFirst = true; + } + var sqlstring = string.Join(" \r\n UNION ALL ", sqls); + if (callType?.IsClass() == false) + { + Regex regex = new Regex(@"\,\d{1,10} as sugarIndex"); + sqlstring = regex.Replace(sqlstring, it => (" as id " + it.Value)); + callType = typeof(SubQueryToListDefaultT); + } + var methodParamters = new object[] { sqlstring, ps.ToArray() }; + this.QueryBuilder.SubToListParameters = null; + this.QueryBuilder.AppendColumns = new List() { + new QueryableAppendColumn(){ Name="sugarIndex",AsName="sugarIndex" } + }; + this.QueryBuilder.AppendValues = null; + var subList = ExpressionBuilderHelper.CallFunc(callType, methodParamters, this.Clone(), "SubQueryList"); + var appendValue = this.QueryBuilder.AppendValues; + var list = (subList as IEnumerable).Cast().ToList(); + if (isFirst && !typeof(TResult).IsAnonymousType()) + { + SetSubListWithClassFirst(result, itemProperty, appendValue, list, 0); + } + else if (isFirst && typeof(TResult).IsAnonymousType()) + { + SetSubListWithAnonymousTypeFirst(result, itemProperty, appendValue, list, 0); + } + else if (typeof(TResult).IsAnonymousType()) + { + SetSubListWithAnonymousType(result, itemProperty, appendValue, list, 0); + } + else + { + SetSubListWithClass(result, itemProperty, appendValue, list, 0); + } + } + private static int SetSubListWithAnonymousType(List result, PropertyInfo itemProperty, List> appendValue, List list, int resIndex) + { + for (int i = 0; i < result.Count; i++) + { + var item = result[i]; + var setValue = Activator.CreateInstance(itemProperty.PropertyType, true) as IList; + if (appendValue != null) + { + var appindex = 0; + foreach (var appValue in appendValue) + { + if (appValue[0].Value.ObjToInt() == i) + { + var addItem = list[appindex]; + if (addItem is SubQueryToListDefaultT) + { + addItem = (addItem as SubQueryToListDefaultT).id; + } + setValue.Add(addItem); + } + appindex++; + } + } + var jobj = JObject.FromObject(item); + var prop = jobj.Property(itemProperty.Name); + prop.Value = JArray.FromObject(setValue); + result[i] = jobj.ToObject(); + //itemProperty.SetValue(item, setValue); + } + return resIndex; + } + private static int SetSubListWithAnonymousTypeFirst(List result, PropertyInfo itemProperty, List> appendValue, List list, int resIndex) + { + for (int i = 0; i < result.Count; i++) + { + var item = result[i]; + object setValue = null; + if (appendValue != null) + { + var appindex = 0; + foreach (var appValue in appendValue) + { + if (appValue[0].Value.ObjToInt() == i) + { + setValue = list[appindex]; + //setValue.Add(addItem); + } + appindex++; + } + } + var jobj = JObject.FromObject(item); + var prop = jobj.Property(itemProperty.Name); + if (setValue != null) + { + prop.Value = JObject.FromObject(setValue); + } + result[i] = jobj.ToObject(); + //itemProperty.SetValue(item, setValue); + } + return resIndex; + } + private static int SetSubListWithClassFirst(List result, PropertyInfo itemProperty, List> appendValue, List list, int resIndex) + { + foreach (var item in result) + { + //var setValue = Activator.CreateInstance(itemProperty.PropertyType, true); + if (appendValue != null) + { + var appindex = 0; + foreach (var appValue in appendValue) + { + if (appValue[0].Value.ObjToInt() == resIndex) + { + var addItem = list[appindex]; + itemProperty.SetValue(item, addItem); + } + appindex++; + } + } + //itemProperty.SetValue(item, setValue); + resIndex++; + } + + return resIndex; + } + + private static int SetSubListWithClass(List result, PropertyInfo itemProperty, List> appendValue, List list, int resIndex) + { + foreach (var item in result) + { + var setValue = Activator.CreateInstance(itemProperty.PropertyType, true) as IList; + if (appendValue != null) + { + var appindex = 0; + foreach (var appValue in appendValue) + { + if (appValue[0].Value.ObjToInt() == resIndex) + { + var addItem = list[appindex]; + if (addItem is SubQueryToListDefaultT) + { + addItem = ((SubQueryToListDefaultT)addItem).id; + } + if (addItem != null && addItem is string && itemProperty?.PropertyType?.GenericTypeArguments?.FirstOrDefault() == UtilConstants.GuidType) + { + addItem = Guid.Parse(addItem + ""); + } + setValue.Add(addItem); + } + appindex++; + } + } + itemProperty.SetValue(item, setValue); + resIndex++; + } + + return resIndex; + } + + private async Task _SubQueryAsync(List result) + { + var isSubToList = this.QueryBuilder.SubToListParameters != null && this.QueryBuilder.SubToListParameters.Count != 0; + if (!isSubToList) + { + return; + } + await Task.Run(() => { _SubQuery(result); }).ConfigureAwait(false); + } + public List SubQueryList(string sql, object parameters) + { + return this.Context.Ado.SqlQuery(sql, parameters); + } + #endregion + + #region Bool + + private bool MasterHasWhereFirstJoin() + { + if (this.QueryBuilder.IsSingle() == false && this.QueryBuilder.SelectValue is LambdaExpression exp && this.QueryBuilder.AsTables?.Count == 0) + { + if (exp.Parameters.Count == this.QueryBuilder.JoinQueryInfos.Count + 1) + { + return true; + } + } + return this.QueryBuilder.JoinIndex == 0 && + this.QueryBuilder.IsSqlQuery == false && + this.QueryBuilder.AsTables.Count == 0 && + this.QueryBuilder.IsSingle() && + (this.QueryBuilder.WhereInfos.Count != 0 || this.QueryBuilder.SelectValue is Expression); + } + #endregion + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/QueryableProperties.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/QueryableProperties.cs new file mode 100644 index 000000000..d613a6395 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/QueryableProperties.cs @@ -0,0 +1,21 @@ +namespace SqlSugar +{ + public partial class QueryableProvider : QueryableAccessory, ISugarQueryable + { + public SqlSugarProvider Context { get; set; } + public IAdo Db { get { return Context.Ado; } } + public IDbBind Bind { get { return this.Db.DbBind; } } + public ISqlBuilder SqlBuilder { get; set; } + public MappingTableList OldMappingTableList { get; set; } + public MappingTableList QueryableMappingTableList { get; set; } + public List> MapperAction { get; set; } + public Action> MapperActionWithCache { get; set; } + public List>> Mappers { get; set; } + public bool IsCache { get; set; } + public int CacheTime { get; set; } + public string CacheKey { get; set; } + public bool IsAs { get; set; } + public QueryBuilder QueryBuilder { get { return this.SqlBuilder.QueryBuilder; } set { this.SqlBuilder.QueryBuilder = value; } } + public EntityInfo EntityInfo { get { return this.Context.EntityMaintenance.GetEntityInfo(); } } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/QueryableProvider.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/QueryableProvider.cs new file mode 100644 index 000000000..e1ff14a8e --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/QueryableProvider.cs @@ -0,0 +1,1830 @@ +using System.Collections; +using System.Data; +using System.Linq.Expressions; +using System.Reflection; +using System.Text; +using System.Text.RegularExpressions; + +namespace SqlSugar +{ + + public partial class QueryableProvider : QueryableAccessory, ISugarQueryable + { + + public ISugarQueryable CrossQuery(string configId) + { + return this.CrossQuery(typeof(Type), configId); + } + public ISugarQueryable CrossQuery(Type type, string configId) + { + if (this.QueryBuilder.CrossQueryItems == null) + { + this.QueryBuilder.CrossQueryItems = new Dictionary(); + } + QueryBuilder.CrossQueryItems.TryAdd(type.FullName, configId); + return this; + } + public ISugarQueryable IncludeLeftJoin(Expression> leftObjectExp) + { + MemberExpression memberExpression; + string navObjectName; + EntityColumnInfo navColumn, navPkColumn; + EntityInfo navEntityInfo; + ExpressionTool.GetOneToOneInfo(this.Context, leftObjectExp, out memberExpression, out navObjectName, out navColumn, out navEntityInfo, out navPkColumn); + var shortName = $"pnv_{navObjectName}"; + var mainShortName = memberExpression.Expression.ToString(); + this.QueryBuilder.TableShortName = mainShortName; + var onWhere = $"{SqlBuilder.GetTranslationColumnName(shortName)}.{SqlBuilder.GetTranslationColumnName(navPkColumn.DbColumnName)}={SqlBuilder.GetTranslationColumnName(mainShortName)}.{SqlBuilder.GetTranslationColumnName(navColumn.DbColumnName)}"; + UtilMethods.IsNullReturnNew(this.Context.TempItems); + this.AddJoinInfo(GetTableName(navEntityInfo, navEntityInfo.DbTableName), shortName, onWhere, JoinType.Left); + this.QueryBuilder.JoinQueryInfos.Last().EntityType = navEntityInfo.Type; + return this; + } + public ISugarQueryable IncludeInnerJoin(Expression> innerObjectExt) + { + MemberExpression memberExpression; + string navObjectName; + EntityColumnInfo navColumn, navPkColumn; + EntityInfo navEntityInfo; + ExpressionTool.GetOneToOneInfo(this.Context, innerObjectExt, out memberExpression, out navObjectName, out navColumn, out navEntityInfo, out navPkColumn); + var shortName = $"pnv_{navObjectName}"; + var mainShortName = memberExpression.Expression.ToString(); + this.QueryBuilder.TableShortName = mainShortName; + var onWhere = $"{SqlBuilder.GetTranslationColumnName(shortName)}.{SqlBuilder.GetTranslationColumnName(navPkColumn.DbColumnName)}={SqlBuilder.GetTranslationColumnName(mainShortName)}.{SqlBuilder.GetTranslationColumnName(navColumn.DbColumnName)}"; + UtilMethods.IsNullReturnNew(this.Context.TempItems); + this.AddJoinInfo(GetTableName(navEntityInfo, navEntityInfo.DbTableName), shortName, onWhere, JoinType.Inner); + this.QueryBuilder.JoinQueryInfos.Last().EntityType = navEntityInfo.Type; + return this; + } + public ISugarQueryable IncludeFullJoin(Expression> fullObjectExp) + { + MemberExpression memberExpression; + string navObjectName; + EntityColumnInfo navColumn, navPkColumn; + EntityInfo navEntityInfo; + ExpressionTool.GetOneToOneInfo(this.Context, fullObjectExp, out memberExpression, out navObjectName, out navColumn, out navEntityInfo, out navPkColumn); + var shortName = $"pnv_{navObjectName}"; + var mainShortName = memberExpression.Expression.ToString(); + this.QueryBuilder.TableShortName = mainShortName; + var onWhere = $"{SqlBuilder.GetTranslationColumnName(shortName)}.{SqlBuilder.GetTranslationColumnName(navPkColumn.DbColumnName)}={SqlBuilder.GetTranslationColumnName(mainShortName)}.{SqlBuilder.GetTranslationColumnName(navColumn.DbColumnName)}"; + UtilMethods.IsNullReturnNew(this.Context.TempItems); + this.AddJoinInfo(GetTableName(navEntityInfo, navEntityInfo.DbTableName), shortName, onWhere, JoinType.Full); + this.QueryBuilder.JoinQueryInfos.Last().EntityType = navEntityInfo.Type; + return this; + } + public ISugarQueryable IncludeRightJoin(Expression> rightObjectExp) + { + MemberExpression memberExpression; + string navObjectName; + EntityColumnInfo navColumn, navPkColumn; + EntityInfo navEntityInfo; + ExpressionTool.GetOneToOneInfo(this.Context, rightObjectExp, out memberExpression, out navObjectName, out navColumn, out navEntityInfo, out navPkColumn); + var shortName = $"pnv_{navObjectName}"; + var mainShortName = memberExpression.Expression.ToString(); + this.QueryBuilder.TableShortName = mainShortName; + var onWhere = $"{SqlBuilder.GetTranslationColumnName(shortName)}.{SqlBuilder.GetTranslationColumnName(navPkColumn.DbColumnName)}={SqlBuilder.GetTranslationColumnName(mainShortName)}.{SqlBuilder.GetTranslationColumnName(navColumn.DbColumnName)}"; + UtilMethods.IsNullReturnNew(this.Context.TempItems); + this.AddJoinInfo(GetTableName(navEntityInfo, navEntityInfo.DbTableName), shortName, onWhere, JoinType.Right); + this.QueryBuilder.JoinQueryInfos.Last().EntityType = navEntityInfo.Type; + return this; + } + public ISugarQueryable LeftJoinIF(bool isJoin, ISugarQueryable joinQueryable, Expression> joinExpression) + { + var result = LeftJoin(joinQueryable, joinExpression); + if (isJoin == false) + { + result.QueryBuilder.JoinQueryInfos.Remove(result.QueryBuilder.JoinQueryInfos.Last()); + } + return result; + } + public ISugarQueryable InnerJoinIF(bool isJoin, ISugarQueryable joinQueryable, Expression> joinExpression) + { + var result = InnerJoin(joinQueryable, joinExpression); + if (isJoin == false) + { + result.QueryBuilder.JoinQueryInfos.Remove(result.QueryBuilder.JoinQueryInfos.Last()); + } + return result; + } + public ISugarQueryable LeftJoin(ISugarQueryable joinQueryable, Expression> joinExpression) + { + + if (MasterHasWhereFirstJoin()) + { + return this.MergeTable().LeftJoin(joinQueryable, joinExpression); + } + + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + var joinInfo = GetJoinInfo(joinExpression, JoinType.Left); + var sqlObject = joinQueryable.ToSql(); + string sql = sqlObject.Key; + this.QueryBuilder.LambdaExpressions.ParameterIndex += 100; + UtilMethods.RepairReplicationParameters(ref sql, sqlObject.Value.ToArray(), this.QueryBuilder.LambdaExpressions.ParameterIndex, ""); + joinInfo.TableName = "(" + sql + ")"; + this.QueryBuilder.Parameters.AddRange(sqlObject.Value); + result.QueryBuilder.JoinQueryInfos.Add(joinInfo); + result.QueryBuilder.LambdaExpressions.ParameterIndex = this.QueryBuilder.LambdaExpressions.ParameterIndex; + return result; + } + public ISugarQueryable InnerJoin(ISugarQueryable joinQueryable, Expression> joinExpression) + { + if (MasterHasWhereFirstJoin()) + { + return this.MergeTable().InnerJoin(joinQueryable, joinExpression); + } + + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + var joinInfo = GetJoinInfo(joinExpression, JoinType.Inner); + var sqlObject = joinQueryable.ToSql(); + string sql = sqlObject.Key; + this.QueryBuilder.LambdaExpressions.ParameterIndex += 100; + UtilMethods.RepairReplicationParameters(ref sql, sqlObject.Value.ToArray(), this.QueryBuilder.LambdaExpressions.ParameterIndex, ""); + joinInfo.TableName = "(" + sql + ")"; + this.QueryBuilder.Parameters.AddRange(sqlObject.Value); + result.QueryBuilder.JoinQueryInfos.Add(joinInfo); + result.QueryBuilder.LambdaExpressions.ParameterIndex = this.QueryBuilder.LambdaExpressions.ParameterIndex; + return result; + } + public ISugarQueryable RightJoin(ISugarQueryable joinQueryable, Expression> joinExpression) + { + + if (MasterHasWhereFirstJoin()) + { + return this.MergeTable().RightJoin(joinQueryable, joinExpression); + } + + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + var joinInfo = GetJoinInfo(joinExpression, JoinType.Right); + var sqlObject = joinQueryable.ToSql(); + string sql = sqlObject.Key; + this.QueryBuilder.LambdaExpressions.ParameterIndex += 100; + UtilMethods.RepairReplicationParameters(ref sql, sqlObject.Value.ToArray(), this.QueryBuilder.LambdaExpressions.ParameterIndex, ""); + joinInfo.TableName = "(" + sql + ")"; + this.QueryBuilder.Parameters.AddRange(sqlObject.Value); + result.QueryBuilder.JoinQueryInfos.Add(joinInfo); + result.QueryBuilder.LambdaExpressions.ParameterIndex = this.QueryBuilder.LambdaExpressions.ParameterIndex; + return result; + } + public ISugarQueryable FullJoin(ISugarQueryable joinQueryable, Expression> joinExpression) + { + + if (MasterHasWhereFirstJoin()) + { + return this.MergeTable().FullJoin(joinQueryable, joinExpression); + } + + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + var joinInfo = GetJoinInfo(joinExpression, JoinType.Full); + var sqlObject = joinQueryable.ToSql(); + string sql = sqlObject.Key; + this.QueryBuilder.LambdaExpressions.ParameterIndex += 100; + UtilMethods.RepairReplicationParameters(ref sql, sqlObject.Value.ToArray(), this.QueryBuilder.LambdaExpressions.ParameterIndex, ""); + joinInfo.TableName = "(" + sql + ")"; + this.QueryBuilder.Parameters.AddRange(sqlObject.Value); + result.QueryBuilder.JoinQueryInfos.Add(joinInfo); + result.QueryBuilder.LambdaExpressions.ParameterIndex = this.QueryBuilder.LambdaExpressions.ParameterIndex; + return result; + } + public ISugarQueryable LeftJoin(Expression> joinExpression, string tableName) + { + var result = LeftJoin(joinExpression); + result.QueryBuilder.JoinQueryInfos.Last().TableName = tableName; + return result; + } + public ISugarQueryable LeftJoinIF(bool isLeftJoin, Expression> joinExpression) + { + var oldAsName = this.QueryBuilder.AsTables?.ToDictionary(it => it.Key, it => it.Value); + var result = LeftJoin(joinExpression); + if (isLeftJoin == false) + { + result.QueryBuilder.JoinQueryInfos.Remove(result.QueryBuilder.JoinQueryInfos.Last()); + result.QueryBuilder.AsTables = oldAsName; + } + return result; + } + public ISugarQueryable InnerJoinIF(bool isJoin, Expression> joinExpression) + { + var oldAsName = this.QueryBuilder.AsTables?.ToDictionary(it => it.Key, it => it.Value); + var result = InnerJoin(joinExpression); + if (isJoin == false) + { + result.QueryBuilder.JoinQueryInfos.Remove(result.QueryBuilder.JoinQueryInfos.Last()); + result.QueryBuilder.AsTables = oldAsName; + } + return result; + } + public ISugarQueryable LeftJoin(Expression> joinExpression) + { + if (MasterHasWhereFirstJoin()) + { + return this.MergeTable().LeftJoin(joinExpression); + } + + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + this.QueryBuilder.IsSqlQuery = false; + result.QueryBuilder.JoinQueryInfos.Add(GetJoinInfo(joinExpression, JoinType.Left)); + return result; + } + public ISugarQueryable FullJoin(Expression> joinExpression) + { + if (MasterHasWhereFirstJoin()) + { + return this.MergeTable().FullJoin(joinExpression); + } + + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + result.QueryBuilder.JoinQueryInfos.Add(GetJoinInfo(joinExpression, JoinType.Full)); + return result; + } + public ISugarQueryable FullJoin(Expression> joinExpression, string tableName) + { + var result = FullJoin(joinExpression); + result.QueryBuilder.JoinQueryInfos.Last().TableName = tableName; + return result; + } + public ISugarQueryable RightJoin(Expression> joinExpression) + { + + if (MasterHasWhereFirstJoin()) + { + return this.MergeTable().RightJoin(joinExpression); + } + + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + result.QueryBuilder.JoinQueryInfos.Add(GetJoinInfo(joinExpression, JoinType.Right)); + return result; + } + public ISugarQueryable RightJoin(Expression> joinExpression, string tableName) + { + var result = RightJoin(joinExpression); + result.QueryBuilder.JoinQueryInfos.Last().TableName = tableName; + return result; + } + public ISugarQueryable InnerJoin(Expression> joinExpression) + { + + if (MasterHasWhereFirstJoin()) + { + return this.MergeTable().InnerJoin(joinExpression); + } + + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + result.QueryBuilder.JoinQueryInfos.Add(GetJoinInfo(joinExpression, JoinType.Inner)); + return result; + } + public ISugarQueryable InnerJoin(Expression> joinExpression, string tableName) + { + var result = InnerJoin(joinExpression); + result.QueryBuilder.JoinQueryInfos.Last().TableName = tableName; + return result; + } + public void Clear() + { + QueryBuilder.Clear(); + } + public ISugarQueryable IgnoreColumns(Expression> columns) + { + var ignoreColumns = QueryBuilder.GetExpressionValue(columns, ResolveExpressType.ArraySingle).GetResultArray().Select(it => this.SqlBuilder.GetNoTranslationColumnName(it).ToLower()).ToList(); + return IgnoreColumns(ignoreColumns.ToArray()); + } + public ISugarQueryable IgnoreColumns(params string[] columns) + { + if (QueryBuilder.IgnoreColumns.IsNullOrEmpty()) + { + QueryBuilder.IgnoreColumns = new List(); + } + QueryBuilder.IgnoreColumns.AddRange(columns); + return this; + } + public void AddQueue() + { + var sqlObj = this.ToSql(); + this.Context.Queues.Add(sqlObj.Key, sqlObj.Value); + } + public ISugarQueryable Clone() + { + var queryable = this.Context.Queryable().AsType(this.QueryBuilder.AsType).Select().WithCacheIF(IsCache, CacheTime); + CopyQueryBuilder(queryable.QueryBuilder); + ((QueryableProvider)queryable).CacheKey = this.CacheKey; + ((QueryableProvider)queryable).MapperAction = this.MapperAction; + ((QueryableProvider)queryable).MapperActionWithCache = this.MapperActionWithCache; + ((QueryableProvider)queryable).Mappers = this.Mappers; + return queryable; + } + public ISugarQueryable Hints(string hints) + { + this.QueryBuilder.Hints = hints; + return this; + } + public virtual ISugarQueryable AS(string tableName) + { + var entityName = typeof(T2).Name; + return _As(tableName, entityName); + } + public ISugarQueryable AS(string tableName) + { + if (tableName == null) return this; + var entityName = typeof(T).Name; + return _As(tableName, entityName); + } + + public ISugarQueryable IF(bool condition, Action> action) + { + if (condition) + action(this); + return this; + } + public ISugarQueryable AsWithAttr() + { + var asName = GetTableName(this.EntityInfo, this.EntityInfo.DbTableName); + this.QueryBuilder.IsCrossQueryWithAttr = true; + return this.AS(asName); + } + public ISugarQueryable Cast() + { + var selectValue = this.Clone().QueryBuilder.GetSelectValue; + return this.Select().Select(selectValue); + } + public ISugarQueryable AsType(Type tableNameType) + { + if (tableNameType == null) + { + return this; + } + this.QueryBuilder.AsType = tableNameType; + return AS(this.Context.EntityMaintenance.GetEntityInfo(tableNameType).DbTableName); + } + public virtual ISugarQueryable With(string withString) + { + if (this.Context.CurrentConnectionConfig.DbType == DbType.SqlServer) + { + QueryBuilder.TableWithString = withString; + } + return this; + } + + public virtual ISugarQueryable Filter(string FilterName, bool isDisabledGobalFilter = false) + { + _Filter(FilterName, isDisabledGobalFilter); + return this; + } + + public ISugarQueryable ClearFilter() + { + this.Filter(null, true); + return this; + } + public ISugarQueryable ClearFilter(params Type[] types) + { + if (types == null || types.Length == 0) + { + return this; + } + this.QueryBuilder.RemoveFilters = types; + this.Filter(null, true); + this.QueryBuilder.IsDisabledGobalFilter = false; + return this; + } + public ISugarQueryable ClearFilter() + { + this.ClearFilter(typeof(FilterType1)); + return this; + } + public ISugarQueryable ClearFilter() + { + this.ClearFilter(typeof(FilterType1), typeof(FilterType2)); + return this; + } + public ISugarQueryable ClearFilter() + { + this.ClearFilter(typeof(FilterType1), typeof(FilterType2), typeof(FilterType3)); + return this; + } + public ISugarQueryable Filter(Type type) + { + if (type == null) + { + return this; + } + this.Context.InitMappingInfo(type); + var whereString = QueryBuilder.GetFilters(type); + if (whereString.HasValue()) + { + this.Where(whereString); + } + UtilMethods.AddDiscrimator(type, this); + return this; + } + + public virtual ISugarQueryable Mapper(Action mapperAction) + { + this.MapperAction = UtilMethods.IsNullReturnNew(this.MapperAction); + this.MapperAction.Add(mapperAction); + return this; + } + public ISugarQueryable Mapper(Expression> expression) + { + var args = ((expression as LambdaExpression).Body as MethodCallExpression).Arguments; + + Type aType = typeof(AType); + Type bType = typeof(BType); + Type bListType = typeof(List); + + this.Context.InitMappingInfo(aType); + this.Context.InitMappingInfo(bType); + this.Context.InitMappingInfo(typeof(MappingType)); + + //Mapping + var mappingEntity = this.Context.EntityMaintenance.GetEntityInfo(typeof(MappingType)); + string m_aPropertyName = (args[0] as MemberExpression).Member.Name; + string m_bPropertyName = (args[1] as MemberExpression).Member.Name; + var m_aDbField = mappingEntity.Columns.First(it => it.PropertyName == m_aPropertyName).DbColumnName; + var m_bDbField = mappingEntity.Columns.First(it => it.PropertyName == m_bPropertyName).DbColumnName; + + //A + var aEntity = this.Context.EntityMaintenance.GetEntityInfo(aType); + var aPropertyName = aEntity.Columns.FirstOrDefault(it => it.IsPrimarykey == true)?.PropertyName; + Check.Exception(aPropertyName == null, aEntity.EntityName + " no primary key"); + + //B + var bEntity = this.Context.EntityMaintenance.GetEntityInfo(bType); + var bProperty = bEntity.Columns.FirstOrDefault(it => it.IsPrimarykey == true)?.PropertyName; + Check.Exception(bProperty == null, bEntity.EntityName + " no primary key"); + var bDbFiled = bEntity.Columns.FirstOrDefault(it => it.IsPrimarykey == true).DbColumnName; + this.Mapper((it, cache) => + { + var list = cache.Get>>(oldList => + { + + + //query mapping by a + var cons = new List() { + new ConditionalModel(){ + ConditionalType=ConditionalType.In, + FieldName= m_aDbField, + FieldValue=string.Join(",",oldList.Select(z=>UtilMethods.GetPropertyValue(z,aPropertyName)).Distinct()) + } + }; + var mappingList = this.Context.Queryable().Where(cons).ToList(); + var bids = mappingList.Select(z => UtilMethods.GetPropertyValue(z, m_bPropertyName)).Distinct().ToList(); + + //queryable b by mapping + cons = new List() { + new ConditionalModel(){ + ConditionalType=ConditionalType.In, + FieldName= bDbFiled, + FieldValue=string.Join(",",mappingList.Select(z=>UtilMethods.GetPropertyValue(z,m_bPropertyName)).Distinct()) + } + }; + List bList = new List(); + if (mappingList.Count != 0) + { + bList = this.Context.Queryable().Where(cons).ToList(); + } + + //get result + Dictionary> result = new Dictionary>(); + var group = mappingList.GroupBy(z => UtilMethods.GetPropertyValue(z, m_aPropertyName)); + foreach (var item in group) + { + var currentBids = item.Select(z => UtilMethods.GetPropertyValue(z, m_bPropertyName)).ToList(); + result.Add(item.Key, bList.Where(z => currentBids.Contains(UtilMethods.GetPropertyValue(z, bProperty))).ToList()); + } + return result; + + }, expression.ToString()); + foreach (var item in aEntity.Columns) + { + var aid = UtilMethods.GetPropertyValue(it, aPropertyName); + if (list.ContainsKey(aid)) + { + if (item.PropertyInfo.PropertyType == bType) + { + var b = UtilMethods.ChangeType(list[aid].FirstOrDefault()); + item.PropertyInfo.SetValue(it, b); + } + else if (item.PropertyInfo.PropertyType == bListType) + { + var bList = UtilMethods.ChangeType>(list[aid]); + item.PropertyInfo.SetValue(it, bList); + } + } + } + }); + return this; + } + + public virtual ISugarQueryable Mapper(Action> mapperAction) + { + this.MapperActionWithCache += mapperAction; + return this; + } + public ISugarQueryable Mapper(Expression> mapperObject, Expression> mainField, Expression> childField) + { + Check.Exception(mapperObject.ReturnType.Name == "IList`1", "Mapper no support IList , Use List"); + if (CallContext.MapperExpression.Value == null) + { + CallContext.MapperExpression.Value = new List(); + } + CallContext.MapperExpression.Value.Add(new MapperExpression() { SqlBuilder = SqlBuilder, QueryBuilder = this.QueryBuilder, Type = MapperExpressionType.oneToOne, FillExpression = mapperObject, MappingField1Expression = mainField, MappingField2Expression = childField, Context = this.Context }); + return _Mapper(mapperObject, mainField, childField); + } + public ISugarQueryable Mapper(Expression>> mapperObject, Expression> mainField, Expression> childField) + { + return _Mapper(mapperObject, mainField, childField); + } + public virtual ISugarQueryable Mapper(Expression>> mapperObject, Expression> mapperField) + { + Check.Exception(mapperObject.ReturnType.Name == "IList`1", "Mapper no support IList , Use List"); + if (CallContext.MapperExpression.Value == null) + { + CallContext.MapperExpression.Value = new List(); + } + CallContext.MapperExpression.Value.Add(new MapperExpression() { SqlBuilder = SqlBuilder, QueryBuilder = this.QueryBuilder, Type = MapperExpressionType.oneToN, FillExpression = mapperObject, MappingField1Expression = mapperField, Context = this.Context }); + return _Mapper(mapperObject, mapperField); + } + public virtual ISugarQueryable Mapper(Expression> mapperObject, Expression> mapperField) + { + if (CallContext.MapperExpression.Value == null) + { + CallContext.MapperExpression.Value = new List(); + } + CallContext.MapperExpression.Value.Add(new MapperExpression() { SqlBuilder = SqlBuilder, QueryBuilder = this.QueryBuilder, Type = MapperExpressionType.oneToOne, FillExpression = mapperObject, MappingField1Expression = mapperField, Context = this.Context }); + return _Mapper(mapperObject, mapperField); + } + public ISugarQueryable Where(string fieldName, string conditionalType, object fieldValue) + { + string parameterName = fieldName.Replace(".", "_") + this.QueryBuilder.WhereIndex; + var whereSql = this.SqlBuilder.GetWhere(fieldName, conditionalType, this.QueryBuilder.WhereIndex); + this.Where(whereSql); + this.QueryBuilder.WhereIndex++; + this.QueryBuilder.Parameters.Add(new SugarParameter(parameterName, fieldValue)); + return this; + } + public virtual ISugarQueryable AddParameters(object parameters) + { + if (parameters != null) + QueryBuilder.Parameters.AddRange(Context.Ado.GetParameters(parameters)); + return this; + } + public virtual ISugarQueryable AddParameters(SugarParameter[] parameters) + { + if (parameters != null) + QueryBuilder.Parameters.AddRange(parameters); + return this; + } + public virtual ISugarQueryable AddParameters(List parameters) + { + if (parameters != null) + QueryBuilder.Parameters.AddRange(parameters); + return this; + } + public virtual ISugarQueryable AddParameters(SugarParameter parameter) + { + if (parameter != null) + QueryBuilder.Parameters.Add(parameter); + return this; + } + public ISugarQueryable AddJoinInfo(Type JoinType, Dictionary keyIsShortName_ValueIsType_Dictionary, FormattableString onExpString, JoinType type = JoinType.Left) + { + var whereExp = DynamicCoreHelper.GetWhere(keyIsShortName_ValueIsType_Dictionary, onExpString); + var name = whereExp.Parameters.Last(it => it.Type == JoinType).Name; + this.Context.InitMappingInfo(JoinType); + var sql = this.QueryBuilder.GetExpressionValue(whereExp, ResolveExpressType.WhereMultiple).GetResultString(); + return AddJoinInfo(JoinType, name, sql, type); + } + public ISugarQueryable AddJoinInfo(Type JoinType, string shortName, string joinWhere, JoinType type = JoinType.Left) + { + this.Context.InitMappingInfo(JoinType); + var tableName = this.Context.EntityMaintenance.GetEntityInfo(JoinType).DbTableName; + QueryBuilder.JoinIndex = +1; + QueryBuilder.JoinQueryInfos + .Add(new JoinQueryInfo() + { + JoinIndex = QueryBuilder.JoinIndex, + TableName = tableName, + ShortName = shortName, + JoinType = type, + JoinWhere = joinWhere, + EntityType = JoinType + }); + return this; + } + public virtual ISugarQueryable AddJoinInfo(string tableName, string shortName, string joinWhere, JoinType type = JoinType.Left) + { + + QueryBuilder.JoinIndex = +1; + QueryBuilder.JoinQueryInfos + .Add(new JoinQueryInfo() + { + JoinIndex = QueryBuilder.JoinIndex, + TableName = tableName, + ShortName = shortName, + JoinType = type, + JoinWhere = joinWhere + }); + return this; + } + + /// + /// if a property that is not empty is a condition + /// + /// + /// + /// + public ISugarQueryable WhereClass(ClassType whereClass, bool ignoreDefaultValue = false) where ClassType : class, new() + { + return WhereClass(new List() { whereClass }, ignoreDefaultValue); + } + public ISugarQueryable WhereClassByPrimaryKey(List list) + { + _WhereClassByPrimaryKey(list); + return this; + } + + public ISugarQueryable WhereClassByWhereColumns(List list, string[] whereColumns) + { + _WhereClassByWhereColumns(list, whereColumns); + return this; + } + public ISugarQueryable WhereClassByPrimaryKey(T data) + { + _WhereClassByPrimaryKey(new List() { data }); + return this; + } + public ISugarQueryable TranLock(DbLockType? LockType = DbLockType.Wait) + { + if (LockType == null) return this; + Check.ExceptionEasy(this.Context.Ado.Transaction == null, "need BeginTran", "需要事务才能使用TranLock"); + Check.ExceptionEasy(this.QueryBuilder.IsSingle() == false, "TranLock, can only be used for single table query", "TranLock只能用在单表查询"); + if (this.Context.CurrentConnectionConfig.DbType == DbType.SqlServer) + { + if (LockType == DbLockType.Wait) + { + this.With("WITH(UpdLock,RowLock)"); + } + else + { + this.With("WITH(UpdLock,RowLock,NoWait)"); + } + } + else + { + this.QueryBuilder.TranLock = (LockType == DbLockType.Error ? " for update nowait" : " for update"); + } + return this; + } + public ISugarQueryable WhereColumns(Dictionary dictionary) + { + return WhereColumns(new List> { dictionary }); + } + public ISugarQueryable WhereColumns(Dictionary columns, bool ignoreDefaultValue) + { + if (ignoreDefaultValue == false || columns == null) + { + return WhereColumns(columns); + } + else + { + var newColumns = new Dictionary(); + foreach (var item in columns) + { + if (!UtilMethods.IsDefaultValue(item.Value)) + { + newColumns.Add(item.Key, item.Value); + } + } + return WhereColumns(newColumns); + } + } + public ISugarQueryable WhereColumns(List> list) + { + List conditionalModels = new List(); + foreach (var model in list) + { + int i = 0; + var clist = new List>(); + foreach (var item in model.Keys) + { + var value = model[item] == null ? "null" : model[item].ObjToString(); + var csType = model[item] == null ? null : model[item].GetType().Name; + if (model[item] is Enum && this.Context?.CurrentConnectionConfig?.MoreSettings?.TableEnumIsString != true) + { + value = Convert.ToInt64(model[item]) + ""; + csType = "Int64"; + } + clist.Add(new KeyValuePair(i == 0 ? WhereType.Or : WhereType.And, new ConditionalModel() + { + FieldName = item, + ConditionalType = ConditionalType.Equal, + FieldValue = value, + CSharpTypeName = csType + })); + i++; + } + conditionalModels.Add(new ConditionalCollections() + { + ConditionalList = clist + }); + } + return this.Where(conditionalModels); + } + + /// + /// if a property that is primary key is a condition + /// + /// + /// + public ISugarQueryable _WhereClassByPrimaryKey(List whereClassTypes) + { + + if (whereClassTypes.HasValue()) + { + var columns = this.Context.EntityMaintenance.GetEntityInfo().Columns.Where(it => it.IsIgnore == false && it.IsPrimarykey == true).ToList(); + Check.Exception(columns == null || columns.Count == 0, "{0} no primary key, Can not use WhereClassByPrimaryKey ", typeof(T).Name); + Check.Exception(this.QueryBuilder.IsSingle() == false, "No support join query"); + List whereModels = new List(); + foreach (var item in whereClassTypes) + { + var cons = new ConditionalCollections(); + foreach (var column in columns) + { + WhereType WhereType = WhereType.And; + var value = column.PropertyInfo.GetValue(item, null); + if (cons.ConditionalList == null) + { + cons.ConditionalList = new List>(); + if (QueryBuilder.WhereInfos.IsNullOrEmpty() && whereModels.IsNullOrEmpty()) + { + + } + else + { + WhereType = WhereType.Or; + } + } + var data = new KeyValuePair(WhereType, new ConditionalModel() + { + ConditionalType = ConditionalType.Equal, + FieldName = this.QueryBuilder.Builder.GetTranslationColumnName(column.DbColumnName), + FieldValue = value.ObjToStringNew(), + CSharpTypeName = column.UnderType.Name + }); + if (value is Enum && this.Context.CurrentConnectionConfig?.MoreSettings?.TableEnumIsString != true) + { + data.Value.FieldValue = Convert.ToInt64(value).ObjToString(); + data.Value.CSharpTypeName = "int"; + } + else if (value != null && column.UnderType == UtilConstants.DateType) + { + data.Value.FieldValue = Convert.ToDateTime(value).ToString("yyyy-MM-dd HH:mm:ss.fff"); + } + //if (this.Context.CurrentConnectionConfig.DbType == DbType.PostgreSQL) + //{ + // data.Value.FieldValueConvertFunc = it => + // { + // return UtilMethods.ChangeType2(it, value.GetType()); + // }; + //} + cons.ConditionalList.Add(data); + } + if (cons.HasValue()) + { + whereModels.Add(cons); + } + } + this.Where(whereModels, true); + } + else + { + this.Where(" 1=2 "); + } + return this; + } + /// + /// if a property that is whereColumns key is a condition + /// + /// + /// + /// + public ISugarQueryable _WhereClassByWhereColumns(List whereClassTypes, string[] whereColumns) + { + + if (whereClassTypes.HasValue()) + { + var columns = this.Context.EntityMaintenance.GetEntityInfo().Columns.Where(it => whereColumns.Any(x => x == it.PropertyName) || whereColumns.Any(x => x.EqualCase(it.DbColumnName))).ToList(); + Check.Exception(columns == null || columns.Count == 0, "{0} no primary key, Can not use whereColumns ", typeof(T).Name); + Check.Exception(this.QueryBuilder.IsSingle() == false, "No support join query"); + List whereModels = new List(); + foreach (var item in whereClassTypes) + { + var cons = new ConditionalCollections(); + foreach (var column in columns) + { + WhereType WhereType = WhereType.And; + var value = column.PropertyInfo.GetValue(item, null); + if (cons.ConditionalList == null) + { + cons.ConditionalList = new List>(); + if (QueryBuilder.WhereInfos.IsNullOrEmpty() && whereModels.IsNullOrEmpty()) + { + + } + else + { + WhereType = WhereType.Or; + } + } + var disableQueryWhereColumnRemoveTrim = this.Context.CurrentConnectionConfig?.MoreSettings?.DisableQueryWhereColumnRemoveTrim == true; + var data = new KeyValuePair(WhereType, new ConditionalModel() + { + ConditionalType = ConditionalType.Equal, + FieldName = this.QueryBuilder.Builder.GetTranslationColumnName(column.DbColumnName), + FieldValue = disableQueryWhereColumnRemoveTrim ? value.ObjToStringNoTrim() : value.ObjToStringNew(), + CSharpTypeName = column.PropertyInfo.PropertyType.Name + }); + if (value == null) + { + data.Value.FieldValue = null; + data.Value.ConditionalType = ConditionalType.EqualNull; + } + if (value is Enum && this.Context.CurrentConnectionConfig?.MoreSettings?.TableEnumIsString != true) + { + data.Value.FieldValue = Convert.ToInt64(value).ObjToString(); + data.Value.CSharpTypeName = "int"; + } + //if (this.Context.CurrentConnectionConfig.DbType == DbType.PostgreSQL) + //{ + // data.Value.FieldValueConvertFunc = it => + // { + // return UtilMethods.ChangeType2(it, value.GetType()); + // }; + //} + cons.ConditionalList.Add(data); + } + if (cons.HasValue()) + { + whereModels.Add(cons); + } + } + this.Where(whereModels, true); + } + else + { + this.Where(" 1=2 "); + } + return this; + } + /// + /// if a property that is not empty is a condition + /// + /// + /// + /// + public ISugarQueryable WhereClass(List whereClassTypes, bool ignoreDefaultValue = false) where ClassType : class, new() + { + + if (whereClassTypes.HasValue()) + { + var columns = this.Context.EntityMaintenance.GetEntityInfo().Columns.Where(it => it.IsIgnore == false).ToList(); + List whereModels = new List(); + foreach (var item in whereClassTypes) + { + var cons = new ConditionalCollections(); + foreach (var column in columns) + { + + var value = column.PropertyInfo.GetValue(item, null); + WhereType WhereType = WhereType.And; + var isNotNull = ignoreDefaultValue == false && value != null; + var isNotNullAndDefault = ignoreDefaultValue && value != null && value.ObjToString() != UtilMethods.DefaultForType(column.PropertyInfo.PropertyType).ObjToString(); + if (isNotNull || isNotNullAndDefault) + { + if (cons.ConditionalList == null) + { + cons.ConditionalList = new List>(); + if (QueryBuilder.WhereInfos.IsNullOrEmpty() && whereModels.IsNullOrEmpty()) + { + + } + else + { + WhereType = WhereType.Or; + } + + } + var data = new KeyValuePair(WhereType, new ConditionalModel() + { + ConditionalType = ConditionalType.Equal, + FieldName = column.DbColumnName, + FieldValue = value.ObjToString(), + CSharpTypeName = column.UnderType.Name + }); + if (value?.GetType().IsEnum() == true) + { + if (this.Context.CurrentConnectionConfig?.MoreSettings?.TableEnumIsString == true) + { + + } + else + { + data.Value.FieldValue = Convert.ToInt64(value).ObjToString(); + } + + } + else if (value != null && column.PropertyInfo.PropertyType == UtilConstants.DateType) + { + data.Value.FieldValue = value.ObjToDate().ToString("yyyy-MM-dd HH:mm:ss.ffffff"); + } + cons.ConditionalList.Add(data); + if (this.Context.CurrentConnectionConfig.DbType == DbType.PostgreSQL) + { + data.Value.FieldValueConvertFunc = it => + { + return UtilMethods.ChangeType2(it, value.GetType()); + }; + } + } + } + if (cons.HasValue()) + { + whereModels.Add(cons); + } + } + this.Where(whereModels); + } + return this; + } + public ISugarQueryable Where(Dictionary keyIsShortName_ValueIsType_Dictionary, FormattableString expressionString) + { + var exp = DynamicCoreHelper.GetWhere(keyIsShortName_ValueIsType_Dictionary, expressionString); + _Where(exp); + return this; + } + public virtual ISugarQueryable Where(string expShortName, FormattableString expressionString) + { + if (expressionString == null && !Regex.IsMatch(expShortName, @"^\w$")) + { + return this.Where(expShortName, new { }); + } + + var exp = DynamicCoreHelper.GetWhere(expShortName, expressionString); + _Where(exp); + return this; + } + public virtual ISugarQueryable Where(Expression> expression) + { + if (IsSingleWithChildTableQuery()) + { + expression = ReplaceMasterTableParameters(expression); + } + this._Where(expression); + return this; + } + + public virtual ISugarQueryable Where(string whereString, object whereObj = null) + { + if (whereString.HasValue()) + this.Where(whereString, whereObj); + return this; + } + public virtual ISugarQueryable Where(IFuncModel funcModel) + { + var obj = this.SqlBuilder.FuncModelToSql(funcModel); + return this.Where(obj.Key, obj.Value); + } + public virtual ISugarQueryable Where(List conditionalModels) + { + if (conditionalModels.IsNullOrEmpty()) return this; + var sqlObj = this.SqlBuilder.ConditionalModelToSql(conditionalModels, 0); + if (sqlObj.Value != null && this.QueryBuilder.Parameters != null) + { + if (sqlObj.Value.Any(it => this.QueryBuilder.Parameters.Any(z => z.ParameterName.EqualCase(it.ParameterName)))) + { + var sql = sqlObj.Key; + this.SqlBuilder.RepairReplicationParameters(ref sql, sqlObj.Value, this.QueryBuilder.Parameters.Count * 10); + return this.Where(sql, sqlObj.Value); + } + } + return this.Where(sqlObj.Key, sqlObj.Value); + } + public ISugarQueryable Where(List conditionalModels, bool isWrap) + { + if (conditionalModels.IsNullOrEmpty()) return this; + var sqlObj = this.SqlBuilder.ConditionalModelToSql(conditionalModels, 0); + if (isWrap) + { + return this.Where("(" + sqlObj.Key + ")", sqlObj.Value); + } + else + { + return this.Where(sqlObj.Key, sqlObj.Value); + } + } + public virtual ISugarQueryable Where(string whereString, object whereObj = null) + { + var whereValue = QueryBuilder.WhereInfos; + whereValue.Add(SqlBuilder.AppendWhereOrAnd(whereValue.Count == 0, whereString + UtilConstants.Space)); + if (whereObj != null) + QueryBuilder.Parameters.AddRange(Context.Ado.GetParameters(whereObj)); + return this; + } + + public virtual ISugarQueryable Having(Expression> expression) + { + this._Having(expression); + return this; + } + public virtual ISugarQueryable HavingIF(bool isHaving, Expression> expression) + { + if (isHaving) + this._Having(expression); + return this; + } + public virtual ISugarQueryable Having(string whereString, object parameters = null) + { + + QueryBuilder.HavingInfos = SqlBuilder.AppendHaving(whereString); + if (parameters != null) + QueryBuilder.Parameters.AddRange(Context.Ado.GetParameters(parameters)); + return this; + } + + public virtual ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (!isWhere) return this; + if (IsSingleWithChildTableQuery()) + { + expression = ReplaceMasterTableParameters(expression); + } + _Where(expression); + return this; + } + public virtual ISugarQueryable WhereIF(bool isWhere, string whereString, object whereObj = null) + { + if (!isWhere) return this; + this.Where(whereString, whereObj); + return this; + } + + public virtual T InSingle(object pkValue) + { + if (pkValue == null) + { + pkValue = -1; + } + Check.Exception(this.QueryBuilder.SelectValue.HasValue(), "'InSingle' and' Select' can't be used together,You can use .Select(it=>...).Single(it.id==1)"); + var list = In(pkValue).ToList(); + if (list == null) return default(T); + else return list.SingleOrDefault(); + } + public ISugarQueryable InIF(bool isIn, string fieldName, params TParamter[] pkValues) + { + if (isIn) + { + return In(fieldName, pkValues); + } + else + { + return this; + } + } + public ISugarQueryable InIF(bool isIn, params TParamter[] pkValues) + { + if (isIn) + { + In(pkValues); + } + return this; + } + public virtual ISugarQueryable In(params TParamter[] pkValues) + { + if (pkValues == null || pkValues.Length == 0) + { + Where(SqlBuilder.SqlFalse); + return this; + } + if (pkValues.Length == 1 && pkValues.First().GetType().FullName.IsCollectionsList() || (pkValues.First() is IEnumerable && pkValues.First().GetType() != UtilConstants.StringType)) + { + var newValues = new List(); + foreach (var item in pkValues.First() as IEnumerable) + { + newValues.Add(item); + } + return In(newValues); + } + var pks = GetPrimaryKeys().Select(it => SqlBuilder.GetTranslationTableName(it)).ToList(); + Check.Exception(pks == null || pks.Count != 1, "Queryable.In(params object[] pkValues): Only one primary key"); + string filed = pks.FirstOrDefault(); + string shortName = QueryBuilder.TableShortName == null ? null : (QueryBuilder.TableShortName + "."); + filed = shortName + filed; + return In(filed, pkValues); + } + public virtual ISugarQueryable In(string filed, params FieldType[] inValues) + { + if (inValues.Length == 1) + { + if (inValues.GetType().IsArray) + { + var whereIndex = QueryBuilder.WhereIndex; + string parameterName = this.SqlBuilder.SqlParameterKeyWord + "InPara" + whereIndex; + this.AddParameters(new SugarParameter(parameterName, inValues[0])); + this.Where(string.Format(QueryBuilder.EqualTemplate, SqlBuilder.GetTranslationColumnName(filed), parameterName)); + QueryBuilder.WhereIndex++; + } + else + { + var values = new List(); + foreach (var item in ((IEnumerable)inValues[0])) + { + if (item != null) + { + values.Add(item.ToString().ToSqlValue()); + } + } + this.Where(string.Format(QueryBuilder.InTemplate, SqlBuilder.GetTranslationColumnName(filed), string.Join(",", values))); + } + } + else + { + var values = new List(); + foreach (var item in inValues) + { + if (item != null) + { + if (UtilMethods.IsNumber(item.GetType().Name)) + { + values.Add(item.ToString()); + } + else + { + values.Add(item.ToString().ToSqlValue()); + } + } + } + this.Where(string.Format(QueryBuilder.InTemplate, SqlBuilder.GetTranslationColumnName(filed), string.Join(",", values))); + + } + return this; + } + public virtual ISugarQueryable In(Expression> expression, params FieldType[] inValues) + { + QueryBuilder.CheckExpression(expression, "In"); + var isSingle = QueryBuilder.IsSingle(); + var lamResult = QueryBuilder.GetExpressionValue(expression, isSingle ? ResolveExpressType.FieldSingle : ResolveExpressType.FieldMultiple); + var fieldName = lamResult.GetResultString(); + var propertyName = ExpressionTool.GetMemberName(expression); + var propertyColumn = this.EntityInfo.Columns.FirstOrDefault(it => it.PropertyName == propertyName); + if (inValues?.Length == 1 && inValues[0]?.GetType()?.FullName?.IsCollectionsList() == true && propertyColumn != null && propertyColumn?.UnderType?.FullName?.IsCollectionsList() != true) + { + return In(fieldName, UtilMethods.ConvertToListOfObjects(inValues[0])); + } + else if (inValues?.Length == 1 && inValues[0]?.GetType()?.IsArray == true && propertyColumn != null && propertyColumn?.UnderType?.IsArray != true) + { + return In(fieldName, UtilMethods.ConvertToListOfObjects(inValues[0])); + } + else + { + return In(fieldName, inValues); + } + } + public virtual ISugarQueryable In(List pkValues) + { + if (pkValues == null || pkValues.Count == 0) + { + Where(SqlBuilder.SqlFalse); + return this; + } + return In(pkValues.ToArray()); + } + public virtual ISugarQueryable In(string InFieldName, List inValues) + { + if (inValues == null || inValues.Count == 0) + { + Where(SqlBuilder.SqlFalse); + return this; + } + return In(InFieldName, inValues.ToArray()); + } + public virtual ISugarQueryable In(Expression> expression, List inValues) + { + if (inValues == null || inValues.Count == 0) + { + Where(SqlBuilder.SqlFalse); + return this; + } + return In(expression, inValues.ToArray()); + } + + public ISugarQueryable InIF(bool isWhere, Expression> expression, ISugarQueryable childQueryExpression) + { + if (isWhere) + { + var sqlObj = childQueryExpression.ToSql(); + _InQueryable(expression, sqlObj); + } + return this; + } + + public virtual ISugarQueryable In(Expression> expression, ISugarQueryable childQueryExpression) + { + var sqlObj = childQueryExpression.ToSql(); + _InQueryable(expression, sqlObj); + return this; + } + public ISugarQueryable SampleBy(int timeNumber, SampleByUnit timeType) + { + SampleByUnit sampleBy = timeType; + if (timeType == SampleByUnit.Month) + { + string sql = "SAMPLE BY " + timeNumber + sampleBy.ToString().Substring(0, 1); + this.QueryBuilder.SampleBy = sql; + } + else if (timeType == SampleByUnit.Millisecond) + { + string sql = "SAMPLE BY " + timeNumber + " T "; + this.QueryBuilder.SampleBy = sql; + } + else if (timeType == SampleByUnit.Microsecond) + { + string sql = "SAMPLE BY " + timeNumber + " U "; + this.QueryBuilder.SampleBy = sql; + } + else + { + string sql = "SAMPLE BY " + timeNumber + sampleBy.ToString().Substring(0, 1).ToLower(); + this.QueryBuilder.SampleBy = sql; + } + return this; + } + public ISugarQueryable SampleBy(int timeNumber, string timeType) + { + string sql = "SAMPLE BY " + timeType; + this.QueryBuilder.SampleBy = sql; + return this; + } + public ISugarQueryable OrderByPropertyNameIF(bool isOrderBy, string orderPropertyName, OrderByType? orderByType = null) + { + if (isOrderBy) + { + return this.OrderByPropertyName(orderPropertyName, orderByType); + } + return this; + } + public ISugarQueryable OrderByPropertyName(string orderPropertyName, OrderByType? orderByType = null) + { + if (orderPropertyName.HasValue()) + { + if (orderPropertyName.Contains(',')) + { + foreach (var item in orderPropertyName.Split(',')) + { + this.OrderByPropertyName(item, orderByType); + } + return this; + } + if (this.QueryBuilder.IsSingle() == false && orderPropertyName.Contains('.')) + { + orderPropertyNameByJoin(orderPropertyName, orderByType); + return this; + } + if (this.Context.EntityMaintenance.GetEntityInfoWithAttr(typeof(T)).Columns.Any(it => + it.DbColumnName?.EqualCase(orderPropertyName) == true + || it.PropertyName?.EqualCase(orderPropertyName) == true)) + { + var name = this.Context.EntityMaintenance.GetEntityInfoWithAttr(typeof(T)).Columns.FirstOrDefault(it => + it.DbColumnName?.EqualCase(orderPropertyName) == true + || it.PropertyName?.EqualCase(orderPropertyName) == true)?.DbColumnName; + return this.OrderBy(this.SqlBuilder.GetTranslationColumnName(name) + " " + orderByType); + } + else + { + Check.ExceptionEasy($"OrderByPropertyName error.{orderPropertyName} does not exist in the entity class", $"OrderByPropertyName出错实体类中不存在{orderPropertyName}"); + } + } + return this; + } + public virtual ISugarQueryable OrderBy(string orderByFields) + { + orderByFields = orderByFields.ToCheckField(); + var orderByValue = QueryBuilder.OrderByValue; + if (QueryBuilder.OrderByValue.IsNullOrEmpty()) + { + QueryBuilder.OrderByValue = QueryBuilder.OrderByTemplate; + } + QueryBuilder.OrderByValue += string.IsNullOrEmpty(orderByValue) ? orderByFields : ("," + orderByFields); + return this; + } + public virtual ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + this._OrderBy(expression, type); + return this; + } + public virtual ISugarQueryable OrderBy(string expShortName, FormattableString expOrderBy, OrderByType type = OrderByType.Asc) + { + var exp = DynamicCoreHelper.GetMember(typeof(T), typeof(object), expShortName, expOrderBy); + this._OrderBy(exp, type); + return this; + } + public virtual ISugarQueryable OrderByDescending(Expression> expression) + { + this._OrderBy(expression, OrderByType.Desc); + return this; + } + public virtual ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public virtual ISugarQueryable GroupByIF(bool isGroupBy, Expression> expression) + { + if (isGroupBy) + { + GroupBy(expression); + } + return this; + } + + public ISugarQueryable GroupByIF(bool isGroupBy, string groupFields) + { + if (isGroupBy) + { + GroupBy(groupFields); + } + + return this; + } + + + public virtual ISugarQueryable OrderByIF(bool isOrderBy, string orderByFields) + { + if (isOrderBy) + return this.OrderBy(orderByFields); + else + return this; + } + public virtual ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + return this.OrderBy(expression, type); + else + return this; + } + + public virtual ISugarQueryable GroupBy(string groupFileds) + { + groupFileds = groupFileds.ToCheckField(); + var croupByValue = QueryBuilder.GroupByValue; + if (QueryBuilder.GroupByValue.IsNullOrEmpty()) + { + QueryBuilder.GroupByValue = QueryBuilder.GroupByTemplate; + } + QueryBuilder.GroupByValue += string.IsNullOrEmpty(croupByValue) ? groupFileds : ("," + groupFileds); + return this; + } + + public virtual ISugarQueryable PartitionBy(Expression> expression) + { + if (QueryBuilder.Take == null) + QueryBuilder.Take = 1; + _PartitionBy(expression); + QueryBuilder.DisableTop = true; + return this; + } + public virtual ISugarQueryable PartitionBy(string groupFileds) + { + var partitionByValue = QueryBuilder.PartitionByValue; + if (QueryBuilder.PartitionByValue.IsNullOrEmpty()) + { + QueryBuilder.PartitionByValue = QueryBuilder.PartitionByTemplate; + } + QueryBuilder.PartitionByValue += string.IsNullOrEmpty(partitionByValue) ? groupFileds : ("," + groupFileds); + return this; + } + + public virtual ISugarQueryable Skip(int num) + { + QueryBuilder.Skip = num; + return this; + } + public virtual ISugarQueryable Take(int num) + { + QueryBuilder.Take = num; + return this; + } + public virtual ISugarQueryable Select(Expression expression) + { + if (IsAppendNavColumns()) + { + SetAppendNavColumns(expression); + } + return _Select(expression); + } + public ISugarQueryable Select(Dictionary keyIsShortName_ValueIsType_Dictionary, FormattableString expSelect, Type resultType) + { + var exp = DynamicCoreHelper.GetMember(keyIsShortName_ValueIsType_Dictionary, resultType, expSelect); + return _Select(exp); + } + public ISugarQueryable Select(string expShortName, FormattableString expSelect, Type resultType) + { + var exp = DynamicCoreHelper.GetMember(typeof(TResult), resultType, expShortName, expSelect); + return _Select(exp); + } + public ISugarQueryable Select(string expShortName, FormattableString expSelect, Type EntityType, Type resultType) + { + var exp = DynamicCoreHelper.GetMember(EntityType, resultType, expShortName, expSelect); + return _Select(exp); + } + public ISugarQueryable Select(string expShortName, FormattableString expSelect, Type resultType) + { + return Select(expShortName, expSelect, resultType); + } + public DynamicCoreSelectModel Select(string expShortName, List columns, params object[] args) + { + DynamicCoreSelectModel dynamicCoreSelectModel = new DynamicCoreSelectModel(); + if (!string.IsNullOrEmpty(this.QueryBuilder.TableShortName) && expShortName != this.QueryBuilder.TableShortName) + { + if (columns.Any(it => it.Contains(expShortName + " "))) + { + var pattern = $@"\b{Regex.Escape(expShortName)}\s*\."; // 匹配 expShortName 后面跟任意空格和点 + var replacement = this.QueryBuilder.TableShortName + "."; + + columns = columns.Select(it => Regex.Replace(it, pattern, replacement)).ToList(); + expShortName = this.QueryBuilder.TableShortName; + } + else + { + columns = columns.Select(it => it.Replace(expShortName + ".", this.QueryBuilder.TableShortName + ".")).ToList(); + expShortName = this.QueryBuilder.TableShortName; + } + } + var selectObj = DynamicCoreHelper.BuildPropertySelector( + expShortName, typeof(T), + columns, + args); + if (IsAppendNavColumns()) + { + SetAppendNavColumns(selectObj.Exp); + } + var exp = selectObj.Exp; + var method = GetType().GetMethod("_Select", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance) + .MakeGenericMethod(selectObj.ResultNewType); + dynamicCoreSelectModel.Value = method.Invoke(this, new object[] { exp }); + return dynamicCoreSelectModel; + } + public virtual ISugarQueryable Select(Expression> expression) + { + if (IsAppendNavColumns()) + { + SetAppendNavColumns(expression); + } + return _Select(expression); + } + public virtual ISugarQueryable SelectIF(bool condition, Expression> trueSelectExpression, Expression> falseSelectExpression) + { + if (condition) + return Select(trueSelectExpression); + else + return Select(falseSelectExpression); + } + public ISugarQueryable Select(Expression> expression, bool isAutoFill) + { + if (typeof(TResult).IsAnonymousType()) + { + return Select(expression); + } + var clone = this.Select(expression).Clone(); + clone.QueryBuilder.IsDistinct = false; + //clone.QueryBuilder.LambdaExpressions.Index = QueryBuilder.LambdaExpressions.Index+1; + var ps = clone.QueryBuilder; + var sql = ps.GetSelectValue; + if (string.IsNullOrEmpty(sql) || sql.Trim() == "*") + { + this.QueryBuilder.SelectValue = null; + return this.Select(); + } + if (sql.StartsWith("*,")) + { + var columns = this.Context.EntityMaintenance.GetEntityInfo() + .Columns.Where(it => typeof(TResult).GetProperties().Any(s => s.Name.EqualCase(it.PropertyName))).Where(it => it.IsIgnore == false).ToList(); + if (columns.Count != 0) + { + sql = string.Join(",", columns.Select(it => $"{SqlBuilder.GetTranslationColumnName(it.DbColumnName)} AS {SqlBuilder.GetTranslationColumnName(it.PropertyName)} ")) + + "," + sql.TrimStart('*').TrimStart(','); + } + } + if (this.QueryBuilder.TableShortName.IsNullOrEmpty()) + { + this.QueryBuilder.TableShortName = clone.QueryBuilder.TableShortName; + } + this.QueryBuilder.Parameters = ps.Parameters; + this.QueryBuilder.SubToListParameters = clone.QueryBuilder.SubToListParameters; + this.QueryBuilder.LambdaExpressions.ParameterIndex = clone.QueryBuilder.LambdaExpressions.ParameterIndex; + var parameters = (expression as LambdaExpression).Parameters; + var columnsResult = this.Context.EntityMaintenance.GetEntityInfo().Columns; + sql = AppendSelect(this.EntityInfo.Columns, sql, parameters, columnsResult, 0); + return this.Select(sql); + } + + public virtual ISugarQueryable Select() + { + var isJoin = this.QueryBuilder.JoinExpression != null; + if (isJoin) + { + var selectValue = new SugarMapper(this.Context).GetSelectValue(this.QueryBuilder); + return this.Select(selectValue); + } + else if (this.QueryBuilder.EntityType == UtilConstants.ObjType || (this.QueryBuilder.AsTables?.Count == 1) || this.QueryBuilder.EntityName != this.QueryBuilder.EntityType.Name) + { + if (typeof(TResult).IsInterface && this.QueryBuilder.AsType == null) + { + Check.ExceptionEasy("Select< interface > requires a full example of AsType(type) db.Queryable().AsType(type).Select().ToList()" + , "Select<接口>需要AsType(type)完整示例db.Queryable().AsType(type).Select().ToList()"); + } + if (this.QueryBuilder.SelectValue.HasValue() && this.QueryBuilder.SelectValue.ObjToString().Contains("AS")) + { + return this.Select(this.QueryBuilder.SelectValue + ""); + } + else + { + if (this.QueryBuilder.IsSingle() && this.EntityInfo?.Type?.GetCustomAttribute() != null && this.QueryBuilder?.SelectValue?.ToString() == "*") + { + var columnAarray = this.Context.EntityMaintenance.GetEntityInfo().Columns; + var sql = string.Empty; + var columns = columnAarray.Where(it => typeof(TResult).GetProperties().Any(s => s.Name.EqualCase(it.PropertyName))).Where(it => it.IsIgnore == false).ToList(); + if (columns.Count != 0) + { + sql = string.Join(",", columns.Select(it => $"{SqlBuilder.GetTranslationColumnName(it.DbColumnName)} AS {SqlBuilder.GetTranslationColumnName(it.PropertyName)} ")); + } + return this.Select(sql); + } + else + { + return this.Select(this.SqlBuilder.SqlSelectAll); + } + } + } + else + { + if (typeof(TResult).IsInterface && typeof(TResult).IsAssignableFrom(this.EntityInfo.Type)) + { + if (this.QueryBuilder.AsTables.Count == 0) + { + this.AsType(this.EntityInfo.Type); + } + } + var selects = this.QueryBuilder.GetSelectValueByString(); + if (selects.ObjToString().ToLower().IsContainsIn(".", "(", " as ")) + { + return this.Select(selects); + } + var resultColumns = this.Context.EntityMaintenance.GetEntityInfo().Columns; + var dbColumns = this.EntityInfo.Columns.Where(it => !it.IsIgnore); + StringBuilder sb = new StringBuilder(); + foreach (var item in resultColumns) + { + var firstColumn = dbColumns.FirstOrDefault(z => + z.PropertyName.EqualCase(item.PropertyName) || + z.DbColumnName.EqualCase(item.PropertyName)); + if (firstColumn != null) + { + var dbColumnName = firstColumn.DbColumnName; + var AsName = item.PropertyName; + sb.Append($"{this.SqlBuilder.GetTranslationColumnName(dbColumnName)} AS {this.SqlBuilder.GetTranslationColumnName(AsName)} ,"); + } + } + selects = sb.ToString().TrimEnd(','); + if (string.IsNullOrEmpty(selects)) + { + selects = "*"; + } + return this.Select(selects); + } + } + + public virtual ISugarQueryable Select(string selectValue) + { + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.Context = this.Context; + result.SqlBuilder = this.SqlBuilder; + result.QueryBuilder.ResultType = this.QueryBuilder.ResultType; + result.IsCache = this.IsCache; + result.CacheTime = this.CacheTime; + QueryBuilder.SelectValue = selectValue; + if (this.IsAs) + { + ((QueryableProvider)result).IsAs = true; + } + return result; + } + public virtual ISugarQueryable Select(string selectValue) + { + QueryBuilder.SelectValue = selectValue; + return this; + } + public virtual ISugarQueryable SelectMergeTable(Expression> expression) + { + return this.Select(expression).MergeTable(); + } + public virtual ISugarQueryable MergeTable() + { + if (IsSubToList()) + { + return MergeTableWithSubToList(); + } + Check.Exception(this.MapperAction != null || this.MapperActionWithCache != null, ErrorMessage.GetThrowMessage("'Mapper’ needs to be written after ‘MergeTable’ ", "Mapper 只能在 MergeTable 之后使用")); + //Check.Exception(this.QueryBuilder.SelectValue.IsNullOrEmpty(),ErrorMessage.GetThrowMessage( "MergeTable need to use Queryable.Select Method .", "使用MergeTable之前必须要有Queryable.Select方法")); + //Check.Exception(this.QueryBuilder.Skip > 0 || this.QueryBuilder.Take > 0 || this.QueryBuilder.OrderByValue.HasValue(),ErrorMessage.GetThrowMessage( "MergeTable Queryable cannot Take Skip OrderBy PageToList ", "使用 MergeTable不能有 Take Skip OrderBy PageToList 等操作,你可以在Mergetable之后操作")); + var sqlobj = this._ToSql(); + if (IsSubToList()) + { + return MergeTableWithSubToListJoin(); + } + var index = QueryBuilder.WhereIndex + 1; + var result = + this.EntityInfo.Discrimator.HasValue() ? + this.Context.Queryable().AS(SqlBuilder.GetPackTable(sqlobj.Key, "MergeTable")).AddParameters(sqlobj.Value).Select("*").With(SqlWith.Null) + : + this.Context.Queryable().AS(SqlBuilder.GetPackTable(sqlobj.Key, "MergeTable")).AddParameters(sqlobj.Value).Select("*").With(SqlWith.Null); + result.QueryBuilder.WhereIndex = index; + result.QueryBuilder.NoCheckInclude = true; + result.QueryBuilder.Includes = this.QueryBuilder.Includes; + result.QueryBuilder.AppendNavInfo = this.QueryBuilder.AppendNavInfo; + result.QueryBuilder.LambdaExpressions.ParameterIndex = QueryBuilder.LambdaExpressions.ParameterIndex++; + result.QueryBuilder.LambdaExpressions.Index = QueryBuilder.LambdaExpressions.Index++; + result.QueryBuilder.IsCrossQueryWithAttr = QueryBuilder.IsCrossQueryWithAttr; + if (this.Context.CurrentConnectionConfig.DbType == DbType.Oracle) + { + result.Select("MergeTable.*"); + } + return result; + } + public ISugarQueryable SplitTable() + { + UtilMethods.StartCustomSplitTable(this.Context, typeof(T)); + //all table + return this.SplitTable(tag => tag); + } + public ISugarQueryable SplitTable(DateTime beginTime, DateTime endTime) + { + UtilMethods.StartCustomSplitTable(this.Context, typeof(T)); + var splitColumn = this.EntityInfo.Columns.FirstOrDefault(it => it.PropertyInfo.GetCustomAttribute() != null); + Check.ExceptionEasy(splitColumn == null, "[SplitFieldAttribute] need to be added to the table field", "需要在分表字段加上属性[SplitFieldAttribute]"); + var columnName = this.SqlBuilder.GetTranslationColumnName(splitColumn.DbColumnName); + var sqlParameterKeyWord = this.SqlBuilder.SqlParameterKeyWord; + var resultQueryable = this.Where($" {columnName}>={sqlParameterKeyWord}spBeginTime AND {columnName}<= {sqlParameterKeyWord}spEndTime", new { spBeginTime = beginTime, spEndTime = endTime }).SplitTable(tas => + { + var result = tas; + var type = this.EntityInfo.Type.GetCustomAttribute(); + Check.ExceptionEasy(type == null, $"{this.EntityInfo.EntityName}need SplitTableAttribute", $"{this.EntityInfo.EntityName}需要特性 SplitTableAttribute"); + if (SplitType.Month == type.SplitType) + { + result = result.Where(y => y.Date >= beginTime.ToString("yyyy-MM").ObjToDate() && y.Date <= endTime.Date.ToString("yyyy-MM").ObjToDate().AddMonths(1).AddDays(-1)).ToList(); + } + else if (SplitType.Year == type.SplitType) + { + result = result.Where(y => y.Date.Year >= beginTime.Year && y.Date.Year <= endTime.Year).ToList(); + } + else if (SplitType.Week == type.SplitType) + { + var begtinWeek = UtilMethods.GetWeekFirstDayMon(beginTime).Date; + var endWeek = UtilMethods.GetWeekLastDaySun(endTime).Date; + result = result.Where(y => + y.Date >= begtinWeek && y.Date <= endWeek).ToList(); + } + else if (SplitType.Month_6 == type.SplitType) + { + var begtinWeek = beginTime.Month <= 6 ? beginTime.ToString("yyyy-01-01") : beginTime.ToString("yyyy-06-01"); + var endWeek = endTime.Month <= 6 ? endTime.ToString("yyyy-07-01") : endTime.ToString("yyyy-12-01"); + result = result.Where(y => + y.Date >= begtinWeek.ObjToDate() && y.Date <= endWeek.ObjToDate().AddMonths(1).AddDays(-1)).ToList(); + } + else if (SplitType.Season == type.SplitType) + { + + var beginSeason = Convert.ToDateTime(beginTime.AddMonths(0 - ((beginTime.Month - 1) % 3)).ToString("yyyy-MM-01")).Date; + var endSeason = DateTime.Parse(endTime.AddMonths(3 - ((endTime.Month - 1) % 3)).ToString("yyyy-MM-01")).AddDays(-1).Date; + result = result.Where(y => + y.Date >= beginSeason && y.Date <= endSeason).ToList(); + } + else + { + result = result.Where(y => y.Date >= beginTime.Date && y.Date <= endTime.Date).ToList(); + } + return result; + }); + if (splitColumn.SqlParameterDbType is System.Data.DbType) + { + foreach (var item in resultQueryable.QueryBuilder.Parameters) + { + if (item.ParameterName.IsContainsIn("spBeginTime", "spEndTime")) + { + item.DbType = (System.Data.DbType)splitColumn.SqlParameterDbType; + } + } + } + return resultQueryable; + } + public ISugarQueryable SplitTable(Func, IEnumerable> getTableNamesFunc) + { + UtilMethods.StartCustomSplitTable(this.Context, typeof(T)); + SplitTableContext helper = new SplitTableContext(Context) + { + EntityInfo = this.EntityInfo + }; + this.Context.MappingTables.Add(this.EntityInfo.EntityName, this.EntityInfo.DbTableName); + var tables = getTableNamesFunc(helper.GetTables()); + List> tableQueryables = new List>(); + foreach (var item in tables) + { + tableQueryables.Add(this.Clone().AS(item.TableName)); + } + if (tableQueryables.Count == 0) + { + var result = this.Context.SqlQueryable("-- No table ").Select(); + result.QueryBuilder.SelectValue = null; + return result; + } + else + { + //if (this.Context.QueryFilter.Any()) + //{ + // foreach (var item in tableQueryables) + // { + // item.QueryBuilder.AppendFilter(); + // } + //} + var unionall = this.Context._UnionAll(tableQueryables.ToArray()); + unionall.QueryBuilder.Includes = this.QueryBuilder.Includes; + unionall.QueryBuilder.EntityType = typeof(T); + unionall.QueryBuilder.IsDisableMasterSlaveSeparation = this.QueryBuilder.IsDisableMasterSlaveSeparation; + unionall.QueryBuilder.IsDisabledGobalFilter = this.QueryBuilder.IsDisabledGobalFilter; + if (unionall.QueryBuilder.Includes?.Count > 0) + { + unionall.QueryBuilder.NoCheckInclude = true; + } + return unionall; + } + //var values= unionall.QueryBuilder.GetSelectValue; + //unionall.QueryBuilder.SelectValue = values; + } + + public ISugarQueryable WithCache(string cacheKey, int cacheDurationInSeconds = int.MaxValue) + { + cacheDurationInSeconds = SetCacheTime(cacheDurationInSeconds); + Check.ArgumentNullException(this.Context.CurrentConnectionConfig.ConfigureExternalServices.DataInfoCacheService, "Use Cache ConnectionConfig.ConfigureExternalServices.DataInfoCacheService is required "); + this.IsCache = true; + this.CacheTime = cacheDurationInSeconds; + this.CacheKey = cacheKey; + return this; + } + public ISugarQueryable WithCache(int cacheDurationInSeconds = int.MaxValue) + { + cacheDurationInSeconds = SetCacheTime(cacheDurationInSeconds); + Check.ArgumentNullException(this.Context.CurrentConnectionConfig.ConfigureExternalServices.DataInfoCacheService, "Use Cache ConnectionConfig.ConfigureExternalServices.DataInfoCacheService is required "); + this.IsCache = true; + this.CacheTime = cacheDurationInSeconds; + return this; + } + public ISugarQueryable WithCacheIF(bool isCache, int cacheDurationInSeconds = int.MaxValue) + { + cacheDurationInSeconds = SetCacheTime(cacheDurationInSeconds); + if (isCache) + { + this.IsCache = true; + this.CacheTime = cacheDurationInSeconds; + } + return this; + } + + public ISugarQueryable Distinct() + { + QueryBuilder.IsDistinct = true; + return this; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/QueryableProvider02-05.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/QueryableProvider02-05.cs new file mode 100644 index 000000000..e8415de52 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/QueryableProvider02-05.cs @@ -0,0 +1,3229 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + #region T2 + public partial class QueryableProvider : QueryableProvider, ISugarQueryable + { + public new ISugarQueryable IF(bool condition, Action> action) + { + throw new Exception("Only Queryable().IF is supported, and Queryable().IF is not supported"); + } + public new ISugarQueryable Hints(string hints) + { + this.QueryBuilder.Hints = hints; + return this; + } + public new ISugarQueryable OrderByPropertyNameIF(bool isOrderBy, string orderPropertyName, OrderByType? orderByType = null) + { + if (isOrderBy) + { + return this.OrderByPropertyName(orderPropertyName, orderByType); + } + return this; + } + public new ISugarQueryable OrderByPropertyName(string orderPropertyName, OrderByType? orderByType = null) + { + base.OrderByPropertyName(orderPropertyName, orderByType); + return this; + } + public new ISugarQueryable SampleBy(int timeNumber, SampleByUnit timeType) + { + base.SampleBy(timeNumber, timeType); + return this; + } + public new ISugarQueryable SampleBy(int timeNumber, string timeType) + { + base.SampleBy(timeNumber, timeType); + return this; + } + public virtual ISugarQueryable SelectMergeTable(Expression> expression) + { + return this.Select(expression).MergeTable(); + } + public ISugarQueryable LeftJoinIF(bool isLeftJoin, Expression> joinExpression) + { + var result = LeftJoin(joinExpression); + if (isLeftJoin == false) + { + result.QueryBuilder.JoinQueryInfos.Remove(result.QueryBuilder.JoinQueryInfos.Last()); + } + return result; + } + public ISugarQueryable InnerJoinIF(bool isJoin, Expression> joinExpression) + { + var result = InnerJoin(joinExpression); + if (isJoin == false) + { + result.QueryBuilder.JoinQueryInfos.Remove(result.QueryBuilder.JoinQueryInfos.Last()); + } + return result; + } + public ISugarQueryable LeftJoin(ISugarQueryable joinQueryable, Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + var joinInfo = GetJoinInfo(joinExpression, JoinType.Left); + var sqlObject = joinQueryable.ToSql(); + string sql = sqlObject.Key; + this.QueryBuilder.LambdaExpressions.ParameterIndex += 100; + UtilMethods.RepairReplicationParameters(ref sql, sqlObject.Value.ToArray(), this.QueryBuilder.LambdaExpressions.ParameterIndex, ""); + joinInfo.TableName = "(" + sql + ")"; + this.QueryBuilder.Parameters.AddRange(sqlObject.Value); + result.QueryBuilder.JoinQueryInfos.Add(joinInfo); + result.QueryBuilder.LambdaExpressions.ParameterIndex = this.QueryBuilder.LambdaExpressions.ParameterIndex; + return result; + } + public ISugarQueryable LeftJoinIF(bool isJoin, ISugarQueryable joinQueryable, Expression> joinExpression) + { + var result = LeftJoin(joinQueryable, joinExpression); + if (isJoin == false) + { + result.QueryBuilder.JoinQueryInfos.Remove(result.QueryBuilder.JoinQueryInfos.Last()); + } + return result; + } + public ISugarQueryable InnerJoinIF(bool isJoin, ISugarQueryable joinQueryable, Expression> joinExpression) + { + var result = InnerJoin(joinQueryable, joinExpression); + if (isJoin == false) + { + result.QueryBuilder.JoinQueryInfos.Remove(result.QueryBuilder.JoinQueryInfos.Last()); + } + return result; + } + public ISugarQueryable InnerJoin(ISugarQueryable joinQueryable, Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + var joinInfo = GetJoinInfo(joinExpression, JoinType.Inner); + var sqlObject = joinQueryable.ToSql(); + string sql = sqlObject.Key; + this.QueryBuilder.LambdaExpressions.ParameterIndex += 100; + UtilMethods.RepairReplicationParameters(ref sql, sqlObject.Value.ToArray(), this.QueryBuilder.LambdaExpressions.ParameterIndex, ""); + joinInfo.TableName = "(" + sql + ")"; + this.QueryBuilder.Parameters.AddRange(sqlObject.Value); + result.QueryBuilder.JoinQueryInfos.Add(joinInfo); + result.QueryBuilder.LambdaExpressions.ParameterIndex = this.QueryBuilder.LambdaExpressions.ParameterIndex; + return result; + } + public ISugarQueryable RightJoin(ISugarQueryable joinQueryable, Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + var joinInfo = GetJoinInfo(joinExpression, JoinType.Right); + var sqlObject = joinQueryable.ToSql(); + string sql = sqlObject.Key; + this.QueryBuilder.LambdaExpressions.ParameterIndex += 100; + UtilMethods.RepairReplicationParameters(ref sql, sqlObject.Value.ToArray(), this.QueryBuilder.LambdaExpressions.ParameterIndex, ""); + joinInfo.TableName = "(" + sql + ")"; + this.QueryBuilder.Parameters.AddRange(sqlObject.Value); + result.QueryBuilder.JoinQueryInfos.Add(joinInfo); + result.QueryBuilder.LambdaExpressions.ParameterIndex = this.QueryBuilder.LambdaExpressions.ParameterIndex; + return result; + } + + public ISugarQueryable FullJoin(ISugarQueryable joinQueryable, Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + var joinInfo = GetJoinInfo(joinExpression, JoinType.Full); + var sqlObject = joinQueryable.ToSql(); + string sql = sqlObject.Key; + this.QueryBuilder.LambdaExpressions.ParameterIndex += 100; + UtilMethods.RepairReplicationParameters(ref sql, sqlObject.Value.ToArray(), this.QueryBuilder.LambdaExpressions.ParameterIndex, ""); + joinInfo.TableName = "(" + sql + ")"; + this.QueryBuilder.Parameters.AddRange(sqlObject.Value); + result.QueryBuilder.JoinQueryInfos.Add(joinInfo); + result.QueryBuilder.LambdaExpressions.ParameterIndex = this.QueryBuilder.LambdaExpressions.ParameterIndex; + return result; + } + public ISugarQueryable LeftJoin(Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + result.QueryBuilder.JoinQueryInfos.Add(GetJoinInfo(joinExpression, JoinType.Left)); + return result; + } + public ISugarQueryable FullJoin(Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + result.QueryBuilder.JoinQueryInfos.Add(GetJoinInfo(joinExpression, JoinType.Full)); + return result; + } + public ISugarQueryable RightJoin(Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + result.QueryBuilder.JoinQueryInfos.Add(GetJoinInfo(joinExpression, JoinType.Right)); + return result; + } + + public ISugarQueryable LeftJoin(Expression> joinExpression, string tableName) + { + var result = LeftJoin(joinExpression); + result.QueryBuilder.JoinQueryInfos.Last().TableName = tableName; + return result; + } + public ISugarQueryable FullJoin(Expression> joinExpression, string tableName) + { + var result = FullJoin(joinExpression); + result.QueryBuilder.JoinQueryInfos.Last().TableName = tableName; + return result; + } + public ISugarQueryable InnerJoin(Expression> joinExpression, string tableName) + { + var result = InnerJoin(joinExpression); + result.QueryBuilder.JoinQueryInfos.Last().TableName = tableName; + return result; + } + public ISugarQueryable RightJoin(Expression> joinExpression, string tableName) + { + var result = RightJoin(joinExpression); + result.QueryBuilder.JoinQueryInfos.Last().TableName = tableName; + return result; + } + + + public ISugarQueryable LeftJoinIF(bool isJoin, Expression> joinExpression, string tableName) + { + var result = LeftJoin(joinExpression); + result.QueryBuilder.JoinQueryInfos.Last().TableName = tableName; + if (isJoin == false) + { + result.QueryBuilder.JoinQueryInfos.Remove(result.QueryBuilder.JoinQueryInfos.Last()); + } + return result; + } + public ISugarQueryable FullJoinIF(bool isJoin, Expression> joinExpression, string tableName) + { + var result = FullJoin(joinExpression); + result.QueryBuilder.JoinQueryInfos.Last().TableName = tableName; + if (isJoin == false) + { + result.QueryBuilder.JoinQueryInfos.Remove(result.QueryBuilder.JoinQueryInfos.Last()); + } + return result; + } + public ISugarQueryable InnerJoinIF(bool isJoin, Expression> joinExpression, string tableName) + { + var result = InnerJoin(joinExpression); + result.QueryBuilder.JoinQueryInfos.Last().TableName = tableName; + if (isJoin == false) + { + result.QueryBuilder.JoinQueryInfos.Remove(result.QueryBuilder.JoinQueryInfos.Last()); + } + return result; + } + public ISugarQueryable RightJoinIF(bool isJoin, Expression> joinExpression, string tableName) + { + var result = RightJoin(joinExpression); + result.QueryBuilder.JoinQueryInfos.Last().TableName = tableName; + if (isJoin == false) + { + result.QueryBuilder.JoinQueryInfos.Remove(result.QueryBuilder.JoinQueryInfos.Last()); + } + return result; + } + + public ISugarQueryable InnerJoin(Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + result.QueryBuilder.JoinQueryInfos.Add(GetJoinInfo(joinExpression, JoinType.Inner)); + return result; + } + #region Where + public new ISugarQueryable Where(List conditionalModels, bool isWrap) + { + base.Where(conditionalModels, isWrap); + return this; + } + public new ISugarQueryable Where(string expShortName, FormattableString expressionString) + { + var exp = DynamicCoreHelper.GetWhere(expShortName, expressionString); + _Where(exp); + return this; + } + public new ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public new ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + public new ISugarQueryable Where(string whereString, object whereObj) + { + Where(whereString, whereObj); + return this; + } + public new ISugarQueryable Where(List conditionalModels) + { + base.Where(conditionalModels); + return this; + } + public new ISugarQueryable Where(IFuncModel funcModel) + { + var obj = this.SqlBuilder.FuncModelToSql(funcModel); + return this.Where(obj.Key, obj.Value); + } + public new ISugarQueryable WhereIF(bool isWhere, string whereString, object whereObj) + { + if (!isWhere) return this; + this.Where(whereString, whereObj); + return this; + } + /// + /// if a property that is not empty is a condition + /// + /// + public new ISugarQueryable WhereClass(ClassType whereClass, bool ignoreDefaultValue = false) where ClassType : class, new() + { + base.WhereClass(whereClass, ignoreDefaultValue); + return this; + } + /// + /// if a property that is not empty is a condition + /// + /// + /// + /// + public new ISugarQueryable WhereClass(List whereClassTypes, bool ignoreDefaultValue = false) where ClassType : class, new() + { + + base.WhereClass(whereClassTypes, ignoreDefaultValue); + return this; + } + #endregion + + #region Select + public ISugarQueryable Select(Expression> expression) + { + if (IsAppendNavColumns()) + { + SetAppendNavColumns(expression); + } + return _Select(expression); + } + public virtual ISugarQueryable SelectIF(bool condition, Expression> trueSelectExpression, Expression> falseSelectExpression) + { + if (condition) + return Select(trueSelectExpression); + else + return Select(falseSelectExpression); + } + public ISugarQueryable Select(Expression> expression, bool isAutoFill) + { + var clone = this.Select(expression).Clone(); + var sql = clone.QueryBuilder.GetSelectValue; + if (this.QueryBuilder.IsSingle() || string.IsNullOrEmpty(sql) || sql.Trim() == "*") + { + sql = " "; + } + this.QueryBuilder.SubToListParameters = clone.QueryBuilder.SubToListParameters; + this.QueryBuilder.Parameters = clone.QueryBuilder.Parameters; + this.QueryBuilder.LambdaExpressions.ParameterIndex = clone.QueryBuilder.LambdaExpressions.ParameterIndex; + var parameters = (expression as LambdaExpression).Parameters; + var columnsResult = this.Context.EntityMaintenance.GetEntityInfo().Columns; + sql = AppendSelect(sql, parameters, columnsResult, 0); + sql = AppendSelect(sql, parameters, columnsResult, 1); + if (sql.Trim().First() == ',') + { + sql = sql.TrimStart(' ').TrimStart(','); + } + return this.Select(sql); + } + #endregion + + #region Order + public new ISugarQueryable OrderBy(List models) + { + base.OrderBy(models); + return this; + } + public new ISugarQueryable OrderBy(string orderByFields) + { + base.OrderBy(orderByFields); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public new virtual ISugarQueryable OrderByDescending(Expression> expression) + { + this._OrderBy(expression, OrderByType.Desc); + return this; + } + public virtual ISugarQueryable OrderByDescending(Expression> expression) + { + this._OrderBy(expression, OrderByType.Desc); + return this; + } + public new ISugarQueryable OrderBy(Expression> expression, OrderByType type) + { + _OrderBy(expression, type); + return this; + } + public new ISugarQueryable OrderByIF(bool isOrderBy, string orderByFields) + { + if (isOrderBy) + base.OrderBy(orderByFields); + return this; + } + public new ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + #endregion + + #region GroupBy + public new ISugarQueryable PartitionBy(Expression> expression) + { + if (QueryBuilder.Take == null) + QueryBuilder.Take = 1; + _PartitionBy(expression); + QueryBuilder.DisableTop = true; + return this; + } + public ISugarQueryable PartitionBy(Expression> expression) + { + if (QueryBuilder.Take == null) + QueryBuilder.Take = 1; + _PartitionBy(expression); + QueryBuilder.DisableTop = true; + return this; + } + public new ISugarQueryable PartitionBy(string groupFileds) + { + base.PartitionBy(groupFileds); + return this; + } + public new virtual ISugarQueryable GroupByIF(bool isGroupBy, Expression> expression) + { + if (isGroupBy) + { + GroupBy(expression); + } + return this; + } + public new virtual ISugarQueryable GroupByIF(bool isGroupBy, string groupFields) + { + if (isGroupBy) + { + GroupBy(groupFields); + } + return this; + } + public virtual ISugarQueryable GroupByIF(bool isGroupBy, Expression> expression) + { + if (isGroupBy) + { + GroupBy(expression); + } + return this; + } + public new virtual ISugarQueryable HavingIF(bool isHaving, Expression> expression) + { + if (isHaving) + this._Having(expression); + return this; + } + public virtual ISugarQueryable HavingIF(bool isHaving, Expression> expression) + { + if (isHaving) + this._Having(expression); + return this; + } + public new ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + + public new ISugarQueryable Having(Expression> expression) + { + this._Having(expression); + return this; + } + + public ISugarQueryable Having(Expression> expression) + { + this._Having(expression); + return this; + } + + public new ISugarQueryable Having(string whereString, object whereObj) + { + base.Having(whereString, whereObj); + return this; + } + #endregion + + #region Aggr + public TResult Max(Expression> expression) + { + return _Max(expression); + } + public TResult Min(Expression> expression) + { + return _Min(expression); + } + public TResult Sum(Expression> expression) + { + return _Sum(expression); + } + public TResult Avg(Expression> expression) + { + return _Avg(expression); + } + public Task MaxAsync(Expression> expression) + { + return _MaxAsync(expression); + } + public Task MinAsync(Expression> expression) + { + return _MinAsync(expression); + } + public Task SumAsync(Expression> expression) + { + return _SumAsync(expression); + } + public Task AvgAsync(Expression> expression) + { + return _AvgAsync(expression); + } + #endregion + + #region In + public new ISugarQueryable InIF(bool isIn, string fieldName, params TParamter[] pkValues) + { + if (isIn) + { + In(fieldName, pkValues); + } + return this; + } + public new ISugarQueryable InIF(bool isIn, params TParamter[] pkValues) + { + if (isIn) + { + In(pkValues); + } + return this; + } + public new ISugarQueryable In(Expression> expression, params FieldType[] inValues) + { + QueryBuilder.CheckExpression(expression, "In"); + var isSingle = QueryBuilder.IsSingle(); + var lamResult = QueryBuilder.GetExpressionValue(expression, isSingle ? ResolveExpressType.FieldSingle : ResolveExpressType.FieldMultiple); + var fieldName = lamResult.GetResultString(); + In(fieldName, inValues); + return this; + } + public new ISugarQueryable In(Expression> expression, List inValues) + { + QueryBuilder.CheckExpression(expression, "In"); + var isSingle = QueryBuilder.IsSingle(); + var lamResult = QueryBuilder.GetExpressionValue(expression, isSingle ? ResolveExpressType.FieldSingle : ResolveExpressType.FieldMultiple); + var fieldName = lamResult.GetResultString(); + In(fieldName, inValues); + return this; + } + public new ISugarQueryable In(Expression> expression, ISugarQueryable childQueryExpression) + { + var sqlObj = childQueryExpression.ToSql(); + _InQueryable(expression, sqlObj); + return this; + } + + public ISugarQueryable In(Expression> expression, params FieldType[] inValues) + { + QueryBuilder.CheckExpression(expression, "In"); + var isSingle = QueryBuilder.IsSingle(); + var lamResult = QueryBuilder.GetExpressionValue(expression, isSingle ? ResolveExpressType.FieldSingle : ResolveExpressType.FieldMultiple); + var fieldName = lamResult.GetResultString(); + In(fieldName, inValues); + return this; + } + public ISugarQueryable In(Expression> expression, List inValues) + { + QueryBuilder.CheckExpression(expression, "In"); + var isSingle = QueryBuilder.IsSingle(); + var lamResult = QueryBuilder.GetExpressionValue(expression, isSingle ? ResolveExpressType.FieldSingle : ResolveExpressType.FieldMultiple); + var fieldName = lamResult.GetResultString(); + In(fieldName, inValues); + return this; + } + public ISugarQueryable In(Expression> expression, ISugarQueryable childQueryExpression) + { + var sqlObj = childQueryExpression.ToSql(); + _InQueryable(expression, sqlObj); + return this; + } + #endregion + + #region Other + public new ISugarQueryable Clone() + { + var queryable = this.Context.Queryable((t, t2) => Array.Empty()).WithCacheIF(IsCache, CacheTime); + base.CopyQueryBuilder(queryable.QueryBuilder); + return queryable; + } + public new ISugarQueryable AS(string tableName) + { + var entityName = typeof(AsT).Name; + _As(tableName, entityName); + return this; + } + public new ISugarQueryable AS(string tableName) + { + var entityName = typeof(T).Name; + _As(tableName, entityName); + return this; + } + public new ISugarQueryable ClearFilter() + { + this.Filter(null, true); + return this; + } + public new ISugarQueryable ClearFilter(params Type[] types) + { + base.ClearFilter(types); + return this; + } + public new ISugarQueryable ClearFilter() + { + this.ClearFilter(typeof(FilterType1)); + return this; + } + public new ISugarQueryable ClearFilter() + { + this.ClearFilter(typeof(FilterType1), typeof(FilterType2)); + return this; + } + public new ISugarQueryable ClearFilter() + { + this.ClearFilter(typeof(FilterType1), typeof(FilterType2), typeof(FilterType3)); + return this; + } + public new ISugarQueryable Filter(string FilterName, bool isDisabledGobalFilter = false) + { + _Filter(FilterName, isDisabledGobalFilter); + return this; + } + public new ISugarQueryable AddParameters(object parameters) + { + if (parameters != null) + QueryBuilder.Parameters.AddRange(Context.Ado.GetParameters(parameters)); + return this; + } + public new ISugarQueryable AddParameters(SugarParameter[] parameters) + { + if (parameters != null) + QueryBuilder.Parameters.AddRange(parameters); + return this; + } + public new ISugarQueryable AddParameters(List parameters) + { + if (parameters != null) + QueryBuilder.Parameters.AddRange(parameters); + return this; + } + public new ISugarQueryable AddJoinInfo(string tableName, string shortName, string joinWhere, JoinType type = JoinType.Left) + { + QueryBuilder.JoinIndex = +1; + QueryBuilder.JoinQueryInfos + .Add(new JoinQueryInfo() + { + JoinIndex = QueryBuilder.JoinIndex, + TableName = tableName, + ShortName = shortName, + JoinType = type, + JoinWhere = joinWhere + }); + return this; + } + public new ISugarQueryable With(string withString) + { + base.With(withString); + return this; + } + public new ISugarQueryable WithCache(int cacheDurationInSeconds = int.MaxValue) + { + cacheDurationInSeconds = SetCacheTime(cacheDurationInSeconds); + this.IsCache = true; + this.CacheTime = cacheDurationInSeconds; + return this; + } + public new ISugarQueryable WithCacheIF(bool isCache, int cacheDurationInSeconds = int.MaxValue) + { + cacheDurationInSeconds = SetCacheTime(cacheDurationInSeconds); + if (isCache) + { + this.IsCache = true; + this.CacheTime = cacheDurationInSeconds; + } + return this; + } + + public bool Any(Expression> expression) + { + _Where(expression); + var result = Any(); + this.QueryBuilder.WhereInfos.Remove(this.QueryBuilder.WhereInfos.Last()); + return result; + } + public new ISugarQueryable Distinct() + { + QueryBuilder.IsDistinct = true; + return this; + } + + public new ISugarQueryable Take(int num) + { + QueryBuilder.Take = num; + return this; + } + #endregion + } + #endregion + #region T3 + public partial class QueryableProvider : QueryableProvider, ISugarQueryable + { + public new ISugarQueryable IF(bool condition, Action> action) + { + throw new Exception("Only Queryable().IF is supported, and Queryable().IF is not supported"); + } + public new ISugarQueryable Hints(string hints) + { + this.QueryBuilder.Hints = hints; + return this; + } + public new ISugarQueryable OrderByPropertyNameIF(bool isOrderBy, string orderPropertyName, OrderByType? orderByType = null) + { + if (isOrderBy) + { + return this.OrderByPropertyName(orderPropertyName, orderByType); + } + return this; + } + public new ISugarQueryable OrderByPropertyName(string orderPropertyName, OrderByType? orderByType = null) + { + base.OrderByPropertyName(orderPropertyName, orderByType); + return this; + } + public new ISugarQueryable SampleBy(int timeNumber, SampleByUnit timeType) + { + base.SampleBy(timeNumber, timeType); + return this; + } + public new ISugarQueryable SampleBy(int timeNumber, string timeType) + { + base.SampleBy(timeNumber, timeType); + return this; + } + public virtual ISugarQueryable SelectMergeTable(Expression> expression) + { + return this.Select(expression).MergeTable(); + } + public ISugarQueryable LeftJoinIF(bool isLeftJoin, Expression> joinExpression) + { + var result = LeftJoin(joinExpression); + if (isLeftJoin == false) + { + result.QueryBuilder.JoinQueryInfos.Remove(result.QueryBuilder.JoinQueryInfos.Last()); + } + return result; + } + public ISugarQueryable InnerJoinIF(bool isJoin, Expression> joinExpression) + { + var result = InnerJoin(joinExpression); + if (isJoin == false) + { + result.QueryBuilder.JoinQueryInfos.Remove(result.QueryBuilder.JoinQueryInfos.Last()); + } + return result; + } + public ISugarQueryable LeftJoin(ISugarQueryable joinQueryable, Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + var joinInfo = GetJoinInfo(joinExpression, JoinType.Left); + var sqlObject = joinQueryable.ToSql(); + string sql = sqlObject.Key; + this.QueryBuilder.LambdaExpressions.ParameterIndex += 100; + UtilMethods.RepairReplicationParameters(ref sql, sqlObject.Value.ToArray(), this.QueryBuilder.LambdaExpressions.ParameterIndex, ""); + joinInfo.TableName = "(" + sql + ")"; + this.QueryBuilder.Parameters.AddRange(sqlObject.Value); + result.QueryBuilder.JoinQueryInfos.Add(joinInfo); + result.QueryBuilder.LambdaExpressions.ParameterIndex = this.QueryBuilder.LambdaExpressions.ParameterIndex; + return result; + } + public ISugarQueryable LeftJoinIF(bool isJoin, ISugarQueryable joinQueryable, Expression> joinExpression) + { + var result = LeftJoin(joinQueryable, joinExpression); + if (isJoin == false) + { + result.QueryBuilder.JoinQueryInfos.Remove(result.QueryBuilder.JoinQueryInfos.Last()); + } + return result; + } + public ISugarQueryable InnerJoinIF(bool isJoin, ISugarQueryable joinQueryable, Expression> joinExpression) + { + var result = InnerJoin(joinQueryable, joinExpression); + if (isJoin == false) + { + result.QueryBuilder.JoinQueryInfos.Remove(result.QueryBuilder.JoinQueryInfos.Last()); + } + return result; + } + public ISugarQueryable InnerJoin(ISugarQueryable joinQueryable, Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + var joinInfo = GetJoinInfo(joinExpression, JoinType.Inner); + var sqlObject = joinQueryable.ToSql(); + string sql = sqlObject.Key; + this.QueryBuilder.LambdaExpressions.ParameterIndex += 100; + UtilMethods.RepairReplicationParameters(ref sql, sqlObject.Value.ToArray(), this.QueryBuilder.LambdaExpressions.ParameterIndex, ""); + joinInfo.TableName = "(" + sql + ")"; + this.QueryBuilder.Parameters.AddRange(sqlObject.Value); + result.QueryBuilder.JoinQueryInfos.Add(joinInfo); + result.QueryBuilder.LambdaExpressions.ParameterIndex = this.QueryBuilder.LambdaExpressions.ParameterIndex; + return result; + } + public ISugarQueryable RightJoin(ISugarQueryable joinQueryable, Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + var joinInfo = GetJoinInfo(joinExpression, JoinType.Right); + var sqlObject = joinQueryable.ToSql(); + string sql = sqlObject.Key; + this.QueryBuilder.LambdaExpressions.ParameterIndex += 100; + UtilMethods.RepairReplicationParameters(ref sql, sqlObject.Value.ToArray(), this.QueryBuilder.LambdaExpressions.ParameterIndex, ""); + joinInfo.TableName = "(" + sql + ")"; + this.QueryBuilder.Parameters.AddRange(sqlObject.Value); + result.QueryBuilder.JoinQueryInfos.Add(joinInfo); + result.QueryBuilder.LambdaExpressions.ParameterIndex = this.QueryBuilder.LambdaExpressions.ParameterIndex; + return result; + } + public ISugarQueryable FullJoin(ISugarQueryable joinQueryable, Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + var joinInfo = GetJoinInfo(joinExpression, JoinType.Full); + var sqlObject = joinQueryable.ToSql(); + string sql = sqlObject.Key; + this.QueryBuilder.LambdaExpressions.ParameterIndex += 100; + UtilMethods.RepairReplicationParameters(ref sql, sqlObject.Value.ToArray(), this.QueryBuilder.LambdaExpressions.ParameterIndex, ""); + joinInfo.TableName = "(" + sql + ")"; + this.QueryBuilder.Parameters.AddRange(sqlObject.Value); + result.QueryBuilder.JoinQueryInfos.Add(joinInfo); + result.QueryBuilder.LambdaExpressions.ParameterIndex = this.QueryBuilder.LambdaExpressions.ParameterIndex; + return result; + } + public ISugarQueryable LeftJoin(Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + result.QueryBuilder.JoinQueryInfos.Add(GetJoinInfo(joinExpression, JoinType.Left)); + return result; + } + public ISugarQueryable FullJoin(Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + result.QueryBuilder.JoinQueryInfos.Add(GetJoinInfo(joinExpression, JoinType.Full)); + return result; + } + public ISugarQueryable RightJoin(Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + result.QueryBuilder.JoinQueryInfos.Add(GetJoinInfo(joinExpression, JoinType.Right)); + return result; + } + + + public ISugarQueryable InnerJoin(Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + result.QueryBuilder.JoinQueryInfos.Add(GetJoinInfo(joinExpression, JoinType.Inner)); + return result; + } + + public ISugarQueryable LeftJoin(Expression> joinExpression, string tableName) + { + var result = LeftJoin(joinExpression); + result.QueryBuilder.JoinQueryInfos.Last().TableName = tableName; + return result; + } + public ISugarQueryable FullJoin(Expression> joinExpression, string tableName) + { + var result = FullJoin(joinExpression); + result.QueryBuilder.JoinQueryInfos.Last().TableName = tableName; + return result; + } + public ISugarQueryable InnerJoin(Expression> joinExpression, string tableName) + { + var result = InnerJoin(joinExpression); + result.QueryBuilder.JoinQueryInfos.Last().TableName = tableName; + return result; + } + public ISugarQueryable RightJoin(Expression> joinExpression, string tableName) + { + var result = RightJoin(joinExpression); + result.QueryBuilder.JoinQueryInfos.Last().TableName = tableName; + return result; + + } + + public ISugarQueryable LeftJoinIF(bool isJoin, Expression> joinExpression, string tableName) + { + var result = LeftJoin(joinExpression); + result.QueryBuilder.JoinQueryInfos.Last().TableName = tableName; + if (isJoin == false) + { + result.QueryBuilder.JoinQueryInfos.Remove(result.QueryBuilder.JoinQueryInfos.Last()); + } + return result; + } + public ISugarQueryable FullJoinIF(bool isJoin, Expression> joinExpression, string tableName) + { + var result = FullJoin(joinExpression); + result.QueryBuilder.JoinQueryInfos.Last().TableName = tableName; + if (isJoin == false) + { + result.QueryBuilder.JoinQueryInfos.Remove(result.QueryBuilder.JoinQueryInfos.Last()); + } + return result; + } + public ISugarQueryable InnerJoinIF(bool isJoin, Expression> joinExpression, string tableName) + { + var result = InnerJoin(joinExpression); + result.QueryBuilder.JoinQueryInfos.Last().TableName = tableName; + if (isJoin == false) + { + result.QueryBuilder.JoinQueryInfos.Remove(result.QueryBuilder.JoinQueryInfos.Last()); + } + return result; + } + public ISugarQueryable RightJoinIF(bool isJoin, Expression> joinExpression, string tableName) + { + var result = RightJoin(joinExpression); + result.QueryBuilder.JoinQueryInfos.Last().TableName = tableName; + if (isJoin == false) + { + result.QueryBuilder.JoinQueryInfos.Remove(result.QueryBuilder.JoinQueryInfos.Last()); + } + return result; + } + + + #region Group + public new ISugarQueryable PartitionBy(Expression> expression) + { + if (QueryBuilder.Take == null) + QueryBuilder.Take = 1; + _PartitionBy(expression); + QueryBuilder.DisableTop = true; + return this; + } + public ISugarQueryable PartitionBy(Expression> expression) + { + if (QueryBuilder.Take == null) + QueryBuilder.Take = 1; + _PartitionBy(expression); + QueryBuilder.DisableTop = true; + return this; + } + public ISugarQueryable PartitionBy(Expression> expression) + { + if (QueryBuilder.Take == null) + QueryBuilder.Take = 1; + _PartitionBy(expression); + QueryBuilder.DisableTop = true; + return this; + } + public new ISugarQueryable PartitionBy(string groupFileds) + { + base.PartitionBy(groupFileds); + return this; + } + public new virtual ISugarQueryable GroupByIF(bool isGroupBy, Expression> expression) + { + if (isGroupBy) + { + GroupBy(expression); + } + return this; + } + public new virtual ISugarQueryable GroupByIF(bool isGroupBy, string groupFields) + { + if (isGroupBy) + { + GroupBy(groupFields); + } + return this; + } + public virtual ISugarQueryable GroupByIF(bool isGroupBy, Expression> expression) + { + if (isGroupBy) + { + GroupBy(expression); + } + return this; + } + public virtual ISugarQueryable GroupByIF(bool isGroupBy, Expression> expression) + { + if (isGroupBy) + { + GroupBy(expression); + } + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public new ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + + public new ISugarQueryable Having(Expression> expression) + { + this._Having(expression); + return this; + } + + public ISugarQueryable Having(Expression> expression) + { + this._Having(expression); + return this; + } + + public ISugarQueryable Having(Expression> expression) + { + this._Having(expression); + return this; + } + + public new ISugarQueryable Having(string whereString, object whereObj) + { + base.Having(whereString, whereObj); + return this; + } + public new virtual ISugarQueryable HavingIF(bool isHaving, Expression> expression) + { + if (isHaving) + this._Having(expression); + return this; + } + public virtual ISugarQueryable HavingIF(bool isHaving, Expression> expression) + { + if (isHaving) + this._Having(expression); + return this; + } + public virtual ISugarQueryable HavingIF(bool isHaving, Expression> expression) + { + if (isHaving) + this._Having(expression); + return this; + } + #endregion + + #region Order + public new ISugarQueryable OrderBy(List models) + { + base.OrderBy(models); + return this; + } + public new virtual ISugarQueryable OrderByDescending(Expression> expression) + { + this._OrderBy(expression, OrderByType.Desc); + return this; + } + public virtual ISugarQueryable OrderByDescending(Expression> expression) + { + this._OrderBy(expression, OrderByType.Desc); + return this; + } + public virtual ISugarQueryable OrderByDescending(Expression> expression) + { + this._OrderBy(expression, OrderByType.Desc); + return this; + } + public new ISugarQueryable OrderBy(string orderByFields) + { + base.OrderBy(orderByFields); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public new ISugarQueryable OrderBy(Expression> expression, OrderByType type) + { + _OrderBy(expression, type); + return this; + } + public new ISugarQueryable OrderByIF(bool isOrderBy, string orderByFields) + { + if (isOrderBy) + base.OrderBy(orderByFields); + return this; + } + public new ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + #endregion + + #region Select + public ISugarQueryable Select(Expression> expression, bool isAutoFill) + { + var clone = this.Select(expression).Clone(); + var sql = clone.QueryBuilder.GetSelectValue; + if (this.QueryBuilder.IsSingle() || string.IsNullOrEmpty(sql) || sql.Trim() == "*") + { + sql = " "; + } + this.QueryBuilder.Parameters = clone.QueryBuilder.Parameters; + this.QueryBuilder.SubToListParameters = clone.QueryBuilder.SubToListParameters; + this.QueryBuilder.LambdaExpressions.ParameterIndex = clone.QueryBuilder.LambdaExpressions.ParameterIndex; + var parameters = (expression as LambdaExpression).Parameters; + var columnsResult = this.Context.EntityMaintenance.GetEntityInfo().Columns; + sql = AppendSelect(sql, parameters, columnsResult, 0); + sql = AppendSelect(sql, parameters, columnsResult, 1); + sql = AppendSelect(sql, parameters, columnsResult, 2); + if (sql.Trim().First() == ',') + { + sql = sql.TrimStart(' ').TrimStart(','); + } + return this.Select(sql); + } + public virtual ISugarQueryable SelectIF(bool condition, Expression> trueSelectExpression, Expression> falseSelectExpression) + { + if (condition) + return Select(trueSelectExpression); + else + return Select(falseSelectExpression); + } + + public ISugarQueryable Select(Expression> expression) + { + if (IsAppendNavColumns()) + { + SetAppendNavColumns(expression); + } + return _Select(expression); + } + + public ISugarQueryable Select(Expression> expression) + { + if (IsAppendNavColumns()) + { + SetAppendNavColumns(expression); + } + return _Select(expression); + } + + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + #endregion + + #region Where + public new ISugarQueryable Where(string expShortName, FormattableString expressionString) + { + var exp = DynamicCoreHelper.GetWhere(expShortName, expressionString); + _Where(exp); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + + public new ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public new ISugarQueryable Where(List conditionalModels) + { + base.Where(conditionalModels); + return this; + } + public new ISugarQueryable Where(List conditionalModels, bool isWrap) + { + base.Where(conditionalModels, isWrap); + return this; + } + public new ISugarQueryable Where(IFuncModel funcModel) + { + var obj = this.SqlBuilder.FuncModelToSql(funcModel); + return this.Where(obj.Key, obj.Value); + } + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public new ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public new ISugarQueryable Where(string whereString, object whereObj) + { + Where(whereString, whereObj); + return this; + } + + public new ISugarQueryable WhereIF(bool isWhere, string whereString, object whereObj) + { + if (!isWhere) return this; + this.Where(whereString, whereObj); + return this; + } + /// + /// if a property that is not empty is a condition + /// + /// + /// + /// + public new ISugarQueryable WhereClass(ClassType whereClass, bool ignoreDefaultValue = false) where ClassType : class, new() + { + base.WhereClass(whereClass, ignoreDefaultValue); + return this; + } + /// + /// if a property that is not empty is a condition + /// + /// + /// + /// + public new ISugarQueryable WhereClass(List whereClassTypes, bool ignoreDefaultValue = false) where ClassType : class, new() + { + + base.WhereClass(whereClassTypes, ignoreDefaultValue); + return this; + } + #endregion + + #region Aggr + public TResult Max(Expression> expression) + { + return _Max(expression); + } + public TResult Min(Expression> expression) + { + return _Min(expression); + } + public TResult Sum(Expression> expression) + { + return _Sum(expression); + } + public TResult Avg(Expression> expression) + { + return _Avg(expression); + } + public Task MaxAsync(Expression> expression) + { + return _MaxAsync(expression); + } + public Task MinAsync(Expression> expression) + { + return _MinAsync(expression); + } + public Task SumAsync(Expression> expression) + { + return _SumAsync(expression); + } + public Task AvgAsync(Expression> expression) + { + return _AvgAsync(expression); + } + #endregion + + #region In + public new ISugarQueryable InIF(bool isIn, string fieldName, params TParamter[] pkValues) + { + if (isIn) + { + In(fieldName, pkValues); + } + return this; + } + public new ISugarQueryable InIF(bool isIn, params TParamter[] pkValues) + { + if (isIn) + { + In(pkValues); + } + return this; + } + public new ISugarQueryable In(Expression> expression, params FieldType[] inValues) + { + QueryBuilder.CheckExpression(expression, "In"); + var isSingle = QueryBuilder.IsSingle(); + var lamResult = QueryBuilder.GetExpressionValue(expression, isSingle ? ResolveExpressType.FieldSingle : ResolveExpressType.FieldMultiple); + var fieldName = lamResult.GetResultString(); + In(fieldName, inValues); + return this; + } + public new ISugarQueryable In(Expression> expression, List inValues) + { + QueryBuilder.CheckExpression(expression, "In"); + var isSingle = QueryBuilder.IsSingle(); + var lamResult = QueryBuilder.GetExpressionValue(expression, isSingle ? ResolveExpressType.FieldSingle : ResolveExpressType.FieldMultiple); + var fieldName = lamResult.GetResultString(); + In(fieldName, inValues); + return this; + } + public new ISugarQueryable In(Expression> expression, ISugarQueryable childQueryExpression) + { + var sqlObj = childQueryExpression.ToSql(); + _InQueryable(expression, sqlObj); + return this; + } + + public ISugarQueryable In(Expression> expression, params FieldType[] inValues) + { + QueryBuilder.CheckExpression(expression, "In"); + var isSingle = QueryBuilder.IsSingle(); + var lamResult = QueryBuilder.GetExpressionValue(expression, isSingle ? ResolveExpressType.FieldSingle : ResolveExpressType.FieldMultiple); + var fieldName = lamResult.GetResultString(); + In(fieldName, inValues); + return this; + } + public ISugarQueryable In(Expression> expression, List inValues) + { + QueryBuilder.CheckExpression(expression, "In"); + var isSingle = QueryBuilder.IsSingle(); + var lamResult = QueryBuilder.GetExpressionValue(expression, isSingle ? ResolveExpressType.FieldSingle : ResolveExpressType.FieldMultiple); + var fieldName = lamResult.GetResultString(); + In(fieldName, inValues); + return this; + } + public ISugarQueryable In(Expression> expression, ISugarQueryable childQueryExpression) + { + var sqlObj = childQueryExpression.ToSql(); + _InQueryable(expression, sqlObj); + return this; + } + + public ISugarQueryable In(Expression> expression, params FieldType[] inValues) + { + QueryBuilder.CheckExpression(expression, "In"); + var isSingle = QueryBuilder.IsSingle(); + var lamResult = QueryBuilder.GetExpressionValue(expression, isSingle ? ResolveExpressType.FieldSingle : ResolveExpressType.FieldMultiple); + var fieldName = lamResult.GetResultString(); + In(fieldName, inValues); + return this; + } + public ISugarQueryable In(Expression> expression, List inValues) + { + QueryBuilder.CheckExpression(expression, "In"); + var isSingle = QueryBuilder.IsSingle(); + var lamResult = QueryBuilder.GetExpressionValue(expression, isSingle ? ResolveExpressType.FieldSingle : ResolveExpressType.FieldMultiple); + var fieldName = lamResult.GetResultString(); + In(fieldName, inValues); + return this; + } + public ISugarQueryable In(Expression> expression, ISugarQueryable childQueryExpression) + { + var sqlObj = childQueryExpression.ToSql(); + _InQueryable(expression, sqlObj); + return this; + } + #endregion + + #region Other + public new ISugarQueryable Take(int num) + { + QueryBuilder.Take = num; + return this; + } + public new ISugarQueryable Clone() + { + var queryable = this.Context.Queryable((t, t2, t3) => Array.Empty()).WithCacheIF(IsCache, CacheTime); + base.CopyQueryBuilder(queryable.QueryBuilder); + return queryable; + } + public new ISugarQueryable AS(string tableName) + { + var entityName = typeof(AsT).Name; + _As(tableName, entityName); + return this; + } + public new ISugarQueryable AS(string tableName) + { + var entityName = typeof(T).Name; + _As(tableName, entityName); + return this; + } + public new ISugarQueryable ClearFilter() + { + this.Filter(null, true); + return this; + } + public new ISugarQueryable Filter(string FilterName, bool isDisabledGobalFilter = false) + { + _Filter(FilterName, isDisabledGobalFilter); + return this; + } + public new ISugarQueryable ClearFilter(params Type[] types) + { + base.ClearFilter(types); + return this; + } + public new ISugarQueryable ClearFilter() + { + this.ClearFilter(typeof(FilterType1)); + return this; + } + public new ISugarQueryable ClearFilter() + { + this.ClearFilter(typeof(FilterType1), typeof(FilterType2)); + return this; + } + public new ISugarQueryable ClearFilter() + { + this.ClearFilter(typeof(FilterType1), typeof(FilterType2), typeof(FilterType3)); + return this; + } + public new ISugarQueryable AddParameters(object parameters) + { + if (parameters != null) + QueryBuilder.Parameters.AddRange(Context.Ado.GetParameters(parameters)); + return this; + } + public new ISugarQueryable AddParameters(SugarParameter[] parameters) + { + if (parameters != null) + QueryBuilder.Parameters.AddRange(parameters); + return this; + } + public new ISugarQueryable AddParameters(List parameters) + { + if (parameters != null) + QueryBuilder.Parameters.AddRange(parameters); + return this; + } + public new ISugarQueryable AddJoinInfo(string tableName, string shortName, string joinWhere, JoinType type = JoinType.Left) + { + QueryBuilder.JoinIndex = +1; + QueryBuilder.JoinQueryInfos + .Add(new JoinQueryInfo() + { + JoinIndex = QueryBuilder.JoinIndex, + TableName = tableName, + ShortName = shortName, + JoinType = type, + JoinWhere = joinWhere + }); + return this; + } + public new ISugarQueryable With(string withString) + { + base.With(withString); + return this; + } + public new ISugarQueryable WithCache(int cacheDurationInSeconds = int.MaxValue) + { + cacheDurationInSeconds = SetCacheTime(cacheDurationInSeconds); + this.IsCache = true; + this.CacheTime = cacheDurationInSeconds; + return this; + } + public new ISugarQueryable WithCacheIF(bool isCache, int cacheDurationInSeconds = int.MaxValue) + { + cacheDurationInSeconds = SetCacheTime(cacheDurationInSeconds); + if (IsCache) + { + this.IsCache = true; + this.CacheTime = cacheDurationInSeconds; + } + return this; + } + + public bool Any(Expression> expression) + { + _Where(expression); + var result = Any(); + this.QueryBuilder.WhereInfos.Remove(this.QueryBuilder.WhereInfos.Last()); + return result; + } + public new ISugarQueryable Distinct() + { + QueryBuilder.IsDistinct = true; + return this; + } + #endregion + } + #endregion + #region T4 + public partial class QueryableProvider : QueryableProvider, ISugarQueryable + { + public new ISugarQueryable IF(bool condition, Action> action) + { + throw new Exception("Only Queryable().IF is supported, and Queryable().IF is not supported"); + } + public new ISugarQueryable Hints(string hints) + { + this.QueryBuilder.Hints = hints; + return this; + } + public new ISugarQueryable OrderByPropertyNameIF(bool isOrderBy, string orderPropertyName, OrderByType? orderByType = null) + { + if (isOrderBy) + { + return this.OrderByPropertyName(orderPropertyName, orderByType); + } + return this; + } + public new ISugarQueryable OrderByPropertyName(string orderPropertyName, OrderByType? orderByType = null) + { + base.OrderByPropertyName(orderPropertyName, orderByType); + return this; + } + public new ISugarQueryable SampleBy(int timeNumber, SampleByUnit timeType) + { + base.SampleBy(timeNumber, timeType); + return this; + } + public new ISugarQueryable SampleBy(int timeNumber, string timeType) + { + base.SampleBy(timeNumber, timeType); + return this; + } + public virtual ISugarQueryable SelectMergeTable(Expression> expression) + { + return this.Select(expression).MergeTable(); + } + public ISugarQueryable LeftJoinIF(bool isLeftJoin, Expression> joinExpression) + { + var result = LeftJoin(joinExpression); + if (isLeftJoin == false) + { + result.QueryBuilder.JoinQueryInfos.Remove(result.QueryBuilder.JoinQueryInfos.Last()); + } + return result; + } + public ISugarQueryable InnerJoinIF(bool isJoin, Expression> joinExpression) + { + var result = InnerJoin(joinExpression); + if (isJoin == false) + { + result.QueryBuilder.JoinQueryInfos.Remove(result.QueryBuilder.JoinQueryInfos.Last()); + } + return result; + } + public ISugarQueryable LeftJoin(ISugarQueryable joinQueryable, Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + var joinInfo = GetJoinInfo(joinExpression, JoinType.Left); + var sqlObject = joinQueryable.ToSql(); + string sql = sqlObject.Key; + this.QueryBuilder.LambdaExpressions.ParameterIndex += 100; + UtilMethods.RepairReplicationParameters(ref sql, sqlObject.Value.ToArray(), this.QueryBuilder.LambdaExpressions.ParameterIndex, ""); + joinInfo.TableName = "(" + sql + ")"; + this.QueryBuilder.Parameters.AddRange(sqlObject.Value); + result.QueryBuilder.JoinQueryInfos.Add(joinInfo); + result.QueryBuilder.LambdaExpressions.ParameterIndex = this.QueryBuilder.LambdaExpressions.ParameterIndex; + return result; + } + public ISugarQueryable LeftJoinIF(bool isJoin, ISugarQueryable joinQueryable, Expression> joinExpression) + { + var result = LeftJoin(joinQueryable, joinExpression); + if (isJoin == false) + { + result.QueryBuilder.JoinQueryInfos.Remove(result.QueryBuilder.JoinQueryInfos.Last()); + } + return result; + } + public ISugarQueryable InnerJoinIF(bool isJoin, ISugarQueryable joinQueryable, Expression> joinExpression) + { + var result = InnerJoin(joinQueryable, joinExpression); + if (isJoin == false) + { + result.QueryBuilder.JoinQueryInfos.Remove(result.QueryBuilder.JoinQueryInfos.Last()); + } + return result; + } + public ISugarQueryable InnerJoin(ISugarQueryable joinQueryable, Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + var joinInfo = GetJoinInfo(joinExpression, JoinType.Inner); + var sqlObject = joinQueryable.ToSql(); + string sql = sqlObject.Key; + this.QueryBuilder.LambdaExpressions.ParameterIndex += 100; + UtilMethods.RepairReplicationParameters(ref sql, sqlObject.Value.ToArray(), this.QueryBuilder.LambdaExpressions.ParameterIndex, ""); + joinInfo.TableName = "(" + sql + ")"; + this.QueryBuilder.Parameters.AddRange(sqlObject.Value); + result.QueryBuilder.JoinQueryInfos.Add(joinInfo); + result.QueryBuilder.LambdaExpressions.ParameterIndex = this.QueryBuilder.LambdaExpressions.ParameterIndex; + return result; + } + public ISugarQueryable RightJoin(ISugarQueryable joinQueryable, Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + var joinInfo = GetJoinInfo(joinExpression, JoinType.Right); + var sqlObject = joinQueryable.ToSql(); + string sql = sqlObject.Key; + this.QueryBuilder.LambdaExpressions.ParameterIndex += 100; + UtilMethods.RepairReplicationParameters(ref sql, sqlObject.Value.ToArray(), this.QueryBuilder.LambdaExpressions.ParameterIndex, ""); + joinInfo.TableName = "(" + sql + ")"; + this.QueryBuilder.Parameters.AddRange(sqlObject.Value); + result.QueryBuilder.JoinQueryInfos.Add(joinInfo); + result.QueryBuilder.LambdaExpressions.ParameterIndex = this.QueryBuilder.LambdaExpressions.ParameterIndex; + return result; + } + public ISugarQueryable LeftJoin(Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + result.QueryBuilder.JoinQueryInfos.Add(GetJoinInfo(joinExpression, JoinType.Left)); + return result; + } + public ISugarQueryable FullJoin(Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + result.QueryBuilder.JoinQueryInfos.Add(GetJoinInfo(joinExpression, JoinType.Full)); + return result; + } + public ISugarQueryable RightJoin(Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + result.QueryBuilder.JoinQueryInfos.Add(GetJoinInfo(joinExpression, JoinType.Right)); + return result; + } + + public ISugarQueryable InnerJoin(Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + result.QueryBuilder.JoinQueryInfos.Add(GetJoinInfo(joinExpression, JoinType.Inner)); + return result; + } + + public ISugarQueryable LeftJoin(Expression> joinExpression, string tableName) + { + var result = LeftJoin(joinExpression); + result.QueryBuilder.JoinQueryInfos.Last().TableName = tableName; + return result; + } + public ISugarQueryable FullJoin(Expression> joinExpression, string tableName) + { + var result = FullJoin(joinExpression); + result.QueryBuilder.JoinQueryInfos.Last().TableName = tableName; + return result; + } + public ISugarQueryable InnerJoin(Expression> joinExpression, string tableName) + { + var result = InnerJoin(joinExpression); + result.QueryBuilder.JoinQueryInfos.Last().TableName = tableName; + return result; + } + public ISugarQueryable RightJoin(Expression> joinExpression, string tableName) + { + var result = RightJoin(joinExpression); + result.QueryBuilder.JoinQueryInfos.Last().TableName = tableName; + return result; + } + public ISugarQueryable LeftJoinIF(bool isJoin, Expression> joinExpression, string tableName) + { + var result = LeftJoin(joinExpression); + result.QueryBuilder.JoinQueryInfos.Last().TableName = tableName; + if (isJoin == false) + { + result.QueryBuilder.JoinQueryInfos.Remove(result.QueryBuilder.JoinQueryInfos.Last()); + } + return result; + } + public ISugarQueryable FullJoinIF(bool isJoin, Expression> joinExpression, string tableName) + { + var result = FullJoin(joinExpression); + result.QueryBuilder.JoinQueryInfos.Last().TableName = tableName; + if (isJoin == false) + { + result.QueryBuilder.JoinQueryInfos.Remove(result.QueryBuilder.JoinQueryInfos.Last()); + } + return result; + } + public ISugarQueryable InnerJoinIF(bool isJoin, Expression> joinExpression, string tableName) + { + var result = InnerJoin(joinExpression); + result.QueryBuilder.JoinQueryInfos.Last().TableName = tableName; + if (isJoin == false) + { + result.QueryBuilder.JoinQueryInfos.Remove(result.QueryBuilder.JoinQueryInfos.Last()); + } + return result; + } + public ISugarQueryable RightJoinIF(bool isJoin, Expression> joinExpression, string tableName) + { + var result = RightJoin(joinExpression); + result.QueryBuilder.JoinQueryInfos.Last().TableName = tableName; + if (isJoin == false) + { + result.QueryBuilder.JoinQueryInfos.Remove(result.QueryBuilder.JoinQueryInfos.Last()); + } + return result; + } + + #region Where + public new ISugarQueryable Where(string expShortName, FormattableString expressionString) + { + var exp = DynamicCoreHelper.GetWhere(expShortName, expressionString); + _Where(exp); + return this; + } + public new ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public new ISugarQueryable Where(List conditionalModels) + { + base.Where(conditionalModels); + return this; + } + public new ISugarQueryable Where(List conditionalModels, bool isWrap) + { + base.Where(conditionalModels, isWrap); + return this; + } + public new ISugarQueryable Where(IFuncModel funcModel) + { + var obj = this.SqlBuilder.FuncModelToSql(funcModel); + return this.Where(obj.Key, obj.Value); + } + public new ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public new ISugarQueryable Where(string whereString, object whereObj) + { + Where(whereString, whereObj); + return this; + } + + public new ISugarQueryable WhereIF(bool isWhere, string whereString, object whereObj) + { + if (!isWhere) return this; + this.Where(whereString, whereObj); + return this; + } + /// + /// if a property that is not empty is a condition + /// + /// + /// + /// + public new ISugarQueryable WhereClass(ClassType whereClass, bool ignoreDefaultValue = false) where ClassType : class, new() + { + base.WhereClass(whereClass, ignoreDefaultValue); + return this; + } + /// + /// if a property that is not empty is a condition + /// + /// + /// + /// + public new ISugarQueryable WhereClass(List whereClassTypes, bool ignoreDefaultValue = false) where ClassType : class, new() + { + + base.WhereClass(whereClassTypes, ignoreDefaultValue); + return this; + } + #endregion + + #region Select + public ISugarQueryable Select(Expression> expression, bool isAutoFill) + { + var clone = this.Select(expression).Clone(); + var sql = clone.QueryBuilder.GetSelectValue; + if (this.QueryBuilder.IsSingle() || string.IsNullOrEmpty(sql) || sql.Trim() == "*") + { + sql = " "; + } + this.QueryBuilder.Parameters = clone.QueryBuilder.Parameters; + this.QueryBuilder.SubToListParameters = clone.QueryBuilder.SubToListParameters; + this.QueryBuilder.LambdaExpressions.ParameterIndex = clone.QueryBuilder.LambdaExpressions.ParameterIndex; + var parameters = (expression as LambdaExpression).Parameters; + var columnsResult = this.Context.EntityMaintenance.GetEntityInfo().Columns; + sql = AppendSelect(sql, parameters, columnsResult, 0); + sql = AppendSelect(sql, parameters, columnsResult, 1); + sql = AppendSelect(sql, parameters, columnsResult, 2); + sql = AppendSelect(sql, parameters, columnsResult, 3); + if (sql.Trim().First() == ',') + { + sql = sql.TrimStart(' ').TrimStart(','); + } + return this.Select(sql); + } + + public ISugarQueryable Select(Expression> expression) + { + if (IsAppendNavColumns()) + { + SetAppendNavColumns(expression); + } + return _Select(expression); + } + public ISugarQueryable Select(Expression> expression) + { + if (IsAppendNavColumns()) + { + SetAppendNavColumns(expression); + } + return _Select(expression); + } + + public virtual ISugarQueryable SelectIF(bool condition, Expression> trueSelectExpression, Expression> falseSelectExpression) + { + if (condition) + return Select(trueSelectExpression); + else + return Select(falseSelectExpression); + } + public ISugarQueryable Select(Expression> expression) + { + if (IsAppendNavColumns()) + { + SetAppendNavColumns(expression); + } + return _Select(expression); + } + #endregion + + #region OrderBy + public new ISugarQueryable OrderBy(List models) + { + base.OrderBy(models); + return this; + } + public new virtual ISugarQueryable OrderByDescending(Expression> expression) + { + this._OrderBy(expression, OrderByType.Desc); + return this; + } + public virtual ISugarQueryable OrderByDescending(Expression> expression) + { + this._OrderBy(expression, OrderByType.Desc); + return this; + } + public virtual ISugarQueryable OrderByDescending(Expression> expression) + { + this._OrderBy(expression, OrderByType.Desc); + return this; + } + public virtual ISugarQueryable OrderByDescending(Expression> expression) + { + this._OrderBy(expression, OrderByType.Desc); + return this; + } + public new ISugarQueryable OrderBy(string orderByFields) + { + base.OrderBy(orderByFields); + return this; + } + public new ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public new ISugarQueryable OrderByIF(bool isOrderBy, string orderByFields) + { + if (isOrderBy) + base.OrderBy(orderByFields); + return this; + } + public new ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + #endregion + + #region GroupBy + public new virtual ISugarQueryable GroupByIF(bool isGroupBy, Expression> expression) + { + if (isGroupBy) + { + GroupBy(expression); + } + return this; + } + public virtual ISugarQueryable GroupByIF(bool isGroupBy, Expression> expression) + { + if (isGroupBy) + { + GroupBy(expression); + } + return this; + } + public virtual ISugarQueryable GroupByIF(bool isGroupBy, Expression> expression) + { + if (isGroupBy) + { + GroupBy(expression); + } + return this; + } + public new virtual ISugarQueryable GroupByIF(bool isGroupBy, string groupFields) + { + if (isGroupBy) + { + GroupBy(groupFields); + } + return this; + } + public virtual ISugarQueryable GroupByIF(bool isGroupBy, Expression> expression) + { + if (isGroupBy) + { + GroupBy(expression); + } + return this; + } + public new ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public new ISugarQueryable Having(Expression> expression) + { + this._Having(expression); + return this; + } + + public ISugarQueryable Having(Expression> expression) + { + this._Having(expression); + return this; + } + + public ISugarQueryable Having(Expression> expression) + { + this._Having(expression); + return this; + } + + public ISugarQueryable Having(Expression> expression) + { + this._Having(expression); + return this; + } + + public new ISugarQueryable Having(string whereString, object whereObj) + { + base.Having(whereString, whereObj); + return this; + } + + public new virtual ISugarQueryable HavingIF(bool isHaving, Expression> expression) + { + if (isHaving) + this._Having(expression); + return this; + } + public virtual ISugarQueryable HavingIF(bool isHaving, Expression> expression) + { + if (isHaving) + this._Having(expression); + return this; + } + public virtual ISugarQueryable HavingIF(bool isHaving, Expression> expression) + { + if (isHaving) + this._Having(expression); + return this; + } + public virtual ISugarQueryable HavingIF(bool isHaving, Expression> expression) + { + if (isHaving) + this._Having(expression); + return this; + } + #endregion + + #region Aggr + public TResult Max(Expression> expression) + { + return _Max(expression); + } + public TResult Min(Expression> expression) + { + return _Min(expression); + } + public TResult Sum(Expression> expression) + { + return _Sum(expression); + } + public TResult Avg(Expression> expression) + { + return _Avg(expression); + } + #endregion + + #region In + public new ISugarQueryable InIF(bool isIn, string fieldName, params TParamter[] pkValues) + { + if (isIn) + { + In(fieldName, pkValues); + } + return this; + } + public new ISugarQueryable InIF(bool isIn, params TParamter[] pkValues) + { + if (isIn) + { + In(pkValues); + } + return this; + } + public new ISugarQueryable In(Expression> expression, params FieldType[] inValues) + { + QueryBuilder.CheckExpression(expression, "In"); + var isSingle = QueryBuilder.IsSingle(); + var lamResult = QueryBuilder.GetExpressionValue(expression, isSingle ? ResolveExpressType.FieldSingle : ResolveExpressType.FieldMultiple); + var fieldName = lamResult.GetResultString(); + In(fieldName, inValues); + return this; + } + public new ISugarQueryable In(Expression> expression, List inValues) + { + QueryBuilder.CheckExpression(expression, "In"); + var isSingle = QueryBuilder.IsSingle(); + var lamResult = QueryBuilder.GetExpressionValue(expression, isSingle ? ResolveExpressType.FieldSingle : ResolveExpressType.FieldMultiple); + var fieldName = lamResult.GetResultString(); + In(fieldName, inValues); + return this; + } + public new ISugarQueryable In(Expression> expression, ISugarQueryable childQueryExpression) + { + var sqlObj = childQueryExpression.ToSql(); + _InQueryable(expression, sqlObj); + return this; + } + + public ISugarQueryable In(Expression> expression, params FieldType[] inValues) + { + QueryBuilder.CheckExpression(expression, "In"); + var isSingle = QueryBuilder.IsSingle(); + var lamResult = QueryBuilder.GetExpressionValue(expression, isSingle ? ResolveExpressType.FieldSingle : ResolveExpressType.FieldMultiple); + var fieldName = lamResult.GetResultString(); + In(fieldName, inValues); + return this; + } + public ISugarQueryable In(Expression> expression, List inValues) + { + QueryBuilder.CheckExpression(expression, "In"); + var isSingle = QueryBuilder.IsSingle(); + var lamResult = QueryBuilder.GetExpressionValue(expression, isSingle ? ResolveExpressType.FieldSingle : ResolveExpressType.FieldMultiple); + var fieldName = lamResult.GetResultString(); + In(fieldName, inValues); + return this; + } + public ISugarQueryable In(Expression> expression, ISugarQueryable childQueryExpression) + { + var sqlObj = childQueryExpression.ToSql(); + _InQueryable(expression, sqlObj); + return this; + } + + public ISugarQueryable In(Expression> expression, params FieldType[] inValues) + { + QueryBuilder.CheckExpression(expression, "In"); + var isSingle = QueryBuilder.IsSingle(); + var lamResult = QueryBuilder.GetExpressionValue(expression, isSingle ? ResolveExpressType.FieldSingle : ResolveExpressType.FieldMultiple); + var fieldName = lamResult.GetResultString(); + In(fieldName, inValues); + return this; + } + public ISugarQueryable In(Expression> expression, List inValues) + { + QueryBuilder.CheckExpression(expression, "In"); + var isSingle = QueryBuilder.IsSingle(); + var lamResult = QueryBuilder.GetExpressionValue(expression, isSingle ? ResolveExpressType.FieldSingle : ResolveExpressType.FieldMultiple); + var fieldName = lamResult.GetResultString(); + In(fieldName, inValues); + return this; + } + public ISugarQueryable In(Expression> expression, ISugarQueryable childQueryExpression) + { + var sqlObj = childQueryExpression.ToSql(); + _InQueryable(expression, sqlObj); + return this; + } + + public ISugarQueryable In(Expression> expression, params FieldType[] inValues) + { + QueryBuilder.CheckExpression(expression, "In"); + var isSingle = QueryBuilder.IsSingle(); + var lamResult = QueryBuilder.GetExpressionValue(expression, isSingle ? ResolveExpressType.FieldSingle : ResolveExpressType.FieldMultiple); + var fieldName = lamResult.GetResultString(); + In(fieldName, inValues); + return this; + } + public ISugarQueryable In(Expression> expression, List inValues) + { + QueryBuilder.CheckExpression(expression, "In"); + var isSingle = QueryBuilder.IsSingle(); + var lamResult = QueryBuilder.GetExpressionValue(expression, isSingle ? ResolveExpressType.FieldSingle : ResolveExpressType.FieldMultiple); + var fieldName = lamResult.GetResultString(); + In(fieldName, inValues); + return this; + } + public ISugarQueryable In(Expression> expression, ISugarQueryable childQueryExpression) + { + var sqlObj = childQueryExpression.ToSql(); + _InQueryable(expression, sqlObj); + return this; + } + #endregion + + + #region Other + public new ISugarQueryable Take(int num) + { + QueryBuilder.Take = num; + return this; + } + public new ISugarQueryable Clone() + { + var queryable = this.Context.Queryable((t, t2, t3, t4) => Array.Empty()).WithCacheIF(IsCache, CacheTime); + base.CopyQueryBuilder(queryable.QueryBuilder); + return queryable; + } + public new ISugarQueryable AS(string tableName) + { + var entityName = typeof(AsT).Name; + _As(tableName, entityName); + return this; + } + public new ISugarQueryable AS(string tableName) + { + var entityName = typeof(T).Name; + _As(tableName, entityName); + return this; + } + public new ISugarQueryable ClearFilter() + { + this.Filter(null, true); + return this; + } + public new ISugarQueryable Filter(string FilterName, bool isDisabledGobalFilter = false) + { + _Filter(FilterName, isDisabledGobalFilter); + return this; + } + + public new ISugarQueryable ClearFilter(params Type[] types) + { + base.ClearFilter(types); + return this; + } + public new ISugarQueryable ClearFilter() + { + this.ClearFilter(typeof(FilterType1)); + return this; + } + public new ISugarQueryable ClearFilter() + { + this.ClearFilter(typeof(FilterType1), typeof(FilterType2)); + return this; + } + public new ISugarQueryable ClearFilter() + { + this.ClearFilter(typeof(FilterType1), typeof(FilterType2), typeof(FilterType3)); + return this; + } + + public new ISugarQueryable AddParameters(object parameters) + { + if (parameters != null) + QueryBuilder.Parameters.AddRange(Context.Ado.GetParameters(parameters)); + return this; + } + public new ISugarQueryable AddParameters(SugarParameter[] parameters) + { + if (parameters != null) + QueryBuilder.Parameters.AddRange(parameters); + return this; + } + public new ISugarQueryable AddParameters(List parameters) + { + if (parameters != null) + QueryBuilder.Parameters.AddRange(parameters); + return this; + } + public new ISugarQueryable AddJoinInfo(string tableName, string shortName, string joinWhere, JoinType type = JoinType.Left) + { + QueryBuilder.JoinIndex = +1; + QueryBuilder.JoinQueryInfos + .Add(new JoinQueryInfo() + { + JoinIndex = QueryBuilder.JoinIndex, + TableName = tableName, + ShortName = shortName, + JoinType = type, + JoinWhere = joinWhere + }); + return this; + } + public new ISugarQueryable With(string withString) + { + base.With(withString); + return this; + } + public new ISugarQueryable WithCache(int cacheDurationInSeconds = int.MaxValue) + { + cacheDurationInSeconds = SetCacheTime(cacheDurationInSeconds); + this.IsCache = true; + this.CacheTime = cacheDurationInSeconds; + return this; + } + public new ISugarQueryable WithCacheIF(bool isCache, int cacheDurationInSeconds = int.MaxValue) + { + cacheDurationInSeconds = SetCacheTime(cacheDurationInSeconds); + if (IsCache) + { + this.IsCache = true; + this.CacheTime = cacheDurationInSeconds; + } + return this; + } + + public bool Any(Expression> expression) + { + _Where(expression); + var result = Any(); + this.QueryBuilder.WhereInfos.Remove(this.QueryBuilder.WhereInfos.Last()); + return result; + } + public new ISugarQueryable Distinct() + { + QueryBuilder.IsDistinct = true; + return this; + } + #endregion + } + #endregion + #region T5 + public partial class QueryableProvider : QueryableProvider, ISugarQueryable + { + public new ISugarQueryable IF(bool condition, Action> action) + { + throw new Exception("Only Queryable().IF is supported, and Queryable().IF is not supported"); + } + public new ISugarQueryable Hints(string hints) + { + this.QueryBuilder.Hints = hints; + return this; + } + public new ISugarQueryable OrderByPropertyName(string orderPropertyName, OrderByType? orderByType = null) + { + base.OrderByPropertyName(orderPropertyName, orderByType); + return this; + } + public new ISugarQueryable OrderByPropertyNameIF(bool isOrderBy, string orderPropertyName, OrderByType? orderByType = null) + { + if (isOrderBy) + { + return this.OrderByPropertyName(orderPropertyName, orderByType); + } + return this; + } + public new ISugarQueryable SampleBy(int timeNumber, SampleByUnit timeType) + { + base.SampleBy(timeNumber, timeType); + return this; + } + public new ISugarQueryable SampleBy(int timeNumber, string timeType) + { + base.SampleBy(timeNumber, timeType); + return this; + } + public virtual ISugarQueryable SelectMergeTable(Expression> expression) + { + return this.Select(expression).MergeTable(); + } + + public ISugarQueryable LeftJoinIF(bool isLeftJoin, Expression> joinExpression) + { + var result = LeftJoin(joinExpression); + if (isLeftJoin == false) + { + result.QueryBuilder.JoinQueryInfos.Remove(result.QueryBuilder.JoinQueryInfos.Last()); + } + return result; + } + public ISugarQueryable InnerJoinIF(bool isJoin, Expression> joinExpression) + { + var result = InnerJoin(joinExpression); + if (isJoin == false) + { + result.QueryBuilder.JoinQueryInfos.Remove(result.QueryBuilder.JoinQueryInfos.Last()); + } + return result; + } + + public ISugarQueryable LeftJoin(ISugarQueryable joinQueryable, Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + var joinInfo = GetJoinInfo(joinExpression, JoinType.Left); + var sqlObject = joinQueryable.ToSql(); + string sql = sqlObject.Key; + this.QueryBuilder.LambdaExpressions.ParameterIndex += 100; + UtilMethods.RepairReplicationParameters(ref sql, sqlObject.Value.ToArray(), this.QueryBuilder.LambdaExpressions.ParameterIndex, ""); + joinInfo.TableName = "(" + sql + ")"; + this.QueryBuilder.Parameters.AddRange(sqlObject.Value); + result.QueryBuilder.JoinQueryInfos.Add(joinInfo); + result.QueryBuilder.LambdaExpressions.ParameterIndex = this.QueryBuilder.LambdaExpressions.ParameterIndex; + return result; + } + + public ISugarQueryable LeftJoinIF(bool isJoin, ISugarQueryable joinQueryable, Expression> joinExpression) + { + var result = LeftJoin(joinQueryable, joinExpression); + if (isJoin == false) + { + result.QueryBuilder.JoinQueryInfos.Remove(result.QueryBuilder.JoinQueryInfos.Last()); + } + return result; + } + public ISugarQueryable InnerJoinIF(bool isJoin, ISugarQueryable joinQueryable, Expression> joinExpression) + { + var result = InnerJoin(joinQueryable, joinExpression); + if (isJoin == false) + { + result.QueryBuilder.JoinQueryInfos.Remove(result.QueryBuilder.JoinQueryInfos.Last()); + } + return result; + } + public ISugarQueryable InnerJoin(ISugarQueryable joinQueryable, Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + var joinInfo = GetJoinInfo(joinExpression, JoinType.Inner); + var sqlObject = joinQueryable.ToSql(); + string sql = sqlObject.Key; + this.QueryBuilder.LambdaExpressions.ParameterIndex += 100; + UtilMethods.RepairReplicationParameters(ref sql, sqlObject.Value.ToArray(), this.QueryBuilder.LambdaExpressions.ParameterIndex, ""); + joinInfo.TableName = "(" + sql + ")"; + this.QueryBuilder.Parameters.AddRange(sqlObject.Value); + result.QueryBuilder.JoinQueryInfos.Add(joinInfo); + result.QueryBuilder.LambdaExpressions.ParameterIndex = this.QueryBuilder.LambdaExpressions.ParameterIndex; + return result; + } + public ISugarQueryable RightJoin(ISugarQueryable joinQueryable, Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + var joinInfo = GetJoinInfo(joinExpression, JoinType.Right); + var sqlObject = joinQueryable.ToSql(); + string sql = sqlObject.Key; + this.QueryBuilder.LambdaExpressions.ParameterIndex += 100; + UtilMethods.RepairReplicationParameters(ref sql, sqlObject.Value.ToArray(), this.QueryBuilder.LambdaExpressions.ParameterIndex, ""); + joinInfo.TableName = "(" + sql + ")"; + this.QueryBuilder.Parameters.AddRange(sqlObject.Value); + result.QueryBuilder.JoinQueryInfos.Add(joinInfo); + result.QueryBuilder.LambdaExpressions.ParameterIndex = this.QueryBuilder.LambdaExpressions.ParameterIndex; + return result; + } + public ISugarQueryable LeftJoin(Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + result.QueryBuilder.JoinQueryInfos.Add(GetJoinInfo(joinExpression, JoinType.Left)); + return result; + } + public ISugarQueryable FullJoin(Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + result.QueryBuilder.JoinQueryInfos.Add(GetJoinInfo(joinExpression, JoinType.Full)); + return result; + } + public ISugarQueryable RightJoin(Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + result.QueryBuilder.JoinQueryInfos.Add(GetJoinInfo(joinExpression, JoinType.Right)); + return result; + } + + public ISugarQueryable InnerJoin(Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + result.QueryBuilder.JoinQueryInfos.Add(GetJoinInfo(joinExpression, JoinType.Inner)); + return result; + } + + public ISugarQueryable LeftJoin(Expression> joinExpression, string tableName) + { + var result = LeftJoin(joinExpression); + result.QueryBuilder.JoinQueryInfos.Last().TableName = tableName; + return result; + } + public ISugarQueryable FullJoin(Expression> joinExpression, string tableName) + { + var result = FullJoin(joinExpression); + result.QueryBuilder.JoinQueryInfos.Last().TableName = tableName; + return result; + } + public ISugarQueryable InnerJoin(Expression> joinExpression, string tableName) + { + var result = InnerJoin(joinExpression); + result.QueryBuilder.JoinQueryInfos.Last().TableName = tableName; + return result; + } + public ISugarQueryable RightJoin(Expression> joinExpression, string tableName) + { + var result = RightJoin(joinExpression); + result.QueryBuilder.JoinQueryInfos.Last().TableName = tableName; + return result; + } + + + #region Where + public new ISugarQueryable Where(string expShortName, FormattableString expressionString) + { + var exp = DynamicCoreHelper.GetWhere(expShortName, expressionString); + _Where(exp); + return this; + } + public new ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public new ISugarQueryable Where(List conditionalModels) + { + base.Where(conditionalModels); + return this; + } + public new ISugarQueryable Where(List conditionalModels, bool isWrap) + { + base.Where(conditionalModels, isWrap); + return this; + } + public new ISugarQueryable Where(IFuncModel funcModel) + { + var obj = this.SqlBuilder.FuncModelToSql(funcModel); + return this.Where(obj.Key, obj.Value); + } + public new ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public new ISugarQueryable Where(string whereString, object whereObj) + { + Where(whereString, whereObj); + return this; + } + + public new ISugarQueryable WhereIF(bool isWhere, string whereString, object whereObj) + { + if (!isWhere) return this; + this.Where(whereString, whereObj); + return this; + } + + /// + /// if a property that is not empty is a condition + /// + /// + /// + /// + public new ISugarQueryable WhereClass(ClassType whereClass, bool ignoreDefaultValue = false) where ClassType : class, new() + { + base.WhereClass(whereClass, ignoreDefaultValue); + return this; + } + /// + /// if a property that is not empty is a condition + /// + /// + /// + /// + public new ISugarQueryable WhereClass(List whereClassTypes, bool ignoreDefaultValue = false) where ClassType : class, new() + { + + base.WhereClass(whereClassTypes, ignoreDefaultValue); + return this; + } + + #endregion + + #region Select + public ISugarQueryable Select(Expression> expression, bool isAutoFill) + { + var clone = this.Select(expression).Clone(); + var sql = clone.QueryBuilder.GetSelectValue; + if (this.QueryBuilder.IsSingle() || string.IsNullOrEmpty(sql) || sql.Trim() == "*") + { + sql = " "; + } + this.QueryBuilder.Parameters = clone.QueryBuilder.Parameters; + this.QueryBuilder.SubToListParameters = clone.QueryBuilder.SubToListParameters; + this.QueryBuilder.LambdaExpressions.ParameterIndex = clone.QueryBuilder.LambdaExpressions.ParameterIndex; + var parameters = (expression as LambdaExpression).Parameters; + var columnsResult = this.Context.EntityMaintenance.GetEntityInfo().Columns; + sql = AppendSelect(sql, parameters, columnsResult, 0); + sql = AppendSelect(sql, parameters, columnsResult, 1); + sql = AppendSelect(sql, parameters, columnsResult, 2); + sql = AppendSelect(sql, parameters, columnsResult, 3); + sql = AppendSelect(sql, parameters, columnsResult, 4); + if (sql.Trim().First() == ',') + { + sql = sql.TrimStart(' ').TrimStart(','); + } + return this.Select(sql); + } + + public virtual ISugarQueryable SelectIF(bool condition, Expression> trueSelectExpression, Expression> falseSelectExpression) + { + if (condition) + return Select(trueSelectExpression); + else + return Select(falseSelectExpression); + } + public ISugarQueryable Select(Expression> expression) + { + if (IsAppendNavColumns()) + { + SetAppendNavColumns(expression); + } + return _Select(expression); + } + public ISugarQueryable Select(Expression> expression) + { + if (IsAppendNavColumns()) + { + SetAppendNavColumns(expression); + } + return _Select(expression); + } + public ISugarQueryable Select(Expression> expression) + { + if (IsAppendNavColumns()) + { + SetAppendNavColumns(expression); + } + return _Select(expression); + } + public ISugarQueryable Select(Expression> expression) + { + if (IsAppendNavColumns()) + { + SetAppendNavColumns(expression); + } + return _Select(expression); + } + #endregion + + #region OrderBy + public new ISugarQueryable OrderBy(List models) + { + base.OrderBy(models); + return this; + } + public new virtual ISugarQueryable OrderByDescending(Expression> expression) + { + this._OrderBy(expression, OrderByType.Desc); + return this; + } + public virtual ISugarQueryable OrderByDescending(Expression> expression) + { + this._OrderBy(expression, OrderByType.Desc); + return this; + } + public virtual ISugarQueryable OrderByDescending(Expression> expression) + { + this._OrderBy(expression, OrderByType.Desc); + return this; + } + public virtual ISugarQueryable OrderByDescending(Expression> expression) + { + this._OrderBy(expression, OrderByType.Desc); + return this; + } + public virtual ISugarQueryable OrderByDescending(Expression> expression) + { + this._OrderBy(expression, OrderByType.Desc); + return this; + } + public new ISugarQueryable OrderBy(string orderByFields) + { + base.OrderBy(orderByFields); + return this; + } + public new ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public new ISugarQueryable OrderByIF(bool isOrderBy, string orderByFields) + { + if (isOrderBy) + base.OrderBy(orderByFields); + return this; + } + public new ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + #endregion + + #region GroupBy + public new virtual ISugarQueryable GroupByIF(bool isGroupBy, Expression> expression) + { + if (isGroupBy) + { + GroupBy(expression); + } + return this; + } + public virtual ISugarQueryable GroupByIF(bool isGroupBy, Expression> expression) + { + if (isGroupBy) + { + GroupBy(expression); + } + return this; + } + public virtual ISugarQueryable GroupByIF(bool isGroupBy, Expression> expression) + { + if (isGroupBy) + { + GroupBy(expression); + } + return this; + } + public new virtual ISugarQueryable GroupByIF(bool isGroupBy, string groupFields) + { + if (isGroupBy) + { + GroupBy(groupFields); + } + return this; + } + public virtual ISugarQueryable GroupByIF(bool isGroupBy, Expression> expression) + { + if (isGroupBy) + { + GroupBy(expression); + } + return this; + } + public virtual ISugarQueryable GroupByIF(bool isGroupBy, Expression> expression) + { + if (isGroupBy) + { + GroupBy(expression); + } + return this; + } + public new ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public new ISugarQueryable Having(Expression> expression) + { + this._Having(expression); + return this; + } + + public ISugarQueryable Having(Expression> expression) + { + this._Having(expression); + return this; + } + + public ISugarQueryable Having(Expression> expression) + { + this._Having(expression); + return this; + } + + public ISugarQueryable Having(Expression> expression) + { + this._Having(expression); + return this; + } + + public ISugarQueryable Having(Expression> expression) + { + this._Having(expression); + return this; + } + + public new ISugarQueryable Having(string whereString, object whereObj) + { + base.Having(whereString, whereObj); + return this; + } + + public new virtual ISugarQueryable HavingIF(bool isHaving, Expression> expression) + { + if (isHaving) + this._Having(expression); + return this; + } + public virtual ISugarQueryable HavingIF(bool isHaving, Expression> expression) + { + if (isHaving) + this._Having(expression); + return this; + } + public virtual ISugarQueryable HavingIF(bool isHaving, Expression> expression) + { + if (isHaving) + this._Having(expression); + return this; + } + public virtual ISugarQueryable HavingIF(bool isHaving, Expression> expression) + { + if (isHaving) + this._Having(expression); + return this; + } + public virtual ISugarQueryable HavingIF(bool isHaving, Expression> expression) + { + if (isHaving) + this._Having(expression); + return this; + } + #endregion + + #region Aggr + public TResult Max(Expression> expression) + { + return _Max(expression); + } + public TResult Min(Expression> expression) + { + return _Min(expression); + } + public TResult Sum(Expression> expression) + { + return _Sum(expression); + } + public TResult Avg(Expression> expression) + { + return _Avg(expression); + } + #endregion + + #region In + public new ISugarQueryable In(Expression> expression, params FieldType[] inValues) + { + var isSingle = QueryBuilder.IsSingle(); + var lamResult = QueryBuilder.GetExpressionValue(expression, isSingle ? ResolveExpressType.FieldSingle : ResolveExpressType.FieldMultiple); + var fieldName = lamResult.GetResultString(); + In(fieldName, inValues); + return this; + } + public new ISugarQueryable In(Expression> expression, List inValues) + { + var isSingle = QueryBuilder.IsSingle(); + var lamResult = QueryBuilder.GetExpressionValue(expression, isSingle ? ResolveExpressType.FieldSingle : ResolveExpressType.FieldMultiple); + var fieldName = lamResult.GetResultString(); + In(fieldName, inValues); + return this; + } + public new ISugarQueryable In(Expression> expression, ISugarQueryable childQueryExpression) + { + var sqlObj = childQueryExpression.ToSql(); + _InQueryable(expression, sqlObj); + return this; + } + #endregion + + #region Other + public new ISugarQueryable Take(int num) + { + QueryBuilder.Take = num; + return this; + } + public new ISugarQueryable Clone() + { + var queryable = this.Context.Queryable((t, t2, t3, t4, t5) => Array.Empty()).WithCacheIF(IsCache, CacheTime); + base.CopyQueryBuilder(queryable.QueryBuilder); + return queryable; + } + public new ISugarQueryable AS(string tableName) + { + var entityName = typeof(AsT).Name; + _As(tableName, entityName); + return this; + } + public new ISugarQueryable AS(string tableName) + { + var entityName = typeof(T).Name; + _As(tableName, entityName); + return this; + } + public new ISugarQueryable ClearFilter() + { + this.Filter(null, true); + return this; + } + public new ISugarQueryable Filter(string FilterName, bool isDisabledGobalFilter = false) + { + _Filter(FilterName, isDisabledGobalFilter); + return this; + } + + + public new ISugarQueryable ClearFilter(params Type[] types) + { + base.ClearFilter(types); + return this; + } + public new ISugarQueryable ClearFilter() + { + this.ClearFilter(typeof(FilterType1)); + return this; + } + public new ISugarQueryable ClearFilter() + { + this.ClearFilter(typeof(FilterType1), typeof(FilterType2)); + return this; + } + public new ISugarQueryable ClearFilter() + { + this.ClearFilter(typeof(FilterType1), typeof(FilterType2), typeof(FilterType3)); + return this; + } + + public new ISugarQueryable AddParameters(object parameters) + { + if (parameters != null) + QueryBuilder.Parameters.AddRange(Context.Ado.GetParameters(parameters)); + return this; + } + public new ISugarQueryable AddParameters(SugarParameter[] parameters) + { + if (parameters != null) + QueryBuilder.Parameters.AddRange(parameters); + return this; + } + public new ISugarQueryable AddParameters(List parameters) + { + if (parameters != null) + QueryBuilder.Parameters.AddRange(parameters); + return this; + } + public new ISugarQueryable AddJoinInfo(string tableName, string shortName, string joinWhere, JoinType type = JoinType.Left) + { + QueryBuilder.JoinIndex = +1; + QueryBuilder.JoinQueryInfos + .Add(new JoinQueryInfo() + { + JoinIndex = QueryBuilder.JoinIndex, + TableName = tableName, + ShortName = shortName, + JoinType = type, + JoinWhere = joinWhere + }); + return this; + } + public new ISugarQueryable With(string withString) + { + base.With(withString); + return this; + } + public new ISugarQueryable WithCache(int cacheDurationInSeconds = int.MaxValue) + { + cacheDurationInSeconds = SetCacheTime(cacheDurationInSeconds); + this.IsCache = true; + this.CacheTime = cacheDurationInSeconds; + return this; + } + public new ISugarQueryable WithCacheIF(bool isCache, int cacheDurationInSeconds = int.MaxValue) + { + cacheDurationInSeconds = SetCacheTime(cacheDurationInSeconds); + if (IsCache) + { + this.IsCache = true; + this.CacheTime = cacheDurationInSeconds; + } + return this; + } + + public bool Any(Expression> expression) + { + _Where(expression); + var result = Any(); + this.QueryBuilder.WhereInfos.Remove(this.QueryBuilder.WhereInfos.Last()); + return result; + } + public new ISugarQueryable Distinct() + { + QueryBuilder.IsDistinct = true; + return this; + } + #endregion + } + #endregion +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/QueryableProvider06-10.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/QueryableProvider06-10.cs new file mode 100644 index 000000000..0530d5bd4 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/QueryableProvider06-10.cs @@ -0,0 +1,4082 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + #region T6 + public partial class QueryableProvider : QueryableProvider, ISugarQueryable + { + public new ISugarQueryable IF(bool condition, Action> action) + { + throw new Exception("Only Queryable().IF is supported, and Queryable().IF is not supported"); + } + public new ISugarQueryable Hints(string hints) + { + this.QueryBuilder.Hints = hints; + return this; + } + public new ISugarQueryable OrderByPropertyNameIF(bool isOrderBy, string orderPropertyName, OrderByType? orderByType = null) + { + if (isOrderBy) + { + return this.OrderByPropertyName(orderPropertyName, orderByType); + } + return this; + } + public new ISugarQueryable OrderByPropertyName(string orderPropertyName, OrderByType? orderByType = null) + { + base.OrderByPropertyName(orderPropertyName, orderByType); + return this; + } + public virtual ISugarQueryable SelectMergeTable(Expression> expression) + { + return this.Select(expression).MergeTable(); + } + public ISugarQueryable LeftJoinIF(bool isLeftJoin, Expression> joinExpression) + { + var result = LeftJoin(joinExpression); + if (isLeftJoin == false) + { + result.QueryBuilder.JoinQueryInfos.Remove(result.QueryBuilder.JoinQueryInfos.Last()); + } + return result; + } + public ISugarQueryable InnerJoinIF(bool isJoin, Expression> joinExpression) + { + var result = InnerJoin(joinExpression); + if (isJoin == false) + { + result.QueryBuilder.JoinQueryInfos.Remove(result.QueryBuilder.JoinQueryInfos.Last()); + } + return result; + } + public ISugarQueryable LeftJoin(ISugarQueryable joinQueryable, Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + var joinInfo = GetJoinInfo(joinExpression, JoinType.Left); + var sqlObject = joinQueryable.ToSql(); + string sql = sqlObject.Key; + this.QueryBuilder.LambdaExpressions.ParameterIndex += 100; + UtilMethods.RepairReplicationParameters(ref sql, sqlObject.Value.ToArray(), this.QueryBuilder.LambdaExpressions.ParameterIndex, ""); + joinInfo.TableName = "(" + sql + ")"; + this.QueryBuilder.Parameters.AddRange(sqlObject.Value); + result.QueryBuilder.JoinQueryInfos.Add(joinInfo); + result.QueryBuilder.LambdaExpressions.ParameterIndex = this.QueryBuilder.LambdaExpressions.ParameterIndex; + return result; + } + public ISugarQueryable InnerJoin(ISugarQueryable joinQueryable, Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + var joinInfo = GetJoinInfo(joinExpression, JoinType.Inner); + var sqlObject = joinQueryable.ToSql(); + string sql = sqlObject.Key; + this.QueryBuilder.LambdaExpressions.ParameterIndex += 100; + UtilMethods.RepairReplicationParameters(ref sql, sqlObject.Value.ToArray(), this.QueryBuilder.LambdaExpressions.ParameterIndex, ""); + joinInfo.TableName = "(" + sql + ")"; + this.QueryBuilder.Parameters.AddRange(sqlObject.Value); + result.QueryBuilder.JoinQueryInfos.Add(joinInfo); + result.QueryBuilder.LambdaExpressions.ParameterIndex = this.QueryBuilder.LambdaExpressions.ParameterIndex; + return result; + } + public ISugarQueryable RightJoin(ISugarQueryable joinQueryable, Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + var joinInfo = GetJoinInfo(joinExpression, JoinType.Right); + var sqlObject = joinQueryable.ToSql(); + string sql = sqlObject.Key; + this.QueryBuilder.LambdaExpressions.ParameterIndex += 100; + UtilMethods.RepairReplicationParameters(ref sql, sqlObject.Value.ToArray(), this.QueryBuilder.LambdaExpressions.ParameterIndex, ""); + joinInfo.TableName = "(" + sql + ")"; + this.QueryBuilder.Parameters.AddRange(sqlObject.Value); + result.QueryBuilder.JoinQueryInfos.Add(joinInfo); + result.QueryBuilder.LambdaExpressions.ParameterIndex = this.QueryBuilder.LambdaExpressions.ParameterIndex; + return result; + } + public ISugarQueryable LeftJoinIF(bool isJoin, ISugarQueryable joinQueryable, Expression> joinExpression) + { + var result = LeftJoin(joinQueryable, joinExpression); + if (isJoin == false) + { + result.QueryBuilder.JoinQueryInfos.Remove(result.QueryBuilder.JoinQueryInfos.Last()); + } + return result; + } + public ISugarQueryable InnerJoinIF(bool isJoin, ISugarQueryable joinQueryable, Expression> joinExpression) + { + var result = InnerJoin(joinQueryable, joinExpression); + if (isJoin == false) + { + result.QueryBuilder.JoinQueryInfos.Remove(result.QueryBuilder.JoinQueryInfos.Last()); + } + return result; + } + public ISugarQueryable LeftJoin(Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + result.QueryBuilder.JoinQueryInfos.Add(GetJoinInfo(joinExpression, JoinType.Left)); + return result; + } + public ISugarQueryable FullJoin(Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + result.QueryBuilder.JoinQueryInfos.Add(GetJoinInfo(joinExpression, JoinType.Full)); + return result; + } + public ISugarQueryable RightJoin(Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + result.QueryBuilder.JoinQueryInfos.Add(GetJoinInfo(joinExpression, JoinType.Right)); + return result; + } + public ISugarQueryable InnerJoin(Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + result.QueryBuilder.JoinQueryInfos.Add(GetJoinInfo(joinExpression, JoinType.Inner)); + return result; + } + + public ISugarQueryable LeftJoin(Expression> joinExpression, string tableName) + { + var result = LeftJoin(joinExpression); + result.QueryBuilder.JoinQueryInfos.Last().TableName = tableName; + return result; + } + public ISugarQueryable FullJoin(Expression> joinExpression, string tableName) + { + var result = FullJoin(joinExpression); + result.QueryBuilder.JoinQueryInfos.Last().TableName = tableName; + return result; + } + public ISugarQueryable InnerJoin(Expression> joinExpression, string tableName) + { + var result = InnerJoin(joinExpression); + result.QueryBuilder.JoinQueryInfos.Last().TableName = tableName; + return result; + } + public ISugarQueryable RightJoin(Expression> joinExpression, string tableName) + { + var result = RightJoin(joinExpression); + result.QueryBuilder.JoinQueryInfos.Last().TableName = tableName; + return result; + } + + #region Where + public new ISugarQueryable Where(string expShortName, FormattableString expressionString) + { + var exp = DynamicCoreHelper.GetWhere(expShortName, expressionString); + _Where(exp); + return this; + } + public new ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public new ISugarQueryable Where(List conditionalModels) + { + base.Where(conditionalModels); + return this; + } + public new ISugarQueryable Where(List conditionalModels, bool isWrap) + { + base.Where(conditionalModels, isWrap); + return this; + } + public new ISugarQueryable Where(IFuncModel funcModel) + { + var obj = this.SqlBuilder.FuncModelToSql(funcModel); + return this.Where(obj.Key, obj.Value); + } + public new ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public new ISugarQueryable Where(string whereString, object whereObj) + { + Where(whereString, whereObj); + return this; + } + + public new ISugarQueryable WhereIF(bool isWhere, string whereString, object whereObj) + { + if (!isWhere) return this; + this.Where(whereString, whereObj); + return this; + } + + /// + /// if a property that is not empty is a condition + /// + /// + /// + /// + public new ISugarQueryable WhereClass(ClassType whereClass, bool ignoreDefaultValue = false) where ClassType : class, new() + { + base.WhereClass(whereClass, ignoreDefaultValue); + return this; + } + /// + /// if a property that is not empty is a condition + /// + /// + /// + /// + public new ISugarQueryable WhereClass(List whereClassTypes, bool ignoreDefaultValue = false) where ClassType : class, new() + { + + base.WhereClass(whereClassTypes, ignoreDefaultValue); + return this; + } + #endregion + + #region Select + public virtual ISugarQueryable SelectIF(bool condition, Expression> trueSelectExpression, Expression> falseSelectExpression) + { + if (condition) + return Select(trueSelectExpression); + else + return Select(falseSelectExpression); + } + public ISugarQueryable Select(Expression> expression) + { + if (IsAppendNavColumns()) + { + SetAppendNavColumns(expression); + } + return _Select(expression); + } + public ISugarQueryable Select(Expression> expression) + { + if (IsAppendNavColumns()) + { + SetAppendNavColumns(expression); + } + return _Select(expression); + } + public ISugarQueryable Select(Expression> expression) + { + if (IsAppendNavColumns()) + { + SetAppendNavColumns(expression); + } + return _Select(expression); + } + public ISugarQueryable Select(Expression> expression) + { + if (IsAppendNavColumns()) + { + SetAppendNavColumns(expression); + } + return _Select(expression); + } + public ISugarQueryable Select(Expression> expression) + { + if (IsAppendNavColumns()) + { + SetAppendNavColumns(expression); + } + return _Select(expression); + } + public ISugarQueryable Select(Expression> expression, bool isAutoFill) + { + var clone = this.Select(expression).Clone(); + var sql = clone.QueryBuilder.GetSelectValue; + if (this.QueryBuilder.IsSingle() || string.IsNullOrEmpty(sql) || sql.Trim() == "*") + { + sql = " "; + } + this.QueryBuilder.Parameters = clone.QueryBuilder.Parameters; + this.QueryBuilder.SubToListParameters = clone.QueryBuilder.SubToListParameters; + this.QueryBuilder.LambdaExpressions.ParameterIndex = clone.QueryBuilder.LambdaExpressions.ParameterIndex; + var parameters = (expression as LambdaExpression).Parameters; + var columnsResult = this.Context.EntityMaintenance.GetEntityInfo().Columns; + sql = AppendSelect(sql, parameters, columnsResult, 0); + sql = AppendSelect(sql, parameters, columnsResult, 1); + sql = AppendSelect(sql, parameters, columnsResult, 2); + sql = AppendSelect(sql, parameters, columnsResult, 3); + sql = AppendSelect(sql, parameters, columnsResult, 4); + sql = AppendSelect(sql, parameters, columnsResult, 5); + if (sql.Trim().First() == ',') + { + sql = sql.TrimStart(' ').TrimStart(','); + } + return this.Select(sql); + } + #endregion + + #region OrderBy + public new ISugarQueryable OrderBy(List models) + { + base.OrderBy(models); + return this; + } + public new virtual ISugarQueryable OrderByDescending(Expression> expression) + { + this._OrderBy(expression, OrderByType.Desc); + return this; + } + public virtual ISugarQueryable OrderByDescending(Expression> expression) + { + this._OrderBy(expression, OrderByType.Desc); + return this; + } + public virtual ISugarQueryable OrderByDescending(Expression> expression) + { + this._OrderBy(expression, OrderByType.Desc); + return this; + } + public virtual ISugarQueryable OrderByDescending(Expression> expression) + { + this._OrderBy(expression, OrderByType.Desc); + return this; + } + public virtual ISugarQueryable OrderByDescending(Expression> expression) + { + this._OrderBy(expression, OrderByType.Desc); + return this; + } + public virtual ISugarQueryable OrderByDescending(Expression> expression) + { + this._OrderBy(expression, OrderByType.Desc); + return this; + } + + public new ISugarQueryable OrderBy(string orderByFields) + { + base.OrderBy(orderByFields); + return this; + } + public new ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public new ISugarQueryable OrderByIF(bool isOrderBy, string orderByFields) + { + if (isOrderBy) + base.OrderBy(orderByFields); + return this; + } + public new ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + #endregion + + #region GroupBy + public new virtual ISugarQueryable GroupByIF(bool isGroupBy, Expression> expression) + { + if (isGroupBy) + { + GroupBy(expression); + } + return this; + } + public virtual ISugarQueryable GroupByIF(bool isGroupBy, Expression> expression) + { + if (isGroupBy) + { + GroupBy(expression); + } + return this; + } + public virtual ISugarQueryable GroupByIF(bool isGroupBy, Expression> expression) + { + if (isGroupBy) + { + GroupBy(expression); + } + return this; + } + public new virtual ISugarQueryable GroupByIF(bool isGroupBy, string groupFields) + { + if (isGroupBy) + { + GroupBy(groupFields); + } + return this; + } + public virtual ISugarQueryable GroupByIF(bool isGroupBy, Expression> expression) + { + if (isGroupBy) + { + GroupBy(expression); + } + return this; + } + public virtual ISugarQueryable GroupByIF(bool isGroupBy, Expression> expression) + { + if (isGroupBy) + { + GroupBy(expression); + } + return this; + } + public virtual ISugarQueryable GroupByIF(bool isGroupBy, Expression> expression) + { + if (isGroupBy) + { + GroupBy(expression); + } + return this; + } + public new ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public new ISugarQueryable Having(Expression> expression) + { + this._Having(expression); + return this; + } + + public ISugarQueryable Having(Expression> expression) + { + this._Having(expression); + return this; + } + + public ISugarQueryable Having(Expression> expression) + { + this._Having(expression); + return this; + } + + public ISugarQueryable Having(Expression> expression) + { + this._Having(expression); + return this; + } + + public ISugarQueryable Having(Expression> expression) + { + this._Having(expression); + return this; + } + public ISugarQueryable Having(Expression> expression) + { + this._Having(expression); + return this; + } + + public new ISugarQueryable Having(string whereString, object whereObj) + { + base.Having(whereString, whereObj); + return this; + } + + public new virtual ISugarQueryable HavingIF(bool isHaving, Expression> expression) + { + if (isHaving) + this._Having(expression); + return this; + } + public virtual ISugarQueryable HavingIF(bool isHaving, Expression> expression) + { + if (isHaving) + this._Having(expression); + return this; + } + public virtual ISugarQueryable HavingIF(bool isHaving, Expression> expression) + { + if (isHaving) + this._Having(expression); + return this; + } + public virtual ISugarQueryable HavingIF(bool isHaving, Expression> expression) + { + if (isHaving) + this._Having(expression); + return this; + } + public virtual ISugarQueryable HavingIF(bool isHaving, Expression> expression) + { + if (isHaving) + this._Having(expression); + return this; + } + public virtual ISugarQueryable HavingIF(bool isHaving, Expression> expression) + { + if (isHaving) + this._Having(expression); + return this; + } + #endregion + + #region Aggr + public TResult Max(Expression> expression) + { + return _Max(expression); + } + public TResult Min(Expression> expression) + { + return _Min(expression); + } + public TResult Sum(Expression> expression) + { + return _Sum(expression); + } + public TResult Avg(Expression> expression) + { + return _Avg(expression); + } + #endregion + + #region In + public new ISugarQueryable In(Expression> expression, params FieldType[] inValues) + { + var isSingle = QueryBuilder.IsSingle(); + var lamResult = QueryBuilder.GetExpressionValue(expression, isSingle ? ResolveExpressType.FieldSingle : ResolveExpressType.FieldMultiple); + var fieldName = lamResult.GetResultString(); + In(fieldName, inValues); + return this; + } + public new ISugarQueryable In(Expression> expression, List inValues) + { + var isSingle = QueryBuilder.IsSingle(); + var lamResult = QueryBuilder.GetExpressionValue(expression, isSingle ? ResolveExpressType.FieldSingle : ResolveExpressType.FieldMultiple); + var fieldName = lamResult.GetResultString(); + In(fieldName, inValues); + return this; + } + public new ISugarQueryable In(Expression> expression, ISugarQueryable childQueryExpression) + { + var sqlObj = childQueryExpression.ToSql(); + _InQueryable(expression, sqlObj); + return this; + } + #endregion + + #region Other + public new ISugarQueryable Take(int num) + { + QueryBuilder.Take = num; + return this; + } + public new ISugarQueryable Clone() + { + var queryable = this.Context.Queryable((t, t2, t3, t4, t5, T6) => Array.Empty()).WithCacheIF(IsCache, CacheTime); + base.CopyQueryBuilder(queryable.QueryBuilder); + return queryable; + } + public new ISugarQueryable AS(string tableName) + { + var entityName = typeof(AsT).Name; + _As(tableName, entityName); + return this; + } + public new ISugarQueryable AS(string tableName) + { + var entityName = typeof(T).Name; + _As(tableName, entityName); + return this; + } + public new ISugarQueryable ClearFilter() + { + this.Filter(null, true); + return this; + } + public new ISugarQueryable Filter(string FilterName, bool isDisabledGobalFilter = false) + { + _Filter(FilterName, isDisabledGobalFilter); + return this; + } + + + public new ISugarQueryable ClearFilter(params Type[] types) + { + base.ClearFilter(types); + return this; + } + public new ISugarQueryable ClearFilter() + { + this.ClearFilter(typeof(FilterType1)); + return this; + } + public new ISugarQueryable ClearFilter() + { + this.ClearFilter(typeof(FilterType1), typeof(FilterType2)); + return this; + } + public new ISugarQueryable ClearFilter() + { + this.ClearFilter(typeof(FilterType1), typeof(FilterType2), typeof(FilterType3)); + return this; + } + + public new ISugarQueryable AddParameters(object parameters) + { + if (parameters != null) + QueryBuilder.Parameters.AddRange(Context.Ado.GetParameters(parameters)); + return this; + } + public new ISugarQueryable AddParameters(SugarParameter[] parameters) + { + if (parameters != null) + QueryBuilder.Parameters.AddRange(parameters); + return this; + } + public new ISugarQueryable AddParameters(List parameters) + { + if (parameters != null) + QueryBuilder.Parameters.AddRange(parameters); + return this; + } + public new ISugarQueryable AddJoinInfo(string tableName, string shortName, string joinWhere, JoinType type = JoinType.Left) + { + QueryBuilder.JoinIndex = +1; + QueryBuilder.JoinQueryInfos + .Add(new JoinQueryInfo() + { + JoinIndex = QueryBuilder.JoinIndex, + TableName = tableName, + ShortName = shortName, + JoinType = type, + JoinWhere = joinWhere + }); + return this; + } + public new ISugarQueryable With(string withString) + { + base.With(withString); + return this; + } + public new ISugarQueryable WithCache(int cacheDurationInSeconds = int.MaxValue) + { + cacheDurationInSeconds = SetCacheTime(cacheDurationInSeconds); + this.IsCache = true; + this.CacheTime = cacheDurationInSeconds; + return this; + } + public new ISugarQueryable WithCacheIF(bool isCache, int cacheDurationInSeconds = int.MaxValue) + { + cacheDurationInSeconds = SetCacheTime(cacheDurationInSeconds); + if (IsCache) + { + this.IsCache = true; + this.CacheTime = cacheDurationInSeconds; + } + return this; + } + + public bool Any(Expression> expression) + { + _Where(expression); + var result = Any(); + this.QueryBuilder.WhereInfos.Remove(this.QueryBuilder.WhereInfos.Last()); + return result; + } + public new ISugarQueryable Distinct() + { + QueryBuilder.IsDistinct = true; + return this; + } + #endregion + } + #endregion + #region T7 + public partial class QueryableProvider : QueryableProvider, ISugarQueryable + { + public new ISugarQueryable Hints(string hints) + { + this.QueryBuilder.Hints = hints; + return this; + } + public new ISugarQueryable OrderByPropertyNameIF(bool isOrderBy, string orderPropertyName, OrderByType? orderByType = null) + { + if (isOrderBy) + { + return this.OrderByPropertyName(orderPropertyName, orderByType); + } + return this; + } + public new ISugarQueryable OrderByPropertyName(string orderPropertyName, OrderByType? orderByType = null) + { + base.OrderByPropertyName(orderPropertyName, orderByType); + return this; + } + public virtual ISugarQueryable SelectMergeTable(Expression> expression) + { + return this.Select(expression).MergeTable(); + } + public ISugarQueryable LeftJoinIF(bool isLeftJoin, Expression> joinExpression) + { + var result = LeftJoin(joinExpression); + if (isLeftJoin == false) + { + result.QueryBuilder.JoinQueryInfos.Remove(result.QueryBuilder.JoinQueryInfos.Last()); + } + return result; + } + public ISugarQueryable InnerJoinIF(bool isJoin, Expression> joinExpression) + { + var result = InnerJoin(joinExpression); + if (isJoin == false) + { + result.QueryBuilder.JoinQueryInfos.Remove(result.QueryBuilder.JoinQueryInfos.Last()); + } + return result; + } + public ISugarQueryable LeftJoin(ISugarQueryable joinQueryable, Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + var joinInfo = GetJoinInfo(joinExpression, JoinType.Left); + var sqlObject = joinQueryable.ToSql(); + string sql = sqlObject.Key; + this.QueryBuilder.LambdaExpressions.ParameterIndex += 100; + UtilMethods.RepairReplicationParameters(ref sql, sqlObject.Value.ToArray(), this.QueryBuilder.LambdaExpressions.ParameterIndex, ""); + joinInfo.TableName = "(" + sql + ")"; + this.QueryBuilder.Parameters.AddRange(sqlObject.Value); + result.QueryBuilder.JoinQueryInfos.Add(joinInfo); + result.QueryBuilder.LambdaExpressions.ParameterIndex = this.QueryBuilder.LambdaExpressions.ParameterIndex; + return result; + } + public ISugarQueryable InnerJoin(ISugarQueryable joinQueryable, Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + var joinInfo = GetJoinInfo(joinExpression, JoinType.Inner); + var sqlObject = joinQueryable.ToSql(); + string sql = sqlObject.Key; + this.QueryBuilder.LambdaExpressions.ParameterIndex += 100; + UtilMethods.RepairReplicationParameters(ref sql, sqlObject.Value.ToArray(), this.QueryBuilder.LambdaExpressions.ParameterIndex, ""); + joinInfo.TableName = "(" + sql + ")"; + this.QueryBuilder.Parameters.AddRange(sqlObject.Value); + result.QueryBuilder.JoinQueryInfos.Add(joinInfo); + result.QueryBuilder.LambdaExpressions.ParameterIndex = this.QueryBuilder.LambdaExpressions.ParameterIndex; + return result; + } + public ISugarQueryable RightJoin(ISugarQueryable joinQueryable, Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + var joinInfo = GetJoinInfo(joinExpression, JoinType.Right); + var sqlObject = joinQueryable.ToSql(); + string sql = sqlObject.Key; + this.QueryBuilder.LambdaExpressions.ParameterIndex += 100; + UtilMethods.RepairReplicationParameters(ref sql, sqlObject.Value.ToArray(), this.QueryBuilder.LambdaExpressions.ParameterIndex, ""); + joinInfo.TableName = "(" + sql + ")"; + this.QueryBuilder.Parameters.AddRange(sqlObject.Value); + result.QueryBuilder.JoinQueryInfos.Add(joinInfo); + result.QueryBuilder.LambdaExpressions.ParameterIndex = this.QueryBuilder.LambdaExpressions.ParameterIndex; + return result; + } + public ISugarQueryable LeftJoinIF(bool isJoin, ISugarQueryable joinQueryable, Expression> joinExpression) + { + var result = LeftJoin(joinQueryable, joinExpression); + if (isJoin == false) + { + result.QueryBuilder.JoinQueryInfos.Remove(result.QueryBuilder.JoinQueryInfos.Last()); + } + return result; + } + public ISugarQueryable InnerJoinIF(bool isJoin, ISugarQueryable joinQueryable, Expression> joinExpression) + { + var result = InnerJoin(joinQueryable, joinExpression); + if (isJoin == false) + { + result.QueryBuilder.JoinQueryInfos.Remove(result.QueryBuilder.JoinQueryInfos.Last()); + } + return result; + } + public ISugarQueryable LeftJoin(Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + result.QueryBuilder.JoinQueryInfos.Add(GetJoinInfo(joinExpression, JoinType.Left)); + return result; + } + public ISugarQueryable FullJoin(Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + result.QueryBuilder.JoinQueryInfos.Add(GetJoinInfo(joinExpression, JoinType.Full)); + return result; + } + public ISugarQueryable RightJoin(Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + result.QueryBuilder.JoinQueryInfos.Add(GetJoinInfo(joinExpression, JoinType.Right)); + return result; + } + public ISugarQueryable InnerJoin(Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + result.QueryBuilder.JoinQueryInfos.Add(GetJoinInfo(joinExpression, JoinType.Inner)); + return result; + } + + public ISugarQueryable LeftJoin(Expression> joinExpression, string tableName) + { + var result = LeftJoin(joinExpression); + result.QueryBuilder.JoinQueryInfos.Last().TableName = tableName; + return result; + } + public ISugarQueryable FullJoin(Expression> joinExpression, string tableName) + { + var result = FullJoin(joinExpression); + result.QueryBuilder.JoinQueryInfos.Last().TableName = tableName; + return result; + } + public ISugarQueryable InnerJoin(Expression> joinExpression, string tableName) + { + var result = InnerJoin(joinExpression); + result.QueryBuilder.JoinQueryInfos.Last().TableName = tableName; + return result; + } + public ISugarQueryable RightJoin(Expression> joinExpression, string tableName) + { + var result = RightJoin(joinExpression); + result.QueryBuilder.JoinQueryInfos.Last().TableName = tableName; + return result; + } + + #region Where + public new ISugarQueryable Where(string expShortName, FormattableString expressionString) + { + var exp = DynamicCoreHelper.GetWhere(expShortName, expressionString); + _Where(exp); + return this; + } + public new ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public new ISugarQueryable Where(List conditionalModels) + { + base.Where(conditionalModels); + return this; + } + public new ISugarQueryable Where(List conditionalModels, bool isWrap) + { + base.Where(conditionalModels, isWrap); + return this; + } + public new ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public new ISugarQueryable Where(string whereString, object whereObj) + { + Where(whereString, whereObj); + return this; + } + + public new ISugarQueryable WhereIF(bool isWhere, string whereString, object whereObj) + { + if (!isWhere) return this; + this.Where(whereString, whereObj); + return this; + } + /// + /// if a property that is not empty is a condition + /// + /// + /// + /// + public new ISugarQueryable WhereClass(ClassType whereClass, bool ignoreDefaultValue = false) where ClassType : class, new() + { + base.WhereClass(whereClass, ignoreDefaultValue); + return this; + } + /// + /// if a property that is not empty is a condition + /// + /// + /// + /// + public new ISugarQueryable WhereClass(List whereClassTypes, bool ignoreDefaultValue = false) where ClassType : class, new() + { + + base.WhereClass(whereClassTypes, ignoreDefaultValue); + return this; + } + #endregion + + #region Select + public virtual ISugarQueryable SelectIF(bool condition, Expression> trueSelectExpression, Expression> falseSelectExpression) + { + if (condition) + return Select(trueSelectExpression); + else + return Select(falseSelectExpression); + } + public ISugarQueryable Select(Expression> expression) + { + if (IsAppendNavColumns()) + { + SetAppendNavColumns(expression); + } + return _Select(expression); + } + public ISugarQueryable Select(Expression> expression) + { + if (IsAppendNavColumns()) + { + SetAppendNavColumns(expression); + } + return _Select(expression); + } + public ISugarQueryable Select(Expression> expression) + { + if (IsAppendNavColumns()) + { + SetAppendNavColumns(expression); + } + return _Select(expression); + } + public ISugarQueryable Select(Expression> expression) + { + if (IsAppendNavColumns()) + { + SetAppendNavColumns(expression); + } + return _Select(expression); + } + public ISugarQueryable Select(Expression> expression) + { + if (IsAppendNavColumns()) + { + SetAppendNavColumns(expression); + } + return _Select(expression); + } + public ISugarQueryable Select(Expression> expression) + { + if (IsAppendNavColumns()) + { + SetAppendNavColumns(expression); + } + return _Select(expression); + } + public ISugarQueryable Select(Expression> expression, bool isAutoFill) + { + var clone = this.Select(expression).Clone(); + var sql = clone.QueryBuilder.GetSelectValue; + if (this.QueryBuilder.IsSingle() || string.IsNullOrEmpty(sql) || sql.Trim() == "*") + { + sql = " "; + } + this.QueryBuilder.Parameters = clone.QueryBuilder.Parameters; + this.QueryBuilder.SubToListParameters = clone.QueryBuilder.SubToListParameters; + this.QueryBuilder.LambdaExpressions.ParameterIndex = clone.QueryBuilder.LambdaExpressions.ParameterIndex; + var parameters = (expression as LambdaExpression).Parameters; + var columnsResult = this.Context.EntityMaintenance.GetEntityInfo().Columns; + sql = AppendSelect(sql, parameters, columnsResult, 0); + sql = AppendSelect(sql, parameters, columnsResult, 1); + sql = AppendSelect(sql, parameters, columnsResult, 2); + sql = AppendSelect(sql, parameters, columnsResult, 3); + sql = AppendSelect(sql, parameters, columnsResult, 4); + sql = AppendSelect(sql, parameters, columnsResult, 5); + sql = AppendSelect(sql, parameters, columnsResult, 6); + if (sql.Trim().First() == ',') + { + sql = sql.TrimStart(' ').TrimStart(','); + } + return this.Select(sql); + } + + #endregion + + #region OrderBy + public new ISugarQueryable OrderBy(List models) + { + base.OrderBy(models); + return this; + } + public new virtual ISugarQueryable OrderByDescending(Expression> expression) + { + this._OrderBy(expression, OrderByType.Desc); + return this; + } + public virtual ISugarQueryable OrderByDescending(Expression> expression) + { + this._OrderBy(expression, OrderByType.Desc); + return this; + } + public virtual ISugarQueryable OrderByDescending(Expression> expression) + { + this._OrderBy(expression, OrderByType.Desc); + return this; + } + public virtual ISugarQueryable OrderByDescending(Expression> expression) + { + this._OrderBy(expression, OrderByType.Desc); + return this; + } + public virtual ISugarQueryable OrderByDescending(Expression> expression) + { + this._OrderBy(expression, OrderByType.Desc); + return this; + } + public virtual ISugarQueryable OrderByDescending(Expression> expression) + { + this._OrderBy(expression, OrderByType.Desc); + return this; + } + public virtual ISugarQueryable OrderByDescending(Expression> expression) + { + this._OrderBy(expression, OrderByType.Desc); + return this; + } + + public new ISugarQueryable OrderBy(string orderByFields) + { + base.OrderBy(orderByFields); + return this; + } + public new ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + #endregion + + #region GroupBy + public new virtual ISugarQueryable GroupByIF(bool isGroupBy, Expression> expression) + { + if (isGroupBy) + { + GroupBy(expression); + } + return this; + } + public virtual ISugarQueryable GroupByIF(bool isGroupBy, Expression> expression) + { + if (isGroupBy) + { + GroupBy(expression); + } + return this; + } + public virtual ISugarQueryable GroupByIF(bool isGroupBy, Expression> expression) + { + if (isGroupBy) + { + GroupBy(expression); + } + return this; + } + public new virtual ISugarQueryable GroupByIF(bool isGroupBy, string groupFields) + { + if (isGroupBy) + { + GroupBy(groupFields); + } + return this; + } + public virtual ISugarQueryable GroupByIF(bool isGroupBy, Expression> expression) + { + if (isGroupBy) + { + GroupBy(expression); + } + return this; + } + public virtual ISugarQueryable GroupByIF(bool isGroupBy, Expression> expression) + { + if (isGroupBy) + { + GroupBy(expression); + } + return this; + } + public virtual ISugarQueryable GroupByIF(bool isGroupBy, Expression> expression) + { + if (isGroupBy) + { + GroupBy(expression); + } + return this; + } + public virtual ISugarQueryable GroupByIF(bool isGroupBy, Expression> expression) + { + if (isGroupBy) + { + GroupBy(expression); + } + return this; + } + public new ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public new ISugarQueryable OrderByIF(bool isOrderBy, string orderByFields) + { + if (isOrderBy) + base.OrderBy(orderByFields); + return this; + } + public new ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public new virtual ISugarQueryable HavingIF(bool isHaving, Expression> expression) + { + if (isHaving) + this._Having(expression); + return this; + } + public virtual ISugarQueryable HavingIF(bool isHaving, Expression> expression) + { + if (isHaving) + this._Having(expression); + return this; + } + public virtual ISugarQueryable HavingIF(bool isHaving, Expression> expression) + { + if (isHaving) + this._Having(expression); + return this; + } + public virtual ISugarQueryable HavingIF(bool isHaving, Expression> expression) + { + if (isHaving) + this._Having(expression); + return this; + } + public virtual ISugarQueryable HavingIF(bool isHaving, Expression> expression) + { + if (isHaving) + this._Having(expression); + return this; + } + public virtual ISugarQueryable HavingIF(bool isHaving, Expression> expression) + { + if (isHaving) + this._Having(expression); + return this; + } + public virtual ISugarQueryable HavingIF(bool isHaving, Expression> expression) + { + if (isHaving) + this._Having(expression); + return this; + } + #endregion + + #region Aggr + public TResult Max(Expression> expression) + { + return _Max(expression); + } + public TResult Min(Expression> expression) + { + return _Min(expression); + } + public TResult Sum(Expression> expression) + { + return _Sum(expression); + } + public TResult Avg(Expression> expression) + { + return _Avg(expression); + } + #endregion + + #region In + public new ISugarQueryable In(Expression> expression, params FieldType[] inValues) + { + var isSingle = QueryBuilder.IsSingle(); + var lamResult = QueryBuilder.GetExpressionValue(expression, isSingle ? ResolveExpressType.FieldSingle : ResolveExpressType.FieldMultiple); + var fieldName = lamResult.GetResultString(); + In(fieldName, inValues); + return this; + } + public new ISugarQueryable In(Expression> expression, List inValues) + { + var isSingle = QueryBuilder.IsSingle(); + var lamResult = QueryBuilder.GetExpressionValue(expression, isSingle ? ResolveExpressType.FieldSingle : ResolveExpressType.FieldMultiple); + var fieldName = lamResult.GetResultString(); + In(fieldName, inValues); + return this; + } + public new ISugarQueryable In(Expression> expression, ISugarQueryable childQueryExpression) + { + var sqlObj = childQueryExpression.ToSql(); + _InQueryable(expression, sqlObj); + return this; + } + #endregion + + #region Other + public new ISugarQueryable Take(int num) + { + QueryBuilder.Take = num; + return this; + } + public new ISugarQueryable Clone() + { + var queryable = this.Context.Queryable((t, t2, t3, t4, t5, T6, t7) => Array.Empty()).WithCacheIF(IsCache, CacheTime); + base.CopyQueryBuilder(queryable.QueryBuilder); + return queryable; + } + public new ISugarQueryable AS(string tableName) + { + var entityName = typeof(AsT).Name; + _As(tableName, entityName); + return this; + } + public new ISugarQueryable AS(string tableName) + { + var entityName = typeof(T).Name; + _As(tableName, entityName); + return this; + } + public new ISugarQueryable ClearFilter() + { + this.Filter(null, true); + return this; + } + public new ISugarQueryable Filter(string FilterName, bool isDisabledGobalFilter = false) + { + _Filter(FilterName, isDisabledGobalFilter); + return this; + } + + public new ISugarQueryable ClearFilter(params Type[] types) + { + base.ClearFilter(types); + return this; + } + public new ISugarQueryable ClearFilter() + { + this.ClearFilter(typeof(FilterType1)); + return this; + } + public new ISugarQueryable ClearFilter() + { + this.ClearFilter(typeof(FilterType1), typeof(FilterType2)); + return this; + } + public new ISugarQueryable ClearFilter() + { + this.ClearFilter(typeof(FilterType1), typeof(FilterType2), typeof(FilterType3)); + return this; + } + + public new ISugarQueryable AddParameters(object parameters) + { + if (parameters != null) + QueryBuilder.Parameters.AddRange(Context.Ado.GetParameters(parameters)); + return this; + } + public new ISugarQueryable AddParameters(SugarParameter[] parameters) + { + if (parameters != null) + QueryBuilder.Parameters.AddRange(parameters); + return this; + } + public new ISugarQueryable AddParameters(List parameters) + { + if (parameters != null) + QueryBuilder.Parameters.AddRange(parameters); + return this; + } + public new ISugarQueryable AddJoinInfo(string tableName, string shortName, string joinWhere, JoinType type = JoinType.Left) + { + QueryBuilder.JoinIndex = +1; + QueryBuilder.JoinQueryInfos + .Add(new JoinQueryInfo() + { + JoinIndex = QueryBuilder.JoinIndex, + TableName = tableName, + ShortName = shortName, + JoinType = type, + JoinWhere = joinWhere + }); + return this; + } + public new ISugarQueryable With(string withString) + { + base.With(withString); + return this; + } + public new ISugarQueryable WithCache(int cacheDurationInSeconds = int.MaxValue) + { + cacheDurationInSeconds = SetCacheTime(cacheDurationInSeconds); + this.IsCache = true; + this.CacheTime = cacheDurationInSeconds; + return this; + } + public new ISugarQueryable WithCacheIF(bool isCache, int cacheDurationInSeconds = int.MaxValue) + { + cacheDurationInSeconds = SetCacheTime(cacheDurationInSeconds); + if (IsCache) + { + this.IsCache = true; + this.CacheTime = cacheDurationInSeconds; + } + return this; + } + + public bool Any(Expression> expression) + { + _Where(expression); + var result = Any(); + this.QueryBuilder.WhereInfos.Remove(this.QueryBuilder.WhereInfos.Last()); + return result; + } + public new ISugarQueryable Distinct() + { + QueryBuilder.IsDistinct = true; + return this; + } + #endregion + } + #endregion + #region T8 + public partial class QueryableProvider : QueryableProvider, ISugarQueryable + { + public new ISugarQueryable Hints(string hints) + { + this.QueryBuilder.Hints = hints; + return this; + } + public new ISugarQueryable OrderByPropertyNameIF(bool isOrderBy, string orderPropertyName, OrderByType? orderByType = null) + { + if (isOrderBy) + { + return this.OrderByPropertyName(orderPropertyName, orderByType); + } + return this; + } + public new ISugarQueryable OrderByPropertyName(string orderPropertyName, OrderByType? orderByType = null) + { + base.OrderByPropertyName(orderPropertyName, orderByType); + return this; + } + public virtual ISugarQueryable SelectMergeTable(Expression> expression) + { + return this.Select(expression).MergeTable(); + } + public ISugarQueryable LeftJoinIF(bool isLeftJoin, Expression> joinExpression) + { + var result = LeftJoin(joinExpression); + if (isLeftJoin == false) + { + result.QueryBuilder.JoinQueryInfos.Remove(result.QueryBuilder.JoinQueryInfos.Last()); + } + return result; + } + public ISugarQueryable InnerJoinIF(bool isJoin, Expression> joinExpression) + { + var result = InnerJoin(joinExpression); + if (isJoin == false) + { + result.QueryBuilder.JoinQueryInfos.Remove(result.QueryBuilder.JoinQueryInfos.Last()); + } + return result; + } + public ISugarQueryable LeftJoin(ISugarQueryable joinQueryable, Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + var joinInfo = GetJoinInfo(joinExpression, JoinType.Left); + var sqlObject = joinQueryable.ToSql(); + string sql = sqlObject.Key; + this.QueryBuilder.LambdaExpressions.ParameterIndex += 100; + UtilMethods.RepairReplicationParameters(ref sql, sqlObject.Value.ToArray(), this.QueryBuilder.LambdaExpressions.ParameterIndex, ""); + joinInfo.TableName = "(" + sql + ")"; + this.QueryBuilder.Parameters.AddRange(sqlObject.Value); + result.QueryBuilder.JoinQueryInfos.Add(joinInfo); + result.QueryBuilder.LambdaExpressions.ParameterIndex = this.QueryBuilder.LambdaExpressions.ParameterIndex; + return result; + } + public ISugarQueryable InnerJoin(ISugarQueryable joinQueryable, Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + var joinInfo = GetJoinInfo(joinExpression, JoinType.Inner); + var sqlObject = joinQueryable.ToSql(); + string sql = sqlObject.Key; + this.QueryBuilder.LambdaExpressions.ParameterIndex += 100; + UtilMethods.RepairReplicationParameters(ref sql, sqlObject.Value.ToArray(), this.QueryBuilder.LambdaExpressions.ParameterIndex, ""); + joinInfo.TableName = "(" + sql + ")"; + this.QueryBuilder.Parameters.AddRange(sqlObject.Value); + result.QueryBuilder.JoinQueryInfos.Add(joinInfo); + result.QueryBuilder.LambdaExpressions.ParameterIndex = this.QueryBuilder.LambdaExpressions.ParameterIndex; + return result; + } + public ISugarQueryable RightJoin(ISugarQueryable joinQueryable, Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + var joinInfo = GetJoinInfo(joinExpression, JoinType.Right); + var sqlObject = joinQueryable.ToSql(); + string sql = sqlObject.Key; + this.QueryBuilder.LambdaExpressions.ParameterIndex += 100; + UtilMethods.RepairReplicationParameters(ref sql, sqlObject.Value.ToArray(), this.QueryBuilder.LambdaExpressions.ParameterIndex, ""); + joinInfo.TableName = "(" + sql + ")"; + this.QueryBuilder.Parameters.AddRange(sqlObject.Value); + result.QueryBuilder.JoinQueryInfos.Add(joinInfo); + result.QueryBuilder.LambdaExpressions.ParameterIndex = this.QueryBuilder.LambdaExpressions.ParameterIndex; + return result; + } + public ISugarQueryable LeftJoinIF(bool isJoin, ISugarQueryable joinQueryable, Expression> joinExpression) + { + var result = LeftJoin(joinQueryable, joinExpression); + if (isJoin == false) + { + result.QueryBuilder.JoinQueryInfos.Remove(result.QueryBuilder.JoinQueryInfos.Last()); + } + return result; + } + public ISugarQueryable InnerJoinIF(bool isJoin, ISugarQueryable joinQueryable, Expression> joinExpression) + { + var result = InnerJoin(joinQueryable, joinExpression); + if (isJoin == false) + { + result.QueryBuilder.JoinQueryInfos.Remove(result.QueryBuilder.JoinQueryInfos.Last()); + } + return result; + } + public ISugarQueryable LeftJoin(Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + result.QueryBuilder.JoinQueryInfos.Add(GetJoinInfo(joinExpression, JoinType.Left)); + return result; + } + public ISugarQueryable FullJoin(Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + result.QueryBuilder.JoinQueryInfos.Add(GetJoinInfo(joinExpression, JoinType.Full)); + return result; + } + public ISugarQueryable RightJoin(Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + result.QueryBuilder.JoinQueryInfos.Add(GetJoinInfo(joinExpression, JoinType.Right)); + return result; + } + public ISugarQueryable InnerJoin(Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + result.QueryBuilder.JoinQueryInfos.Add(GetJoinInfo(joinExpression, JoinType.Inner)); + return result; + } + + public ISugarQueryable LeftJoin(Expression> joinExpression, string tableName) + { + var result = LeftJoin(joinExpression); + result.QueryBuilder.JoinQueryInfos.Last().TableName = tableName; + return result; + } + public ISugarQueryable FullJoin(Expression> joinExpression, string tableName) + { + var result = FullJoin(joinExpression); + result.QueryBuilder.JoinQueryInfos.Last().TableName = tableName; + return result; + } + public ISugarQueryable InnerJoin(Expression> joinExpression, string tableName) + { + var result = InnerJoin(joinExpression); + result.QueryBuilder.JoinQueryInfos.Last().TableName = tableName; + return result; + } + public ISugarQueryable RightJoin(Expression> joinExpression, string tableName) + { + var result = RightJoin(joinExpression); + result.QueryBuilder.JoinQueryInfos.Last().TableName = tableName; + return result; + } + + #region Where + public new ISugarQueryable Where(string expShortName, FormattableString expressionString) + { + var exp = DynamicCoreHelper.GetWhere(expShortName, expressionString); + _Where(exp); + return this; + } + public new ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public new ISugarQueryable Where(List conditionalModels) + { + base.Where(conditionalModels); + return this; + } + public new ISugarQueryable Where(List conditionalModels, bool isWrap) + { + base.Where(conditionalModels, isWrap); + return this; + } + public new ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public new ISugarQueryable Where(string whereString, object whereObj) + { + Where(whereString, whereObj); + return this; + } + + public new ISugarQueryable WhereIF(bool isWhere, string whereString, object whereObj) + { + if (!isWhere) return this; + this.Where(whereString, whereObj); + return this; + } + /// + /// if a property that is not empty is a condition + /// + /// + /// + /// + public new ISugarQueryable WhereClass(ClassType whereClass, bool ignoreDefaultValue = false) where ClassType : class, new() + { + base.WhereClass(whereClass, ignoreDefaultValue); + return this; + } + /// + /// if a property that is not empty is a condition + /// + /// + /// + /// + public new ISugarQueryable WhereClass(List whereClassTypes, bool ignoreDefaultValue = false) where ClassType : class, new() + { + + base.WhereClass(whereClassTypes, ignoreDefaultValue); + return this; + } + #endregion + + #region Select + public virtual ISugarQueryable SelectIF(bool condition, Expression> trueSelectExpression, Expression> falseSelectExpression) + { + if (condition) + return Select(trueSelectExpression); + else + return Select(falseSelectExpression); + } + public ISugarQueryable Select(Expression> expression) + { + if (IsAppendNavColumns()) + { + SetAppendNavColumns(expression); + } + return _Select(expression); + } + public ISugarQueryable Select(Expression> expression) + { + if (IsAppendNavColumns()) + { + SetAppendNavColumns(expression); + } + return _Select(expression); + } + public ISugarQueryable Select(Expression> expression) + { + if (IsAppendNavColumns()) + { + SetAppendNavColumns(expression); + } + return _Select(expression); + } + public ISugarQueryable Select(Expression> expression) + { + if (IsAppendNavColumns()) + { + SetAppendNavColumns(expression); + } + return _Select(expression); + } + public ISugarQueryable Select(Expression> expression) + { + if (IsAppendNavColumns()) + { + SetAppendNavColumns(expression); + } + return _Select(expression); + } + public ISugarQueryable Select(Expression> expression) + { + if (IsAppendNavColumns()) + { + SetAppendNavColumns(expression); + } + return _Select(expression); + } + public ISugarQueryable Select(Expression> expression) + { + if (IsAppendNavColumns()) + { + SetAppendNavColumns(expression); + } + return _Select(expression); + } + public ISugarQueryable Select(Expression> expression, bool isAutoFill) + { + var clone = this.Select(expression).Clone(); + var sql = clone.QueryBuilder.GetSelectValue; + if (this.QueryBuilder.IsSingle() || string.IsNullOrEmpty(sql) || sql.Trim() == "*") + { + sql = " "; + } + this.QueryBuilder.Parameters = clone.QueryBuilder.Parameters; + this.QueryBuilder.SubToListParameters = clone.QueryBuilder.SubToListParameters; + this.QueryBuilder.LambdaExpressions.ParameterIndex = clone.QueryBuilder.LambdaExpressions.ParameterIndex; + var parameters = (expression as LambdaExpression).Parameters; + var columnsResult = this.Context.EntityMaintenance.GetEntityInfo().Columns; + sql = AppendSelect(sql, parameters, columnsResult, 0); + sql = AppendSelect(sql, parameters, columnsResult, 1); + sql = AppendSelect(sql, parameters, columnsResult, 2); + sql = AppendSelect(sql, parameters, columnsResult, 3); + sql = AppendSelect(sql, parameters, columnsResult, 4); + sql = AppendSelect(sql, parameters, columnsResult, 5); + sql = AppendSelect(sql, parameters, columnsResult, 6); + sql = AppendSelect(sql, parameters, columnsResult, 7); + if (sql.Trim().First() == ',') + { + sql = sql.TrimStart(' ').TrimStart(','); + } + return this.Select(sql); + } + + #endregion + + #region OrderBy + public new ISugarQueryable OrderBy(List models) + { + base.OrderBy(models); + return this; + } + public new virtual ISugarQueryable OrderByDescending(Expression> expression) + { + this._OrderBy(expression, OrderByType.Desc); + return this; + } + public virtual ISugarQueryable OrderByDescending(Expression> expression) + { + this._OrderBy(expression, OrderByType.Desc); + return this; + } + public virtual ISugarQueryable OrderByDescending(Expression> expression) + { + this._OrderBy(expression, OrderByType.Desc); + return this; + } + public virtual ISugarQueryable OrderByDescending(Expression> expression) + { + this._OrderBy(expression, OrderByType.Desc); + return this; + } + public virtual ISugarQueryable OrderByDescending(Expression> expression) + { + this._OrderBy(expression, OrderByType.Desc); + return this; + } + public virtual ISugarQueryable OrderByDescending(Expression> expression) + { + this._OrderBy(expression, OrderByType.Desc); + return this; + } + public virtual ISugarQueryable OrderByDescending(Expression> expression) + { + this._OrderBy(expression, OrderByType.Desc); + return this; + } + public virtual ISugarQueryable OrderByDescending(Expression> expression) + { + this._OrderBy(expression, OrderByType.Desc); + return this; + } + public new ISugarQueryable OrderBy(string orderByFields) + { + base.OrderBy(orderByFields); + return this; + } + public new ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public new ISugarQueryable OrderByIF(bool isOrderBy, string orderByFields) + { + if (isOrderBy) + base.OrderBy(orderByFields); + return this; + } + public new ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + #endregion + + #region GroupBy + public new ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + + #endregion + + #region Aggr + public TResult Max(Expression> expression) + { + return _Max(expression); + } + public TResult Min(Expression> expression) + { + return _Min(expression); + } + public TResult Sum(Expression> expression) + { + return _Sum(expression); + } + public TResult Avg(Expression> expression) + { + return _Avg(expression); + } + #endregion + + #region In + public new ISugarQueryable In(Expression> expression, params FieldType[] inValues) + { + var isSingle = QueryBuilder.IsSingle(); + var lamResult = QueryBuilder.GetExpressionValue(expression, isSingle ? ResolveExpressType.FieldSingle : ResolveExpressType.FieldMultiple); + var fieldName = lamResult.GetResultString(); + In(fieldName, inValues); + return this; + } + public new ISugarQueryable In(Expression> expression, List inValues) + { + var isSingle = QueryBuilder.IsSingle(); + var lamResult = QueryBuilder.GetExpressionValue(expression, isSingle ? ResolveExpressType.FieldSingle : ResolveExpressType.FieldMultiple); + var fieldName = lamResult.GetResultString(); + In(fieldName, inValues); + return this; + } + public new ISugarQueryable In(Expression> expression, ISugarQueryable childQueryExpression) + { + var sqlObj = childQueryExpression.ToSql(); + _InQueryable(expression, sqlObj); + return this; + } + #endregion + + #region Other + public new ISugarQueryable Take(int num) + { + QueryBuilder.Take = num; + return this; + } + public new ISugarQueryable Clone() + { + var queryable = this.Context.Queryable((t, t2, t3, t4, t5, T6, t7, t8) => Array.Empty()).WithCacheIF(IsCache, CacheTime); + base.CopyQueryBuilder(queryable.QueryBuilder); + return queryable; + } + public new ISugarQueryable AS(string tableName) + { + var entityName = typeof(AsT).Name; + _As(tableName, entityName); + return this; + } + public new ISugarQueryable AS(string tableName) + { + var entityName = typeof(T).Name; + _As(tableName, entityName); + return this; + } + public new ISugarQueryable ClearFilter() + { + this.Filter(null, true); + return this; + } + public new ISugarQueryable Filter(string FilterName, bool isDisabledGobalFilter = false) + { + _Filter(FilterName, isDisabledGobalFilter); + return this; + } + public new ISugarQueryable AddParameters(object parameters) + { + if (parameters != null) + QueryBuilder.Parameters.AddRange(Context.Ado.GetParameters(parameters)); + return this; + } + + public new ISugarQueryable ClearFilter(params Type[] types) + { + base.ClearFilter(types); + return this; + } + public new ISugarQueryable ClearFilter() + { + this.ClearFilter(typeof(FilterType1)); + return this; + } + public new ISugarQueryable ClearFilter() + { + this.ClearFilter(typeof(FilterType1), typeof(FilterType2)); + return this; + } + public new ISugarQueryable ClearFilter() + { + this.ClearFilter(typeof(FilterType1), typeof(FilterType2), typeof(FilterType3)); + return this; + } + + public new ISugarQueryable AddParameters(SugarParameter[] parameters) + { + if (parameters != null) + QueryBuilder.Parameters.AddRange(parameters); + return this; + } + public new ISugarQueryable AddParameters(List parameters) + { + if (parameters != null) + QueryBuilder.Parameters.AddRange(parameters); + return this; + } + public new ISugarQueryable AddJoinInfo(string tableName, string shortName, string joinWhere, JoinType type = JoinType.Left) + { + QueryBuilder.JoinIndex = +1; + QueryBuilder.JoinQueryInfos + .Add(new JoinQueryInfo() + { + JoinIndex = QueryBuilder.JoinIndex, + TableName = tableName, + ShortName = shortName, + JoinType = type, + JoinWhere = joinWhere + }); + return this; + } + public new ISugarQueryable With(string withString) + { + base.With(withString); + return this; + } + public new ISugarQueryable WithCache(int cacheDurationInSeconds = int.MaxValue) + { + cacheDurationInSeconds = SetCacheTime(cacheDurationInSeconds); + this.IsCache = true; + this.CacheTime = cacheDurationInSeconds; + return this; + } + public new ISugarQueryable WithCacheIF(bool isCache, int cacheDurationInSeconds = int.MaxValue) + { + cacheDurationInSeconds = SetCacheTime(cacheDurationInSeconds); + if (IsCache) + { + this.IsCache = true; + this.CacheTime = cacheDurationInSeconds; + } + return this; + } + + public bool Any(Expression> expression) + { + _Where(expression); + var result = Any(); + this.QueryBuilder.WhereInfos.Remove(this.QueryBuilder.WhereInfos.Last()); + return result; + } + public new ISugarQueryable Distinct() + { + QueryBuilder.IsDistinct = true; + return this; + } + #endregion + } + #endregion + #region T9 + public partial class QueryableProvider : QueryableProvider, ISugarQueryable + { + public new ISugarQueryable Hints(string hints) + { + this.QueryBuilder.Hints = hints; + return this; + } + public new ISugarQueryable OrderByPropertyNameIF(bool isOrderBy, string orderPropertyName, OrderByType? orderByType = null) + { + if (isOrderBy) + { + return this.OrderByPropertyName(orderPropertyName, orderByType); + } + return this; + } + public new ISugarQueryable OrderByPropertyName(string orderPropertyName, OrderByType? orderByType = null) + { + base.OrderByPropertyName(orderPropertyName, orderByType); + return this; + } + public virtual ISugarQueryable SelectMergeTable(Expression> expression) + { + return this.Select(expression).MergeTable(); + } + public ISugarQueryable LeftJoinIF(bool isLeftJoin, Expression> joinExpression) + { + var result = LeftJoin(joinExpression); + if (isLeftJoin == false) + { + result.QueryBuilder.JoinQueryInfos.Remove(result.QueryBuilder.JoinQueryInfos.Last()); + } + return result; + } + public ISugarQueryable InnerJoinIF(bool isJoin, Expression> joinExpression) + { + var result = InnerJoin(joinExpression); + if (isJoin == false) + { + result.QueryBuilder.JoinQueryInfos.Remove(result.QueryBuilder.JoinQueryInfos.Last()); + } + return result; + } + public ISugarQueryable LeftJoin(ISugarQueryable joinQueryable, Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + var joinInfo = GetJoinInfo(joinExpression, JoinType.Left); + var sqlObject = joinQueryable.ToSql(); + string sql = sqlObject.Key; + this.QueryBuilder.LambdaExpressions.ParameterIndex += 100; + UtilMethods.RepairReplicationParameters(ref sql, sqlObject.Value.ToArray(), this.QueryBuilder.LambdaExpressions.ParameterIndex, ""); + joinInfo.TableName = "(" + sql + ")"; + this.QueryBuilder.Parameters.AddRange(sqlObject.Value); + result.QueryBuilder.JoinQueryInfos.Add(joinInfo); + result.QueryBuilder.LambdaExpressions.ParameterIndex = this.QueryBuilder.LambdaExpressions.ParameterIndex; + return result; + } + public ISugarQueryable InnerJoin(ISugarQueryable joinQueryable, Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + var joinInfo = GetJoinInfo(joinExpression, JoinType.Inner); + var sqlObject = joinQueryable.ToSql(); + string sql = sqlObject.Key; + this.QueryBuilder.LambdaExpressions.ParameterIndex += 100; + UtilMethods.RepairReplicationParameters(ref sql, sqlObject.Value.ToArray(), this.QueryBuilder.LambdaExpressions.ParameterIndex, ""); + joinInfo.TableName = "(" + sql + ")"; + this.QueryBuilder.Parameters.AddRange(sqlObject.Value); + result.QueryBuilder.JoinQueryInfos.Add(joinInfo); + result.QueryBuilder.LambdaExpressions.ParameterIndex = this.QueryBuilder.LambdaExpressions.ParameterIndex; + return result; + } + public ISugarQueryable RightJoin(ISugarQueryable joinQueryable, Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + var joinInfo = GetJoinInfo(joinExpression, JoinType.Right); + var sqlObject = joinQueryable.ToSql(); + string sql = sqlObject.Key; + this.QueryBuilder.LambdaExpressions.ParameterIndex += 100; + UtilMethods.RepairReplicationParameters(ref sql, sqlObject.Value.ToArray(), this.QueryBuilder.LambdaExpressions.ParameterIndex, ""); + joinInfo.TableName = "(" + sql + ")"; + this.QueryBuilder.Parameters.AddRange(sqlObject.Value); + result.QueryBuilder.JoinQueryInfos.Add(joinInfo); + result.QueryBuilder.LambdaExpressions.ParameterIndex = this.QueryBuilder.LambdaExpressions.ParameterIndex; + return result; + } + public ISugarQueryable LeftJoinIF(bool isJoin, ISugarQueryable joinQueryable, Expression> joinExpression) + { + var result = LeftJoin(joinQueryable, joinExpression); + if (isJoin == false) + { + result.QueryBuilder.JoinQueryInfos.Remove(result.QueryBuilder.JoinQueryInfos.Last()); + } + return result; + } + public ISugarQueryable InnerJoinIF(bool isJoin, ISugarQueryable joinQueryable, Expression> joinExpression) + { + var result = InnerJoin(joinQueryable, joinExpression); + if (isJoin == false) + { + result.QueryBuilder.JoinQueryInfos.Remove(result.QueryBuilder.JoinQueryInfos.Last()); + } + return result; + } + public ISugarQueryable LeftJoin(Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + result.QueryBuilder.JoinQueryInfos.Add(GetJoinInfo(joinExpression, JoinType.Left)); + return result; + } + public ISugarQueryable FullJoin(Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + result.QueryBuilder.JoinQueryInfos.Add(GetJoinInfo(joinExpression, JoinType.Full)); + return result; + } + public ISugarQueryable RightJoin(Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + result.QueryBuilder.JoinQueryInfos.Add(GetJoinInfo(joinExpression, JoinType.Right)); + return result; + } + public ISugarQueryable InnerJoin(Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + result.QueryBuilder.JoinQueryInfos.Add(GetJoinInfo(joinExpression, JoinType.Inner)); + return result; + } + + public ISugarQueryable LeftJoin(Expression> joinExpression, string tableName) + { + var result = LeftJoin(joinExpression); + result.QueryBuilder.JoinQueryInfos.Last().TableName = tableName; + return result; + } + public ISugarQueryable FullJoin(Expression> joinExpression, string tableName) + { + var result = FullJoin(joinExpression); + result.QueryBuilder.JoinQueryInfos.Last().TableName = tableName; + return result; + } + public ISugarQueryable InnerJoin(Expression> joinExpression, string tableName) + { + var result = InnerJoin(joinExpression); + result.QueryBuilder.JoinQueryInfos.Last().TableName = tableName; + return result; + } + public ISugarQueryable RightJoin(Expression> joinExpression, string tableName) + { + var result = RightJoin(joinExpression); + result.QueryBuilder.JoinQueryInfos.Last().TableName = tableName; + return result; + } + + #region Where + public new ISugarQueryable Where(string expShortName, FormattableString expressionString) + { + var exp = DynamicCoreHelper.GetWhere(expShortName, expressionString); + _Where(exp); + return this; + } + public new ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public new ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + public new ISugarQueryable Where(List conditionalModels) + { + base.Where(conditionalModels); + return this; + } + public new ISugarQueryable Where(List conditionalModels, bool isWrap) + { + base.Where(conditionalModels, isWrap); + return this; + } + + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public new ISugarQueryable Where(string whereString, object whereObj) + { + Where(whereString, whereObj); + return this; + } + + public new ISugarQueryable WhereIF(bool isWhere, string whereString, object whereObj) + { + if (!isWhere) return this; + this.Where(whereString, whereObj); + return this; + } + #endregion + + #region Select + public ISugarQueryable Select(Expression> expression, bool isAutoFill) + { + var clone = this.Select(expression).Clone(); + var sql = clone.QueryBuilder.GetSelectValue; + if (this.QueryBuilder.IsSingle() || string.IsNullOrEmpty(sql) || sql.Trim() == "*") + { + sql = " "; + } + this.QueryBuilder.Parameters = clone.QueryBuilder.Parameters; + this.QueryBuilder.SubToListParameters = clone.QueryBuilder.SubToListParameters; + this.QueryBuilder.LambdaExpressions.ParameterIndex = clone.QueryBuilder.LambdaExpressions.ParameterIndex; + var parameters = (expression as LambdaExpression).Parameters; + var columnsResult = this.Context.EntityMaintenance.GetEntityInfo().Columns; + sql = AppendSelect(sql, parameters, columnsResult, 0); + sql = AppendSelect(sql, parameters, columnsResult, 1); + sql = AppendSelect(sql, parameters, columnsResult, 2); + sql = AppendSelect(sql, parameters, columnsResult, 3); + sql = AppendSelect(sql, parameters, columnsResult, 4); + sql = AppendSelect(sql, parameters, columnsResult, 5); + sql = AppendSelect(sql, parameters, columnsResult, 6); + sql = AppendSelect(sql, parameters, columnsResult, 7); + sql = AppendSelect(sql, parameters, columnsResult, 8); + if (sql.Trim().First() == ',') + { + sql = sql.TrimStart(' ').TrimStart(','); + } + return this.Select(sql); + } + + public ISugarQueryable Select(Expression> expression) + { + if (IsAppendNavColumns()) + { + SetAppendNavColumns(expression); + } + return _Select(expression); + } + public ISugarQueryable Select(Expression> expression) + { + if (IsAppendNavColumns()) + { + SetAppendNavColumns(expression); + } + return _Select(expression); + } + public ISugarQueryable Select(Expression> expression) + { + if (IsAppendNavColumns()) + { + SetAppendNavColumns(expression); + } + return _Select(expression); + } + public ISugarQueryable Select(Expression> expression) + { + if (IsAppendNavColumns()) + { + SetAppendNavColumns(expression); + } + return _Select(expression); + } + public ISugarQueryable Select(Expression> expression) + { + if (IsAppendNavColumns()) + { + SetAppendNavColumns(expression); + } + return _Select(expression); + } + public ISugarQueryable Select(Expression> expression) + { + if (IsAppendNavColumns()) + { + SetAppendNavColumns(expression); + } + return _Select(expression); + } + public ISugarQueryable Select(Expression> expression) + { + if (IsAppendNavColumns()) + { + SetAppendNavColumns(expression); + } + return _Select(expression); + } + public virtual ISugarQueryable SelectIF(bool condition, Expression> trueSelectExpression, Expression> falseSelectExpression) + { + if (condition) + return Select(trueSelectExpression); + else + return Select(falseSelectExpression); + } + public ISugarQueryable Select(Expression> expression) + { + if (IsAppendNavColumns()) + { + SetAppendNavColumns(expression); + } + return _Select(expression); + } + #endregion + + #region OrderBy + public new virtual ISugarQueryable OrderByDescending(Expression> expression) + { + this._OrderBy(expression, OrderByType.Desc); + return this; + } + public virtual ISugarQueryable OrderByDescending(Expression> expression) + { + this._OrderBy(expression, OrderByType.Desc); + return this; + } + public virtual ISugarQueryable OrderByDescending(Expression> expression) + { + this._OrderBy(expression, OrderByType.Desc); + return this; + } + public virtual ISugarQueryable OrderByDescending(Expression> expression) + { + this._OrderBy(expression, OrderByType.Desc); + return this; + } + public virtual ISugarQueryable OrderByDescending(Expression> expression) + { + this._OrderBy(expression, OrderByType.Desc); + return this; + } + public virtual ISugarQueryable OrderByDescending(Expression> expression) + { + this._OrderBy(expression, OrderByType.Desc); + return this; + } + public virtual ISugarQueryable OrderByDescending(Expression> expression) + { + this._OrderBy(expression, OrderByType.Desc); + return this; + } + public virtual ISugarQueryable OrderByDescending(Expression> expression) + { + this._OrderBy(expression, OrderByType.Desc); + return this; + } + public virtual ISugarQueryable OrderByDescending(Expression> expression) + { + this._OrderBy(expression, OrderByType.Desc); + return this; + } + public new ISugarQueryable OrderBy(List models) + { + base.OrderBy(models); + return this; + } + public new ISugarQueryable OrderBy(string orderByFields) + { + base.OrderBy(orderByFields); + return this; + } + public new ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + + public new ISugarQueryable OrderByIF(bool isOrderBy, string orderByFields) + { + if (isOrderBy) + base.OrderBy(orderByFields); + return this; + } + public new ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + #endregion + + #region GroupBy + public new ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + #endregion + + #region Aggr + public TResult Max(Expression> expression) + { + return _Max(expression); + } + public TResult Min(Expression> expression) + { + return _Min(expression); + } + public TResult Sum(Expression> expression) + { + return _Sum(expression); + } + public TResult Avg(Expression> expression) + { + return _Avg(expression); + } + #endregion + + #region In + public new ISugarQueryable In(Expression> expression, params FieldType[] inValues) + { + var isSingle = QueryBuilder.IsSingle(); + var lamResult = QueryBuilder.GetExpressionValue(expression, isSingle ? ResolveExpressType.FieldSingle : ResolveExpressType.FieldMultiple); + var fieldName = lamResult.GetResultString(); + In(fieldName, inValues); + return this; + } + public new ISugarQueryable In(Expression> expression, List inValues) + { + var isSingle = QueryBuilder.IsSingle(); + var lamResult = QueryBuilder.GetExpressionValue(expression, isSingle ? ResolveExpressType.FieldSingle : ResolveExpressType.FieldMultiple); + var fieldName = lamResult.GetResultString(); + In(fieldName, inValues); + return this; + } + public new ISugarQueryable In(Expression> expression, ISugarQueryable childQueryExpression) + { + var sqlObj = childQueryExpression.ToSql(); + _InQueryable(expression, sqlObj); + return this; + } + #endregion + + #region Other + public new ISugarQueryable Distinct() + { + QueryBuilder.IsDistinct = true; + return this; + } + public new ISugarQueryable Take(int num) + { + QueryBuilder.Take = num; + return this; + } + public new ISugarQueryable Clone() + { + var queryable = this.Context.Queryable((t, t2, t3, t4, t5, T6, t7, t8, t9) => Array.Empty()).WithCacheIF(IsCache, CacheTime); + base.CopyQueryBuilder(queryable.QueryBuilder); + return queryable; + } + public new ISugarQueryable AS(string tableName) + { + var entityName = typeof(AsT).Name; + _As(tableName, entityName); + return this; + } + public new ISugarQueryable AS(string tableName) + { + var entityName = typeof(T).Name; + _As(tableName, entityName); + return this; + } + public new ISugarQueryable ClearFilter() + { + this.Filter(null, true); + return this; + } + public new ISugarQueryable Filter(string FilterName, bool isDisabledGobalFilter = false) + { + _Filter(FilterName, isDisabledGobalFilter); + return this; + } + public new ISugarQueryable ClearFilter(params Type[] types) + { + base.ClearFilter(types); + return this; + } + public new ISugarQueryable ClearFilter() + { + this.ClearFilter(typeof(FilterType1)); + return this; + } + public new ISugarQueryable ClearFilter() + { + this.ClearFilter(typeof(FilterType1), typeof(FilterType2)); + return this; + } + public new ISugarQueryable ClearFilter() + { + this.ClearFilter(typeof(FilterType1), typeof(FilterType2), typeof(FilterType3)); + return this; + } + public new ISugarQueryable AddParameters(object parameters) + { + if (parameters != null) + QueryBuilder.Parameters.AddRange(Context.Ado.GetParameters(parameters)); + return this; + } + public new ISugarQueryable AddParameters(SugarParameter[] parameters) + { + if (parameters != null) + QueryBuilder.Parameters.AddRange(parameters); + return this; + } + public new ISugarQueryable AddParameters(List parameters) + { + if (parameters != null) + QueryBuilder.Parameters.AddRange(parameters); + return this; + } + public new ISugarQueryable AddJoinInfo(string tableName, string shortName, string joinWhere, JoinType type = JoinType.Left) + { + QueryBuilder.JoinIndex = +1; + QueryBuilder.JoinQueryInfos + .Add(new JoinQueryInfo() + { + JoinIndex = QueryBuilder.JoinIndex, + TableName = tableName, + ShortName = shortName, + JoinType = type, + JoinWhere = joinWhere + }); + return this; + } + public new ISugarQueryable With(string withString) + { + base.With(withString); + return this; + } + public new ISugarQueryable WithCache(int cacheDurationInSeconds = int.MaxValue) + { + cacheDurationInSeconds = SetCacheTime(cacheDurationInSeconds); + this.IsCache = true; + this.CacheTime = cacheDurationInSeconds; + return this; + } + public new ISugarQueryable WithCacheIF(bool isCache, int cacheDurationInSeconds = int.MaxValue) + { + cacheDurationInSeconds = SetCacheTime(cacheDurationInSeconds); + if (IsCache) + { + this.IsCache = true; + this.CacheTime = cacheDurationInSeconds; + } + return this; + } + #endregion + } + #endregion + #region T10 + public partial class QueryableProvider : QueryableProvider, ISugarQueryable + { + public new ISugarQueryable Hints(string hints) + { + this.QueryBuilder.Hints = hints; + return this; + } + public new ISugarQueryable OrderByPropertyNameIF(bool isOrderBy, string orderPropertyName, OrderByType? orderByType = null) + { + if (isOrderBy) + { + return this.OrderByPropertyName(orderPropertyName, orderByType); + } + return this; + } + public new ISugarQueryable OrderByPropertyName(string orderPropertyName, OrderByType? orderByType = null) + { + base.OrderByPropertyName(orderPropertyName, orderByType); + return this; + } + public virtual ISugarQueryable SelectMergeTable(Expression> expression) + { + return this.Select(expression).MergeTable(); + } + public ISugarQueryable LeftJoinIF(bool isLeftJoin, Expression> joinExpression) + { + var result = LeftJoin(joinExpression); + if (isLeftJoin == false) + { + result.QueryBuilder.JoinQueryInfos.Remove(result.QueryBuilder.JoinQueryInfos.Last()); + } + return result; + } + public ISugarQueryable InnerJoinIF(bool isJoin, Expression> joinExpression) + { + var result = InnerJoin(joinExpression); + if (isJoin == false) + { + result.QueryBuilder.JoinQueryInfos.Remove(result.QueryBuilder.JoinQueryInfos.Last()); + } + return result; + } + public ISugarQueryable LeftJoin(ISugarQueryable joinQueryable, Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + var joinInfo = GetJoinInfo(joinExpression, JoinType.Left); + var sqlObject = joinQueryable.ToSql(); + string sql = sqlObject.Key; + this.QueryBuilder.LambdaExpressions.ParameterIndex += 100; + UtilMethods.RepairReplicationParameters(ref sql, sqlObject.Value.ToArray(), this.QueryBuilder.LambdaExpressions.ParameterIndex, ""); + joinInfo.TableName = "(" + sql + ")"; + this.QueryBuilder.Parameters.AddRange(sqlObject.Value); + result.QueryBuilder.JoinQueryInfos.Add(joinInfo); + result.QueryBuilder.LambdaExpressions.ParameterIndex = this.QueryBuilder.LambdaExpressions.ParameterIndex; + return result; + } + public ISugarQueryable InnerJoin(ISugarQueryable joinQueryable, Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + var joinInfo = GetJoinInfo(joinExpression, JoinType.Inner); + var sqlObject = joinQueryable.ToSql(); + string sql = sqlObject.Key; + this.QueryBuilder.LambdaExpressions.ParameterIndex += 100; + UtilMethods.RepairReplicationParameters(ref sql, sqlObject.Value.ToArray(), this.QueryBuilder.LambdaExpressions.ParameterIndex, ""); + joinInfo.TableName = "(" + sql + ")"; + this.QueryBuilder.Parameters.AddRange(sqlObject.Value); + result.QueryBuilder.JoinQueryInfos.Add(joinInfo); + result.QueryBuilder.LambdaExpressions.ParameterIndex = this.QueryBuilder.LambdaExpressions.ParameterIndex; + return result; + } + public ISugarQueryable RightJoin(ISugarQueryable joinQueryable, Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + var joinInfo = GetJoinInfo(joinExpression, JoinType.Right); + var sqlObject = joinQueryable.ToSql(); + string sql = sqlObject.Key; + this.QueryBuilder.LambdaExpressions.ParameterIndex += 100; + UtilMethods.RepairReplicationParameters(ref sql, sqlObject.Value.ToArray(), this.QueryBuilder.LambdaExpressions.ParameterIndex, ""); + joinInfo.TableName = "(" + sql + ")"; + this.QueryBuilder.Parameters.AddRange(sqlObject.Value); + result.QueryBuilder.JoinQueryInfos.Add(joinInfo); + result.QueryBuilder.LambdaExpressions.ParameterIndex = this.QueryBuilder.LambdaExpressions.ParameterIndex; + return result; + } + public ISugarQueryable LeftJoinIF(bool isJoin, ISugarQueryable joinQueryable, Expression> joinExpression) + { + var result = LeftJoin(joinQueryable, joinExpression); + if (isJoin == false) + { + result.QueryBuilder.JoinQueryInfos.Remove(result.QueryBuilder.JoinQueryInfos.Last()); + } + return result; + } + public ISugarQueryable InnerJoinIF(bool isJoin, ISugarQueryable joinQueryable, Expression> joinExpression) + { + var result = InnerJoin(joinQueryable, joinExpression); + if (isJoin == false) + { + result.QueryBuilder.JoinQueryInfos.Remove(result.QueryBuilder.JoinQueryInfos.Last()); + } + return result; + } + public ISugarQueryable LeftJoin(Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + result.QueryBuilder.JoinQueryInfos.Add(GetJoinInfo(joinExpression, JoinType.Left)); + return result; + } + public ISugarQueryable FullJoin(Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + result.QueryBuilder.JoinQueryInfos.Add(GetJoinInfo(joinExpression, JoinType.Full)); + return result; + } + public ISugarQueryable RightJoin(Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + result.QueryBuilder.JoinQueryInfos.Add(GetJoinInfo(joinExpression, JoinType.Right)); + return result; + } + + public ISugarQueryable InnerJoin(Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + result.QueryBuilder.JoinQueryInfos.Add(GetJoinInfo(joinExpression, JoinType.Inner)); + return result; + } + + public ISugarQueryable LeftJoin(Expression> joinExpression, string tableName) + { + var result = LeftJoin(joinExpression); + result.QueryBuilder.JoinQueryInfos.Last().TableName = tableName; + return result; + } + public ISugarQueryable FullJoin(Expression> joinExpression, string tableName) + { + var result = FullJoin(joinExpression); + result.QueryBuilder.JoinQueryInfos.Last().TableName = tableName; + return result; + } + public ISugarQueryable InnerJoin(Expression> joinExpression, string tableName) + { + var result = InnerJoin(joinExpression); + result.QueryBuilder.JoinQueryInfos.Last().TableName = tableName; + return result; + } + public ISugarQueryable RightJoin(Expression> joinExpression, string tableName) + { + var result = RightJoin(joinExpression); + result.QueryBuilder.JoinQueryInfos.Last().TableName = tableName; + return result; + } + + #region Where + public new ISugarQueryable Where(string expShortName, FormattableString expressionString) + { + var exp = DynamicCoreHelper.GetWhere(expShortName, expressionString); + _Where(exp); + return this; + } + public new ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public new ISugarQueryable Where(List conditionalModels) + { + base.Where(conditionalModels); + return this; + } + public new ISugarQueryable Where(List conditionalModels, bool isWrap) + { + base.Where(conditionalModels, isWrap); + return this; + } + public new ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + public new ISugarQueryable Where(string whereString, object whereObj) + { + Where(whereString, whereObj); + return this; + } + + public new ISugarQueryable WhereIF(bool isWhere, string whereString, object whereObj) + { + if (!isWhere) return this; + this.Where(whereString, whereObj); + return this; + } + #endregion + + #region Select + public ISugarQueryable Select(Expression> expression, bool isAutoFill) + { + var clone = this.Select(expression).Clone(); + var sql = clone.QueryBuilder.GetSelectValue; + if (this.QueryBuilder.IsSingle() || string.IsNullOrEmpty(sql) || sql.Trim() == "*") + { + sql = " "; + } + this.QueryBuilder.Parameters = clone.QueryBuilder.Parameters; + this.QueryBuilder.SubToListParameters = clone.QueryBuilder.SubToListParameters; + this.QueryBuilder.LambdaExpressions.ParameterIndex = clone.QueryBuilder.LambdaExpressions.ParameterIndex; + var parameters = (expression as LambdaExpression).Parameters; + var columnsResult = this.Context.EntityMaintenance.GetEntityInfo().Columns; + sql = AppendSelect(sql, parameters, columnsResult, 0); + sql = AppendSelect(sql, parameters, columnsResult, 1); + sql = AppendSelect(sql, parameters, columnsResult, 2); + sql = AppendSelect(sql, parameters, columnsResult, 3); + sql = AppendSelect(sql, parameters, columnsResult, 4); + sql = AppendSelect(sql, parameters, columnsResult, 5); + sql = AppendSelect(sql, parameters, columnsResult, 6); + sql = AppendSelect(sql, parameters, columnsResult, 7); + sql = AppendSelect(sql, parameters, columnsResult, 8); + sql = AppendSelect(sql, parameters, columnsResult, 9); + if (sql.Trim().First() == ',') + { + sql = sql.TrimStart(' ').TrimStart(','); + } + return this.Select(sql); + } + + public virtual ISugarQueryable SelectIF(bool condition, Expression> trueSelectExpression, Expression> falseSelectExpression) + { + if (condition) + return Select(trueSelectExpression); + else + return Select(falseSelectExpression); + } + public ISugarQueryable Select(Expression> expression) + { + if (IsAppendNavColumns()) + { + SetAppendNavColumns(expression); + } + return _Select(expression); + } + public ISugarQueryable Select(Expression> expression) + { + if (IsAppendNavColumns()) + { + SetAppendNavColumns(expression); + } + return _Select(expression); + } + public ISugarQueryable Select(Expression> expression) + { + if (IsAppendNavColumns()) + { + SetAppendNavColumns(expression); + } + return _Select(expression); + } + public ISugarQueryable Select(Expression> expression) + { + if (IsAppendNavColumns()) + { + SetAppendNavColumns(expression); + } + return _Select(expression); + } + public ISugarQueryable Select(Expression> expression) + { + if (IsAppendNavColumns()) + { + SetAppendNavColumns(expression); + } + return _Select(expression); + } + public ISugarQueryable Select(Expression> expression) + { + if (IsAppendNavColumns()) + { + SetAppendNavColumns(expression); + } + return _Select(expression); + } + public ISugarQueryable Select(Expression> expression) + { + if (IsAppendNavColumns()) + { + SetAppendNavColumns(expression); + } + return _Select(expression); + } + public ISugarQueryable Select(Expression> expression) + { + if (IsAppendNavColumns()) + { + SetAppendNavColumns(expression); + } + return _Select(expression); + } + public ISugarQueryable Select(Expression> expression) + { + if (IsAppendNavColumns()) + { + SetAppendNavColumns(expression); + } + return _Select(expression); + } + #endregion + + #region OrderBy + public new virtual ISugarQueryable OrderByDescending(Expression> expression) + { + this._OrderBy(expression, OrderByType.Desc); + return this; + } + public virtual ISugarQueryable OrderByDescending(Expression> expression) + { + this._OrderBy(expression, OrderByType.Desc); + return this; + } + public virtual ISugarQueryable OrderByDescending(Expression> expression) + { + this._OrderBy(expression, OrderByType.Desc); + return this; + } + public virtual ISugarQueryable OrderByDescending(Expression> expression) + { + this._OrderBy(expression, OrderByType.Desc); + return this; + } + public virtual ISugarQueryable OrderByDescending(Expression> expression) + { + this._OrderBy(expression, OrderByType.Desc); + return this; + } + public virtual ISugarQueryable OrderByDescending(Expression> expression) + { + this._OrderBy(expression, OrderByType.Desc); + return this; + } + public virtual ISugarQueryable OrderByDescending(Expression> expression) + { + this._OrderBy(expression, OrderByType.Desc); + return this; + } + public virtual ISugarQueryable OrderByDescending(Expression> expression) + { + this._OrderBy(expression, OrderByType.Desc); + return this; + } + public virtual ISugarQueryable OrderByDescending(Expression> expression) + { + this._OrderBy(expression, OrderByType.Desc); + return this; + } + public virtual ISugarQueryable OrderByDescending(Expression> expression) + { + this._OrderBy(expression, OrderByType.Desc); + return this; + } + public new ISugarQueryable OrderBy(List models) + { + base.OrderBy(models); + return this; + } + public new ISugarQueryable OrderBy(string orderByFields) + { + base.OrderBy(orderByFields); + return this; + } + public new ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + + public new ISugarQueryable OrderByIF(bool isOrderBy, string orderByFields) + { + if (isOrderBy) + base.OrderBy(orderByFields); + return this; + } + public new ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + #endregion + + #region GroupBy + public new ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + #endregion + + #region Aggr + public TResult Max(Expression> expression) + { + return _Max(expression); + } + public TResult Min(Expression> expression) + { + return _Min(expression); + } + public TResult Sum(Expression> expression) + { + return _Sum(expression); + } + public TResult Avg(Expression> expression) + { + return _Avg(expression); + } + #endregion + + #region In + public new ISugarQueryable In(Expression> expression, params FieldType[] inValues) + { + var isSingle = QueryBuilder.IsSingle(); + var lamResult = QueryBuilder.GetExpressionValue(expression, isSingle ? ResolveExpressType.FieldSingle : ResolveExpressType.FieldMultiple); + var fieldName = lamResult.GetResultString(); + In(fieldName, inValues); + return this; + } + public new ISugarQueryable In(Expression> expression, List inValues) + { + var isSingle = QueryBuilder.IsSingle(); + var lamResult = QueryBuilder.GetExpressionValue(expression, isSingle ? ResolveExpressType.FieldSingle : ResolveExpressType.FieldMultiple); + var fieldName = lamResult.GetResultString(); + In(fieldName, inValues); + return this; + } + public new ISugarQueryable In(Expression> expression, ISugarQueryable childQueryExpression) + { + var sqlObj = childQueryExpression.ToSql(); + _InQueryable(expression, sqlObj); + return this; + } + #endregion + + #region Other + public new ISugarQueryable Distinct() + { + QueryBuilder.IsDistinct = true; + return this; + } + public new ISugarQueryable Take(int num) + { + QueryBuilder.Take = num; + return this; + } + public new ISugarQueryable Clone() + { + var queryable = this.Context.Queryable((t, t2, t3, t4, t5, T6, t7, t8, t9, t10) => Array.Empty()).WithCacheIF(IsCache, CacheTime); + base.CopyQueryBuilder(queryable.QueryBuilder); + return queryable; + } + public new ISugarQueryable AS(string tableName) + { + var entityName = typeof(AsT).Name; + _As(tableName, entityName); + return this; + } + public new ISugarQueryable AS(string tableName) + { + var entityName = typeof(T).Name; + _As(tableName, entityName); + return this; + } + public new ISugarQueryable ClearFilter() + { + this.Filter(null, true); + return this; + } + public new ISugarQueryable Filter(string FilterName, bool isDisabledGobalFilter = false) + { + _Filter(FilterName, isDisabledGobalFilter); + return this; + } + + public new ISugarQueryable ClearFilter(params Type[] types) + { + base.ClearFilter(types); + return this; + } + public new ISugarQueryable ClearFilter() + { + this.ClearFilter(typeof(FilterType1)); + return this; + } + public new ISugarQueryable ClearFilter() + { + this.ClearFilter(typeof(FilterType1), typeof(FilterType2)); + return this; + } + public new ISugarQueryable ClearFilter() + { + this.ClearFilter(typeof(FilterType1), typeof(FilterType2), typeof(FilterType3)); + return this; + } + + public new ISugarQueryable AddParameters(object parameters) + { + if (parameters != null) + QueryBuilder.Parameters.AddRange(Context.Ado.GetParameters(parameters)); + return this; + } + public new ISugarQueryable AddParameters(SugarParameter[] parameters) + { + if (parameters != null) + QueryBuilder.Parameters.AddRange(parameters); + return this; + } + public new ISugarQueryable AddParameters(List parameters) + { + if (parameters != null) + QueryBuilder.Parameters.AddRange(parameters); + return this; + } + public new ISugarQueryable AddJoinInfo(string tableName, string shortName, string joinWhere, JoinType type = JoinType.Left) + { + QueryBuilder.JoinIndex = +1; + QueryBuilder.JoinQueryInfos + .Add(new JoinQueryInfo() + { + JoinIndex = QueryBuilder.JoinIndex, + TableName = tableName, + ShortName = shortName, + JoinType = type, + JoinWhere = joinWhere + }); + return this; + } + public new ISugarQueryable With(string withString) + { + base.With(withString); + return this; + } + public new ISugarQueryable WithCache(int cacheDurationInSeconds = int.MaxValue) + { + cacheDurationInSeconds = SetCacheTime(cacheDurationInSeconds); + this.IsCache = true; + this.CacheTime = cacheDurationInSeconds; + return this; + } + public new ISugarQueryable WithCacheIF(bool isCache, int cacheDurationInSeconds = int.MaxValue) + { + cacheDurationInSeconds = SetCacheTime(cacheDurationInSeconds); + if (IsCache) + { + this.IsCache = true; + this.CacheTime = cacheDurationInSeconds; + } + return this; + } + #endregion + } + #endregion +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/QueryableProvider11-12.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/QueryableProvider11-12.cs new file mode 100644 index 000000000..4eac1034c --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/QueryableProvider/QueryableProvider11-12.cs @@ -0,0 +1,1436 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + #region T11 + public partial class QueryableProvider : QueryableProvider, ISugarQueryable + { + public new ISugarQueryable Hints(string hints) + { + this.QueryBuilder.Hints = hints; + return this; + } + public new ISugarQueryable OrderByPropertyNameIF(bool isOrderBy, string orderPropertyName, OrderByType? orderByType = null) + { + if (isOrderBy) + { + return this.OrderByPropertyName(orderPropertyName, orderByType); + } + return this; + } + public new ISugarQueryable OrderByPropertyName(string orderPropertyName, OrderByType? orderByType = null) + { + base.OrderByPropertyName(orderPropertyName, orderByType); + return this; + } + public virtual ISugarQueryable SelectMergeTable(Expression> expression) + { + return this.Select(expression).MergeTable(); + } + public ISugarQueryable LeftJoinIF(bool isLeftJoin, Expression> joinExpression) + { + var result = LeftJoin(joinExpression); + if (isLeftJoin == false) + { + result.QueryBuilder.JoinQueryInfos.Remove(result.QueryBuilder.JoinQueryInfos.Last()); + } + return result; + } + public ISugarQueryable InnerJoinIF(bool isJoin, Expression> joinExpression) + { + var result = InnerJoin(joinExpression); + if (isJoin == false) + { + result.QueryBuilder.JoinQueryInfos.Remove(result.QueryBuilder.JoinQueryInfos.Last()); + } + return result; + } + public ISugarQueryable LeftJoin(ISugarQueryable joinQueryable, Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + var joinInfo = GetJoinInfo(joinExpression, JoinType.Left); + var sqlObject = joinQueryable.ToSql(); + string sql = sqlObject.Key; + this.QueryBuilder.LambdaExpressions.ParameterIndex += 100; + UtilMethods.RepairReplicationParameters(ref sql, sqlObject.Value.ToArray(), this.QueryBuilder.LambdaExpressions.ParameterIndex, ""); + joinInfo.TableName = "(" + sql + ")"; + this.QueryBuilder.Parameters.AddRange(sqlObject.Value); + result.QueryBuilder.JoinQueryInfos.Add(joinInfo); + result.QueryBuilder.LambdaExpressions.ParameterIndex = this.QueryBuilder.LambdaExpressions.ParameterIndex; + return result; + } + public ISugarQueryable InnerJoin(ISugarQueryable joinQueryable, Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + var joinInfo = GetJoinInfo(joinExpression, JoinType.Inner); + var sqlObject = joinQueryable.ToSql(); + string sql = sqlObject.Key; + this.QueryBuilder.LambdaExpressions.ParameterIndex += 100; + UtilMethods.RepairReplicationParameters(ref sql, sqlObject.Value.ToArray(), this.QueryBuilder.LambdaExpressions.ParameterIndex, ""); + joinInfo.TableName = "(" + sql + ")"; + this.QueryBuilder.Parameters.AddRange(sqlObject.Value); + result.QueryBuilder.JoinQueryInfos.Add(joinInfo); + result.QueryBuilder.LambdaExpressions.ParameterIndex = this.QueryBuilder.LambdaExpressions.ParameterIndex; + return result; + } + public ISugarQueryable RightJoin(ISugarQueryable joinQueryable, Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + var joinInfo = GetJoinInfo(joinExpression, JoinType.Right); + var sqlObject = joinQueryable.ToSql(); + string sql = sqlObject.Key; + this.QueryBuilder.LambdaExpressions.ParameterIndex += 100; + UtilMethods.RepairReplicationParameters(ref sql, sqlObject.Value.ToArray(), this.QueryBuilder.LambdaExpressions.ParameterIndex, ""); + joinInfo.TableName = "(" + sql + ")"; + this.QueryBuilder.Parameters.AddRange(sqlObject.Value); + result.QueryBuilder.JoinQueryInfos.Add(joinInfo); + result.QueryBuilder.LambdaExpressions.ParameterIndex = this.QueryBuilder.LambdaExpressions.ParameterIndex; + return result; + } + public ISugarQueryable LeftJoinIF(bool isJoin, ISugarQueryable joinQueryable, Expression> joinExpression) + { + var result = LeftJoin(joinQueryable, joinExpression); + if (isJoin == false) + { + result.QueryBuilder.JoinQueryInfos.Remove(result.QueryBuilder.JoinQueryInfos.Last()); + } + return result; + } + public ISugarQueryable InnerJoinIF(bool isJoin, ISugarQueryable joinQueryable, Expression> joinExpression) + { + var result = InnerJoin(joinQueryable, joinExpression); + if (isJoin == false) + { + result.QueryBuilder.JoinQueryInfos.Remove(result.QueryBuilder.JoinQueryInfos.Last()); + } + return result; + } + public ISugarQueryable LeftJoin(Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + result.QueryBuilder.JoinQueryInfos.Add(GetJoinInfo(joinExpression, JoinType.Left)); + return result; + } + public ISugarQueryable FullJoin(Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + result.QueryBuilder.JoinQueryInfos.Add(GetJoinInfo(joinExpression, JoinType.Full)); + return result; + } + public ISugarQueryable RightJoin(Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + result.QueryBuilder.JoinQueryInfos.Add(GetJoinInfo(joinExpression, JoinType.Right)); + return result; + } + + public ISugarQueryable InnerJoin(Expression> joinExpression) + { + this.Context.InitMappingInfo(); + var result = InstanceFactory.GetQueryable(this.Context.CurrentConnectionConfig); + result.SqlBuilder = this.SqlBuilder; + result.Context = this.Context; + result.QueryBuilder.JoinQueryInfos.Add(GetJoinInfo(joinExpression, JoinType.Inner)); + return result; + } + + public ISugarQueryable LeftJoin(Expression> joinExpression, string tableName) + { + var result = LeftJoin(joinExpression); + result.QueryBuilder.JoinQueryInfos.Last().TableName = tableName; + return result; + } + public ISugarQueryable FullJoin(Expression> joinExpression, string tableName) + { + var result = FullJoin(joinExpression); + result.QueryBuilder.JoinQueryInfos.Last().TableName = tableName; + return result; + } + public ISugarQueryable InnerJoin(Expression> joinExpression, string tableName) + { + var result = InnerJoin(joinExpression); + result.QueryBuilder.JoinQueryInfos.Last().TableName = tableName; + return result; + } + public ISugarQueryable RightJoin(Expression> joinExpression, string tableName) + { + var result = RightJoin(joinExpression); + result.QueryBuilder.JoinQueryInfos.Last().TableName = tableName; + return result; + } + + #region Where + public new ISugarQueryable Where(string expShortName, FormattableString expressionString) + { + var exp = DynamicCoreHelper.GetWhere(expShortName, expressionString); + _Where(exp); + return this; + } + public new ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public new ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + public new ISugarQueryable Where(List conditionalModels) + { + base.Where(conditionalModels); + return this; + } + public new ISugarQueryable Where(List conditionalModels, bool isWrap) + { + base.Where(conditionalModels, isWrap); + return this; + } + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + public new ISugarQueryable Where(string whereString, object whereObj) + { + Where(whereString, whereObj); + return this; + } + + public new ISugarQueryable WhereIF(bool isWhere, string whereString, object whereObj) + { + if (!isWhere) return this; + this.Where(whereString, whereObj); + return this; + } + #endregion + + #region Select + public virtual ISugarQueryable SelectIF(bool condition, Expression> trueSelectExpression, Expression> falseSelectExpression) + { + if (condition) + return Select(trueSelectExpression); + else + return Select(falseSelectExpression); + } + public ISugarQueryable Select(Expression> expression, bool isAutoFill) + { + var clone = this.Select(expression).Clone(); + var sql = clone.QueryBuilder.GetSelectValue; + if (this.QueryBuilder.IsSingle() || string.IsNullOrEmpty(sql) || sql.Trim() == "*") + { + sql = " "; + } + this.QueryBuilder.Parameters = clone.QueryBuilder.Parameters; + this.QueryBuilder.SubToListParameters = clone.QueryBuilder.SubToListParameters; + this.QueryBuilder.LambdaExpressions.ParameterIndex = clone.QueryBuilder.LambdaExpressions.ParameterIndex; + var parameters = (expression as LambdaExpression).Parameters; + var columnsResult = this.Context.EntityMaintenance.GetEntityInfo().Columns; + sql = AppendSelect(sql, parameters, columnsResult, 0); + sql = AppendSelect(sql, parameters, columnsResult, 1); + sql = AppendSelect(sql, parameters, columnsResult, 2); + sql = AppendSelect(sql, parameters, columnsResult, 3); + sql = AppendSelect(sql, parameters, columnsResult, 4); + sql = AppendSelect(sql, parameters, columnsResult, 5); + sql = AppendSelect(sql, parameters, columnsResult, 6); + sql = AppendSelect(sql, parameters, columnsResult, 7); + sql = AppendSelect(sql, parameters, columnsResult, 8); + sql = AppendSelect(sql, parameters, columnsResult, 9); + sql = AppendSelect(sql, parameters, columnsResult, 10); + if (sql.Trim().First() == ',') + { + sql = sql.TrimStart(' ').TrimStart(','); + } + return this.Select(sql); + } + public ISugarQueryable Select(Expression> expression) + { + return _Select(expression); + } + public ISugarQueryable Select(Expression> expression) + { + return _Select(expression); + } + public ISugarQueryable Select(Expression> expression) + { + return _Select(expression); + } + public ISugarQueryable Select(Expression> expression) + { + return _Select(expression); + } + public ISugarQueryable Select(Expression> expression) + { + return _Select(expression); + } + public ISugarQueryable Select(Expression> expression) + { + return _Select(expression); + } + public ISugarQueryable Select(Expression> expression) + { + return _Select(expression); + } + public ISugarQueryable Select(Expression> expression) + { + return _Select(expression); + } + public ISugarQueryable Select(Expression> expression) + { + return _Select(expression); + } + public ISugarQueryable Select(Expression> expression) + { + return _Select(expression); + } + #endregion + + #region OrderBy + public new ISugarQueryable OrderBy(List models) + { + base.OrderBy(models); + return this; + } + public new ISugarQueryable OrderBy(string orderByFields) + { + base.OrderBy(orderByFields); + return this; + } + public new ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + + public new ISugarQueryable OrderByIF(bool isOrderBy, string orderByFields) + { + if (isOrderBy) + base.OrderBy(orderByFields); + return this; + } + public new ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + #endregion + + #region GroupBy + public new ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + #endregion + + #region Aggr + public TResult Max(Expression> expression) + { + return _Max(expression); + } + public TResult Min(Expression> expression) + { + return _Min(expression); + } + public TResult Sum(Expression> expression) + { + return _Sum(expression); + } + public TResult Avg(Expression> expression) + { + return _Avg(expression); + } + #endregion + + #region In + public new ISugarQueryable In(Expression> expression, params FieldType[] inValues) + { + var isSingle = QueryBuilder.IsSingle(); + var lamResult = QueryBuilder.GetExpressionValue(expression, isSingle ? ResolveExpressType.FieldSingle : ResolveExpressType.FieldMultiple); + var fieldName = lamResult.GetResultString(); + In(fieldName, inValues); + return this; + } + public new ISugarQueryable In(Expression> expression, List inValues) + { + var isSingle = QueryBuilder.IsSingle(); + var lamResult = QueryBuilder.GetExpressionValue(expression, isSingle ? ResolveExpressType.FieldSingle : ResolveExpressType.FieldMultiple); + var fieldName = lamResult.GetResultString(); + In(fieldName, inValues); + return this; + } + public new ISugarQueryable In(Expression> expression, ISugarQueryable childQueryExpression) + { + var sqlObj = childQueryExpression.ToSql(); + _InQueryable(expression, sqlObj); + return this; + } + #endregion + + #region Other + public new ISugarQueryable Distinct() + { + QueryBuilder.IsDistinct = true; + return this; + } + public new ISugarQueryable Take(int num) + { + QueryBuilder.Take = num; + return this; + } + public new ISugarQueryable Clone() + { + var queryable = this.Context.Queryable((t, t2, t3, t4, t5, T6, t7, t8, t9, t10, t11) => Array.Empty()).WithCacheIF(IsCache, CacheTime); + base.CopyQueryBuilder(queryable.QueryBuilder); + return queryable; + } + public new ISugarQueryable AS(string tableName) + { + var entityName = typeof(AsT).Name; + _As(tableName, entityName); + return this; + } + public new ISugarQueryable AS(string tableName) + { + var entityName = typeof(T).Name; + _As(tableName, entityName); + return this; + } + public new ISugarQueryable ClearFilter() + { + this.Filter(null, true); + return this; + } + public new ISugarQueryable Filter(string FilterName, bool isDisabledGobalFilter = false) + { + _Filter(FilterName, isDisabledGobalFilter); + return this; + } + + + public new ISugarQueryable ClearFilter(params Type[] types) + { + base.ClearFilter(types); + return this; + } + public new ISugarQueryable ClearFilter() + { + this.ClearFilter(typeof(FilterType1)); + return this; + } + public new ISugarQueryable ClearFilter() + { + this.ClearFilter(typeof(FilterType1), typeof(FilterType2)); + return this; + } + public new ISugarQueryable ClearFilter() + { + this.ClearFilter(typeof(FilterType1), typeof(FilterType2), typeof(FilterType3)); + return this; + } + + public new ISugarQueryable AddParameters(object parameters) + { + if (parameters != null) + QueryBuilder.Parameters.AddRange(Context.Ado.GetParameters(parameters)); + return this; + } + public new ISugarQueryable AddParameters(SugarParameter[] parameters) + { + if (parameters != null) + QueryBuilder.Parameters.AddRange(parameters); + return this; + } + public new ISugarQueryable AddParameters(List parameters) + { + if (parameters != null) + QueryBuilder.Parameters.AddRange(parameters); + return this; + } + public new ISugarQueryable AddJoinInfo(string tableName, string shortName, string joinWhere, JoinType type = JoinType.Left) + { + QueryBuilder.JoinIndex = +1; + QueryBuilder.JoinQueryInfos + .Add(new JoinQueryInfo() + { + JoinIndex = QueryBuilder.JoinIndex, + TableName = tableName, + ShortName = shortName, + JoinType = type, + JoinWhere = joinWhere + }); + return this; + } + public new ISugarQueryable With(string withString) + { + base.With(withString); + return this; + } + public new ISugarQueryable WithCache(int cacheDurationInSeconds = int.MaxValue) + { + cacheDurationInSeconds = SetCacheTime(cacheDurationInSeconds); + this.IsCache = true; + this.CacheTime = cacheDurationInSeconds; + return this; + } + public new ISugarQueryable WithCacheIF(bool isCache, int cacheDurationInSeconds = int.MaxValue) + { + cacheDurationInSeconds = SetCacheTime(cacheDurationInSeconds); + if (IsCache) + { + this.IsCache = true; + this.CacheTime = cacheDurationInSeconds; + } + return this; + } + #endregion + } + #endregion + #region T12 + public partial class QueryableProvider : QueryableProvider, ISugarQueryable + { + public new ISugarQueryable Hints(string hints) + { + this.QueryBuilder.Hints = hints; + return this; + } + public new ISugarQueryable OrderByPropertyNameIF(bool isOrderBy, string orderPropertyName, OrderByType? orderByType = null) + { + if (isOrderBy) + { + return this.OrderByPropertyName(orderPropertyName, orderByType); + } + return this; + } + public new ISugarQueryable OrderByPropertyName(string orderPropertyName, OrderByType? orderByType = null) + { + base.OrderByPropertyName(orderPropertyName, orderByType); + return this; + } + public virtual ISugarQueryable SelectMergeTable(Expression> expression) + { + return this.Select(expression).MergeTable(); + } + + #region Where + public new ISugarQueryable Where(string expShortName, FormattableString expressionString) + { + var exp = DynamicCoreHelper.GetWhere(expShortName, expressionString); + _Where(exp); + return this; + } + public new ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public ISugarQueryable Where(Expression> expression) + { + _Where(expression); + return this; + } + public new ISugarQueryable Where(List conditionalModels) + { + base.Where(conditionalModels); + return this; + } + public new ISugarQueryable Where(List conditionalModels, bool isWrap) + { + base.Where(conditionalModels, isWrap); + return this; + } + public new ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + public ISugarQueryable WhereIF(bool isWhere, Expression> expression) + { + if (isWhere) + _Where(expression); + return this; + } + public new ISugarQueryable Where(string whereString, object whereObj) + { + Where(whereString, whereObj); + return this; + } + + public new ISugarQueryable WhereIF(bool isWhere, string whereString, object whereObj) + { + if (!isWhere) return this; + this.Where(whereString, whereObj); + return this; + } + #endregion + + #region Select + public ISugarQueryable Select(Expression> expression, bool isAutoFill) + { + var clone = this.Select(expression).Clone(); + var sql = clone.QueryBuilder.GetSelectValue; + if (this.QueryBuilder.IsSingle() || string.IsNullOrEmpty(sql) || sql.Trim() == "*") + { + sql = " "; + } + this.QueryBuilder.Parameters = clone.QueryBuilder.Parameters; + this.QueryBuilder.SubToListParameters = clone.QueryBuilder.SubToListParameters; + this.QueryBuilder.LambdaExpressions.ParameterIndex = clone.QueryBuilder.LambdaExpressions.ParameterIndex; + var parameters = (expression as LambdaExpression).Parameters; + var columnsResult = this.Context.EntityMaintenance.GetEntityInfo().Columns; + sql = AppendSelect(sql, parameters, columnsResult, 0); + sql = AppendSelect(sql, parameters, columnsResult, 1); + sql = AppendSelect(sql, parameters, columnsResult, 2); + sql = AppendSelect(sql, parameters, columnsResult, 3); + sql = AppendSelect(sql, parameters, columnsResult, 4); + sql = AppendSelect(sql, parameters, columnsResult, 5); + sql = AppendSelect(sql, parameters, columnsResult, 6); + sql = AppendSelect(sql, parameters, columnsResult, 7); + sql = AppendSelect(sql, parameters, columnsResult, 8); + sql = AppendSelect(sql, parameters, columnsResult, 9); + sql = AppendSelect(sql, parameters, columnsResult, 10); + sql = AppendSelect(sql, parameters, columnsResult, 11); + if (sql.Trim().First() == ',') + { + sql = sql.TrimStart(' ').TrimStart(','); + } + return this.Select(sql); + } + public ISugarQueryable Select(Expression> expression) + { + return _Select(expression); + } + public ISugarQueryable Select(Expression> expression) + { + return _Select(expression); + } + public ISugarQueryable Select(Expression> expression) + { + return _Select(expression); + } + public ISugarQueryable Select(Expression> expression) + { + return _Select(expression); + } + public ISugarQueryable Select(Expression> expression) + { + return _Select(expression); + } + public ISugarQueryable Select(Expression> expression) + { + return _Select(expression); + } + public ISugarQueryable Select(Expression> expression) + { + return _Select(expression); + } + public ISugarQueryable Select(Expression> expression) + { + return _Select(expression); + } + public ISugarQueryable Select(Expression> expression) + { + return _Select(expression); + } + public ISugarQueryable Select(Expression> expression) + { + return _Select(expression); + } + public ISugarQueryable Select(Expression> expression) + { + return _Select(expression); + } + public virtual ISugarQueryable SelectIF(bool condition, Expression> trueSelectExpression, Expression> falseSelectExpression) + { + if (condition) + return Select(trueSelectExpression); + else + return Select(falseSelectExpression); + } + #endregion + + #region OrderBy + public new ISugarQueryable OrderBy(List models) + { + base.OrderBy(models); + return this; + } + public new ISugarQueryable OrderBy(string orderByFields) + { + base.OrderBy(orderByFields); + return this; + } + public new ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc) + { + _OrderBy(expression, type); + return this; + } + public new ISugarQueryable OrderByIF(bool isOrderBy, string orderByFields) + { + if (isOrderBy) + base.OrderBy(orderByFields); + return this; + } + public new ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + public ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc) + { + if (isOrderBy) + _OrderBy(expression, type); + return this; + } + #endregion + + #region GroupBy + public new ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + public ISugarQueryable GroupBy(Expression> expression) + { + _GroupBy(expression); + return this; + } + #endregion + + #region Aggr + public TResult Max(Expression> expression) + { + return _Max(expression); + } + public TResult Min(Expression> expression) + { + return _Min(expression); + } + public TResult Sum(Expression> expression) + { + return _Sum(expression); + } + public TResult Avg(Expression> expression) + { + return _Avg(expression); + } + #endregion + + #region In + public new ISugarQueryable In(Expression> expression, params FieldType[] inValues) + { + var isSingle = QueryBuilder.IsSingle(); + var lamResult = QueryBuilder.GetExpressionValue(expression, isSingle ? ResolveExpressType.FieldSingle : ResolveExpressType.FieldMultiple); + var fieldName = lamResult.GetResultString(); + In(fieldName, inValues); + return this; + } + public new ISugarQueryable In(Expression> expression, List inValues) + { + var isSingle = QueryBuilder.IsSingle(); + var lamResult = QueryBuilder.GetExpressionValue(expression, isSingle ? ResolveExpressType.FieldSingle : ResolveExpressType.FieldMultiple); + var fieldName = lamResult.GetResultString(); + In(fieldName, inValues); + return this; + } + public new ISugarQueryable In(Expression> expression, ISugarQueryable childQueryExpression) + { + var sqlObj = childQueryExpression.ToSql(); + _InQueryable(expression, sqlObj); + return this; + } + #endregion + + #region Other + public new ISugarQueryable Distinct() + { + QueryBuilder.IsDistinct = true; + return this; + } + public new ISugarQueryable Take(int num) + { + QueryBuilder.Take = num; + return this; + } + public new ISugarQueryable Clone() + { + var queryable = this.Context.Queryable((t, t2, t3, t4, t5, T6, t7, t8, t9, t10, t11, t12) => Array.Empty()).WithCacheIF(IsCache, CacheTime); + base.CopyQueryBuilder(queryable.QueryBuilder); + return queryable; + } + public new ISugarQueryable AS(string tableName) + { + var entityName = typeof(AsT).Name; + _As(tableName, entityName); + return this; + } + public new ISugarQueryable AS(string tableName) + { + var entityName = typeof(T).Name; + _As(tableName, entityName); + return this; + } + public new ISugarQueryable ClearFilter() + { + this.Filter(null, true); + return this; + } + public new ISugarQueryable Filter(string FilterName, bool isDisabledGobalFilter = false) + { + _Filter(FilterName, isDisabledGobalFilter); + return this; + } + + public new ISugarQueryable ClearFilter(params Type[] types) + { + base.ClearFilter(types); + return this; + } + public new ISugarQueryable ClearFilter() + { + this.ClearFilter(typeof(FilterType1)); + return this; + } + public new ISugarQueryable ClearFilter() + { + this.ClearFilter(typeof(FilterType1), typeof(FilterType2)); + return this; + } + public new ISugarQueryable ClearFilter() + { + this.ClearFilter(typeof(FilterType1), typeof(FilterType2), typeof(FilterType3)); + return this; + } + public new ISugarQueryable AddParameters(object parameters) + { + if (parameters != null) + QueryBuilder.Parameters.AddRange(Context.Ado.GetParameters(parameters)); + return this; + } + public new ISugarQueryable AddParameters(SugarParameter[] parameters) + { + if (parameters != null) + QueryBuilder.Parameters.AddRange(parameters); + return this; + } + public new ISugarQueryable AddParameters(List parameters) + { + if (parameters != null) + QueryBuilder.Parameters.AddRange(parameters); + return this; + } + public new ISugarQueryable AddJoinInfo(string tableName, string shortName, string joinWhere, JoinType type = JoinType.Left) + { + QueryBuilder.JoinIndex = +1; + QueryBuilder.JoinQueryInfos + .Add(new JoinQueryInfo() + { + JoinIndex = QueryBuilder.JoinIndex, + TableName = tableName, + ShortName = shortName, + JoinType = type, + JoinWhere = joinWhere + }); + return this; + } + public new ISugarQueryable With(string withString) + { + base.With(withString); + return this; + } + public new ISugarQueryable WithCache(int cacheDurationInSeconds = int.MaxValue) + { + cacheDurationInSeconds = SetCacheTime(cacheDurationInSeconds); + this.IsCache = true; + this.CacheTime = cacheDurationInSeconds; + return this; + } + public new ISugarQueryable WithCacheIF(bool isCache, int cacheDurationInSeconds = int.MaxValue) + { + cacheDurationInSeconds = SetCacheTime(cacheDurationInSeconds); + if (IsCache) + { + this.IsCache = true; + this.CacheTime = cacheDurationInSeconds; + } + return this; + } + #endregion + } + #endregion +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/Reportable/ReportableProvider.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/Reportable/ReportableProvider.cs new file mode 100644 index 000000000..a9db418e5 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/Reportable/ReportableProvider.cs @@ -0,0 +1,285 @@ +using System.Linq.Expressions; +using System.Text; + +namespace SqlSugar +{ + public class ReportableProvider : IReportable + { + public SqlSugarProvider Context { get; set; } + private List datas = new List(); + private List dates = new List(); + private bool isDates = false; + internal QueryBuilder queryBuilder; + internal InsertBuilder formatBuilder { get; set; } + + public ReportableProvider(T data) + { + datas.Add(data); + Init(); + } + + public ReportableProvider(List list) + { + datas = list; + Init(); + } + + //public IReportable MakeUp(Func auto) + //{ + // throw new NotImplementedException(); + //} + + public ISugarQueryable ToQueryable() + { + StringBuilder sb = new StringBuilder(); + if (datas.Count != 0) + { + if (isDates) + { + var da = this.dates; + Each(sb, da); + } + else + { + var da = this.datas; + Each(sb, da); + } + } + else + { + if (typeof(T).IsClass()) + { + + var result = (T)Activator.CreateInstance(typeof(T), true); + datas.Add(result); + ClassMethod(result, sb, true); + } + else + { + sb.Append("SELECT NULL as ColumnName "); + sb.Append(GetNextSql); + } + } + return this.Context.SqlQueryable(sb.ToString()).Select(); + } + public ISugarQueryable> ToQueryable() + { + return ToQueryable().Select>(); + } + + private bool _isOnlySelectEntity = false; + public ISugarQueryable> ToQueryable(bool isOnlySelectEntity) + { + _isOnlySelectEntity = isOnlySelectEntity; + return ToQueryable(); + } + + private void Each(StringBuilder sb, List list) + { + int i = 0; + foreach (var item in list) + { + ++i; + var isLast = i == list.Count; + var isClass = typeof(T).IsClass(); + if (isClass) + { + ClassMethod(item, sb, isLast); + } + else + { + NoClassMethod(item, sb, isLast); + } + } + } + + private void ClassMethod(Y data, StringBuilder sb, bool isLast) + { + var columns = new StringBuilder(); + var entity = this.Context.EntityMaintenance.GetEntityInfo(); + columns.Append(string.Join(",", entity.Columns.Where(it => it.IsIgnore == false).Select(it => GetSelect(it, data)))); + if (_isOnlySelectEntity == false) + { + columns.Append(",null as NoCacheColumn"); + } + sb.AppendLine(" SELECT " + columns.ToString()); + sb.Append(GetNextSql); + if (!isLast) + { + sb.AppendLine(" UNION ALL "); + } + } + + private string GetSelect(EntityColumnInfo it, Y data) + { + + return string.Format(" {0} AS {1} ", FormatValue(it.PropertyInfo.GetValue(data, null), it), queryBuilder.Builder.GetTranslationColumnName(it.PropertyName)); + } + + private void NoClassMethod(Y data, StringBuilder sb, bool isLast) + { + sb.AppendLine(" SELECT " + FormatValue(data)); + sb.Append(" AS " + this.queryBuilder.Builder.GetTranslationColumnName("ColumnName") + " "); + sb.Append(GetNextSql); + if (!isLast) + { + sb.AppendLine(" UNION ALL "); + } + } + public string GetNextSql + { + get + { + var isDual = queryBuilder.Builder.FullSqlDateNow?.ToLower()?.Contains(" dual") == true; + if (isDual) + { + return " from dual "; + } + else + { + return null; + } + } + } + private void Init() + { + if (datas.Count == 1) + { + var data = datas.First(); + isDates = data is ReportableDateType; + if (data is ReportableDateType) + { + var type = UtilMethods.ChangeType2(data, typeof(ReportableDateType)); + switch (type) + { + case ReportableDateType.MonthsInLast1years: + dates.AddRange(GetMonths(1)); + break; + case ReportableDateType.MonthsInLast3years: + dates.AddRange(GetMonths(3)); + break; + case ReportableDateType.MonthsInLast10years: + dates.AddRange(GetMonths(10)); + break; + case ReportableDateType.years1: + dates.AddRange(GetYears(1)); + break; + case ReportableDateType.years3: + dates.AddRange(GetYears(3)); + break; + case ReportableDateType.years10: + dates.AddRange(GetYears(10)); + break; + default: + break; + } + } + } + } + + private List GetYears(int v) + { + List result = new List(); + for (int i = 0; i < v; i++) + { + var year = (DateTime.Now.AddYears(i * -1).Year + "-01" + "-01").ObjToDate(); + result.Add(year); + } + return result; + } + private List GetMonths(int v) + { + List result = new List(); + var years = GetYears(v); + foreach (var item in years) + { + for (int i = 0; i < 12; i++) + { + result.Add(item.AddMonths(i)); + } + } + return result; + } + private object FormatValue(object value, EntityColumnInfo entityColumnInfo = null) + { + if (entityColumnInfo != null && entityColumnInfo.UnderType == UtilConstants.DateType && value == null && this.Context.CurrentConnectionConfig.DbType == DbType.SqlServer) + { + return $" CAST( NULL AS DATETIME) "; + } + else if (entityColumnInfo != null && entityColumnInfo.UnderType == UtilConstants.DateType && value == null && this.Context.CurrentConnectionConfig.DbType == DbType.PostgreSQL) + { + return $" CAST( NULL AS timestamp) "; + } + if (value == null) + return "null"; + var type = UtilMethods.GetUnderType(value.GetType()); + if (type.IsIn(typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(short), typeof(ushort))) + { + return value; + } + else if (type.IsIn(typeof(decimal), typeof(double))) + { + Expression, object>> exp = it => Convert.ToDecimal(it.ColumnName); + var result = queryBuilder.LambdaExpressions.DbMehtods.ToDecimal(new MethodCallExpressionModel() + { + Args = new List() { + new MethodCallExpressionArgs(){ + IsMember=true, + MemberName= formatBuilder.FormatValue(value) + } + } + }); + return result; + } + else if (type.IsIn(typeof(Guid))) + { + Expression, object>> exp = it => Convert.ToDecimal(it.ColumnName); + var result = queryBuilder.LambdaExpressions.DbMehtods.ToGuid(new MethodCallExpressionModel() + { + Args = new List() { + new MethodCallExpressionArgs(){ + IsMember=true, + MemberName= formatBuilder.FormatValue(value) + } + } + }); + return result; + } + else if (type.IsIn(typeof(DateTime)) || type.Name == "DateOnly") + { + if (this.Context.CurrentConnectionConfig.DbType == DbType.Oracle) + { + return queryBuilder.LambdaExpressions.DbMehtods.Oracle_ToDate(new MethodCallExpressionModel() + { + Args = new List() { + new MethodCallExpressionArgs(){ + IsMember=true, + MemberName= value.ObjToDate().ToString("yyyy-MM-dd HH:mm:ss").ToSqlValue() + }, + new MethodCallExpressionArgs(){ + IsMember=true, + MemberName= "yyyy-mm-dd hh24:mi:ss".ToSqlValue() + } + } + }); ; + } + //Expression, object>> exp = it => Convert.ToDecimal(it.ColumnName); + var result = queryBuilder.LambdaExpressions.DbMehtods.ToDate(new MethodCallExpressionModel() + { + Args = new List() { + new MethodCallExpressionArgs(){ + IsMember=true, + MemberName= formatBuilder.FormatValue(value) + } + } + }); + return result; + } + else + { + return formatBuilder.FormatValue(value); + } + } + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SaveableProvider/SaveableProvider.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SaveableProvider/SaveableProvider.cs new file mode 100644 index 000000000..4152ac440 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SaveableProvider/SaveableProvider.cs @@ -0,0 +1,198 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public partial class SaveableProvider : ISaveable where T : class, new() + { + internal SaveableProvider(SqlSugarProvider context, List saveObjects) + { + this.saveObjects = saveObjects; + this.Context = context; + this.Context.InitMappingInfo(); + } + internal SaveableProvider(SqlSugarProvider context, T saveObject) + { + this.saveObjects = new List() { saveObject }; + this.Context = context; + this.Context.InitMappingInfo(); + } + public SqlSugarProvider Context { get; set; } + public List saveObjects = new List(); + public List existsObjects = null; + public List insertObjects + { + get + { + var isDisableMasterSlaveSeparation = this.Context.Ado.IsDisableMasterSlaveSeparation; + this.Context.Ado.IsDisableMasterSlaveSeparation = true; + List result = new List(); + var pks = GetPrimaryKeys(); + Check.Exception(pks.IsNullOrEmpty(), "Need primary key"); + Check.Exception(pks.Count > 1, "Multiple primary keys are not supported"); + var pkInfo = this.EntityInfo.Columns.Where(it => it.IsIgnore == false).Where(it => it.DbColumnName.Equals(pks.First(), StringComparison.CurrentCultureIgnoreCase)).First(); + var pkValues = saveObjects.Select(it => it.GetType().GetProperty(pkInfo.PropertyName).GetValue(it, null)); + if (existsObjects == null) + existsObjects = this.Context.Queryable().In(pkValues).ToList(); + this.Context.Ado.IsDisableMasterSlaveSeparation = isDisableMasterSlaveSeparation; + return saveObjects.Where(it => ! + existsObjects.Any(e => + e.GetType().GetProperty(pkInfo.PropertyName).GetValue(e, null).ObjToString() + == + it.GetType().GetProperty(pkInfo.PropertyName).GetValue(it, null).ObjToString())).ToList(); + } + } + public List updatObjects + { + get + { + var isDisableMasterSlaveSeparation = this.Context.Ado.IsDisableMasterSlaveSeparation; + this.Context.Ado.IsDisableMasterSlaveSeparation = true; + List result = new List(); + var pks = GetPrimaryKeys(); + Check.Exception(pks.IsNullOrEmpty(), "Need primary key"); + Check.Exception(pks.Count > 1, "Multiple primary keys are not supported"); + var pkInfo = this.EntityInfo.Columns.Where(it => it.IsIgnore == false).Where(it => it.DbColumnName.Equals(pks.First(), StringComparison.CurrentCultureIgnoreCase)).First(); + var pkValues = saveObjects.Select(it => it.GetType().GetProperty(pkInfo.PropertyName).GetValue(it, null)); + if (existsObjects == null) + existsObjects = this.Context.Queryable().In(pkValues).ToList(); + this.Context.Ado.IsDisableMasterSlaveSeparation = isDisableMasterSlaveSeparation; + return saveObjects.Where(it => + existsObjects.Any(e => + e.GetType().GetProperty(pkInfo.PropertyName).GetValue(e, null).ObjToString() + == + it.GetType().GetProperty(pkInfo.PropertyName).GetValue(it, null).ObjToString())).ToList(); + } + } + + public IInsertable insertable { get; set; } + public IUpdateable updateable { get; set; } + + public EntityInfo EntityInfo + { + get + { + return this.Context.EntityMaintenance.GetEntityInfo(); + } + } + + #region Core + public int ExecuteCommand() + { + LoadInsertable(); + LoadUpdateable(); + var insertCount = 0; + var updateCount = 0; + if (insertable != null) + { + insertCount = insertable.ExecuteCommand(); + } + if (updateable != null) + { + updateCount = updateable.ExecuteCommand(); + } + return updateCount + insertCount; + } + + public T ExecuteReturnEntity() + { + LoadInsertable(); + LoadUpdateable(); + insertable?.ExecuteCommandIdentityIntoEntity(); + updateable?.ExecuteCommand(); + return saveObjects.First(); + } + + public List ExecuteReturnList() + { + LoadInsertable(); + LoadUpdateable(); + insertable?.ExecuteCommand(); + updateable?.ExecuteCommand(); + return saveObjects; + } + #endregion + #region Core Async + public Task ExecuteCommandAsync() + { + return Task.FromResult(ExecuteCommand()); + } + + public Task ExecuteReturnEntityAsync() + { + return Task.FromResult(ExecuteReturnEntity()); + } + + public Task> ExecuteReturnListAsync() + { + return Task.FromResult(ExecuteReturnList()); + } + #endregion + public ISaveable InsertColumns(Expression> columns) + { + LoadInsertable(); + this.insertable?.InsertColumns(columns); + return this; + } + + public ISaveable EnableDiffLogEvent(object businessData = null) + { + LoadInsertable(); + LoadUpdateable(); + this.insertable?.EnableDiffLogEvent(businessData); + this.updateable?.EnableDiffLogEvent(businessData); + return this; + } + + public ISaveable RemoveDataCache() + { + this.insertable?.RemoveDataCache(); + this.updateable?.RemoveDataCache(); + return this; + } + public ISaveable InsertIgnoreColumns(Expression> columns) + { + LoadInsertable(); + this.insertable?.IgnoreColumns(columns); + return this; + } + + public ISaveable UpdateColumns(Expression> columns) + { + LoadUpdateable(); + this.updateable?.UpdateColumns(columns); + return this; + } + + public ISaveable UpdateIgnoreColumns(Expression> columns) + { + LoadUpdateable(); + this.updateable?.IgnoreColumns(columns); + return this; + } + + public ISaveable UpdateWhereColumns(Expression> columns) + { + LoadUpdateable(); + this.updateable?.WhereColumns(columns); + return this; + } + protected virtual List GetPrimaryKeys() + { + return this.EntityInfo.Columns.Where(it => it.IsPrimarykey).Select(it => it.DbColumnName).ToList(); + } + private void LoadInsertable() + { + var temp = insertObjects; + if (insertable == null && temp.HasValue()) + insertable = this.Context.Insertable(temp); + } + private void LoadUpdateable() + { + var temp = updatObjects; + if (updateable == null && temp.HasValue()) + updateable = this.Context.Updateable(temp); + } + + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SaveableProvider/Storageable.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SaveableProvider/Storageable.cs new file mode 100644 index 000000000..e4cfbb0d6 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SaveableProvider/Storageable.cs @@ -0,0 +1,628 @@ +using System.Linq.Expressions; +using System.Reflection; + +namespace SqlSugar +{ + public class Storageable : IStorageable where T : class, new() + { + SqlSugarProvider Context { get; set; } + internal ISqlBuilder Builder; + List Parameters; + List> allDatas = new List>(); + List dbDataList = new List(); + List, bool>, string>> whereFuncs = new List, bool>, string>>(); + Expression> whereExpression; + Func formatTime; + DbLockType? lockType; + private string asname { get; set; } + private bool isDisableFilters = false; + public Storageable(List datas, SqlSugarProvider context) + { + this.Context = context; + if (datas == null) + datas = new List(); + this.allDatas = datas.Select(it => new StorageableInfo() + { + Item = it + }).ToList(); + } + + Expression> queryableWhereExp; + public IStorageable TableDataRange(Expression> exp) + { + this.queryableWhereExp = exp; + return this; + } + + public IStorageable SplitInsert(Func, bool> conditions, string message = null) + { + whereFuncs.Add(new KeyValuePair, bool>, string>(StorageType.Insert, conditions, message)); + return this; + } + public IStorageable SplitDelete(Func, bool> conditions, string message = null) + { + whereFuncs.Add(new KeyValuePair, bool>, string>(StorageType.Delete, conditions, message)); + return this; + } + public IStorageable SplitUpdate(Func, bool> conditions, string message = null) + { + whereFuncs.Add(new KeyValuePair, bool>, string>(StorageType.Update, conditions, message)); + return this; + } + + public IStorageable Saveable(string inserMessage = null, string updateMessage = null) + { + return this + .SplitUpdate(it => it.Any(), updateMessage) + .SplitInsert(it => true, inserMessage); + } + public IStorageable SplitError(Func, bool> conditions, string message = null) + { + whereFuncs.Add(new KeyValuePair, bool>, string>(StorageType.Error, conditions, message)); + return this; + } + + public IStorageable SplitIgnore(Func, bool> conditions, string message = null) + { + whereFuncs.Add(new KeyValuePair, bool>, string>(StorageType.Ignore, conditions, message)); + return this; + } + + public IStorageable DisableFilters() + { + this.isDisableFilters = true; + return this; + } + + public IStorageable TranLock(DbLockType dbLockType = DbLockType.Wait) + { + this.lockType = dbLockType; + return this; + } + public IStorageable TranLock(DbLockType? LockType) + { + if (LockType != null) + { + this.lockType = LockType; + return this; + } + else + { + return this; + } + } + public IStorageable SplitOther(Func, bool> conditions, string message = null) + { + whereFuncs.Add(new KeyValuePair, bool>, string>(StorageType.Other, conditions, message)); + return this; + } + public StorageablePage PageSize(int PageSize, Action ActionCallBack = null) + { + if (PageSize > 10000) + { + Check.ExceptionEasy("Advanced save page Settings should not exceed 10,000, and the reasonable number of pages is about 2000", "高级保存分页设置不要超过1万,合理分页数在2000左右"); + } + StorageablePage page = new StorageablePage(); + page.Context = this.Context; + page.PageSize = PageSize; + page.Data = this.allDatas.Select(it => it.Item).ToList(); + page.ActionCallBack = ActionCallBack; + page.TableName = this.asname; + page.whereExpression = this.whereExpression; + page.lockType = this.lockType; + return page; + } + public StorageableSplitProvider SplitTable() + { + StorageableSplitProvider result = new StorageableSplitProvider(); + result.Context = this.Context; + result.SaveInfo = this; + result.List = allDatas.Select(it => it.Item).ToList(); + result.EntityInfo = this.Context.EntityMaintenance.GetEntityInfoWithAttr(typeof(T)); + result.whereExpression = this.whereExpression; + return result; + } + public IStorageable DefaultAddElseUpdate() + { + var column = this.Context.EntityMaintenance.GetEntityInfo().Columns.FirstOrDefault(it => it.IsPrimarykey); + if (column == null) Check.ExceptionEasy("DefaultAddElseUpdate() need primary key", "DefaultAddElseUpdate()这个方法只能用于主键"); + return this.SplitUpdate(it => + { + var itemPkValue = column.PropertyInfo.GetValue(it.Item); + var defaultValue = UtilMethods.GetDefaultValue(column.PropertyInfo.PropertyType); + var result = itemPkValue != null && itemPkValue.ObjToString() != defaultValue.ObjToString(); + return result; + + }).SplitInsert(it => true); + } + + public int ExecuteCommand() + { + var result = 0; + var x = this.ToStorage(); + result += x.AsInsertable.ExecuteCommand(); + var updateRow = x.AsUpdateable.ExecuteCommand(); + if (updateRow < 0) updateRow = 0; + result += updateRow; + return result; + } + public T ExecuteReturnEntity() + { + var x = this.ToStorage(); + if (x.InsertList?.Count > 0) + { + var data = x.AsInsertable.ExecuteReturnEntity(); + x.AsUpdateable.ExecuteCommand(); + return data; + } + else + { + x.AsInsertable.ExecuteCommand(); + x.AsUpdateable.ExecuteCommand(); + return x.UpdateList.FirstOrDefault()?.Item; + } + } + public async Task ExecuteReturnEntityAsync() + { + var x = this.ToStorage(); + if (x.InsertList.Count != 0) + { + var data = await x.AsInsertable.ExecuteReturnEntityAsync().ConfigureAwait(false); + await x.AsUpdateable.ExecuteCommandAsync().ConfigureAwait(false); + return data; + } + else + { + await x.AsInsertable.ExecuteCommandAsync().ConfigureAwait(false); + await x.AsUpdateable.ExecuteCommandAsync().ConfigureAwait(false); + return x.UpdateList.FirstOrDefault()?.Item; + } + } + public Task ExecuteCommandAsync(CancellationToken cancellationToken) + { + this.Context.Ado.CancellationToken = cancellationToken; + return ExecuteCommandAsync(); + } + public async Task ExecuteCommandAsync() + { + var result = 0; + var x = await ToStorageAsync().ConfigureAwait(false); + result += await x.AsInsertable.ExecuteCommandAsync().ConfigureAwait(false); + var updateCount = await x.AsUpdateable.ExecuteCommandAsync().ConfigureAwait(false); + if (updateCount < 0) + updateCount = 0; + result += updateCount; + return result; + } + public int ExecuteSqlBulkCopy() + { + var storage = this.ToStorage(); + return storage.BulkCopy() + storage.BulkUpdate(); + } + public async Task ExecuteSqlBulkCopyAsync() + { + var storage = await ToStorageAsync().ConfigureAwait(false); + return await storage.BulkCopyAsync().ConfigureAwait(false) + await storage.BulkUpdateAsync().ConfigureAwait(false); + } + public StorageableResult ToStorage() + { + if (whereFuncs == null || whereFuncs.Count == 0) + { + return this.Saveable().ToStorage(); + } + if (this.allDatas.Count == 0) + return new StorageableResult() + { + AsDeleteable = this.Context.Deleteable().AS(asname).Where(it => false), + AsInsertable = this.Context.Insertable(new List()).AS(asname), + AsUpdateable = this.Context.Updateable(new List()).AS(asname), + InsertList = new List>(), + UpdateList = new List>(), + DeleteList = new List>(), + ErrorList = new List>(), + IgnoreList = new List>(), + OtherList = new List>(), + TotalList = new List>() + }; + var pkInfos = this.Context.EntityMaintenance.GetEntityInfo().Columns.Where(it => it.IsPrimarykey); + if (whereExpression == null && !pkInfos.Any()) + { + Check.ExceptionEasy(true, "Need primary key or WhereColumn", "使用Storageable实体需要主键或者使用WhereColumn指定条件列"); + } + if (whereExpression == null && pkInfos.Any()) + { + this.Context.Utilities.PageEach(allDatas, 300, item => + { + var addItems = this.Context.Queryable().Filter(null, this.isDisableFilters).TranLock(this.lockType).AS(asname).WhereClassByPrimaryKey(item.Select(it => it.Item).ToList()).ToList(); + dbDataList.AddRange(addItems); + }); + } + var pkProperties = GetPkProperties(pkInfos); + var messageList = allDatas.Select(it => new StorageableMessage() + { + Item = it.Item, + Database = dbDataList, + PkFields = pkProperties + }).ToList(); + foreach (var item in whereFuncs.OrderByDescending(it => (int)it.key)) + { + List> whereList = messageList.Where(it => it.StorageType == null).ToList(); + Func, bool> exp = item.value1; + var list = whereList.Where(exp).ToList(); + foreach (var it in list) + { + it.StorageType = item.key; + it.StorageMessage = item.value2; + } + } + var delete = messageList.Where(it => it.StorageType == StorageType.Delete).ToList(); + var update = messageList.Where(it => it.StorageType == StorageType.Update).ToList(); + var inset = messageList.Where(it => it.StorageType == StorageType.Insert).ToList(); + var error = messageList.Where(it => it.StorageType == StorageType.Error).ToList(); + var ignore = messageList.Where(it => it.StorageType == StorageType.Ignore || it.StorageType == null).ToList(); + var other = messageList.Where(it => it.StorageType == StorageType.Other).ToList(); + StorageableResult result = new StorageableResult() + { + _WhereColumnList = wherecolumnList, + _AsName = asname, + _Context = this.Context, + AsDeleteable = this.Context.Deleteable().AS(asname), + AsUpdateable = this.Context.Updateable(update.Select(it => it.Item).ToList()).AS(asname), + AsInsertable = this.Context.Insertable(inset.Select(it => it.Item).ToList()).AS(asname), + OtherList = other, + InsertList = inset, + DeleteList = delete, + UpdateList = update, + ErrorList = error, + IgnoreList = ignore, + TotalList = messageList + }; + if (this.whereExpression != null) + { + result.AsUpdateable.WhereColumns(whereExpression); + result.AsDeleteable.WhereColumns(update.Select(it => it.Item).ToList(), whereExpression); + } + if (this.whereExpression != null) + { + result.AsDeleteable.WhereColumns(delete.Select(it => it.Item).ToList(), whereExpression); + } + else + { + result.AsDeleteable.Where(delete.Select(it => it.Item).ToList()); + } + return result; + } + + public StorageableResult GetStorageableResult() + { + if (whereFuncs == null || whereFuncs.Count == 0) + { + return this.Saveable().GetStorageableResult(); + } + if (this.allDatas.Count == 0) + return new StorageableResult() + { + //AsDeleteable = this.Context.Deleteable().AS(asname).Where(it => false), + //AsInsertable = this.Context.Insertable(new List()).AS(asname), + //AsUpdateable = this.Context.Updateable(new List()).AS(asname), + InsertList = new List>(), + UpdateList = new List>(), + DeleteList = new List>(), + ErrorList = new List>(), + IgnoreList = new List>(), + OtherList = new List>(), + TotalList = new List>() + }; + var pkInfos = this.Context.EntityMaintenance.GetEntityInfo().Columns.Where(it => it.IsPrimarykey); + if (whereExpression == null && !pkInfos.Any()) + { + Check.ExceptionEasy(true, "Need primary key or WhereColumn", "使用Storageable实体需要主键或者使用WhereColumn指定条件列"); + } + if (whereExpression == null && pkInfos.Any()) + { + this.Context.Utilities.PageEach(allDatas, 300, item => + { + var addItems = this.Context.Queryable().Filter(null, this.isDisableFilters).TranLock(this.lockType).AS(asname).WhereClassByPrimaryKey(item.Select(it => it.Item).ToList()).ToList(); + dbDataList.AddRange(addItems); + }); + } + var pkProperties = GetPkProperties(pkInfos); + var messageList = allDatas.Select(it => new StorageableMessage() + { + Item = it.Item, + Database = dbDataList, + PkFields = pkProperties + }).ToList(); + foreach (var item in whereFuncs.OrderByDescending(it => (int)it.key)) + { + List> whereList = messageList.Where(it => it.StorageType == null).ToList(); + Func, bool> exp = item.value1; + var list = whereList.Where(exp).ToList(); + foreach (var it in list) + { + it.StorageType = item.key; + it.StorageMessage = item.value2; + } + } + var delete = messageList.Where(it => it.StorageType == StorageType.Delete).ToList(); + var update = messageList.Where(it => it.StorageType == StorageType.Update).ToList(); + var inset = messageList.Where(it => it.StorageType == StorageType.Insert).ToList(); + var error = messageList.Where(it => it.StorageType == StorageType.Error).ToList(); + var ignore = messageList.Where(it => it.StorageType == StorageType.Ignore || it.StorageType == null).ToList(); + var other = messageList.Where(it => it.StorageType == StorageType.Other).ToList(); + StorageableResult result = new StorageableResult() + { + _WhereColumnList = wherecolumnList, + _AsName = asname, + _Context = this.Context, + //AsDeleteable = this.Context.Deleteable().AS(asname), + //AsUpdateable = this.Context.Updateable(update.Select(it => it.Item).ToList()).AS(asname), + //AsInsertable = this.Context.Insertable(inset.Select(it => it.Item).ToList()).AS(asname), + OtherList = other, + InsertList = inset, + DeleteList = delete, + UpdateList = update, + ErrorList = error, + IgnoreList = ignore, + TotalList = messageList + }; + //if (this.whereExpression != null) + //{ + // result.AsUpdateable.WhereColumns(whereExpression); + // result.AsDeleteable.WhereColumns(update.Select(it => it.Item).ToList(), whereExpression); + //} + //result.AsDeleteable.Where(delete.Select(it => it.Item).ToList()); + return result; + } + + public async Task> ToStorageAsync() + { + if (whereFuncs == null || whereFuncs.Count == 0) + { + return await Saveable().ToStorageAsync().ConfigureAwait(false); + } + if (this.allDatas.Count == 0) + return new StorageableResult() + { + AsDeleteable = this.Context.Deleteable().AS(asname).Where(it => false), + AsInsertable = this.Context.Insertable(new List()).AS(asname), + AsUpdateable = this.Context.Updateable(new List()).AS(asname), + InsertList = new List>(), + UpdateList = new List>(), + DeleteList = new List>(), + ErrorList = new List>(), + IgnoreList = new List>(), + OtherList = new List>(), + TotalList = new List>() + }; + var pkInfos = this.Context.EntityMaintenance.GetEntityInfo().Columns.Where(it => it.IsPrimarykey); + if (whereExpression == null && !pkInfos.Any()) + { + Check.Exception(true, "Need primary key or WhereColumn"); + } + if (whereExpression == null && pkInfos.Any()) + { + await Context.Utilities.PageEachAsync(allDatas, 300, async item => + { + var addItems = await Context.Queryable().Filter(null, isDisableFilters).AS(asname).TranLock(lockType).WhereClassByPrimaryKey(item.Select(it => it.Item).ToList()).ToListAsync().ConfigureAwait(false); + dbDataList.AddRange(addItems); + }).ConfigureAwait(false); + } + var pkProperties = GetPkProperties(pkInfos); + var messageList = allDatas.Select(it => new StorageableMessage() + { + Item = it.Item, + Database = dbDataList, + PkFields = pkProperties + }).ToList(); + foreach (var item in whereFuncs.OrderByDescending(it => (int)it.key)) + { + List> whereList = messageList.Where(it => it.StorageType == null).ToList(); + Func, bool> exp = item.value1; + var list = whereList.Where(exp).ToList(); + foreach (var it in list) + { + it.StorageType = item.key; + it.StorageMessage = item.value2; + } + } + var delete = messageList.Where(it => it.StorageType == StorageType.Delete).ToList(); + var update = messageList.Where(it => it.StorageType == StorageType.Update).ToList(); + var inset = messageList.Where(it => it.StorageType == StorageType.Insert).ToList(); + var error = messageList.Where(it => it.StorageType == StorageType.Error).ToList(); + var ignore = messageList.Where(it => it.StorageType == StorageType.Ignore || it.StorageType == null).ToList(); + var other = messageList.Where(it => it.StorageType == StorageType.Other).ToList(); + StorageableResult result = new StorageableResult() + { + _WhereColumnList = wherecolumnList, + _AsName = asname, + _Context = this.Context, + AsDeleteable = this.Context.Deleteable().AS(asname), + AsUpdateable = this.Context.Updateable(update.Select(it => it.Item).ToList()).AS(asname), + AsInsertable = this.Context.Insertable(inset.Select(it => it.Item).ToList()).AS(asname), + OtherList = other, + InsertList = inset, + DeleteList = delete, + UpdateList = update, + ErrorList = error, + IgnoreList = ignore, + TotalList = messageList + }; + if (this.whereExpression != null) + { + result.AsUpdateable.WhereColumns(whereExpression); + result.AsDeleteable.WhereColumns(delete.Select(it => it.Item).ToList(), whereExpression); + } + result.AsDeleteable.Where(delete.Select(it => it.Item).ToList()); + return result; + } + + + private string[] GetPkProperties(IEnumerable pkInfos) + { + if (whereExpression == null) + { + return pkInfos.Select(it => it.PropertyName).ToArray(); + } + else + { + return wherecolumnList.Select(it => it.PropertyName).ToArray(); + } + } + List wherecolumnList; + public IStorageable WhereColumns(Expression> columns, Func formatTime) + { + this.formatTime = formatTime; + return WhereColumns(columns); + } + public IStorageable WhereColumns(Expression> columns) + { + + if (columns == null) + return this; + else if (asname == null && typeof(T).GetCustomAttribute() != null) + { + whereExpression = columns; + return this; + } + else + { + List list = GetExpressionValue(columns, ResolveExpressType.ArraySingle).GetResultArray().Select(it => Builder.GetNoTranslationColumnName(it)).ToList(); + var dbColumns = this.Context.EntityMaintenance.GetEntityInfo().Columns.Where(it => it.IsIgnore == false); + var whereColumns = dbColumns.Where(it => list.Any(y => + it.DbColumnName.Equals(y, StringComparison.CurrentCultureIgnoreCase) || + it.PropertyName.Equals(y, StringComparison.CurrentCultureIgnoreCase)) + ).ToList(); + wherecolumnList = whereColumns; + if (whereColumns.Count == 0) + { + whereColumns = dbColumns.Where(it => it.IsPrimarykey).ToList(); + } + if (whereColumns.Count > 0) + { + if (queryableWhereExp == null) + { + this.Context.Utilities.PageEach(allDatas, 200, itemList => + { + List conditList = new List(); + SetConditList(itemList, whereColumns, conditList); + var addItem = this.Context.Queryable().AS(asname) + .Filter(null, this.isDisableFilters) + .TranLock(this.lockType) + .Where(conditList, true).ToList(); + this.dbDataList.AddRange(addItem); + }); + } + else + { + this.dbDataList.AddRange(this.Context.Queryable().AS(asname).Where(queryableWhereExp).ToList()); + } + } + this.whereExpression = columns; + return this; + } + } + + + public IStorageable WhereColumns(string[] columns) + { + var list = columns.Select(it => this.Context.EntityMaintenance.GetPropertyName(it)).ToList(); + var exp = ExpressionBuilderHelper.CreateNewFields(this.Context.EntityMaintenance.GetEntityInfo(), list); + return this.WhereColumns(exp); + } + public IStorageable WhereColumns(string[] columns, Func formatTime) + { + this.formatTime = formatTime; + return WhereColumns(columns); + } + private void SetConditList(List> itemList, List whereColumns, List conditList) + { + ; + foreach (var dataItem in itemList) + { + var condition = new ConditionalCollections() + { + ConditionalList = new List>() + }; + conditList.Add(condition); + int i = 0; + foreach (var item in whereColumns) + { + var value = item.PropertyInfo.GetValue(dataItem.Item, null); + if (value is string str && str == "null") + { + value = $"[null]"; + } + if (value?.GetType().IsEnum() == true) + { + if (this.Context.CurrentConnectionConfig.MoreSettings?.TableEnumIsString == true) + { + value = value.ToString(); + } + else + { + value = Convert.ToInt64(value); + } + } + if (item.SqlParameterDbType != null && item.SqlParameterDbType is Type && UtilMethods.HasInterface((Type)item.SqlParameterDbType, typeof(ISugarDataConverter))) + { + var columnInfo = item; + var type = columnInfo.SqlParameterDbType as Type; + var ParameterConverter = type.GetMethod("ParameterConverter").MakeGenericMethod(columnInfo.PropertyInfo.PropertyType); + var obj = Activator.CreateInstance(type); + var p = ParameterConverter.Invoke(obj, new object[] { value, 100 }) as SugarParameter; + value = p.Value; + } + condition.ConditionalList.Add(new KeyValuePair(i == 0 ? WhereType.Or : WhereType.And, new ConditionalModel() + { + FieldName = item.DbColumnName, + ConditionalType = ConditionalType.Equal, + CSharpTypeName = UtilMethods.GetTypeName(value), + FieldValue = value == null ? "null" : value.ObjToString(formatTime), + FieldValueConvertFunc = this.Context.CurrentConnectionConfig.DbType == DbType.PostgreSQL ? + UtilMethods.GetTypeConvert(value) : null + })); + ++i; + } + } + } + + public virtual ExpressionResult GetExpressionValue(Expression expression, ResolveExpressType resolveType) + { + ILambdaExpressions resolveExpress = InstanceFactory.GetLambdaExpressions(this.Context.CurrentConnectionConfig); ; + if (this.Context.CurrentConnectionConfig.MoreSettings != null) + { + resolveExpress.TableEnumIsString = this.Context.CurrentConnectionConfig.MoreSettings.TableEnumIsString; + resolveExpress.PgSqlIsAutoToLower = this.Context.CurrentConnectionConfig.MoreSettings.PgSqlIsAutoToLower; + } + else + { + resolveExpress.PgSqlIsAutoToLower = true; + } + resolveExpress.MappingColumns = Context.MappingColumns; + resolveExpress.MappingTables = Context.MappingTables; + resolveExpress.IgnoreComumnList = Context.IgnoreColumns; + resolveExpress.SqlFuncServices = Context.CurrentConnectionConfig.ConfigureExternalServices == null ? null : Context.CurrentConnectionConfig.ConfigureExternalServices.SqlFuncServices; + resolveExpress.InitMappingInfo = Context.InitMappingInfo; + resolveExpress.RefreshMapping = () => + { + resolveExpress.MappingColumns = Context.MappingColumns; + resolveExpress.MappingTables = Context.MappingTables; + resolveExpress.IgnoreComumnList = Context.IgnoreColumns; + resolveExpress.SqlFuncServices = Context.CurrentConnectionConfig.ConfigureExternalServices == null ? null : Context.CurrentConnectionConfig.ConfigureExternalServices.SqlFuncServices; + }; + resolveExpress.Resolve(expression, resolveType); + if (this.Parameters == null) + this.Parameters = new List(); + this.Parameters.AddRange(resolveExpress.Parameters); + var result = resolveExpress.Result; + return result; + } + + public IStorageable As(string tableName) + { + this.asname = tableName; + return this; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SaveableProvider/StorageableDataTable.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SaveableProvider/StorageableDataTable.cs new file mode 100644 index 000000000..96b37de07 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SaveableProvider/StorageableDataTable.cs @@ -0,0 +1,239 @@ +using System.Data; + +namespace SqlSugar +{ + public class StorageableDataTable + { + internal DataTable DataTable { get; set; } + internal SqlSugarProvider Context { get; set; } + internal string[] Columns { get; set; } = Array.Empty(); + internal string SugarGroupId = "SugarGroupId"; + internal string SugarUpdateRows = "SugarUpdateRows"; + internal string SugarColumns = "SugarColumns"; + internal string SugarErrorMessage = "SugarErrorMessage"; + internal List dbDataList = new List(); + internal Func formatTime; + List, string>> whereFuncs = new List, string>>(); + public StorageableDataTable WhereColumns(string name) + { + return WhereColumns(new string[] { name }); + } + public StorageableDataTable WhereColumns(string name, Func formatTime) + { + this.formatTime = formatTime; + return WhereColumns(new string[] { name }); + } + public StorageableDataTable WhereColumns(string[] names, Func formatTime) + { + this.formatTime = formatTime; + return WhereColumns(names); + } + public StorageableDataTable WhereColumns(string[] names) + { + this.Columns = names; + var queryable = this.Context.Queryable(); + Check.Exception(Columns == null || Columns.Length == 0, "need WhereColums"); + var tableName = queryable.SqlBuilder.GetTranslationTableName(DataTable.TableName); + this.Context.Utilities.PageEach(DataTable.Rows.Cast(), 200, itemList => + { + List conditList = new List(); + SetConditList(itemList, Columns, conditList); + string selector = null; + if (queryable.SqlBuilder.SqlParameterKeyWord == ":") + { + //Oracle driver bug: Error when querying DataTable after dynamically adding columns using '*'. + selector = " * /*" + Guid.NewGuid() + "*/"; + } + var addItem = this.Context.Queryable().AS(tableName).Where(conditList).Select(selector).ToDataTable().Rows.Cast().ToList(); + this.dbDataList.AddRange(addItem); + }); + return this; + } + public StorageableDataTable WhereColumns(List names) + { + return WhereColumns(names.ToArray()); + } + public StorageableDataTable SplitInsert(Func conditions, string message = null) + { + whereFuncs.Add(new KeyValuePair, string>(StorageType.Insert, conditions, message)); + return this; + } + public StorageableDataTable SplitDelete(Func conditions, string message = null) + { + whereFuncs.Add(new KeyValuePair, string>(StorageType.Delete, conditions, message)); + return this; + } + public StorageableDataTable SplitUpdate(Func conditions, string message = null) + { + whereFuncs.Add(new KeyValuePair, string>(StorageType.Update, conditions, message)); + return this; + } + + public StorageableDataTable Saveable(string inserMessage = null, string updateMessage = null) + { + SplitUpdate(it => it.Any(), updateMessage); + SplitInsert(it => true, inserMessage); + return this; + } + public StorageableDataTable SplitError(Func conditions, string message = null) + { + whereFuncs.Add(new KeyValuePair, string>(StorageType.Error, conditions, message)); + return this; + } + public StorageableDataTable SplitIgnore(Func conditions, string message = null) + { + whereFuncs.Add(new KeyValuePair, string>(StorageType.Ignore, conditions, message)); + return this; + } + + public DataTableResult ToStorage() + { + if (whereFuncs == null || whereFuncs.Count == 0) + { + Saveable(); + } + foreach (DataRow row in DataTable.Rows) + { + foreach (var item in whereFuncs.OrderByDescending(it => (int)it.key)) + { + SplitMethod(item.value1, item.key, row, item.value2); + } + if (row[SugarGroupId] == null || row[SugarGroupId] == DBNull.Value) + { + row[SugarGroupId] = StorageType.Ignore; + } + } + DataTable.Columns.Remove(SugarUpdateRows); + DataTable.Columns.Remove(SugarColumns); + var Groups = DataTable.Rows.Cast() + .Where(it => it[SugarGroupId] != null && it[SugarGroupId] != DBNull.Value) + .GroupBy(it => ((StorageType)it[SugarGroupId]).ToString()).Select(it => new DataTableGroups { Type = it.Key, DataTable = it.CopyToDataTable() }) + .ToList(); + DataTable.Columns.Remove(SugarGroupId); + DataTable.Columns.Remove(SugarErrorMessage); + var inserList = new List>(); + var updateList = new List>(); + var DeleteList = Groups.FirstOrDefault(it => it.Type == StorageType.Delete.ToString()); + if (Groups.Any(it => it.Type == StorageType.Insert.ToString())) + { + foreach (var item in Groups) + { + if (item.Type == StorageType.Insert.ToString()) + { + item.DataTable.Columns.Remove(SugarGroupId); + item.DataTable.Columns.Remove(SugarErrorMessage); + inserList.AddRange(this.Context.Utilities.DataTableToDictionaryList(item.DataTable)); + } + } + } + if (Groups.Any(it => it.Type == StorageType.Update.ToString())) + { + foreach (var item in Groups) + { + if (item.Type == StorageType.Update.ToString()) + { + item.DataTable.Columns.Remove(SugarGroupId); + item.DataTable.Columns.Remove(SugarErrorMessage); + updateList.AddRange(this.Context.Utilities.DataTableToDictionaryList(item.DataTable)); + } + } + } + List conditionalModels = new List(); + if (DeleteList != null) + { + SetConditList(DeleteList.DataTable.Rows.Cast().ToList(), Columns, conditionalModels); + } + var tableName = this.Context.Queryable().SqlBuilder.GetTranslationTableName(DataTable.TableName); + DataTableResult result = new DataTableResult() + { + DataTableGroups = Groups, + AsDeleteable = this.Context.Deleteable().AS(tableName).Where(conditionalModels), + AsUpdateable = this.Context.Updateable(updateList).AS(tableName).WhereColumns(Columns), + AsInsertable = this.Context.Insertable(inserList).AS(tableName) + }; + return result; + } + + private void SplitMethod(Func conditions, StorageType type, DataRow item, string message) + { + item[SugarColumns] = Columns; + item[SugarUpdateRows] = dbDataList; + if ((item[SugarGroupId] == null || item[SugarGroupId] == DBNull.Value) && conditions(item)) + { + item[SugarGroupId] = type; + item[SugarErrorMessage] = message; + } + } + private void SetConditList(List itemList, string[] whereColumns, List conditList) + { + ; + foreach (var dataItem in itemList) + { + var condition = new ConditionalCollections() + { + ConditionalList = new List>() + }; + conditList.Add(condition); + int i = 0; + foreach (var name in whereColumns) + { + var value = dataItem[name]; + if (value?.GetType().IsEnum() == true) + { + value = Convert.ToInt64(value); + } + condition.ConditionalList.Add(new KeyValuePair(i == 0 ? WhereType.Or : WhereType.And, new ConditionalModel() + { + FieldName = name, + ConditionalType = ConditionalType.Equal, + FieldValue = value.ObjToString(this.formatTime), + CSharpTypeName = value?.GetType()?.Name + })); + ++i; + } + } + } + } + + public class DataTableResult + { + public List DataTableGroups { get; set; } + public IUpdateable> AsUpdateable { get; set; } + public IDeleteable AsDeleteable { get; set; } + public IInsertable> AsInsertable { get; set; } + } + public class DataTableGroups + { + public string Type { get; set; } + public DataTable DataTable { get; set; } + } + public static class StorageableDataTableExtensions + { + public static bool Any(this DataRow row) + { + var list = row["SugarUpdateRows"] as List; + var columns = row["SugarColumns"] as string[]; + return list.Any(it => + { + var result = true; + foreach (var name in columns) + { + + if (result) + { + result = row[name].ObjToString() == it[name].ObjToString(); + if (result == false && it[name] != null && it[name].GetType() == UtilConstants.DecType) + { + result = row[name].ObjToDecimal() == it[name].ObjToDecimal(); + } + if (result == false) + { + break; + } + } + } + return result; + }); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SaveableProvider/StorageableMethodInfo.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SaveableProvider/StorageableMethodInfo.cs new file mode 100644 index 000000000..90fe35f6a --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SaveableProvider/StorageableMethodInfo.cs @@ -0,0 +1,157 @@ +using System.Reflection; + +namespace SqlSugar +{ + public class StorageableMethodInfo + { + internal SqlSugarProvider Context { get; set; } + internal MethodInfo MethodInfo { get; set; } + internal object objectValue { get; set; } + public int ExecuteCommand() + { + if (Context == null) return 0; + object objectValue = null; + MethodInfo method = GetSaveMethod(ref objectValue); + if (method == null) return 0; + return (int)method.Invoke(objectValue, Array.Empty()); + } + + public StorageableAsMethodInfo AsInsertable + { + get + { + var type = "AsInsertable"; + return GetAs(type); + } + } + public StorageableAsMethodInfo AsUpdateable + { + get + { + var type = "AsUpdateable"; + return GetAs(type); + } + } + + private StorageableAsMethodInfo GetAs(string type) + { + object objectValue = null; + MethodInfo method = GetSaveMethod(ref objectValue); + if (method == null) return new StorageableAsMethodInfo(null); + method = objectValue.GetType().GetMethod("ToStorage"); + objectValue = method.Invoke(objectValue, Array.Empty()); + StorageableAsMethodInfo result = new StorageableAsMethodInfo(type); + result.ObjectValue = objectValue; + result.Method = method; + return result; + } + + private MethodInfo GetSaveMethod(ref object callValue) + { + if (objectValue == null) + return null; + callValue = MethodInfo.Invoke(Context, new object[] { objectValue }); + return callValue.GetType().GetMyMethod("ExecuteCommand", 0); + } + + public StorageableMethodInfo ToStorage() + { + return this; + } + + public StorageableSplitTableMethodInfo SplitTable() + { + object objectValue = null; + MethodInfo method = GetSaveMethod(ref objectValue); + if (method == null) return new StorageableSplitTableMethodInfo(null); + method = objectValue.GetType().GetMethod("SplitTable"); + objectValue = method.Invoke(objectValue, Array.Empty()); + StorageableSplitTableMethodInfo result = new StorageableSplitTableMethodInfo(null); + result.ObjectValue = objectValue; + result.Method = method; + return result; + } + + public StorageableSplitTableMethodInfo AS(string tableName) + { + object objectValue = null; + MethodInfo method = GetSaveMethod(ref objectValue); + if (method == null) return new StorageableSplitTableMethodInfo(null); + method = objectValue.GetType().GetMyMethod("As", 1); + objectValue = method.Invoke(objectValue, new object[] { tableName }); + StorageableSplitTableMethodInfo result = new StorageableSplitTableMethodInfo(null); + result.ObjectValue = objectValue; + result.Method = method; + return result; + } + + public StorageableSplitTableMethodInfo WhereColumns(string[] strings) + { + object objectValue = null; + MethodInfo method = GetSaveMethod(ref objectValue); + if (method == null) return new StorageableSplitTableMethodInfo(null); + method = objectValue.GetType().GetMyMethod("WhereColumns", 1, typeof(string[])); + objectValue = method.Invoke(objectValue, new object[] { strings }); + StorageableSplitTableMethodInfo result = new StorageableSplitTableMethodInfo(null); + result.ObjectValue = objectValue; + result.Method = method; + return result; + } + } + + public class StorageableAsMethodInfo + { + private StorageableAsMethodInfo() { } + private string type; + public StorageableAsMethodInfo(string type) + { + this.type = type; + } + internal object ObjectValue { get; set; } + internal MethodInfo Method { get; set; } + public int ExecuteCommand() + { + if (type == null) return 0; + PropertyInfo property = ObjectValue.GetType().GetProperty(type); + var value = property.GetValue(ObjectValue); + var newObj = value.GetType().GetMethod("ExecuteCommand").Invoke(value, Array.Empty()); + return (int)newObj; + } + public StorageableCommonMethodInfo IgnoreColumns(params string[] ignoreColumns) + { + PropertyInfo property = ObjectValue?.GetType().GetProperty(type); + var value = property?.GetValue(ObjectValue); + var newObj = value?.GetType().GetMyMethod("IgnoreColumns", 1, typeof(string[])).Invoke(value, new object[] { ignoreColumns }); + StorageableCommonMethodInfo result = new StorageableCommonMethodInfo(); + result.Value = newObj; + return result; + } + } + public class StorageableCommonMethodInfo + { + public object Value { get; set; } + public int ExecuteCommand() + { + if (Value == null) return 0; + var newObj = Value.GetType().GetMethod("ExecuteCommand").Invoke(Value, Array.Empty()); + return (int)newObj; + } + } + + public class StorageableSplitTableMethodInfo + { + private StorageableSplitTableMethodInfo() { } + private string type; + public StorageableSplitTableMethodInfo(string type) + { + this.type = type; + } + internal object ObjectValue { get; set; } + internal MethodInfo Method { get; set; } + public int ExecuteCommand() + { + var newObj = ObjectValue.GetType().GetMethod("ExecuteCommand").Invoke(ObjectValue, Array.Empty()); + return (int)newObj; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SaveableProvider/StorageablePage.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SaveableProvider/StorageablePage.cs new file mode 100644 index 000000000..2b77de3c8 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SaveableProvider/StorageablePage.cs @@ -0,0 +1,151 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public class StorageablePage where T : class, new() + { + internal DbLockType? lockType { get; set; } + + public SqlSugarProvider Context { get; set; } + public List Data { get; set; } + public int PageSize { get; internal set; } + public Action ActionCallBack { get; internal set; } + public string TableName { get; internal set; } + public Expression> whereExpression { get; internal set; } + + public int ExecuteCommand() + { + if (Data.Count == 1 && Data.First() == null) + { + return 0; + } + if (PageSize == 0) { PageSize = 1000; } + var result = 0; + var isNoTran = this.Context.Ado.IsNoTran(); + try + { + if (isNoTran) + { + this.Context.Ado.BeginTran(); + } + this.Context.Utilities.PageEach(Data, PageSize, pageItem => + { + result += this.Context.Storageable(pageItem).As(TableName).TranLock(lockType).WhereColumns(whereExpression).ExecuteCommand(); + if (ActionCallBack != null) + { + ActionCallBack(result); + } + }); + if (isNoTran) + { + this.Context.Ado.CommitTran(); + } + } + catch (Exception) + { + if (isNoTran) + { + this.Context.Ado.RollbackTran(); + } + throw; + } + return result; + } + public async Task ExecuteCommandAsync(CancellationToken? cancellationToken = null) + { + if (cancellationToken != null) + this.Context.Ado.CancellationToken = cancellationToken.Value; + if (Data.Count == 1 && Data.First() == null) + { + return 0; + } + if (PageSize == 0) { PageSize = 1000; } + var result = 0; + var isNoTran = this.Context.Ado.IsNoTran(); + try + { + if (isNoTran) + { + await Context.Ado.BeginTranAsync().ConfigureAwait(false); + } + await Context.Utilities.PageEachAsync(Data, PageSize, async pageItem => + { + result += await Context.Storageable(pageItem).As(TableName).TranLock(lockType).WhereColumns(whereExpression).ExecuteCommandAsync().ConfigureAwait(false); + if (ActionCallBack != null) + { + ActionCallBack(result); + } + }).ConfigureAwait(false); + if (isNoTran) + { + await Context.Ado.CommitTranAsync().ConfigureAwait(false); + } + } + catch (Exception) + { + if (isNoTran) + { + await Context.Ado.RollbackTranAsync().ConfigureAwait(false); + } + throw; + } + return result; + } + public int ExecuteSqlBulkCopy() + { + if (Data.Count == 1 && Data.First() == null) + { + return 0; + } + if (PageSize == 0) { PageSize = 1000; } + var result = 0; + var isNoTran = this.Context.Ado.IsNoTran(); + try + { + + this.Context.Utilities.PageEach(Data, PageSize, pageItem => + { + result += this.Context.Storageable(pageItem).As(TableName).TranLock(lockType).WhereColumns(whereExpression).ExecuteSqlBulkCopy(); + if (ActionCallBack != null) + { + ActionCallBack(result); + } + }); + } + catch (Exception) + { + throw; + } + return result; + } + public async Task ExecuteSqlBulkCopyAsync(CancellationToken? cancellationToken = null) + { + if (cancellationToken != null) + this.Context.Ado.CancellationToken = cancellationToken.Value; + if (Data.Count == 1 && Data.First() == null) + { + return 0; + } + if (PageSize == 0) { PageSize = 1000; } + var result = 0; + var isNoTran = this.Context.Ado.IsNoTran(); + try + { + await Context.Utilities.PageEachAsync(Data, PageSize, async pageItem => + { + result += await Context.Storageable(pageItem).As(TableName).TranLock(lockType).WhereColumns(whereExpression).ExecuteSqlBulkCopyAsync().ConfigureAwait(false); + if (ActionCallBack != null) + { + ActionCallBack(result); + } + }).ConfigureAwait(false); + } + catch (Exception) + { + throw; + } + return result; + } + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SaveableProvider/StorageableSplitProvider.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SaveableProvider/StorageableSplitProvider.cs new file mode 100644 index 000000000..fecae336a --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SaveableProvider/StorageableSplitProvider.cs @@ -0,0 +1,205 @@ +using System.Linq.Expressions; +using System.Reflection; + +namespace SqlSugar +{ + public class StorageableSplitProvider where T : class, new() + { + internal Storageable SaveInfo { get; set; } + internal SqlSugarProvider Context { get; set; } + internal List List { get; set; } + internal EntityInfo EntityInfo { get; set; } + internal Expression> whereExpression { get; set; } + + internal int pageSize = 1000; + internal Action ActionCallBack = null; + public StorageableSplitProvider PageSize(int size, Action ActionCallBack = null) + { + this.pageSize = size; + return this; + } + public int ExecuteCommand() + { + if (List.Count > pageSize) + { + var result = 0; + this.Context.Utilities.PageEach(List, pageSize, pageItem => + { + result += _ExecuteCommand(pageItem); + }); + return result; + } + else + { + var list = List; + return _ExecuteCommand(list); + } + + } + public int ExecuteSqlBulkCopy() + { + if (List.Count > pageSize) + { + var result = 0; + this.Context.Utilities.PageEach(List, pageSize, pageItem => + { + result += _ExecuteSqlBulkCopy(pageItem); + }); + return result; + } + else + { + var list = List; + return _ExecuteSqlBulkCopy(list); + } + + } + + + public async Task ExecuteCommandAsync() + { + if (List.Count > pageSize) + { + var result = 0; + await Context.Utilities.PageEachAsync(List, pageSize, async pageItem => + { + result += await _ExecuteCommandAsync(pageItem).ConfigureAwait(false); + if (ActionCallBack != null) + { + ActionCallBack(result); + } + }).ConfigureAwait(false); + return result; + } + else + { + var list = List; + return await _ExecuteCommandAsync(list).ConfigureAwait(false); + } + } + public async Task ExecuteSqlBulkCopyAsync() + { + if (List.Count > pageSize) + { + var result = 0; + await Context.Utilities.PageEachAsync(List, pageSize, async pageItem => + { + result += await _ExecuteSqlBulkCopyAsync(pageItem).ConfigureAwait(false); + if (ActionCallBack != null) + { + ActionCallBack(result); + } + }).ConfigureAwait(false); + return result; + } + else + { + var list = List; + return await _ExecuteSqlBulkCopyAsync(list).ConfigureAwait(false); + } + } + + + private async Task _ExecuteCommandAsync(List list) + { + int resultValue = 0; + List groupModels; + int result; + GroupDataList(list, out groupModels, out result); + foreach (var item in groupModels.GroupBy(it => it.GroupName)) + { + var addList = item.Select(it => it.Item).ToList(); + resultValue += await Context.Storageable(addList).As(item.Key).WhereColumns(whereExpression).ExecuteCommandAsync().ConfigureAwait(false); + if (ActionCallBack != null) + { + ActionCallBack(resultValue); + } + } + return resultValue; + } + private int _ExecuteCommand(List list) + { + int resultValue = 0; + List groupModels; + int result; + GroupDataList(list, out groupModels, out result); + foreach (var item in groupModels.GroupBy(it => it.GroupName)) + { + var addList = item.Select(it => it.Item).ToList(); + resultValue += this.Context.Storageable(addList).As(item.Key).WhereColumns(whereExpression).ExecuteCommand(); + } + return resultValue; + } + + private async Task _ExecuteSqlBulkCopyAsync(List list) + { + int resultValue = 0; + List groupModels; + int result; + GroupDataList(list, out groupModels, out result); + foreach (var item in groupModels.GroupBy(it => it.GroupName)) + { + var addList = item.Select(it => it.Item).ToList(); + resultValue += await Context.Storageable(addList).As(item.Key).WhereColumns(whereExpression).ExecuteSqlBulkCopyAsync().ConfigureAwait(false); + if (ActionCallBack != null) + { + ActionCallBack(resultValue); + } + } + return resultValue; + } + private int _ExecuteSqlBulkCopy(List list) + { + int resultValue = 0; + List groupModels; + int result; + GroupDataList(list, out groupModels, out result); + foreach (var item in groupModels.GroupBy(it => it.GroupName)) + { + var addList = item.Select(it => it.Item).ToList(); + resultValue += this.Context.Storageable(addList).As(item.Key).WhereColumns(whereExpression).ExecuteSqlBulkCopy(); + } + return resultValue; + } + + private void GroupDataList(List datas, out List groupModels, out int result) + { + var attribute = typeof(T).GetCustomAttribute() as SplitTableAttribute; + Check.Exception(attribute == null, $"{typeof(T).Name} need SplitTableAttribute"); + groupModels = new List(); + var db = this.Context; + foreach (var item in datas) + { + var value = db.SplitHelper().GetValue(attribute.SplitType, item); + var tableName = db.SplitHelper().GetTableName(attribute.SplitType, value); + groupModels.Add(new GroupModel() { GroupName = tableName, Item = item }); + } + var tablenames = groupModels.Select(it => it.GroupName).Distinct().ToList(); + CreateTable(tablenames); + result = 0; + } + private void CreateTable(List tableNames) + { + var isLog = this.Context.Ado.IsEnableLogEvent; + this.Context.Ado.IsEnableLogEvent = false; + foreach (var item in tableNames) + { + if (!this.Context.DbMaintenance.IsAnyTable(item, false)) + { + if (item != null) + { + this.Context.MappingTables.Add(EntityInfo.EntityName, item); + this.Context.CodeFirst.InitTables(); + } + } + } + this.Context.Ado.IsEnableLogEvent = isLog; + this.Context.MappingTables.Add(EntityInfo.EntityName, EntityInfo.DbTableName); + } + internal class GroupModel + { + public string GroupName { get; set; } + public T Item { get; set; } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SqlBuilderProvider/DeleteBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SqlBuilderProvider/DeleteBuilder.cs new file mode 100644 index 000000000..201d7980f --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SqlBuilderProvider/DeleteBuilder.cs @@ -0,0 +1,181 @@ +using System.Linq.Expressions; +using System.Text; +namespace SqlSugar +{ + public class DeleteBuilder : IDMLBuilder + { + #region Fields + private List _WhereInfos; + #endregion + + #region Common Properties + public EntityInfo EntityInfo { get; set; } + public SqlSugarProvider Context { get; set; } + public ILambdaExpressions LambdaExpressions { get; set; } + public List Parameters { get; set; } + public StringBuilder sql { get; set; } + public ISqlBuilder Builder { get; set; } + public string TableWithString { get; set; } + public string AsName { get; set; } + public virtual List WhereInfos + { + get + { + _WhereInfos = UtilMethods.IsNullReturnNew(_WhereInfos); + return _WhereInfos; + } + set { _WhereInfos = value; } + } + public virtual List BigDataInValues { get; set; } + public virtual string BigDataFiled { get; set; } + #endregion + + #region Sql Template + public virtual string SqlTemplate + { + get + { + return "DELETE FROM {0}{1}"; + } + } + public string WhereInTemplate + { + get + { + return "{0} IN ({1})"; + } + } + public string WhereInOrTemplate + { + get + { + return "OR"; + } + } + public string WhereInAndTemplate + { + get + { + return "AND"; + } + } + public string WhereInEqualTemplate + { + get + { + return Builder.SqlTranslationLeft + "{0}" + Builder.SqlTranslationRight + "=N'{1}'"; + } + } + public string WhereInAreaTemplate + { + get + { + return "({0})"; + } + } + #endregion + + #region Get Sql + public virtual string GetTableNameString + { + get + { + var result = Builder.GetTranslationTableName(EntityInfo.EntityName); + if (AsName.HasValue()) + { + result = Builder.GetTranslationColumnName(AsName); + } + result += UtilConstants.Space; + if (this.TableWithString.HasValue()) + { + result += TableWithString + UtilConstants.Space; + } + return result; + } + } + public virtual string GetWhereString + { + get + { + if (_WhereInfos == null || _WhereInfos.Count == 0) return null; + string whereString = null; + int i = 0; + foreach (var item in _WhereInfos) + { + var isFirst = i == 0; + whereString += isFirst ? "WHERE " : "AND "; + whereString += (item + UtilConstants.Space); + ++i; + } + return whereString; + } + } + + #endregion + + #region Public methods + public virtual void Clear() + { + } + public virtual string ToSqlString() + { + if (this.BigDataInValues.IsNullOrEmpty()) + { + return string.Format(SqlTemplate, GetTableNameString, GetWhereString); + } + else//big data + { + var whereString = GetWhereString; + var sql = string.Format(SqlTemplate, GetTableNameString, whereString); + sql += whereString.IsNullOrEmpty() ? " WHERE " : " AND "; + StringBuilder batchDeleteSql = new StringBuilder(); + int pageSize = 1000; + int pageIndex = 1; + int totalRecord = this.BigDataInValues.Count; + int pageCount = (totalRecord + pageSize - 1) / pageSize; + while (pageCount >= pageIndex) + { + var inValues = this.BigDataInValues.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList(); + batchDeleteSql.Append(sql + string.Format(WhereInTemplate, BigDataFiled, inValues.ToArray().ToJoinSqlInVals())); + batchDeleteSql.Append(';'); + pageIndex++; + } + return batchDeleteSql.ToString(); + } + } + public virtual ExpressionResult GetExpressionValue(Expression expression, ResolveExpressType resolveType) + { + ILambdaExpressions resolveExpress = this.LambdaExpressions; + this.LambdaExpressions.Clear(); + if (this.Context.CurrentConnectionConfig.MoreSettings != null) + { + resolveExpress.TableEnumIsString = this.Context.CurrentConnectionConfig.MoreSettings.TableEnumIsString; + resolveExpress.PgSqlIsAutoToLower = this.Context.CurrentConnectionConfig.MoreSettings.PgSqlIsAutoToLower; + } + else + { + resolveExpress.PgSqlIsAutoToLower = true; + } + resolveExpress.SugarContext = new ExpressionOutParameter() { Context = this.Context }; + resolveExpress.MappingColumns = Context.MappingColumns; + resolveExpress.MappingTables = Context.MappingTables; + resolveExpress.IgnoreComumnList = Context.IgnoreColumns; + resolveExpress.SqlFuncServices = Context.CurrentConnectionConfig.ConfigureExternalServices == null ? null : Context.CurrentConnectionConfig.ConfigureExternalServices.SqlFuncServices; + resolveExpress.InitMappingInfo = Context.InitMappingInfo; + resolveExpress.RefreshMapping = () => + { + resolveExpress.MappingColumns = Context.MappingColumns; + resolveExpress.MappingTables = Context.MappingTables; + resolveExpress.IgnoreComumnList = Context.IgnoreColumns; + resolveExpress.SqlFuncServices = Context.CurrentConnectionConfig.ConfigureExternalServices == null ? null : Context.CurrentConnectionConfig.ConfigureExternalServices.SqlFuncServices; + }; + resolveExpress.Resolve(expression, resolveType); + if (this.Parameters == null) + this.Parameters = new List(); + this.Parameters.AddRange(resolveExpress.Parameters); + var result = resolveExpress.Result; + return result; + } + #endregion + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SqlBuilderProvider/Entities.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SqlBuilderProvider/Entities.cs new file mode 100644 index 000000000..36345a0ba --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SqlBuilderProvider/Entities.cs @@ -0,0 +1,14 @@ +namespace SqlSugar +{ + public class ReSetValueBySqlExpListModel + { + public string DbColumnName { get; set; } + public string Sql { get; set; } + public ReSetValueBySqlExpListModelType? Type { get; set; } + } + public enum ReSetValueBySqlExpListModelType + { + Default, + List + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SqlBuilderProvider/InsertBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SqlBuilderProvider/InsertBuilder.cs new file mode 100644 index 000000000..6ef9e0449 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SqlBuilderProvider/InsertBuilder.cs @@ -0,0 +1,396 @@ +using System.Globalization; +using System.Linq.Expressions; +using System.Text; +namespace SqlSugar +{ + public partial class InsertBuilder : IDMLBuilder + { + #region Init + public InsertBuilder() + { + this.sql = new StringBuilder(); + this.Parameters = new List(); + this.DbColumnInfoList = new List(); + } + + #endregion + + #region Common Properties + public SqlSugarProvider Context { get; set; } + public ILambdaExpressions LambdaExpressions { get; set; } + public ISqlBuilder Builder { get; set; } + public StringBuilder sql { get; set; } + public List Parameters { get; set; } + public string TableWithString { get; set; } + public List DbColumnInfoList { get; set; } + public bool IsNoInsertNull { get; set; } + public bool IsReturnIdentity { get; set; } + public EntityInfo EntityInfo { get; set; } + public Dictionary OracleSeqInfoList { get; set; } + public bool IsBlukCopy { get; set; } + public virtual bool IsOleDb { get; set; } + public virtual Func ConvertInsertReturnIdFunc { get; set; } + public virtual bool IsNoPage { get; set; } + + public virtual bool IsReturnPkList { get; set; } + public string AsName { get; set; } + public bool IsOffIdentity { get; set; } + #endregion + + #region SqlTemplate + public virtual string SqlTemplate + { + get + { + if (IsReturnIdentity) + { + return @"INSERT INTO {0} + ({1}) + VALUES + ({2}) ;SELECT SCOPE_IDENTITY();"; + } + else + { + return @"INSERT INTO {0} + ({1}) + VALUES + ({2}) ;"; + + } + } + } + public virtual string SqlTemplateBatch + { + get + { + return "INSERT {0} ({1})"; + } + } + public virtual string SqlTemplateBatchSelect + { + get + { + return "{0} AS {1}"; + } + } + public virtual string SqlTemplateBatchUnion + { + get + { + return "\t\r\nUNION ALL "; + } + } + + #endregion + + #region Methods + + public virtual void ActionMinDate() + { + if (this.Parameters != null) + { + foreach (var item in this.Parameters) + { + if (item.DbType == System.Data.DbType.Date || item.DbType == System.Data.DbType.DateTime) + { + if (item.Value != null && item.Value != DBNull.Value) + { + if (item.Value is DateTime) + { + if (Convert.ToDateTime(item.Value) == DateTime.MinValue) + { + item.Value = UtilMethods.GetMinDate(this.Context.CurrentConnectionConfig); + } + } + } + } + } + } + } + public virtual void Clear() + { + + } + public virtual string GetTableNameString + { + get + { + if (AsName.HasValue()) + { + return Builder.GetTranslationTableName(AsName); + } + var result = Builder.GetTranslationTableName(EntityInfo.EntityName); + result += UtilConstants.Space; + if (this.TableWithString.HasValue()) + { + result += TableWithString + UtilConstants.Space; + } + return result; + } + } + + public bool MySqlIgnore { get; internal set; } + public bool IsWithAttr { get; internal set; } + public string[] ConflictNothing { get; set; } + + public virtual ExpressionResult GetExpressionValue(Expression expression, ResolveExpressType resolveType) + { + ILambdaExpressions resolveExpress = this.LambdaExpressions; + this.LambdaExpressions.Clear(); + if (this.Context.CurrentConnectionConfig.MoreSettings != null) + { + resolveExpress.TableEnumIsString = this.Context.CurrentConnectionConfig.MoreSettings.TableEnumIsString; + resolveExpress.PgSqlIsAutoToLower = this.Context.CurrentConnectionConfig.MoreSettings.PgSqlIsAutoToLower; + } + else + { + resolveExpress.PgSqlIsAutoToLower = true; + } + resolveExpress.MappingColumns = Context.MappingColumns; + resolveExpress.MappingTables = Context.MappingTables; + resolveExpress.IgnoreComumnList = Context.IgnoreColumns; + resolveExpress.SqlFuncServices = Context.CurrentConnectionConfig.ConfigureExternalServices == null ? null : Context.CurrentConnectionConfig.ConfigureExternalServices.SqlFuncServices; + resolveExpress.Resolve(expression, resolveType); + this.Parameters.AddRange(resolveExpress.Parameters); + var result = resolveExpress.Result; + return result; + } + public virtual string ToSqlString() + { + if (IsNoInsertNull) + { + DbColumnInfoList = DbColumnInfoList.Where(it => it.Value != null).ToList(); + } + var groupList = DbColumnInfoList.GroupBy(it => it.TableId).ToList(); + var isSingle = groupList.Count == 1; + string columnsString = string.Join(",", groupList.First().Select(it => Builder.GetTranslationColumnName(it.DbColumnName))); + if (isSingle) + { + string columnParametersString = string.Join(",", this.DbColumnInfoList.Select(it => this.GetDbColumn(it, Builder.SqlParameterKeyWord + it.DbColumnName))); + return string.Format(SqlTemplate, GetTableNameString, columnsString, columnParametersString); + } + else + { + StringBuilder batchInsetrSql = new StringBuilder(); + int pageSize = 200; + if (this.EntityInfo.Columns.Count > 30) + { + pageSize = 50; + } + else if (this.EntityInfo.Columns.Count > 20) + { + pageSize = 100; + } + int pageIndex = 1; + int totalRecord = groupList.Count; + int pageCount = (totalRecord + pageSize - 1) / pageSize; + while (pageCount >= pageIndex) + { + batchInsetrSql.AppendFormat(SqlTemplateBatch, GetTableNameString, columnsString); + int i = 0; + foreach (var columns in groupList.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList()) + { + var isFirst = i == 0; + if (!isFirst) + { + batchInsetrSql.Append(SqlTemplateBatchUnion); + } + batchInsetrSql.Append("\r\n SELECT " + string.Join(",", columns.Select(it => string.Format(SqlTemplateBatchSelect, this.GetDbColumn(it, FormatValue(it.Value)), Builder.GetTranslationColumnName(it.DbColumnName))))); + ++i; + } + pageIndex++; + batchInsetrSql.Append("\r\n;\r\n"); + } + var result = batchInsetrSql.ToString(); + if (this.Context.CurrentConnectionConfig.DbType == DbType.SqlServer) + { + result += "select @@identity;"; + } + return result; + } + } + public virtual object FormatValue(object value) + { + var N = "N"; + if (this.Context.CurrentConnectionConfig.DbType == DbType.Sqlite) + { + N = ""; + } + if (value == null) + { + return "NULL"; + } + else + { + var type = UtilMethods.GetUnderType(value.GetType()); + if (type == UtilConstants.DateType) + { + var date = value.ObjToDate(); + if (date < UtilMethods.GetMinDate(this.Context.CurrentConnectionConfig)) + { + date = UtilMethods.GetMinDate(this.Context.CurrentConnectionConfig); + } + return "'" + date.ToString("yyyy-MM-dd HH:mm:ss.fff") + "'"; + } + else if (type == UtilConstants.ByteArrayType) + { + string bytesString = "0x" + Convert.ToHexString((byte[])value); + return bytesString; + } + else if (type.IsEnum()) + { + if (this.Context.CurrentConnectionConfig.MoreSettings?.TableEnumIsString == true) + { + return value.ToSqlValue(); + } + else + { + return Convert.ToInt64(value); + } + } + else if (type == UtilConstants.BoolType) + { + return value.ObjToBool() ? "1" : "0"; + } + else if (type == UtilConstants.StringType || type == UtilConstants.ObjType) + { + return N + "'" + value.ToString().ToSqlFilter() + "'"; + } + else if (type == UtilConstants.DateTimeOffsetType) + { + return FormatDateTimeOffset(value); + } + else if (type == UtilConstants.FloatType) + { + return N + "'" + Convert.ToDouble(value).ToString() + "'"; + } + else if (value is decimal v) + { + return v.ToString(CultureInfo.InvariantCulture); + } + else + { + return N + "'" + value.ToString() + "'"; + } + } + } + + public virtual string FormatDateTimeOffset(object value) + { + var date = UtilMethods.ConvertFromDateTimeOffset((DateTimeOffset)value); + return "'" + date.ToString("yyyy-MM-dd HH:mm:ss.fff") + "'"; + } + + private int GetDbColumnIndex = 0; + public virtual string GetDbColumn(DbColumnInfo columnInfo, object name) + { + if (columnInfo.InsertServerTime) + { + return LambdaExpressions.DbMehtods.GetDate(); + } + else if (UtilMethods.IsErrorDecimalString() == true) + { + var pname = Builder.SqlParameterKeyWord + "Decimal" + GetDbColumnIndex; + var p = new SugarParameter(pname, columnInfo.Value); + this.Parameters.Add(p); + GetDbColumnIndex++; + return pname; + } + else if (columnInfo.InsertSql.HasValue()) + { + if (columnInfo.InsertSql.Contains("{0}")) + { + if (columnInfo.Value == null) + { + return string.Format(columnInfo.InsertSql, "null").Replace("'null'", "null"); + } + else + { + return string.Format(columnInfo.InsertSql, columnInfo.Value?.ObjToString().ToSqlFilter()); + } + } + return columnInfo.InsertSql; + } + else if (columnInfo.SqlParameterDbType is Type && IsNoParameterConvert(columnInfo)) + { + var type = columnInfo.SqlParameterDbType as Type; + var ParameterConverter = type.GetMethod("ParameterConverter").MakeGenericMethod(typeof(string)); + var obj = Activator.CreateInstance(type); + var p = ParameterConverter.Invoke(obj, new object[] { columnInfo.Value, GetDbColumnIndex }) as SugarParameter; + return p.ParameterName; + } + else if (columnInfo.SqlParameterDbType is Type) + { + var type = columnInfo.SqlParameterDbType as Type; + var ParameterConverter = type.GetMethod("ParameterConverter").MakeGenericMethod(columnInfo.PropertyType); + var obj = Activator.CreateInstance(type); + var p = ParameterConverter.Invoke(obj, new object[] { columnInfo.Value, GetDbColumnIndex }) as SugarParameter; + GetDbColumnIndex++; + //this.Parameters.RemoveAll(it => it.ParameterName == it.ParameterName); + UtilMethods.ConvertParameter(p, this.Builder); + this.Parameters.Add(p); + return p.ParameterName; + } + else if (columnInfo.DataType?.Equals("nvarchar2") == true) + { + var pname = Builder.SqlParameterKeyWord + columnInfo.DbColumnName + "_ts" + GetDbColumnIndex; + var p = new SugarParameter(pname, columnInfo.Value); + p.IsNvarchar2 = true; + this.Parameters.Add(p); + GetDbColumnIndex++; + return pname; + } + else if (columnInfo.PropertyType?.Name == "TimeOnly") + { + var timeSpan = UtilMethods.TimeOnlyToTimeSpan(columnInfo.Value); + var pname = Builder.SqlParameterKeyWord + columnInfo.DbColumnName + "_ts" + GetDbColumnIndex; + this.Parameters.Add(new SugarParameter(pname, timeSpan)); + GetDbColumnIndex++; + return pname; + } + else if (columnInfo.PropertyType?.Name == "DateOnly") + { + var timeSpan = UtilMethods.DateOnlyToDateTime(columnInfo.Value); + var pname = Builder.SqlParameterKeyWord + columnInfo.DbColumnName + "_ts" + GetDbColumnIndex; + if (timeSpan == null) + { + this.Parameters.Add(new SugarParameter(pname, null) { DbType = System.Data.DbType.Date }); + } + else + { + this.Parameters.Add(new SugarParameter(pname, Convert.ToDateTime(timeSpan))); + } + GetDbColumnIndex++; + return pname; + } + else if (UtilMethods.IsErrorParameterName(this.Context.CurrentConnectionConfig, columnInfo)) + { + var pname = Builder.SqlParameterKeyWord + "CrorrPara" + GetDbColumnIndex; + var p = new SugarParameter(pname, columnInfo.Value); + this.Parameters.Add(p); + GetDbColumnIndex++; + return pname; + + } + else + { + return name + ""; + } + } + + private static bool IsNoParameterConvert(DbColumnInfo columnInfo) + { + if (columnInfo.SqlParameterDbType is Type t) + { + var isAssignableFrom = typeof(DbConvert.NoParameterCommonPropertyConvert).IsAssignableFrom(t); + if (isAssignableFrom) + { + return isAssignableFrom; + } + } + return (Type)columnInfo.SqlParameterDbType == UtilConstants.SqlConvertType; + } + + #endregion + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SqlBuilderProvider/QueryBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SqlBuilderProvider/QueryBuilder.cs new file mode 100644 index 000000000..a7ae7307b --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SqlBuilderProvider/QueryBuilder.cs @@ -0,0 +1,1183 @@ +using System.Linq.Expressions; +using System.Reflection; +using System.Text; +using System.Text.RegularExpressions; + +namespace SqlSugar +{ + public abstract class QueryBuilder : IDMLBuilder + { + + public QueryBuilder() + { + this.Parameters = new List(); + } + + #region Private Fileds + protected List _JoinQueryInfos; + protected Dictionary _EasyJoinInfos; + private List _WhereInfos; + private string _HavingInfos; + protected string _TableNameString; + #endregion + + #region Service object + public StringBuilder sql { get; set; } + public SqlSugarProvider Context { get; set; } + public ILambdaExpressions LambdaExpressions { get; set; } + public ISqlBuilder Builder { get; set; } + #endregion + + #region Splicing basic + public List GroupParameters { get; set; } + public string GroupBySql { get; set; } + public string GroupBySqlOld { get; set; } + public Type AsType { get; set; } + public bool IsParameterizedConstructor { get; set; } + public string Hints { get; set; } + internal AppendNavInfo AppendNavInfo { get; set; } + public Type[] RemoveFilters { get; set; } + public Dictionary SubToListParameters { get; set; } + internal List AppendColumns { get; set; } + internal List> AppendValues { get; set; } + public bool IsCrossQueryWithAttr { get; set; } + public Dictionary CrossQueryItems { get; set; } + public bool IsSelectSingleFiledJson { get; set; } + public bool IsSelectSingleFiledArray { get; set; } + public string TranLock { get; set; } + public bool IsDisableMasterSlaveSeparation { get; set; } + public bool IsEnableMasterSlaveSeparation { get; set; } + public bool IsQueryInQuery { get; set; } + public List Includes { get; set; } + public List IgnoreColumns { get; set; } + public bool IsCount { get; set; } + public bool IsSqlQuery { get; set; } + public bool IsSqlQuerySelect { get; set; } + public int? Skip { get; set; } + public int ExternalPageIndex { get; set; } + public int ExternalPageSize { get; set; } + public int? Take { get; set; } + public bool DisableTop { get; set; } + public string SampleBy { get; set; } + public string OrderByValue { get; set; } + public object SelectValue { get; set; } + public string SelectCacheKey { get; set; } + public string EntityName { get; set; } + public string OldSql { get; set; } + + public Type EntityType { get; set; } + public Type ResultType { get; set; } + public string TableWithString { get; set; } + public string GroupByValue { get; set; } + public string PartitionByValue { get; set; } + public int WhereIndex { get; set; } + public bool IsDistinct { get; set; } + public int JoinIndex { get; set; } + public bool IsDisabledGobalFilter { get; set; } + public virtual List Parameters { get; set; } + public Expression JoinExpression { get; set; } + public Dictionary EasyJoinInfos + { + get + { + _EasyJoinInfos = UtilMethods.IsNullReturnNew(_EasyJoinInfos); + return _EasyJoinInfos; + } + set { _EasyJoinInfos = value; } + } + public virtual List JoinQueryInfos + { + get + { + _JoinQueryInfos = UtilMethods.IsNullReturnNew(_JoinQueryInfos); + return _JoinQueryInfos; + } + set { _JoinQueryInfos = value; } + } + public virtual string TableShortName { get; set; } + public virtual List WhereInfos + { + get + { + _WhereInfos = UtilMethods.IsNullReturnNew(_WhereInfos); + return _WhereInfos; + } + set { _WhereInfos = value; } + } + public virtual string HavingInfos + { + get + { + return _HavingInfos; + } + set + { + _HavingInfos = value; + } + } + #endregion + + #region Lambada Type + public ResolveExpressType SelectType + { + get + { + return this.IsSingle() ? ResolveExpressType.SelectSingle : ResolveExpressType.SelectMultiple; + } + } + public ResolveExpressType WheretType + { + get + { + return this.IsSingle() ? ResolveExpressType.WhereSingle : ResolveExpressType.WhereMultiple; + } + } + #endregion + + #region Sql Template + public Dictionary AsTables = new Dictionary(); + public virtual string SqlTemplate + { + get + { + if (this.SampleBy.HasValue()) + { + return "SELECT {0} FROM {1}{2} " + this.SampleBy + " {3}{4}"; + } + return "SELECT {0} FROM {1}{2}{3}{4} "; + } + } + public virtual string JoinTemplate + { + get + { + return "{0}JOIN {1}{2}ON {3} "; + } + } + public virtual string PageTempalte + { + get + { + return @"SELECT * FROM ({0}) T WHERE RowIndex BETWEEN {1} AND {2}"; + } + } + public virtual string ExternalPageTempalte + { + get + { + return @"SELECT * FROM ({0}) T WHERE RowIndex2 BETWEEN {1} AND {2}"; + } + } + public virtual string DefaultOrderByTemplate + { + get + { + return "ORDER BY " + this.Builder.SqlDateNow + " "; + } + } + public virtual string OrderByTemplate + { + get + { + return "ORDER BY "; + } + } + public virtual string GroupByTemplate + { + get + { + return "GROUP BY "; + } + } + public virtual string PartitionByTemplate + { + get + { + return "PARTITION BY "; + } + } + public virtual string MaxTemplate + { + get + { + return "MAX({0})"; + } + } + public virtual string MinTemplate + { + get + { + return "MIN({0})"; + } + } + public virtual string SumTemplate + { + get + { + return "SUM({0})"; + } + } + public virtual string AvgTemplate + { + get + { + return "AVG({0})"; + } + } + public virtual string InTemplate + { + get + { + return "{0} IN ({1}) "; + } + } + public virtual string EqualTemplate + { + get + { + return "{0} = {1} "; + } + } + #endregion + + #region Common Methods + public virtual bool IsSingle() + { + var isSingle = Builder.QueryBuilder.JoinQueryInfos.IsNullOrEmpty() && EasyJoinInfos.Count == 0; + return isSingle; + } + public virtual ExpressionResult GetExpressionValue(Expression expression, ResolveExpressType resolveType) + { + ILambdaExpressions resolveExpress = this.LambdaExpressions; + if (resolveType.IsIn(ResolveExpressType.FieldSingle, ResolveExpressType.FieldMultiple, ResolveExpressType.SelectSingle, ResolveExpressType.SelectMultiple) && (expression is LambdaExpression) && (expression as LambdaExpression).Body is BinaryExpression) + { + resolveType = resolveType.IsIn(ResolveExpressType.SelectSingle, ResolveExpressType.FieldSingle) ? ResolveExpressType.WhereSingle : ResolveExpressType.WhereMultiple; + } + this.LambdaExpressions.Clear(); + if (this.Context.CurrentConnectionConfig.MoreSettings != null) + { + resolveExpress.TableEnumIsString = this.Context.CurrentConnectionConfig.MoreSettings.TableEnumIsString; + resolveExpress.PgSqlIsAutoToLower = this.Context.CurrentConnectionConfig.MoreSettings.PgSqlIsAutoToLower; + } + else + { + resolveExpress.PgSqlIsAutoToLower = true; + } + resolveExpress.SugarContext = new ExpressionOutParameter() { Context = this.Context, QueryBuilder = this }; + resolveExpress.RootExpression = expression; + resolveExpress.JoinQueryInfos = Builder.QueryBuilder.JoinQueryInfos; + resolveExpress.IsSingle = IsSingle() && resolveType != ResolveExpressType.WhereMultiple; + resolveExpress.MappingColumns = Context.MappingColumns; + resolveExpress.MappingTables = Context.MappingTables; + resolveExpress.IgnoreComumnList = Context.IgnoreColumns; + resolveExpress.SqlFuncServices = Context.CurrentConnectionConfig.ConfigureExternalServices == null ? null : Context.CurrentConnectionConfig.ConfigureExternalServices.SqlFuncServices; + resolveExpress.InitMappingInfo = this.Context.InitMappingInfo; + resolveExpress.RefreshMapping = () => + { + resolveExpress.MappingColumns = Context.MappingColumns; + resolveExpress.MappingTables = Context.MappingTables; + resolveExpress.IgnoreComumnList = Context.IgnoreColumns; + resolveExpress.SqlFuncServices = Context.CurrentConnectionConfig.ConfigureExternalServices == null ? null : Context.CurrentConnectionConfig.ConfigureExternalServices.SqlFuncServices; + }; + resolveExpress.Resolve(expression, resolveType); + this.Parameters.AddRange(resolveExpress.Parameters.Select(it => new SugarParameter(it.ParameterName, it.Value, it.DbType) { Size = it.Size, TypeName = it.TypeName, IsNvarchar2 = it.IsNvarchar2 })); + var result = resolveExpress.Result; + var isSingleTableHasSubquery = IsSingle() && resolveExpress.SingleTableNameSubqueryShortName.HasValue(); + if (isSingleTableHasSubquery) + { + if (this.TableShortName?.StartsWith('\"') == true) + { + Check.Exception(!string.IsNullOrEmpty(this.TableShortName) && resolveExpress.SingleTableNameSubqueryShortName != this.TableShortName.TrimEnd('\"').TrimStart('\"'), "{0} and {1} need same name", resolveExpress.SingleTableNameSubqueryShortName, this.TableShortName); + this.TableShortName = resolveExpress.SingleTableNameSubqueryShortName; + } + else + { + Check.Exception(!string.IsNullOrEmpty(this.TableShortName) && resolveExpress.SingleTableNameSubqueryShortName != this.TableShortName, "{0} and {1} need same name", resolveExpress.SingleTableNameSubqueryShortName, this.TableShortName); + this.TableShortName = resolveExpress.SingleTableNameSubqueryShortName; + } + } + return result; + } + + internal string GetFilters(Type type) + { + var result = ""; + if (this.Context != null) + { + var db = Context; + BindingFlags flag = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public; + var index = 0; + if (db.QueryFilter.GetFilterList != null) + { + foreach (var item in db.QueryFilter.GetFilterList) + { + if (this.RemoveFilters?.Length > 0) + { + if (this.RemoveFilters.Contains(item.type)) + { + continue; + } + } + + PropertyInfo field = item.GetType().GetProperty("exp", flag); + if (field != null) + { + Type ChildType = item.type; + var isInterface = ChildType.IsInterface && type.GetInterfaces().Any(it => it == ChildType); + if (ChildType == type || isInterface) + { + var entityInfo = db.EntityMaintenance.GetEntityInfo(ChildType); + var exp = field.GetValue(item, null) as Expression; + var whereStr = index == 0 ? " " : " AND "; + index++; + result += (whereStr + GetExpressionValue(exp, ResolveExpressType.WhereSingle).GetString()); + if (isInterface) + { + result = ReplaceFilterColumnName(result, type); + } + } + } + } + } + } + return result; + } + + public virtual string ToSqlString() + { + string oldOrderBy = this.OrderByValue; + string externalOrderBy = oldOrderBy; + var isIgnoreOrderBy = this.IsCount && this.PartitionByValue.IsNullOrEmpty(); + AppendFilter(); + sql = new StringBuilder(); + if (this.OrderByValue == null && (Skip != null || Take != null)) this.OrderByValue = " ORDER BY GetDate() "; + if (this.PartitionByValue.HasValue()) + { + this.OrderByValue = this.PartitionByValue + this.OrderByValue; + } + var isRowNumber = Skip != null || Take != null; + var rowNumberString = string.Format(",ROW_NUMBER() OVER({0}) AS RowIndex ", GetOrderByString); + string groupByValue = GetGroupByString + HavingInfos; + string orderByValue = (!isRowNumber && this.OrderByValue.HasValue()) ? GetOrderByString : null; + if (isIgnoreOrderBy) { orderByValue = null; } + sql.AppendFormat(SqlTemplate, GetSelectValue, GetTableNameString, GetWhereValueString, groupByValue, orderByValue); + sql.Replace(UtilConstants.ReplaceKey, isRowNumber ? (isIgnoreOrderBy ? null : rowNumberString) : null); + if (isIgnoreOrderBy) { this.OrderByValue = oldOrderBy; return sql.ToString(); } + var result = ToPageSql(sql.ToString(), this.Take, this.Skip); + if (ExternalPageIndex > 0) + { + if (externalOrderBy.IsNullOrEmpty()) + { + externalOrderBy = " ORDER BY GetDate() "; + } + result = string.Format("SELECT *,ROW_NUMBER() OVER({0}) AS RowIndex2 FROM ({1}) ExternalTable ", GetExternalOrderBy(externalOrderBy), result); + result = ToPageSql2(result, ExternalPageIndex, ExternalPageSize, true); + } + this.OrderByValue = oldOrderBy; + return result; + } + + public virtual void AppendFilter() + { + if (!IsDisabledGobalFilter && this.Context.QueryFilter.GetFilterList.HasValue()) + { + var gobalFilterList = this.Context.QueryFilter.GetFilterList.Where(it => it.FilterName.IsNullOrEmpty()).ToList(); + if (this.RemoveFilters?.Length > 0) + { + gobalFilterList = gobalFilterList.Where(it => !this.RemoveFilters.Contains(it.type)).ToList(); + } + foreach (var item in gobalFilterList) + { + if (item.GetType().Name.StartsWith("TableFilterItem")) + { + AppendTableFilter(item); + } + } + foreach (var item in gobalFilterList.Where(it => it.GetType().Name == "SqlFilterItem").Where(it => it.IsJoinQuery == !IsSingle())) + { + var filterResult = item.FilterValue(this.Context); + WhereInfos.Add(this.Builder.AppendWhereOrAnd(this.WhereInfos.IsNullOrEmpty(), filterResult.Sql + UtilConstants.Space)); + var filterParamters = this.Context.Ado.GetParameters(filterResult.Parameters); + if (filterParamters.HasValue()) + { + this.Parameters.AddRange(filterParamters); + } + } + } + } + + private void AppendTableFilter(SqlFilterItem item) + { + BindingFlags flag = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public; + Type type = item.GetType(); + PropertyInfo field = type.GetProperty("exp", flag); + Type ChildType = item.type; + var entityInfo = this.Context.EntityMaintenance.GetEntityInfoWithAttr(ChildType); + var exp = field.GetValue(item, null) as Expression; + var isMain = ChildType == this.EntityType || (ChildType.IsInterface && this.EntityType.GetInterfaces().Any(it => it == ChildType)); + var isSingle = IsSingle(); + var itName = (exp as LambdaExpression).Parameters[0].Name; + itName = this.Builder.GetTranslationColumnName(itName) + "."; + var isEasyJoin = this.EasyJoinInfos.Count > 0; + + string sql = ""; + if (isSingle) + { + if (ChildType.IsInterface && this.EntityType.GetInterfaces().Any(it => it == ChildType)) + { + //future + } + else if (ChildType != this.EntityType && isSingle) + { + return; + } + sql = GetSql(exp, isSingle); + if (ChildType.IsInterface) + { + var filterType = this.EntityType; + sql = ReplaceFilterColumnName(sql, filterType); + } + } + else if (isEasyJoin && ChildType.IsInterface && this.JoinExpression != null && (this.JoinExpression as LambdaExpression)?.Parameters?.Any(it => it.Type.GetInterfaces().Any(z => z == ChildType)) == true) + { + var parameters = (this.JoinExpression as LambdaExpression).Parameters.Where(it => it.Type.GetInterfaces().Any(z => z == ChildType)).ToList(); + foreach (var parameter in parameters) + { + var shortName = this.Builder.GetTranslationColumnName(parameter.Name) + "."; + var mysql = GetSql(exp, isSingle); + sql += mysql.Replace(itName, shortName); + var filterType = parameter.Type; + sql = ReplaceFilterColumnName(sql, filterType); + } + } + else if (isMain) + { + if (TableShortName == null) + return; + + var isSameName = !isSingle && JoinQueryInfos.Any(it => it.TableName == entityInfo.DbTableName); + if (isSameName || ChildType.IsInterface) + { + var mysql = GetSql(exp, isSingle); + if (ChildType.IsInterface && item.IsJoinQuery == true) + { + foreach (var joinInfoItem in this.JoinQueryInfos.Where(it => it.EntityType.GetInterfaces().Any(z => z == ChildType))) + { + var addSql = mysql.Replace(itName, this.Builder.GetTranslationColumnName(joinInfoItem.ShortName) + "."); + addSql = ReplaceFilterColumnName(addSql, joinInfoItem.EntityType, joinInfoItem.ShortName); + joinInfoItem.JoinWhere += (" AND " + Regex.Replace(addSql, "^ (WHERE|AND) ", "")); + } + } + else if (ChildType.IsInterface && item.IsJoinQuery == false) + { + { + var addSql = mysql.Replace(itName, this.Builder.GetTranslationColumnName(TableShortName) + "."); + addSql = ReplaceFilterColumnName(addSql, EntityType, TableShortName); + var andOrWhere = this.WhereInfos.Count != 0 ? " AND " : "WHERE"; + this.WhereInfos.Add(andOrWhere + Regex.Replace(addSql, "^ (WHERE|AND) ", "")); + } + foreach (var joinInfoItem in this.JoinQueryInfos.Where(it => it.EntityType.GetInterfaces().Any(z => z == ChildType))) + { + var addSql = mysql.Replace(itName, this.Builder.GetTranslationColumnName(joinInfoItem.ShortName) + "."); + addSql = ReplaceFilterColumnName(addSql, joinInfoItem.EntityType, joinInfoItem.ShortName); + this.WhereInfos.Add(" AND " + Regex.Replace(addSql, "^ (WHERE|AND) ", "")); + } + return; + } + else + { + foreach (var joinInfoItem in this.JoinQueryInfos.Where(it => it.TableName == entityInfo.DbTableName)) + { + var addSql = mysql.Replace(itName, this.Builder.GetTranslationColumnName(joinInfoItem.ShortName) + "."); + addSql = ReplaceFilterColumnName(addSql, joinInfoItem.EntityType, joinInfoItem.ShortName); + joinInfoItem.JoinWhere += (" AND " + Regex.Replace(addSql, "^ (WHERE|AND) ", "")); + } + } + sql = mysql.Replace(itName, this.Builder.GetTranslationColumnName(TableShortName) + "."); + } + else + { + var shortName = this.Builder.GetTranslationColumnName(TableShortName) + "."; + sql = GetSql(exp, isSingle); + sql = sql.Replace(itName, shortName); + } + + if (ChildType.IsInterface) + { + sql = ReplaceFilterColumnName(sql, this.EntityType); + } + } + else if (isEasyJoin) + { + var easyInfo = EasyJoinInfos.FirstOrDefault(it => + it.Value.Equals(entityInfo.DbTableName, StringComparison.CurrentCultureIgnoreCase) || + it.Value.Equals(entityInfo.EntityName, StringComparison.CurrentCultureIgnoreCase)); + if (easyInfo.Key == null) + { + return; + } + var shortName = this.Builder.GetTranslationColumnName(easyInfo.Key.Trim()) + "."; + sql = GetSql(exp, isSingle); + sql = sql.Replace(itName, shortName); + } + else + { + var easyInfo = JoinQueryInfos.FirstOrDefault(it => + it.TableName.Equals(entityInfo.DbTableName, StringComparison.CurrentCultureIgnoreCase) || + it.TableName.Equals(entityInfo.EntityName, StringComparison.CurrentCultureIgnoreCase) || + it.EntityType == ChildType); + if (easyInfo == null) + { + if (ChildType.IsInterface && JoinQueryInfos.Any(it => it.EntityType?.GetInterfaces().Any(z => z == ChildType) == true)) + { + easyInfo = JoinQueryInfos.FirstOrDefault(it => it.EntityType.GetInterfaces().Any(z => z == ChildType)); + } + else + { + return; + } + } + var shortName = this.Builder.GetTranslationColumnName(easyInfo.ShortName.Trim()) + "."; + sql = GetSql(exp, isSingle); + sql = sql.Replace(itName, shortName); + } + if (item.IsJoinQuery == false || isMain || isSingle || isEasyJoin) + { + if (item.IsJoinQuery == false && ChildType.IsInterface) + { + foreach (var joinInfo in this.JoinQueryInfos) + { + sql = ReplaceFilterColumnName(sql, joinInfo.EntityType, Builder.GetTranslationColumnName(joinInfo.ShortName)); + } + } + WhereInfos.Add(sql); + } + else + { + var isSameName = !isSingle && this.JoinQueryInfos.Count(it => it.TableName == entityInfo.DbTableName) > 1; + foreach (var joinInfo in this.JoinQueryInfos) + { + var isInterface = ChildType.IsInterface && joinInfo.EntityType?.GetInterfaces().Any(it => it == ChildType) == true; + if (isInterface + && isSameName == false + && this.JoinQueryInfos.Where(it => it.EntityType != null).Count(it => it.EntityType.GetInterfaces().Any(z => z == ChildType)) > 1) + { + sql = GetSql(exp, false); + var shortName = this.Builder.GetTranslationColumnName(joinInfo.ShortName.Trim()) + "."; + sql = sql.Replace(itName, shortName); + } + if (isInterface || joinInfo.TableName.EqualCase(entityInfo.EntityName) || joinInfo.TableName.EqualCase(entityInfo.DbTableName) || joinInfo.EntityType == ChildType) + { + var mysql = sql; + if (isSameName) + { + var firstShortName = this.JoinQueryInfos.First(it => it.TableName == entityInfo.DbTableName).ShortName; + mysql = mysql.Replace(Builder.GetTranslationColumnName(firstShortName), Builder.GetTranslationColumnName(joinInfo.ShortName)); + } + if (mysql.StartsWith(" WHERE ")) + { + mysql = Regex.Replace(mysql, $"^ WHERE ", " AND "); + } + if (isInterface) + { + mysql = ReplaceFilterColumnName(mysql, joinInfo.EntityType, Builder.GetTranslationColumnName(joinInfo.ShortName)); + } + joinInfo.JoinWhere = joinInfo.JoinWhere + mysql; + } + } + } + } + + private string ReplaceFilterColumnName(string sql, Type filterType, string shortName = null) + { + foreach (var column in this.Context.EntityMaintenance.GetEntityInfoWithAttr(filterType).Columns.Where(it => it.IsIgnore == false)) + { + if (shortName == null) + { + sql = sql.Replace(Builder.GetTranslationColumnName(column.PropertyName), Builder.GetTranslationColumnName(column.DbColumnName)); + } + else + { + sql = sql.Replace(Builder.GetTranslationColumnName(shortName) + "." + Builder.GetTranslationColumnName(column.PropertyName), Builder.GetTranslationColumnName(shortName) + "." + Builder.GetTranslationColumnName(column.DbColumnName)); + sql = sql.Replace(shortName + "." + Builder.GetTranslationColumnName(column.PropertyName), shortName + "." + Builder.GetTranslationColumnName(column.DbColumnName)); + } + } + return sql; + } + + private string GetSql(Expression exp, bool isSingle) + { + var expValue = GetExpressionValue(exp, isSingle ? ResolveExpressType.WhereSingle : ResolveExpressType.WhereMultiple); + var sql = expValue.GetResultString(); + if (WhereInfos.Count == 0) + { + sql = (" WHERE " + sql); + } + else + { + sql = (" AND " + sql); + } + + return sql; + } + + public virtual string GetExternalOrderBy(string externalOrderBy) + { + return Regex.Replace(externalOrderBy, @"\[\w+\]\.", ""); + } + + public virtual string ToCountSql(string sql) + { + if (sql?.Contains("-- No table") == true) + { + return "-- No table"; + } + return string.Format(" SELECT COUNT(1) FROM ({0}) CountTable ", sql); + } + + public virtual string ToPageSql(string sql, int? take, int? skip, bool isExternal = false) + { + string temp = isExternal ? ExternalPageTempalte : PageTempalte; + if (skip != null && take == null) + { + return string.Format(temp, sql.ToString(), skip.ObjToInt() + 1, long.MaxValue); + } + else if (skip == null && take != null) + { + return string.Format(temp, sql.ToString(), 1, take.ObjToInt()); + } + else if (skip != null && take != null) + { + return string.Format(temp, sql.ToString(), skip.ObjToInt() + 1, skip.ObjToInt() + take.ObjToInt()); + } + else + { + return sql.ToString(); + } + } + + public virtual string ToPageSql2(string sql, int? pageIndex, int? pageSize, bool isExternal = false) + { + string temp = isExternal ? ExternalPageTempalte : PageTempalte; + return string.Format(temp, sql.ToString(), (pageIndex - 1) * pageSize + 1, pageIndex * pageSize); + } + + public virtual string GetSelectByItems(List> items) + { + var array = items.Select(it => + { + JoinMapper dynamicObj = it.Value; + var dbName = Builder.GetTranslationColumnName(dynamicObj.DbName); + var asName = Builder.GetTranslationColumnName(dynamicObj.AsName); + return string.Format("{0}.{1} AS {2}", it.Key, dbName, asName); + }); + return string.Join(",", array); + } + + public virtual string ToJoinString(JoinQueryInfo joinInfo) + { + var name = joinInfo.TableName; + if (this.AsTables.Count != 0) + { + if (this.Context.MappingTables?.Any(it => it.DbTableName == name) == true) + { + name = this.Context.MappingTables.First(it => it.DbTableName == name).EntityName; + } + if (this.AsTables.Any(it => it.Key == name)) + { + name = this.AsTables.First(it => it.Key == name).Value; + } + } + var isSubQuery = name?.StartsWith('(') == true && name.EndsWith(')'); + var shortName = joinInfo.ShortName; + if (shortName.HasValue()) + { + shortName = this.Builder.GetTranslationColumnName(shortName); + } + var result = string.Format( + this.JoinTemplate, + joinInfo.JoinType.ToString() + UtilConstants.Space, + Builder.GetTranslationTableName(name) + UtilConstants.Space, + shortName + UtilConstants.Space + (TableWithString == SqlWith.Null || isSubQuery ? " " : TableWithString), + joinInfo.JoinWhere); + if (joinInfo.EntityType != null && this.Context.EntityMaintenance.GetEntityInfoWithAttr(joinInfo.EntityType).Discrimator.HasValue()) + { + var entityInfo = this.Context.EntityMaintenance.GetEntityInfoWithAttr(joinInfo.EntityType); + result = $" {result} AND {shortName}.{UtilMethods.GetDiscrimator(entityInfo, this.Builder)}"; + } + if (joinInfo.JoinType == JoinType.Cross) + { + + var onIndex = result.IndexOf(" ON "); + result = result.Substring(0, onIndex); + } + return result; + } + public virtual void Clear() + { + this.Skip = 0; + this.Take = 0; + this.sql = null; + this.WhereIndex = 0; + this.Parameters = null; + this.GroupByValue = null; + this._TableNameString = null; + this.WhereInfos = null; + this.JoinQueryInfos = null; + this.IsDistinct = false; + } + public virtual bool IsComplexModel(string sql) + { + return Regex.IsMatch(sql, @"AS \[\w+\.\w+\]") || Regex.IsMatch(sql, @"AS \[\w+\.\w+\.\w+\]"); + } + public string GetSqlQuerySql(string result) + { + if (this.Hints.HasValue()) + { + result = ReplaceHints(result); + } + if (GetTableNameString == " (-- No table ) t ") + { + result = "-- No table "; + return result; + } + if (this.IsSqlQuerySelect == true) + { + return result; + } + if (this.JoinQueryInfos.Count == 0 && string.IsNullOrEmpty(OrderByValue) && this.IsSqlQuery && this.OldSql.HasValue() && (Skip == null && Take == null) && (this.WhereInfos == null || this.WhereInfos.Count == 0)) + { + return this.OldSql; + } + else + { + return result; + } + } + + protected virtual string ReplaceHints(string result) + { + result = Regex.Replace(result, "^SELECT ", it => + { + return $"{it} {Hints} "; + }); + return result; + } + + protected string SubToListMethod(string result) + { + string oldResult = result; + List names = new List(); + var allShortName = new List(); + if (IsSingleSubToList()) + { + this.TableShortName = (SelectValue as LambdaExpression).Parameters[0].Name; + } + allShortName.Add(this.Builder.SqlTranslationLeft + Builder.GetNoTranslationColumnName(this.TableShortName.ObjToString().ToLower()) + this.Builder.SqlTranslationRight + "."); + if (this.JoinQueryInfos.HasValue()) + { + foreach (var item in this.JoinQueryInfos) + { + allShortName.Add(this.Builder.SqlTranslationLeft + Builder.GetNoTranslationColumnName(item.ShortName.ObjToString().ToLower()) + this.Builder.SqlTranslationRight + "."); + } + } + else if (this.EasyJoinInfos != null && this.EasyJoinInfos.Count != 0) + { + Check.ExceptionEasy("No Supprt Subquery.ToList(), Inner Join Or Left Join", "Subquery.ToList请使用Inner方式联表"); + } + if (this.TableShortName == null) + { + //Empty + } + else + { + var name = Builder.GetTranslationColumnName(this.TableShortName) + @"\."; + foreach (var paramter in this.SubToListParameters) + { + var regex = $@"\{Builder.SqlTranslationLeft}[\w]{{1,20}}?\{Builder.SqlTranslationRight}\.\{Builder.SqlTranslationLeft}.{{1,50}}?\{Builder.SqlTranslationRight}"; + var matches = Regex + .Matches(paramter.Value.ObjToString(), regex, RegexOptions.IgnoreCase).Cast() + .Where(it => allShortName.Any(z => it.Value.ObjToString().Contains(z, StringComparison.CurrentCultureIgnoreCase))) + .Select(it => it.Value).ToList(); + names.AddRange(matches); + } + int i = 0; + names = names.Distinct().ToList(); + if (names.Count != 0) + { + List colums = new List(); + foreach (var item in names) + { + result = (result + $",{item} as app_ext_col_{i}"); + colums.Add(new QueryableAppendColumn() { AsName = $"app_ext_col_{i}", Name = item, Index = i }); + i++; + } + this.AppendColumns = colums; + } + } + if (HasAppText(oldResult)) + { + return oldResult; + } + return result; + } + + #endregion + + #region Get SQL Partial + public string Offset { get; set; } + public virtual string GetSelectValue + { + get + { + string result = string.Empty; + if (this.SelectValue == null || this.SelectValue is string) + { + result = GetSelectValueByString(); + } + else + { + result = GetSelectValueByExpression(); + } + if (this.SelectType == ResolveExpressType.SelectMultiple) + { + this.SelectCacheKey = this.SelectCacheKey + string.Join("-", this._JoinQueryInfos.Select(it => it.TableName)); + } + if (IsDistinct && result?.StartsWith("DISTINCT ") != true) + { + result = " DISTINCT " + result; + } + if (this.SubToListParameters != null && this.SubToListParameters.Count != 0) + { + result = SubToListMethod(result); + } + return result; + } + } + + public virtual string GetSelectValueByExpression() + { + var expression = this.SelectValue as Expression; + string result = string.Empty; + if (this.IgnoreColumns != null && this.IgnoreColumns.Count != 0) + { + var expArray = GetExpressionValue(expression, this.SelectType).GetResultArray() + .Where(it => + !this.IgnoreColumns.Any(z => it.Contains(Builder.GetTranslationColumnName(z))) + ).ToArray(); + result = string.Join(",", expArray); + } + else + { + result = GetExpressionValue(expression, this.SelectType).GetResultString(); + if (result == null && ExpressionTool.GetMethodName(ExpressionTool.GetLambdaExpressionBody(expression)) == "End") + { + result = GetExpressionValue(expression, ResolveExpressType.FieldSingle).GetResultString(); + } + } + if (result == null && this.AppendNavInfo?.AppendProperties == null) + { + return "*"; + } + if (this.AppendNavInfo?.AppendProperties?.Count > 0) + { + if (result == null) + { + result = "*"; + } + result += ","; + var shortName = ""; + if (this.TableShortName.HasValue()) + { + shortName = $"{Builder.GetTranslationColumnName(this.TableShortName)}."; + } + if (this.GroupByValue.HasValue()) + { + result += string.Join(",", this.AppendNavInfo.AppendProperties.Select(it => "max(" + shortName + Builder.GetTranslationColumnName(it.Value) + ") AS SugarNav_" + it.Key)); + } + else + { + result += string.Join(",", this.AppendNavInfo.AppendProperties.Select(it => shortName + Builder.GetTranslationColumnName(it.Value) + " AS SugarNav_" + it.Key)); + } + } + if (result.Contains("/**/*")) + { + return result.Replace("/**/*", ""); + } + if (this.IsSingle() && this.SubToListParameters != null && expression is LambdaExpression && this.SubToListParameters.Count > 0 && this.TableShortName == null) + { + this.TableShortName = this.Builder.GetTranslationColumnName((expression as LambdaExpression).Parameters[0].Name); + } + if (result.Contains(".*") && this.IsSingle()) + { + return "*"; + } + else + { + if (expression is LambdaExpression && (expression as LambdaExpression).Body is MethodCallExpression && this.Context.CurrentConnectionConfig.DbType == DbType.SqlServer && this.OrderByValue.HasValue()) + { + result = result + " AS columnName"; + } + if (this.GroupParameters?.Count > 0 && this.GroupBySql.HasValue()) + { + var selectSql = UtilMethods.GetSqlString(DbType.SqlServer, result, UtilMethods.CopySugarParameters(this.Parameters).ToArray()); + if (selectSql.Contains(this.GroupBySql)) + { + result = selectSql; + this.GroupByIsReplace = true; + } + } + this.SelectCacheKey = result; + return result; + } + } + public virtual string GetSelectValueByString() + { + string result; + if (this.SelectValue.IsNullOrEmpty()) + { + string pre = null; + if (this.JoinQueryInfos.HasValue() && this.JoinQueryInfos.Any(it => TableShortName.HasValue())) + { + pre = Builder.GetTranslationColumnName(TableShortName) + "."; + } + else if (this.EasyJoinInfos?.Count > 0 && this.EasyJoinInfos.Any(it => TableShortName.HasValue())) + { + pre = Builder.GetTranslationColumnName(TableShortName) + "."; + } + var columns = this.Context.EntityMaintenance.GetEntityInfo(this.EntityType).Columns.Where(it => !it.IsIgnore); + if (this.IgnoreColumns.HasValue()) + { + columns = columns.Where(c => !this.IgnoreColumns.Any(i => c.PropertyName.Equals(i, StringComparison.CurrentCultureIgnoreCase) || c.DbColumnName.Equals(i, StringComparison.CurrentCultureIgnoreCase))).ToList(); + } + result = string.Join(",", columns.Select(it => GetSelectStringByColumnInfo(it, pre))); + } + else + { + result = this.SelectValue.ObjToString(); + this.SelectCacheKey = result; + } + if (result.IsNullOrEmpty()) + { + result = "*"; + } + if (result.StartsWith(UtilConstants.GroupReplaceKey)) + { + this.GroupByIsReplace = true; + result = result.Replace(UtilConstants.GroupReplaceKey, string.Empty); + } + return result; + } + + private string GetSelectStringByColumnInfo(EntityColumnInfo it, string pre) + { + if (it.QuerySql.HasValue()) + { + return it.QuerySql + " AS " + Builder.GetTranslationColumnName(it.EntityName, it.PropertyName); + } + return pre + Builder.GetTranslationColumnName(it.EntityName, it.PropertyName); + } + + public virtual string GetWhereValueString + { + get + { + if (this.WhereInfos == null) return null; + else + { + return string.Join(UtilConstants.Space, this.WhereInfos); + } + } + } + public virtual string GetJoinValueString + { + get + { + if (this.JoinQueryInfos.IsNullOrEmpty()) return null; + else + { + return string.Join(UtilConstants.Space, this.JoinQueryInfos.Select(it => this.ToJoinString(it))); + } + } + } + public virtual string MasterDbTableName { get; set; } + public virtual string GetTableNameString + { + get + { + var name = EntityName; + if (this.AsTables.Any(it => it.Key == EntityName)) + { + name = this.AsTables.FirstOrDefault(it => it.Key == EntityName).Value; + if (this.IsQueryInQuery && this.SelectValue != null && this.SelectValue is Expression) + { + if (this.SelectValue.ToString().Contains("Subqueryable()") && name.TrimStart().StartsWith('(')) + { + var oldName = name; + name = Regex.Match(name, @"\(.+\)").Value; + if (name.IsNullOrEmpty()) + { + name = oldName; + } + } + } + } + var result = Builder.GetTranslationTableName(name); + result += UtilConstants.Space; + if (MasterDbTableName.HasValue()) + { + result = MasterDbTableName; + } + if (IsSingle() && result.Contains("MergeTable") && result.Trim().EndsWith(" MergeTable") && TableShortName != null) + { + result = result.Replace(") MergeTable ", ") " + this.Builder.GetTranslationColumnName(TableShortName) + UtilConstants.Space); + TableShortName = null; + } + if (IsSingle() && result.Contains("unionTable") && result.Trim().EndsWith(" unionTable") && TableShortName != null) + { + result = result.Replace(" ) unionTable ", ") " + TableShortName + UtilConstants.Space); + TableShortName = null; + } + if (this.TableShortName.HasValue() && !IsSqlQuery) + { + result += (Builder.GetTranslationColumnName(TableShortName) + UtilConstants.Space); + } + if (this.TableWithString.HasValue() && this.TableWithString != SqlWith.Null) + { + if (!result.TrimStart().StartsWith('(')) + { + result += TableWithString + UtilConstants.Space; + } + } + if (!this.IsSingle()) + { + result += GetJoinValueString + UtilConstants.Space; + } + if (this.EasyJoinInfos.IsValuable()) + { + + if (this.TableWithString.HasValue() && this.TableWithString != SqlWith.Null) + { + result += "," + string.Join(",", this.EasyJoinInfos.Select(it => string.Format("{0} {1} {2} ", GetTableName(it.Value), " " + Builder.GetTranslationColumnName(it.Key.ObjToString().Trim()), TableWithString))); + } + else + { + result += "," + string.Join(",", this.EasyJoinInfos.Select(it => string.Format("{0} {1} ", GetTableName(it.Value), " " + Builder.GetTranslationColumnName(it.Key.ObjToString().Trim())))); + } + } + return result; + } + } + public virtual string GetOrderByString + { + get + { + if (this.OrderByValue == null) return null; + if (IsCount && this.PartitionByValue.IsNullOrEmpty()) return null; + else + { + return this.OrderByValue; + } + } + } + public virtual string GetGroupByString + { + get + { + if (this.GroupByValue == null) return null; + if (this.GroupByValue.Last() != ' ') + { + return this.GroupByValue + UtilConstants.Space; + } + return this.GroupByValue; + } + } + + + #endregion + + #region NoCopy + + internal bool GroupByIsReplace { get; set; } + internal List QueryableFormats { get; set; } + internal bool IsClone { get; set; } + public bool NoCheckInclude { get; set; } + public virtual bool IsSelectNoAll { get; set; } = false; + public List AutoAppendedColumns { get; set; } + public Dictionary MappingKeys { get; set; } + public List> SelectNewIgnoreColumns { get; set; } + #endregion + + private string GetTableName(string entityName) + { + if (this.AsTables?.Any(it => it.Key == entityName) == true) + { + entityName = this.AsTables.First(it => it.Key == entityName).Value; + } + var result = this.Context.EntityMaintenance.GetTableName(entityName); + return this.Builder.GetTranslationTableName(result); + } + + public void CheckExpression(Expression expression, string methodName) + { + if (IsSingle() == false && this.JoinExpression != null) + { + var jsoinParameters = (this.JoinExpression as LambdaExpression).Parameters; + var currentParametres = (expression as LambdaExpression).Parameters; + if ((expression as LambdaExpression).Body.ToString() == "True") + { + return; + } + if (currentParametres?.Count > 0) + { + foreach (var item in currentParametres) + { + var index = currentParametres.IndexOf(item); + var name = item.Name; + var joinName = jsoinParameters[index].Name; + Check.Exception(!name.Equals(joinName, StringComparison.CurrentCultureIgnoreCase), ErrorMessage.ExpressionCheck, joinName, methodName, name); + } + } + } + } + public void CheckExpressionNew(Expression expression, string methodName) + { + if (IsSingle() == false && this.JoinExpression != null) + { + var jsoinParameters = (this.JoinExpression as LambdaExpression).Parameters; + var currentParametres = (expression as LambdaExpression).Parameters; + if ((expression as LambdaExpression).Body.ToString() == "True") + { + return; + } + if (currentParametres?.Count > 0) + { + if (jsoinParameters.Count + 1 != currentParametres.Count) + { + var str1 = "(" + string.Join(",", currentParametres.Select(it => it.Name)) + ")=>"; + var str2 = "(" + string.Join(",", jsoinParameters.Select(it => it.Name)) + "," + currentParametres.Last().Type.Name + " )=>"; + throw new Exception(ErrorMessage.GetThrowMessage($"Join {currentParametres.Last().Type.Name} error , Please change {str1} to {str2}.", $"Join {currentParametres.Last().Type.Name} 错误, 请把 {str1} 改成 {str2} ")); + } + foreach (var item in currentParametres.Take(jsoinParameters.Count)) + { + var index = currentParametres.IndexOf(item); + var name = item.Name; + var joinName = jsoinParameters[index].Name; + Check.Exception(!name.Equals(joinName, StringComparison.CurrentCultureIgnoreCase), ErrorMessage.ExpressionCheck, joinName, methodName, name); + } + } + } + } + private bool IsSingleSubToList() + { + return this.SubToListParameters != null + && this.TableShortName == null + && this.SelectValue is Expression + && this.IsSingle(); + } + private static bool HasAppText(string result) + { + return result.HasValue() && result.Contains("app_ext_col_0"); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SqlBuilderProvider/SqlBuilderAccessory.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SqlBuilderProvider/SqlBuilderAccessory.cs new file mode 100644 index 000000000..ede17666b --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SqlBuilderProvider/SqlBuilderAccessory.cs @@ -0,0 +1,15 @@ +namespace SqlSugar +{ + public partial class SqlBuilderAccessory + { + protected DeleteBuilder _DeleteBuilder; + + protected InsertBuilder _InsertBuilder; + + protected QueryBuilder _QueryBuilder; + + protected SqlQueryBuilder _SqlQueryBuilder; + + protected UpdateBuilder _UpdateBuilder; + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SqlBuilderProvider/SqlBuilderProvider.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SqlBuilderProvider/SqlBuilderProvider.cs new file mode 100644 index 000000000..36decdc25 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SqlBuilderProvider/SqlBuilderProvider.cs @@ -0,0 +1,195 @@ +using System.Data; +using System.Data.Common; +using System.Text; +using System.Text.RegularExpressions; +namespace SqlSugar +{ + public abstract partial class SqlBuilderProvider : SqlBuilderAccessory, ISqlBuilder + { + #region Properties + public SqlSugarProvider Context { get; set; } + public CommandType CommandType { get; set; } + public DeleteBuilder DeleteBuilder { get; set; } + public InsertBuilder InsertBuilder { get; set; } + public QueryBuilder QueryBuilder { get; set; } + public UpdateBuilder UpdateBuilder { get; set; } + public SqlQueryBuilder SqlQueryBuilder + { + get + { + base._SqlQueryBuilder = UtilMethods.IsNullReturnNew(base._SqlQueryBuilder); + return base._SqlQueryBuilder; + } + set { base._SqlQueryBuilder = value; } + } + #endregion + + #region abstract Methods + + public virtual bool SupportReadToken { get; set; } = true; + + public virtual Task GetReaderByToken(IDataReader dataReader, CancellationToken cancellationToken) + { + return ((DbDataReader)dataReader).ReadAsync(cancellationToken); + } + public virtual void ChangeJsonType(SugarParameter paramter) + { + + } + public virtual string GetTranslationTableName(string name) + { + Check.ArgumentNullException(name, string.Format(null, ErrorMessage.ObjNotExistCompositeFormat, "Table Name")); + if (!name.Contains("<>f__AnonymousType") && name.IsContainsIn("(", ")", SqlTranslationLeft) && name != "Dictionary`2") + { + var tableInfo = this.Context + .MappingTables? + .FirstOrDefault(it => it.EntityName.Equals(name, StringComparison.CurrentCultureIgnoreCase)); + if (tableInfo != null) + { + return GetTranslationColumnName(tableInfo.DbTableName); + } + return name; + } + if (Context.MappingTables == null) + { + return name; + } + var context = this.Context; + var mappingInfo = context + .MappingTables + .FirstOrDefault(it => it.EntityName.Equals(name, StringComparison.CurrentCultureIgnoreCase)); + name = (mappingInfo == null ? name : mappingInfo.DbTableName); + if (name.IsContainsIn("(", ")", SqlTranslationLeft)) + { + return name; + } + if (name.Contains('.')) + { + return string.Join(".", name.Split('.').Select(it => SqlTranslationLeft + it + SqlTranslationRight)); + } + else + { + return SqlTranslationLeft + name + SqlTranslationRight; + } + } + public virtual string GetTranslationColumnName(string entityName, string propertyName) + { + Check.ArgumentNullException(entityName, string.Format(null, ErrorMessage.ObjNotExistCompositeFormat, "Table Name")); + Check.ArgumentNullException(propertyName, string.Format(null, ErrorMessage.ObjNotExistCompositeFormat, "Column Name")); + var context = this.Context; + var mappingInfo = context + .MappingColumns + .FirstOrDefault(it => + it.EntityName.Equals(entityName, StringComparison.CurrentCultureIgnoreCase) && + it.PropertyName.Equals(propertyName, StringComparison.CurrentCultureIgnoreCase)); + return (mappingInfo == null ? SqlTranslationLeft + propertyName + SqlTranslationRight : SqlTranslationLeft + mappingInfo.DbColumnName + SqlTranslationRight); + } + + public virtual string GetTranslationColumnName(string propertyName) + { + if (propertyName.Contains(SqlTranslationLeft)) return propertyName; + if (propertyName.Contains('.')) + { + return string.Join(".", propertyName.Split('.').Select(it => SqlTranslationLeft + it + SqlTranslationRight)); + } + else + return SqlTranslationLeft + propertyName + SqlTranslationRight; + } + + public virtual string GetNoTranslationColumnName(string name) + { + if (name.Contains('=')) + { + name = name.Split('=').First(); + } + if (!name.Contains(SqlTranslationLeft)) return name; + if (!name.Contains('.') && name.StartsWith(SqlTranslationLeft) && name.EndsWith(SqlTranslationRight)) + { + var result = name.TrimStart(Convert.ToChar(SqlTranslationLeft)).TrimEnd(Convert.ToChar(SqlTranslationRight)); + return result; + } + return name == null ? string.Empty : Regex.Match(name, @".*" + "\\" + SqlTranslationLeft + "(.*?)" + "\\" + SqlTranslationRight + "").Groups[1].Value; + } + public virtual string GetPackTable(string sql, string shortName) + { + return UtilMethods.GetPackTable(sql, shortName); + } + public virtual string GetDefaultShortName() + { + return "t"; + } + + + public string GetWhere(string fieldName, string conditionalType, int? parameterIndex = null) + { + return string.Format(" {0} {1} {2}{3} ", this.GetTranslationColumnName(fieldName), conditionalType, this.SqlParameterKeyWord, fieldName.Replace(".", "_") + parameterIndex); + } + public virtual string GetUnionAllSql(List sqlList) + { + return string.Join(" UNION ALL \r\n", sqlList); + } + public virtual string GetUnionSql(List sqlList) + { + return string.Join(" UNION \r\n", sqlList); + } + public virtual void RepairReplicationParameters(ref string appendSql, SugarParameter[] parameters, int addIndex) + { + UtilMethods.RepairReplicationParameters(ref appendSql, parameters, addIndex); + } + public virtual string GetUnionFomatSql(string sql) + { + return sql; + } + public virtual Type GetNullType(string tableName, string columnName) + { + return null; + } + + public virtual string RemoveParentheses(string sql) + { + return sql; + } + + private static object GetFieldValue(ConditionalModel item) + { + if (item.FieldValueConvertFunc != null) + { + return item.FieldValueConvertFunc(item.FieldValue); + } + else if (item.CSharpTypeName.HasValue()) + { + return UtilMethods.ConvertDataByTypeName(item.CSharpTypeName, item.FieldValue); + } + else + { + return item.FieldValue; + } + } + public virtual void FormatSaveQueueSql(StringBuilder sqlBuilder) + { + } + public virtual string RemoveN(string sql) + { + return sql; + } + #endregion + + #region Common SqlTemplate + public string AppendWhereOrAnd(bool isWhere, string sqlString) + { + return isWhere ? (" WHERE " + sqlString) : (" AND " + sqlString); + } + public string AppendHaving(string sqlString) + { + return " HAVING " + sqlString; + } + public virtual string SqlParameterKeyWord { get { return "@"; } } + public abstract string SqlTranslationLeft { get; } + public abstract string SqlTranslationRight { get; } + public virtual string SqlFalse { get { return "1=2 "; } } + public virtual string SqlDateNow { get { return "GETDATE()"; } } + public virtual string FullSqlDateNow { get { return "SELECT GETDATE()"; } } + public virtual string SqlSelectAll { get { return "*"; } } + #endregion + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SqlBuilderProvider/SqlBuilderProvider_Condition.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SqlBuilderProvider/SqlBuilderProvider_Condition.cs new file mode 100644 index 000000000..3d441a48c --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SqlBuilderProvider/SqlBuilderProvider_Condition.cs @@ -0,0 +1,457 @@ +using System.Data; +using System.Text; +using System.Text.RegularExpressions; +namespace SqlSugar +{ + public abstract partial class SqlBuilderProvider : SqlBuilderAccessory, ISqlBuilder + { + #region Core + public KeyValuePair ConditionalModelToSql(List models, int beginIndex = 0) + { + if (models.IsNullOrEmpty()) return new KeyValuePair(); + StringBuilder builder = new StringBuilder(); + List parameters = new List(); + var sqlBuilder = InstanceFactory.GetSqlbuilder(this.Context.CurrentConnectionConfig); + var mainIndex = 0; + var indexTree = 0; + foreach (var model in models) + { + if (model is ConditionalModel) + { + var item = model as ConditionalModel; + if (item.CustomConditionalFunc != null) + { + var colIndex = mainIndex + beginIndex; + var colType = colIndex == 0 ? "" : "AND"; + var custom = item.CustomConditionalFunc.GetConditionalSql(item, colIndex); + parameters.AddRange(custom.Value); + builder.AppendFormat(" " + colType + " " + custom.Key); + mainIndex++; + continue; + } + else if (item.FieldName == UtilMethods.FieldNameSql()) + { + builder.Append(item.FieldValue); + continue; + } + var index = mainIndex + beginIndex; + var type = index == 0 ? "" : "AND"; + if (beginIndex > 0) + { + type = null; + } + string temp = " {0} {1} {2} {3} "; + string parameterName = string.Format("{0}Condit{1}{2}", sqlBuilder.SqlParameterKeyWord, item.FieldName, index); + if (this.Context?.CurrentConnectionConfig?.MoreSettings?.MaxParameterNameLength > 0 && parameterName.Length > this.Context?.CurrentConnectionConfig?.MoreSettings?.MaxParameterNameLength) + { + parameterName = string.Format("{0}Condit{1}{2}", sqlBuilder.SqlParameterKeyWord, item.FieldName.GetHashCode().ToString().Replace("-", ""), index); + } + if (parameterName.Contains('.')) + { + parameterName = parameterName.Replace(".", "_"); + } + if (parameterName.Contains('[')) + { + parameterName = parameterName.Replace('[', '_'); + } + if (parameterName.Contains(']')) + { + parameterName = parameterName.Replace(']', '_'); + } + if (!string.IsNullOrEmpty(this.SqlTranslationLeft) && parameterName.Contains(this.SqlTranslationLeft)) + { + parameterName = parameterName.Replace(this.SqlTranslationLeft, "_"); + } + string oldName = item.FieldName; + item.FieldName = GetTranslationColumnName(item.FieldName); + item.FieldName = item.FieldName.ToCheckField(); + var isILike = this.Context.CurrentConnectionConfig?.MoreSettings?.EnableILike == true; + switch (item.ConditionalType) + { + case ConditionalType.Equal: + Equal(builder, parameters, item, type, temp, parameterName); + break; + case ConditionalType.Like: + builder.AppendFormat(temp, type, item.FieldName.ToSqlFilter(), isILike ? "ILIKE" : "LIKE", parameterName); + parameters.Add(new SugarParameter(parameterName, "%" + item.FieldValue + "%")); + break; + case ConditionalType.GreaterThan: + builder.AppendFormat(temp, type, item.FieldName.ToSqlFilter(), ">", parameterName); + parameters.Add(new SugarParameter(parameterName, GetFieldValue(item))); + break; + case ConditionalType.GreaterThanOrEqual: + builder.AppendFormat(temp, type, item.FieldName.ToSqlFilter(), ">=", parameterName); + parameters.Add(new SugarParameter(parameterName, GetFieldValue(item))); + break; + case ConditionalType.LessThan: + builder.AppendFormat(temp, type, item.FieldName.ToSqlFilter(), "<", parameterName); + parameters.Add(new SugarParameter(parameterName, GetFieldValue(item))); + break; + case ConditionalType.LessThanOrEqual: + builder.AppendFormat(temp, type, item.FieldName.ToSqlFilter(), "<=", parameterName); + parameters.Add(new SugarParameter(parameterName, GetFieldValue(item))); + break; + case ConditionalType.In: + In(builder, item, type, temp); + //parameters.Add(new SugarParameter(parameterName, item.FieldValue)); + break; + case ConditionalType.NotIn: + NotIn(builder, parameters, item, type, temp, parameterName); + break; + case ConditionalType.LikeLeft: + builder.AppendFormat(temp, type, item.FieldName.ToSqlFilter(), isILike ? "ILIKE" : "LIKE", parameterName); + parameters.Add(new SugarParameter(parameterName, item.FieldValue + "%")); + break; + case ConditionalType.NoLike: + builder.AppendFormat(temp, type, item.FieldName.ToSqlFilter(), isILike ? "NOT ILIKE" : " NOT LIKE", parameterName); + parameters.Add(new SugarParameter(parameterName, "%" + item.FieldValue + "%")); + break; + case ConditionalType.LikeRight: + builder.AppendFormat(temp, type, item.FieldName.ToSqlFilter(), isILike ? "ILIKE" : "LIKE", parameterName); + parameters.Add(new SugarParameter(parameterName, "%" + item.FieldValue)); + break; + case ConditionalType.NoEqual: + builder.AppendFormat(temp, type, item.FieldName.ToSqlFilter(), "<>", parameterName); + parameters.Add(new SugarParameter(parameterName, GetFieldValue(item))); + break; + case ConditionalType.IsNullOrEmpty: + builder.AppendFormat(" {0} (({1}) OR ({2})) ", type, item.FieldName.ToSqlFilter() + " IS NULL ", item.FieldName.ToSqlFilter() + " = '' "); + parameters.Add(new SugarParameter(parameterName, item.FieldValue)); + break; + case ConditionalType.IsNot: + IsNot(builder, parameters, item, type, temp, parameterName); + break; + case ConditionalType.EqualNull: + EqualNull(builder, parameters, item, type, temp, parameterName); + break; + case ConditionalType.InLike: + InLike(builder, parameters, item, index, type, parameterName); + break; + case ConditionalType.Range: + Range(builder, parameters, item, index, type, parameterName); + break; + default: + break; + } + item.FieldName = oldName; + } + else if (model is ConditionalCollections) + { + var item = model as ConditionalCollections; + if (item?.ConditionalList.HasValue() == true) + { + foreach (var con in item.ConditionalList) + { + var index = item.ConditionalList.IndexOf(con); + var isFirst = index == 0; + var isLast = index == (item.ConditionalList.Count - 1); + if (models.IndexOf(item) == 0 && index == 0 && beginIndex == 0) + { + builder.AppendFormat(" ( "); + + } + else if (isFirst) + { + builder.AppendFormat(" {0} ( ", con.Key.ToString().ToUpper()); + } + List conModels = new List(); + conModels.Add(con.Value); + var childSqlInfo = ConditionalModelToSql(conModels, 1000 * (1 + index) + models.IndexOf(item)); + if (!isFirst && con.Value.FieldName != $"[value=sql{UtilConstants.ReplaceKey}]") + { + + builder.AppendFormat(" {0} ", con.Key.ToString().ToUpper()); + } + builder.Append(childSqlInfo.Key); + if (conModels?.FirstOrDefault() is ConditionalModel conModel) + { + if (conModel.CustomConditionalFunc != null) + { + builder.Replace(" AND ( AND", " AND ( "); + builder.Replace(" OR ( AND", " OR ( "); + builder.Replace(" ( AND ", " ( "); + builder.Replace(" ( OR ", " ( "); + builder.Replace(" OR AND ", " OR "); + builder.Replace(" AND AND ", " AND "); + } + } + parameters.AddRange(childSqlInfo.Value); + if (isLast) + { + builder.Append(" ) "); + } + else + { + + } + } + } + } + else + { + var item = model as ConditionalTree; + BuilderTree(builder, item, ref indexTree, parameters, ref mainIndex); + } + mainIndex++; + } + return new KeyValuePair(builder.ToString(), parameters.ToArray()); + } + + #endregion + + #region Case Method + private static void Range(StringBuilder builder, List parameters, ConditionalModel item, int index, string type, string parameterName) + { + var value = item.FieldValue; + var valueArray = (value + "").Split(','); + if (valueArray.Length != 2) + { + Check.ExceptionEasy($"The {item.FieldName} value is not a valid format, but is properly separated by a comma (1,2)", $"{item.FieldName} 值不是有效格式,正确是 1,2 这种中间逗号隔开"); + } + var firstValue = GetFieldValue(new ConditionalModel() { CSharpTypeName = item.CSharpTypeName, FieldValue = valueArray.FirstOrDefault() }); + var lastValue = GetFieldValue(new ConditionalModel() { CSharpTypeName = item.CSharpTypeName, FieldValue = valueArray.LastOrDefault() }); + var parameterNameFirst = parameterName + "_01"; + var parameterNameLast = parameterName + "_02"; + builder.AppendFormat("( {0}>={1} AND {0}<={2} )", item.FieldName.ToSqlFilter(), parameterNameFirst, parameterNameLast); + parameters.Add(new SugarParameter(parameterNameFirst, firstValue)); + parameters.Add(new SugarParameter(parameterNameLast, lastValue)); + } + private static void InLike(StringBuilder builder, List parameters, ConditionalModel item, int index, string type, string parameterName) + { + var array = (item.FieldValue + "").Split(',').ToList(); + List sqls = new List(); + int i = 0; + foreach (var val in array) + { + var itemParameterName = $"{parameterName}{index}{i}"; + sqls.Add(item.FieldName.ToSqlFilter() + " LIKE " + itemParameterName); + parameters.Add(new SugarParameter(itemParameterName, "%" + val + "%")); + i++; + } + builder.Append($" {type} ({string.Join(" OR ", sqls)}) "); + } + + private static void EqualNull(StringBuilder builder, List parameters, ConditionalModel item, string type, string temp, string parameterName) + { + if (GetFieldValue(item) == null || (item.FieldValue == null)) + { + builder.AppendFormat(temp, type, item.FieldName.ToSqlFilter(), " IS ", " NULL "); + } + else + { + builder.AppendFormat(temp, type, item.FieldName.ToSqlFilter(), "=", parameterName); + parameters.Add(new SugarParameter(parameterName, GetFieldValue(item))); + } + } + + private static void IsNot(StringBuilder builder, List parameters, ConditionalModel item, string type, string temp, string parameterName) + { + if (item.FieldValue == null) + { + builder.AppendFormat(temp, type, item.FieldName.ToSqlFilter(), " IS NOT ", "NULL"); + } + else + { + builder.AppendFormat(temp, type, item.FieldName.ToSqlFilter(), "<>", parameterName); + parameters.Add(new SugarParameter(parameterName, item.FieldValue)); + } + } + + private static void NotIn(StringBuilder builder, List parameters, ConditionalModel item, string type, string temp, string parameterName) + { + if (item.FieldValue == null) item.FieldValue = string.Empty; + var inValue2 = ("(" + item.FieldValue.Split(',').ToJoinSqlInVals() + ")"); + if (item.CSharpTypeName.HasValue() && UtilMethods.IsNumber(item.CSharpTypeName)) + { + inValue2 = inValue2.Replace("'", ""); + } + else if (inValue2.Contains("'null'")) + { + inValue2 = inValue2.Replace("'null'", "null"); + } + if (inValue2.Contains("[null]")) + { + inValue2 = inValue2.Replace("[null]", "'null'"); + } + if (inValue2.Contains("[comma]")) + { + inValue2 = inValue2.Replace("[comma]", ","); + } + builder.AppendFormat(temp, type, item.FieldName.ToSqlFilter(), "NOT IN", inValue2); + parameters.Add(new SugarParameter(parameterName, item.FieldValue)); + } + + private void In(StringBuilder builder, ConditionalModel item, string type, string temp) + { + if (item.FieldValue == null) item.FieldValue = string.Empty; + var inValue1 = string.Empty; + var inArray = item.FieldValue.Split(','); + var pageSize = 1000; + if (inArray.Length > pageSize && this.Context.CurrentConnectionConfig.DbType == DbType.Oracle) + { + InBig(builder, item, type, temp, inArray, pageSize); + return; + } + inValue1 = In_GetInValue(item, inArray); + if (item.CSharpTypeName.HasValue() && UtilMethods.IsNumber(item.CSharpTypeName)) + { + inValue1 = inValue1.Replace("'", ""); + } + else if (inValue1.Contains("'null'")) + { + inValue1 = inValue1.Replace("'null'", "null"); + } + if (inValue1.Contains("[comma]")) + { + inValue1 = inValue1.Replace("[comma]", ","); + } + if (inValue1.Contains("[null]")) + { + inValue1 = inValue1.Replace("[null]", "'null'"); + } + if (item.CSharpTypeName.EqualCase("guid") && inValue1 == "('')") + { + inValue1 = $"('{Guid.Empty.ToString()}')"; + } + else if (inValue1 == "()") + { + inValue1 = $"(NULL)"; + } + if (inArray.Length == 1 && inValue1 != "(null)") + { + builder.AppendFormat(temp, type, item.FieldName.ToSqlFilter(), "=", inValue1.TrimStart('(').TrimEnd(')')); + } + else + { + builder.AppendFormat(temp, type, item.FieldName.ToSqlFilter(), "IN", inValue1); + } + } + + private void InBig(StringBuilder builder, ConditionalModel item, string type, string temp, string[] inArray, int pageSize) + { + var sqlList = new List(); + this.Context.Utilities.PageEach(inArray, pageSize, items => + { + if (item.CSharpTypeName.EqualCase("string") || item.CSharpTypeName == null) + { + sqlList.Add("(" + item.FieldName.ToSqlFilter() + " IN (" + items.Distinct().ToArray().ToJoinSqlInVals() + "))"); + } + else + { + sqlList.Add("(" + item.FieldName.ToSqlFilter() + " IN (" + items.Select(it => string.IsNullOrEmpty(it) ? "null" : it).Distinct().ToArray().ToJoinSqlInVals() + "))"); + } + }); + var inValue1 = $" {string.Join(" OR ", sqlList)} "; + if (item.CSharpTypeName.HasValue() && UtilMethods.IsNumber(item.CSharpTypeName)) + { + inValue1 = inValue1.Replace("'", ""); + } + builder.AppendFormat(temp, type, "", " ", inValue1); + } + + private string In_GetInValue(ConditionalModel item, string[] inArray) + { + string inValue1; + if (item.CSharpTypeName.EqualCase("string") || item.CSharpTypeName == null) + { + inValue1 = ("(" + inArray.Distinct().ToArray().ToJoinSqlInVals() + ")"); + } + if (item.CSharpTypeName.EqualCase("bool") || item.CSharpTypeName.EqualCase("Boolean")) + { + var lam = InstanceFactory.GetLambdaExpressions(this.Context.CurrentConnectionConfig); + lam.Context = this.Context; + var value = lam.DbMehtods.TrueValue(); + inValue1 = ("(" + string.Join(",", inArray.Distinct().ToArray()) + ")"); + if (value.EqualCase("true")) + { + inValue1 = inValue1.Replace("1", "true").Replace("0", "false"); + } + } + else + { + if (item.CSharpTypeName.EqualCase("nstring")) + { + inValue1 = ("(" + inArray.Select(it => string.IsNullOrEmpty(it) ? "null" : it).Distinct().ToArray().ToJoinSqlInValsByVarchar() + ")"); + } + else + { + inValue1 = ("(" + inArray.Select(it => string.IsNullOrEmpty(it) ? "null" : it).Distinct().ToArray().ToJoinSqlInVals() + ")"); + } + } + + return inValue1; + } + + private static void Equal(StringBuilder builder, List parameters, ConditionalModel item, string type, string temp, string parameterName) + { + if (item.FieldValue != null && item.FieldValue == "null" && item.FieldValue != "[null]") + { + builder.AppendFormat($" {item.FieldName.ToSqlFilter()} is null "); + } + else + { + if (item.FieldValue == "[null]") + { + item.FieldValue = "null"; + } + builder.AppendFormat(temp, type, item.FieldName.ToSqlFilter(), "=", parameterName); + parameters.Add(new SugarParameter(parameterName, GetFieldValue(item))); + } + } + #endregion + + #region ConditionalCollections + private void BuilderTree(StringBuilder builder, ConditionalTree item, ref int indexTree, List parameters, ref int mainIndex) + { + var conditionals = ToConditionalCollections(item, ref indexTree, parameters); + var sqlobj = ConditionalModelToSql(new List { conditionals }, mainIndex); + var sql = sqlobj.Key; + RepairReplicationParameters(ref sql, sqlobj.Value, indexTree); + parameters.AddRange(sqlobj.Value); + var buiderSql = sql; + builder.Append(buiderSql); + indexTree++; + } + private ConditionalCollections ToConditionalCollections(ConditionalTree item, ref int indexTree, List parameters) + { + List> list = new List>(); + var index = 0; + foreach (var it in item.ConditionalList) + { + ConditionalModel model = new ConditionalModel(); + if (it.Value is ConditionalModel) + { + model = (ConditionalModel)it.Value; + } + else + { + var tree = it.Value as ConditionalTree; + var con = ToConditionalCollections(tree, ref indexTree, parameters); + var sqlobj = ConditionalModelToSql(new List { con }, index); + var sql = sqlobj.Key; + if (sql.StartsWith(" NULL ")) + { + sql = Regex.Replace(sql, "^ NULL ", it.Key.ToString().ToUpper()); + } + RepairReplicationParameters(ref sql, sqlobj.Value, indexTree); + model = new ConditionalModel() + { + FieldName = $"[value=sql{UtilConstants.ReplaceKey}]", + FieldValue = sql + }; + parameters.AddRange(sqlobj.Value); + indexTree++; + } + list.Add(new KeyValuePair(it.Key, model)); + index++; + } + var result = new ConditionalCollections() + { + ConditionalList = list + }; + return result; + } + #endregion + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SqlBuilderProvider/SqlQueryBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SqlBuilderProvider/SqlQueryBuilder.cs new file mode 100644 index 000000000..ec9c63343 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SqlBuilderProvider/SqlQueryBuilder.cs @@ -0,0 +1,80 @@ +using System.Text; +using System.Text.RegularExpressions; + +namespace SqlSugar +{ + public class SqlQueryBuilder : IDMLBuilder + { + + #region Fields + private string _Fields; + private StringBuilder _Sql; + private List _Parameters; + #endregion + + #region Properties + public SqlSugarProvider Context { get; set; } + public string Fields + { + get + { + if (this._Fields.IsNullOrEmpty()) + { + this._Fields = Regex.Match(this.sql.ObjToString().Replace("\n", string.Empty).Replace("\r", string.Empty).Trim(), @"select(.*?)from", RegexOptions.IgnoreCase).Groups[1].Value; + if (this._Fields.IsNullOrEmpty()) + { + this._Fields = "*"; + } + } + return this._Fields; + } + set + { + _Fields = value; + } + } + public StringBuilder sql + { + get + { + _Sql = UtilMethods.IsNullReturnNew(_Sql); + return _Sql; + } + set + { + _Sql = value; + } + } + public string SqlTemplate + { + get + { + return null; + } + } + public List Parameters + { + get + { + _Parameters = UtilMethods.IsNullReturnNew(_Parameters); + return _Parameters; + } + set + { + _Parameters = value; + } + } + #endregion + + #region Methods + public string ToSqlString() + { + return sql.ToString(); + } + public void Clear() + { + this.sql = null; + } + #endregion + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SqlBuilderProvider/UpdateBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SqlBuilderProvider/UpdateBuilder.cs new file mode 100644 index 000000000..6012e2d91 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SqlBuilderProvider/UpdateBuilder.cs @@ -0,0 +1,614 @@ +using System.Globalization; +using System.Linq.Expressions; +using System.Text; +using System.Text.RegularExpressions; + +namespace SqlSugar +{ + public class UpdateBuilder : IDMLBuilder + { + public UpdateBuilder() + { + this.sql = new StringBuilder(); + this.DbColumnInfoList = new List(); + this.SetValues = new List>(); + this.WhereValues = new List(); + this.Parameters = new List(); + } + public SqlSugarProvider Context { get; set; } + public ILambdaExpressions LambdaExpressions { get; set; } + public ISqlBuilder Builder { get; set; } + public StringBuilder sql { get; set; } + public List Parameters { get; set; } + public string TableName { get; set; } + public string TableWithString { get; set; } + public List DbColumnInfoList { get; set; } + public List WhereValues { get; set; } + public string AppendWhere { get; set; } + public List> SetValues { get; set; } + public bool IsNoUpdateNull { get; set; } + public bool IsNoUpdateDefaultValue { get; set; } + public List PrimaryKeys { get; set; } + public List OldPrimaryKeys { get; set; } + public bool IsOffIdentity { get; set; } + public bool IsWhereColumns { get; set; } + public bool? IsListUpdate { get; set; } + public List UpdateColumns { get; set; } + public List IgnoreColumns { get; set; } + public List JoinInfos { get; set; } + public string ShortName { get; set; } + public Dictionary ReSetValueBySqlExpList { get; set; } + public virtual string ReSetValueBySqlExpListType { get; set; } + public EntityInfo EntityInfo { get; set; } + public virtual string SqlTemplate + { + get + { + return @"UPDATE {0} SET + {1} {2}"; + + } + } + + public virtual string SqlTemplateBatch + { + get + { + return @"UPDATE S SET {0} FROM {1} S {2} INNER JOIN "; + } + } + + public virtual string SqlTemplateJoin + { + get + { + return @" ( + {0} + + ) T ON {1} + ; "; + } + } + + public virtual string SqlTemplateBatchSet + { + get + { + return "{0} AS {1}"; + } + } + public virtual string SqlTemplateBatchSelect + { + get + { + return "{0} AS {1}"; + } + } + + public virtual string SqlTemplateBatchUnion + { + get + { + return "\t\t\r\nUNION ALL "; + } + } + + public virtual void Clear() + { + + } + public virtual string GetTableNameString + { + get + { + var result = Builder.GetTranslationTableName(TableName); + result += UtilConstants.Space; + if (this.TableWithString.HasValue()) + { + result += TableWithString + UtilConstants.Space; + } + return result; + } + } + public virtual string GetTableNameStringNoWith + { + get + { + var result = Builder.GetTranslationTableName(TableName); + return result; + } + } + + + public virtual ExpressionResult GetExpressionValue(Expression expression, ResolveExpressType resolveType, bool isMapping = true) + { + ILambdaExpressions resolveExpress = this.LambdaExpressions; + this.LambdaExpressions.Clear(); + if (this.Context.CurrentConnectionConfig.MoreSettings != null) + { + resolveExpress.PgSqlIsAutoToLower = this.Context.CurrentConnectionConfig.MoreSettings.PgSqlIsAutoToLower; + resolveExpress.TableEnumIsString = this.Context.CurrentConnectionConfig.MoreSettings.TableEnumIsString; + } + else + { + resolveExpress.PgSqlIsAutoToLower = true; + } + if (isMapping) + { + resolveExpress.MappingColumns = Context.MappingColumns; + resolveExpress.MappingTables = Context.MappingTables; + resolveExpress.IgnoreComumnList = Context.IgnoreColumns; + resolveExpress.SqlFuncServices = Context.CurrentConnectionConfig.ConfigureExternalServices == null ? null : Context.CurrentConnectionConfig.ConfigureExternalServices.SqlFuncServices; + } + resolveExpress.InitMappingInfo = Context.InitMappingInfo; + resolveExpress.RefreshMapping = () => + { + resolveExpress.MappingColumns = Context.MappingColumns; + resolveExpress.MappingTables = Context.MappingTables; + resolveExpress.IgnoreComumnList = Context.IgnoreColumns; + resolveExpress.SqlFuncServices = Context.CurrentConnectionConfig.ConfigureExternalServices == null ? null : Context.CurrentConnectionConfig.ConfigureExternalServices.SqlFuncServices; + }; + resolveExpress.SugarContext = new ExpressionOutParameter() { Context = this.Context }; + resolveExpress.Resolve(expression, resolveType); + this.Parameters.AddRange(resolveExpress.Parameters); + var result = resolveExpress.Result; + return result; + } + public virtual string ToSqlString() + { + if (IsNoUpdateNull) + { + DbColumnInfoList = DbColumnInfoList.Where(it => it.Value != null || (it.UpdateServerTime == true || !string.IsNullOrEmpty(it.UpdateSql))).ToList(); + } + if (IsNoUpdateDefaultValue) + { + DbColumnInfoList = DbColumnInfoList.Where(it => + { + if (it.Value.ObjToString() == "0" && it.PropertyType.IsEnum) + { + return it.Value.ObjToLong() != UtilMethods.DefaultForType(it.PropertyType).ObjToLong(); + } + else if (it.UpdateServerTime == true || !string.IsNullOrEmpty(it.UpdateSql)) + { + return true; + } + else + { + return it.Value.ObjToString() != UtilMethods.DefaultForType(it.PropertyType).ObjToString(); + } + + }).ToList(); + } + var groupList = DbColumnInfoList.GroupBy(it => it.TableId).ToList(); + var isSingle = groupList.Count == 1; + if (isSingle && this.IsListUpdate == null) + { + ActionMinDate(); + return ToSingleSqlString(groupList); + } + else + { + return TomultipleSqlString(groupList); + } + } + + protected virtual string TomultipleSqlString(List> groupList) + { + Check.Exception(PrimaryKeys == null || PrimaryKeys.Count == 0, " Update List need Primary key"); + int pageSize = 200; + int pageIndex = 1; + int totalRecord = groupList.Count; + int pageCount = (totalRecord + pageSize - 1) / pageSize; + StringBuilder batchUpdateSql = new StringBuilder(); + while (pageCount >= pageIndex) + { + StringBuilder updateTable = new StringBuilder(); + string setValues = string.Join(",", groupList.First().Where(it => it.IsPrimarykey == false && (it.IsIdentity == false || (IsOffIdentity && it.IsIdentity))).Select(it => + { + if (SetValues.IsValuable()) + { + var setValue = SetValues.Where(sv => sv.Key == Builder.GetTranslationColumnName(it.DbColumnName)); + if (setValue?.Any() == true) + { + return setValue.First().Value; + } + } + var result = string.Format("S.{0}=T.{0}", Builder.GetTranslationColumnName(it.DbColumnName)); + return result; + })); + batchUpdateSql.AppendFormat(SqlTemplateBatch.ToString(), setValues, GetTableNameStringNoWith, TableWithString); + int i = 0; + foreach (var columns in groupList.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList()) + { + var isFirst = i == 0; + if (!isFirst) + { + updateTable.Append(SqlTemplateBatchUnion); + } + updateTable.Append("\r\n SELECT " + string.Join(",", columns.Select(it => string.Format(SqlTemplateBatchSelect, FormatValue(it.Value), Builder.GetTranslationColumnName(it.DbColumnName))))); + ++i; + } + pageIndex++; + updateTable.Append("\r\n"); + string whereString = null; + if (this.WhereValues.HasValue()) + { + foreach (var item in WhereValues) + { + var isFirst = whereString == null; + whereString += (isFirst ? null : " AND "); + whereString += Regex.Replace(item, "\\" + this.Builder.SqlTranslationLeft, "S." + this.Builder.SqlTranslationLeft); + } + } + if (PrimaryKeys.HasValue()) + { + foreach (var item in PrimaryKeys) + { + var isFirst = whereString == null; + whereString += (isFirst ? null : " AND "); + whereString += string.Format("S.{0}=T.{0}", Builder.GetTranslationColumnName(item)); + } + } + batchUpdateSql.AppendFormat(SqlTemplateJoin, updateTable, whereString); + } + return batchUpdateSql.ToString(); + } + + protected virtual string ToSingleSqlString(List> groupList) + { + string columnsString = string.Join(",", groupList.First().Where(it => it.IsPrimarykey == false && (it.IsIdentity == false || (IsOffIdentity && it.IsIdentity))).Select(it => + { + if (SetValues.IsValuable()) + { + var setValue = SetValues.Where(sv => it.IsPrimarykey == false && (it.IsIdentity == false || (IsOffIdentity && it.IsIdentity))).Where(sv => sv.Key == Builder.GetTranslationColumnName(it.DbColumnName) || sv.Key == Builder.GetTranslationColumnName(it.PropertyName)); + if (setValue?.Any() == true) + { + return setValue.First().Value; + } + else if (JoinInfos != null && JoinInfos.Count != 0) + { + setValue = SetValues.Where(sv => it.IsPrimarykey == false && (it.IsIdentity == false || (IsOffIdentity && it.IsIdentity))).Where(sv => sv.Key == Builder.GetNoTranslationColumnName(it.DbColumnName) || sv.Key == Builder.GetNoTranslationColumnName(it.PropertyName)); + return Builder.GetTranslationColumnName(this.ShortName) + "." + Builder.GetTranslationColumnName(setValue.First().Key) + "=" + setValue.First().Value; + } + } + var result = Builder.GetTranslationColumnName(it.DbColumnName) + "=" + GetDbColumn(it, this.Context.Ado.SqlParameterKeyWord + it.DbColumnName); + return result; + })); + string whereString = null; + if (this.WhereValues.HasValue()) + { + foreach (var item in WhereValues) + { + var isFirst = whereString == null; + whereString += (isFirst ? " WHERE " : " AND "); + whereString += item; + } + } + else if (PrimaryKeys.HasValue()) + { + if (IsWhereColumns == false) + { + int i = 100000; + foreach (var item in PrimaryKeys) + { + i++; + var isFirst = whereString == null; + whereString += (isFirst ? " WHERE " : " AND "); + var pkIsSugarDataConverter = GetPkIsSugarDataConverter(); + if (pkIsSugarDataConverter && GetColumnInfo(item) != null) + { + var columnInfo = GetColumnInfo(item); + var value = this.DbColumnInfoList.FirstOrDefault(it => it.DbColumnName.EqualCase(item) || it.PropertyName.EqualCase(item))?.Value; + var p = UtilMethods.GetParameterConverter(i, this.Context, value, this.EntityInfo, this.EntityInfo?.Columns.First(it => it.DbColumnName.Equals(item) || it.PropertyName.Equals(item))); + whereString += Builder.GetTranslationColumnName(item) + "=" + p.ParameterName; + this.Parameters.Add(p); + } + else + { + whereString += Builder.GetTranslationColumnName(item) + "=" + this.Context.Ado.SqlParameterKeyWord + item; + var columnInfo = GetColumnInfo(item); + if (columnInfo?.SqlParameterDbType is System.Data.DbType valueDbType) + { + var p = this.Parameters?.FirstOrDefault(it => it.ParameterName == this.Context.Ado.SqlParameterKeyWord + item); + if (p != null) + { + p.DbType = valueDbType; + } + } + } + } + } + } + if (PrimaryKeys.HasValue() && IsWhereColumns) + { + foreach (var item in PrimaryKeys) + { + var isFirst = whereString == null; + whereString += (isFirst ? " WHERE " : " AND "); + whereString += Builder.GetTranslationColumnName(item) + "=" + this.Context.Ado.SqlParameterKeyWord + item; + } + } + if (this.JoinInfos != null && this.JoinInfos.Count != 0) + { + return GetJoinUpdate(columnsString, ref whereString); + } + return string.Format(SqlTemplate, GetTableNameString, columnsString, whereString); + } + + private EntityColumnInfo GetColumnInfo(string item) + { + var columnInfo = this.EntityInfo?.Columns?.FirstOrDefault(it => it.DbColumnName.EqualCase(item) || it.PropertyName.EqualCase(item)); + return columnInfo; + } + + private bool GetPkIsSugarDataConverter() + { + return this.EntityInfo?.Columns.Any(it => it.IsPrimarykey && it.SqlParameterDbType is Type && typeof(ISugarDataConverter).IsAssignableFrom((it.SqlParameterDbType as Type))) == true; + } + + protected virtual string GetJoinUpdate(string columnsString, ref string whereString) + { + var tableName = Builder.GetTranslationColumnName(this.TableName); + this.TableName = Builder.GetTranslationColumnName(this.ShortName); + var joinString = $" FROM {tableName} {Builder.GetTranslationColumnName(this.ShortName)} "; + foreach (var item in this.JoinInfos) + { + joinString += $"\r\n JOIN {Builder.GetTranslationColumnName(item.TableName)} {Builder.GetTranslationColumnName(item.ShortName)} ON {item.JoinWhere} "; + } + whereString = joinString + "\r\n" + whereString; + return string.Format(SqlTemplate, GetTableNameString, columnsString, whereString); + } + + public virtual void ActionMinDate() + { + if (this.Parameters != null) + { + foreach (var item in this.Parameters) + { + if (item.DbType == System.Data.DbType.Date || item.DbType == System.Data.DbType.DateTime) + { + if (item.Value != null && item.Value != DBNull.Value) + { + if (item.Value is DateTime) + { + if (Convert.ToDateTime(item.Value) == DateTime.MinValue) + { + item.Value = UtilMethods.GetMinDate(this.Context.CurrentConnectionConfig); + } + } + } + } + } + } + } + public virtual object FormatValue(object value) + { + if (value == null) + { + return "NULL"; + } + else + { + var type = UtilMethods.GetUnderType(value.GetType()); + if (type == UtilConstants.DateType) + { + var date = value.ObjToDate(); + if (date < UtilMethods.GetMinDate(this.Context.CurrentConnectionConfig)) + { + date = UtilMethods.GetMinDate(this.Context.CurrentConnectionConfig); + } + return "'" + date.ToString("yyyy-MM-dd HH:mm:ss.fff") + "'"; + } + else if (type == UtilConstants.ByteArrayType) + { + string bytesString = "0x" + Convert.ToHexString((byte[])value); + return bytesString; + } + else if (type.IsEnum()) + { + if (this.Context.CurrentConnectionConfig.MoreSettings?.TableEnumIsString == true) + { + return value.ToSqlValue(); + } + else + { + return Convert.ToInt64(value); + } + } + else if (type == UtilConstants.BoolType) + { + return value.ObjToBool() ? "1" : "0"; + } + else if (type == UtilConstants.DateTimeOffsetType) + { + return FormatDateTimeOffset(value); + } + else if (type == UtilConstants.StringType || type == UtilConstants.ObjType) + { + return "N'" + value.ToString().ToSqlFilter() + "'"; + } + else if (type == UtilConstants.IntType || type == UtilConstants.LongType) + { + return value; + } + else if (UtilMethods.IsNumber(type.Name)) + { + if (value is decimal v) + { + return v.ToString(CultureInfo.InvariantCulture); + } + if (value.ObjToString().Contains(',')) + { + return $"'{value}'"; + } + else + { + return value; + } + } + else + { + return "N'" + value.ToString() + "'"; + } + } + } + + public virtual string FormatDateTimeOffset(object value) + { + var date = UtilMethods.ConvertFromDateTimeOffset((DateTimeOffset)value); + return "'" + date.ToString("yyyy-MM-dd HH:mm:ss.fff") + "'"; + } + private int GetDbColumnIndex = 0; + public virtual string GetDbColumn(DbColumnInfo columnInfo, object name) + { + if (columnInfo.UpdateServerTime) + { + return LambdaExpressions.DbMehtods.GetDate(); + } + else if (columnInfo.PropertyType.FullName == "NetTopologySuite.Geometries.Geometry") + { + var pname = Builder.SqlParameterKeyWord + "Geometry" + GetDbColumnIndex; + var p = new SugarParameter(pname, columnInfo.Value); + p.DbType = System.Data.DbType.Object; + this.Parameters.Add(p); + GetDbColumnIndex++; + return pname; + } + else if (UtilMethods.IsErrorDecimalString() == true) + { + var pname = Builder.SqlParameterKeyWord + "Decimal" + GetDbColumnIndex; + var p = new SugarParameter(pname, columnInfo.Value); + this.Parameters.Add(p); + GetDbColumnIndex++; + return pname; + } + else if (IsListSetExp(columnInfo) || IsSingleSetExp(columnInfo)) + { + if (this.ReSetValueBySqlExpList[columnInfo.PropertyName].Type == ReSetValueBySqlExpListModelType.List) + { + return Builder.GetTranslationColumnName(columnInfo.DbColumnName) + this.ReSetValueBySqlExpList[columnInfo.PropertyName].Sql + name; + } + else + { + return this.ReSetValueBySqlExpList[columnInfo.PropertyName].Sql; + } + } + else if (columnInfo.UpdateSql.HasValue()) + { + if (columnInfo.UpdateSql.Contains("{0}")) + { + if (columnInfo.Value == null) + { + return string.Format(columnInfo.UpdateSql, "null").Replace("'null'", "null"); + } + else + { + return string.Format(columnInfo.UpdateSql, columnInfo.Value?.ObjToString().ToSqlFilter()); + } + } + return columnInfo.UpdateSql; + } + else if (columnInfo.SqlParameterDbType is Type && IsNoParameterConvert(columnInfo)) + { + var type = columnInfo.SqlParameterDbType as Type; + var ParameterConverter = type.GetMethod("ParameterConverter").MakeGenericMethod(typeof(string)); + var obj = Activator.CreateInstance(type); + var p = ParameterConverter.Invoke(obj, new object[] { columnInfo.Value, GetDbColumnIndex }) as SugarParameter; + return p.ParameterName; + } + else if (columnInfo.SqlParameterDbType is Type) + { + var type = columnInfo.SqlParameterDbType as Type; + var ParameterConverter = type.GetMethod("ParameterConverter").MakeGenericMethod(columnInfo.PropertyType); + var obj = Activator.CreateInstance(type); + var p = ParameterConverter.Invoke(obj, new object[] { columnInfo.Value, GetDbColumnIndex }) as SugarParameter; + GetDbColumnIndex++; + //this.Parameters.RemoveAll(it => it.ParameterName == it.ParameterName); + this.Parameters.Add(p); + return p.ParameterName; + } + else if (columnInfo.PropertyType?.Name == "TimeOnly" && name?.ObjToString().StartsWith(Builder.SqlParameterKeyWord) == false) + { + var timeSpan = UtilMethods.TimeOnlyToTimeSpan(columnInfo.Value); + var pname = Builder.SqlParameterKeyWord + columnInfo.DbColumnName + "_ts" + GetDbColumnIndex; + if (timeSpan == null) + { + this.Parameters.Add(new SugarParameter(pname, null) { DbType = System.Data.DbType.Date }); + } + else + { + this.Parameters.Add(new SugarParameter(pname, timeSpan)); + } + GetDbColumnIndex++; + return pname; + } + else if (columnInfo.PropertyType?.Name == "DateOnly") + { + var timeSpan = UtilMethods.DateOnlyToDateTime(columnInfo.Value); + var pname = Builder.SqlParameterKeyWord + columnInfo.DbColumnName + "_ts" + GetDbColumnIndex; + if (timeSpan == null) + { + this.Parameters.Add(new SugarParameter(pname, null) { DbType = System.Data.DbType.Date }); + } + else + { + this.Parameters.Add(new SugarParameter(pname, Convert.ToDateTime(timeSpan))); + } + GetDbColumnIndex++; + return pname; + } + else if (UtilMethods.IsErrorParameterName(this.Context.CurrentConnectionConfig, columnInfo)) + { + var pname = Builder.SqlParameterKeyWord + "CrorrPara" + GetDbColumnIndex; + var p = new SugarParameter(pname, columnInfo.Value); + this.Parameters.Add(p); + GetDbColumnIndex++; + return pname; + + } + else + { + return name + ""; + } + } + + private static bool IsNoParameterConvert(DbColumnInfo columnInfo) + { + if (columnInfo.SqlParameterDbType is Type t) + { + var isAssignableFrom = typeof(DbConvert.NoParameterCommonPropertyConvert).IsAssignableFrom(t); + if (isAssignableFrom) + { + return isAssignableFrom; + } + } + return (Type)columnInfo.SqlParameterDbType == UtilConstants.SqlConvertType; + } + + private bool IsSingleSetExp(DbColumnInfo columnInfo) + { + return this.ReSetValueBySqlExpList?.ContainsKey(columnInfo.PropertyName) == true && + this.IsListUpdate == null && + DbColumnInfoList.GroupBy(it => it.TableId).Count() == 1; + } + private bool IsListSetExp(DbColumnInfo columnInfo) + { + return this.ReSetValueBySqlExpListType != null && this.ReSetValueBySqlExpList?.ContainsKey(columnInfo.PropertyName) == true; + } + //public virtual string GetDbColumn(DbColumnInfo columnInfo, string name) + //{ + // if (columnInfo.UpdateServerTime) + // { + // return LambdaExpressions.DbMehtods.GetDate(); + // } + // else if (columnInfo.UpdateSql.HasValue()) + // { + // return columnInfo.UpdateSql; + // } + // else + // { + // return name + ""; + // } + //} + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SugarProvider/SqlSugarAccessory.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SugarProvider/SqlSugarAccessory.cs new file mode 100644 index 000000000..e24c6a642 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SugarProvider/SqlSugarAccessory.cs @@ -0,0 +1,683 @@ +using System.Linq.Expressions; +namespace SqlSugar +{ + public partial class SqlSugarProvider + { + #region Properties + public SqlSugarProvider Context + { + get + { + _Context = this; + return _Context; + } + set + { + _Context = value; + } + } + public SqlSugarClient Root { get; set; } + public ConnectionConfig CurrentConnectionConfig { get; set; } + public Dictionary TempItems { get { if (_TempItems == null) { _TempItems = new Dictionary(); } return _TempItems; } set { _TempItems = value; } } + public Guid ContextID { get; set; } + public MappingTableList MappingTables { get; set; } + public MappingColumnList MappingColumns { get; set; } + public IgnoreColumnList IgnoreColumns { get; set; } + public IgnoreColumnList IgnoreInsertColumns { get; set; } + public SugarActionType SugarActionType { get; set; } = SugarActionType.UnKnown; + public ConfigQuery ConfigQuery + { + get + { + if (_SqlConfigTable == null) + { + _SqlConfigTable = new ConfigQuery() { Context = this.Context }; + } + return _SqlConfigTable; + } + set + { + _SqlConfigTable = value; + } + } + + + #endregion + + #region Fields + public Dictionary _TempItems; + public QueueList _Queues; + protected ISqlBuilder _SqlBuilder; + protected SqlSugarProvider _Context { get; set; } + protected EntityMaintenance _EntityProvider; + protected IAdo _Ado; + protected ILambdaExpressions _LambdaExpressions; + protected IContextMethods _RewritableMethods; + protected IDbMaintenance _DbMaintenance; + protected QueryFilterProvider _QueryFilterProvider; + protected ConfigQuery _SqlConfigTable; + //protected SimpleClient _SimpleClient; + protected IAdo ContextAdo + { + get + { + return this._Ado; + } + set + { + this._Ado = value; + } + } + protected IContextMethods ContextRewritableMethods + { + get + { + return this._RewritableMethods; + } + set + { + this._RewritableMethods = value; + } + } + #endregion + + #region Init mappingInfo + protected void InitMappingInfo() + { + InitMappingInfo(); + InitMappingInfo(); + } + protected void InitMappingInfo() + { + InitMappingInfo(); + InitMappingInfo(); + } + protected void InitMappingInfo() + { + InitMappingInfo(); + InitMappingInfo(); + } + protected void InitMappingInfo() + { + InitMappingInfo(); + InitMappingInfo(); + } + protected void InitMappingInfo() + { + InitMappingInfo(); + InitMappingInfo(); + } + protected void InitMappingInfo() + { + InitMappingInfo(); + InitMappingInfo(); + } + protected void InitMappingInfo() + { + InitMappingInfo(); + InitMappingInfo(); + } + + #region 9-12 + protected void InitMappingInfo() + { + InitMappingInfo(); + InitMappingInfo(); + } + protected void InitMappingInfo() + { + InitMappingInfo(); + InitMappingInfo(); + } + protected void InitMappingInfo() + { + InitMappingInfo(); + InitMappingInfo(); + } + protected void InitMappingInfo() + { + InitMappingInfo(); + InitMappingInfo(); + } + #endregion + + public void InitMappingInfo() + { + InitMappingInfo(typeof(T)); + } + public void InitMappingInfo(Type type) + { + string cacheKey = "Context.InitAttributeMappingTables" + type.FullName + this.Context?.CurrentConnectionConfig?.ConfigId; + var entityInfo = this.Context.Utilities.GetReflectionInoCacheInstance().GetOrCreate(cacheKey, + () => + { + var result = this.Context.EntityMaintenance.GetEntityInfoWithAttr(type); + return result; + }); + //var copyObj = CopyEntityInfo(entityInfo); + InitMappingInfo(entityInfo); + } + public void InitMappingInfoNoCache(Type type) + { + var entityInfo = this.Context.EntityMaintenance.GetEntityInfoNoCache(type); + InitMappingInfo(entityInfo); + } + public EntityInfo GetEntityNoCacheInitMappingInfo(Type type) + { + var entityInfo = this.Context.EntityMaintenance.GetEntityInfoNoCache(type); + InitMappingInfo(entityInfo); + return entityInfo; + } + //private EntityInfo CopyEntityInfo(EntityInfo entityInfo) + //{ + // EntityInfo result = new EntityInfo() + // { + // DbTableName = entityInfo.DbTableName, + // EntityName = entityInfo.EntityName, + // Type = entityInfo.Type + // }; + // List columns = new List(); + // if (entityInfo.Columns.HasValue()) + // { + // foreach (var item in entityInfo.Columns) + // { + // EntityColumnInfo column = new EntityColumnInfo() + // { + // ColumnDescription = item.ColumnDescription, + // DataType = item.DataType, + // DbColumnName = item.DbColumnName, + // DbTableName = item.DbTableName, + // DecimalDigits = item.DecimalDigits, + // DefaultValue = item.DefaultValue, + // EntityName = item.EntityName, + // IsIdentity = item.IsIdentity, + // IsIgnore = item.IsIgnore, + // IsNullable = item.IsNullable, + // IsOnlyIgnoreInsert = item.IsOnlyIgnoreInsert, + // IsPrimarykey = item.IsPrimarykey, + // Length = item.Length, + // OldDbColumnName = item.OldDbColumnName, + // OracleSequenceName = item.OracleSequenceName, + // PropertyInfo = item.PropertyInfo, + // PropertyName = item.PropertyName, + // IsArray=item.IsArray, + // IsJson=item.IsJson + // }; + // columns.Add(item); + // } + // } + // result.Columns = columns; + // return result; + //} + + private void InitMappingInfo(EntityInfo entityInfo) + { + if (this.MappingTables == null) + this.MappingTables = new MappingTableList(); + if (this.MappingColumns == null) + this.MappingColumns = new MappingColumnList(); + if (this.IgnoreColumns == null) + this.IgnoreColumns = new IgnoreColumnList(); + if (this.IgnoreInsertColumns == null) + this.IgnoreInsertColumns = new IgnoreColumnList(); + if (!this.MappingTables.Any(it => it.EntityName == entityInfo.EntityName)) + { + if (entityInfo.DbTableName != entityInfo.EntityName && entityInfo.DbTableName.HasValue()) + { + this.MappingTables.Add(entityInfo.EntityName, entityInfo.DbTableName); + } + } + if (entityInfo.Columns.Any(it => it.EntityName == entityInfo.EntityName)) + { + var mappingColumnInfos = this.MappingColumns.Where(it => it.EntityName == entityInfo.EntityName); + foreach (var item in entityInfo.Columns.Where(it => it.IsIgnore == false)) + { + if (!mappingColumnInfos.Any(it => it.PropertyName == item.PropertyName)) + if (item.PropertyName != item.DbColumnName && item.DbColumnName.HasValue()) + this.MappingColumns.Add(item.PropertyName, item.DbColumnName, item.EntityName); + } + var ignoreInfos = this.IgnoreColumns.Where(it => it.EntityName == entityInfo.EntityName); + foreach (var item in entityInfo.Columns.Where(it => it.IsIgnore)) + { + if (!ignoreInfos.Any(it => it.PropertyName == item.PropertyName)) + this.IgnoreColumns.Add(item.PropertyName, item.EntityName); + } + + var ignoreInsertInfos = this.IgnoreInsertColumns.Where(it => it.EntityName == entityInfo.EntityName); + foreach (var item in entityInfo.Columns.Where(it => it.IsOnlyIgnoreInsert)) + { + if (!ignoreInsertInfos.Any(it => it.PropertyName == item.PropertyName)) + this.IgnoreInsertColumns.Add(item.PropertyName, item.EntityName); + } + } + } + #endregion + + #region Create Instance + protected ISugarQueryable CreateQueryable() + { + ISugarQueryable result = InstanceFactory.GetQueryable(this.CurrentConnectionConfig); + return CreateQueryable(result); + } + protected ISugarQueryable CreateQueryable(ISugarQueryable result) + { + this.SugarActionType = SugarActionType.Query; + Check.Exception(typeof(T).IsClass() == false || typeof(T).GetConstructors().Length == 0, "Queryable<{0}> Error ,{0} is invalid , need is a class,and can new().", typeof(T).Name); + var sqlBuilder = InstanceFactory.GetSqlbuilder(CurrentConnectionConfig); + result.Context = this.Context; + result.SqlBuilder = sqlBuilder; + result.SqlBuilder.QueryBuilder = InstanceFactory.GetQueryBuilder(CurrentConnectionConfig); + result.SqlBuilder.QueryBuilder.Builder = sqlBuilder; + result.SqlBuilder.Context = result.SqlBuilder.QueryBuilder.Context = this; + result.SqlBuilder.QueryBuilder.EntityType = typeof(T); + result.SqlBuilder.QueryBuilder.EntityName = typeof(T).Name; + result.SqlBuilder.QueryBuilder.LambdaExpressions = InstanceFactory.GetLambdaExpressions(CurrentConnectionConfig); + if (StaticConfig.CompleteQueryableFunc != null) + { + StaticConfig.CompleteQueryableFunc(result); + } + return result; + } + protected InsertableProvider CreateInsertable(T[] insertObjs) where T : class, new() + { + this.SugarActionType = SugarActionType.Insert; + var result = InstanceFactory.GetInsertableProvider(this.CurrentConnectionConfig); + var sqlBuilder = InstanceFactory.GetSqlbuilder(this.CurrentConnectionConfig); ; + result.Context = this; + result.EntityInfo = this.Context.EntityMaintenance.GetEntityInfo(); + result.SqlBuilder = sqlBuilder; + result.InsertObjs = insertObjs; + sqlBuilder.InsertBuilder = result.InsertBuilder = InstanceFactory.GetInsertBuilder(this.CurrentConnectionConfig); + sqlBuilder.InsertBuilder.Builder = sqlBuilder; + sqlBuilder.InsertBuilder.LambdaExpressions = InstanceFactory.GetLambdaExpressions(this.CurrentConnectionConfig); + sqlBuilder.Context = result.SqlBuilder.InsertBuilder.Context = this; + result.Init(); + if (StaticConfig.CompleteInsertableFunc != null) + { + StaticConfig.CompleteInsertableFunc(result); + } + return result; + } + protected DeleteableProvider CreateDeleteable() where T : class, new() + { + this.SugarActionType = SugarActionType.Delete; + var result = InstanceFactory.GetDeleteableProvider(this.CurrentConnectionConfig); + var sqlBuilder = InstanceFactory.GetSqlbuilder(this.CurrentConnectionConfig); ; + result.Context = this; + result.SqlBuilder = sqlBuilder; + sqlBuilder.DeleteBuilder = result.DeleteBuilder = InstanceFactory.GetDeleteBuilder(this.CurrentConnectionConfig); + sqlBuilder.DeleteBuilder.Builder = sqlBuilder; + sqlBuilder.DeleteBuilder.LambdaExpressions = InstanceFactory.GetLambdaExpressions(this.CurrentConnectionConfig); + sqlBuilder.Context = result.SqlBuilder.DeleteBuilder.Context = this; + if (StaticConfig.CompleteDeleteableFunc != null) + { + StaticConfig.CompleteDeleteableFunc(result); + } + return result; + } + protected UpdateableProvider CreateUpdateable(T[] UpdateObjs) where T : class, new() + { + this.SugarActionType = SugarActionType.Update; + var result = InstanceFactory.GetUpdateableProvider(this.CurrentConnectionConfig); + var sqlBuilder = InstanceFactory.GetSqlbuilder(this.CurrentConnectionConfig); ; + result.Context = this; + result.EntityInfo = this.Context.EntityMaintenance.GetEntityInfo(); + result.SqlBuilder = sqlBuilder; + result.UpdateObjs = UpdateObjs; + sqlBuilder.UpdateBuilder = result.UpdateBuilder = InstanceFactory.GetUpdateBuilder(this.CurrentConnectionConfig); + sqlBuilder.UpdateBuilder.Builder = sqlBuilder; + sqlBuilder.UpdateBuilder.LambdaExpressions = InstanceFactory.GetLambdaExpressions(this.CurrentConnectionConfig); + sqlBuilder.Context = result.SqlBuilder.UpdateBuilder.Context = this; + result.Init(); + if (StaticConfig.CompleteUpdateableFunc != null) + { + StaticConfig.CompleteUpdateableFunc(result); + } + return result; + } + + protected void CreateQueryJoin(Expression joinExpression, Type[] types, ISugarQueryable queryable) + { + this.SugarActionType = SugarActionType.Query; + this.CreateQueryable(queryable); + string shortName = string.Empty; + List paramters = new List(); + queryable.SqlBuilder.QueryBuilder.JoinQueryInfos = this.GetJoinInfos(queryable.SqlBuilder, joinExpression, ref paramters, ref shortName, types); + if (queryable.SqlBuilder.QueryBuilder.JoinQueryInfos.Count != 0) + { + queryable.SqlBuilder.QueryBuilder.JoinQueryInfos.Last().EntityType = types.Last(); + } + queryable.SqlBuilder.QueryBuilder.TableShortName = shortName; + queryable.SqlBuilder.QueryBuilder.JoinExpression = joinExpression; + if (paramters != null) + { + queryable.SqlBuilder.QueryBuilder.Parameters.AddRange(paramters); + } + UtilMethods.AddDiscrimator(typeof(T), queryable, queryable.QueryBuilder.TableShortName + "."); + } + protected void CreateEasyQueryJoin(Expression joinExpression, Type[] types, ISugarQueryable queryable) + { + this.SugarActionType = SugarActionType.Query; + this.CreateQueryable(queryable); + string shortName = string.Empty; + queryable.SqlBuilder.QueryBuilder.EasyJoinInfos = this.GetEasyJoinInfo(joinExpression, ref shortName, queryable.SqlBuilder, types); + queryable.SqlBuilder.QueryBuilder.TableShortName = shortName; + queryable.SqlBuilder.QueryBuilder.JoinExpression = joinExpression; + var isNoPgAuto = this.Context.CurrentConnectionConfig.MoreSettings?.PgSqlIsAutoToLower == false; + if (isNoPgAuto) + queryable.SqlBuilder.QueryBuilder.TableShortName = queryable.SqlBuilder.GetTranslationColumnName(shortName); + } + #endregion + + #region Private methods + private void _ThenMapper(IEnumerable list, Action action) + { + MapperContext result = new MapperContext(); + result.context = this.Context; + if (result.context.TempItems == null) + { + result.context.TempItems = new Dictionary(); + } + var key = "Queryable_To_Context"; + result.context.TempItems.Add(key, result); + result.list = list.ToList(); + foreach (var item in list) + { + action.Invoke(item); + } + result.context.TempItems.Remove(key); + } + private async Task _ThenMapperAsync(IEnumerable list, Func action) + { + MapperContext result = new MapperContext(); + result.context = this.Context; + if (result.context.TempItems == null) + { + result.context.TempItems = new Dictionary(); + } + var key = "Queryable_To_Context"; + result.context.TempItems.Add(key, result); + result.list = list.ToList(); + foreach (var item in list) + { + await action.Invoke(item).ConfigureAwait(false); + } + result.context.TempItems.Remove(key); + } + internal string GetN() + { + var N = "N"; + if (_Context.CurrentConnectionConfig.MoreSettings?.DisableNvarchar == true) + { + N = ""; + } + return N; + } + public bool IsVarchar() + { + if (_Context.CurrentConnectionConfig.MoreSettings?.DisableNvarchar == true) + { + return true; + } + return false; + } + private void CheckDbDependency(ConnectionConfig config) + { + switch (config.DbType) + { + case DbType.MySql: + DependencyManagement.TryMySqlData(); + break; + case DbType.SqlServer: + break; + case DbType.Sqlite: + DependencyManagement.TrySqlite(); + break; + case DbType.Oracle: + DependencyManagement.TryOracle(); + break; + case DbType.PostgreSQL: + DependencyManagement.TryPostgreSQL(); + break; + case DbType.OpenGauss: + config.DbType = DbType.PostgreSQL; + if (this.CurrentConnectionConfig.MoreSettings == null) + this.CurrentConnectionConfig.MoreSettings = new ConnMoreSettings(); + this.CurrentConnectionConfig.MoreSettings.DatabaseModel = DbType.OpenGauss; + break; + case DbType.HG: + InstanceFactory.CustomDllName = SugarCompatible.IsFramework ? throw new Exception("Only.NET CORE is supported") : "SqlSugar.HGCore"; + break; + case DbType.Kdbndp: + DependencyManagement.TryKdbndb(); + break; + case DbType.Dm: + DependencyManagement.TryDm(); + break; + case DbType.Oscar: + DependencyManagement.TryOscar(); + break; + case DbType.MySqlConnector: + InstanceFactory.CustomDllName = SugarCompatible.IsFramework ? "SqlSugar.MySqlConnector" : "SqlSugar.MySqlConnectorCore"; + if (SugarCompatible.IsFramework.ObjToBool() == false) + { + config.DbType = DbType.MySql; + InstanceFactory.CustomDllName = null; + } + break; + case DbType.Access: + InstanceFactory.CustomDllName = SugarCompatible.IsFramework ? "SqlSugar.Access" : "SqlSugar.AccessCore"; + break; + case DbType.Custom: + Check.Exception(InstanceFactory.CustomDbName.IsNullOrEmpty(), "DbType.Custom: InstanceFactory.CustomDbName is not null "); + Check.Exception(InstanceFactory.CustomNamespace.IsNullOrEmpty(), "DbType.Custom: InstanceFactory.CustomNamespace is not null "); + Check.Exception(InstanceFactory.CustomDllName.IsNullOrEmpty(), "DbType.Custom: InstanceFactory.CustomDllName is not null "); + break; + case DbType.QuestDB: + DependencyManagement.TryPostgreSQL(); + break; + case DbType.ClickHouse: + Check.Exception(SugarCompatible.IsFramework, "ClickHouse only support .net core"); + InstanceFactory.CustomDllName = SugarCompatible.IsFramework ? "SqlSugar.ClickHouse" : "SqlSugar.ClickHouseCore"; + break; + case DbType.GBase: + Check.Exception(SugarCompatible.IsFramework, "GBase only support .net core"); + InstanceFactory.CustomDllName = SugarCompatible.IsFramework ? "SqlSugar.GBase" : "SqlSugar.GBaseCore"; + break; + case DbType.Odbc: + InstanceFactory.CustomDllName = SugarCompatible.IsFramework ? "SqlSugar.Odbc" : "SqlSugar.OdbcCore"; + break; + case DbType.OceanBaseForOracle: + Check.Exception(SugarCompatible.IsFramework, "OceanBaseForOracle only support .net core"); + InstanceFactory.CustomDllName = SugarCompatible.IsFramework ? "SqlSugar.OceanBaseForOracle" : "SqlSugar.OceanBaseForOracleCore"; + break; + case DbType.TDSQLForPGODBC: + Check.Exception(SugarCompatible.IsFramework, "TDSQLForPGODBC only support .net core"); + InstanceFactory.CustomDllName = SugarCompatible.IsFramework ? "SqlSugar.TDSQLForPGODBC" : "SqlSugar.TDSQLForPGODBC"; + break; + case DbType.GaussDB: + config.DbType = DbType.PostgreSQL; + if (this.CurrentConnectionConfig.MoreSettings == null) + this.CurrentConnectionConfig.MoreSettings = new ConnMoreSettings(); + this.CurrentConnectionConfig.MoreSettings.DatabaseModel = DbType.GaussDB; + break; + case DbType.Vastbase: + config.DbType = DbType.PostgreSQL; + if (this.CurrentConnectionConfig.MoreSettings == null) + this.CurrentConnectionConfig.MoreSettings = new ConnMoreSettings(); + this.CurrentConnectionConfig.MoreSettings.DatabaseModel = DbType.Vastbase; + break; + case DbType.OceanBase: + config.DbType = DbType.MySql; + if (this.CurrentConnectionConfig.MoreSettings == null) + this.CurrentConnectionConfig.MoreSettings = new ConnMoreSettings(); + this.CurrentConnectionConfig.MoreSettings.DatabaseModel = DbType.OceanBase; + break; + case DbType.Tidb: + config.DbType = DbType.MySql; + break; + case DbType.PolarDB: + config.DbType = DbType.MySql; + break; + case DbType.TDSQL: + config.DbType = DbType.MySql; + break; + case DbType.Doris: + config.DbType = DbType.MySql; + if (this.CurrentConnectionConfig.MoreSettings == null) + this.CurrentConnectionConfig.MoreSettings = new ConnMoreSettings(); + this.CurrentConnectionConfig.MoreSettings.DatabaseModel = DbType.Doris; + this.CurrentConnectionConfig.MoreSettings.DisableNvarchar = true; + break; + case DbType.TDengine: + Check.Exception(SugarCompatible.IsFramework, "TDengine only support .net core"); + InstanceFactory.CustomDllName = SugarCompatible.IsFramework ? "SqlSugar.TDengine" : "SqlSugar.TDengineCore"; + break; + case DbType.HANA: + Check.Exception(SugarCompatible.IsFramework, "NANA only support .net core"); + InstanceFactory.CustomDllName = "SqlSugar.HANAConnector"; + break; + case DbType.Xugu: + Check.Exception(SugarCompatible.IsFramework, "Xugu only support .net core"); + //InstanceFactory.CustomDbName = "Xugu"; + InstanceFactory.CustomDllName = "SqlSugar.XuguCore"; + //InstanceFactory.CustomNamespace = "SqlSugar.Xugu"; + break; + case DbType.GoldenDB: + config.DbType = DbType.MySql; + break; + case DbType.DB2: + Check.Exception(SugarCompatible.IsFramework, "Db2 only support .net core"); + InstanceFactory.CustomDllName = "SqlSugar.Db2Core"; + break; + case DbType.GaussDBNative: + Check.Exception(SugarCompatible.IsFramework, "GaussDBNative only support .net core"); + InstanceFactory.CustomDllName = "SqlSugar.GaussDBCore"; + break; + case DbType.DuckDB: + InstanceFactory.CustomDllName = SugarCompatible.IsFramework ? throw new Exception("Only.NET CORE is supported") : "SqlSugar.DuckDBCore"; + break; + case DbType.MongoDb: + InstanceFactory.CustomDllName = SugarCompatible.IsFramework ? throw new Exception("Only.NET CORE is supported") : "SqlSugar.MongoDbCore"; + break; + default: + throw new Exception("ConnectionConfig.DbType is null"); + } + } + protected List GetJoinInfos(ISqlBuilder sqlBuilder, Expression joinExpression, ref List parameters, ref string shortName, params Type[] entityTypeArray) + { + List result = new List(); + var lambdaParameters = ((LambdaExpression)joinExpression).Parameters.ToList(); + ILambdaExpressions expressionContext = sqlBuilder.QueryBuilder.LambdaExpressions; + expressionContext.MappingColumns = this.MappingColumns; + expressionContext.MappingTables = this.MappingTables; + expressionContext.IsSingle = false; + expressionContext.SugarContext = new ExpressionOutParameter() { Context = this.Context }; + if (this.Context.CurrentConnectionConfig.MoreSettings != null) + { + expressionContext.PgSqlIsAutoToLower = this.Context.CurrentConnectionConfig.MoreSettings.PgSqlIsAutoToLower; + } + else + { + expressionContext.PgSqlIsAutoToLower = true; + } + if (this.Context.CurrentConnectionConfig.ConfigureExternalServices != null) + expressionContext.SqlFuncServices = this.Context.CurrentConnectionConfig.ConfigureExternalServices.SqlFuncServices; + expressionContext.Resolve(joinExpression, ResolveExpressType.Join); + int i = 0; + var joinArray = MergeJoinArray(expressionContext.Result.GetResultArray()); + if (joinArray == null) return null; + parameters = expressionContext.Parameters; + foreach (var entityType in entityTypeArray) + { + var isFirst = i == 0; ++i; + JoinQueryInfo joinInfo = new JoinQueryInfo(); + var hasMappingTable = expressionContext.MappingTables.HasValue(); + MappingTable mappingInfo = null; + if (hasMappingTable) + { + mappingInfo = expressionContext.MappingTables.FirstOrDefault(it => it.EntityName.Equals(entityType.Name, StringComparison.CurrentCultureIgnoreCase)); + joinInfo.TableName = mappingInfo != null ? mappingInfo.DbTableName : entityType.Name; + } + else + { + joinInfo.TableName = entityType.Name; + } + var isNoPgAuto = this.Context.CurrentConnectionConfig.MoreSettings?.PgSqlIsAutoToLower == false; + if (isFirst) + { + var firstItem = lambdaParameters.First(); + lambdaParameters.Remove(firstItem); + shortName = firstItem.Name; + if (isNoPgAuto) + shortName = sqlBuilder.GetTranslationColumnName(shortName); + } + var joinString = joinArray[i * 2 - 2]; + joinInfo.ShortName = lambdaParameters[i - 1].Name; + joinInfo.JoinType = (JoinType)Enum.Parse(typeof(JoinType), joinString); + joinInfo.JoinWhere = joinArray[i * 2 - 1]; + joinInfo.JoinIndex = i; + joinInfo.EntityType = entityType; + if (isNoPgAuto) + joinInfo.ShortName = sqlBuilder.GetTranslationColumnName(joinInfo.ShortName); + result.Add((joinInfo)); + } + expressionContext.Clear(); + return result; + } + + private string[] MergeJoinArray(string[] joinArray) + { + List result = new List(); + string joinValue = null; + int i = 0; + if (joinArray == null) return null; + foreach (var item in joinArray) + { + ++i; + var isLast = joinArray.Length == i; + var isJoinType = item.IsIn(JoinType.Full.ToString(), JoinType.Inner.ToString(), JoinType.Left.ToString(), JoinType.Right.ToString(), JoinType.Cross.ToString()); + if (isJoinType) + { + if (joinValue != null) + result.Add(joinValue); + joinValue = null; + result.Add(item); + } + else + { + isJoinType = false; + joinValue += joinValue == null ? item : ("," + item); + } + if (isLast) + { + result.Add(joinValue); + } + } + return result.ToArray(); ; + } + + protected Dictionary GetEasyJoinInfo(Expression joinExpression, ref string shortName, ISqlBuilder builder, params Type[] entityTypeArray) + { + Dictionary result = new Dictionary(); + var lambdaParameters = ((LambdaExpression)joinExpression).Parameters.ToList(); + shortName = lambdaParameters.First().Name; + var isNoPgAuto = this.Context.CurrentConnectionConfig.MoreSettings?.PgSqlIsAutoToLower == false; + var index = 1; + foreach (var item in entityTypeArray) + { + if (isNoPgAuto) + { + result.Add(UtilConstants.Space + builder.GetTranslationColumnName(lambdaParameters[index].Name), item.Name); + } + else + { + result.Add(UtilConstants.Space + lambdaParameters[index].Name, item.Name); + } + ++index; + } + return result; + } + #endregion + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SugarProvider/SqlSugarCoreProvider.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SugarProvider/SqlSugarCoreProvider.cs new file mode 100644 index 000000000..42917f97b --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SugarProvider/SqlSugarCoreProvider.cs @@ -0,0 +1,84 @@ +using System.Diagnostics; + +namespace SqlSugar +{ + /// + /// Partial SqlSugarScope + /// + public partial class SqlSugarScope : ISqlSugarClient, ITenant + { + + private List _configs; + private Action _configAction; + + protected virtual SqlSugarClient GetContext() + { + SqlSugarClient result = null; + var key = _configs.GetHashCode().ToString(); + StackTrace st = new StackTrace(true); + var methods = st.GetFrames(); + var isAsync = UtilMethods.IsAnyAsyncMethod(methods); + if (methods?.Length >= 0) + { + foreach (var method in methods.Take(35)) + { + var refType = method.GetMethod()?.ReflectedType; + if (refType != null) + { + var getInterfaces = refType.Name.StartsWith('<') ? refType?.ReflectedType?.GetInterfaces() : refType?.GetInterfaces(); + if (getInterfaces?.Any(it => it.Name.IsIn("IJob")) == true) + { + key = $"{key}IJob"; + break; + } + } + } + } + if (isAsync) + { + result = GetAsyncContext(key); + } + else + { + result = GetThreadContext(key); + } + return result; + } + private SqlSugarClient GetAsyncContext(string key) + { + SqlSugarClient result = CallContextAsync.GetData(key); + if (result == null) + { + List configList = GetCopyConfigs(); + CallContextAsync.SetData(key, new SqlSugarClient(configList)); + result = CallContextAsync.GetData(key); + if (this._configAction != null) + { + this._configAction(result); + } + } + + return result; + } + private SqlSugarClient GetThreadContext(string key) + { + SqlSugarClient result = CallContextThread.GetData(key); + if (result == null) + { + List configList = GetCopyConfigs(); + CallContextThread.SetData(key, new SqlSugarClient(configList)); + result = CallContextThread.GetData(key); + if (this._configAction != null) + { + this._configAction(result); + } + } + return result; + } + private List GetCopyConfigs() + { + return _configs.Select(it => UtilMethods.CopyConfig(it)).ToList(); + } + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SugarProvider/SqlSugarProvider.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SugarProvider/SqlSugarProvider.cs new file mode 100644 index 000000000..da73be30e --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SugarProvider/SqlSugarProvider.cs @@ -0,0 +1,1864 @@ +using System.Collections; +using System.Data; +using System.Dynamic; +using System.Linq.Expressions; +using System.Text; +namespace SqlSugar +{ + /// + /// ** description:Create datathis.access object + /// ** author:sunkaixuan + /// ** date:2017/1/2 + /// ** email:610262374@qq.com + /// + public partial class SqlSugarProvider : ISqlSugarClient + { + + #region Constructor + public SqlSugarProvider(ConnectionConfig config) + { + this.Context = this; + this.CurrentConnectionConfig = config; + this.ContextID = Guid.NewGuid(); + Check.ArgumentNullException(config, "config is null"); + CheckDbDependency(config); + if (StaticConfig.CompleteDbFunc != null) + { + StaticConfig.CompleteDbFunc(this); + } + } + #endregion + + #region ADO Methods + /// + ///Datathis.operation + /// + public virtual IAdo Ado + { + get + { + if (this.ContextAdo == null) + { + var result = InstanceFactory.GetAdo(this.Context.CurrentConnectionConfig); + this.ContextAdo = result; + result.Context = this; + return result; + } + return this._Ado; + } + } + #endregion + + #region Aop Log Methods + public virtual AopProvider Aop { get { return new AopProvider(this); } } + #endregion + + #region Util Methods + [Obsolete("Use SqlSugarClient.Utilities")] + public virtual IContextMethods RewritableMethods + { + get { return this.Context.Utilities; } + set { this.Context.Utilities = value; } + } + public virtual IContextMethods Utilities + { + get + { + if (ContextRewritableMethods == null) + { + ContextRewritableMethods = new ContextMethods(); + ContextRewritableMethods.Context = this; + } + return ContextRewritableMethods; + } + set { ContextRewritableMethods = value; } + } + #endregion + + #region Queryable + public QueryMethodInfo QueryableByObject(Type entityType) + { + QueryMethodInfo result = new QueryMethodInfo(); + var method = this.GetType().GetMyMethod("Queryable", 0); + var methodT = method.MakeGenericMethod(entityType); + var queryableObj = methodT.Invoke(this, Array.Empty()); + result.QueryableObj = queryableObj; + result.Context = this.Context; + result.EntityType = entityType; + return result; + } + public QueryMethodInfo QueryableByObject(Type entityType, string shortName) + { + return this.QueryableByObject(entityType).AS(this.Context.EntityMaintenance.GetTableName(entityType), shortName); + } + /// + /// Get datebase time + /// + /// + public DateTime GetDate() + { + var sqlBuilder = InstanceFactory.GetSqlbuilder(this.Context.CurrentConnectionConfig); + return this.Ado.GetDateTime(sqlBuilder.FullSqlDateNow); + } + public ISugarQueryable MasterQueryable() + { + var result = this.Queryable(); + result.QueryBuilder.IsDisableMasterSlaveSeparation = true; + return result; + } + + public ISugarQueryable SlaveQueryable() + { + var result = this.Queryable(); + result.QueryBuilder.IsEnableMasterSlaveSeparation = true; + return result; + } + /// + /// Lambda Query operation + /// + public virtual ISugarQueryable Queryable() + { + + InitMappingInfo(); + var result = this.CreateQueryable(); + UtilMethods.AddDiscrimator(typeof(T), result); + return result; + } + /// + /// Lambda Query operation + /// + public virtual ISugarQueryable Queryable(string shortName) + { + Check.Exception(shortName.HasValue() && shortName.Length > 40, ErrorMessage.GetThrowMessage("shortName参数长度不能超过40,你可能是想用这个方法 db.SqlQueryable(sql)而不是db.Queryable(shortName)", "Queryable.shortName max length 20")); + var queryable = Queryable(); + queryable.SqlBuilder.QueryBuilder.TableShortName = shortName; + return queryable; + } + /// + /// Lambda Query operation + /// + public virtual ISugarQueryable Queryable(string tableName, string shortName) + { + var queryable = Queryable(); + queryable.SqlBuilder.QueryBuilder.EntityName = tableName; + queryable.SqlBuilder.QueryBuilder.TableShortName = shortName; + return queryable; + } + public virtual ISugarQueryable Queryable(Expression> joinExpression) + { + InitMappingInfo(); + var types = new Type[] { typeof(T2) }; + var queryable = InstanceFactory.GetQueryable(this.CurrentConnectionConfig); + this.CreateQueryJoin(joinExpression, types, queryable); + return queryable; + } + public virtual ISugarQueryable Queryable(Expression> joinExpression) + { + InitMappingInfo(); + var types = new Type[] { typeof(T2) }; + var queryable = InstanceFactory.GetQueryable(this.CurrentConnectionConfig); + this.CreateQueryJoin(joinExpression, types, queryable); + return queryable; + } + public virtual ISugarQueryable Queryable(Expression> joinExpression) + { + InitMappingInfo(); + var types = new Type[] { typeof(T2), typeof(T3) }; + var queryable = InstanceFactory.GetQueryable(this.CurrentConnectionConfig); + this.CreateQueryJoin(joinExpression, types, queryable); + return queryable; + } + public virtual ISugarQueryable Queryable(Expression> joinExpression) + { + InitMappingInfo(); + var types = new Type[] { typeof(T2), typeof(T3) }; + var queryable = InstanceFactory.GetQueryable(this.CurrentConnectionConfig); + this.CreateQueryJoin(joinExpression, types, queryable); + return queryable; + } + public virtual ISugarQueryable Queryable(Expression> joinExpression) + { + InitMappingInfo(); + var types = new Type[] { typeof(T2), typeof(T3), typeof(T4) }; + var queryable = InstanceFactory.GetQueryable(this.CurrentConnectionConfig); + this.CreateQueryJoin(joinExpression, types, queryable); + return queryable; + } + public virtual ISugarQueryable Queryable(Expression> joinExpression) + { + InitMappingInfo(); + var types = new Type[] { typeof(T2), typeof(T3), typeof(T4) }; + var queryable = InstanceFactory.GetQueryable(this.CurrentConnectionConfig); + this.CreateQueryJoin(joinExpression, types, queryable); + return queryable; + } + public virtual ISugarQueryable Queryable(Expression> joinExpression) + { + InitMappingInfo(); + var types = new Type[] { typeof(T2), typeof(T3), typeof(T4), typeof(T5) }; + var queryable = InstanceFactory.GetQueryable(this.CurrentConnectionConfig); + this.CreateQueryJoin(joinExpression, types, queryable); + return queryable; + } + public virtual ISugarQueryable Queryable(Expression> joinExpression) + { + InitMappingInfo(); + var types = new Type[] { typeof(T2), typeof(T3), typeof(T4), typeof(T5) }; + var queryable = InstanceFactory.GetQueryable(this.CurrentConnectionConfig); + this.CreateQueryJoin(joinExpression, types, queryable); + return queryable; + } + public virtual ISugarQueryable Queryable(Expression> joinExpression) + { + InitMappingInfo(); + var types = new Type[] { typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6) }; + var queryable = InstanceFactory.GetQueryable(this.CurrentConnectionConfig); + this.CreateQueryJoin(joinExpression, types, queryable); + return queryable; + } + public virtual ISugarQueryable Queryable(Expression> joinExpression) + { + InitMappingInfo(); + var types = new Type[] { typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6) }; + var queryable = InstanceFactory.GetQueryable(this.CurrentConnectionConfig); + this.CreateQueryJoin(joinExpression, types, queryable); + return queryable; + } + public virtual ISugarQueryable Queryable(Expression> joinExpression) + { + InitMappingInfo(); + var types = new Type[] { typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7) }; + var queryable = InstanceFactory.GetQueryable(this.CurrentConnectionConfig); + this.CreateQueryJoin(joinExpression, types, queryable); + return queryable; + } + public virtual ISugarQueryable Queryable(Expression> joinExpression) + { + InitMappingInfo(); + var types = new Type[] { typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7) }; + var queryable = InstanceFactory.GetQueryable(this.CurrentConnectionConfig); + this.CreateQueryJoin(joinExpression, types, queryable); + return queryable; + } + public virtual ISugarQueryable Queryable(Expression> joinExpression) + { + InitMappingInfo(); + var types = new Type[] { typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8) }; + var queryable = InstanceFactory.GetQueryable(this.CurrentConnectionConfig); + this.CreateQueryJoin(joinExpression, types, queryable); + return queryable; + } + public virtual ISugarQueryable Queryable(Expression> joinExpression) + { + InitMappingInfo(); + var types = new Type[] { typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8) }; + var queryable = InstanceFactory.GetQueryable(this.CurrentConnectionConfig); + this.CreateQueryJoin(joinExpression, types, queryable); + return queryable; + } + #region 9-12 + public virtual ISugarQueryable Queryable(Expression> joinExpression) + { + InitMappingInfo(); + var types = new Type[] { typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9) }; + var queryable = InstanceFactory.GetQueryable(this.CurrentConnectionConfig); + this.CreateQueryJoin(joinExpression, types, queryable); + return queryable; + } + public virtual ISugarQueryable Queryable(Expression> joinExpression) + { + InitMappingInfo(); + var types = new Type[] { typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9) }; + var queryable = InstanceFactory.GetQueryable(this.CurrentConnectionConfig); + this.CreateQueryJoin(joinExpression, types, queryable); + return queryable; + } + public virtual ISugarQueryable Queryable(Expression> joinExpression) + { + InitMappingInfo(); + var types = new Type[] { typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9), typeof(T10) }; + var queryable = InstanceFactory.GetQueryable(this.CurrentConnectionConfig); + this.CreateQueryJoin(joinExpression, types, queryable); + return queryable; + } + public virtual ISugarQueryable Queryable(Expression> joinExpression) + { + InitMappingInfo(); + var types = new Type[] { typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9), typeof(T10) }; + var queryable = InstanceFactory.GetQueryable(this.CurrentConnectionConfig); + this.CreateQueryJoin(joinExpression, types, queryable); + return queryable; + } + public virtual ISugarQueryable Queryable(Expression> joinExpression) + { + InitMappingInfo(); + var types = new Type[] { typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9), typeof(T10), typeof(T11) }; + var queryable = InstanceFactory.GetQueryable(this.CurrentConnectionConfig); + this.CreateQueryJoin(joinExpression, types, queryable); + return queryable; + } + public virtual ISugarQueryable Queryable(Expression> joinExpression) + { + InitMappingInfo(); + var types = new Type[] { typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9), typeof(T10), typeof(T11) }; + var queryable = InstanceFactory.GetQueryable(this.CurrentConnectionConfig); + this.CreateQueryJoin(joinExpression, types, queryable); + return queryable; + } + public virtual ISugarQueryable Queryable(Expression> joinExpression) + { + InitMappingInfo(); + var types = new Type[] { typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9), typeof(T10), typeof(T11), typeof(T12) }; + var queryable = InstanceFactory.GetQueryable(this.CurrentConnectionConfig); + this.CreateQueryJoin(joinExpression, types, queryable); + return queryable; + } + public virtual ISugarQueryable Queryable(Expression> joinExpression) + { + InitMappingInfo(); + var types = new Type[] { typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9), typeof(T10), typeof(T11), typeof(T12) }; + var queryable = InstanceFactory.GetQueryable(this.CurrentConnectionConfig); + this.CreateQueryJoin(joinExpression, types, queryable); + return queryable; + } + #endregion + public virtual ISugarQueryable Queryable(Expression> joinExpression) where T : class, new() + { + InitMappingInfo(); + var types = new Type[] { typeof(T2) }; + var queryable = InstanceFactory.GetQueryable(this.CurrentConnectionConfig); + this.CreateEasyQueryJoin(joinExpression, types, queryable); + queryable.Where(joinExpression); + return queryable; + } + public virtual ISugarQueryable Queryable(Expression> joinExpression) where T : class, new() + { + InitMappingInfo(); + var types = new Type[] { typeof(T2), typeof(T3) }; + var queryable = InstanceFactory.GetQueryable(this.CurrentConnectionConfig); + this.CreateEasyQueryJoin(joinExpression, types, queryable); + queryable.Where(joinExpression); + return queryable; + } + public virtual ISugarQueryable Queryable(Expression> joinExpression) where T : class, new() + { + InitMappingInfo(); + var types = new Type[] { typeof(T2), typeof(T3), typeof(T4) }; + var queryable = InstanceFactory.GetQueryable(this.CurrentConnectionConfig); + this.CreateEasyQueryJoin(joinExpression, types, queryable); + queryable.Where(joinExpression); + return queryable; + } + public virtual ISugarQueryable Queryable(Expression> joinExpression) where T : class, new() + { + InitMappingInfo(); + var types = new Type[] { typeof(T2), typeof(T3), typeof(T4), typeof(T5) }; + var queryable = InstanceFactory.GetQueryable(this.CurrentConnectionConfig); + this.CreateEasyQueryJoin(joinExpression, types, queryable); + queryable.Where(joinExpression); + return queryable; + } + public virtual ISugarQueryable Queryable(Expression> joinExpression) where T : class, new() + { + InitMappingInfo(); + var types = new Type[] { typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6) }; + var queryable = InstanceFactory.GetQueryable(this.CurrentConnectionConfig); + this.CreateEasyQueryJoin(joinExpression, types, queryable); + queryable.Where(joinExpression); + return queryable; + } + public virtual ISugarQueryable Queryable(Expression> joinExpression) where T : class, new() + { + InitMappingInfo(); + var types = new Type[] { typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7) }; + var queryable = InstanceFactory.GetQueryable(this.CurrentConnectionConfig); + this.CreateEasyQueryJoin(joinExpression, types, queryable); + queryable.Where(joinExpression); + return queryable; + } + public virtual ISugarQueryable Queryable(Expression> joinExpression) where T : class, new() + { + InitMappingInfo(); + var types = new Type[] { typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8) }; + var queryable = InstanceFactory.GetQueryable(this.CurrentConnectionConfig); + this.CreateEasyQueryJoin(joinExpression, types, queryable); + queryable.Where(joinExpression); + return queryable; + } + + #region 9-12 + public virtual ISugarQueryable Queryable(Expression> joinExpression) where T : class, new() + { + InitMappingInfo(); + var types = new Type[] { typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9) }; + var queryable = InstanceFactory.GetQueryable(this.CurrentConnectionConfig); + this.CreateEasyQueryJoin(joinExpression, types, queryable); + queryable.Where(joinExpression); + return queryable; + } + public virtual ISugarQueryable Queryable(Expression> joinExpression) where T : class, new() + { + InitMappingInfo(); + var types = new Type[] { typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9), typeof(T10) }; + var queryable = InstanceFactory.GetQueryable(this.CurrentConnectionConfig); + this.CreateEasyQueryJoin(joinExpression, types, queryable); + queryable.Where(joinExpression); + return queryable; + } + public virtual ISugarQueryable Queryable(Expression> joinExpression) where T : class, new() + { + InitMappingInfo(); + var types = new Type[] { typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9), typeof(T10), typeof(T11) }; + var queryable = InstanceFactory.GetQueryable(this.CurrentConnectionConfig); + this.CreateEasyQueryJoin(joinExpression, types, queryable); + queryable.Where(joinExpression); + return queryable; + } + public virtual ISugarQueryable Queryable(Expression> joinExpression) where T : class, new() + { + InitMappingInfo(); + var types = new Type[] { typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9), typeof(T10), typeof(T11), typeof(T12) }; + var queryable = InstanceFactory.GetQueryable(this.CurrentConnectionConfig); + this.CreateEasyQueryJoin(joinExpression, types, queryable); + queryable.Where(joinExpression); + return queryable; + } + public virtual ISugarQueryable Queryable(ISugarQueryable queryable) + { + var sqlobj = queryable.ToSql(); + var QueryBuilder = queryable.QueryBuilder; + var newQueryable = this.SqlQueryable(sqlobj.Key).AddParameters(sqlobj.Value); + var result = newQueryable.Select(newQueryable.QueryBuilder.SelectValue + ""); + result.QueryBuilder.IsSqlQuery = false; + result.QueryBuilder.NoCheckInclude = true; + result.QueryBuilder.WhereIndex = (QueryBuilder.WhereIndex + 1); + var appendIndex = result.QueryBuilder.Parameters == null ? 1 : result.QueryBuilder.Parameters.Count + 1; + result.QueryBuilder.LambdaExpressions.ParameterIndex = (QueryBuilder.LambdaExpressions.ParameterIndex + appendIndex); + result.QueryBuilder.Includes = queryable.QueryBuilder.Includes?.ToList(); + return result; + } + public virtual ISugarQueryable Queryable(ISugarQueryable queryable, string shortName) + { + var result = Queryable(queryable); + var key = result.QueryBuilder.AsTables.First().Key; + var value = result.QueryBuilder.AsTables.First().Value; + result.QueryBuilder.AsTables.Remove(key); + result.QueryBuilder.AsTables.Add(key, value.TrimEnd(' ').TrimEnd('t') + shortName); + return result; + } + + public virtual ISugarQueryable Queryable( + ISugarQueryable joinQueryable1, ISugarQueryable joinQueryable2, Expression> joinExpression) where T : class, new() where T2 : class, new() + { + return Queryable(joinQueryable1, joinQueryable2, JoinType.Inner, joinExpression); + } + public virtual ISugarQueryable Queryable( + ISugarQueryable joinQueryable1, ISugarQueryable joinQueryable2, JoinType joinType, Expression> joinExpression) where T : class, new() where T2 : class, new() + { + Check.Exception(joinQueryable1.QueryBuilder.Take != null || joinQueryable1.QueryBuilder.Skip != null || joinQueryable1.QueryBuilder.OrderByValue.HasValue(), "joinQueryable1 Cannot have 'Skip' 'ToPageList' 'Take' Or 'OrderBy'"); + Check.Exception(joinQueryable2.QueryBuilder.Take != null || joinQueryable2.QueryBuilder.Skip != null || joinQueryable2.QueryBuilder.OrderByValue.HasValue(), "joinQueryable2 Cannot have 'Skip' 'ToPageList' 'Take' Or 'OrderBy'"); + + var sqlBuilder = InstanceFactory.GetSqlbuilder(this.Context.CurrentConnectionConfig); + + sqlBuilder.Context = this; + InitMappingInfo(); + var types = new Type[] { typeof(T2) }; + var queryable = InstanceFactory.GetQueryable(this.CurrentConnectionConfig); + queryable.Context = this.Context; + queryable.SqlBuilder = sqlBuilder; + queryable.QueryBuilder = InstanceFactory.GetQueryBuilder(this.CurrentConnectionConfig); + queryable.QueryBuilder.JoinQueryInfos = new List(); + queryable.QueryBuilder.Builder = sqlBuilder; + queryable.QueryBuilder.Context = this; + queryable.QueryBuilder.EntityType = typeof(T); + queryable.QueryBuilder.LambdaExpressions = InstanceFactory.GetLambdaExpressions(this.CurrentConnectionConfig); + + //master + var shortName1 = joinExpression.Parameters[0].Name; + var sqlObj1 = joinQueryable1.ToSql(); + string sql1 = sqlObj1.Key; + UtilMethods.RepairReplicationParameters(ref sql1, sqlObj1.Value.ToArray(), 0, "Join"); + queryable.QueryBuilder.EntityName = sqlBuilder.GetPackTable(sql1, sqlBuilder.GetTranslationColumnName(shortName1)); ; + queryable.QueryBuilder.Parameters.AddRange(sqlObj1.Value); + + //join table 1 + var shortName2 = joinExpression.Parameters[1].Name; + var sqlObj2 = joinQueryable2.ToSql(); + string sql2 = sqlObj2.Key; + UtilMethods.RepairReplicationParameters(ref sql2, sqlObj2.Value.ToArray(), 1, "Join"); + queryable.QueryBuilder.Parameters.AddRange(sqlObj2.Value); + var exp = queryable.QueryBuilder.GetExpressionValue(joinExpression, ResolveExpressType.WhereMultiple); + queryable.QueryBuilder.JoinQueryInfos.Add(new JoinQueryInfo() { JoinIndex = 0, JoinType = joinType, JoinWhere = exp.GetResultString(), TableName = sqlBuilder.GetPackTable(sql2, sqlBuilder.GetTranslationColumnName(shortName2)) }); + + return queryable; + } + + + public virtual ISugarQueryable Queryable( + ISugarQueryable joinQueryable1, ISugarQueryable joinQueryable2, ISugarQueryable joinQueryable3, + JoinType joinType1, Expression> joinExpression1, + JoinType joinType2, Expression> joinExpression2 + ) where T : class, new() where T2 : class, new() where T3 : class, new() + { + Check.Exception(joinQueryable1.QueryBuilder.Take != null || joinQueryable1.QueryBuilder.Skip != null || joinQueryable1.QueryBuilder.OrderByValue.HasValue(), "joinQueryable1 Cannot have 'Skip' 'ToPageList' 'Take' Or 'OrderBy'"); + Check.Exception(joinQueryable2.QueryBuilder.Take != null || joinQueryable2.QueryBuilder.Skip != null || joinQueryable2.QueryBuilder.OrderByValue.HasValue(), "joinQueryable2 Cannot have 'Skip' 'ToPageList' 'Take' Or 'OrderBy'"); + Check.Exception(joinQueryable3.QueryBuilder.Take != null || joinQueryable3.QueryBuilder.Skip != null || joinQueryable3.QueryBuilder.OrderByValue.HasValue(), "joinQueryable3 Cannot have 'Skip' 'ToPageList' 'Take' Or 'OrderBy'"); + + var sqlBuilder = InstanceFactory.GetSqlbuilder(this.Context.CurrentConnectionConfig); + + sqlBuilder.Context = this; + InitMappingInfo(); + var types = new Type[] { typeof(T2) }; + var queryable = InstanceFactory.GetQueryable(this.CurrentConnectionConfig); + queryable.Context = this.Context; + queryable.SqlBuilder = sqlBuilder; + queryable.QueryBuilder = InstanceFactory.GetQueryBuilder(this.CurrentConnectionConfig); + queryable.QueryBuilder.JoinQueryInfos = new List(); + queryable.QueryBuilder.Builder = sqlBuilder; + queryable.QueryBuilder.Context = this; + queryable.QueryBuilder.EntityType = typeof(T); + queryable.QueryBuilder.LambdaExpressions = InstanceFactory.GetLambdaExpressions(this.CurrentConnectionConfig); + + //master + var shortName1 = joinExpression1.Parameters[0].Name; + var sqlObj1 = joinQueryable1.ToSql(); + string sql1 = sqlObj1.Key; + UtilMethods.RepairReplicationParameters(ref sql1, sqlObj1.Value.ToArray(), 0, "Join"); + queryable.QueryBuilder.EntityName = sqlBuilder.GetPackTable(sql1, shortName1); ; + queryable.QueryBuilder.Parameters.AddRange(sqlObj1.Value); + + //join table 1 + var shortName2 = joinExpression1.Parameters[1].Name; + var sqlObj2 = joinQueryable2.ToSql(); + string sql2 = sqlObj2.Key; + UtilMethods.RepairReplicationParameters(ref sql2, sqlObj2.Value.ToArray(), 1, "Join"); + queryable.QueryBuilder.Parameters.AddRange(sqlObj2.Value); + var exp = queryable.QueryBuilder.GetExpressionValue(joinExpression1, ResolveExpressType.WhereMultiple); + queryable.QueryBuilder.JoinQueryInfos.Add(new JoinQueryInfo() { JoinIndex = 0, JoinType = joinType1, JoinWhere = exp.GetResultString(), TableName = sqlBuilder.GetPackTable(sql2, shortName2) }); + + + //join table 2 + var shortName3 = joinExpression1.Parameters[2].Name; + var sqlObj3 = joinQueryable3.ToSql(); + string sql3 = sqlObj3.Key; + UtilMethods.RepairReplicationParameters(ref sql3, sqlObj3.Value.ToArray(), 2, "Join"); + queryable.QueryBuilder.Parameters.AddRange(sqlObj3.Value); + var exp2 = queryable.QueryBuilder.GetExpressionValue(joinExpression2, ResolveExpressType.WhereMultiple); + queryable.QueryBuilder.JoinQueryInfos.Add(new JoinQueryInfo() { JoinIndex = 1, JoinType = joinType2, JoinWhere = exp2.GetResultString(), TableName = sqlBuilder.GetPackTable(sql3, shortName3) }); + return queryable; + } + + public virtual ISugarQueryable Queryable( + ISugarQueryable joinQueryable1, ISugarQueryable joinQueryable2, ISugarQueryable joinQueryable3, ISugarQueryable joinQueryable4, + JoinType joinType1, Expression> joinExpression1, + JoinType joinType2, Expression> joinExpression2, + JoinType joinType3, Expression> joinExpression3 + ) where T : class, new() where T2 : class, new() where T3 : class, new() where T4 : class, new() + { + Check.Exception(joinQueryable1.QueryBuilder.Take != null || joinQueryable1.QueryBuilder.Skip != null || joinQueryable1.QueryBuilder.OrderByValue.HasValue(), "joinQueryable1 Cannot have 'Skip' 'ToPageList' 'Take' Or 'OrderBy'"); + Check.Exception(joinQueryable2.QueryBuilder.Take != null || joinQueryable2.QueryBuilder.Skip != null || joinQueryable2.QueryBuilder.OrderByValue.HasValue(), "joinQueryable2 Cannot have 'Skip' 'ToPageList' 'Take' Or 'OrderBy'"); + Check.Exception(joinQueryable3.QueryBuilder.Take != null || joinQueryable3.QueryBuilder.Skip != null || joinQueryable3.QueryBuilder.OrderByValue.HasValue(), "joinQueryable3 Cannot have 'Skip' 'ToPageList' 'Take' Or 'OrderBy'"); + Check.Exception(joinQueryable4.QueryBuilder.Take != null || joinQueryable4.QueryBuilder.Skip != null || joinQueryable4.QueryBuilder.OrderByValue.HasValue(), "joinQueryable4 Cannot have 'Skip' 'ToPageList' 'Take' Or 'OrderBy'"); + var sqlBuilder = InstanceFactory.GetSqlbuilder(this.Context.CurrentConnectionConfig); + + sqlBuilder.Context = this; + InitMappingInfo(); + var types = new Type[] { typeof(T2) }; + var queryable = InstanceFactory.GetQueryable(this.CurrentConnectionConfig); + queryable.Context = this.Context; + queryable.SqlBuilder = sqlBuilder; + queryable.QueryBuilder = InstanceFactory.GetQueryBuilder(this.CurrentConnectionConfig); + queryable.QueryBuilder.JoinQueryInfos = new List(); + queryable.QueryBuilder.Builder = sqlBuilder; + queryable.QueryBuilder.Context = this; + queryable.QueryBuilder.EntityType = typeof(T); + queryable.QueryBuilder.LambdaExpressions = InstanceFactory.GetLambdaExpressions(this.CurrentConnectionConfig); + + //master + var shortName1 = joinExpression1.Parameters[0].Name; + var sqlObj1 = joinQueryable1.ToSql(); + string sql1 = sqlObj1.Key; + UtilMethods.RepairReplicationParameters(ref sql1, sqlObj1.Value.ToArray(), 0, "Join"); + queryable.QueryBuilder.EntityName = sqlBuilder.GetPackTable(sql1, shortName1); ; + queryable.QueryBuilder.Parameters.AddRange(sqlObj1.Value); + + //join table 1 + var shortName2 = joinExpression1.Parameters[1].Name; + var sqlObj2 = joinQueryable2.ToSql(); + string sql2 = sqlObj2.Key; + UtilMethods.RepairReplicationParameters(ref sql2, sqlObj2.Value.ToArray(), 1, "Join"); + queryable.QueryBuilder.Parameters.AddRange(sqlObj2.Value); + var exp = queryable.QueryBuilder.GetExpressionValue(joinExpression1, ResolveExpressType.WhereMultiple); + queryable.QueryBuilder.JoinQueryInfos.Add(new JoinQueryInfo() { JoinIndex = 0, JoinType = joinType1, JoinWhere = exp.GetResultString(), TableName = sqlBuilder.GetPackTable(sql2, shortName2) }); + + + //join table 2 + var shortName3 = joinExpression1.Parameters[2].Name; + var sqlObj3 = joinQueryable3.ToSql(); + string sql3 = sqlObj3.Key; + UtilMethods.RepairReplicationParameters(ref sql3, sqlObj3.Value.ToArray(), 2, "Join"); + queryable.QueryBuilder.Parameters.AddRange(sqlObj3.Value); + var exp2 = queryable.QueryBuilder.GetExpressionValue(joinExpression2, ResolveExpressType.WhereMultiple); + queryable.QueryBuilder.JoinQueryInfos.Add(new JoinQueryInfo() { JoinIndex = 1, JoinType = joinType2, JoinWhere = exp2.GetResultString(), TableName = sqlBuilder.GetPackTable(sql3, shortName3) }); + + //join table 3 + var shortName4 = joinExpression1.Parameters[3].Name; + var sqlObj4 = joinQueryable4.ToSql(); + string sql4 = sqlObj4.Key; + UtilMethods.RepairReplicationParameters(ref sql4, sqlObj4.Value.ToArray(), 3, "Join"); + queryable.QueryBuilder.Parameters.AddRange(sqlObj4.Value); + var exp3 = queryable.QueryBuilder.GetExpressionValue(joinExpression3, ResolveExpressType.WhereMultiple); + queryable.QueryBuilder.JoinQueryInfos.Add(new JoinQueryInfo() { JoinIndex = 1, JoinType = joinType3, JoinWhere = exp3.GetResultString(), TableName = sqlBuilder.GetPackTable(sql4, shortName4) }); + + return queryable; + } + #endregion + + public virtual ISugarQueryable UnionAll(params ISugarQueryable[] queryables) where T : class + { + return _UnionAll(queryables); + } + + internal ISugarQueryable _UnionAll(ISugarQueryable[] queryables) + { + var sqlBuilder = InstanceFactory.GetSqlbuilder(this.Context.CurrentConnectionConfig); + Check.Exception(queryables.IsNullOrEmpty(), "UnionAll.queryables is null "); + int i = 1; + List>> allItems = new List>>(); + foreach (var item in queryables) + { + var sqlObj = item.ToSql(); + string sql = sqlObj.Key; + if (this.CurrentConnectionConfig?.MoreSettings?.MaxParameterNameLength > 0) + { + UtilMethods.RepairReplicationParameters(this.Context, ref sql, sqlObj.Value.ToArray(), i, "UnionAll"); + } + else + { + UtilMethods.RepairReplicationParameters(ref sql, sqlObj.Value.ToArray(), i, "UnionAll"); + } + if (sqlObj.Value.HasValue()) + allItems.Add(new KeyValuePair>(sqlBuilder.GetUnionFomatSql(sql), sqlObj.Value)); + else + allItems.Add(new KeyValuePair>(sqlBuilder.GetUnionFomatSql(sql), new List())); + i++; + } + var allSql = sqlBuilder.GetUnionAllSql(allItems.Select(it => it.Key).ToList()); + var allParameters = allItems.SelectMany(it => it.Value).ToArray(); + var resulut = this.Context.Queryable().AS(UtilMethods.GetPackTable(allSql, "unionTable")).With(SqlWith.Null); + resulut.AddParameters(allParameters); + if (this.Context.CurrentConnectionConfig.DbType == DbType.Oracle && sqlBuilder.SqlSelectAll == "*") + { + return resulut.Select("unionTable.*"); + } + else if (this.Context.CurrentConnectionConfig?.MoreSettings?.IsWithNoLockQuery == true) + { + return resulut.Select(sqlBuilder.SqlSelectAll).With(SqlWith.Null); + } + else + { + return resulut.Select(sqlBuilder.SqlSelectAll); + } + } + + public virtual ISugarQueryable UnionAll(List> queryables) where T : class + { + Check.Exception(queryables.IsNullOrEmpty(), "UnionAll.queryables is null "); + return UnionAll(queryables.ToArray()); + } + public virtual ISugarQueryable Union(params ISugarQueryable[] queryables) where T : class + { + var sqlBuilder = InstanceFactory.GetSqlbuilder(this.Context.CurrentConnectionConfig); + Check.Exception(queryables.IsNullOrEmpty(), "UnionAll.queryables is null "); + int i = 1; + List>> allItems = new List>>(); + foreach (var item in queryables) + { + item.QueryBuilder.DisableTop = true; + var sqlObj = item.ToSql(); + string sql = sqlObj.Key; + if (this.CurrentConnectionConfig?.MoreSettings?.MaxParameterNameLength > 0) + { + UtilMethods.RepairReplicationParameters(this.Context, ref sql, sqlObj.Value.ToArray(), i, "Union"); + } + else + { + UtilMethods.RepairReplicationParameters(ref sql, sqlObj.Value.ToArray(), i, "Union"); + } + if (sqlObj.Value.HasValue()) + allItems.Add(new KeyValuePair>(sqlBuilder.GetUnionFomatSql(sql), sqlObj.Value)); + else + allItems.Add(new KeyValuePair>(sqlBuilder.GetUnionFomatSql(sql), new List())); + i++; + } + var allSql = sqlBuilder.GetUnionSql(allItems.Select(it => it.Key).ToList()); + var allParameters = allItems.SelectMany(it => it.Value).ToArray(); + var resulut = this.Context.Queryable().AS(UtilMethods.GetPackTable(allSql, "unionTable")).With(SqlWith.Null); + resulut.AddParameters(allParameters); + if (this.Context.CurrentConnectionConfig.DbType == DbType.Oracle && sqlBuilder.SqlSelectAll == "*") + { + return resulut.Select("unionTable.*"); + } + else + { + return resulut.Select(sqlBuilder.SqlSelectAll); + } + } + public virtual ISugarQueryable Union(List> queryables) where T : class + { + Check.Exception(queryables.IsNullOrEmpty(), "Union.queryables is null "); + return Union(queryables.ToArray()); + } + #endregion + + #region SqlQueryable + public ISugarQueryable SqlQueryable(string sql) where T : class, new() + { + var sqlBuilder = InstanceFactory.GetSqlbuilder(this.Context.CurrentConnectionConfig); + var result = this.Context.Queryable().AS(sqlBuilder.GetPackTable(sql, sqlBuilder.GetDefaultShortName())).With(SqlWith.Null).Select(sqlBuilder.GetDefaultShortName() + ".*"); + result.QueryBuilder.IsSqlQuery = true; + result.QueryBuilder.OldSql = sql; + result.QueryBuilder.NoCheckInclude = true; + return result; + } + #endregion + + #region Insertable + public IInsertable> InsertableByDynamic(object insertDynamicObject) + { + return this.Insertable>(insertDynamicObject); + } + public InsertMethodInfo InsertableByObject(object singleEntityObjectOrListObject) + { + if (singleEntityObjectOrListObject == null) + return new InsertMethodInfo(); + if (singleEntityObjectOrListObject.GetType().FullName.IsCollectionsList()) + { + var list = ((IList)singleEntityObjectOrListObject); + if (list == null || list.Count == 0) + return new InsertMethodInfo(); + var type = list[0].GetType(); + var newList = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(type)); + foreach (var item in list) + { + newList.Add(item); + } + var methods = this.Context.GetType().GetMethods() + .Where(it => it.Name == "Insertable") + .Where(it => it.GetGenericArguments().Length != 0) + .Where(it => it.GetParameters().Any(z => z.ParameterType.Name.StartsWith("List"))) + .Where(it => it.Name == "Insertable").ToList(); + var method = methods.Single().MakeGenericMethod(newList.GetType().GetGenericArguments().First()); + InsertMethodInfo result = new InsertMethodInfo() + { + Context = this.Context, + MethodInfo = method, + objectValue = newList + }; + return result; + } + else + { + var methods = this.Context.GetType().GetMethods() + .Where(it => it.Name == "Insertable") + .Where(it => it.GetGenericArguments().Length != 0) + .Where(it => it.GetParameters().Any(z => z.ParameterType.Name == "T")) + .Where(it => it.Name == "Insertable").ToList(); + var method = methods.Single().MakeGenericMethod(singleEntityObjectOrListObject.GetType()); + InsertMethodInfo result = new InsertMethodInfo() + { + Context = this.Context, + MethodInfo = method, + objectValue = singleEntityObjectOrListObject + }; + return result; + } + } + public virtual IInsertable Insertable(T[] insertObjs) where T : class, new() + { + UtilMethods.CheckArray(insertObjs); + InitMappingInfo(); + InsertableProvider result = this.CreateInsertable(insertObjs); + return result; + } + public virtual IInsertable Insertable(List insertObjs) where T : class, new() + { + if (insertObjs?.IsNullOrEmpty() != false) + { + insertObjs = new List(); + insertObjs.Add(default(T)); + } + return this.Context.Insertable(insertObjs.ToArray()); + } + public virtual IInsertable Insertable(T insertObj) where T : class, new() + { + return this.Context.Insertable(new T[] { insertObj }); + } + public virtual IInsertable Insertable(Dictionary columnDictionary) where T : class, new() + { + InitMappingInfo(); + Check.Exception(columnDictionary == null || columnDictionary.Count == 0, "Insertable.columnDictionary can't be null"); + var insertObject = this.Context.Utilities.DeserializeObject(this.Context.Utilities.SerializeObject(columnDictionary)); + var columns = columnDictionary.Select(it => it.Key).ToList(); + return this.Context.Insertable(insertObject).InsertColumns(columns.ToArray()); ; + } + public virtual IInsertable Insertable(dynamic insertDynamicObject) where T : class, new() + { + InitMappingInfo(); + if (insertDynamicObject is T) + { + return this.Context.Insertable((T)insertDynamicObject); + } + else + { + var columns = ((object)insertDynamicObject).GetType().GetProperties().Select(it => it.Name).ToList(); + Check.Exception(columns.IsNullOrEmpty(), "Insertable.updateDynamicObject can't be null"); + T insertObject = this.Context.Utilities.DeserializeObject(this.Context.Utilities.SerializeObject(insertDynamicObject)); + return this.Context.Insertable(insertObject).InsertColumns(columns.ToArray()); + } + } + #endregion + + #region Deleteable + public DeleteMethodInfo DeleteableByObject(object singleEntityObjectOrListObject) + { + if (singleEntityObjectOrListObject == null) + return new DeleteMethodInfo(); + if (singleEntityObjectOrListObject.GetType().FullName.IsCollectionsList()) + { + var list = ((IList)singleEntityObjectOrListObject); + if (list == null || list.Count == 0) + return new DeleteMethodInfo(); + var type = list[0].GetType(); + var newList = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(type)); + foreach (var item in list) + { + newList.Add(item); + } + var methods = this.Context.GetType().GetMethods() + .Where(it => it.Name == "Deleteable") + .Where(it => it.GetGenericArguments().Length != 0) + .Where(it => it.GetParameters().Any(z => z.Name != "pkValue" && z.ParameterType.Name.StartsWith("List"))) + .Where(it => it.Name == "Deleteable").ToList(); + var method = methods.FirstOrDefault().MakeGenericMethod(newList.GetType().GetGenericArguments().FirstOrDefault()); + DeleteMethodInfo result = new DeleteMethodInfo() + { + Context = this.Context, + MethodInfo = method, + objectValue = newList + }; + return result; + } + else + { + var methods = this.Context.GetType().GetMethods() + .Where(it => it.Name == "Deleteable") + .Where(it => it.GetGenericArguments().Length != 0) + .Where(it => it.GetParameters().Any(z => z.ParameterType.Name == "T")) + .Where(it => it.Name == "Deleteable").ToList(); + var method = methods.Single().MakeGenericMethod(singleEntityObjectOrListObject.GetType()); + DeleteMethodInfo result = new DeleteMethodInfo() + { + Context = this.Context, + MethodInfo = method, + objectValue = singleEntityObjectOrListObject + }; + return result; + } + } + public virtual IDeleteable Deleteable() where T : class, new() + { + InitMappingInfo(); + DeleteableProvider result = this.CreateDeleteable(); + if (this.Context.CurrentConnectionConfig?.MoreSettings?.IsAutoDeleteQueryFilter == true) + { + return result.EnableQueryFilter(); + } + return result; + } + public virtual IDeleteable Deleteable(Expression> expression) where T : class, new() + { + InitMappingInfo(); + return this.Context.Deleteable().Where(expression); + } + public virtual IDeleteable Deleteable(dynamic primaryKeyValue) where T : class, new() + { + InitMappingInfo(); + return this.Context.Deleteable().In(primaryKeyValue); + } + public virtual IDeleteable Deleteable(dynamic[] primaryKeyValues) where T : class, new() + { + InitMappingInfo(); + return this.Context.Deleteable().In(primaryKeyValues); + } + public virtual IDeleteable Deleteable(List pkValue) where T : class, new() + { + InitMappingInfo(); + return this.Context.Deleteable().In(pkValue); + } + public virtual IDeleteable Deleteable(T deleteObj) where T : class, new() + { + InitMappingInfo(); + return this.Context.Deleteable().Where(deleteObj); + } + public virtual IDeleteable Deleteable(List deleteObjs) where T : class, new() + { + InitMappingInfo(); + return this.Context.Deleteable().Where(deleteObjs); + } + #endregion + + #region Updateable + public UpdateMethodInfo UpdateableByObject(object singleEntityObjectOrListObject) + { + if (singleEntityObjectOrListObject == null) + return new UpdateMethodInfo(); + if (singleEntityObjectOrListObject.GetType().FullName.IsCollectionsList()) + { + var list = ((IList)singleEntityObjectOrListObject); + if (list == null || list.Count == 0) + return new UpdateMethodInfo(); + var type = list[0].GetType(); + var newList = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(type)); + foreach (var item in list) + { + newList.Add(item); + } + var methods = this.Context.GetType().GetMethods() + .Where(it => it.Name == "Updateable") + .Where(it => it.GetGenericArguments().Length != 0) + .Where(it => it.GetParameters().Any(z => z.ParameterType.Name.StartsWith("List"))) + .Where(it => it.Name == "Updateable").ToList(); + var method = methods.Single().MakeGenericMethod(newList.GetType().GetGenericArguments().First()); + UpdateMethodInfo result = new UpdateMethodInfo() + { + Context = this.Context, + MethodInfo = method, + objectValue = newList + }; + return result; + } + else + { + var methods = this.Context.GetType().GetMethods() + .Where(it => it.Name == "Updateable") + .Where(it => it.GetGenericArguments().Length != 0) + .Where(it => it.GetParameters().Any(z => z.ParameterType.Name == "T")) + .Where(it => it.Name == "Updateable").ToList(); + var method = methods.Single().MakeGenericMethod(singleEntityObjectOrListObject.GetType()); + UpdateMethodInfo result = new UpdateMethodInfo() + { + Context = this.Context, + MethodInfo = method, + objectValue = singleEntityObjectOrListObject + }; + return result; + } + } + public UpdateExpressionMethodInfo UpdateableByObject(Type entityType) + { + UpdateExpressionMethodInfo reslut = new UpdateExpressionMethodInfo(); + var methods = this.Context.GetType().GetMethods() + .Where(it => it.Name == "Updateable") + .Where(it => it.GetGenericArguments().Length != 0) + .Where(it => it.GetParameters().Length == 0) + .Where(it => it.Name == "Updateable").ToList(); + var method = methods.Single().MakeGenericMethod(entityType); + reslut.Context = this.Context; + reslut.MethodInfo = method; + reslut.Type = entityType; + reslut.objectValue = method.Invoke(Context, Array.Empty()); + return reslut; + } + public virtual IUpdateable Updateable(T[] UpdateObjs) where T : class, new() + { + InitMappingInfo(); + Check.ExceptionEasy(UpdateObjs is IList && typeof(T).FullName.IsCollectionsList(), "The methods you encapsulate are loaded incorrectly, so List should be Updateable(List UpdateObjs)where T: class, new()", "你封装的方法进错重载,List应该进Updateable(List UpdateObjs)where T : class, new()重载"); + UpdateableProvider result = this.CreateUpdateable(UpdateObjs); + return result; + } + public virtual IUpdateable Updateable(List UpdateObjs) where T : class, new() + { + //Check.ArgumentNullException(UpdateObjs, "Updateable.UpdateObjs can't be null"); + if (UpdateObjs == null) + { + UpdateObjs = new List(); + } + var result = (UpdateableProvider)Updateable(UpdateObjs.ToArray()); + result.UpdateBuilder.IsListUpdate = true; + return result; + } + public virtual IUpdateable Updateable(T UpdateObj) where T : class, new() + { + + return this.Context.Updateable(new T[] { UpdateObj }); + } + public virtual IUpdateable Updateable() where T : class, new() + { + var result = this.Context.Updateable(new T[] { new T() }); + result.UpdateParameterIsNull = true; + if (this.Context.CurrentConnectionConfig?.MoreSettings?.IsAutoUpdateQueryFilter == true) + { + return result.EnableQueryFilter(); + } + return result; + } + public virtual IUpdateable Updateable(Expression> columns) where T : class, new() + { + var result = this.Context.Updateable().SetColumns(columns); + result.UpdateParameterIsNull = true; + return result; + } + public virtual IUpdateable Updateable(Expression> columns) where T : class, new() + { + var result = this.Context.Updateable().SetColumns(columns); + result.UpdateParameterIsNull = true; + return result; + } + + public IUpdateable> UpdateableByDynamic(object updateDynamicObject) + { + return this.Updateable>(updateDynamicObject); + } + + public virtual IUpdateable Updateable(Dictionary columnDictionary) where T : class, new() + { + InitMappingInfo(); + Check.Exception(columnDictionary == null || columnDictionary.Count == 0, "Updateable.columnDictionary can't be null"); + var updateObject = this.Context.Utilities.DeserializeObject(this.Context.Utilities.SerializeObject(columnDictionary)); + var columns = columnDictionary.Select(it => it.Key).ToList(); + return this.Context.Updateable(updateObject).UpdateColumns(columns.ToArray()); ; + } + public virtual IUpdateable Updateable(dynamic updateDynamicObject) where T : class, new() + { + InitMappingInfo(); + if (updateDynamicObject is T) + { + return this.Context.Updateable((T)updateDynamicObject); + } + else + { + var columns = ((object)updateDynamicObject).GetType().GetProperties().Select(it => it.Name).ToList(); + Check.Exception(columns.IsNullOrEmpty(), "Updateable.updateDynamicObject can't be null"); + T updateObject = this.Context.Utilities.DeserializeObject(this.Context.Utilities.SerializeObject(updateDynamicObject)); + return this.Context.Updateable(updateObject).UpdateColumns(columns.ToArray()); ; + } + } + #endregion + + #region Saveable + public GridSaveProvider GridSave(List saveList) where T : class, new() + { + Check.ExceptionEasy(saveList == null, "saveList is null", "saveList 不能是 null"); + var isTran = this.Context.TempItems?.Any(it => it.Key == "OldData_" + saveList.GetHashCode()) == true; + Check.ExceptionEasy(isTran == false, "saveList no tracking", "saveList 没有使用跟踪"); + var oldList = (List)this.Context.TempItems.FirstOrDefault(it => it.Key == "OldData_" + saveList.GetHashCode()).Value; + return GridSave(oldList, saveList); + } + public GridSaveProvider GridSave(List oldList, List saveList) where T : class, new() + { + GridSaveProvider result = new GridSaveProvider(); + result.Context = this; + result.OldList = oldList; + result.SaveList = saveList; + return result; + } + public IStorageable Storageable(T[] dataList) where T : class, new() + { + return Storageable(dataList?.ToList()); + } + public ISaveable Saveable(List saveObjects) where T : class, new() + { + return new SaveableProvider(this, saveObjects); + } + public ISaveable Saveable(T saveObject) where T : class, new() + { + return new SaveableProvider(this, saveObject); + } + public StorageableDataTable Storageable(List> dictionaryList, string tableName) + { + DataTable dt = this.Context.Utilities.DictionaryListToDataTable(dictionaryList); + dt.TableName = tableName; + return this.Context.Storageable(dt); + } + public StorageableDataTable Storageable(Dictionary dictionary, string tableName) + { + DataTable dt = this.Context.Utilities.DictionaryListToDataTable(new List>() { dictionary }); + dt.TableName = tableName; + return this.Context.Storageable(dt); + } + public IStorageable Storageable(List dataList) where T : class, new() + { + dataList = dataList.Where(it => it != null).ToList(); + this.InitMappingInfo(); + var sqlBuilder = InstanceFactory.GetSqlbuilder(this.Context.CurrentConnectionConfig); + var result = new Storageable(dataList, this); + result.Builder = sqlBuilder; + return result; + } + public IStorageable Storageable(IList dataList) where T : class, new() + { + return Storageable(dataList?.ToList()); + } + public IStorageable Storageable(T data) where T : class, new() + { + return Storageable(new List() { data }); + } + public StorageableDataTable Storageable(DataTable data) + { + var result = new StorageableDataTable(); + Check.Exception(data.TableName.IsNullOrEmpty() || data.TableName == "Table", ErrorMessage.GetThrowMessage("DataTable data.TableName is null", "参数DataTable没有设置TableName ,参数.TableName=表名")); + result.DataTable = data; + result.Context = this; + data.Columns.Add(new DataColumn("SugarGroupId", typeof(StorageType))); + data.Columns.Add(new DataColumn("SugarUpdateRows", typeof(List))); + data.Columns.Add(new DataColumn("SugarErrorMessage", typeof(string))); + data.Columns.Add(new DataColumn("SugarColumns", typeof(string[]))); + return result; + } + public StorageableMethodInfo StorageableByObject(object singleEntityObjectOrList) + { + if (singleEntityObjectOrList == null) + return new StorageableMethodInfo(); + if (singleEntityObjectOrList.GetType().FullName.IsCollectionsList()) + { + var list = ((IList)singleEntityObjectOrList); + if (list == null || list.Count == 0) + return new StorageableMethodInfo(); + var type = list[0].GetType(); + var newList = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(type)); + foreach (var item in list) + { + newList.Add(item); + } + var methods = this.Context.GetType().GetMethods() + .Where(it => it.Name == "Storageable") + .Where(it => it.GetGenericArguments().Length != 0) + .Where(it => it.GetParameters().Any(z => z.ParameterType.Name.StartsWith("List"))) + .Where(it => it.Name == "Storageable").ToList(); + var method = methods.Single().MakeGenericMethod(newList.GetType().GetGenericArguments().First()); + StorageableMethodInfo result = new StorageableMethodInfo() + { + Context = this.Context, + MethodInfo = method, + objectValue = newList + }; + return result; + } + else + { + var methods = this.Context.GetType().GetMethods() + .Where(it => it.Name == "Storageable") + .Where(it => it.GetGenericArguments().Length != 0) + .Where(it => it.GetParameters().Any(z => z.ParameterType.Name == "T")) + .Where(it => it.Name == "Storageable").ToList(); + var method = methods.Single().MakeGenericMethod(singleEntityObjectOrList.GetType()); + StorageableMethodInfo result = new StorageableMethodInfo() + { + Context = this.Context, + MethodInfo = method, + objectValue = singleEntityObjectOrList + }; + return result; + } + } + #endregion + + #region Reportable + public IReportable Reportable(T data) + { + var result = new ReportableProvider(data); + result.formatBuilder = InstanceFactory.GetInsertBuilder(this.Context.CurrentConnectionConfig); + result.Context = this; + result.formatBuilder.Context = this; + result.queryBuilder = this.Queryable().QueryBuilder; + return result; + } + public IReportable Reportable(List list) + { + var result = new ReportableProvider(list); + result.formatBuilder = InstanceFactory.GetInsertBuilder(this.Context.CurrentConnectionConfig); + result.Context = this; + result.formatBuilder.Context = this; + result.queryBuilder = this.Queryable().QueryBuilder; + return result; + } + public IReportable Reportable(T[] list) + { + if (list == null) + list = Array.Empty(); + var result = new ReportableProvider(list.ToList()); + result.formatBuilder = InstanceFactory.GetInsertBuilder(this.Context.CurrentConnectionConfig); + result.Context = this; + result.formatBuilder.Context = this; + result.queryBuilder = this.Queryable().QueryBuilder; + return result; + } + #endregion + + #region Nav CUD + public InsertNavTaskInit InsertNav(T data) where T : class, new() + { + return InsertNav(new List() { data }); + } + public InsertNavTaskInit InsertNav(List datas) where T : class, new() + { + var result = new InsertNavTaskInit(); + var provider = new InsertNavProvider(); + provider._Roots = datas; + provider._Context = this; + result.insertNavProvider = provider; + result.NavContext = new NavContext() { Items = new List() }; + return result; + } + public InsertNavTaskInit InsertNav(T data, InsertNavRootOptions rootOptions) where T : class, new() + { + return InsertNav(new List() { data }, rootOptions); ; + } + public InsertNavTaskInit InsertNav(List datas, InsertNavRootOptions rootOptions) where T : class, new() + { + var result = new InsertNavTaskInit(); + var provider = new InsertNavProvider(); + provider._Roots = datas; + provider._Context = this; + provider._RootOptions = rootOptions; + result.insertNavProvider = provider; + result.NavContext = new NavContext() { Items = new List() }; + return result; + } + public DeleteNavTaskInit DeleteNav(T data) where T : class, new() + { + return DeleteNav(new List() { data }); + } + public DeleteNavTaskInit DeleteNav(List datas) where T : class, new() + { + var result = new DeleteNavTaskInit(); + result.deleteNavProvider = new DeleteNavProvider(); + result.deleteNavProvider._Roots = datas; + result.deleteNavProvider._Context = this; + return result; + } + public DeleteNavTaskInit DeleteNav(Expression> whereExpression) where T : class, new() + { + return DeleteNav(this.Queryable().Where(whereExpression).ToList()); + } + + public DeleteNavTaskInit DeleteNav(T data, DeleteNavRootOptions options) where T : class, new() + { + return DeleteNav(new List() { data }, options); + } + public DeleteNavTaskInit DeleteNav(List datas, DeleteNavRootOptions options) where T : class, new() + { + var result = new DeleteNavTaskInit(); + result.deleteNavProvider = new DeleteNavProvider(); + result.deleteNavProvider._Roots = datas; + result.deleteNavProvider._Context = this; + result.deleteNavProvider._RootOptions = options; + return result; + } + public DeleteNavTaskInit DeleteNav(Expression> whereExpression, DeleteNavRootOptions options) where T : class, new() + { + return DeleteNav(this.Queryable().Where(whereExpression).ToList(), options); + } + + public UpdateNavTaskInit UpdateNav(T data) where T : class, new() + { + return UpdateNav(new List() { data }); + } + public UpdateNavTaskInit UpdateNav(List datas) where T : class, new() + { + var result = new UpdateNavTaskInit(); + var provider = new UpdateNavProvider(); + provider._Roots = datas; + provider._Context = this; + result.UpdateNavProvider = provider; + result.NavContext = new NavContext() { Items = new List() { } }; + return result; + } + public UpdateNavTaskInit UpdateNav(T data, UpdateNavRootOptions rootOptions) where T : class, new() + { + return UpdateNav(new List() { data }, rootOptions); + } + public UpdateNavTaskInit UpdateNav(List datas, UpdateNavRootOptions rootOptions) where T : class, new() + { + var result = new UpdateNavTaskInit(); + var provider = new UpdateNavProvider(); + provider._Roots = datas; + provider._RootOptions = rootOptions; + provider._Context = this; + result.UpdateNavProvider = provider; + result.NavContext = new NavContext() { Items = new List() { } }; + return result; ; + } + #endregion + + #region DbFirst + public virtual IDbFirst DbFirst + { + get + { + IDbFirst dbFirst = InstanceFactory.GetDbFirst(this.Context.CurrentConnectionConfig); + dbFirst.Context = this.Context; + dbFirst.Init(); + return dbFirst; + } + } + #endregion + + #region CodeFirst + public virtual ICodeFirst CodeFirst + { + get + { + ICodeFirst codeFirst = InstanceFactory.GetCodeFirst(this.Context.CurrentConnectionConfig); + codeFirst.Context = this; + return codeFirst; + } + } + #endregion + + #region Db Maintenance + public virtual IDbMaintenance DbMaintenance + { + get + { + if (this._DbMaintenance == null) + { + IDbMaintenance maintenance = InstanceFactory.GetDbMaintenance(this.Context.CurrentConnectionConfig); + this._DbMaintenance = maintenance; + maintenance.Context = this; + } + return this._DbMaintenance; + } + } + #endregion + + #region Entity Maintenance + public virtual EntityMaintenance EntityMaintenance + { + get + { + if (this._EntityProvider == null) + { + this._EntityProvider = new EntityMaintenance(); + this._EntityProvider.Context = this; + } + return this._EntityProvider; + } + set { this._EntityProvider = value; } + } + #endregion + + #region Gobal Filter + public virtual QueryFilterProvider QueryFilter + { + get + { + if (this._QueryFilterProvider == null) + { + this._QueryFilterProvider = new QueryFilterProvider(); + this._QueryFilterProvider.Context = this; + } + return this._QueryFilterProvider; + } + set { this._QueryFilterProvider = value; } + } + #endregion + + #region SimpleClient + public T CreateContext(bool isTran) where T : SugarUnitOfWork, new() + { + Check.ExceptionEasy(" var childDb=Db.GetConnection(configId); use Db.CreateContext ", " 例如 var childDb=Db.GetConnection(configId);其中Db才能使用CreateContext,childDb不能使用"); + return null; + } + public SugarUnitOfWork CreateContext(bool isTran = true) + { + Check.ExceptionEasy(" var childDb=Db.GetConnection(configId); use Db.CreateContext ", " 例如 var childDb=Db.GetConnection(configId);其中Db才能使用CreateContext,childDb不能使用"); + return null; + } + //[Obsolete("Use SqlSugarClient.GetSimpleClient() Or SqlSugarClient.GetSimpleClient() ")] + //public virtual SimpleClient SimpleClient + //{ + // get + // { + // if (this._SimpleClient == null) + // this._SimpleClient = new SimpleClient(this); + // return this._SimpleClient; + // } + //} + public virtual SimpleClient GetSimpleClient() where T : class, new() + { + return new SimpleClient(this); + } + + public RepositoryType GetRepository() where RepositoryType : ISugarRepository, new() + { + Type type = typeof(RepositoryType); + var isAnyParamter = type.GetConstructors().Any(z => z.GetParameters().Length != 0); + object o = null; + if (isAnyParamter) + { + o = Activator.CreateInstance(type, new string[] { null }); + } + else + { + o = Activator.CreateInstance(type); + } + var result = (RepositoryType)o; + if (result.Context == null) + { + result.Context = this.Context; + } + return result; + } + //public virtual SimpleClient GetSimpleClient() + //{ + // if (this._SimpleClient == null) + // this._SimpleClient = new SimpleClient(this); + // return this._SimpleClient; + //} + #endregion + + #region Dispose OR Close + public virtual void Close() + { + this.Context.Ado?.Close(); + } + public virtual void Open() + { + this.Context.Ado?.Open(); + } + public virtual void Dispose() + { + this.Context.Ado?.Dispose(); + } + #endregion + + #region Queue + public int SaveQueues(bool isTran = true) + { + return SaveQueuesProvider(isTran, (sql, parameters) => { return this.Ado.ExecuteCommand(sql, parameters); }); + } + + public async Task SaveQueuesAsync(bool isTran = true) + { + return await SaveQueuesProviderAsync(isTran, (sql, parameters) => { return Ado.ExecuteCommandAsync(sql, parameters); }).ConfigureAwait(false); + } + public List SaveQueues(bool isTran = true) + { + return SaveQueuesProvider(isTran, (sql, parameters) => { return this.Ado.SqlQuery(sql, parameters); }); + } + public async Task> SaveQueuesAsync(bool isTran = true) + { + return await SaveQueuesProviderAsync(isTran, (sql, parameters) => { return Ado.SqlQueryAsync(sql, parameters); }).ConfigureAwait(false); + } + public Tuple, List> SaveQueues(bool isTran = true) + { + return SaveQueuesProvider(isTran, (sql, parameters) => { return this.Ado.SqlQuery(sql, parameters); }); + } + public async Task, List>> SaveQueuesAsync(bool isTran = true) + { + return await SaveQueuesProviderAsync(isTran, (sql, parameters) => { return Ado.SqlQueryAsync(sql, parameters); }).ConfigureAwait(false); + } + public Tuple, List, List> SaveQueues(bool isTran = true) + { + return SaveQueuesProvider(isTran, (sql, parameters) => { return this.Ado.SqlQuery(sql, parameters); }); + } + public async Task, List, List>> SaveQueuesAsync(bool isTran = true) + { + return await SaveQueuesProviderAsync(isTran, (sql, parameters) => { return Ado.SqlQueryAsync(sql, parameters); }).ConfigureAwait(false); + } + public Tuple, List, List, List> SaveQueues(bool isTran = true) + { + return SaveQueuesProvider(isTran, (sql, parameters) => { return this.Ado.SqlQuery(sql, parameters); }); + } + public async Task, List, List, List>> SaveQueuesAsync(bool isTran = true) + { + return await SaveQueuesProviderAsync(isTran, (sql, parameters) => { return Ado.SqlQueryAsync(sql, parameters); }).ConfigureAwait(false); + } + public Tuple, List, List, List, List> SaveQueues(bool isTran = true) + { + return SaveQueuesProvider(isTran, (sql, parameters) => { return this.Ado.SqlQuery(sql, parameters); }); + } + public async Task, List, List, List, List>> SaveQueuesAsync(bool isTran = true) + { + return await SaveQueuesProviderAsync(isTran, (sql, parameters) => { return Ado.SqlQueryAsync(sql, parameters); }).ConfigureAwait(false); + } + public Tuple, List, List, List, List, List> SaveQueues(bool isTran = true) + { + return SaveQueuesProvider(isTran, (sql, parameters) => { return this.Ado.SqlQuery(sql, parameters); }); + } + public async Task, List, List, List, List, List>> SaveQueuesAsync(bool isTran = true) + { + return await SaveQueuesProviderAsync(isTran, (sql, parameters) => { return Ado.SqlQueryAsync(sql, parameters); }).ConfigureAwait(false); + } + public Tuple, List, List, List, List, List, List> SaveQueues(bool isTran = true) + { + return SaveQueuesProvider(isTran, (sql, parameters) => { return this.Ado.SqlQuery(sql, parameters); }); + } + public async Task, List, List, List, List, List, List>> SaveQueuesAsync(bool isTran = true) + { + return await SaveQueuesProviderAsync(isTran, (sql, parameters) => { return Ado.SqlQueryAsync(sql, parameters); }).ConfigureAwait(false); + } + public void AddQueue(string sql, object parsmeters = null) + { + if (Queues == null) + { + Queues = new QueueList(); + } + var pars = this.Context.Ado.GetParameters(parsmeters); + if (pars != null) + { + foreach (var par in pars) + { + if (par.ParameterName.StartsWith(':')) + { + par.ParameterName = ("@" + par.ParameterName.Trim(':')); + } + } + } + this.Queues.Add(sql, pars); + + } + public void AddQueue(string sql, SugarParameter parsmeter) + { + if (Queues == null) + { + Queues = new QueueList(); + } + this.Queues.Add(sql, new List() { parsmeter }); + } + public void AddQueue(string sql, List parsmeters) + { + if (Queues == null) + { + Queues = new QueueList(); + } + this.Queues.Add(sql, parsmeters); + } + public QueueList Queues { get { if (_Queues == null) { _Queues = new QueueList(); } return _Queues; } set { _Queues = value; } } + + + + private async Task SaveQueuesProviderAsync(bool isTran, Func, Task> func) + { + try + { + //if (this.CurrentConnectionConfig.DbType == DbType.Oracle) + //{ + // throw new Exception("Oracle no support SaveQueues"); + //} + if (this.Queues == null || this.Queues.Count == 0) return default(T); + isTran = isTran && this.Ado.Transaction == null; + if (isTran) this.Ado.BeginTran(); + StringBuilder sqlBuilder = new StringBuilder(); + var parsmeters = new List(); + var index = 1; + if (this.Queues.HasValue()) + { + var repeatList = + Queues.SelectMany(it => it.Parameters ?? Array.Empty()).Select(it => it.ParameterName) + .GroupBy(it => it?.ToLower()) + .Where(it => it.Count() > 1); + var repeatCount = repeatList.Count(); + var isParameterNameRepeat = repeatList.Any(); + foreach (var item in Queues) + { + if (item.Sql == null) + item.Sql = string.Empty; + if (item.Parameters == null) + item.Parameters = Array.Empty(); + var itemParsmeters = item.Parameters.OrderByDescending(it => it.ParameterName.Length).ToList(); + List addParameters = new List(); + var itemSql = item.Sql; + foreach (var itemParameter in itemParsmeters) + { + var newName = itemParameter.ParameterName + "_q_" + index; + SugarParameter parameter = new SugarParameter(newName, itemParameter.Value); + parameter.DbType = itemParameter.DbType; + if (repeatCount > 500 || (isParameterNameRepeat && repeatList.Any(it => it.Key.EqualCase(itemParameter.ParameterName)))) + { + if (newName.StartsWith(':') && itemSql.Contains(itemParameter.ParameterName.ToLower().Replace(":", "@"), StringComparison.CurrentCultureIgnoreCase)) + { + itemParameter.ParameterName = itemParameter.ParameterName.Replace(":", "@"); + } + itemSql = UtilMethods.ReplaceSqlParameter(itemSql, itemParameter, newName); + addParameters.Add(parameter); + } + else + { + parameter.ParameterName = itemParameter.ParameterName; + addParameters.Add(parameter); + } + } + parsmeters.AddRange(addParameters); + itemSql = itemSql + .TrimEnd('\r') + .TrimEnd('\n') + .TrimEnd('\r') + .TrimEnd('\n') + .TrimEnd(';') + ";"; + if (itemSql == "begin;") + { + itemSql = itemSql.TrimEnd(';') + "\n"; + } + sqlBuilder.AppendLine(itemSql); + index++; + } + } + this.Queues.Clear(); + var builder = InstanceFactory.GetSqlbuilder(this.Context.CurrentConnectionConfig); + builder.FormatSaveQueueSql(sqlBuilder); + var result = await func(sqlBuilder.ToString(), parsmeters).ConfigureAwait(false); + if (isTran) this.Ado.CommitTran(); + return result; + } + catch (Exception) + { + if (isTran) this.Ado.RollbackTran(); + throw; + } + } + private T SaveQueuesProvider(bool isTran, Func, T> func) + { + try + { + //if (this.CurrentConnectionConfig.DbType == DbType.Oracle) + //{ + // throw new Exception("Oracle no support SaveQueues"); + //} + if (this.Queues == null || this.Queues.Count == 0) return default(T); + isTran = isTran && this.Ado.Transaction == null; + if (isTran) this.Ado.BeginTran(); + StringBuilder sqlBuilder = new StringBuilder(); + var parsmeters = new List(); + var index = 1; + if (this.Queues.HasValue()) + { + var repeatList = + Queues.SelectMany(it => it.Parameters ?? Array.Empty()).Select(it => it.ParameterName) + .GroupBy(it => it?.ToLower()) + .Where(it => it.Count() > 1); + var repeatCount = repeatList.Count(); + var isParameterNameRepeat = repeatList.Any(); + foreach (var item in Queues) + { + if (item.Sql == null) + item.Sql = string.Empty; + if (item.Parameters == null) + item.Parameters = Array.Empty(); + var itemParsmeters = item.Parameters.OrderByDescending(it => it.ParameterName.Length).ToList(); + List addParameters = new List(); + var itemSql = item.Sql; + foreach (var itemParameter in itemParsmeters) + { + var newName = itemParameter.ParameterName + "_q_" + index; + SugarParameter parameter = new SugarParameter(newName, itemParameter.Value); + parameter.DbType = itemParameter.DbType; + if (repeatCount > 500 || (isParameterNameRepeat && repeatList.Any(it => it.Key.EqualCase(itemParameter.ParameterName)))) + { + if (newName.StartsWith(':') && itemSql.Contains(itemParameter.ParameterName.ToLower().Replace(":", "@"), StringComparison.CurrentCultureIgnoreCase)) + { + itemParameter.ParameterName = itemParameter.ParameterName.Replace(":", "@"); + } + itemSql = UtilMethods.ReplaceSqlParameter(itemSql, itemParameter, newName); + } + else + { + parameter.ParameterName = itemParameter.ParameterName; + } + addParameters.Add(parameter); + } + parsmeters.AddRange(addParameters); + itemSql = itemSql + .TrimEnd('\r') + .TrimEnd('\n') + .TrimEnd('\r') + .TrimEnd('\n') + .TrimEnd(';') + ";"; + if (itemSql?.StartsWith("INSERT INTO ") == true && itemSql?.EndsWith(" returning ;") == true) + { + itemSql = itemSql.Replace(" returning ;", " ;"); + } + if (itemSql == "begin;") + { + itemSql = itemSql.TrimEnd(';') + "\n"; + } + sqlBuilder.AppendLine(itemSql); + index++; + } + } + this.Queues.Clear(); + var builder = InstanceFactory.GetSqlbuilder(this.Context.CurrentConnectionConfig); + builder.FormatSaveQueueSql(sqlBuilder); + var result = func(sqlBuilder.ToString(), parsmeters); + if (isTran) this.Ado.CommitTran(); + return result; + } + catch (Exception) + { + if (isTran) this.Ado.RollbackTran(); + throw; + } + } + + #endregion + + #region Cache + public SugarCacheProvider DataCache + { + get + { + var services = this.CurrentConnectionConfig.ConfigureExternalServices; + if (services == null) + return new SugarCacheProvider(); + if (services.DataInfoCacheService == null) + return new SugarCacheProvider(); + SugarCacheProvider cache = new SugarCacheProvider(); + cache.Servie = services.DataInfoCacheService; + return cache; + } + } + #endregion + + #region Split table + public SplitTableContext SplitHelper() where T : class, new() + { + UtilMethods.StartCustomSplitTable(this, typeof(T)); + var result = new SplitTableContext(this.Context) + { + EntityInfo = this.Context.EntityMaintenance.GetEntityInfo() + }; + return result; + } + public SplitTableContext SplitHelper(Type entityType) + { + UtilMethods.StartCustomSplitTable(this, entityType); + var result = new SplitTableContext(this.Context) + { + EntityInfo = this.Context.EntityMaintenance.GetEntityInfo(entityType) + }; + return result; + } + public SplitTableContextResult SplitHelper(T data) where T : class, new() + { + UtilMethods.StartCustomSplitTable(this, typeof(T)); + var result = new SplitTableContext(this.Context) + { + EntityInfo = this.Context.EntityMaintenance.GetEntityInfo() + }; + return new SplitTableContextResult() + { + Items = new List { data }, + Helper = result + }; + } + public SplitTableContextResult SplitHelper(List data) where T : class, new() + { + UtilMethods.StartCustomSplitTable(this, typeof(T)); + var result = new SplitTableContext(this.Context) + { + EntityInfo = this.Context.EntityMaintenance.GetEntityInfo() + }; + return new SplitTableContextResult() + { + Items = data, + Helper = result + }; + } + #endregion + + #region AsTenant + public ITenant AsTenant() + { + if (this.Root != null) + { + return this.Root; + } + else + { + Check.Exception(true, ErrorMessage.GetThrowMessage("Child objects do not support tenant methods, var childDb= Db.GetConnection(confid) ,Db is master ", "Db子对象不支持租户方法,请使用主对象,例如:var childDb= Db.GetConnection(confid) Db是主对象,childDb是子对象 ")); + return null; + } + } + + #endregion + + #region Fastest + public IFastest Fastest() where T : class, new() + { + return new FastestProvider(this); + } + #endregion + + #region Other + public Task AsyncLock(int timeOutSeconds = 30) + { + var result = new SugarAsyncLock(this); + return result.AsyncLock(timeOutSeconds); + } + public DynamicBuilder DynamicBuilder() + { + return new DynamicBuilder(this.Context); + } + public void Tracking(T data) where T : class, new() + { + if (data != null) + { + UtilMethods.IsNullReturnNew(this.TempItems); + var key = "Tracking_" + data.GetHashCode() + ""; + if (!this.TempItems.ContainsKey(key)) + { + var newT = new T(); + FastCopy.Copy(data, newT); + this.TempItems.Add(key, newT); + } + } + } + public void ClearTracking() + { + if (this.Context.TempItems != null) + { + var removeKeys = this.Context.TempItems.Where(it => it.Key.StartsWith("Tracking_") || it.Key.StartsWith("OldData_")).Select(it => it.Key).ToList(); + foreach (string key in removeKeys) + { + this.Context.TempItems.Remove(key); + } + } + } + public void Tracking(List datas) where T : class, new() + { + foreach (var data in datas) + { + this.Tracking(data); + } + if (datas != null) + { + Check.ExceptionEasy(this.Context.TempItems.ContainsKey("OldData_" + datas.GetHashCode()), "The object already has a trace", "对象已存在跟踪,如果要在跟踪可以先清除 db.ClearTracking() "); + this.Context.TempItems.Add("OldData_" + datas.GetHashCode(), datas.Cast().ToList()); + } + } + public SqlSugarClient CopyNew() + { + var result = new SqlSugarClient(UtilMethods.CopyConfig(this.Ado.Context.CurrentConnectionConfig)); + result.QueryFilter = this.QueryFilter; + return result; + } + public void ThenMapper(IEnumerable list, Action action) + { + this.Context.Utilities.PageEach(list, 200, pageList => + { + _ThenMapper(pageList, action); + }); + } + + public async Task ThenMapperAsync(IEnumerable list, Func action) + { + await Context.Utilities.PageEachAsync(list, 200, async pageList => + { + await _ThenMapperAsync(pageList, action).ConfigureAwait(false); + }).ConfigureAwait(false); + } + #endregion + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SugarProvider/SqlSugarScopeProvider.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SugarProvider/SqlSugarScopeProvider.cs new file mode 100644 index 000000000..95ec55a17 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/SugarProvider/SqlSugarScopeProvider.cs @@ -0,0 +1,862 @@ +using System.Data; +using System.Diagnostics; +using System.Dynamic; +using System.Linq.Expressions; + +namespace SqlSugar +{ + public class SqlSugarScopeProvider : ISqlSugarClient + { + internal SqlSugarProvider conn; + internal string initThreadMainId; + internal string initkey = null; + StackFrame[] frames; + + public SqlSugarScopeProvider(SqlSugarProvider conn) + { + this.conn = conn; + this.initThreadMainId = GetCurrentThreadId(); + var key = GetKey(); + this.initkey = key; + this.GetContext(true); + } + + private static string GetCurrentThreadId() + { + return Environment.CurrentManagedThreadId + ""; + } + + public SqlSugarProvider ScopedContext { get { return GetContext(); } } + private SqlSugarProvider GetAsyncContext(bool isInit = false) + { + if (isInit) + { + CallContextAsync.SetData(GetKey(), this.conn); + isInit = false; + return conn; + } + else + { + SqlSugarProvider result = CallContextAsync.GetData(GetKey()); + if (result == null) + { + SqlSugarProvider db = new SqlSugarProvider(this.conn.CurrentConnectionConfig); + CallContextAsync.SetData(GetKey(), db); + return db; + } + else + { + + return result; + } + } + } + private SqlSugarProvider GetThreadContext(bool isInit = false) + { + if (isInit) + { + CallContextThread.SetData(GetKey(), this.conn); + isInit = false; + return conn; + } + else + { + SqlSugarProvider result = CallContextThread.GetData(GetKey()); + if (result == null) + { + SqlSugarProvider db = new SqlSugarProvider(this.conn.CurrentConnectionConfig); + CallContextThread.SetData(GetKey(), db); + return db; + } + else + { + + return result; + } + } + } + protected virtual SqlSugarProvider GetContext(bool isInit = false) + { + SqlSugarProvider result = null; + var key = GetKey(); ; + StackTrace st = new StackTrace(true); + var methods = st.GetFrames(); + var isAsync = UtilMethods.IsAnyAsyncMethod(methods); + if (isAsync) + { + result = GetAsyncContext(isInit); + } + else + { + result = GetThreadContext(isInit); + } + return result; + } + private string GetKey() + { + if (!string.IsNullOrEmpty(this.initkey) && this.initThreadMainId == GetCurrentThreadId()) + { + return this.initkey; + } + var key = "SqlSugarProviderScope_" + conn.CurrentConnectionConfig.ConfigId; + if (frames == null) + { + frames = new StackTrace(true).GetFrames(); + } + if (true) + { + foreach (var method in frames.Take(35)) + { + var refType = method.GetMethod()?.ReflectedType; + if (refType != null) + { + var getInterfaces = refType.Name.StartsWith('<') ? refType?.ReflectedType?.GetInterfaces() : refType?.GetInterfaces(); + if (getInterfaces?.Any(it => it.Name.IsIn("IJob")) == true) + { + key = $"{key}IJob"; + break; + } + } + } + } + return key; + } + + #region API + public Task AsyncLock(int timeOutSeconds = 30) + { + return ScopedContext.AsyncLock(timeOutSeconds); + } + public SugarActionType SugarActionType { get => ScopedContext.SugarActionType; set => ScopedContext.SugarActionType = value; } + public MappingTableList MappingTables { get => ScopedContext.MappingTables; set => ScopedContext.MappingTables = value; } + public MappingColumnList MappingColumns { get => ScopedContext.MappingColumns; set => ScopedContext.MappingColumns = value; } + public IgnoreColumnList IgnoreColumns { get => ScopedContext.IgnoreColumns; set => ScopedContext.IgnoreColumns = value; } + public IgnoreColumnList IgnoreInsertColumns { get => ScopedContext.IgnoreInsertColumns; set => ScopedContext.IgnoreInsertColumns = value; } + public Dictionary TempItems { get => ScopedContext.TempItems; set => ScopedContext.TempItems = value; } + public ConfigQuery ConfigQuery { get => ScopedContext.ConfigQuery; set => ScopedContext.ConfigQuery = value; } + + public Guid ContextID { get => ScopedContext.ContextID; set => ScopedContext.ContextID = value; } + public ConnectionConfig CurrentConnectionConfig { get => ScopedContext.CurrentConnectionConfig; set => ScopedContext.CurrentConnectionConfig = value; } + + public IAdo Ado => ScopedContext.Ado; + + public AopProvider Aop => ScopedContext.Aop; + + public ICodeFirst CodeFirst => ScopedContext.CodeFirst; + + public IDbFirst DbFirst => ScopedContext.DbFirst; + + public IDbMaintenance DbMaintenance => ScopedContext.DbMaintenance; + + public EntityMaintenance EntityMaintenance { get => ScopedContext.EntityMaintenance; set => ScopedContext.EntityMaintenance = value; } + public QueryFilterProvider QueryFilter { get => ScopedContext.QueryFilter; set => ScopedContext.QueryFilter = value; } + public IContextMethods Utilities { get => ScopedContext.Utilities; set => ScopedContext.Utilities = value; } + public QueueList Queues { get => ScopedContext.Queues; set => ScopedContext.Queues = value; } + + public SugarCacheProvider DataCache => ScopedContext.DataCache; + + + public void AddQueue(string sql, object parsmeters = null) + { + ScopedContext.AddQueue(sql, parsmeters); + } + + public void AddQueue(string sql, List parsmeters) + { + ScopedContext.AddQueue(sql, parsmeters); + } + + public void AddQueue(string sql, SugarParameter parsmeter) + { + ScopedContext.AddQueue(sql, parsmeter); + } + + + public void Close() + { + ScopedContext.Close(); + } + public DeleteMethodInfo DeleteableByObject(object singleEntityObjectOrListObject) + { + return ScopedContext.DeleteableByObject(singleEntityObjectOrListObject); + } + public IDeleteable Deleteable() where T : class, new() + { + return ScopedContext.Deleteable(); + } + + public IDeleteable Deleteable(dynamic primaryKeyValue) where T : class, new() + { + return ScopedContext.Deleteable(primaryKeyValue); + } + + public IDeleteable Deleteable(dynamic[] primaryKeyValues) where T : class, new() + { + return ScopedContext.Deleteable(primaryKeyValues); + } + + public IDeleteable Deleteable(Expression> expression) where T : class, new() + { + return ScopedContext.Deleteable(expression); + } + + public IDeleteable Deleteable(List pkValue) where T : class, new() + { + return ScopedContext.Deleteable(pkValue); + } + + public IDeleteable Deleteable(List deleteObjs) where T : class, new() + { + return ScopedContext.Deleteable(deleteObjs); + } + + public IDeleteable Deleteable(T deleteObj) where T : class, new() + { + return ScopedContext.Deleteable(deleteObj); + } + + public void Dispose() + { + ScopedContext.Dispose(); + } + + public DateTime GetDate() + { + return ScopedContext.GetDate(); + } + public T CreateContext(bool isTran) where T : SugarUnitOfWork, new() + { + Check.ExceptionEasy(" var childDb=Db.GetConnection(configId); use Db.CreateContext ", " 例如 var childDb=Db.GetConnection(configId);其中Db才能使用CreateContext,childDb不能使用"); + return null; + } + public SugarUnitOfWork CreateContext(bool isTran = true) + { + Check.ExceptionEasy(" var childDb=Db.GetConnection(configId); use Db.CreateContext ", " 例如 var childDb=Db.GetConnection(configId);其中Db才能使用CreateContext,childDb不能使用"); + return null; + } + public SimpleClient GetSimpleClient() where T : class, new() + { + return ScopedContext.GetSimpleClient(); + } + public RepositoryType GetRepository() where RepositoryType : ISugarRepository, new() + { + return ScopedContext.GetRepository(); + } + public void InitMappingInfo(Type type) + { + ScopedContext.InitMappingInfo(type); + } + + public void InitMappingInfo() + { + ScopedContext.InitMappingInfo(); + } + public IInsertable> InsertableByDynamic(object insertDynamicObject) + { + return ScopedContext.InsertableByDynamic(insertDynamicObject); + } + public InsertMethodInfo InsertableByObject(object singleEntityObjectOrListObject) + { + return ScopedContext.InsertableByObject(singleEntityObjectOrListObject); + } + public IInsertable Insertable(Dictionary columnDictionary) where T : class, new() + { + return ScopedContext.Insertable(columnDictionary); + } + + public IInsertable Insertable(dynamic insertDynamicObject) where T : class, new() + { + return ScopedContext.Insertable((object)insertDynamicObject); + } + + public IInsertable Insertable(List insertObjs) where T : class, new() + { + return ScopedContext.Insertable(insertObjs); + } + + public IInsertable Insertable(T insertObj) where T : class, new() + { + return ScopedContext.Insertable(insertObj); + } + + public IInsertable Insertable(T[] insertObjs) where T : class, new() + { + return ScopedContext.Insertable(insertObjs); + } + + public void Open() + { + ScopedContext.Open(); + } + public ISugarQueryable SlaveQueryable() + { + return ScopedContext.SlaveQueryable(); + } + public ISugarQueryable MasterQueryable() + { + return ScopedContext.MasterQueryable(); + } + public ISugarQueryable Queryable(string tableName, string shortName) + { + return ScopedContext.Queryable(tableName, shortName); + } + + public ISugarQueryable Queryable(Expression> joinExpression) where T : class, new() + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) where T : class, new() + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) where T : class, new() + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) where T : class, new() + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) where T : class, new() + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) where T : class, new() + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) where T : class, new() + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) where T : class, new() + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) where T : class, new() + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) where T : class, new() + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) where T : class, new() + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(ISugarQueryable joinQueryable1, ISugarQueryable joinQueryable2, Expression> joinExpression) + where T : class, new() + where T2 : class, new() + { + return ScopedContext.Queryable(joinQueryable1, joinQueryable2, joinExpression); + } + + public ISugarQueryable Queryable(ISugarQueryable joinQueryable1, ISugarQueryable joinQueryable2, JoinType joinType, Expression> joinExpression) + where T : class, new() + where T2 : class, new() + { + return ScopedContext.Queryable(joinQueryable1, joinQueryable2, joinType, joinExpression); + } + + public ISugarQueryable Queryable(ISugarQueryable joinQueryable1, ISugarQueryable joinQueryable2, ISugarQueryable joinQueryable3, JoinType joinType1, Expression> joinExpression1, JoinType joinType2, Expression> joinExpression2) + where T : class, new() + where T2 : class, new() + where T3 : class, new() + { + return ScopedContext.Queryable(joinQueryable1, joinQueryable2, joinQueryable3, joinType1, joinExpression1, joinType2, joinExpression2); + } + public ISugarQueryable Queryable(ISugarQueryable joinQueryable1, ISugarQueryable joinQueryable2, ISugarQueryable joinQueryable3, ISugarQueryable joinQueryable4, JoinType joinType1, Expression> joinExpression1, JoinType joinType2, Expression> joinExpression2, JoinType joinType3, Expression> joinExpression3) + where T : class, new() + where T2 : class, new() + where T3 : class, new() + where T4 : class, new() + { + return ScopedContext.Queryable(joinQueryable1, joinQueryable2, joinQueryable3, joinQueryable4, joinType1, joinExpression1, joinType2, joinExpression2, joinType3, joinExpression3); + } + public ISugarQueryable Queryable() + { + return ScopedContext.Queryable(); + } + + public ISugarQueryable Queryable(ISugarQueryable queryable) + { + return ScopedContext.Queryable(queryable); + } + + public ISugarQueryable Queryable(ISugarQueryable queryable, string shortName) + { + return ScopedContext.Queryable(queryable, shortName); + } + public ISugarQueryable Queryable(string shortName) + { + return ScopedContext.Queryable(shortName); + } + + public IReportable Reportable(T data) + { + return ScopedContext.Reportable(data); + } + + public IReportable Reportable(List list) + { + return ScopedContext.Reportable(list); + } + + public IReportable Reportable(T[] array) + { + return ScopedContext.Reportable(array); + } + + public int SaveQueues(bool isTran = true) + { + return ScopedContext.SaveQueues(isTran); + } + + public Tuple, List, List, List, List, List, List> SaveQueues(bool isTran = true) + { + return ScopedContext.SaveQueues(isTran); + } + + public Tuple, List, List, List, List, List> SaveQueues(bool isTran = true) + { + return ScopedContext.SaveQueues(isTran); + } + + public Tuple, List, List, List, List> SaveQueues(bool isTran = true) + { + return ScopedContext.SaveQueues(isTran); + } + + public Tuple, List, List, List> SaveQueues(bool isTran = true) + { + return ScopedContext.SaveQueues(isTran); + } + + public Tuple, List, List> SaveQueues(bool isTran = true) + { + return ScopedContext.SaveQueues(isTran); + } + + public Tuple, List> SaveQueues(bool isTran = true) + { + return ScopedContext.SaveQueues(isTran); + } + + public List SaveQueues(bool isTran = true) + { + return ScopedContext.SaveQueues(isTran); + } + + public Task SaveQueuesAsync(bool isTran = true) + { + return ScopedContext.SaveQueuesAsync(isTran); + } + + public Task, List, List, List, List, List, List>> SaveQueuesAsync(bool isTran = true) + { + return ScopedContext.SaveQueuesAsync(isTran); + } + + public Task, List, List, List, List, List>> SaveQueuesAsync(bool isTran = true) + { + return ScopedContext.SaveQueuesAsync(isTran); + } + + public Task, List, List, List, List>> SaveQueuesAsync(bool isTran = true) + { + return ScopedContext.SaveQueuesAsync(isTran); + } + + public Task, List, List, List>> SaveQueuesAsync(bool isTran = true) + { + return ScopedContext.SaveQueuesAsync(isTran); + } + + public Task, List, List>> SaveQueuesAsync(bool isTran = true) + { + return ScopedContext.SaveQueuesAsync(isTran); + } + + public Task, List>> SaveQueuesAsync(bool isTran = true) + { + return ScopedContext.SaveQueuesAsync(isTran); + } + + public Task> SaveQueuesAsync(bool isTran = true) + { + return ScopedContext.SaveQueuesAsync(isTran); + } + + public ISugarQueryable SqlQueryable(string sql) where T : class, new() + { + return ScopedContext.SqlQueryable(sql); + } + public IStorageable Storageable(T[] dataList) where T : class, new() + { + return ScopedContext.Storageable(dataList); + } + public StorageableDataTable Storageable(List> dictionaryList, string tableName) + { + return ScopedContext.Storageable(dictionaryList, tableName); + } + public StorageableDataTable Storageable(Dictionary dictionary, string tableName) + { + return ScopedContext.Storageable(dictionary, tableName); + } + + public IStorageable Storageable(List dataList) where T : class, new() + { + return ScopedContext.Storageable(dataList); + } + public IStorageable Storageable(IList dataList) where T : class, new() + { + return ScopedContext.Storageable(dataList?.ToList()); + } + public IStorageable Storageable(T data) where T : class, new() + { + return ScopedContext.Storageable(data); + } + public StorageableDataTable Storageable(DataTable data) + { + return ScopedContext.Storageable(data); + } + public StorageableMethodInfo StorageableByObject(object singleEntityObjectOrListObject) + { + return ScopedContext.StorageableByObject(singleEntityObjectOrListObject); + } + + public ISugarQueryable Union(List> queryables) where T : class + { + return ScopedContext.Union(queryables); + } + + public ISugarQueryable Union(params ISugarQueryable[] queryables) where T : class + { + return ScopedContext.Union(queryables); + } + + public ISugarQueryable UnionAll(List> queryables) where T : class + { + return ScopedContext.UnionAll(queryables); + } + + public ISugarQueryable UnionAll(params ISugarQueryable[] queryables) where T : class + { + return ScopedContext.UnionAll(queryables); + } + public UpdateExpressionMethodInfo UpdateableByObject(Type entityType) + { + return ScopedContext.UpdateableByObject(entityType); + } + public UpdateMethodInfo UpdateableByObject(object singleEntityObjectOrListObject) + { + return ScopedContext.UpdateableByObject(singleEntityObjectOrListObject); + } + public IUpdateable> UpdateableByDynamic(object updateDynamicObject) + { + return ScopedContext.UpdateableByDynamic(updateDynamicObject); + } + + public IUpdateable Updateable() where T : class, new() + { + return ScopedContext.Updateable(); + } + + public IUpdateable Updateable(Dictionary columnDictionary) where T : class, new() + { + return ScopedContext.Updateable(columnDictionary); + } + + public IUpdateable Updateable(dynamic updateDynamicObject) where T : class, new() + { + return ScopedContext.Updateable((object)updateDynamicObject); + } + + public IUpdateable Updateable(Expression> columns) where T : class, new() + { + return ScopedContext.Updateable(columns); + } + + public IUpdateable Updateable(Expression> columns) where T : class, new() + { + return ScopedContext.Updateable(columns); + } + + public IUpdateable Updateable(List UpdateObjs) where T : class, new() + { + return ScopedContext.Updateable(UpdateObjs); + } + + public IUpdateable Updateable(T UpdateObj) where T : class, new() + { + return ScopedContext.Updateable(UpdateObj); + } + + public IUpdateable Updateable(T[] UpdateObjs) where T : class, new() + { + return ScopedContext.Updateable(UpdateObjs); + } + public SplitTableContext SplitHelper() where T : class, new() + { + return ScopedContext.SplitHelper(); + } + public SplitTableContext SplitHelper(Type entityType) + { + return ScopedContext.SplitHelper(entityType); + } + public SplitTableContextResult SplitHelper(T data) where T : class, new() + { + return ScopedContext.SplitHelper(data); + } + public SplitTableContextResult SplitHelper(List dataList) where T : class, new() + { + return ScopedContext.SplitHelper(dataList); + } + public IFastest Fastest() where T : class, new() + { + return ScopedContext.Fastest(); + } + + public void ThenMapper(IEnumerable list, Action action) + { + ScopedContext.ThenMapper(list, action); + } + + public Task ThenMapperAsync(IEnumerable list, Func action) + { + return ScopedContext.ThenMapperAsync(list, action); + } + + public ITenant AsTenant() + { + return ScopedContext.AsTenant(); + } + + public ISaveable Saveable(List saveObjects) where T : class, new() + { + return ScopedContext.Saveable(saveObjects); + } + + public ISaveable Saveable(T saveObject) where T : class, new() + { + return ScopedContext.Saveable(saveObject); + } + public InsertNavTaskInit InsertNav(T data) where T : class, new() + { + return ScopedContext.InsertNav(data); + } + public InsertNavTaskInit InsertNav(List datas) where T : class, new() + { + return ScopedContext.InsertNav(datas); + } + public InsertNavTaskInit InsertNav(T data, InsertNavRootOptions rootOptions) where T : class, new() + { + return ScopedContext.InsertNav(data, rootOptions); + } + public InsertNavTaskInit InsertNav(List datas, InsertNavRootOptions rootOptions) where T : class, new() + { + return ScopedContext.InsertNav(datas, rootOptions); + } + public DeleteNavTaskInit DeleteNav(T data) where T : class, new() + { + return ScopedContext.DeleteNav(data); + } + public DeleteNavTaskInit DeleteNav(List datas) where T : class, new() + { + return ScopedContext.DeleteNav(datas); + } + public DeleteNavTaskInit DeleteNav(Expression> whereExpression) where T : class, new() + { + return ScopedContext.DeleteNav(whereExpression); + } + public DeleteNavTaskInit DeleteNav(T data, DeleteNavRootOptions options) where T : class, new() + { + return ScopedContext.DeleteNav(data, options); + } + public DeleteNavTaskInit DeleteNav(List datas, DeleteNavRootOptions options) where T : class, new() + { + return ScopedContext.DeleteNav(datas, options); + } + public DeleteNavTaskInit DeleteNav(Expression> whereExpression, DeleteNavRootOptions options) where T : class, new() + { + return ScopedContext.DeleteNav(whereExpression, options); + } + public UpdateNavTaskInit UpdateNav(T data) where T : class, new() + { + return ScopedContext.UpdateNav(data); + } + public UpdateNavTaskInit UpdateNav(List datas) where T : class, new() + { + return ScopedContext.UpdateNav(datas); + } + public UpdateNavTaskInit UpdateNav(List datas, UpdateNavRootOptions rootOptions) where T : class, new() + { + return this.ScopedContext.UpdateNav(datas, rootOptions); + } + public UpdateNavTaskInit UpdateNav(T data, UpdateNavRootOptions rootOptions) where T : class, new() + { + return this.ScopedContext.UpdateNav(data, rootOptions); + } + public SqlSugarClient CopyNew() + { + var result = new SqlSugarClient(UtilMethods.CopyConfig(this.Ado.Context.CurrentConnectionConfig)); + result.QueryFilter = this.QueryFilter; + return result; + } + public DynamicBuilder DynamicBuilder() + { + return ScopedContext.DynamicBuilder(); + } + public void Tracking(T data) where T : class, new() + { + ScopedContext.Tracking(data); + } + public void Tracking(List datas) where T : class, new() + { + ScopedContext.Tracking(datas); + } + public QueryMethodInfo QueryableByObject(Type entityType) + { + return ScopedContext.QueryableByObject(entityType); + } + public QueryMethodInfo QueryableByObject(Type entityType, string shortName) + { + return ScopedContext.QueryableByObject(entityType, shortName); + } + public GridSaveProvider GridSave(List oldList, List saveList) where T : class, new() + { + return ScopedContext.GridSave(oldList, saveList); + } + public GridSaveProvider GridSave(List saveList) where T : class, new() + { + return ScopedContext.GridSave(saveList); + } + public void ClearTracking() + { + ScopedContext.ClearTracking(); + } + #endregion + } +} \ No newline at end of file diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/UpdateProvider/ParameterUpdateable.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/UpdateProvider/ParameterUpdateable.cs new file mode 100644 index 000000000..96f0ea851 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/UpdateProvider/ParameterUpdateable.cs @@ -0,0 +1,197 @@ +using System.Data; +using System.Text; +using System.Text.RegularExpressions; + +namespace SqlSugar +{ + public class ParameterUpdateable where T : class, new() + { + internal UpdateableProvider Updateable { get; set; } + internal SqlSugarProvider Context { get; set; } + public int ExecuteCommand() + { + var result = 0; + var list = Updateable.UpdateObjs; + var count = list.Length; + var size = GetPageSize(20, count); + Context.Utilities.PageEach(list.ToList(), size, item => + { + Before(item.ToList()); + List allParamter = new List(); + var sql = GetSql(item); + result += Context.Ado.ExecuteCommand(sql.Key, sql.Value); + After(item.ToList()); + }); + return result < 0 ? count : result; + } + public async Task ExecuteCommandAsync() + { + var result = 0; + var list = Updateable.UpdateObjs; + var count = list.Length; + var size = GetPageSize(20, count); + await Context.Utilities.PageEachAsync(list.ToList(), size, async item => + { + Before(item.ToList()); + List allParamter = new List(); + var sql = GetSql(item); + result += await Context.Ado.ExecuteCommandAsync(sql.Key, sql.Value).ConfigureAwait(false); + After(item.ToList()); + }).ConfigureAwait(false); + return result < 0 ? count : result; + } + + + private void Before(List updateObjects) + { + if (this.Updateable.IsEnableDiffLogEvent && updateObjects.Count > 0) + { + var isDisableMasterSlaveSeparation = this.Updateable.Ado.IsDisableMasterSlaveSeparation; + this.Updateable.Ado.IsDisableMasterSlaveSeparation = true; + var parameters = Updateable.UpdateBuilder.Parameters; + if (parameters == null) + parameters = new List(); + Updateable.diffModel.BeforeData = GetDiffTable(updateObjects); + Updateable.diffModel.Sql = this.Updateable.UpdateBuilder.ToSqlString(); + Updateable.diffModel.Parameters = parameters.ToArray(); + this.Updateable.Ado.IsDisableMasterSlaveSeparation = isDisableMasterSlaveSeparation; + } + } + + protected void After(List updateObjects) + { + if (this.Updateable.IsEnableDiffLogEvent && updateObjects.Count > 0) + { + var isDisableMasterSlaveSeparation = this.Updateable.Ado.IsDisableMasterSlaveSeparation; + this.Updateable.Ado.IsDisableMasterSlaveSeparation = true; + Updateable.diffModel.AfterData = GetDiffTable(updateObjects); + Updateable.diffModel.Time = this.Context.Ado.SqlExecutionTime; + if (this.Context.CurrentConnectionConfig.AopEvents.OnDiffLogEvent != null) + this.Context.CurrentConnectionConfig.AopEvents.OnDiffLogEvent(Updateable.diffModel); + this.Updateable.Ado.IsDisableMasterSlaveSeparation = isDisableMasterSlaveSeparation; + } + if (this.Updateable.RemoveCacheFunc != null) + { + this.Updateable.RemoveCacheFunc(); + } + } + private List GetDiffTable(List updateObjects) + { + var builder = Updateable.UpdateBuilder.Builder; + var tableWithString = builder.GetTranslationColumnName(Updateable.UpdateBuilder.TableName); + var wheres = Updateable.WhereColumnList ?? Updateable.UpdateBuilder.PrimaryKeys; + if (wheres == null) + { + wheres = Updateable.UpdateBuilder.DbColumnInfoList + .Where(it => it.IsPrimarykey).Select(it => it.DbColumnName).Distinct().ToList(); + } + var sqlDb = this.Context.CopyNew(); + sqlDb.Aop.DataExecuting = null; + var dataColumns = sqlDb.Updateable(updateObjects).UpdateBuilder.DbColumnInfoList; + List parameters = new List(); + StringBuilder allWhereString = new StringBuilder(); + string columnStr = string.Join(",", dataColumns.Select(x => x.DbColumnName).Distinct().ToList()); + foreach (var item in dataColumns.GroupBy(it => it.TableId)) + { + StringBuilder whereString = new StringBuilder(); + foreach (var whereItem in wheres) + { + var pk = item.FirstOrDefault(it => it.DbColumnName.EqualCase(whereItem)); + var paraterName = FormatValue(pk.PropertyType, pk.DbColumnName, pk.Value, parameters); + whereString.Append($" {pk.DbColumnName} = {paraterName} AND"); + } + allWhereString.Append($" {Regex.Replace(whereString.ToString(), "AND$", "")} OR"); + } + string key = $"SELECT {columnStr} FROM {tableWithString} WHERE {Regex.Replace(allWhereString.ToString(), "OR$", "")}"; + + var dt = sqlDb.Ado.GetDataTable(key, parameters); + return Updateable.GetTableDiff(dt); + } + + #region Values Helper + + public KeyValuePair GetSql(List updateObjects) + { + var inserable = Updateable as UpdateableProvider; + var builder = inserable.UpdateBuilder.Builder; + var columns = inserable.UpdateBuilder.DbColumnInfoList.GroupBy(it => it.DbColumnName).Select(it => it.Key).Distinct().ToList(); + var tableWithString = builder.GetTranslationColumnName(inserable.UpdateBuilder.TableName); + var wheres = inserable.WhereColumnList ?? inserable.UpdateBuilder.PrimaryKeys; + if (wheres == null) + { + wheres = inserable.UpdateBuilder.DbColumnInfoList + .Where(it => it.IsPrimarykey).Select(it => it.DbColumnName).Distinct().ToList(); + } + StringBuilder sbAllSql = new StringBuilder(); + var sqlTemp = ($" UPDATE {tableWithString} SET {{0}} WHERE {{1}};\r\n"); + List parameters = new List(); + Check.ExceptionEasy(wheres?.Count == 0, "Updates cannot be without a primary key or condition", "更新不能没有主键或者条件"); + var sqlDb = this.Context.CopyNew(); + sqlDb.Aop.DataExecuting = null; + foreach (var item in sqlDb.Updateable(updateObjects).UpdateBuilder.DbColumnInfoList.GroupBy(it => it.TableId)) + { + var list = item.ToList(); + Check.ExceptionEasy(list?.Count == 0, "Set has no columns", "更新Set没有列"); + StringBuilder setString = new StringBuilder(); + foreach (var setItem in list) + { + if (setItem.IsPrimarykey) { continue; } + if (Updateable.UpdateBuilder.UpdateColumns?.Count > 0) + { + if (!Updateable.UpdateBuilder.UpdateColumns.Any(it => it.EqualCase(setItem.DbColumnName))) + { + continue; + } + } + if (Updateable.UpdateBuilder.IgnoreColumns?.Count > 0) + { + if (Updateable.UpdateBuilder.IgnoreColumns.Any(it => it.EqualCase(setItem.DbColumnName))) + { + continue; + } + } + var paraterName = FormatValue(setItem.PropertyType, setItem.DbColumnName, setItem.Value, parameters); + setString.Append($" {builder.GetTranslationColumnName(setItem.DbColumnName)} = {paraterName} ,"); + } + StringBuilder whereString = new StringBuilder(); + foreach (var whereItem in wheres) + { + var pk = list.FirstOrDefault(it => it.DbColumnName.EqualCase(whereItem)); + var paraterName = FormatValue(pk.PropertyType, pk.DbColumnName, pk.Value, parameters); + whereString.Append($" {pk.DbColumnName} = {paraterName} AND"); + } + var builderItem = string.Format(sqlTemp, setString.ToString().TrimEnd(','), whereString.ToString().TrimEnd('D').TrimEnd('N').TrimEnd('A')); + sbAllSql.Append(builderItem); + } + builder.FormatSaveQueueSql(sbAllSql); + return new KeyValuePair(sbAllSql.ToString(), parameters.ToArray()); + } + + private int GetPageSize(int pageSize, int count) + { + if (pageSize * count > 2100) + { + pageSize = 50; + } + if (pageSize * count > 2100) + { + pageSize = 20; + } + if (pageSize * count > 2100) + { + pageSize = 10; + } + + return pageSize; + } + private string FormatValue(Type type, string name, object value, List allParamter) + { + var keyword = this.Updateable.UpdateBuilder.Builder.SqlParameterKeyWord; + var result = keyword + name + allParamter.Count; + var addParameter = new SugarParameter(result, value, type); + allParamter.Add(addParameter); + return result; + } + #endregion + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/UpdateProvider/SplitTableUpdateByObjectProvider.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/UpdateProvider/SplitTableUpdateByObjectProvider.cs new file mode 100644 index 000000000..d9ee88343 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/UpdateProvider/SplitTableUpdateByObjectProvider.cs @@ -0,0 +1,164 @@ +using System.Reflection; + +namespace SqlSugar +{ + public class SplitTableUpdateByObjectProvider where T : class, new() + { + public SqlSugarProvider Context; + public UpdateableProvider updateobj; + public T[] UpdateObjects { get; set; } + + public IEnumerable Tables { get; set; } + internal List WhereColumns { get; set; } + internal bool IsEnableDiffLogEvent { get; set; } + internal object BusinessData { get; set; } + + public int ExecuteCommandWithOptLock(bool isThrowError = false) + { + List groupModels; + int result; + GroupDataList(UpdateObjects, out groupModels, out result); + var dataEvent = this.Context.CurrentConnectionConfig.AopEvents?.DataExecuting; + this.Context.Aop.DataExecuting = null; + foreach (var item in groupModels.GroupBy(it => it.GroupName)) + { + var addList = item.Select(it => it.Item).ToList(); + if (IsVersion()) + { + Check.ExceptionEasy(addList.Count > 1, "The version number can only be used for single record updates", "版本号只能用于单条记录更新"); + result += this.Context.Updateable(addList.First()) + .WhereColumns(this.WhereColumns?.ToArray()) + .EnableDiffLogEventIF(this.IsEnableDiffLogEvent, this.BusinessData) + .UpdateColumns(updateobj.UpdateBuilder.UpdateColumns?.ToArray()) + .IgnoreColumns(this.updateobj.UpdateBuilder.IsNoUpdateNull, this.updateobj.UpdateBuilder.IsOffIdentity, this.updateobj.UpdateBuilder.IsNoUpdateDefaultValue) + .IgnoreColumns(GetIgnoreColumns()).AS(item.Key).ExecuteCommandWithOptLock(isThrowError); + } + else + { + result += this.Context.Updateable(addList) + .WhereColumns(this.WhereColumns?.ToArray()) + .EnableDiffLogEventIF(this.IsEnableDiffLogEvent, this.BusinessData) + .UpdateColumns(updateobj.UpdateBuilder.UpdateColumns?.ToArray()) + .IgnoreColumns(this.updateobj.UpdateBuilder.IsNoUpdateNull, this.updateobj.UpdateBuilder.IsOffIdentity, this.updateobj.UpdateBuilder.IsNoUpdateDefaultValue) + .IgnoreColumns(GetIgnoreColumns()).AS(item.Key).ExecuteCommandWithOptLock(isThrowError); + } + } + this.Context.Aop.DataExecuting = dataEvent; + return result; + } + public int ExecuteCommand() + { + List groupModels; + int result; + GroupDataList(UpdateObjects, out groupModels, out result); + foreach (var item in groupModels.GroupBy(it => it.GroupName)) + { + var addList = item.Select(it => it.Item).ToList(); + var dataEvent = this.Context.CurrentConnectionConfig.AopEvents?.DataExecuting; + this.Context.Aop.DataExecuting = null; + result += this.Context.Updateable(addList) + .EnableDiffLogEventIF(this.IsEnableDiffLogEvent, this.BusinessData) + .WhereColumns(this.WhereColumns?.ToArray()) + .UpdateColumns(updateobj.UpdateBuilder.UpdateColumns?.ToArray()) + .IgnoreColumns(this.updateobj.UpdateBuilder.IsNoUpdateNull, this.updateobj.UpdateBuilder.IsOffIdentity, this.updateobj.UpdateBuilder.IsNoUpdateDefaultValue) + .IgnoreColumns(GetIgnoreColumns()).AS(item.Key).ExecuteCommand(); + this.Context.Aop.DataExecuting = dataEvent; + } + return result; + } + + + public async Task ExecuteCommandAsync() + { + List groupModels; + int result; + GroupDataList(UpdateObjects, out groupModels, out result); + foreach (var item in groupModels.GroupBy(it => it.GroupName)) + { + var addList = item.Select(it => it.Item).ToList(); + var dataEvent = this.Context.CurrentConnectionConfig.AopEvents?.DataExecuting; + this.Context.Aop.DataExecuting = null; + result += await Context.Updateable(addList) + .WhereColumns(WhereColumns?.ToArray()) + .EnableDiffLogEventIF(IsEnableDiffLogEvent, BusinessData) + .UpdateColumns(updateobj.UpdateBuilder.UpdateColumns?.ToArray()) + .IgnoreColumns(updateobj.UpdateBuilder.IsNoUpdateNull, updateobj.UpdateBuilder.IsOffIdentity, updateobj.UpdateBuilder.IsNoUpdateDefaultValue) + .IgnoreColumns(GetIgnoreColumns()).AS(item.Key).ExecuteCommandAsync().ConfigureAwait(false); + this.Context.Aop.DataExecuting = dataEvent; + } + return result; + } + public async Task ExecuteCommandWithOptLockAsync(bool isThrowError = false) + { + List groupModels; + int result; + GroupDataList(UpdateObjects, out groupModels, out result); + var dataEvent = this.Context.CurrentConnectionConfig.AopEvents?.DataExecuting; + this.Context.Aop.DataExecuting = null; + foreach (var item in groupModels.GroupBy(it => it.GroupName)) + { + var addList = item.Select(it => it.Item).ToList(); + if (IsVersion()) + { + Check.ExceptionEasy(addList.Count > 1, "The version number can only be used for single record updates", "版本号只能用于单条记录更新"); + result += await Context.Updateable(addList.First()) + .WhereColumns(WhereColumns?.ToArray()) + .EnableDiffLogEventIF(IsEnableDiffLogEvent, BusinessData) + .UpdateColumns(updateobj.UpdateBuilder.UpdateColumns?.ToArray()) + .IgnoreColumns(updateobj.UpdateBuilder.IsNoUpdateNull, updateobj.UpdateBuilder.IsOffIdentity, updateobj.UpdateBuilder.IsNoUpdateDefaultValue) + .IgnoreColumns(GetIgnoreColumns()).AS(item.Key).ExecuteCommandWithOptLockAsync(isThrowError).ConfigureAwait(false); + + } + else + { + result += await Context.Updateable(addList) + .WhereColumns(WhereColumns?.ToArray()) + .EnableDiffLogEventIF(IsEnableDiffLogEvent, BusinessData) + .UpdateColumns(updateobj.UpdateBuilder.UpdateColumns?.ToArray()) + .IgnoreColumns(updateobj.UpdateBuilder.IsNoUpdateNull, updateobj.UpdateBuilder.IsOffIdentity, updateobj.UpdateBuilder.IsNoUpdateDefaultValue) + .IgnoreColumns(GetIgnoreColumns()).AS(item.Key).ExecuteCommandWithOptLockAsync(isThrowError).ConfigureAwait(false); + } + } + this.Context.Aop.DataExecuting = dataEvent; + return result; + } + private string[] GetIgnoreColumns() + { + if (this.updateobj.UpdateBuilder.DbColumnInfoList.Count != 0) + { + var columns = this.updateobj.UpdateBuilder.DbColumnInfoList.Select(it => it.DbColumnName).Distinct().ToList(); + var result = this.Context.EntityMaintenance.GetEntityInfo().Columns.Where(x => !columns.Any(y => y.EqualCase(x.DbColumnName))).Select(it => it.DbColumnName).ToArray(); + result = result.Where(it => !string.IsNullOrEmpty(it)).ToArray(); + return result; + } + else + { + return null; + } + } + private void GroupDataList(T[] datas, out List groupModels, out int result) + { + var attribute = typeof(T).GetCustomAttribute() as SplitTableAttribute; + Check.Exception(attribute == null, $"{typeof(T).Name} need SplitTableAttribute"); + groupModels = new List(); + var db = this.Context; + foreach (var item in datas) + { + var value = db.SplitHelper().GetValue(attribute.SplitType, item); + var tableName = db.SplitHelper().GetTableName(attribute.SplitType, value); + groupModels.Add(new GroupModel() { GroupName = tableName, Item = item }); + } + result = 0; + } + private bool IsVersion() + { + return this.Context.EntityMaintenance.GetEntityInfo().Columns.Any(it => it.IsEnableUpdateVersionValidation); + } + + internal class GroupModel + { + public string GroupName { get; set; } + public T Item { get; set; } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/UpdateProvider/SplitTableUpdateProvider.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/UpdateProvider/SplitTableUpdateProvider.cs new file mode 100644 index 000000000..ae177822d --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/UpdateProvider/SplitTableUpdateProvider.cs @@ -0,0 +1,109 @@ +using System.Text.RegularExpressions; + +namespace SqlSugar +{ + public class SplitTableUpdateProvider where T : class, new() + { + public SqlSugarProvider Context; + public UpdateableProvider updateobj; + + public IEnumerable Tables { get; set; } + + public int ExecuteCommandWithOptLock(bool isThrowError = false) + { + var updates = updateobj.UpdateObjs; + var tableName = this.Context.SplitHelper(updates.FirstOrDefault()).GetTableName(); + var names = updateobj.UpdateBuilder.DbColumnInfoList.Select(it => it.DbColumnName).Distinct().ToArray(); + return this.Context.Updateable(updates).AS(tableName) + .UpdateColumns(names).ExecuteCommandWithOptLock(isThrowError); + } + public int ExecuteCommand() + { + if (this.Context.Ado.Transaction == null) + { + try + { + this.Context.Ado.BeginTran(); + var result = _ExecuteCommand(); + this.Context.Ado.CommitTran(); + return result; + } + catch (Exception) + { + this.Context.Ado.RollbackTran(); + throw; + } + } + else + { + return _ExecuteCommand(); + } + } + public async Task ExecuteCommandWithOptLockAsync(bool isThrowError = false) + { + var updates = updateobj.UpdateObjs; + var tableName = this.Context.SplitHelper(updates.FirstOrDefault()).GetTableName(); + var names = updateobj.UpdateBuilder.DbColumnInfoList.Select(it => it.DbColumnName).Distinct().ToArray(); + return await Context.Updateable(updates).AS(tableName) + .UpdateColumns(names).ExecuteCommandWithOptLockAsync(isThrowError).ConfigureAwait(false); + } + public async Task ExecuteCommandAsync() + { + if (this.Context.Ado.Transaction == null) + { + try + { + this.Context.Ado.BeginTran(); + var result = await _ExecuteCommandAsync().ConfigureAwait(false); + this.Context.Ado.CommitTran(); + return result; + } + catch (Exception) + { + this.Context.Ado.RollbackTran(); + throw; + } + } + else + { + return await _ExecuteCommandAsync().ConfigureAwait(false); + } + } + private int _ExecuteCommand() + { + var result = 0; + var sqlobj = updateobj.ToSql(); + + foreach (var item in Tables) + { + var newsqlobj = GetSqlObj(sqlobj, item.TableName); + result += this.Context.Ado.ExecuteCommand(newsqlobj.Key, newsqlobj.Value); + } + return result; + } + + private async Task _ExecuteCommandAsync() + { + var result = 0; + var sqlobj = updateobj.ToSql(); + foreach (var item in Tables) + { + var newsqlobj = GetSqlObj(sqlobj, item.TableName); + result += await Context.Ado.ExecuteCommandAsync(newsqlobj.Key, newsqlobj.Value).ConfigureAwait(false); + } + return result; + } + + private KeyValuePair> GetSqlObj(KeyValuePair> keyValuePair, string asName) + { + List pars = new List(); + string sql = keyValuePair.Key; + if (keyValuePair.Value != null) + { + pars = keyValuePair.Value.Select(it => new SugarParameter(it.ParameterName, it.Value)).ToList(); + } + sql = Regex.Replace(sql, updateobj.EntityInfo.DbTableName, asName, RegexOptions.IgnoreCase); + return new KeyValuePair>(sql, pars); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/UpdateProvider/UpdateExpressionMethodInfo.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/UpdateProvider/UpdateExpressionMethodInfo.cs new file mode 100644 index 000000000..8c82aba0d --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/UpdateProvider/UpdateExpressionMethodInfo.cs @@ -0,0 +1,61 @@ +using System.Reflection; +namespace SqlSugar +{ + public class UpdateExpressionMethodInfo + { + internal SqlSugarProvider Context { get; set; } + internal MethodInfo MethodInfo { get; set; } + internal object objectValue { get; set; } + internal Type Type { get; set; } + + public int ExecuteCommand() + { + if (Context == null) return 0; + var result = objectValue.GetType().GetMethod("ExecuteCommand").Invoke(objectValue, Array.Empty()); + return (int)result; + } + + public async Task ExecuteCommandAsync() + { + if (Context == null) return 0; + var result = objectValue.GetType().GetMyMethod("ExecuteCommandAsync", 0).Invoke(objectValue, Array.Empty()); + return await ((Task)result).ConfigureAwait(false); + } + + public UpdateExpressionMethodInfo Where(string expShortName, FormattableString whereExpressionString) + { + var newMethod = objectValue.GetType().GetMyMethod("Where", 1); + var exp = DynamicCoreHelper.GetWhere(Type, expShortName, whereExpressionString); + var result = newMethod.Invoke(objectValue, new object[] { exp }); + return new UpdateExpressionMethodInfo() + { + objectValue = result, + Type = this.Type, + Context = this.Context + }; + } + public UpdateExpressionMethodInfo SetColumns(string expShortName, FormattableString fieldExpressionString) + { + + var newMethod = objectValue.GetType().GetMethods() + .Where(it => + { + var isTrue = it.Name == "SetColumns" && it.GetParameters().Length == 1; + if (isTrue) + { + return it.GetParameters().First().ToString().Contains(",System.Boolean"); + } + return false; + }) + .Single(); + var exp1 = DynamicCoreHelper.GetWhere(Type, expShortName, fieldExpressionString); + var result = newMethod.Invoke(objectValue, new object[] { exp1 }); + return new UpdateExpressionMethodInfo() + { + objectValue = result, + Type = this.Type, + Context = this.Context + }; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/UpdateProvider/UpdateMethodInfo.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/UpdateProvider/UpdateMethodInfo.cs new file mode 100644 index 000000000..5742abb27 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/UpdateProvider/UpdateMethodInfo.cs @@ -0,0 +1,122 @@ +using System.Reflection; + +namespace SqlSugar +{ + public class UpdateMethodInfo + { + internal SqlSugarProvider Context { get; set; } + internal MethodInfo MethodInfo { get; set; } + internal object objectValue { get; set; } + + public int ExecuteCommandWithOptLock(bool isThrowError = false) + { + if (Context == null) return 0; + var inertable = MethodInfo.Invoke(Context, new object[] { objectValue }); + var result = inertable.GetType().GetMyMethod("ExecuteCommandWithOptLock", 1, typeof(bool)).Invoke(inertable, new object[] { isThrowError }); + return (int)result; + } + + public async Task ExecuteCommandWithOptLockAsync(bool isThrowError = false) + { + if (Context == null) return 0; + var inertable = MethodInfo.Invoke(Context, new object[] { objectValue }); + var result = inertable.GetType().GetMyMethod("ExecuteCommandWithOptLockAsync", 1, typeof(bool)).Invoke(inertable, new object[] { isThrowError }); + return await ((Task)result).ConfigureAwait(false); + } + public int ExecuteCommand() + { + if (Context == null) return 0; + var inertable = MethodInfo.Invoke(Context, new object[] { objectValue }); + var result = inertable.GetType().GetMethod("ExecuteCommand").Invoke(inertable, Array.Empty()); + return (int)result; + } + + public async Task ExecuteCommandAsync() + { + if (Context == null) return 0; + var inertable = MethodInfo.Invoke(Context, new object[] { objectValue }); + var result = inertable.GetType().GetMyMethod("ExecuteCommandAsync", 0).Invoke(inertable, Array.Empty()); + return await ((Task)result).ConfigureAwait(false); + } + public UpdateCommonMethodInfo EnableDiffLogEvent(object businessData = null) + { + if (Context == null) + { + return new UpdateCommonMethodInfo(); + } + var inertable = MethodInfo.Invoke(Context, new object[] { objectValue }); + var newMethod = inertable.GetType().GetMyMethod("EnableDiffLogEvent", 1, typeof(object)); + var result = newMethod.Invoke(inertable, new object[] { businessData }); + return new UpdateCommonMethodInfo() + { + Context = result + }; + } + public UpdateCommonMethodInfo IgnoreColumns(params string[] ignoreColumns) + { + if (Context == null) + { + return new UpdateCommonMethodInfo(); + } + var inertable = MethodInfo.Invoke(Context, new object[] { objectValue }); + var newMethod = inertable.GetType().GetMyMethod("IgnoreColumns", 1, typeof(string[])); + var result = newMethod.Invoke(inertable, new object[] { ignoreColumns }); + return new UpdateCommonMethodInfo() + { + Context = result + }; + } + public UpdateCommonMethodInfo IgnoreNullColumns() + { + var inertable = MethodInfo.Invoke(Context, new object[] { objectValue }); + var newMethod = inertable.GetType().GetMyMethod("IgnoreNullColumns", 0); + var result = newMethod.Invoke(inertable, Array.Empty()); + return new UpdateCommonMethodInfo() + { + Context = result + }; + } + public UpdateCommonMethodInfo UpdateColumns(params string[] updateColumns) + { + var inertable = MethodInfo.Invoke(Context, new object[] { objectValue }); + var newMethod = inertable.GetType().GetMyMethod("UpdateColumns", 1, typeof(string[])); + var result = newMethod.Invoke(inertable, new object[] { updateColumns }); + return new UpdateCommonMethodInfo() + { + Context = result + }; + } + + public UpdateCommonMethodInfo WhereColumns(params string[] whereColumns) + { + var inertable = MethodInfo.Invoke(Context, new object[] { objectValue }); + var newMethod = inertable.GetType().GetMyMethod("WhereColumns", 1, typeof(string[])); + var result = newMethod.Invoke(inertable, new object[] { whereColumns }); + return new UpdateCommonMethodInfo() + { + Context = result, + }; + } + + public UpdateCommonMethodInfo AS(string tableName) + { + var inertable = MethodInfo.Invoke(Context, new object[] { objectValue }); + var newMethod = inertable.GetType().GetMyMethod("AS", 1, typeof(string)); + var result = newMethod.Invoke(inertable, new object[] { tableName }); + return new UpdateCommonMethodInfo() + { + Context = result + }; + } + public UpdateCommonMethodInfo SplitTable() + { + var inertable = MethodInfo.Invoke(Context, new object[] { objectValue }); + var newMethod = inertable.GetType().GetMyMethod("SplitTable", 0); + var result = newMethod.Invoke(inertable, Array.Empty()); + return new UpdateCommonMethodInfo() + { + Context = result + }; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/UpdateProvider/UpdateNavMethodInfo.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/UpdateProvider/UpdateNavMethodInfo.cs new file mode 100644 index 000000000..6906cbf6e --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/UpdateProvider/UpdateNavMethodInfo.cs @@ -0,0 +1,49 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public class UpdateNavMethodInfo + { + internal object MethodInfos { get; set; } + internal SqlSugarProvider Context { get; set; } + + public UpdateNavMethodInfo IncludeByNameString(string navMemberName, UpdateNavOptions updateNavOptions = null) + { + var type = MethodInfos.GetType().GetGenericArguments()[0]; + var entityInfo = this.Context.EntityMaintenance.GetEntityInfo(type); + Type properyItemType; + bool isList; + Expression exp = UtilMethods.GetIncludeExpression(navMemberName, entityInfo, out properyItemType, out isList); + var method = this.MethodInfos.GetType().GetMyMethod("Include", 2, isList) + .MakeGenericMethod(properyItemType); + var obj = method.Invoke(this.MethodInfos, new object[] { exp, updateNavOptions }); + this.MethodInfos = obj; + return this; + } + public UpdateNavMethodInfo ThenIncludeByNameString(string navMemberName, UpdateNavOptions updateNavOptions = null) + { + var type = MethodInfos.GetType().GetGenericArguments()[1]; + var entityInfo = this.Context.EntityMaintenance.GetEntityInfo(type); + Type properyItemType; + bool isList; + Expression exp = UtilMethods.GetIncludeExpression(navMemberName, entityInfo, out properyItemType, out isList); + var method = this.MethodInfos.GetType().GetMyMethod("ThenInclude", 2, isList) + .MakeGenericMethod(properyItemType); + var obj = method.Invoke(this.MethodInfos, new object[] { exp, updateNavOptions }); + this.MethodInfos = obj; + return this; + } + public async Task ExecuteCommandAsync() + { + if (Context == null) return false; + var result = MethodInfos.GetType().GetMethod("ExecuteCommandAsync").Invoke(MethodInfos, Array.Empty()); + return await ((Task)result).ConfigureAwait(false); + } + public bool ExecuteCommand() + { + if (Context == null) return false; + var result = MethodInfos.GetType().GetMethod("ExecuteCommand").Invoke(MethodInfos, Array.Empty()); + return (bool)result; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/UpdateProvider/UpdateableFilter.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/UpdateProvider/UpdateableFilter.cs new file mode 100644 index 000000000..8f402adb5 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/UpdateProvider/UpdateableFilter.cs @@ -0,0 +1,102 @@ +namespace SqlSugar +{ + public class UpdateableFilter where T : class, new() + { + public T[] DataList { get; set; } + public SqlSugarProvider Context { get; set; } + public int PageSize { get; internal set; } + public string TableName { get; internal set; } + public bool IsEnableDiffLogEvent { get; internal set; } + public DiffLogModel DiffModel { get; internal set; } + public List UpdateColumns { get; internal set; } + public int ExecuteCommand() + { + if (DataList.Length == 1 && DataList.First() == null) + { + return 0; + } + PageSize = 1; + var result = 0; + var isNoTran = this.Context.Ado.IsNoTran(); + try + { + if (isNoTran) + { + this.Context.Ado.BeginTran(); + } + this.Context.Utilities.PageEach(DataList, PageSize, pageItem => + { + result += SetFilterSql(this.Context.Updateable(pageItem.First()).AS(TableName).EnableDiffLogEventIF(IsEnableDiffLogEvent, DiffModel).UpdateColumns(UpdateColumns.ToArray())).ExecuteCommand(); + }); + if (isNoTran) + { + this.Context.Ado.CommitTran(); + } + } + catch (Exception) + { + if (isNoTran) + { + this.Context.Ado.RollbackTran(); + } + throw; + } + return result; + } + + + + public async Task ExecuteCommandAsync() + { + if (DataList.Length == 1 && DataList.First() == null) + { + return 0; + } + PageSize = 1; + var result = 0; + var isNoTran = this.Context.Ado.IsNoTran(); + try + { + if (isNoTran) + { + await Context.Ado.BeginTranAsync().ConfigureAwait(false); + } + await Context.Utilities.PageEachAsync(DataList, PageSize, async pageItem => + { + result += await SetFilterSql(Context.Updateable(pageItem.First()).AS(TableName).EnableDiffLogEventIF(IsEnableDiffLogEvent, DiffModel).UpdateColumns(UpdateColumns.ToArray())).ExecuteCommandAsync().ConfigureAwait(false); + }).ConfigureAwait(false); + if (isNoTran) + { + await Context.Ado.CommitTranAsync().ConfigureAwait(false); + } + } + catch (Exception) + { + if (isNoTran) + { + await Context.Ado.RollbackTranAsync().ConfigureAwait(false); + } + throw; + } + return result; + } + + + private IUpdateable SetFilterSql(IUpdateable updateable) + { + var queryable = this.Context.Queryable(); + queryable.QueryBuilder.LambdaExpressions.ParameterIndex = 10000; + var sqlobj = queryable.ToSql(); + var sql = UtilMethods.RemoveBeforeFirstWhere(sqlobj.Key); + if (sql != sqlobj.Key) + { + updateable.UpdateBuilder.AppendWhere = sql; + } + if (sqlobj.Value != null) + { + updateable.UpdateBuilder.Parameters.AddRange(sqlobj.Value); + } + return updateable; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/UpdateProvider/UpdateableHelper.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/UpdateProvider/UpdateableHelper.cs new file mode 100644 index 000000000..8057d5a20 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/UpdateProvider/UpdateableHelper.cs @@ -0,0 +1,889 @@ +using System.Data; +using System.Linq.Expressions; +using System.Text.RegularExpressions; + +namespace SqlSugar +{ + public partial class UpdateableProvider : IUpdateable where T : class, new() + { + private bool IsUpdateNullByList() + { + return this.UpdateObjs.Length > 1 && (this.UpdateBuilder.IsNoUpdateNull || this.UpdateBuilder.IsNoUpdateDefaultValue); + } + + private int DatasTrackingExecommand() + { + var trakRows = 0; + var isNoTran = this.Context.Ado.IsNoTran(); + try + { + if (isNoTran) + { + this.Context.Ado.BeginTran(); + } + int i = 0; + foreach (var item in this.UpdateObjs) + { + var newUpdateable = this.Clone(); + (newUpdateable as UpdateableProvider).UpdateObjs = new[] { item }; + newUpdateable.UpdateBuilder.IsListUpdate = null; + newUpdateable.UpdateBuilder.DbColumnInfoList = + newUpdateable.UpdateBuilder.DbColumnInfoList.Where(it => it.TableId == i).ToList(); + AppendTracking(item, newUpdateable); + if (newUpdateable.UpdateBuilder.DbColumnInfoList?.Count > 0) + { + trakRows += newUpdateable.ExecuteCommand(); + } + ++i; + } + if (isNoTran) + { + this.Context.Ado.CommitTran(); + } + } + catch (Exception) + { + if (isNoTran) + { + this.Context.Ado.RollbackTran(); + } + throw; + } + return trakRows; + } + private async Task DatasTrackingExecommandAsync() + { + var isNoTran = this.Context.Ado.IsNoTran(); + var trakRows = 0; + try + { + if (isNoTran) + { + await Context.Ado.BeginTranAsync().ConfigureAwait(false); + } + int i = 0; + foreach (var item in this.UpdateObjs) + { + var newUpdateable = this.Clone(); + (newUpdateable as UpdateableProvider).UpdateObjs = new[] { item }; + newUpdateable.UpdateBuilder.IsListUpdate = null; + newUpdateable.UpdateBuilder.DbColumnInfoList = + newUpdateable.UpdateBuilder.DbColumnInfoList.Where(it => it.TableId == i).ToList(); + AppendTracking(item, newUpdateable); + if (newUpdateable.UpdateBuilder.DbColumnInfoList?.Count > 0) + { + trakRows += await newUpdateable.ExecuteCommandAsync().ConfigureAwait(false); + } + ++i; + } + if (isNoTran) + { + await Context.Ado.CommitTranAsync().ConfigureAwait(false); + } + } + catch (Exception) + { + if (isNoTran) + { + await Context.Ado.RollbackTranAsync().ConfigureAwait(false); + } + throw; + } + return trakRows; + } + private bool UpdateObjectNotWhere() + { + if (this.Context?.CurrentConnectionConfig?.MoreSettings?.DatabaseModel == DbType.SqlServer) + { + return false; + } + return this.Context.CurrentConnectionConfig.DbType != DbType.MySql + && this.Context.CurrentConnectionConfig.DbType != DbType.MySqlConnector + && this.Context.CurrentConnectionConfig.DbType != DbType.SqlServer; + } + private void AppendTracking(T item, IUpdateable newUpdateable) + { + if (IsTrakingData() || IsTrakingDatas()) + { + var trackingData = this.Context.TempItems.FirstOrDefault(it => it.Key.StartsWith("Tracking_" + item.GetHashCode())); + var diffColumns = FastCopy.GetDiff(item, (T)trackingData.Value); + if (diffColumns.Count > 0) + { + var pks = EntityInfo.Columns + .Where(it => it.IsPrimarykey).Select(it => it.PropertyName).ToList(); + diffColumns = diffColumns.Where(it => !pks.Contains(it)).ToList(); + if (diffColumns.Count > 0) + { + newUpdateable.UpdateColumns(diffColumns.ToArray()); + } + } + else + { + (newUpdateable as UpdateableProvider).UpdateObjs = new T[] { null }; + newUpdateable.UpdateBuilder.DbColumnInfoList = new List(); + } + } + } + private void AppendSets() + { + if (SetColumnsIndex > 0) + { + var keys = UpdateBuilder.SetValues.Select(it => SqlBuilder.GetNoTranslationColumnName(it.Key.ToLower())).ToList(); + var addKeys = keys.Where(k => !this.UpdateBuilder.DbColumnInfoList.Any(it => it.PropertyName.Equals(k, StringComparison.CurrentCultureIgnoreCase) || it.DbColumnName.Equals(k, StringComparison.CurrentCultureIgnoreCase))).ToList(); + var addItems = this.EntityInfo.Columns.Where(it => !GetPrimaryKeys().Any(p => p.Equals(it.PropertyName?.ToLower(), StringComparison.CurrentCultureIgnoreCase) || p.Equals(it.DbColumnName?.ToLower(), StringComparison.CurrentCultureIgnoreCase)) && addKeys.Any(k => it.PropertyName?.ToLower() == k || it.DbColumnName?.ToLower() == k)).ToList(); + this.UpdateBuilder.DbColumnInfoList.AddRange(addItems.Select(it => new DbColumnInfo() { PropertyName = it.PropertyName, DbColumnName = it.DbColumnName })); + } + SetColumnsIndex++; + } + private string _ExecuteCommand() + { + CheckWhere(); + PreToSql(); + AutoRemoveDataCache(); + Check.ExceptionEasy(this.UpdateParameterIsNull && this.UpdateBuilder.DbColumnInfoList.Count == this.UpdateObjs.Length && this.UpdateObjs.Length == this.UpdateBuilder.DbColumnInfoList.Count(it => it.IsPrimarykey), "The primary key cannot be updated", "主键不能更新,更新主键会对代码逻辑存在未知隐患,如果非要更新:建议你删除在插入或者新建一个没主键的类。"); + Check.Exception(UpdateBuilder.WhereValues.IsNullOrEmpty() && GetPrimaryKeys().IsNullOrEmpty(), "You cannot have no primary key and no conditions"); + string sql = UpdateBuilder.ToSqlString(); + ValidateVersion(); + RestoreMapping(); + Before(sql); + return sql; + } + + private void CheckWhere() + { + if (UpdateParameterIsNull && UpdateBuilder.WhereValues.IsNullOrEmpty()) + { + Check.ExceptionEasy("Update requires conditions", "更新需要条件 Where"); + } + } + + private void _WhereColumn(string columnName) + { + var columnInfos = columns.Where(it => it.DbColumnName.Equals(columnName, StringComparison.OrdinalIgnoreCase) || it.PropertyName.Equals(columnName, StringComparison.OrdinalIgnoreCase)).ToList(); + if (!this.UpdateBuilder.DbColumnInfoList.Any(y => y.DbColumnName == columnInfos.First().DbColumnName)) + { + this.UpdateBuilder.DbColumnInfoList.AddRange(columnInfos); + } + } + + private void AutoRemoveDataCache() + { + var moreSetts = this.Context.CurrentConnectionConfig.MoreSettings; + var extService = this.Context.CurrentConnectionConfig.ConfigureExternalServices; + if (moreSetts?.IsAutoRemoveDataCache == true && extService?.DataInfoCacheService != null) + { + this.RemoveDataCache(); + } + } + internal void Init() + { + this.UpdateBuilder.TableName = EntityInfo.EntityName; + if (IsMappingTable) + { + var mappingInfo = this.Context.MappingTables.SingleOrDefault(it => it.EntityName == EntityInfo.EntityName); + if (mappingInfo != null) + { + this.UpdateBuilder.TableName = mappingInfo.DbTableName; + } + } + //Check.Exception(UpdateObjs == null || UpdateObjs.Any(), "UpdateObjs is null"); + int i = 0; + if (this.EntityInfo.Columns.Any(it => it.IsPrimarykey)) + { + this.UpdateBuilder.OldPrimaryKeys = this.EntityInfo.Columns.Where(it => it.IsPrimarykey).Select(it => it.DbColumnName).ToList(); + } + foreach (var item in UpdateObjs) + { + List updateItem = new List(); + var isDic = item is Dictionary; + if (item is Dictionary) + { + Check.ExceptionEasy("To use Updateable dictionary, use string or object", "Updateable字典请使用string,object类型"); + } + if (isDic) + { + SetUpdateItemByDic(i, item, updateItem); + } + else + { + DataAop(item); + SetUpdateItemByEntity(i, item, updateItem); + Tracking(item); + } + ++i; + } + this.columns = this.UpdateBuilder.DbColumnInfoList; + + var ignoreColumns = EntityInfo.Columns.Where(it => it.IsOnlyIgnoreUpdate).ToList(); + if (ignoreColumns != null && ignoreColumns.Count != 0) + { + this.IgnoreColumns(ignoreColumns.Select(it => it.PropertyName).ToArray()); + } + } + + private void Tracking(T item) + { + if (IsTrakingData()) + { + var trackingData = this.Context.TempItems.FirstOrDefault(it => it.Key.StartsWith("Tracking_" + item.GetHashCode())); + if (trackingData.Key == null && trackingData.Value == null) + { + return; + } + var diffColumns = FastCopy.GetDiff(item, (T)trackingData.Value); + if (diffColumns.Count > 0) + { + var pks = EntityInfo.Columns + .Where(it => it.IsPrimarykey).Select(it => it.PropertyName).ToList(); + diffColumns = diffColumns.Where(it => !pks.Contains(it)).ToList(); + if (diffColumns.Count > 0) + { + this.UpdateColumns(diffColumns.ToArray()); + } + } + else + { + this.UpdateObjs = new T[] { null }; + this.UpdateBuilder.DbColumnInfoList = new List(); + } + } + } + + private bool IsTrakingData() + { + return this.UpdateParameterIsNull == false + && this.Context.TempItems?.Any(it => it.Key.StartsWith("Tracking_")) == true + && this.UpdateObjs.Length == 1; + } + + private bool IsTrakingDatas() + { + return this.UpdateParameterIsNull == false + && this.Context.TempItems?.Any(it => it.Key.StartsWith("Tracking_")) == true + && this.UpdateObjs.Length > 1; + } + private void DataAop(T item) + { + var dataEvent = this.Context.CurrentConnectionConfig.AopEvents?.DataExecuting; + if (dataEvent != null && item != null) + { + foreach (var columnInfo in this.EntityInfo.Columns) + { + if (columnInfo.ForOwnsOnePropertyInfo != null) + { + var data = columnInfo.ForOwnsOnePropertyInfo.GetValue(item, null); + if (data != null) + { + dataEvent(columnInfo.PropertyInfo.GetValue(data, null), new DataFilterModel() { OperationType = DataFilterType.UpdateByObject, EntityValue = item, EntityColumnInfo = columnInfo }); + } + } + else if (columnInfo.PropertyInfo.Name == "Item" && columnInfo.IsIgnore) + { + //class index + } + else + { + dataEvent(columnInfo.PropertyInfo.GetValue(item, null), new DataFilterModel() { OperationType = DataFilterType.UpdateByObject, EntityValue = item, EntityColumnInfo = columnInfo }); + } + } + } + } + + private void DataChangesAop(T[] items) + { + if (typeof(T).FullName.StartsWith("System.Collections.Generic.Dictionary`")) + { + return; + } + var dataEvent = this.Context.CurrentConnectionConfig.AopEvents?.DataChangesExecuted; + if (dataEvent != null) + { + foreach (var item in items) + { + if (item != null) + { + foreach (var columnInfo in this.EntityInfo.Columns) + { + if (columnInfo.ForOwnsOnePropertyInfo != null) + { + var data = columnInfo.ForOwnsOnePropertyInfo.GetValue(item, null); + if (data != null) + { + dataEvent(columnInfo.PropertyInfo.GetValue(data, null), new DataFilterModel() { OperationType = DataFilterType.UpdateByObject, EntityValue = item, EntityColumnInfo = columnInfo }); + } + } + else if (columnInfo.PropertyInfo.Name == "Item" && columnInfo.IsIgnore) + { + //class index + } + else + { + dataEvent(columnInfo.PropertyInfo.GetValue(item, null), new DataFilterModel() { OperationType = DataFilterType.UpdateByObject, EntityValue = item, EntityColumnInfo = columnInfo }); + } + } + } + } + } + } + + private void CheckTranscodeing(bool checkIsJson = true) + { + if (this.EntityInfo.Columns.Any(it => it.IsTranscoding)) + { + Check.Exception(true, ErrorMessage.GetThrowMessage("UpdateColumns no support IsTranscoding", "SetColumns方式更新不支持IsTranscoding,你可以使用db.Updateable(实体)的方式更新")); + } + //if (checkIsJson && this.EntityInfo.Columns.Any(it => it.IsJson)) + //{ + // Check.Exception(true, ErrorMessage.GetThrowMessage("UpdateColumns no support IsJson", "SetColumns方式更新不支持IsJson,你可以使用db.Updateable(实体)的方式更新")); + //} + //if (this.EntityInfo.Columns.Any(it => it.IsArray)) + //{ + // Check.Exception(true, ErrorMessage.GetThrowMessage("UpdateColumns no support IsArray", "SetColumns方式更新不支持IsArray,你可以使用db.Updateable(实体)的方式更新")); + //} + } + private void SetUpdateItemByDic(int i, T item, List updateItem) + { + foreach (var column in (item as Dictionary).OrderBy(it => it.Key)) + { + var columnInfo = new DbColumnInfo() + { + Value = column.Value, + DbColumnName = column.Key, + PropertyName = column.Key, + PropertyType = column.Value == null ? DBNull.Value.GetType() : UtilMethods.GetUnderType(column.Value.GetType()), + TableId = i + }; + if (columnInfo.PropertyType.IsEnum()) + { + if (this.Context.CurrentConnectionConfig.MoreSettings?.TableEnumIsString == true) + { + columnInfo.PropertyType = UtilConstants.StringType; + columnInfo.Value = columnInfo.Value.ToString(); + } + else + { + columnInfo.Value = Convert.ToInt64(columnInfo.Value); + } + } + updateItem.Add(columnInfo); + } + this.UpdateBuilder.DbColumnInfoList.AddRange(updateItem); + } + private void SetUpdateItemByEntity(int i, T item, List updateItem) + { + foreach (var column in EntityInfo.Columns) + { + if (column.IsIgnore) continue; + Check.ExceptionEasy(item == null, "db.Updateable(data) data is required ", "db.Updateable(data) data不能是null"); + var columnInfo = new DbColumnInfo() + { + Value = GetValue(item, column), + DbColumnName = GetDbColumnName(column.PropertyName), + PropertyName = column.PropertyName, + PropertyType = UtilMethods.GetUnderType(column.PropertyInfo), + SqlParameterDbType = column.SqlParameterDbType, + TableId = i, + UpdateSql = column.UpdateSql, + UpdateServerTime = column.UpdateServerTime, + IsPrimarykey = column.IsPrimarykey + }; + if (column.ForOwnsOnePropertyInfo != null) + { + columnInfo.DbColumnName = column.DbColumnName; + } + if (columnInfo.PropertyType.IsEnum() && columnInfo.Value != null) + { + if (this.Context.CurrentConnectionConfig.MoreSettings?.TableEnumIsString == true) + { + columnInfo.PropertyType = UtilConstants.StringType; + columnInfo.Value = columnInfo.Value.ToString(); + } + else + { + columnInfo.Value = Convert.ToInt64(columnInfo.Value); + } + } + if (column.IsJson) + { + columnInfo.IsJson = true; + if (columnInfo.Value != null) + columnInfo.Value = this.Context.Utilities.SerializeObject(columnInfo.Value); + } + if (column.IsArray) + { + columnInfo.IsArray = true; + } + var tranColumn = EntityInfo.Columns.FirstOrDefault(it => it.IsTranscoding && it.DbColumnName.Equals(column.DbColumnName, StringComparison.CurrentCultureIgnoreCase)); + if (tranColumn != null && columnInfo.Value.HasValue()) + { + columnInfo.Value = UtilMethods.EncodeBase64(columnInfo.Value.ToString()); + } + updateItem.Add(columnInfo); + } + this.UpdateBuilder.DbColumnInfoList.AddRange(updateItem); + } + + private static object GetValue(T item, EntityColumnInfo column) + { + if (column.ForOwnsOnePropertyInfo != null) + { + var owsPropertyValue = column.ForOwnsOnePropertyInfo.GetValue(item, null); + return column.PropertyInfo.GetValue(owsPropertyValue, null); + } + else + { + return column.PropertyInfo.GetValue(item, null); + } + } + private string GetSetSql(string value, Expression> columns) + { + if (value.Contains("= \"SYSDATE\"")) + { + value = value.Replace("= \"SYSDATE\"", "= SYSDATE"); + } + var shortName = (columns as LambdaExpression).Parameters.First().Name; + var replaceKey = "," + this.SqlBuilder.GetTranslationColumnName(shortName) + "."; + var newKey = "," + this.SqlBuilder.GetTranslationColumnName(this.EntityInfo.DbTableName) + "."; + if (replaceKey != newKey) + { + value = value.Replace(replaceKey, ","); + } + return value; + } + + private void PreToSql() + { + if (this.UpdateBuilder.UpdateColumns.HasValue()) + { + var columns = this.UpdateBuilder.UpdateColumns; + this.UpdateBuilder.DbColumnInfoList = this.UpdateBuilder.DbColumnInfoList.Where(it => GetPrimaryKeys().Select( + iit => iit.ToLower()).Contains(it.DbColumnName.ToLower()) + || columns.Contains(it.PropertyName, StringComparer.OrdinalIgnoreCase) + || columns.Contains(it.DbColumnName, StringComparer.OrdinalIgnoreCase)).ToList(); + } + UpdateBuilder.EntityInfo = this.EntityInfo; + UpdateBuilder.PrimaryKeys = GetPrimaryKeys(); + if (this.IsWhereColumns) + { + foreach (var pkName in UpdateBuilder.PrimaryKeys) + { + if (WhereColumnList?.Count > 0) + { + continue; + } + var isContains = this.UpdateBuilder.DbColumnInfoList.Select(it => it.DbColumnName.ToLower()).Contains(pkName.ToLower()); + Check.Exception(isContains == false, "Use UpdateColumns().WhereColumn() ,UpdateColumns need {0}", pkName); + } + } + #region IgnoreColumns + if (this.Context.IgnoreColumns?.Any() == true) + { + var currentIgnoreColumns = this.Context.IgnoreColumns.Where(it => it.EntityName == this.EntityInfo.EntityName).ToList(); + this.UpdateBuilder.DbColumnInfoList = this.UpdateBuilder.DbColumnInfoList.Where(it => + { + return !currentIgnoreColumns.Any(i => it.PropertyName.Equals(i.PropertyName, StringComparison.CurrentCulture)); + }).ToList(); + } + #endregion + if (this.IsSingle) + { + var isDic = this.EntityInfo.DbTableName.StartsWith("Dictionary`"); + foreach (var item in this.UpdateBuilder.DbColumnInfoList) + { + if (this.UpdateBuilder.Parameters == null) this.UpdateBuilder.Parameters = new List(); + if (this.UpdateBuilder.SetValues.Any(it => this.SqlBuilder.GetNoTranslationColumnName(it.Key) == item.PropertyName)) + { + continue; + } + if (item.SqlParameterDbType is Type) + { + continue; + } + var parameter = new SugarParameter(this.SqlBuilder.SqlParameterKeyWord + item.DbColumnName, item.Value, item.PropertyType); + if (item.IsJson) + { + parameter.IsJson = true; + SqlBuilder.ChangeJsonType(parameter); + } + if (item.IsArray) + { + parameter.IsArray = true; + if (parameter.Value == null || parameter.Value == DBNull.Value) + { + ArrayNull(item, parameter); + } + } + if (item.Value == null && isDic) + { + var type = this.SqlBuilder.GetNullType(this.UpdateBuilder.GetTableNameString, item.DbColumnName); + if (type != null) + { + parameter = new SugarParameter(this.SqlBuilder.SqlParameterKeyWord + item.DbColumnName, item.Value, type); + } + } + this.UpdateBuilder.Parameters.Add(parameter); + } + } + + #region Identities + List identities = GetIdentityKeys(); + if (identities != null && identities.Count != 0) + { + this.UpdateBuilder.DbColumnInfoList.ForEach(it => + { + var mappingInfo = identities.SingleOrDefault(i => it.DbColumnName.Equals(i, StringComparison.CurrentCultureIgnoreCase)); + if (mappingInfo != null && mappingInfo.Length != 0) + { + it.IsIdentity = true; + } + }); + } + #endregion + List primaryKey = GetPrimaryKeys(); + if (primaryKey?.Count > 0) + { + this.UpdateBuilder.DbColumnInfoList.ForEach(it => + { + var mappingInfo = primaryKey.SingleOrDefault(i => it.DbColumnName.Equals(i, StringComparison.CurrentCultureIgnoreCase)); + if (mappingInfo != null && mappingInfo.Length != 0) + { + it.IsPrimarykey = true; + } + }); + } + if (this.UpdateBuilder.Parameters.HasValue() && this.UpdateBuilder.SetValues.IsValuable()) + { + this.UpdateBuilder.Parameters.RemoveAll(it => this.UpdateBuilder.SetValues.Any(v => (SqlBuilder.SqlParameterKeyWord + SqlBuilder.GetNoTranslationColumnName(v.Key)) == it.ParameterName)); + } + } + + private static void ArrayNull(DbColumnInfo item, SugarParameter parameter) + { + if (item.PropertyType.IsIn(typeof(Guid[]), typeof(Guid?[]))) + { + parameter.DbType = System.Data.DbType.Guid; + } + else if (item.PropertyType.IsIn(typeof(int[]), typeof(int?[]))) + { + parameter.DbType = System.Data.DbType.Int32; + } + else if (item.PropertyType.IsIn(typeof(long[]), typeof(long?[]))) + { + parameter.DbType = System.Data.DbType.Int64; + } + else if (item.PropertyType.IsIn(typeof(short[]), typeof(short?[]))) + { + parameter.DbType = System.Data.DbType.Int16; + } + } + private void OptRollBack(int updateRows, T updateData, object oldValue, string name) + { + if (updateRows == 0) + { + var verInfo = this.EntityInfo.Columns.FirstOrDefault(it => it.PropertyName == name); + if (verInfo != null) + { + verInfo.PropertyInfo.SetValue(updateData, oldValue); + } + } + } + private string GetDbColumnName(string propertyName) + { + if (!IsMappingColumns) + { + return propertyName; + } + if (this.Context.MappingColumns.Any(it => it.EntityName.Equals(EntityInfo.EntityName, StringComparison.CurrentCultureIgnoreCase))) + { + this.MappingColumnList = this.Context.MappingColumns.Where(it => it.EntityName.Equals(EntityInfo.EntityName, StringComparison.CurrentCultureIgnoreCase)).ToList(); + } + if (MappingColumnList == null || MappingColumnList.Count == 0) + { + return propertyName; + } + else + { + var mappInfo = this.MappingColumnList.FirstOrDefault(it => it.PropertyName.Equals(propertyName, StringComparison.CurrentCultureIgnoreCase)); + return mappInfo == null ? propertyName : mappInfo.DbColumnName; + } + } + protected List GetPrimaryKeys() + { + if (this.WhereColumnList.HasValue()) + { + return this.WhereColumnList; + } + else + { + return this.EntityInfo.Columns.Where(it => it.IsPrimarykey).Select(it => it.DbColumnName).ToList(); + } + } + protected virtual List GetIdentityKeys() + { + + return this.EntityInfo.Columns.Where(it => it.IsIdentity).Select(it => it.DbColumnName).ToList(); + } + private void RestoreMapping() + { + if (IsAs) + { + this.Context.MappingTables = OldMappingTableList; + } + } + + + private void ValidateVersion() + { + var versionColumn = this.EntityInfo.Columns.FirstOrDefault(it => it.IsEnableUpdateVersionValidation); + var pks = this.UpdateBuilder.DbColumnInfoList.Where(it => it.IsPrimarykey).ToList(); + if (versionColumn != null && this.IsVersionValidation) + { + Check.Exception(pks.IsNullOrEmpty(), "UpdateVersionValidation the primary key is required."); + List conModels = new List(); + foreach (var item in pks) + { + conModels.Add(new ConditionalModel() { CSharpTypeName = item.PropertyType.Name, FieldName = item.DbColumnName, ConditionalType = ConditionalType.Equal, FieldValue = item.Value.ObjToString() }); + } + var dbInfo = this.Context.Queryable().Where(conModels).First(); + if (dbInfo != null) + { + var currentVersion = this.EntityInfo.Type.GetProperty(versionColumn.PropertyName).GetValue(UpdateObjs.Last(), null); + var dbVersion = this.EntityInfo.Type.GetProperty(versionColumn.PropertyName).GetValue(dbInfo, null); + Check.Exception(currentVersion == null, "UpdateVersionValidation entity property {0} is not null", versionColumn.PropertyName); + Check.Exception(dbVersion == null, "UpdateVersionValidation database column {0} is not null", versionColumn.DbColumnName); + if (versionColumn.PropertyInfo.PropertyType.IsIn(UtilConstants.IntType, UtilConstants.LongType)) + { + if (Convert.ToInt64(dbVersion) != Convert.ToInt64(currentVersion)) + { + throw new VersionExceptions(string.Format("UpdateVersionValidation {0} Not the latest version ", versionColumn.PropertyName)); + } + } + else if (versionColumn.PropertyInfo.PropertyType.IsIn(UtilConstants.DateType)) + { + if (dbVersion.ObjToDate() != currentVersion.ObjToDate()) + { + throw new VersionExceptions(string.Format("UpdateVersionValidation {0} Not the latest version ", versionColumn.PropertyName)); + } + } + else if (versionColumn.PropertyInfo.PropertyType.IsIn(UtilConstants.ByteArrayType)) + { + if (UtilMethods.GetLong((byte[])dbVersion) != UtilMethods.GetLong((byte[])currentVersion)) + { + throw new VersionExceptions(string.Format("UpdateVersionValidation {0} Not the latest version ", versionColumn.PropertyName)); + } + } + else + { + Check.ThrowNotSupportedException(string.Format("UpdateVersionValidation Not Supported Type [ {0} ] , {1}", versionColumn.PropertyInfo.PropertyType, versionColumn.PropertyName)); + } + } + } + } + private void After(string sql) + { + if (this.IsEnableDiffLogEvent && !string.IsNullOrEmpty(sql)) + { + var isDisableMasterSlaveSeparation = this.Ado.IsDisableMasterSlaveSeparation; + this.Ado.IsDisableMasterSlaveSeparation = true; + var parameters = UpdateBuilder.Parameters; + if (parameters == null) + parameters = new List(); + diffModel.AfterData = GetDiffTable(sql, parameters); + diffModel.Time = this.Context.Ado.SqlExecutionTime; + if (this.Context.CurrentConnectionConfig.AopEvents.OnDiffLogEvent != null) + this.Context.CurrentConnectionConfig.AopEvents.OnDiffLogEvent(diffModel); + this.Ado.IsDisableMasterSlaveSeparation = isDisableMasterSlaveSeparation; + } + if (this.RemoveCacheFunc != null) + { + this.RemoveCacheFunc(); + } + + DataChangesAop(this.UpdateObjs); + } + private string _ExecuteCommandWithOptLock(T updateData, ref object oldVerValue) + { + Check.ExceptionEasy(UpdateParameterIsNull == true, "Optimistic lock can only be an entity update method", "乐观锁只能是实体更新方式"); + var verColumn = this.EntityInfo.Columns.FirstOrDefault(it => it.IsEnableUpdateVersionValidation); + Check.ExceptionEasy(verColumn == null, $" {this.EntityInfo.EntityName} need IsEnableUpdateVersionValidation=true ", $"实体{this.EntityInfo.EntityName}没有找到版本标识特性 IsEnableUpdateVersionValidation"); + Check.ExceptionEasy(UpdateObjs.Length > 1, $"Optimistic lock can only handle a single update ", $"乐观锁只能处理单条更新"); + Check.ExceptionEasy(!verColumn.UnderType.IsIn(UtilConstants.StringType, UtilConstants.LongType, UtilConstants.GuidType, UtilConstants.DateType), $"Optimistic locks can only be guid, long, and string types", $"乐观锁只能是Guid、Long和字符串类型"); + var oldValue = verColumn.PropertyInfo.GetValue(updateData); + oldVerValue = oldValue; + var newValue = UtilMethods.GetRandomByType(verColumn.UnderType); + verColumn.PropertyInfo.SetValue(updateData, newValue); + var data = this.UpdateBuilder.DbColumnInfoList.FirstOrDefault(it => + it.PropertyName.EqualCase(verColumn.PropertyName)); + if (data == null) + { + data = new DbColumnInfo() { DbColumnName = verColumn.DbColumnName, PropertyName = verColumn.PropertyName, Value = newValue }; + this.UpdateBuilder.DbColumnInfoList.Add(data); + } + data.Value = newValue; + var pks = GetPrimaryKeys(); + Check.ExceptionEasy(pks.Count == 0, "need primary key or WhereColumn", "需要主键或者WhereColumn"); + this.Where(verColumn.DbColumnName, "=", oldValue); + foreach (var p in pks) + { + var pkColumn = this.EntityInfo.Columns.FirstOrDefault( + it => it.DbColumnName.EqualCase(p) || it.PropertyName.EqualCase(p)); + this.Where(pkColumn.DbColumnName, "=", pkColumn.PropertyInfo.GetValue(updateData)); + } + return verColumn.PropertyName; + } + private void Before(string sql) + { + if (this.IsEnableDiffLogEvent && !string.IsNullOrEmpty(sql)) + { + var isDisableMasterSlaveSeparation = this.Ado.IsDisableMasterSlaveSeparation; + this.Ado.IsDisableMasterSlaveSeparation = true; + var parameters = UpdateBuilder.Parameters; + if (parameters == null) + parameters = new List(); + diffModel.BeforeData = GetDiffTable(sql, parameters); + diffModel.Sql = sql; + diffModel.Parameters = parameters.ToArray(); + this.Ado.IsDisableMasterSlaveSeparation = isDisableMasterSlaveSeparation; + } + } + private bool IsPrimaryKey(DbColumnInfo it) + { + var result = GetPrimaryKeys().Any(p => p.Equals(it.DbColumnName, StringComparison.CurrentCultureIgnoreCase) || p.Equals(it.PropertyName, StringComparison.CurrentCultureIgnoreCase)); + return result; + } + + internal List GetTableDiff(DataTable dt) + { + List result = new List(); + if (dt.Rows?.Count > 0) + { + foreach (DataRow row in dt.Rows) + { + DiffLogTableInfo item = new DiffLogTableInfo(); + item.TableDescription = this.EntityInfo.TableDescription; + item.TableName = this.EntityInfo.DbTableName; + item.Columns = new List(); + foreach (DataColumn col in dt.Columns) + { + try + { + var sugarColumn = this.EntityInfo.Columns.Where(it => it.DbColumnName != null).First(it => + it.DbColumnName.Equals(col.ColumnName, StringComparison.CurrentCultureIgnoreCase)); + DiffLogColumnInfo addItem = new DiffLogColumnInfo(); + addItem.Value = row[col.ColumnName]; + addItem.ColumnName = col.ColumnName; + addItem.IsPrimaryKey = sugarColumn.IsPrimarykey; + addItem.ColumnDescription = sugarColumn.ColumnDescription; + item.Columns.Add(addItem); + } + catch (Exception ex) + { + Check.ExceptionEasy(col.ColumnName + " No corresponding entity attribute found in difference log ." + ex.Message, col.ColumnName + "在差异日志中可能没有找到相应的实体属性,详细:" + ex.Message); + } + } + result.Add(item); + } + } + return result; + } + private List GetDiffTable(string sql, List parameters) + { + List result = new List(); + DataTable dt = null; + if (this.UpdateParameterIsNull) + { + var whereSql = Regex.Replace(sql, ".* WHERE ", "", RegexOptions.Singleline); + if (IsExists(sql)) + { + if (this.UpdateBuilder.SetValues != null) + { + foreach (var item in this.UpdateBuilder.SetValues) + { + if (item.Value?.Contains("SELECT") == true) + { + sql = sql.Replace(item.Value, null); + } + } + } + whereSql = UtilMethods.RemoveBeforeFirstWhere(sql); + } + dt = this.Context.Queryable().AS(this.UpdateBuilder.TableName).Filter(null, true).Where(whereSql).AddParameters(parameters).ToDataTable(); + } + else + { + if (this.UpdateObjs.ToList().Count == 0) + { + dt = new DataTable(); + } + else if (this.WhereColumnList?.Count > 0) + { + dt = this.Context.Queryable().Filter(null, true).WhereClassByWhereColumns(this.UpdateObjs.ToList(), this.WhereColumnList.ToArray()).ToDataTable(); + } + else if (this.UpdateBuilder.TableName.HasValue()) + { + dt = this.Context.Queryable().AS(this.UpdateBuilder.TableName).Filter(null, true).WhereClassByPrimaryKey(this.UpdateObjs.ToList()).ToDataTable(); + } + else + { + dt = this.Context.Queryable().Filter(null, true).WhereClassByPrimaryKey(this.UpdateObjs.ToList()).ToDataTable(); + } + } + if (dt.Rows?.Count > 0) + { + foreach (DataRow row in dt.Rows) + { + DiffLogTableInfo item = new DiffLogTableInfo(); + item.TableDescription = this.EntityInfo.TableDescription; + item.TableName = this.EntityInfo.DbTableName; + item.Columns = new List(); + foreach (DataColumn col in dt.Columns) + { + try + { + var sugarColumn = this.EntityInfo.Columns.Where(it => it.DbColumnName != null).First(it => + it.DbColumnName.Equals(col.ColumnName, StringComparison.CurrentCultureIgnoreCase)); + DiffLogColumnInfo addItem = new DiffLogColumnInfo(); + addItem.Value = row[col.ColumnName]; + addItem.ColumnName = col.ColumnName; + addItem.IsPrimaryKey = sugarColumn.IsPrimarykey; + addItem.ColumnDescription = sugarColumn.ColumnDescription; + item.Columns.Add(addItem); + } + catch (Exception ex) + { + Check.ExceptionEasy(col.ColumnName + " No corresponding entity attribute found in difference log ." + ex.Message, col.ColumnName + "在差异日志中可能没有找到相应的实体属性,详细:" + ex.Message); + } + } + result.Add(item); + } + } + return result; + } + + private static bool IsExists(string sql) + { + return UtilMethods.CountSubstringOccurrences(sql, "WHERE") > 1; + } + protected bool IsCorrectErrorSqlParameterName() + { + return this.Context?.CurrentConnectionConfig?.MoreSettings?.IsCorrectErrorSqlParameterName == true; + } + + protected void ThrowUpdateByExpression() + { + Check.Exception(UpdateParameterIsNull == true, ErrorMessage.GetThrowMessage(" no support UpdateColumns and WhereColumns", "根据表达式更新 db.Updateable() 禁止使用 UpdateColumns和WhereColumns,你可以使用 SetColumns Where 等。更新分为2种方式 1.根据表达式更新 2.根据实体或者集合更新, 具体用法请查看文档 ")); + } + protected void ThrowUpdateByObject() + { + Check.Exception(UpdateParameterIsNull == false, ErrorMessage.GetThrowMessage(" no support SetColumns and Where", "根据对像更新 db.Updateabe(对象) 禁止使用 SetColumns和Where ,你可以使用WhereColumns 和 UpdateColumns。 更新分为2种方式 1.根据表达式更新 2.根据实体或者集合更新 , 具体用法请查看文档 ")); + } + protected void ThrowUpdateByExpressionByMesage(string message) + { + Check.Exception(UpdateParameterIsNull == true, ErrorMessage.GetThrowMessage(" no support " + message, "根据表达式更新 db.Updateable()禁止使用 " + message + "。 更新分为2种方式 1.根据表达式更新 2.根据实体或者集合更新 , 具体用法请查看文档 ")); + } + private void ThrowUpdateByObjectByMesage(string message) + { + Check.Exception(UpdateParameterIsNull == false, ErrorMessage.GetThrowMessage(" no support " + message, "根据对象更新 db.Updateable(对象)禁止使用 " + message + "。 更新分为2种方式 1.根据表达式更新 2.根据实体或者集合更新 , 具体用法请查看文档 ")); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/UpdateProvider/UpdateablePage.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/UpdateProvider/UpdateablePage.cs new file mode 100644 index 000000000..5dab32102 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/UpdateProvider/UpdateablePage.cs @@ -0,0 +1,101 @@ +namespace SqlSugar +{ + public class UpdateablePage where T : class, new() + { + public T[] DataList { get; set; } + public SqlSugarProvider Context { get; set; } + public int PageSize { get; internal set; } + public string TableName { get; internal set; } + public bool IsEnableDiffLogEvent { get; internal set; } + public DiffLogModel DiffModel { get; internal set; } + public List UpdateColumns { get; internal set; } + public string[] WhereColumnList { get; internal set; } + public Dictionary ReSetValueBySqlExpList { get; internal set; } + + public UpdateableFilter EnableQueryFilter() + { + return new UpdateableFilter() + { + Context = Context, + DataList = DataList, + DiffModel = DiffModel, + IsEnableDiffLogEvent = IsEnableDiffLogEvent, + PageSize = PageSize, + TableName = TableName, + UpdateColumns = UpdateColumns + }; + } + public int ExecuteCommand() + { + if (DataList.Length == 1 && DataList.First() == null) + { + return 0; + } + if (PageSize == 0) { PageSize = 1000; } + var result = 0; + var isNoTran = this.Context.Ado.IsNoTran(); + try + { + if (isNoTran) + { + this.Context.Ado.BeginTran(); + } + this.Context.Utilities.PageEach(DataList, PageSize, pageItem => + { + var updateable = this.Context.Updateable(pageItem).AS(TableName); + updateable.UpdateBuilder.ReSetValueBySqlExpList = this.ReSetValueBySqlExpList; + result += updateable.WhereColumns(WhereColumnList).EnableDiffLogEventIF(IsEnableDiffLogEvent, DiffModel).UpdateColumns(UpdateColumns.ToArray()).ExecuteCommand(); + }); + if (isNoTran) + { + this.Context.Ado.CommitTran(); + } + } + catch (Exception) + { + if (isNoTran) + { + this.Context.Ado.RollbackTran(); + } + throw; + } + return result; + } + public async Task ExecuteCommandAsync() + { + if (DataList.Length == 1 && DataList.First() == null) + { + return 0; + } + if (PageSize == 0) { PageSize = 1000; } + var result = 0; + var isNoTran = this.Context.Ado.IsNoTran(); + try + { + if (isNoTran) + { + await Context.Ado.BeginTranAsync().ConfigureAwait(false); + } + await Context.Utilities.PageEachAsync(DataList, PageSize, async pageItem => + { + var updateable = Context.Updateable(pageItem); + updateable.UpdateBuilder.ReSetValueBySqlExpList = ReSetValueBySqlExpList; + result += await updateable.AS(TableName).WhereColumns(WhereColumnList).EnableDiffLogEventIF(IsEnableDiffLogEvent, DiffModel).UpdateColumns(UpdateColumns.ToArray()).ExecuteCommandAsync().ConfigureAwait(false); + }).ConfigureAwait(false); + if (isNoTran) + { + await Context.Ado.CommitTranAsync().ConfigureAwait(false); + } + } + catch (Exception) + { + if (isNoTran) + { + await Context.Ado.RollbackTranAsync().ConfigureAwait(false); + } + throw; + } + return result; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/UpdateProvider/UpdateableProvider.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/UpdateProvider/UpdateableProvider.cs new file mode 100644 index 000000000..439418899 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/UpdateProvider/UpdateableProvider.cs @@ -0,0 +1,1015 @@ +using System.Data; +using System.Linq.Expressions; +using System.Text.RegularExpressions; + +namespace SqlSugar +{ + public partial class UpdateableProvider : IUpdateable where T : class, new() + { + #region Property + public SqlSugarProvider Context { get; internal set; } + public EntityInfo EntityInfo { get; internal set; } + public ISqlBuilder SqlBuilder { get; internal set; } + public UpdateBuilder UpdateBuilder { get; set; } + public IAdo Ado { get { return Context.Ado; } } + public T[] UpdateObjs { get; set; } + /// + /// true : by expression update + /// false: by object update + /// + public bool UpdateParameterIsNull { get; set; } + public bool IsMappingTable { get { return this.Context.MappingTables?.Any() == true; } } + public bool IsMappingColumns { get { return this.Context.MappingColumns?.Any() == true; } } + public bool IsSingle { get { return this.UpdateObjs.Length == 1; } } + public List MappingColumnList { get; set; } + private List IgnoreColumnNameList { get; set; } + internal List WhereColumnList { get; set; } + private bool IsWhereColumns { get; set; } + private bool IsOffIdentity { get; set; } + private bool IsVersionValidation { get; set; } + public MappingTableList OldMappingTableList { get; set; } + public bool IsAs { get; set; } + public bool IsEnableDiffLogEvent { get; set; } + public DiffLogModel diffModel { get; set; } + internal Action RemoveCacheFunc { get; set; } + private int SetColumnsIndex { get; set; } + private List columns { get; set; } + #endregion + + #region Core + public virtual string ToSqlString() + { + var sqlObj = this.ToSql(); + var result = sqlObj.Key; + if (result == null) return null; + result = UtilMethods.GetSqlString(this.Context.CurrentConnectionConfig, sqlObj); + return result; + } + + public KeyValuePair> ToSql() + { + PreToSql(); + string sql = UpdateBuilder.ToSqlString(); + RestoreMapping(); + return new KeyValuePair>(sql, UpdateBuilder.Parameters); + } + public void AddQueue() + { + var sqlObj = this.ToSql(); + this.Context.Queues.Add(sqlObj.Key, sqlObj.Value); + } + public virtual int ExecuteCommandWithOptLockIF(bool? IsVersionValidation, bool? IsOptLock = null) + { + if (IsOptLock == true) + { + return ExecuteCommandWithOptLock(IsVersionValidation ?? false); + } + else + { + return this.ExecuteCommand(); + } + } + public virtual int ExecuteCommandWithOptLock(bool IsVersionValidation = false) + { + Check.ExceptionEasy(UpdateObjs?.Length > 1, " OptLock can only be used on a single object, and the argument cannot be List", "乐观锁只能用于单个对象,参数不能是List,如果是一对多操作请更新主表统一用主表验证"); + var updateData = UpdateObjs.FirstOrDefault(); + if (updateData == null) return 0; + object oldValue = null; + var name = _ExecuteCommandWithOptLock(updateData, ref oldValue); + var result = this.ExecuteCommand(); + OptRollBack(result, updateData, oldValue, name); + if (result == 0 && IsVersionValidation) + { + throw new VersionExceptions(string.Format("UpdateVersionValidation {0} Not the latest version ", name)); + } + return result; + } + + public virtual int ExecuteCommand() + { + //if (this.UpdateBuilder.UpdateColumns.HasValue()) + //{ + // var columns = this.UpdateBuilder.UpdateColumns; + // this.UpdateBuilder.DbColumnInfoList = this.UpdateBuilder.DbColumnInfoList.Where(it => GetPrimaryKeys().Select(iit => iit.ToLower()).Contains(it.DbColumnName.ToLower()) || columns.Contains(it.PropertyName, StringComparer.OrdinalIgnoreCase)).ToList(); + //} + if (this.IsTrakingDatas() || IsUpdateNullByList()) + { + int trakRows = DatasTrackingExecommand(); + return trakRows; + } + string sql = _ExecuteCommand(); + if (this.UpdateBuilder.AppendWhere.HasValue()) + { + sql += " AND " + this.UpdateBuilder.AppendWhere; + } + if (string.IsNullOrEmpty(sql)) + { + return 0; + } + var result = 0; + if (this.Context.CurrentConnectionConfig?.MoreSettings?.IsCorrectErrorSqlParameterName == true) + { + UpdateBuilder.Parameters = UpdateBuilder.Parameters.Where(it => UtilMethods.NoErrorParameter(it.ParameterName)).ToList(); + } + List oldParas = null; + if (IsEnableDiffLogEvent) + { + oldParas = UtilMethods.CopySugarParameters(UpdateBuilder.Parameters); + } + if (sql != Environment.NewLine) + { + result = this.Ado.ExecuteCommand(sql, UpdateBuilder.Parameters == null ? null : UpdateBuilder.Parameters.ToArray()); + } + if (oldParas != null && UpdateBuilder.Parameters != null) + { + if (string.Join(",", oldParas.Select(it => it.ParameterName)) != string.Join(",", UpdateBuilder.Parameters.Select(it => it.ParameterName))) + { + UpdateBuilder.Parameters = oldParas; + } + } + After(sql); + return result; + } + public bool ExecuteCommandHasChange() + { + return this.ExecuteCommand() > 0; + } + + public virtual async Task ExecuteCommandWithOptLockAsync(bool IsVersionValidation = false) + { + Check.ExceptionEasy(UpdateObjs?.Length > 1, " OptLock can only be used on a single object, and the argument cannot be List", "乐观锁只能用于单个对象,参数不能是List,如果是一对多操作请更新主表统一用主表验证"); + var updateData = UpdateObjs.FirstOrDefault(); + if (updateData == null) return 0; + object oldValue = null; + var name = _ExecuteCommandWithOptLock(updateData, ref oldValue); + var result = await ExecuteCommandAsync().ConfigureAwait(false); + OptRollBack(result, updateData, oldValue, name); + if (result == 0 && IsVersionValidation) + { + throw new VersionExceptions(string.Format("UpdateVersionValidation {0} Not the latest version ", name)); + } + return result; + } + public Task ExecuteCommandAsync(CancellationToken token) + { + this.Context.Ado.CancellationToken = token; + return ExecuteCommandAsync(); + } + public virtual async Task ExecuteCommandAsync() + { + //if (this.UpdateBuilder.UpdateColumns.HasValue()) + //{ + // var columns = this.UpdateBuilder.UpdateColumns; + // this.UpdateBuilder.DbColumnInfoList = this.UpdateBuilder.DbColumnInfoList.Where(it => GetPrimaryKeys().Select(iit => iit.ToLower()).Contains(it.DbColumnName.ToLower()) || columns.Contains(it.PropertyName, StringComparer.OrdinalIgnoreCase)).ToList(); + //} + if (this.IsTrakingDatas() || IsUpdateNullByList()) + { + int trakRows = await DatasTrackingExecommandAsync().ConfigureAwait(false); + return trakRows; + } + string sql = _ExecuteCommand(); + if (this.UpdateBuilder.AppendWhere.HasValue()) + { + sql += " AND " + this.UpdateBuilder.AppendWhere; + } + if (string.IsNullOrEmpty(sql)) + { + return 0; + } + var result = await Ado.ExecuteCommandAsync(sql, UpdateBuilder.Parameters == null ? null : UpdateBuilder.Parameters.ToArray()).ConfigureAwait(false); + After(sql); + return result; + } + public Task ExecuteCommandHasChangeAsync(CancellationToken token) + { + this.Context.Ado.CancellationToken = token; + return ExecuteCommandHasChangeAsync(); + } + + public async Task ExecuteCommandHasChangeAsync() + { + return await ExecuteCommandAsync().ConfigureAwait(false) > 0; + } + #endregion + + #region Common + public UpdateablePage PageSize(int pageSize) + { + ThrowUpdateByExpressionByMesage(" PageSize(num) "); + UpdateablePage result = new UpdateablePage(); + result.PageSize = pageSize; + result.Context = this.Context; + result.DataList = this.UpdateObjs; + result.TableName = this.UpdateBuilder.TableName; + result.IsEnableDiffLogEvent = this.IsEnableDiffLogEvent; + result.WhereColumnList = this.WhereColumnList?.ToArray(); + result.DiffModel = this.diffModel; + result.ReSetValueBySqlExpList = this.UpdateBuilder.ReSetValueBySqlExpList; + if (this.UpdateBuilder.DbColumnInfoList.Count != 0) + result.UpdateColumns = this.UpdateBuilder.DbColumnInfoList.GroupBy(it => it.TableId).First().Select(it => it.DbColumnName).ToList(); + if (this.UpdateBuilder?.UpdateColumns?.Count > 0) + result.UpdateColumns = this.UpdateBuilder.UpdateColumns; + return result; + } + public IUpdateable InnerJoin(Expression> joinExpress) + { + UpdateableProvider result = new UpdateableProvider(); + result.updateableObj = this; + var querybale = this.Context.Queryable().LeftJoin(joinExpress); + result.updateableObj.UpdateBuilder.JoinInfos = querybale.QueryBuilder.JoinQueryInfos; + result.updateableObj.UpdateBuilder.ShortName = joinExpress.Parameters.FirstOrDefault()?.Name; + return result; + } + public IUpdateable InnerJoin(ISugarQueryable queryable, Expression> joinExpress) + { + var tableName = $" ({queryable.Clone().ToSqlString()}) "; ; + return this.InnerJoin(joinExpress, tableName); + } + public IUpdateable InnerJoin(Expression> joinExpress, string TableName) + { + UpdateableProvider result = new UpdateableProvider(); + result.updateableObj = this; + var querybale = this.Context.Queryable().LeftJoin(joinExpress); + result.updateableObj.UpdateBuilder.JoinInfos = querybale.QueryBuilder.JoinQueryInfos; + result.updateableObj.UpdateBuilder.ShortName = joinExpress.Parameters.FirstOrDefault()?.Name; + result.updateableObj.UpdateBuilder.TableName = TableName; + return result; + } + public IUpdateable Clone() + { + this.Context.SugarActionType = SugarActionType.Update; + var result = InstanceFactory.GetUpdateableProvider(this.Context.CurrentConnectionConfig); + var sqlBuilder = InstanceFactory.GetSqlbuilder(this.Context.CurrentConnectionConfig); ; + result.Context = this.Context; + result.EntityInfo = this.Context.EntityMaintenance.GetEntityInfo(); + result.SqlBuilder = sqlBuilder; + result.SqlBuilder.Context = this.Context; + result.UpdateObjs = UpdateObjs; + result.WhereColumnList = this.WhereColumnList; + result.IsWhereColumns = this.IsWhereColumns; + result.IgnoreColumnNameList = this.IgnoreColumnNameList; + result.IsAs = this.IsAs; + result.IsOffIdentity = this.IsOffIdentity; + result.IsEnableDiffLogEvent = this.IsEnableDiffLogEvent; + result.diffModel = this.diffModel; + result.UpdateParameterIsNull = this.UpdateParameterIsNull; + result.RemoveCacheFunc = this.RemoveCacheFunc; + result.SetColumnsIndex = this.SetColumnsIndex; + result.OldMappingTableList = this.OldMappingTableList; + result.MappingColumnList = this.MappingColumnList; + result.columns = this.columns.ToList(); + result.UpdateBuilder = InstanceFactory.GetUpdateBuilder(this.Context.CurrentConnectionConfig); + result.UpdateBuilder.Builder = sqlBuilder; + result.UpdateBuilder.LambdaExpressions = InstanceFactory.GetLambdaExpressions(this.Context.CurrentConnectionConfig); + result.Context = this.Context; + result.UpdateBuilder.DbColumnInfoList = this.UpdateBuilder.DbColumnInfoList.ToList(); + result.UpdateBuilder.TableName = this.UpdateBuilder.TableName; + result.UpdateBuilder.WhereValues = this.UpdateBuilder?.WhereValues?.ToList(); + result.UpdateBuilder.Parameters = this.UpdateBuilder?.Parameters?.ToList(); + result.UpdateBuilder.IsListUpdate = this.UpdateBuilder.IsListUpdate; + result.UpdateBuilder.IsWhereColumns = this.UpdateBuilder.IsWhereColumns; + result.UpdateBuilder.WhereValues = this.UpdateBuilder?.WhereValues?.ToList(); + result.UpdateBuilder.IsNoUpdateDefaultValue = this.UpdateBuilder.IsNoUpdateDefaultValue; + result.UpdateBuilder.IsNoUpdateNull = this.UpdateBuilder.IsNoUpdateNull; + result.UpdateBuilder.SetValues = this.UpdateBuilder?.SetValues?.ToList(); + result.UpdateBuilder.UpdateColumns = this.UpdateBuilder?.UpdateColumns?.ToList(); + result.UpdateBuilder.Context = this.Context; + return result; + } + public IUpdateable With(string lockString) + { + if (this.Context.CurrentConnectionConfig.DbType == DbType.SqlServer) + this.UpdateBuilder.TableWithString = lockString; + return this; + } + public SplitTableUpdateProvider SplitTable(Func, IEnumerable> getTableNamesFunc) + { + UtilMethods.StartCustomSplitTable(this.Context, typeof(T)); + this.Context.MappingTables.Add(this.EntityInfo.EntityName, this.EntityInfo.DbTableName); + SplitTableUpdateProvider result = new SplitTableUpdateProvider(); + result.Context = this.Context; + SplitTableContext helper = new SplitTableContext(Context) + { + EntityInfo = this.EntityInfo + }; + var tables = getTableNamesFunc(helper.GetTables()); + result.Tables = tables; + result.updateobj = this; + return result; + } + public SplitTableUpdateByObjectProvider SplitTable() + { + UtilMethods.StartCustomSplitTable(this.Context, typeof(T)); + Check.ExceptionEasy(UpdateParameterIsNull, "SplitTable() not supported db.Updateable(),use db.Updateable(list)", ".SplitTable()不支持 db.Updateable()方式更新,请使用 db.Updateable(list) 对象方式更新, 或者使用 .SplitTable(+1)重载"); + SplitTableUpdateByObjectProvider result = new SplitTableUpdateByObjectProvider(); + result.Context = this.Context; + result.UpdateObjects = this.UpdateObjs; + result.IsEnableDiffLogEvent = this.IsEnableDiffLogEvent; + result.BusinessData = this.diffModel?.BusinessData; + if (this.IsWhereColumns) + result.WhereColumns = this.WhereColumnList; + SplitTableContext helper = new SplitTableContext(Context) + { + EntityInfo = this.EntityInfo + }; + result.updateobj = this; + return result; + } + public IUpdateable RemoveDataCache() + { + this.RemoveCacheFunc = () => + { + var cacheService = this.Context.CurrentConnectionConfig.ConfigureExternalServices.DataInfoCacheService; + CacheSchemeMain.RemoveCache(cacheService, this.Context.EntityMaintenance.GetTableName()); + }; + return this; + } + public IUpdateable RemoveDataCache(string likeString) + { + this.RemoveCacheFunc = () => + { + var cacheService = this.Context.CurrentConnectionConfig.ConfigureExternalServices.DataInfoCacheService; + CacheSchemeMain.RemoveCacheByLike(cacheService, likeString); + }; + return this; + } + public IUpdateable IsEnableUpdateVersionValidation() + { + this.IsVersionValidation = true; + return this; + } + public IUpdateable AsType(Type tableNameType) + { + return AS(this.Context.EntityMaintenance.GetEntityInfo(tableNameType).DbTableName); + } + public IUpdateable AS(string tableName) + { + //if (tableName == null) return this; + //var entityName = typeof(T).Name; + //IsAs = true; + //OldMappingTableList = this.Context.MappingTables; + //this.Context.MappingTables = this.Context.Utilities.TranslateCopy(this.Context.MappingTables); + //if (this.Context.MappingTables.Any(it => it.EntityName == entityName)) + //{ + // this.Context.MappingTables.Add(this.Context.MappingTables.First(it => it.EntityName == entityName).DbTableName, tableName); + //} + //this.Context.MappingTables.Add(entityName, tableName); + this.UpdateBuilder.TableName = tableName; + if (tableName.IsNullOrEmpty()) + this.UpdateBuilder.TableName = this.EntityInfo.DbTableName; + return this; ; + } + public IUpdateable EnableDiffLogEventIF(bool isEnableDiffLog, object businessData = null) + { + if (isEnableDiffLog) + { + return EnableDiffLogEvent(businessData); + } + return this; + } + public IUpdateable EnableDiffLogEvent(object businessData = null) + { + //Check.Exception(this.UpdateObjs.HasValue() && this.UpdateObjs.Count() > 1, "DiffLog does not support batch operations"); + diffModel = new DiffLogModel(); + this.IsEnableDiffLogEvent = true; + diffModel.BusinessData = businessData; + diffModel.DiffType = DiffType.update; + return this; + } + + + + public IUpdateable IgnoreColumns(bool ignoreAllNullColumns, bool isOffIdentity = false, bool ignoreAllDefaultValue = false) + { + //Check.Exception(this.UpdateObjs.Count() > 1 && ignoreAllNullColumns, ErrorMessage.GetThrowMessage("ignoreNullColumn NoSupport batch insert", "ignoreNullColumn 不支持批量操作")); + UpdateBuilder.IsOffIdentity = isOffIdentity; + if (this.UpdateBuilder.LambdaExpressions == null) + this.UpdateBuilder.LambdaExpressions = InstanceFactory.GetLambdaExpressions(this.Context.CurrentConnectionConfig); + this.UpdateBuilder.IsNoUpdateNull = ignoreAllNullColumns; + this.UpdateBuilder.IsNoUpdateDefaultValue = ignoreAllDefaultValue; + return this; + } + public IUpdateable IgnoreColumns(Expression> columns) + { + var ignoreColumns = UpdateBuilder.GetExpressionValue(columns, ResolveExpressType.ArraySingle).GetResultArray().Select(it => this.SqlBuilder.GetNoTranslationColumnName(it).ToLower()).ToList(); + this.UpdateBuilder.DbColumnInfoList = this.UpdateBuilder.DbColumnInfoList.Where(it => !ignoreColumns.Contains(it.PropertyName.ToLower())).ToList(); + this.UpdateBuilder.DbColumnInfoList = this.UpdateBuilder.DbColumnInfoList.Where(it => !ignoreColumns.Contains(it.DbColumnName.ToLower())).ToList(); + this.UpdateBuilder.IgnoreColumns = ignoreColumns; + return this; + } + public IUpdateable IgnoreColumnsIF(bool IsIgnore, Expression> columns) + { + if (IsIgnore) this.IgnoreColumns(columns); + return this; + } + public IUpdateable IgnoreNullColumns(bool isIgnoreNull = true) + { + if (isIgnoreNull) + { + return IgnoreColumns(isIgnoreNull); + } + else + { + return this; + } + } + public IUpdateable IgnoreColumns(string[] columns) + { + if (columns.HasValue()) + { + var ignoreColumns = columns.Select(it => it.ToLower()).ToList(); + this.UpdateBuilder.IgnoreColumns = ignoreColumns; + this.UpdateBuilder.DbColumnInfoList = this.UpdateBuilder.DbColumnInfoList.Where(it => !ignoreColumns.Contains(it.PropertyName.ToLower())).ToList(); + this.UpdateBuilder.DbColumnInfoList = this.UpdateBuilder.DbColumnInfoList.Where(it => !ignoreColumns.Contains(it.DbColumnName.ToLower())).ToList(); + } + return this; + } + + + public IUpdateable ReSetValue(Action setValueExpression) + { + ThrowUpdateByExpression(); + if (this.UpdateObjs.HasValue()) + { + var oldColumns = this.UpdateBuilder.DbColumnInfoList.Select(it => it.PropertyName).ToList(); + foreach (var item in UpdateObjs) + { + setValueExpression(item); + } + this.UpdateBuilder.DbColumnInfoList = new List(); + Init(); + this.UpdateBuilder.DbColumnInfoList = this.UpdateBuilder.DbColumnInfoList.Where(it => oldColumns.Contains(it.PropertyName)).ToList(); + } + return this; + } + public IUpdateable PublicSetColumns(Expression> filedNameExpression, string computationalSymbol) + { + if (UpdateParameterIsNull == true) + { + Check.Exception(UpdateParameterIsNull == true, ErrorMessage.GetThrowMessage("The PublicSetColumns(exp,string) overload can only be used to update entity objects: db.Updateable(object)", "PublicSetColumns(exp,string)重载只能用在实体对象更新:db.Updateable(对象),请区分表达式更新和实体对象更不同用法 ")); + } + else + { + var name = ExpressionTool.GetMemberName(filedNameExpression); + if (name == null) + { + Check.ExceptionEasy(filedNameExpression + " format error ", filedNameExpression + "参数格式错误"); + } + //var value = this.UpdateBuilder.GetExpressionValue(ValueExpExpression, ResolveExpressType.WhereSingle).GetResultString(); + if (this.UpdateBuilder.ReSetValueBySqlExpList == null) + { + this.UpdateBuilder.ReSetValueBySqlExpList = new Dictionary(); + } + if (!this.UpdateBuilder.ReSetValueBySqlExpList.ContainsKey(name)) + { + this.UpdateBuilder.ReSetValueBySqlExpList.Add(name, new ReSetValueBySqlExpListModel() + { + Type = ReSetValueBySqlExpListModelType.List, + Sql = computationalSymbol, + DbColumnName = this.SqlBuilder.GetTranslationColumnName(this.EntityInfo.Columns.First(it => it.PropertyName == name).DbColumnName) + }); + } + } + return this; + } + + public IUpdateable PublicSetColumns(Expression> filedNameExpression, Expression> ValueExpExpression) + { + if (UpdateParameterIsNull == true) + { + return SetColumns(filedNameExpression, ValueExpExpression); + } + else + { + var name = ExpressionTool.GetMemberName(filedNameExpression); + if (name == null) + { + Check.ExceptionEasy(filedNameExpression + " format error ", filedNameExpression + "参数格式错误"); + } + var value = this.UpdateBuilder.GetExpressionValue(ValueExpExpression, ResolveExpressType.WhereSingle).GetResultString(); + if (this.UpdateBuilder.ReSetValueBySqlExpList == null) + { + this.UpdateBuilder.ReSetValueBySqlExpList = new Dictionary(); + } + if (!this.UpdateBuilder.ReSetValueBySqlExpList.ContainsKey(name)) + { + this.UpdateBuilder.ReSetValueBySqlExpList.Add(name, new ReSetValueBySqlExpListModel() + { + Sql = value, + DbColumnName = this.SqlBuilder.GetTranslationColumnName(this.EntityInfo.Columns.First(it => it.PropertyName == name).DbColumnName) + }); ; + } + } + return this; + } + #endregion + + #region Update by object + public IUpdateable CallEntityMethod(Expression> method) + { + ThrowUpdateByExpression(); + if (this.UpdateObjs.HasValue()) + { + var oldColumns = this.UpdateBuilder.DbColumnInfoList.Select(it => it.PropertyName).ToList(); + var expression = (LambdaExpression.Lambda(method).Body as LambdaExpression).Body; + Check.Exception(!(expression is MethodCallExpression), method.ToString() + " is not method"); + var callExpresion = expression as MethodCallExpression; + UtilMethods.DataInoveByExpresson(this.UpdateObjs, callExpresion); + this.UpdateBuilder.DbColumnInfoList = new List(); + Init(); + this.UpdateBuilder.DbColumnInfoList = this.UpdateBuilder.DbColumnInfoList.Where(it => oldColumns.Contains(it.PropertyName)).ToList(); + } + return this; + } + + public IUpdateable WhereColumns(Expression> columns) + { + ThrowUpdateByExpression(); + this.IsWhereColumns = true; + UpdateBuilder.IsWhereColumns = true; + Check.Exception(UpdateParameterIsNull == true, "Updateable().Updateable is error,Use Updateable(obj).WhereColumns"); + var whereColumns = UpdateBuilder.GetExpressionValue(columns, ResolveExpressType.ArraySingle).GetResultArray().Select(it => this.SqlBuilder.GetNoTranslationColumnName(it)).ToList(); + if (this.WhereColumnList == null) this.WhereColumnList = new List(); + foreach (var item in whereColumns) + { + _WhereColumn(item); + var columnInfo = this.EntityInfo.Columns.FirstOrDefault(it => it.PropertyName.EqualCase(item)); + if (columnInfo != null) + { + this.WhereColumnList.Add(columnInfo.DbColumnName); + } + else + { + this.WhereColumnList.Add(item); + } + } + return this; + } + public IUpdateable WhereColumns(string columnName) + { + + ThrowUpdateByExpression(); + if (this.WhereColumnList == null) this.WhereColumnList = new List(); + _WhereColumn(columnName); + this.WhereColumnList.Add(columnName); + return this; + } + + public IUpdateable WhereColumns(string[] columnNames) + { + if (columnNames == null) return this; + + ThrowUpdateByExpression(); + if (this.WhereColumnList == null) this.WhereColumnList = new List(); + foreach (var columnName in columnNames) + { + _WhereColumn(columnName); + this.WhereColumnList.Add(columnName); + } + return this; + } + public IUpdateable UpdateColumns(Expression> columns, bool appendColumnsByDataFilter) + { + ThrowUpdateByExpression(); + var updateColumns = UpdateBuilder.GetExpressionValue(columns, ResolveExpressType.ArraySingle).GetResultArray().Select(it => this.SqlBuilder.GetNoTranslationColumnName(it)).ToList(); + return UpdateColumns(updateColumns.ToArray(), appendColumnsByDataFilter); + } + public IUpdateable UpdateColumns(Expression> columns) + { + ThrowUpdateByExpression(); + var updateColumns = UpdateBuilder.GetExpressionValue(columns, ResolveExpressType.ArraySingle).GetResultArray().Select(it => this.SqlBuilder.GetNoTranslationColumnName(it)).ToList(); + if (this.UpdateBuilder.UpdateColumns == null) + { + this.UpdateBuilder.UpdateColumns = new List(); + } + this.UpdateBuilder.UpdateColumns.AddRange(updateColumns); + //List primaryKeys = GetPrimaryKeys(); + //foreach (var item in this.UpdateBuilder.DbColumnInfoList) + //{ + // var mappingInfo = primaryKeys.SingleOrDefault(i => item.DbColumnName.Equals(i, StringComparison.CurrentCultureIgnoreCase)); + // if (mappingInfo != null && mappingInfo.Any()) + // { + // item.IsPrimarykey = true; + // } + //} + //this.UpdateBuilder.DbColumnInfoList = this.UpdateBuilder.DbColumnInfoList.Where(it => updateColumns.Any(uc => uc.Equals(it.PropertyName, StringComparison.CurrentCultureIgnoreCase) || uc.Equals(it.DbColumnName, StringComparison.CurrentCultureIgnoreCase)) || it.IsPrimarykey || it.IsIdentity).ToList(); + return this; + } + public IUpdateable UpdateColumns(string[] columns, bool appendColumnsByDataFilter) + { + List updateColumns = new List(); + if (appendColumnsByDataFilter) + { + var newData = new T() { }; + UtilMethods.ClearPublicProperties(newData, this.EntityInfo); + var data = ((UpdateableProvider)this.Context.Updateable(newData)).UpdateObjs.First(); + foreach (var item in this.EntityInfo.Columns.Where(it => !it.IsPrimarykey && !it.IsIgnore && !it.IsOnlyIgnoreUpdate)) + { + var value = item.PropertyInfo.GetValue(data); + if (value?.Equals("") == false) + { + if (!value.Equals(UtilMethods.GetDefaultValue(item.UnderType))) + { + updateColumns.Add(item.PropertyName); + } + } + } + } + updateColumns.AddRange(columns); + return UpdateColumns(updateColumns.ToArray()); + } + public IUpdateable UpdateColumns(string[] columns) + { + if (columns == null || columns.Length == 0) return this; + ThrowUpdateByExpression(); + if (this.UpdateBuilder.UpdateColumns == null) + { + this.UpdateBuilder.UpdateColumns = new List(); + } + this.UpdateBuilder.UpdateColumns.AddRange(columns); + //if (columns.HasValue()) + //{ + + // //this.UpdateBuilder.DbColumnInfoList = this.UpdateBuilder.DbColumnInfoList.Where(it => GetPrimaryKeys().Select(iit => iit.ToLower()).Contains(it.DbColumnName.ToLower()) || columns.Contains(it.PropertyName, StringComparer.OrdinalIgnoreCase)).ToList(); + //} + return this; + } + public IUpdateable UpdateColumnsIF(bool isUpdateColumns, Expression> columns) + { + ThrowUpdateByExpression(); + if (isUpdateColumns) + UpdateColumns(columns); + return this; + } + public IUpdateable UpdateColumnsIF(bool isUpdateColumns, string[] columns) + { + ThrowUpdateByExpression(); + if (isUpdateColumns) + UpdateColumns(columns); + return this; + } + #endregion + + #region Update by expression + public IUpdateable EnableQueryFilter() + { + try + { + ThrowUpdateByObject(); + } + catch + { + Check.ExceptionEasy("Updateable(obj) no support, use Updateable().SetColumn ", "更新过滤器只能用在表达式方式更新 ,更新分为实体更新和表达式更新 。正确用法 Updateable().SetColum(..).Where(..)"); + } + var queryable = this.Context.Queryable(); + queryable.QueryBuilder.LambdaExpressions.ParameterIndex = 1000; + var sqlable = queryable.ToSql(); + var whereInfos = Regex.Split(sqlable.Key, " Where ", RegexOptions.IgnoreCase); + if (whereInfos.Length > 1) + { + this.Where(whereInfos.Last(), sqlable.Value); + } + return this; + } + public virtual IUpdateable SetColumns(string fieldName, object fieldValue) + { + ThrowUpdateByObject(); + var columnInfo = this.EntityInfo.Columns.FirstOrDefault(it => it.PropertyName.EqualCase(fieldName)); + if (columnInfo != null) + { + fieldName = columnInfo.DbColumnName; + } + var parameterName = this.SqlBuilder.SqlParameterKeyWord + "Const" + this.UpdateBuilder.LambdaExpressions.ParameterIndex; + this.UpdateBuilder.LambdaExpressions.ParameterIndex = this.UpdateBuilder.LambdaExpressions.ParameterIndex + 1; + if (UpdateBuilder.Parameters == null) + { + UpdateBuilder.Parameters = new List(); + } + UpdateBuilder.Parameters.Add(new SugarParameter(parameterName, fieldValue)); + if (columnInfo?.UpdateServerTime == true) + { + var nowTime = this.Context.Queryable().QueryBuilder.LambdaExpressions.DbMehtods.GetDate(); + UpdateBuilder.SetValues.Add(new KeyValuePair(SqlBuilder.GetTranslationColumnName(fieldName), $"{SqlBuilder.GetTranslationColumnName(fieldName)}={nowTime}")); + } + else + { + UpdateBuilder.SetValues.Add(new KeyValuePair(SqlBuilder.GetTranslationColumnName(fieldName), $"{SqlBuilder.GetTranslationColumnName(fieldName)}={parameterName}")); + } + this.UpdateBuilder.DbColumnInfoList = this.UpdateBuilder.DbColumnInfoList.Where(it => (UpdateParameterIsNull == false && IsPrimaryKey(it)) || UpdateBuilder.SetValues.Any(v => SqlBuilder.GetNoTranslationColumnName(v.Key).Equals(it.DbColumnName, StringComparison.CurrentCultureIgnoreCase) || SqlBuilder.GetNoTranslationColumnName(v.Key).Equals(it.PropertyName, StringComparison.CurrentCultureIgnoreCase)) || it.IsPrimarykey == true).ToList(); + if (!this.UpdateBuilder.DbColumnInfoList.Any(it => it.DbColumnName.EqualCase(fieldName))) + { + this.UpdateBuilder.DbColumnInfoList.Add(new DbColumnInfo() + { + DbColumnName = fieldName, + Value = fieldValue, + PropertyName = fieldName, + PropertyType = fieldValue?.GetType() + }); + } + AppendSets(); + return this; + } + public IUpdateable SetColumnsIF(bool isUpdateColumns, Expression> filedNameExpression, object fieldValue) + { + if (isUpdateColumns) + { + return SetColumns(filedNameExpression, fieldValue); + } + else + { + return this; + } + } + public virtual IUpdateable SetColumns(Expression> filedNameExpression, Expression> valueExpression) + { + if (valueExpression == null) + { + return SetColumns(filedNameExpression, (object)null); + } + var name = UpdateBuilder.GetExpressionValue(filedNameExpression, ResolveExpressType.FieldSingle).GetString(); + name = UpdateBuilder.Builder.GetTranslationColumnName(name); + var exp = ExpressionTool.RemoveConvert((valueExpression as LambdaExpression).Body); + var value = UpdateBuilder.GetExpressionValue(exp, ResolveExpressType.WhereSingle).GetString(); + value = $" {name}={value} "; + this.UpdateBuilder.SetValues.Add(new KeyValuePair(name, value)); + this.UpdateBuilder.DbColumnInfoList = this.UpdateBuilder.DbColumnInfoList.Where(it => (UpdateParameterIsNull == false && IsPrimaryKey(it)) || UpdateBuilder.SetValues.Any(v => SqlBuilder.GetNoTranslationColumnName(v.Key).Equals(it.DbColumnName, StringComparison.CurrentCultureIgnoreCase) || SqlBuilder.GetNoTranslationColumnName(v.Key).Equals(it.PropertyName, StringComparison.CurrentCultureIgnoreCase)) || it.IsPrimarykey == true).ToList(); + AppendSets(); + if (typeof(T) == UtilConstants.ObjType) + { + this.UpdateBuilder.DbColumnInfoList.Add(new DbColumnInfo() + { + DbColumnName = UpdateBuilder.Builder.GetNoTranslationColumnName(name), + Value = value, + PropertyName = name, + SqlParameterDbType = typeof(SqlSugar.DbConvert.NoParameterCommonPropertyConvert) + }); + } + return this; + } + public virtual IUpdateable SetColumns(Expression> filedNameExpression, object fieldValue) + { + var name = UpdateBuilder.GetExpressionValue(filedNameExpression, ResolveExpressType.FieldSingle).GetString(); + name = UpdateBuilder.Builder.GetNoTranslationColumnName(name); + return SetColumns(name, fieldValue); + } + public virtual IUpdateable SetColumns(Expression> columns) + { + ThrowUpdateByObject(); + var expResult = UpdateBuilder.GetExpressionValue(columns, ResolveExpressType.Update); + var resultArray = expResult.GetResultArray(); + Check.ArgumentNullException(resultArray, "UpdateColumns Parameter error, UpdateColumns(it=>new T{ it.id=1}) is valid, UpdateColumns(it=>T) is error"); + var keys = ExpressionTool.GetNewExpressionItemList(columns).ToArray(); + if (resultArray.HasValue()) + { + int i = 0; + foreach (var item in resultArray) + { + string key = key = keys[i].Key; + i++; + var value = item; + value = GetSetSql(value, columns); + UpdateBuilder.SetValues.Add(new KeyValuePair(SqlBuilder.GetTranslationColumnName(key), value)); + } + } + this.UpdateBuilder.DbColumnInfoList = UpdateBuilder.DbColumnInfoList.Where(it => it.UpdateServerTime == true || it.UpdateSql.HasValue() || (UpdateParameterIsNull == false && IsPrimaryKey(it)) || UpdateBuilder.SetValues.Any(v => SqlBuilder.GetNoTranslationColumnName(v.Key).Equals(it.DbColumnName, StringComparison.CurrentCultureIgnoreCase) || SqlBuilder.GetNoTranslationColumnName(v.Key).Equals(it.PropertyName, StringComparison.CurrentCultureIgnoreCase)) || it.IsPrimarykey == true).ToList(); + CheckTranscodeing(false); + AppendSets(); + return this; + } + + + public virtual IUpdateable SetColumns(Expression> columns, bool appendColumnsByDataFilter) + { + ThrowUpdateByObject(); + var expResult = UpdateBuilder.GetExpressionValue(columns, ResolveExpressType.Update); + var resultArray = expResult.GetResultArray(); + Check.ArgumentNullException(resultArray, "UpdateColumns Parameter error, UpdateColumns(it=>new T{ it.id=1}) is valid, UpdateColumns(it=>T) is error"); + if (resultArray.HasValue()) + { + foreach (var item in resultArray) + { + string key = SqlBuilder.GetNoTranslationColumnName(item); + var value = item; + if (value.Contains("= \"SYSDATE\"")) + { + value = value.Replace("= \"SYSDATE\"", "= SYSDATE"); + } + UpdateBuilder.SetValues.Add(new KeyValuePair(SqlBuilder.GetTranslationColumnName(key), value)); + } + } + if (appendColumnsByDataFilter) + { + var newData = new T() { }; + UtilMethods.ClearPublicProperties(newData, this.EntityInfo); + var data = ((UpdateableProvider)this.Context.Updateable(newData)).UpdateObjs.First(); + foreach (var item in this.EntityInfo.Columns.Where(it => !it.IsPrimarykey && !it.IsIgnore && !it.IsOnlyIgnoreUpdate)) + { + var value = item.PropertyInfo.GetValue(data); + if (value?.Equals("") == false) + { + if (!value.Equals(UtilMethods.GetDefaultValue(item.UnderType))) + { + var pName = this.SqlBuilder.SqlParameterKeyWord + item.PropertyName + 1000; + var p = new SugarParameter(pName, value); + this.UpdateBuilder.Parameters.Add(p); + UpdateBuilder.SetValues.Add(new KeyValuePair(SqlBuilder.GetTranslationColumnName(item.DbColumnName), SqlBuilder.GetTranslationColumnName(item.DbColumnName) + "=" + pName)); + } + } + } + } + if (this.EntityInfo.Columns.Any(it => it.UpdateServerTime || it.UpdateSql.HasValue())) + { + var appendColumns = this.EntityInfo.Columns.Where(it => it.UpdateServerTime || it.UpdateSql.HasValue()); + foreach (var item in appendColumns) + { + if (item.UpdateServerTime) + { + UpdateBuilder.SetValues.Add(new KeyValuePair(SqlBuilder.GetTranslationColumnName(item.DbColumnName), SqlBuilder.GetTranslationColumnName(item.DbColumnName) + "=" + UpdateBuilder.LambdaExpressions.DbMehtods.GetDate())); + } + else if (item.UpdateSql.HasValue()) + { + UpdateBuilder.SetValues.Add(new KeyValuePair(SqlBuilder.GetTranslationColumnName(item.DbColumnName), SqlBuilder.GetTranslationColumnName(item.DbColumnName) + "=" + item.UpdateSql)); + } + } + } + this.UpdateBuilder.DbColumnInfoList = UpdateBuilder.DbColumnInfoList.Where(it => it.UpdateServerTime || it.UpdateSql.HasValue() || (UpdateParameterIsNull == false && IsPrimaryKey(it)) || UpdateBuilder.SetValues.Any(v => SqlBuilder.GetNoTranslationColumnName(v.Key).Equals(it.DbColumnName, StringComparison.CurrentCultureIgnoreCase) || SqlBuilder.GetNoTranslationColumnName(v.Key).Equals(it.PropertyName, StringComparison.CurrentCultureIgnoreCase)) || it.IsPrimarykey == true).ToList(); + CheckTranscodeing(false); + AppendSets(); + return this; + } + public virtual IUpdateable SetColumns(Expression> columns) + { + ThrowUpdateByObject(); + + var binaryExp = columns.Body as BinaryExpression; + Check.Exception(!binaryExp.NodeType.IsIn(ExpressionType.Equal), "No support {0}", columns.ToString()); + Check.Exception(!(binaryExp.Left is MemberExpression) && !(binaryExp.Left is UnaryExpression), "No support {0}", columns.ToString()); + Check.Exception(ExpressionTool.IsConstExpression(binaryExp.Left as MemberExpression), "No support {0}", columns.ToString()); + if (UpdateBuilder.LambdaExpressions.ParameterIndex <= 1 && + this.EntityInfo.Columns + .Select(it => it.PropertyName.TrimEnd('2')) + .GroupBy(it => it) + .Any(it => it.Count() > 1) + ) + { + UpdateBuilder.LambdaExpressions.ParameterIndex = 100; + } + var expResult = UpdateBuilder.GetExpressionValue(columns, ResolveExpressType.WhereSingle).GetResultString().Replace(")", " )").Replace("(", "( ").Trim().TrimStart('(').TrimEnd(')').Replace("= =", "="); + if (IsCorrectErrorSqlParameterName()) + { + expResult = UpdateBuilder.GetExpressionValue(binaryExp.Right, ResolveExpressType.WhereSingle).GetResultString().Trim(); + } + if (expResult.EndsWith(" IS NULL ")) + { + expResult = Regex.Split(expResult, " IS NULL ")[0] + " = NULL "; + } + else if (!expResult.Contains('=') && expResult.Contains("IS NULL ")) + { + expResult = Regex.Split(expResult, "IS NULL ")[0] + " = NULL "; + } + string key = SqlBuilder.GetNoTranslationColumnName(expResult); + if (IsCorrectErrorSqlParameterName() && binaryExp.Left is MemberExpression member) + { + key = this.EntityInfo.Columns.First(it => it.PropertyName == member.Member.Name).DbColumnName; + expResult = $" {this.SqlBuilder.GetTranslationColumnName(key)}={expResult} "; + } + if (EntityInfo.Columns.Where(it => it.IsJson || it.IsTranscoding).Any(it => it.DbColumnName.EqualCase(key) || it.PropertyName.EqualCase(key))) + { + CheckTranscodeing(); + } + + if (columns.ToString().Contains("Subqueryable().")) + { + expResult = expResult.Replace(this.SqlBuilder.GetTranslationColumnName((ExpressionTool.RemoveConvert(binaryExp.Left) as MemberExpression).Expression + "") + ".", this.UpdateBuilder.GetTableNameString.TrimEnd() + "."); + } + + UpdateBuilder.SetValues.Add(new KeyValuePair(SqlBuilder.GetTranslationColumnName(key), expResult)); + this.UpdateBuilder.DbColumnInfoList = this.UpdateBuilder.DbColumnInfoList.Where(it => (UpdateParameterIsNull == false && IsPrimaryKey(it)) || UpdateBuilder.SetValues.Any(v => SqlBuilder.GetNoTranslationColumnName(v.Key).Equals(it.DbColumnName, StringComparison.CurrentCultureIgnoreCase) || SqlBuilder.GetNoTranslationColumnName(v.Key).Equals(it.PropertyName, StringComparison.CurrentCultureIgnoreCase)) || it.IsPrimarykey == true).ToList(); + AppendSets(); + return this; + } + public IUpdateable SetColumnsIF(bool isUpdateColumns, Expression> columns) + { + ThrowUpdateByObject(); + if (isUpdateColumns) + SetColumns(columns); + return this; + } + public IUpdateable SetColumnsIF(bool isUpdateColumns, Expression> columns) + { + ThrowUpdateByObject(); + if (isUpdateColumns) + SetColumns(columns); + return this; + } + public IUpdateable In(Expression> inField, ISugarQueryable childQueryExpression) + { + var lamResult = UpdateBuilder.GetExpressionValue(inField, ResolveExpressType.FieldSingle); + this.UpdateBuilder.LambdaExpressions.ParameterIndex = childQueryExpression.QueryBuilder.LambdaExpressions.ParameterIndex + 1; + var fieldName = lamResult.GetResultString(); + if (this.UpdateBuilder.SetValues.Count == 0) + { + var sql = childQueryExpression.ToSql(); + Where($" {fieldName} IN ( SELECT {fieldName} FROM ( {sql.Key} ) SUBDEL) ", sql.Value); + } + else + { + Where($" {fieldName} IN ( SELECT {fieldName} FROM ( {childQueryExpression.ToSqlString()} ) SUBDEL) "); + } + return this; + } + public IUpdateable WhereIF(bool isWhere, Expression> expression) + { + if (UpdateBuilder.WhereValues.Count != 0 != true) + { + Check.ExceptionEasy(!StaticConfig.EnableAllWhereIF, "Need to program startup configuration StaticConfig. EnableAllWhereIF = true; Tip: This operation is very risky if there are no conditions it is easy to update the entire table", " 需要程序启动时配置StaticConfig.EnableAllWhereIF=true; 提示:该操作存在很大的风险如果没有条件很容易将整个表全部更新"); + } + if (isWhere) + { + return Where(expression); + } + return this; + } + public IUpdateable Where(Expression> expression) + { + Check.Exception(UpdateObjectNotWhere() && UpdateObjs.Length > 1, ErrorMessage.GetThrowMessage("update List no support where", "集合更新不支持Where请使用WhereColumns")); + var expResult = UpdateBuilder.GetExpressionValue(expression, ResolveExpressType.WhereSingle); + var whereString = expResult.GetResultString(); + if (expression.ToString().Contains("Subqueryable()")) + { + var entityTableName = this.EntityInfo.DbTableName; + if (UpdateBuilder.TableName.HasValue()) + { + entityTableName = UpdateBuilder.TableName; + } + if (ExpressionTool.GetParameters(expression).First().Type == typeof(T)) + { + var tableName = this.SqlBuilder.GetTranslationColumnName(entityTableName); + whereString = whereString.Replace(tableName, $"( SELECT * FROM {tableName}) "); + } + whereString = whereString.Replace(this.SqlBuilder.GetTranslationColumnName(expression.Parameters.First().Name) + ".", this.SqlBuilder.GetTranslationTableName(entityTableName) + "."); + } + else if (expResult.IsNavicate) + { + var entityTableName2 = this.EntityInfo.DbTableName; + if (this.UpdateBuilder.TableName.HasValue()) + { + entityTableName2 = this.UpdateBuilder.TableName; + } + whereString = whereString.Replace(expression.Parameters.First().Name + ".", this.SqlBuilder.GetTranslationTableName(entityTableName2) + "."); + whereString = whereString.Replace(this.SqlBuilder.GetTranslationColumnName(expression.Parameters.First().Name) + ".", this.SqlBuilder.GetTranslationTableName(entityTableName2) + "."); + } + UpdateBuilder.WhereValues.Add(whereString); + return this; + } + public IUpdateable Where(string whereSql, object parameters = null) + { + Check.Exception(UpdateObjectNotWhere() && UpdateObjs.Length > 1, ErrorMessage.GetThrowMessage("update List no support where", "集合更新不支持Where请使用WhereColumns")); + if (whereSql.HasValue()) + { + UpdateBuilder.WhereValues.Add(whereSql); + } + if (parameters != null) + { + UpdateBuilder.Parameters.AddRange(Context.Ado.GetParameters(parameters)); + } + return this; + } + public IUpdateable Where(string fieldName, string conditionalType, object fieldValue) + { + Check.Exception(UpdateObjectNotWhere() && UpdateObjs.Length > 1, ErrorMessage.GetThrowMessage("update List no support where", "集合更新不支持Where请使用WhereColumns")); + var whereSql = this.SqlBuilder.GetWhere(fieldName, conditionalType, 0); + this.Where(whereSql); + string parameterName = this.SqlBuilder.SqlParameterKeyWord + fieldName + "0"; + this.UpdateBuilder.Parameters.Add(new SugarParameter(parameterName, fieldValue)); + return this; + } + public IUpdateable Where(List conditionalModels) + { + Check.Exception(UpdateObjectNotWhere() && UpdateObjs.Length > 1, ErrorMessage.GetThrowMessage("update List no support where", "集合更新不支持Where请使用WhereColumns")); + var sql = this.Context.Queryable().SqlBuilder.ConditionalModelToSql(conditionalModels); + var result = this; + result.Where(sql.Key, sql.Value); + return result; + } + public ParameterUpdateable UseParameter() + { + ThrowUpdateByExpressionByMesage(ErrorMessage.GetThrowMessage("UseParameter can only be updated through entity objects", "UseParameter只能通过实体对象更新,不能是表达式方式更新")); + ParameterUpdateable parameter = new ParameterUpdateable(); + parameter.Context = this.Context; + parameter.Updateable = (UpdateableProvider)this; + return parameter; + } + public IUpdateable In(object[] ids) + { + ThrowUpdateByObjectByMesage(" In(object[] ids) "); + List conditionalModels = new List(); + var column = this.EntityInfo.Columns.FirstOrDefault(it => it.IsPrimarykey); + Check.ExceptionEasy(column == null, "In need primary key", "In需要实体有主键"); + conditionalModels.Add(new ConditionalModel() { FieldName = column.DbColumnName, ConditionalType = ConditionalType.In, FieldValue = string.Join(",", ids), CSharpTypeName = column.UnderType?.Name }); + return this.Where(conditionalModels); + } + #endregion + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/UpdateProvider/UpdateableProviderT2.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/UpdateProvider/UpdateableProviderT2.cs new file mode 100644 index 000000000..bf7f05d0e --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/UpdateProvider/UpdateableProviderT2.cs @@ -0,0 +1,57 @@ +using System.Linq.Expressions; +namespace SqlSugar +{ + public class UpdateableProvider : IUpdateable where T : class, new() + { + public IUpdateable updateableObj { get; set; } + public int ExecuteCommand() + { + return this.updateableObj.ExecuteCommand(); + } + + public Task ExecuteCommandAsync() + { + return this.updateableObj.ExecuteCommandAsync(); + } + + public IUpdateable InnerJoin(Expression> joinExpress) + { + updateableObj.UpdateBuilder.Context.InitMappingInfo(); + UpdateableProvider result = new UpdateableProvider(); + result.updateableObj = updateableObj; + var joinIno = ((QueryableProvider)updateableObj.UpdateBuilder.Context.Queryable()).GetJoinInfo(joinExpress, JoinType.Inner); + result.updateableObj.UpdateBuilder.JoinInfos.Add(joinIno); + result.updateableObj.UpdateBuilder.ShortName = joinExpress.Parameters.FirstOrDefault()?.Name; + return result; + } + + public IUpdateable SetColumns(Expression> columns) + { + var exp = ((columns as LambdaExpression).Body as MemberInitExpression).Bindings; + var items = ExpressionTool.GetMemberBindingItemList(exp); + var UpdateBuilder = updateableObj.UpdateBuilder; + var SqlBuilder = UpdateBuilder.Builder; + UpdateBuilder.LambdaExpressions.IsSingle = false; + foreach (var item in items) + { + var dbColumnName = updateableObj.UpdateBuilder.Context.EntityMaintenance.GetDbColumnName(item.Key); + var value = updateableObj.UpdateBuilder.GetExpressionValue(ExpressionTool.RemoveConvert(item.Value), ResolveExpressType.WhereMultiple).GetString(); + if (ExpressionTool.GetMethodName(ExpressionTool.RemoveConvert(item.Value)) == "End") + { + value = UtilMethods.RemoveEqualOne(value); + } + this.updateableObj.UpdateBuilder.SetValues.Add(new KeyValuePair(dbColumnName, value)); + } + UpdateBuilder.DbColumnInfoList = UpdateBuilder.DbColumnInfoList + .Where(it => it.UpdateServerTime == true || it.UpdateSql.HasValue() || UpdateBuilder.SetValues.Any(v => SqlBuilder.GetNoTranslationColumnName(v.Key).Equals(it.DbColumnName, StringComparison.CurrentCultureIgnoreCase) || SqlBuilder.GetNoTranslationColumnName(v.Key).Equals(it.PropertyName, StringComparison.CurrentCultureIgnoreCase)) || it.IsPrimarykey == true).ToList(); + return this; + } + + public IUpdateable Where(Expression> whereExpression) + { + var value = updateableObj.UpdateBuilder.GetExpressionValue(whereExpression, ResolveExpressType.WhereMultiple).GetString(); + updateableObj.UpdateBuilder.WhereValues.Add(value); + return this; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/UpdateProvider/UpdateableProviderT3.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/UpdateProvider/UpdateableProviderT3.cs new file mode 100644 index 000000000..64105b975 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/UpdateProvider/UpdateableProviderT3.cs @@ -0,0 +1,55 @@ +using System.Linq.Expressions; +namespace SqlSugar +{ + public class UpdateableProvider : IUpdateable where T : class, new() + { + internal IUpdateable updateableObj; + + public int ExecuteCommand() + { + return this.updateableObj.ExecuteCommand(); + } + + public Task ExecuteCommandAsync() + { + return this.updateableObj.ExecuteCommandAsync(); + } + + public IUpdateable InnerJoin(Expression> joinExpress) + { + updateableObj.UpdateBuilder.Context.InitMappingInfo(); + UpdateableProvider result = new UpdateableProvider(); + result.updateableObj = updateableObj; + var joinIno = ((QueryableProvider)updateableObj.UpdateBuilder.Context.Queryable()).GetJoinInfo(joinExpress, JoinType.Inner); + result.updateableObj.UpdateBuilder.JoinInfos.Add(joinIno); + result.updateableObj.UpdateBuilder.ShortName = joinExpress.Parameters.FirstOrDefault()?.Name; + return result; + } + + public IUpdateable SetColumns(Expression> columns) + { + var exp = ((columns as LambdaExpression).Body as MemberInitExpression).Bindings; + var items = ExpressionTool.GetMemberBindingItemList(exp); + var UpdateBuilder = updateableObj.UpdateBuilder; + var SqlBuilder = UpdateBuilder.Builder; + foreach (var item in items) + { + updateableObj.UpdateBuilder.LambdaExpressions.IsSingle = false; + var dbColumnName = updateableObj.UpdateBuilder.Context.EntityMaintenance.GetDbColumnName(item.Key); + var value = updateableObj.UpdateBuilder.GetExpressionValue(ExpressionTool.RemoveConvert(item.Value), ResolveExpressType.WhereMultiple).GetString(); + this.updateableObj.UpdateBuilder.SetValues.Add(new KeyValuePair(dbColumnName, value)); + } + UpdateBuilder.DbColumnInfoList = UpdateBuilder.DbColumnInfoList + .Where(it => it.UpdateServerTime == true || it.UpdateSql.HasValue() || UpdateBuilder.SetValues.Any(v => SqlBuilder.GetNoTranslationColumnName(v.Key).Equals(it.DbColumnName, StringComparison.CurrentCultureIgnoreCase) || SqlBuilder.GetNoTranslationColumnName(v.Key).Equals(it.PropertyName, StringComparison.CurrentCultureIgnoreCase)) || it.IsPrimarykey == true).ToList(); + return this; + } + + public IUpdateable Where(Expression> whereExpression) + { + + var value = updateableObj.UpdateBuilder.GetExpressionValue(whereExpression, ResolveExpressType.WhereMultiple).GetString(); + updateableObj.UpdateBuilder.WhereValues.Add(value); + return this; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/UpdateProvider/UpdateableProviderT4.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/UpdateProvider/UpdateableProviderT4.cs new file mode 100644 index 000000000..2bcf86153 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Abstract/UpdateProvider/UpdateableProviderT4.cs @@ -0,0 +1,44 @@ +using System.Linq.Expressions; +namespace SqlSugar +{ + public class UpdateableProvider : IUpdateable where T : class, new() + { + public IUpdateable updateableObj { get; set; } + public int ExecuteCommand() + { + return this.updateableObj.ExecuteCommand(); + } + + public Task ExecuteCommandAsync() + { + return this.updateableObj.ExecuteCommandAsync(); + } + + public IUpdateable SetColumns(Expression> columns) + { + updateableObj.UpdateBuilder.Context.InitMappingInfo(); + var exp = ((columns as LambdaExpression).Body as MemberInitExpression).Bindings; + var items = ExpressionTool.GetMemberBindingItemList(exp); + var UpdateBuilder = updateableObj.UpdateBuilder; + var SqlBuilder = UpdateBuilder.Builder; + foreach (var item in items) + { + updateableObj.UpdateBuilder.LambdaExpressions.IsSingle = false; + var dbColumnName = updateableObj.UpdateBuilder.Context.EntityMaintenance.GetDbColumnName(item.Key); + var value = updateableObj.UpdateBuilder.GetExpressionValue(ExpressionTool.RemoveConvert(item.Value), ResolveExpressType.WhereMultiple).GetString(); + this.updateableObj.UpdateBuilder.SetValues.Add(new KeyValuePair(dbColumnName, value)); + } + UpdateBuilder.DbColumnInfoList = UpdateBuilder.DbColumnInfoList + .Where(it => it.UpdateServerTime == true || it.UpdateSql.HasValue() || UpdateBuilder.SetValues.Any(v => SqlBuilder.GetNoTranslationColumnName(v.Key).Equals(it.DbColumnName, StringComparison.CurrentCultureIgnoreCase) || SqlBuilder.GetNoTranslationColumnName(v.Key).Equals(it.PropertyName, StringComparison.CurrentCultureIgnoreCase)) || it.IsPrimarykey == true).ToList(); + return this; + } + + public IUpdateable Where(Expression> whereExpression) + { + + var value = updateableObj.UpdateBuilder.GetExpressionValue(whereExpression, ResolveExpressType.WhereMultiple).GetString(); + updateableObj.UpdateBuilder.WhereValues.Add(value); + return this; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/CacheScheme/CacheKeyBuider.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/CacheScheme/CacheKeyBuider.cs new file mode 100644 index 000000000..c5a068cac --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/CacheScheme/CacheKeyBuider.cs @@ -0,0 +1,73 @@ +namespace SqlSugar +{ + internal static class CacheKeyBuider + { + public static CacheKey GetKey(SqlSugarProvider context, QueryBuilder queryBuilder) + { + CacheKey result = new CacheKey(); + result.Database = context.Context.Ado.Connection.Database; + AddTables(context, queryBuilder, result); + AddIdentificationList(queryBuilder, result); + if (queryBuilder.ResultType != null) + { + result.IdentificationList.Add(queryBuilder.ResultType.FullName); + } + return result; + } + + private static void AddIdentificationList(QueryBuilder queryBuilder, CacheKey result) + { + result.IdentificationList = new List(); + result.IdentificationList.Add(queryBuilder.GetTableNameString); + result.IdentificationList.Add(queryBuilder.GetJoinValueString); + result.IdentificationList.Add(queryBuilder.GetOrderByString); + result.IdentificationList.Add(queryBuilder.GetGroupByString); + result.IdentificationList.Add(queryBuilder.GetWhereValueString); + result.IdentificationList.Add(queryBuilder.PartitionByValue); + result.IdentificationList.Add(queryBuilder.Take.ObjToString()); + result.IdentificationList.Add(queryBuilder.Skip.ObjToString()); + result.IdentificationList.Add(queryBuilder.IsCount.ObjToString()); + result.IdentificationList.Add(UtilMethods.GetMD5(queryBuilder.GetSelectValue.ObjToString())); + if (queryBuilder.Parameters.HasValue()) + { + foreach (var item in queryBuilder.Parameters) + { + result.IdentificationList.Add(item.ParameterName + "_" + item.Value); + } + } + } + + private static void AddTables(SqlSugarProvider context, QueryBuilder queryBuilder, CacheKey result) + { + result.Tables = new List(); + result.Tables.Add(context.EntityMaintenance.GetTableName(queryBuilder.EntityName)); + if (queryBuilder.EasyJoinInfos.HasValue()) + { + foreach (var item in queryBuilder.EasyJoinInfos) + { + result.Tables.Add(context.EntityMaintenance.GetTableName(item.Value)); + } + } + if (queryBuilder.JoinQueryInfos.HasValue()) + { + foreach (var item in queryBuilder.JoinQueryInfos) + { + result.Tables.Add(queryBuilder.Builder.GetNoTranslationColumnName(item.TableName)); + } + } + } + } + + internal class SugarCacheDictionary + { + + } + internal class SugarCacheDictionaryList + { + + } + internal class SugarCacheDataTable + { + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/CacheScheme/CacheSchemeMain.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/CacheScheme/CacheSchemeMain.cs new file mode 100644 index 000000000..d225bfe6b --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/CacheScheme/CacheSchemeMain.cs @@ -0,0 +1,61 @@ +namespace SqlSugar +{ + internal static class CacheSchemeMain + { + public static T GetOrCreate(ICacheService cacheService, QueryBuilder queryBuilder, Func getData, int cacheDurationInSeconds, SqlSugarProvider context, string cacheKey) + { + CacheKey key = CacheKeyBuider.GetKey(context, queryBuilder); + key.AppendKey = cacheKey; + string keyString = key.ToString(); + var result = cacheService.GetOrCreate(keyString, getData, cacheDurationInSeconds); + return result; + } + + public static void RemoveCache(ICacheService cacheService, string tableName) + { + if (cacheService == null) + { + return; + } + if (StaticConfig.CacheRemoveByLikeStringFunc != null) + { + StaticConfig.CacheRemoveByLikeStringFunc(cacheService, UtilConstants.Dot + tableName.ToLower() + UtilConstants.Dot); + return; + } + var keys = cacheService.GetAllKey(); + if (keys.HasValue()) + { + foreach (var item in keys) + { + if (item.Contains($"{UtilConstants.Dot}{tableName}{UtilConstants.Dot}", StringComparison.CurrentCultureIgnoreCase)) + { + cacheService.Remove(item); + } + } + } + } + public static void RemoveCacheByLike(ICacheService cacheService, string likeString) + { + if (cacheService == null) + { + return; + } + if (StaticConfig.CacheRemoveByLikeStringFunc != null) + { + StaticConfig.CacheRemoveByLikeStringFunc(cacheService, likeString); + return; + } + var keys = cacheService.GetAllKey(); + if (keys.HasValue()) + { + foreach (var item in keys) + { + if (item?.Contains(likeString, StringComparison.CurrentCultureIgnoreCase) == true) + { + cacheService.Remove(item); + } + } + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/DistributedSystem/Snowflake/DisposableAction.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/DistributedSystem/Snowflake/DisposableAction.cs new file mode 100644 index 000000000..a28b064c5 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/DistributedSystem/Snowflake/DisposableAction.cs @@ -0,0 +1,19 @@ +namespace SqlSugar.DistributedSystem.Snowflake +{ + public class DisposableAction : IDisposable + { + readonly Action _action; + + public DisposableAction(Action action) + { + if (action == null) + throw new ArgumentNullException(nameof(action)); + _action = action; + } + + public void Dispose() + { + _action(); + } + } +} \ No newline at end of file diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/DistributedSystem/Snowflake/IdWorker.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/DistributedSystem/Snowflake/IdWorker.cs new file mode 100644 index 000000000..111d45544 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/DistributedSystem/Snowflake/IdWorker.cs @@ -0,0 +1,129 @@ +/* Copyright 2010-2012 Twitter, Inc.*/ + +/* + * An object that generates IDs. + * This is broken into a separate class in case + * we ever want to support multiple worker threads + * per process + */ + +namespace SqlSugar.DistributedSystem.Snowflake +{ + public class IdWorker + { + public const long Twepoch = 1288834974657L; + + const int WorkerIdBits = 5; + const int DatacenterIdBits = 5; + const int SequenceBits = 12; + const long MaxWorkerId = -1L ^ (-1L << WorkerIdBits); + const long MaxDatacenterId = -1L ^ (-1L << DatacenterIdBits); + + private const int WorkerIdShift = SequenceBits; + private const int DatacenterIdShift = SequenceBits + WorkerIdBits; + public const int TimestampLeftShift = SequenceBits + WorkerIdBits + DatacenterIdBits; + private const long SequenceMask = -1L ^ (-1L << SequenceBits); + + private long _sequence = 0L; + private long _lastTimestamp = -1L; + + + public IdWorker(long workerId, long datacenterId, long sequence = 0L) + { + WorkerId = workerId; + DatacenterId = datacenterId; + _sequence = sequence; + + // sanity check for workerId + if (workerId > MaxWorkerId || workerId < 0) + { + throw new ArgumentException(String.Format("worker Id can't be greater than {0} or less than 0", MaxWorkerId)); + } + + if (datacenterId > MaxDatacenterId || datacenterId < 0) + { + throw new ArgumentException(String.Format("datacenter Id can't be greater than {0} or less than 0", MaxDatacenterId)); + } + + //log.info( + // String.Format("worker starting. timestamp left shift {0}, datacenter id bits {1}, worker id bits {2}, sequence bits {3}, workerid {4}", + // TimestampLeftShift, DatacenterIdBits, WorkerIdBits, SequenceBits, workerId) + // ); + } + + public long WorkerId { get; protected set; } + public long DatacenterId { get; protected set; } + + public long Sequence + { + get { return _sequence; } + internal set { _sequence = value; } + } + + // def get_timestamp() = System.currentTimeMillis + + readonly object _lock = new Object(); + public long getID() + { + return NextId(); + } + public virtual long NextId() + { + if (StaticConfig.CustomSnowFlakeFunc != null) + { + return StaticConfig.CustomSnowFlakeFunc(); + } + lock (_lock) + { + var timestamp = TimeGen(); + + if (timestamp < _lastTimestamp) + { + if (StaticConfig.CustomSnowFlakeTimeErrorFunc != null) + { + return StaticConfig.CustomSnowFlakeTimeErrorFunc(); + } + //exceptionCounter.incr(1); + //log.Error("clock is moving backwards. Rejecting requests until %d.", _lastTimestamp); + throw new InvalidSystemClock(String.Format( + "服务器时间出现回退你可以使用StaticConfig.CustomSnowFlakeTimeErrorFunc=【自定义方法】处理让他不报错返回新ID,Clock moved backwards. Refusing to generate id for {0} milliseconds", _lastTimestamp - timestamp)); + } + + if (_lastTimestamp == timestamp) + { + _sequence = (_sequence + 1) & SequenceMask; + if (_sequence == 0) + { + timestamp = TilNextMillis(_lastTimestamp); + } + } + else + { + _sequence = 0; + } + + _lastTimestamp = timestamp; + var id = ((timestamp - Twepoch) << TimestampLeftShift) | + (DatacenterId << DatacenterIdShift) | + (WorkerId << WorkerIdShift) | _sequence; + + return id; + } + } + + protected virtual long TilNextMillis(long lastTimestamp) + { + var timestamp = TimeGen(); + while (timestamp <= lastTimestamp) + { + timestamp = TimeGen(); + } + return timestamp; + } + + protected virtual long TimeGen() + { + return System.CurrentTimeMillis(); + } + } +} \ No newline at end of file diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/DistributedSystem/Snowflake/InvalidSystemClock.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/DistributedSystem/Snowflake/InvalidSystemClock.cs new file mode 100644 index 000000000..56831f81b --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/DistributedSystem/Snowflake/InvalidSystemClock.cs @@ -0,0 +1,15 @@ +namespace SqlSugar.DistributedSystem.Snowflake +{ + public class InvalidSystemClock : Exception + { + public InvalidSystemClock(string message) : base(message) { } + + public InvalidSystemClock() : base() + { + } + + public InvalidSystemClock(string? message, Exception? innerException) : base(message, innerException) + { + } + } +} \ No newline at end of file diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/DistributedSystem/Snowflake/SnowFlakeSingle.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/DistributedSystem/Snowflake/SnowFlakeSingle.cs new file mode 100644 index 000000000..3ab5ff447 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/DistributedSystem/Snowflake/SnowFlakeSingle.cs @@ -0,0 +1,39 @@ +namespace SqlSugar +{ + public sealed class SnowFlakeSingle + { + private static object LockObject = new object(); + private static DistributedSystem.Snowflake.IdWorker worker; + public static int WorkId = 1; + public static int DatacenterId = 1; + private SnowFlakeSingle() + { + + } + static SnowFlakeSingle() { } + public static DistributedSystem.Snowflake.IdWorker Instance + { + get + { + if (worker == null) + { + lock (LockObject) + { + if (worker == null) + { + worker = new DistributedSystem.Snowflake.IdWorker(WorkId, DatacenterId); + } + } + } + return worker; + } + } + public static DistributedSystem.Snowflake.IdWorker instance + { + get + { + return Instance; + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/DistributedSystem/Snowflake/TimeExtensions.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/DistributedSystem/Snowflake/TimeExtensions.cs new file mode 100644 index 000000000..7b6e8d1e5 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/DistributedSystem/Snowflake/TimeExtensions.cs @@ -0,0 +1,38 @@ +namespace SqlSugar.DistributedSystem.Snowflake +{ + public static class System + { + public static Func currentTimeFunc = InternalCurrentTimeMillis; + + public static long CurrentTimeMillis() + { + return currentTimeFunc(); + } + + public static IDisposable StubCurrentTime(Func func) + { + currentTimeFunc = func; + return new DisposableAction(() => + { + currentTimeFunc = InternalCurrentTimeMillis; + }); + } + + public static IDisposable StubCurrentTime(long millis) + { + currentTimeFunc = () => millis; + return new DisposableAction(() => + { + currentTimeFunc = InternalCurrentTimeMillis; + }); + } + + private static readonly DateTime Jan1st1970 = new DateTime + (1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + + private static long InternalCurrentTimeMillis() + { + return (long)(DateTime.UtcNow - Jan1st1970).TotalMilliseconds; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/DistributedSystem/Snowflake/ValueToStringConverter.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/DistributedSystem/Snowflake/ValueToStringConverter.cs new file mode 100644 index 000000000..63547ca21 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/DistributedSystem/Snowflake/ValueToStringConverter.cs @@ -0,0 +1,20 @@ +using Newtonsoft.Json; + +namespace SqlSugar +{ + public class ValueToStringConverter : JsonConverter + { + public override bool CanRead => false; + + public override bool CanConvert(Type objectType) => objectType.IsValueType; + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + => throw new NotSupportedException(); + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + var str = value?.ToString(); + writer.WriteValue(str); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/AsyncRef.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/AsyncRef.cs new file mode 100644 index 000000000..3e2e6cac6 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/AsyncRef.cs @@ -0,0 +1,16 @@ +namespace SqlSugar +{ + public class RefAsync + { + public RefAsync() { } + public RefAsync(T value) { Value = value; } + public T Value { get; set; } + public override string ToString() + { + T value = Value; + return value == null ? "" : value.ToString(); + } + public static implicit operator T(RefAsync r) { return r.Value; } + public static implicit operator RefAsync(T value) { return new RefAsync(value); } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/CacheKey.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/CacheKey.cs new file mode 100644 index 000000000..66cb935f1 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/CacheKey.cs @@ -0,0 +1,16 @@ +namespace SqlSugar +{ + public class CacheKey + { + public string AppendKey { get; set; } + public string Database { get; set; } + public List Tables { get; set; } + public List IdentificationList { get; set; } + public new string ToString() + { + var result = "SqlSugarDataCache" + UtilConstants.Dot + string.Join(UtilConstants.Dot, this.Tables) + UtilConstants.Dot + string.Join(UtilConstants.Dot, this.IdentificationList.Where(it => it.HasValue())); + result = result + AppendKey; + return result; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/ConditionalModel.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/ConditionalModel.cs new file mode 100644 index 000000000..d33140e0b --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/ConditionalModel.cs @@ -0,0 +1,38 @@ +namespace SqlSugar +{ + public interface IConditionalModel + { + + } + public class ConditionalCollections : IConditionalModel + { + public List> ConditionalList { get; set; } + } + public class ConditionalTree : IConditionalModel + { + public List> ConditionalList { get; set; } + } + public class ConditionalModel : IConditionalModel + { + public ConditionalModel() + { + this.ConditionalType = ConditionalType.Equal; + } + public string FieldName { get; set; } + public string FieldValue { get; set; } + public string CSharpTypeName { get; set; } + + + public ICustomConditionalFunc CustomConditionalFunc { get; set; } + public object CustomParameterValue { get; set; } + + public ConditionalType ConditionalType { get; set; } + [Newtonsoft.Json.JsonIgnoreAttribute] + public Func FieldValueConvertFunc { get; set; } + + public static List Create(params IConditionalModel[] conditionalModel) + { + return conditionalModel.ToList(); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/ConfigQuery.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/ConfigQuery.cs new file mode 100644 index 000000000..79c3683ac --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/ConfigQuery.cs @@ -0,0 +1,62 @@ +using System.Linq.Expressions; +namespace SqlSugar +{ + public class ConfigQuery + { + public SqlSugarProvider Context { get; set; } + public void SetTable(Expression> keyExpression, Expression> valueTextExpression, string uniqueCode = null, Expression> whereExpression = null) + { + lock (SqlFuncExtendsion.TableInfos) + { + var entity = this.Context.EntityMaintenance.GetEntityInfo(); + ExpressionContext context = new ExpressionContext(); + var query = Context.Queryable().QueryBuilder; + var keyValue = query.GetExpressionValue(keyExpression, ResolveExpressType.FieldSingle).GetString(); + var ValueValue = query.GetExpressionValue(valueTextExpression, ResolveExpressType.FieldSingle).GetString(); + string where = null; + if (whereExpression != null) + { + where = query.GetExpressionValue(whereExpression, ResolveExpressType.WhereSingle).GetResultString(); + } + context.MappingTables = this.Context.MappingTables; + if (!SqlFuncExtendsion.TableInfos.Any(y => y.Type == typeof(T) && y.Code == uniqueCode)) + { + SqlFuncExtendsion.TableInfos.Add(new ConfigTableInfo() + { + Type = typeof(T), + TableName = entity.DbTableName, + Key = keyValue, + Value = ValueValue, + Where = where, + Parameter = query.Parameters, + Code = uniqueCode + }); + } + else + { + Check.Exception(true, "SetKeyValue error , entity & uniqueCode already exist"); + } + } + } + public void SetTable(Expression> key, Expression> value) + { + SetTable(key, value, null, null); + } + + public bool Any() + { + return SqlFuncExtendsion.TableInfos.Count != 0; + } + } + + public class ConfigTableInfo + { + public string Code { get; set; } + public Type Type { get; set; } + public string TableName { get; set; } + public string Key { get; set; } + public string Value { get; set; } + public string Where { get; set; } + public List Parameter { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/ConnMoreSettings.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/ConnMoreSettings.cs new file mode 100644 index 000000000..9570f1415 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/ConnMoreSettings.cs @@ -0,0 +1,37 @@ +namespace SqlSugar +{ + public class ConnMoreSettings + { + public bool IsAutoRemoveDataCache { get; set; } + public bool IsWithNoLockQuery { get; set; } + public bool DisableWithNoLockWithTran { get; set; } + public bool IsWithNoLockSubquery { get; set; } + + public bool DisableNvarchar { get; set; } + public bool DisableMillisecond { get; set; } + public bool PgSqlIsAutoToLower { get; set; } = true; + public bool PgSqlIsAutoToLowerCodeFirst { get; set; } = true; + public bool PgSqlIsAutoToLowerSchema { get; set; } = true; + public bool EnableILike { get; set; } + public bool IsAutoToUpper { get; set; } = true; + public int DefaultCacheDurationInSeconds { get; set; } + public bool? TableEnumIsString { get; set; } + public DateTime? DbMinDate { get; set; } = DateTime.MinValue.Date.AddYears(1900 - 1); + public bool IsNoReadXmlDescription { get; set; } + public bool SqlServerCodeFirstNvarchar { get; set; } + public bool OracleCodeFirstNvarchar2 { get; set; } + public bool SqliteCodeFirstEnableDefaultValue { get; set; } + public bool SqliteCodeFirstEnableDescription { get; set; } + public bool IsAutoUpdateQueryFilter { get; set; } + public bool IsAutoDeleteQueryFilter { get; set; } + public bool EnableModelFuncMappingColumn { get; set; } + public bool EnableOracleIdentity { get; set; } + public bool EnableCodeFirstUpdatePrecision { get; set; } + public bool SqliteCodeFirstEnableDropColumn { get; set; } + public bool IsCorrectErrorSqlParameterName { get; set; } + public int MaxParameterNameLength { get; set; } + public bool DisableQueryWhereColumnRemoveTrim { get; set; } + public DbType? DatabaseModel { get; set; } + public bool ClickHouseEnableFinal { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/ConnectionConfig.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/ConnectionConfig.cs new file mode 100644 index 000000000..5fbaac673 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/ConnectionConfig.cs @@ -0,0 +1,155 @@ +using Newtonsoft.Json; + +using System.Data; +using System.Reflection; +namespace SqlSugar +{ + public class ConnectionConfig + { + /// + ///Connection unique code + /// + public object ConfigId { get; set; } + /// + ///DbType.SqlServer Or Other + /// + public DbType DbType { get; set; } + /// + ///Database Connection string + /// + public string ConnectionString { get; set; } + /// + /// QueryableWithAttr queries go to DbLinkName, which is commonly used for cross-library queries + /// + public string DbLinkName { get; set; } + /// + /// true does not need to close the connection + /// + public bool IsAutoCloseConnection { get; set; } + /// + /// Default Attribute + /// + public InitKeyType InitKeyType = InitKeyType.Attribute; + /// + /// Exception prompt language + /// + public LanguageType LanguageType { get => ErrorMessage.SugarLanguageType; set => ErrorMessage.SugarLanguageType = value; } + + /// + /// Configure External Services replace default services,For example, Redis storage + /// + [JsonIgnore] + public ConfigureExternalServices ConfigureExternalServices = new ConfigureExternalServices(); + /// + /// If SlaveConnectionStrings has value,ConnectionString is write operation, SlaveConnectionStrings is read operation. + /// All operations within a transaction is ConnectionString + /// + public List SlaveConnectionConfigs { get; set; } + /// + /// More Gobal Settings + /// + public ConnMoreSettings MoreSettings { get; set; } + ///// + ///// Used for debugging errors or BUG,Used for debugging, which has an impact on Performance + ///// + //public SugarDebugger Debugger { get; set; } + + public string IndexSuffix { get; set; } + + [JsonIgnore] + public AopEvents AopEvents { get; set; } + + public SqlMiddle SqlMiddle { get; set; } + } + public class SqlMiddle + { + public bool? IsSqlMiddle { get; set; } + public Func GetScalar { get; set; } = (s, p) => throw new Exception("SqlMiddle.GetScalar is null"); + public Func ExecuteCommand { get; set; } = (s, p) => throw new Exception("SqlMiddle.ExecuteCommand is null"); + public Func GetDataReader { get; set; } = (s, p) => throw new Exception("SqlMiddle.GetDataReader is null"); + public Func GetDataSetAll { get; set; } = (s, p) => throw new Exception("SqlMiddle.GetDataSetAll is null"); + public Func> GetScalarAsync { get; set; } = (s, p) => throw new Exception("SqlMiddle.GetScalarAsync is null"); + public Func> ExecuteCommandAsync { get; set; } = (s, p) => throw new Exception("SqlMiddle.ExecuteCommandAsync is null"); + public Func> GetDataReaderAsync { get; set; } = (s, p) => throw new Exception("SqlMiddle.GetDataReaderAsync is null"); + public Func> GetDataSetAllAsync { get; set; } = (s, p) => throw new Exception("SqlMiddle.GetDataSetAllAsync is null"); + + } + public class AopEvents + { + public Action OnDiffLogEvent { get; set; } + public Action OnError { get; set; } + public Action OnLogExecuting { get; set; } + public Action OnLogExecuted { get; set; } + public Func> OnExecutingChangeSql { get; set; } + public Action DataExecuting { get; set; } + public Action DataChangesExecuted { get; set; } + public Action DataExecuted { get; set; } + public Action CheckConnectionExecuting { get; set; } + public Action CheckConnectionExecuted { get; set; } + public Action OnGetDataReadering { get; set; } + public Action OnGetDataReadered { get; set; } + } + public class ConfigureExternalServices + { + + private ISerializeService _SerializeService; + private ICacheService _ReflectionInoCache; + private ICacheService _DataInfoCache; + private IRazorService _RazorService; + public ISplitTableService SplitTableService { get; set; } + public IRazorService RazorService + { + get + { + if (_RazorService == null) + return _RazorService; + else + return _RazorService; + } + set { _RazorService = value; } + } + + public ISerializeService SerializeService + { + get + { + if (_SerializeService == null) + return DefaultServices.Serialize; + else + return _SerializeService; + } + set { _SerializeService = value; } + } + + public ICacheService ReflectionInoCacheService + { + get + { + if (_ReflectionInoCache == null) + return DefaultServices.ReflectionInoCache; + else + return _ReflectionInoCache; + } + set { _ReflectionInoCache = value; } + } + + public ICacheService DataInfoCacheService + { + get + { + if (_DataInfoCache == null) + return DefaultServices.DataInoCache; + else + return _DataInfoCache; + } + set { _DataInfoCache = value; } + } + + public List SqlFuncServices { get; set; } + public List> AppendDataReaderTypeMappings { get; set; } + + + public Action EntityService { get; set; } + public Action EntityNameService { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/DbColumnInfo.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/DbColumnInfo.cs new file mode 100644 index 000000000..5052e7de8 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/DbColumnInfo.cs @@ -0,0 +1,31 @@ +namespace SqlSugar +{ + public class DbColumnInfo + { + public string TableName { get; set; } + public int TableId { get; set; } + public string DbColumnName { get; set; } + public string PropertyName { get; set; } + public string DataType { get; set; } + public string OracleDataType { get; set; } + public Type PropertyType { get; set; } + public int Length { get; set; } + public string ColumnDescription { get; set; } + public string DefaultValue { get; set; } + public bool IsNullable { get; set; } + public bool IsIdentity { get; set; } + public bool IsPrimarykey { get; set; } + public object Value { get; set; } + public int DecimalDigits { get; set; } + public int Scale { get; set; } + public bool IsArray { get; set; } + public bool IsJson { get; set; } + public bool? IsUnsigned { get; set; } + public int CreateTableFieldSort { get; set; } + public bool InsertServerTime { get; set; } + public string InsertSql { get; set; } + public bool UpdateServerTime { get; set; } + public string UpdateSql { get; set; } + public object SqlParameterDbType { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/DbFastestProperties.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/DbFastestProperties.cs new file mode 100644 index 000000000..bca5764d7 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/DbFastestProperties.cs @@ -0,0 +1,13 @@ +namespace SqlSugar +{ + public class DbFastestProperties + { + public bool HasOffsetTime { get; set; } + public string[] WhereColumns { get; set; } + public bool IsOffIdentity { get; set; } + public bool IsMerge { get; set; } + public bool IsNoCopyDataTable { get; set; } + public bool IsConvertDateTimeOffsetToDateTime { get; set; } + public bool NoPage { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/DbResult.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/DbResult.cs new file mode 100644 index 000000000..b986af799 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/DbResult.cs @@ -0,0 +1,10 @@ +namespace SqlSugar +{ + public class DbResult + { + public bool IsSuccess { get; set; } + public Exception ErrorException { get; set; } + public string ErrorMessage { get; set; } + public T Data { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/DbTableInfo.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/DbTableInfo.cs new file mode 100644 index 000000000..c10c6ca7f --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/DbTableInfo.cs @@ -0,0 +1,30 @@ +namespace SqlSugar +{ + public class DbTableInfo + { + public string Name { get; set; } + public string Description { get; set; } + public DbObjectType DbObjectType { get; set; } + } + + public class RazorTableInfo + { + public string DbTableName { get; set; } + public string ClassName { get; set; } + public string Description { get; set; } + public DbObjectType DbObjectType { get; set; } + public List Columns { get; set; } + } + + public class RazorColumnInfo + { + public string DbColumnName { get; set; } + public string DataType { get; set; } + public int Length { get; set; } + public string ColumnDescription { get; set; } + public string DefaultValue { get; set; } + public bool IsNullable { get; set; } + public bool IsIdentity { get; set; } + public bool IsPrimarykey { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/DefaultCustom.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/DefaultCustom.cs new file mode 100644 index 000000000..8e0797763 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/DefaultCustom.cs @@ -0,0 +1,107 @@ +using System.Data; +using System.Text; + +namespace SqlSugar.DbConvert +{ + public class EnumToStringConvert : ISugarDataConverter + { + public SugarParameter ParameterConverter(object columnValue, int columnIndex) + { + var name = "@MyEnum" + columnIndex; + Type undertype = SqlSugar.UtilMethods.GetUnderType(typeof(T));//获取没有nullable的枚举类型 + if (columnValue == null) + { + return new SugarParameter(name, null); + } + else + { + var enumObjString = Enum.Parse(undertype, columnValue + "").ToString(); + return new SugarParameter(name, enumObjString); + } + } + + public T QueryConverter(IDataRecord dr, int i) + { + + var str = dr.GetString(i); + Type undertype = SqlSugar.UtilMethods.GetUnderType(typeof(T));//获取没有nullable的枚举类型 + return (T)Enum.Parse(undertype, str); + } + } + + public class NoParameterCommonPropertyConvert : ISugarDataConverter + { + public SugarParameter ParameterConverter(object columnValue, int columnIndex) + { + if (columnValue == null) + { + return new SugarParameter("null", null, null); + } + return new SugarParameter($"{columnValue}", null, null); + } + + public T QueryConverter(IDataRecord dr, int i) + { + + var value = dr.GetValue(i); + return (T)UtilMethods.ChangeType2(value, typeof(T)); + } + } + public class CommonPropertyConvert : ISugarDataConverter + { + public SugarParameter ParameterConverter(object columnValue, int columnIndex) + { + var name = "@Common" + columnIndex; + Type undertype = SqlSugar.UtilMethods.GetUnderType(typeof(T));//获取没有nullable的枚举类型 + return new SugarParameter(name, columnValue, undertype); + } + + public T QueryConverter(IDataRecord dr, int i) + { + + + var value = dr.GetValue(i); + if (value is byte[] && typeof(T) != UtilConstants.ByteArrayType) + { + value = Encoding.UTF8.GetString((byte[])value); + } + return (T)UtilMethods.ChangeType2(value, typeof(T)); + + } + } + + + public class Nvarchar2PropertyConvert : ISugarDataConverter + { + public SugarParameter ParameterConverter(object columnValue, int columnIndex) + { + var name = "@Common" + columnIndex; + Type undertype = SqlSugar.UtilMethods.GetUnderType(typeof(T));//获取没有nullable的枚举类型 + return new SugarParameter(name, columnValue, undertype) { IsNvarchar2 = true }; + } + + public T QueryConverter(IDataRecord dr, int i) + { + + var value = dr.GetString(i); + return (T)(object)value; + } + } + + public class NClobPropertyConvert : ISugarDataConverter + { + public SugarParameter ParameterConverter(object columnValue, int columnIndex) + { + var name = "@Common" + columnIndex; + Type undertype = SqlSugar.UtilMethods.GetUnderType(typeof(T));//获取没有nullable的枚举类型 + return new SugarParameter(name, columnValue, undertype) { IsNClob = true }; + } + + public T QueryConverter(IDataRecord dr, int i) + { + + var value = dr.GetString(i); + return (T)(object)value; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/DefaultServices.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/DefaultServices.cs new file mode 100644 index 000000000..20ce86e6e --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/DefaultServices.cs @@ -0,0 +1,9 @@ +namespace SqlSugar +{ + public static class DefaultServices + { + public static ICacheService ReflectionInoCache = new ReflectionInoCacheService(); + public static ICacheService DataInoCache = null; + public static ISerializeService Serialize = new SerializeService(); + } +} \ No newline at end of file diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/DeleteNavOptions.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/DeleteNavOptions.cs new file mode 100644 index 000000000..8f6960030 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/DeleteNavOptions.cs @@ -0,0 +1,63 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public class DeleteNavOptions + { + public bool ManyToManyIsDeleteA { get; set; } + public bool ManyToManyIsDeleteB { get; set; } + } + public class DeleteNavRootOptions + { + public bool IsDiffLogEvent { get; set; } + public object DiffLogBizData { get; set; } + } + public class InsertNavRootOptions + { + public string[] IgnoreColumns { get; set; } + public string[] InsertColumns { get; set; } + public bool IsDiffLogEvent { get; set; } + public object DiffLogBizData { get; set; } + } + //public class InertNavRootOptions + //{ + // public string[] IgnoreColumns { get; set; } + // public string[] InsertColumns { get; set; } + //} + public class UpdateNavRootOptions + { + public string[] IgnoreColumns { get; set; } + public string[] UpdateColumns { get; set; } + public bool IsIgnoreAllNullColumns { get; set; } + public bool IsInsertRoot { get; set; } + public bool IsDisableUpdateRoot { get; set; } + public bool IsDiffLogEvent { get; set; } + public object DiffLogBizData { get; set; } + public string[] IgnoreInsertColumns { get; set; } + public bool IsOptLock { get; set; } + + } + public class UpdateNavOptions + { + public bool ManyToManyIsUpdateA { get; set; } + public bool ManyToManyIsUpdateB { get; set; } + public object ManyToManySaveMappingTemplate { get; set; } + public bool ManyToManyEnableLogicDelete { get; set; } + public bool OneToManyDeleteAll { get; set; } + public bool OneToManyEnableLogicDelete { get; set; } + public bool OneToManyNoDeleteNull { get; set; } + public bool OneToManyInsertOrUpdate { get; set; } + public Expression RootFunc { get; set; } + public Expression CurrentFunc { get; set; } + public string[] IgnoreColumns { get; set; } + public bool IgnoreNullColumns { get; set; } + public bool OneToOneSaveByPrimaryKey { get; set; } + } + + public class InsertNavOptions + { + public bool OneToManyIfExistsNoInsert { get; set; } + public bool ManyToManyNoDeleteMap { get; set; } + public object ManyToManySaveMappingTemplate { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/DiffLogModel.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/DiffLogModel.cs new file mode 100644 index 000000000..e30c7e134 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/DiffLogModel.cs @@ -0,0 +1,26 @@ +namespace SqlSugar +{ + public class DiffLogModel + { + public List AfterData { get; set; } + public List BeforeData { get; set; } + public SugarParameter[] Parameters { get; set; } + public string Sql { get; set; } + public TimeSpan? Time { get; set; } + public object BusinessData { get; set; } + public DiffType DiffType { get; set; } + } + public class DiffLogTableInfo + { + public string TableName { get; set; } + public string TableDescription { get; set; } + public List Columns { get; set; } + } + public class DiffLogColumnInfo + { + public string ColumnName { get; set; } + public string ColumnDescription { get; set; } + public object Value { get; set; } + public bool IsPrimaryKey { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/DiffType.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/DiffType.cs new file mode 100644 index 000000000..11acb5bef --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/DiffType.cs @@ -0,0 +1,9 @@ +namespace SqlSugar +{ + public enum DiffType + { + insert = 0, + update = 1, + delete = 2 + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/DiscriminatorObject .cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/DiscriminatorObject .cs new file mode 100644 index 000000000..3bcbd5f7b --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/DiscriminatorObject .cs @@ -0,0 +1,8 @@ +namespace SqlSugar +{ + public struct DiscriminatorObject + { + public string FieldName { get; set; } + public string FieldValue { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/DynamicSelectModel.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/DynamicSelectModel.cs new file mode 100644 index 000000000..88910390a --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/DynamicSelectModel.cs @@ -0,0 +1,183 @@ +namespace SqlSugar +{ + public class DynamicCoreSelectModel + { + public object Value { get; set; } + + public object ToList() + { + if (Value is null) + { + throw new InvalidOperationException("Value cannot be null."); + } + + var method = Value.GetType().GetMyMethod("ToList", 0); + if (method == null) + { + throw new InvalidOperationException("The Value object does not have a ToList method with no parameters."); + } + + return method.Invoke(Value, null); + } + + public async Task ToListAsync() + { + if (Value is null) + { + throw new InvalidOperationException("Value cannot be null."); + } + + var method = Value.GetType().GetMyMethod("ToListAsync", 0); + if (method == null) + { + throw new InvalidOperationException("The Value object does not have a ToListAsync method with no parameters."); + } + + var task = (Task)method.Invoke(Value, null); + return await GetTask(task).ConfigureAwait(false); + } + + public object ToPageList(int pageNumber, int pageSize) + { + if (Value is null) + { + throw new InvalidOperationException("Value cannot be null."); + } + + var method = Value.GetType().GetMyMethod("ToPageList", 2); + if (method == null) + { + throw new InvalidOperationException("The Value object does not have a ToPageList method with two parameters."); + } + + return method.Invoke(Value, new object[] { pageNumber, pageSize }); + } + + public async Task ToPageListAsync(int pageNumber, int pageSize) + { + if (Value is null) + { + throw new InvalidOperationException("Value cannot be null."); + } + + var method = Value.GetType().GetMyMethod("ToPageListAsync", 2); + if (method == null) + { + throw new InvalidOperationException("The Value object does not have a ToPageListAsync method with two parameters."); + } + + var task = (Task)method.Invoke(Value, new object[] { pageNumber, pageSize }); + return await GetTask(task).ConfigureAwait(false); + } + + public object ToPageList(int pageNumber, int pageSize, ref int totalNumber) + { + if (Value is null) + { + throw new InvalidOperationException("Value cannot be null."); + } + + var method = Value.GetType().GetMyMethod("ToPageList", 3); + if (method == null) + { + throw new InvalidOperationException("The Value object does not have a ToPageList method with three parameters."); + } + + var parameters = new object[] { pageNumber, pageSize, totalNumber }; + var result = method.Invoke(Value, parameters); + totalNumber = (int)parameters[2]; + return result; + } + + public async Task ToPageListAsync(int pageNumber, int pageSize, RefAsync totalNumber) + { + if (Value is null) + { + throw new InvalidOperationException("Value cannot be null."); + } + + var method = Value.GetType().GetMyMethod("ToPageListAsync", 3, typeof(int), typeof(int), typeof(RefAsync)); + if (method == null) + { + throw new InvalidOperationException("The Value object does not have a ToPageListAsync method with three parameters."); + } + + var parameters = new object[] { pageNumber, pageSize, totalNumber }; + var task = (Task)method.Invoke(Value, parameters); + var result = await GetTask(task).ConfigureAwait(false); + return result; + } + public object Single() + { + if (Value is null) + { + throw new InvalidOperationException("Value cannot be null."); + } + + var method = Value.GetType().GetMyMethod("Single", 0); + if (method == null) + { + throw new InvalidOperationException("The Value object does not have a Single method with no parameters."); + } + + return method.Invoke(Value, null); + } + public object First() + { + if (Value is null) + { + throw new InvalidOperationException("Value cannot be null."); + } + + var method = Value.GetType().GetMyMethod("First", 0); + if (method == null) + { + throw new InvalidOperationException("The Value object does not have a First method with no parameters."); + } + + return method.Invoke(Value, null); + } + + public async Task SingleAsync() + { + if (Value is null) + { + throw new InvalidOperationException("Value cannot be null."); + } + + var method = Value.GetType().GetMyMethod("SingleAsync", 0); + if (method == null) + { + throw new InvalidOperationException("The Value object does not have a SingleAsync method with no parameters."); + } + + var task = (Task)method.Invoke(Value, null); + return await GetTask(task).ConfigureAwait(false); + } + + public async Task FirstAsync() + { + if (Value is null) + { + throw new InvalidOperationException("Value cannot be null."); + } + + var method = Value.GetType().GetMyMethod("FirstAsync", 0); + if (method == null) + { + throw new InvalidOperationException("The Value object does not have a FirstAsync method with no parameters."); + } + + var task = (Task)method.Invoke(Value, null); + return await GetTask(task).ConfigureAwait(false); + } + + private static async Task GetTask(Task task) + { + await task.ConfigureAwait(false); // 等待任务完成 + var resultProperty = task.GetType().GetProperty("Result"); + var value = resultProperty.GetValue(task); + return value; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/EntityColumnInfo.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/EntityColumnInfo.cs new file mode 100644 index 000000000..7fca005ff --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/EntityColumnInfo.cs @@ -0,0 +1,49 @@ +using System.Reflection; + +namespace SqlSugar +{ + public class EntityColumnInfo + { + public PropertyInfo PropertyInfo { get; set; } + public string PropertyName { get; set; } + public string DbColumnName { get; set; } + public string OldDbColumnName { get; set; } + public int Length { get; set; } + public string ColumnDescription { get; set; } + public string DefaultValue { get; set; } + public bool IsNullable { get; set; } + public bool IsIdentity { get; set; } + public bool IsPrimarykey { get; set; } + public bool IsTreeKey { get; set; } + public bool IsEnableUpdateVersionValidation { get; set; } + public object SqlParameterDbType { get; set; } + public string EntityName { get; set; } + public string DbTableName { get; set; } + public bool IsIgnore { get; set; } + public string DataType { get; set; } + public int DecimalDigits { get; set; } + public string OracleSequenceName { get; set; } + public bool IsOnlyIgnoreInsert { get; set; } + public bool IsOnlyIgnoreUpdate { get; set; } + public bool IsTranscoding { get; set; } + public string SerializeDateTimeFormat { get; set; } + public bool IsJson { get; set; } + public bool NoSerialize { get; set; } + public string[] IndexGroupNameList { get; set; } + public string[] UIndexGroupNameList { get; set; } + public bool IsArray { get; set; } + public Type UnderType { get; set; } + public Navigate Navigat { get; set; } + public int CreateTableFieldSort { get; set; } + public object SqlParameterSize { get; set; } + public string InsertSql { get; set; } + public bool InsertServerTime { get; set; } + public bool UpdateServerTime { get; set; } + public string UpdateSql { get; set; } + public object ExtendedAttribute { get; set; } + public bool IsDisabledAlterColumn { get; set; } + public string QuerySql { get; set; } + public bool IsOwnsOne { get; set; } + public PropertyInfo ForOwnsOnePropertyInfo { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/EntityInfo.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/EntityInfo.cs new file mode 100644 index 000000000..69fa341b9 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/EntityInfo.cs @@ -0,0 +1,17 @@ +namespace SqlSugar +{ + public class EntityInfo + { + private string _DbTableName; + public string EntityName { get; set; } + public string DbTableName { get { return _DbTableName == null ? EntityName : _DbTableName; } set { _DbTableName = value; } } + public string TableDescription { get; set; } + public Type Type { get; set; } + public List Columns { get; set; } + public bool IsDisabledDelete { get; set; } + public bool IsDisabledUpdateAll { get; set; } + public List Indexs { get; set; } + public bool IsCreateTableFiledSort { get; set; } + public string Discrimator { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/JoinInfoParameter.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/JoinInfoParameter.cs new file mode 100644 index 000000000..731c6f5e5 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/JoinInfoParameter.cs @@ -0,0 +1,10 @@ +namespace SqlSugar +{ + public class JoinInfoParameter + { + public string TableName { get; set; } + public string ShortName { get; set; } + public IFuncModel Models { get; set; } + public JoinType Type { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/JoinMapper.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/JoinMapper.cs new file mode 100644 index 000000000..bc36ed222 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/JoinMapper.cs @@ -0,0 +1,8 @@ +namespace SqlSugar +{ + public class JoinMapper + { + public string AsName { get; set; } + public string DbName { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/JoinQueryInfo.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/JoinQueryInfo.cs new file mode 100644 index 000000000..8c2c68599 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/JoinQueryInfo.cs @@ -0,0 +1,61 @@ +namespace SqlSugar +{ + + public class JoinQueryInfo + { + public JoinType JoinType { get; set; } + public string TableName { get; set; } + public string ShortName { get; set; } + public int JoinIndex { get; set; } + public string JoinWhere { get; set; } + public Type EntityType { get; set; } + } + public class JoinQueryInfos + { + private JoinQueryInfos() { } + public JoinQueryInfos(JoinType joinType, bool whereExpress) + { + + } + public JoinQueryInfos(JoinType joinType, bool whereExpress, JoinType joinType2, bool whereExpress2) + { + + } + public JoinQueryInfos(JoinType joinType, bool whereExpress, JoinType joinType2, bool whereExpress2, JoinType joinType3, bool whereExpress3) + { + + } + public JoinQueryInfos(JoinType joinType, bool whereExpress, JoinType joinType2, bool whereExpress2, JoinType joinType3, bool whereExpress3, JoinType joinType4, bool whereExpress4) + { + + } + public JoinQueryInfos(JoinType joinType, bool whereExpress, JoinType joinType2, bool whereExpress2, JoinType joinType3, bool whereExpress3, JoinType joinType4, bool whereExpress4, JoinType joinType5, bool whereExpress5) + { + + } + public JoinQueryInfos(JoinType joinType, bool whereExpress, JoinType joinType2, bool whereExpress2, JoinType joinType3, bool whereExpress3, JoinType joinType4, bool whereExpress4, JoinType joinType5, bool whereExpress5, JoinType joinType6, bool whereExpress6) + { + + } + public JoinQueryInfos(JoinType joinType, bool whereExpress, JoinType joinType2, bool whereExpress2, JoinType joinType3, bool whereExpress3, JoinType joinType4, bool whereExpress4, JoinType joinType5, bool whereExpress5, JoinType joinType6, bool whereExpress6, JoinType joinType7, bool whereExpress7) + { + + } + public JoinQueryInfos(JoinType joinType, bool whereExpress, JoinType joinType2, bool whereExpress2, JoinType joinType3, bool whereExpress3, JoinType joinType4, bool whereExpress4, JoinType joinType5, bool whereExpress5, JoinType joinType6, bool whereExpress6, JoinType joinType7, bool whereExpress7, JoinType joinType8, bool whereExpress8) + { + + } + public JoinQueryInfos(JoinType joinType, bool whereExpress, JoinType joinType2, bool whereExpress2, JoinType joinType3, bool whereExpress3, JoinType joinType4, bool whereExpress4, JoinType joinType5, bool whereExpress5, JoinType joinType6, bool whereExpress6, JoinType joinType7, bool whereExpress7, JoinType joinType8, bool whereExpress8, JoinType joinType9, bool whereExpress9) + { + + } + public JoinQueryInfos(JoinType joinType, bool whereExpress, JoinType joinType2, bool whereExpress2, JoinType joinType3, bool whereExpress3, JoinType joinType4, bool whereExpress4, JoinType joinType5, bool whereExpress5, JoinType joinType6, bool whereExpress6, JoinType joinType7, bool whereExpress7, JoinType joinType8, bool whereExpress8, JoinType joinType9, bool whereExpress9, JoinType joinType10, bool whereExpress10) + { + + } + public JoinQueryInfos(JoinType joinType, bool whereExpress, JoinType joinType2, bool whereExpress2, JoinType joinType3, bool whereExpress3, JoinType joinType4, bool whereExpress4, JoinType joinType5, bool whereExpress5, JoinType joinType6, bool whereExpress6, JoinType joinType7, bool whereExpress7, JoinType joinType8, bool whereExpress8, JoinType joinType9, bool whereExpress9, JoinType joinType10, bool whereExpress10, JoinType joinType11, bool whereExpress11) + { + + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/ManyToManyConfig.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/ManyToManyConfig.cs new file mode 100644 index 000000000..574f7378e --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/ManyToManyConfig.cs @@ -0,0 +1,10 @@ +namespace SqlSugar +{ + public class ManyToMany + { + public static ManyToMany Config(AField aField, BField bField) + { + return null; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/MapperCache.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/MapperCache.cs new file mode 100644 index 000000000..55fae56d0 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/MapperCache.cs @@ -0,0 +1,153 @@ +namespace SqlSugar +{ + public class MapperCache + { + private Dictionary caches = new Dictionary(); + private List _list { get; set; } + private ISqlSugarClient _context { get; set; } + public int GetIndex { get; set; } + private MapperCache() + { + } + public MapperCache(List list, ISqlSugarClient context) + { + _list = list; + _context = context; + } + public Result Get(Func, Result> action) + { + GetIndex++; + string key = "Get" + typeof(Result) + action.GetHashCode() + action.Method.Name; + if (caches.TryGetValue(key, out object? value)) + { + return (Result)value; + } + else + { + var result = action(_list); + caches.Add(key, result); + return result; + } + } + public Result Get(Func, Result> action, string cachekey) + { + GetIndex++; + string key = "Get" + typeof(Result) + action.GetHashCode() + action.Method.Name + cachekey; + if (caches.TryGetValue(key, out object? value)) + { + return (Result)value; + } + else + { + var result = action(_list); + caches.Add(key, result); + return result; + } + } + public List GetListByPrimaryKeys(Func action) where Result : class, new() + { + { + string key = "GetListById" + typeof(Result) + action.GetHashCode().ToString(); + return GetListByPrimaryKeys(action, key); + } + } + public List GetListByPrimaryKeys(Func action) where Result : class, new() + { + { + string key = "GetListById" + typeof(Result) + action.GetHashCode().ToString(); + return GetListByPrimaryKeys(action, key); + } + } + public List GetListByPrimaryKeys(Func action) where Result : class, new() + { + { + string key = "GetListById" + typeof(Result) + action.GetHashCode().ToString(); + return GetListByPrimaryKeys(action, key); + } + } + public List GetListByPrimaryKeys(Func action) where Result : class, new() + { + { + string key = "GetListById" + typeof(Result) + action.GetHashCode().ToString(); + return GetListByPrimaryKeys(action, key); + } + } + public List GetListByPrimaryKeys(Func action) where Result : class, new() + { + { + string key = "GetListById" + typeof(Result) + action.GetHashCode().ToString(); + return GetListByPrimaryKeys(action, key); + } + } + public List GetListByPrimaryKeys(Func action) where Result : class, new() + { + { + string key = "GetListById" + typeof(Result) + action.GetHashCode().ToString(); + return GetListByPrimaryKeys(action, key); + } + } + public List GetListByPrimaryKeys(Func action) where Result : class, new() + { + { + string key = "GetListById" + typeof(Result) + action.GetHashCode().ToString(); + return GetListByPrimaryKeys(action, key); + } + } + public List GetListByPrimaryKeys(Func action) where Result : class, new() + { + { + string key = "GetListById" + typeof(Result) + action.GetHashCode().ToString(); + return GetListByPrimaryKeys(action, key); + } + } + public List GetListByPrimaryKeys(Func action) where Result : class, new() + { + { + string key = "GetListById" + typeof(Result) + action.GetHashCode().ToString(); + return GetListByPrimaryKeys(action, key); + } + } + public List GetListByPrimaryKeys(Func action) where Result : class, new() + { + { + string key = "GetListById" + typeof(Result) + action.GetHashCode().ToString(); + return GetListByPrimaryKeys(action, key); + } + } + public List GetListByPrimaryKeys(Func action) where Result : class, new() + { + { + string key = "GetListById" + typeof(Result) + action.GetHashCode().ToString(); + return GetListByPrimaryKeys(action, key); + } + } + public List GetListByPrimaryKeys(Func action) where Result : class, new() + { + { + string key = "GetListById" + typeof(Result) + action.GetHashCode().ToString(); + return GetListByPrimaryKeys(action, key); + } + } + public List GetListByPrimaryKeys(Func action) where Result : class, new() + { + { + string key = "GetListById" + typeof(Result) + action.GetHashCode().ToString(); + return GetListByPrimaryKeys(action, key); + } + } + private List GetListByPrimaryKeys(Func action, string key) where Result : class, new() + { + if (caches.TryGetValue(key, out object? value)) + { + return (List)value; + } + else + { + var ids = _list.Select(action).ToList().Distinct().ToList(); + var result = _context.Queryable().In(ids).ToList(); + caches.Add(key, result); + return result; + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/Mapping/IgnoreComumn.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/Mapping/IgnoreComumn.cs new file mode 100644 index 000000000..e2ac38a3b --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/Mapping/IgnoreComumn.cs @@ -0,0 +1,8 @@ +namespace SqlSugar +{ + public class IgnoreColumn + { + public string EntityName { get; set; } + public string PropertyName { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/Mapping/MappingColumn.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/Mapping/MappingColumn.cs new file mode 100644 index 000000000..b1e69472e --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/Mapping/MappingColumn.cs @@ -0,0 +1,9 @@ +namespace SqlSugar +{ + public class MappingColumn + { + public string PropertyName { get; set; } + public string DbColumnName { get; set; } + public string EntityName { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/Mapping/MappingTable.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/Mapping/MappingTable.cs new file mode 100644 index 000000000..bf1cd7d73 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/Mapping/MappingTable.cs @@ -0,0 +1,9 @@ +namespace SqlSugar +{ + public class MappingTable + { + public string EntityName { get; set; } + public string DbTableName { get; set; } + public string DbShortTaleName { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/Mapping/SugarMappingAttribute.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/Mapping/SugarMappingAttribute.cs new file mode 100644 index 000000000..1ab98a5a5 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/Mapping/SugarMappingAttribute.cs @@ -0,0 +1,463 @@ +namespace SqlSugar +{ + [AttributeUsage(AttributeTargets.Class, Inherited = true)] + public class SugarTable : Attribute + { + public SugarTable() { } + public string TableName { get; set; } + public string TableDescription { get; set; } + public bool IsDisabledDelete { get; set; } + public bool IsDisabledUpdateAll { get; set; } + public bool IsCreateTableFiledSort { get; set; } + public string Discrimator { get; set; } + public SugarTable(string tableName) + { + this.TableName = tableName; + } + public SugarTable(string tableName, string tableDescription) + { + this.TableName = tableName; + this.TableDescription = tableDescription; + } + + public SugarTable(string tableName, string tableDescription, bool isDisabledDelete) + { + this.TableName = tableName; + this.TableDescription = tableDescription; + this.IsDisabledDelete = isDisabledDelete; + } + public SugarTable(string tableName, string tableDescription, bool isDisabledDelete, bool isCreateTableFieldSort) + { + this.TableName = tableName; + this.TableDescription = tableDescription; + this.IsDisabledDelete = isDisabledDelete; + this.IsCreateTableFiledSort = isCreateTableFieldSort; + } + } + [AttributeUsage(AttributeTargets.Property, Inherited = true)] + public class SugarColumn : Attribute + { + private string _ColumnName; + public string ColumnName + { + get { return _ColumnName; } + set { _ColumnName = value; } + } + + private bool _IsIgnore; + public bool IsIgnore + { + get { return _IsIgnore; } + set { _IsIgnore = value; } + } + + private bool _IsPrimaryKey; + public bool IsPrimaryKey + { + get { return _IsPrimaryKey; } + set { _IsPrimaryKey = value; } + } + + private bool _IsIdentity; + public bool IsIdentity + { + get { return _IsIdentity; } + set { _IsIdentity = value; } + } + + private string _MappingKeys; + public string MappingKeys + { + get { return _MappingKeys; } + set { _MappingKeys = value; } + } + + private string _ColumnDescription; + public string ColumnDescription + { + get { return _ColumnDescription; } + set { _ColumnDescription = value; } + } + + private int _Length; + public int Length + { + get { return _Length; } + set { _Length = value; } + } + + private bool _IsNullable; + public bool IsNullable + { + get { return _IsNullable; } + set { _IsNullable = value; } + } + + private string _OldColumnName; + public string OldColumnName + { + get { return _OldColumnName; } + set { _OldColumnName = value; } + } + + private string _ColumnDataType; + public string ColumnDataType + { + get { return _ColumnDataType; } + set { _ColumnDataType = value; } + } + + private int _DecimalDigits; + public int DecimalDigits + { + get { return _DecimalDigits; } + set { _DecimalDigits = value; } + } + + private string _OracleSequenceName; + public string OracleSequenceName + { + get { return _OracleSequenceName; } + set { _OracleSequenceName = value; } + } + + private bool _IsOnlyIgnoreInsert; + public bool IsOnlyIgnoreInsert + { + get { return _IsOnlyIgnoreInsert; } + set { _IsOnlyIgnoreInsert = value; } + } + + private bool _IsOnlyIgnoreUpdate; + public bool IsOnlyIgnoreUpdate + { + get { return _IsOnlyIgnoreUpdate; } + set { _IsOnlyIgnoreUpdate = value; } + } + + + private bool _IsEnableUpdateVersionValidation; + public bool IsEnableUpdateVersionValidation + { + get { return _IsEnableUpdateVersionValidation; } + set { _IsEnableUpdateVersionValidation = value; } + } + + + + private bool _IsTranscoding; + public bool IsTranscoding + { + get { return _IsTranscoding; } + set { _IsTranscoding = value; } + } + + private bool _NoSerialize; + public bool NoSerialize + { + get { return _NoSerialize; } + set { _NoSerialize = value; } + } + + private string _SerializeDateTimeFormat; + public string SerializeDateTimeFormat + { + get { return _SerializeDateTimeFormat; } + set { _SerializeDateTimeFormat = value; } + } + + private bool _IsJson; + public bool IsJson + { + get { return _IsJson; } + set { _IsJson = value; } + } + + + private string _DefaultValue; + public string DefaultValue + { + get { return _DefaultValue; } + set { _DefaultValue = value; } + } + + private string[] _IndexGroupNameList; + public string[] IndexGroupNameList + { + get { return _IndexGroupNameList; } + set { _IndexGroupNameList = value; } + } + + public string[] UniqueGroupNameList { get; set; } + + private bool _IsArray; + public bool IsArray + { + get { return _IsArray; } + set { _IsArray = value; } + } + + private bool _IsTreeKey; + public bool IsTreeKey + { + get { return _IsTreeKey; } + set { _IsTreeKey = value; } + } + + public object SqlParameterDbType { get; set; } + public object SqlParameterSize { get; set; } + public int CreateTableFieldSort { get; set; } + public bool InsertServerTime { get; set; } + public string InsertSql { get; set; } + public string QuerySql { get; set; } + public bool UpdateServerTime { get; set; } + public string UpdateSql { get; set; } + public object ExtendedAttribute { get; set; } + public bool IsDisabledAlterColumn { get; set; } + public bool IsOwnsOne { get; set; } + } + + + [AttributeUsage(AttributeTargets.Class, Inherited = true)] + public class TenantAttribute : Attribute + { + public object configId { get; set; } + public TenantAttribute(object configId) + { + this.configId = configId; + } + } + + [AttributeUsage(AttributeTargets.Property, Inherited = true)] + public class Navigate : Attribute + { + internal string Name { get; set; } + internal string Name2 { get; set; } + internal Type MappingType { get; set; } + internal string MappingAId { get; set; } + internal string MappingBId { get; set; } + internal NavigateType NavigatType { get; set; } + internal string WhereSql { get; set; } + internal string AClassId { get; set; } + + internal string BClassId { get; set; } + + public string GetName() + { + return Name; + } + public string GetName2() + { + return Name2; + } + public Type GetMappingType() + { + return MappingType; + } + public string GetMappingAId() + { + return MappingAId; + } + public string GetMappingBId() + { + return MappingBId; + } + public NavigateType GetNavigateType() + { + return NavigatType; + } + + public string GetWhereSql() + { + return WhereSql; + } + public Navigate(NavigateType navigatType, string ifSingleMasterTableColumn_IfListChildTableColumn) + { + this.Name = ifSingleMasterTableColumn_IfListChildTableColumn; + this.NavigatType = navigatType; + } + public Navigate(NavigateType navigatType, string ifSingleMasterTableColumn_IfListChildTableColumn, string ifSingleChildTableColumn_IfListMasterTableColumn) + { + Check.ExceptionEasy(navigatType == NavigateType.ManyToMany, "Correct usage [Navigate(typeof(ABMapping), nameof(abmapping.aid), nameof(abmapp.bid))], incorrect usage: [Navigate(Navigate.ManyToMany, nameof(ABMapping.Aid), nameof(ABMapping.BId))]", "多对多第一个参数是Type不是NavigateType,正确用法[Navigate(typeof(ABMapping), nameof(ABMapping.Aid), nameof(ABMapping.BId))],错误用法:[Navigate(Navigate.ManyToMany, nameof(ABMapping.Aid), nameof(ABMapping.BId))]"); + this.Name = ifSingleMasterTableColumn_IfListChildTableColumn; + this.Name2 = ifSingleChildTableColumn_IfListMasterTableColumn; + this.NavigatType = navigatType; + } + + public Navigate(NavigateType navigatType, string ifSingleMasterTableColumn_IfListChildTableColumn, string ifSingleChildTableColumn_IfListMasterTableColumn, string whereSql) + { + this.Name = ifSingleMasterTableColumn_IfListChildTableColumn; + this.Name2 = ifSingleChildTableColumn_IfListMasterTableColumn; + this.NavigatType = navigatType; + this.WhereSql = whereSql; + //Check.ExceptionEasy(navigatType != NavigateType.OneToOne, "Currently, only one-to-one navigation configuration Sql conditions are supported", "目前导航配置Sql条件只支持一对一"); + } + + public Navigate(Type MappingTableType, string typeAId, string typeBId) + { + this.MappingType = MappingTableType; + this.MappingAId = typeAId; + this.MappingBId = typeBId; + this.NavigatType = NavigateType.ManyToMany; + } + public Navigate(Type MappingTableType, string mappingAId, string mappingBId, string aClassId, string bClassId) + { + this.MappingType = MappingTableType; + this.MappingAId = mappingAId; + this.MappingBId = mappingBId; + this.AClassId = aClassId; + this.BClassId = bClassId; + this.NavigatType = NavigateType.ManyToMany; + } + public Navigate(Type MappingTableType, string typeAiD, string typeBId, string mappingSql) + { + this.MappingType = MappingTableType; + this.MappingAId = typeAiD; + this.MappingBId = typeBId; + this.NavigatType = NavigateType.ManyToMany; + this.WhereSql += mappingSql; + } + } + + + + [AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = true)] + public class SugarIndexAttribute : Attribute + { + public string IndexName { get; set; } + public Dictionary IndexFields { get; set; } + public bool IsUnique { get; set; } + public SugarIndexAttribute(string indexName, string fieldName, OrderByType sortType, bool isUnique = false) + { + this.IndexName = indexName; + IndexFields = new Dictionary(); + IndexFields.Add(fieldName, sortType); + this.IsUnique = isUnique; + + } + public SugarIndexAttribute(string indexName, string fieldName1, OrderByType sortType1, string fieldName2, OrderByType sortType2, bool isUnique = false) + { + this.IndexName = indexName; + IndexFields = new Dictionary(); + IndexFields.Add(fieldName1, sortType1); + IndexFields.Add(fieldName2, sortType2); + this.IsUnique = isUnique; + } + public SugarIndexAttribute(string indexName, string fieldName1, OrderByType sortType1, string fieldName2, OrderByType sortType2, string fieldName3, OrderByType sortType3, bool isUnique = false) + { + this.IndexName = indexName; + IndexFields = new Dictionary(); + IndexFields.Add(fieldName1, sortType1); + IndexFields.Add(fieldName2, sortType2); + IndexFields.Add(fieldName3, sortType3); + this.IsUnique = isUnique; + } + public SugarIndexAttribute(string indexName, string fieldName1, OrderByType sortType1, string fieldName2, OrderByType sortType2, string fieldName3, OrderByType sortType3, string fieldName4, OrderByType sortType4, bool isUnique = false) + { + this.IndexName = indexName; + IndexFields = new Dictionary(); + IndexFields.Add(fieldName1, sortType1); + IndexFields.Add(fieldName2, sortType2); + IndexFields.Add(fieldName3, sortType3); + IndexFields.Add(fieldName4, sortType4); + this.IsUnique = isUnique; + } + public SugarIndexAttribute(string indexName, string fieldName1, OrderByType sortType1, string fieldName2, OrderByType sortType2, string fieldName3, OrderByType sortType3, string fieldName4, OrderByType sortType4, string fieldName5, OrderByType sortType5, bool isUnique = false) + { + this.IndexName = indexName; + IndexFields = new Dictionary(); + IndexFields.Add(fieldName1, sortType1); + IndexFields.Add(fieldName2, sortType2); + IndexFields.Add(fieldName3, sortType3); + IndexFields.Add(fieldName4, sortType4); + IndexFields.Add(fieldName5, sortType5); + this.IsUnique = isUnique; + } + public SugarIndexAttribute(string indexName, string fieldName1, OrderByType sortType1, string fieldName2, OrderByType sortType2, string fieldName3, OrderByType sortType3, string fieldName4, OrderByType sortType4, string fieldName5, OrderByType sortType5, string fieldName6, OrderByType sortType6, bool isUnique = false) + { + this.IndexName = indexName; + IndexFields = new Dictionary(); + IndexFields.Add(fieldName1, sortType1); + IndexFields.Add(fieldName2, sortType2); + IndexFields.Add(fieldName3, sortType3); + IndexFields.Add(fieldName4, sortType4); + IndexFields.Add(fieldName5, sortType5); + IndexFields.Add(fieldName6, sortType6); + this.IsUnique = isUnique; + } + public SugarIndexAttribute(string indexName, string fieldName1, OrderByType sortType1, string fieldName2, OrderByType sortType2, string fieldName3, OrderByType sortType3, string fieldName4, OrderByType sortType4, string fieldName5, OrderByType sortType5, string fieldName6, OrderByType sortType6, string fieldName7, OrderByType sortType7, bool isUnique = false) + { + this.IndexName = indexName; + IndexFields = new Dictionary(); + IndexFields.Add(fieldName1, sortType1); + IndexFields.Add(fieldName2, sortType2); + IndexFields.Add(fieldName3, sortType3); + IndexFields.Add(fieldName4, sortType4); + IndexFields.Add(fieldName5, sortType5); + IndexFields.Add(fieldName6, sortType6); + IndexFields.Add(fieldName7, sortType7); + this.IsUnique = isUnique; + } + public SugarIndexAttribute(string indexName, string fieldName1, OrderByType sortType1, string fieldName2, OrderByType sortType2, string fieldName3, OrderByType sortType3, string fieldName4, OrderByType sortType4, string fieldName5, OrderByType sortType5, string fieldName6, OrderByType sortType6, string fieldName7, OrderByType sortType7, string fieldName8, OrderByType sortType8, bool isUnique = false) + { + this.IndexName = indexName; + IndexFields = new Dictionary(); + IndexFields.Add(fieldName1, sortType1); + IndexFields.Add(fieldName2, sortType2); + IndexFields.Add(fieldName3, sortType3); + IndexFields.Add(fieldName4, sortType4); + IndexFields.Add(fieldName5, sortType5); + IndexFields.Add(fieldName6, sortType6); + IndexFields.Add(fieldName7, sortType7); + IndexFields.Add(fieldName8, sortType8); + this.IsUnique = isUnique; + } + public SugarIndexAttribute(string indexName, string fieldName1, OrderByType sortType1, string fieldName2, OrderByType sortType2, string fieldName3, OrderByType sortType3, string fieldName4, OrderByType sortType4, string fieldName5, OrderByType sortType5, string fieldName6, OrderByType sortType6, string fieldName7, OrderByType sortType7, string fieldName8, OrderByType sortType8, string fieldName9, OrderByType sortType9, bool isUnique = false) + { + this.IndexName = indexName; + IndexFields = new Dictionary(); + IndexFields.Add(fieldName1, sortType1); + IndexFields.Add(fieldName2, sortType2); + IndexFields.Add(fieldName3, sortType3); + IndexFields.Add(fieldName4, sortType4); + IndexFields.Add(fieldName5, sortType5); + IndexFields.Add(fieldName6, sortType6); + IndexFields.Add(fieldName7, sortType7); + IndexFields.Add(fieldName8, sortType8); + IndexFields.Add(fieldName9, sortType9); + this.IsUnique = isUnique; + } + public SugarIndexAttribute(string indexName, string fieldName1, OrderByType sortType1, string fieldName2, OrderByType sortType2, string fieldName3, OrderByType sortType3, string fieldName4, OrderByType sortType4, string fieldName5, OrderByType sortType5, string fieldName6, OrderByType sortType6, string fieldName7, OrderByType sortType7, string fieldName8, OrderByType sortType8, string fieldName9, OrderByType sortType9, string fieldName10, OrderByType sortType10, bool isUnique = false) + { + this.IndexName = indexName; + IndexFields = new Dictionary(); + IndexFields.Add(fieldName1, sortType1); + IndexFields.Add(fieldName2, sortType2); + IndexFields.Add(fieldName3, sortType3); + IndexFields.Add(fieldName4, sortType4); + IndexFields.Add(fieldName5, sortType5); + IndexFields.Add(fieldName6, sortType6); + IndexFields.Add(fieldName7, sortType7); + IndexFields.Add(fieldName8, sortType8); + IndexFields.Add(fieldName9, sortType9); + IndexFields.Add(fieldName10, sortType10); + this.IsUnique = isUnique; + } + + public SugarIndexAttribute(string indexName, string[] fieldNames, OrderByType[] sortTypes, bool isUnique = false) + { + if (fieldNames.Length != sortTypes.Length) + { + Check.ExceptionEasy($"SugarIndexAttribute {indexName} fieldNames.Length!=sortTypes.Length 检查索引特性", $"SugarIndexAttribute {indexName} fieldNames.Length!=sortTypes.Length"); + } + this.IndexName = indexName; + IndexFields = new Dictionary(); + for (int i = 0; i < fieldNames.Length; i++) + { + IndexFields.Add(fieldNames[i], sortTypes[i]); + } + this.IsUnique = isUnique; + } + } + +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/ModelContext.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/ModelContext.cs new file mode 100644 index 000000000..616602369 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/ModelContext.cs @@ -0,0 +1,19 @@ +using Newtonsoft.Json; + +namespace SqlSugar +{ + public class ModelContext + { + [SugarColumn(IsIgnore = true)] + [JsonIgnore] + public SqlSugarProvider Context { get; set; } + public ISugarQueryable CreateMapping() where T : class, new() + { + Check.ArgumentNullException(Context, "Please use Sqlugar.ModelContext"); + using (Context) + { + return Context.Queryable(); + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/NavigationInitializer.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/NavigationInitializer.cs new file mode 100644 index 000000000..63b2576c9 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/NavigationInitializer.cs @@ -0,0 +1,54 @@ +namespace SqlSugar +{ + internal static class OneToOneGlobalInstanceRegistry + { + private static readonly Dictionary _instances = new Dictionary(); + private static readonly object _lock = new object(); + + public static Dictionary Instances => _instances; + + public static bool IsAny() + { + return _instances?.Count > 0; + } + public static bool IsNavigationInitializerCreated(object instance) + { + if (instance == null) + return false; + + Type type = instance.GetType(); + + lock (_lock) + { + return _instances.ContainsKey(type) && _instances[type] == instance; + } + } + + public static void RegisterInstance(Type type, object instance) + { + lock (_lock) + { + if (!_instances.ContainsKey(type)) + { + _instances[type] = instance; + } + } + } + } + + public class OneToOneInitializer where T : new() + { + public static implicit operator T(OneToOneInitializer initializer) + { + Type type = typeof(T); + + if (!OneToOneGlobalInstanceRegistry.Instances.ContainsKey(type)) + { + T instance = new T(); + OneToOneGlobalInstanceRegistry.RegisterInstance(type, instance); + } + + return (T)OneToOneGlobalInstanceRegistry.Instances[type]; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/PageModel.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/PageModel.cs new file mode 100644 index 000000000..b6e229fd2 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/PageModel.cs @@ -0,0 +1,12 @@ +namespace SqlSugar +{ + public class PageModel + { + public int PageIndex { get; set; } + public int PageSize { get; set; } + /// + /// output + /// + public int TotalCount { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/PropertyMetadata.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/PropertyMetadata.cs new file mode 100644 index 000000000..bde1f3dc3 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/PropertyMetadata.cs @@ -0,0 +1,11 @@ +using System.Reflection.Emit; + +namespace SqlSugar +{ + public class PropertyMetadata + { + public string Name { get; set; } + public Type Type { get; set; } + public List CustomAttributes { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/QueueItem.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/QueueItem.cs new file mode 100644 index 000000000..8332deb14 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/QueueItem.cs @@ -0,0 +1,8 @@ +namespace SqlSugar +{ + public class QueueItem + { + public string Sql { get; set; } + public SugarParameter[] Parameters { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/SchemaInfo.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/SchemaInfo.cs new file mode 100644 index 000000000..1b0939c4c --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/SchemaInfo.cs @@ -0,0 +1,8 @@ +namespace SqlSugar +{ + public class SchemaInfo + { + public string TableName { get; set; } + public string SchemaName { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/SingleColumnsEntity.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/SingleColumnsEntity.cs new file mode 100644 index 000000000..a680b77a4 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/SingleColumnsEntity.cs @@ -0,0 +1,7 @@ +namespace SqlSugar +{ + public class SingleColumnEntity + { + public T ColumnName { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/SlaveConnectionConfig.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/SlaveConnectionConfig.cs new file mode 100644 index 000000000..c11e984d4 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/SlaveConnectionConfig.cs @@ -0,0 +1,12 @@ +namespace SqlSugar +{ + public class SlaveConnectionConfig + { + /// + ///Default value is 1 + ///If value is 0 means permanent non execution + /// + public int HitRate = 1; + public string ConnectionString { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/SqlFilter.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/SqlFilter.cs new file mode 100644 index 000000000..5b0acdbdd --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/SqlFilter.cs @@ -0,0 +1,53 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public class SqlFilterItem + { + /// + /// Equal to NULL representing global + /// + public string FilterName { get; set; } + public Func FilterValue { get; set; } + /// + /// Is it a multiple table query? + /// + public bool IsJoinQuery { get; set; } + internal Type type { get; set; } + } + + public class TableFilterItem : SqlFilterItem + { + private TableFilterItem() + { + + } + private Expression exp { get; set; } + public TableFilterItem(Expression> expression, bool isJoinOn = false) + { + exp = expression; + type = typeof(T); + base.IsJoinQuery = isJoinOn; + this.IsJoinQuery = isJoinOn; + } + + public TableFilterItem(Type entityType, Expression expression, bool isJoinOn = false) + { + exp = expression; + type = entityType; + base.IsJoinQuery = isJoinOn; + this.IsJoinQuery = isJoinOn; + } + + private new string FilterName { get; set; } + private new Func FilterValue { get; set; } + private new bool IsJoinQuery { get; set; } + } + + + public class SqlFilterResult + { + public string Sql { get; set; } + public object Parameters { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/SqlSguarTransaction.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/SqlSguarTransaction.cs new file mode 100644 index 000000000..a02d138d5 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/SqlSguarTransaction.cs @@ -0,0 +1,47 @@ +namespace SqlSugar +{ + public class SqlSugarTransaction : IDisposable + { + private readonly SqlSugarClient context; + + public SqlSugarTransaction(SqlSugarClient client) + { + context = client; + context.BeginTran(); + } + public void CommitTran() + { + context.CommitTran(); + } + public void RollbackTran() + { + context.RollbackTran(); + } + public void Dispose() + { + context.RollbackTran(); + } + } + public class SqlSugarTransactionAdo : IDisposable + { + private readonly ISqlSugarClient context; + + public SqlSugarTransactionAdo(ISqlSugarClient client) + { + context = client; + context.Ado.BeginTran(); + } + public void CommitTran() + { + context.Ado.CommitTran(); + } + public void RollbackTran() + { + context.Ado.RollbackTran(); + } + public void Dispose() + { + context.Ado.RollbackTran(); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/SqlWith.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/SqlWith.cs new file mode 100644 index 000000000..35c97cf7f --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/SqlWith.cs @@ -0,0 +1,15 @@ +namespace SqlSugar +{ + public partial class SqlWith + { + public const string NoLock = "WITH(NOLOCK) "; + public const string HoldLock = "WITH(HOLDLOCK)"; + public const string PagLock = "WITH(PAGLOCK)"; + public const string ReadCommitted = "WITH(READCOMMITTED)"; + public const string TabLockX = "WITH(TABLOCKX)"; + public const string UpdLock = "WITH(UPDLOCK)"; + public const string RowLock = "WITH(ROWLOCK)"; + public const string ReadPast = "WITH(READPAST)"; + public const string Null = "Non"; + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/StackTraceInfo.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/StackTraceInfo.cs new file mode 100644 index 000000000..33c6a9399 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/StackTraceInfo.cs @@ -0,0 +1,18 @@ +namespace SqlSugar +{ + public class StackTraceInfo + { + public string FirstFileName { get { return this.MyStackTraceList.First().FileName; } } + public string FirstMethodName { get { return this.MyStackTraceList.First().MethodName; } } + public int FirstLine { get { return this.MyStackTraceList.First().Line; } } + + public List MyStackTraceList { get; set; } + public List SugarStackTraceList { get; set; } + } + public class StackTraceInfoItem + { + public string FileName { get; set; } + public string MethodName { get; set; } + public int Line { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/SubInsertTree.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/SubInsertTree.cs new file mode 100644 index 000000000..1b7299040 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/SubInsertTree.cs @@ -0,0 +1,16 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public class SubInsertTree + { + public object Expression { get; set; } + public List ChildExpression { get; set; } + } + + internal class SubInsertTreeExpression + { + public Expression Expression { get; set; } + public List Childs { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/SugarAbMapping.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/SugarAbMapping.cs new file mode 100644 index 000000000..9f0bbb5d4 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/SugarAbMapping.cs @@ -0,0 +1,8 @@ +namespace SqlSugar +{ + public class SugarAbMapping + { + public string Aid { get; set; } + public string Bid { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/SugarConnection.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/SugarConnection.cs new file mode 100644 index 000000000..67b154202 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/SugarConnection.cs @@ -0,0 +1,17 @@ +using System.Data; + +namespace SqlSugar +{ + public class SugarConnection : IDisposable + { + public IDbConnection conn { get; set; } + public bool IsAutoClose { get; set; } + public ISqlSugarClient Context { get; set; } + + public void Dispose() + { + conn.Close(); + this.Context.CurrentConnectionConfig.IsAutoCloseConnection = IsAutoClose; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/SugarDebugger.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/SugarDebugger.cs new file mode 100644 index 000000000..73ddd29db --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/SugarDebugger.cs @@ -0,0 +1,13 @@ +namespace SqlSugar +{ + /// + /// Used for debugging errors or BUG,Used for debugging, which has an impact on Performance + /// + public class SugarDebugger + { + /// + /// If you use the same Db object across threads, you will be prompted + /// + public bool EnableThreadSecurityValidation { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/SugarList.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/SugarList.cs new file mode 100644 index 000000000..4c0528f34 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/SugarList.cs @@ -0,0 +1,66 @@ +namespace SqlSugar +{ + public class MappingTableList : List + { + public void Add(string entityName, string dbTableName) + { + this.RemoveAll(it => it.EntityName.Equals(entityName, StringComparison.CurrentCultureIgnoreCase)); + this.Add(new MappingTable() { EntityName = entityName, DbTableName = dbTableName }); + } + public void Add(string entityName, string dbTableName, string dbTableShortName) + { + this.RemoveAll(it => it.EntityName.Equals(entityName, StringComparison.CurrentCultureIgnoreCase)); + this.Add(new MappingTable() { EntityName = entityName, DbTableName = dbTableName, DbShortTaleName = dbTableShortName }); + } + public new void Clear() + { + this.RemoveAll(it => true); + } + } + + public class IgnoreColumnList : List + { + public void Add(string propertyName, string EntityName) + { + this.RemoveAll(it => it.EntityName == EntityName && it.PropertyName.Equals(propertyName, StringComparison.CurrentCultureIgnoreCase)); + this.Add(new IgnoreColumn() { PropertyName = propertyName, EntityName = EntityName }); + } + + public new void Clear() + { + this.RemoveAll(it => true); + } + } + + public class MappingColumnList : List + { + public void Add(string propertyName, string dbColumnName, string entityName) + { + this.RemoveAll(it => it.EntityName == entityName && it.PropertyName.Equals(propertyName, StringComparison.CurrentCultureIgnoreCase)); + this.Add(new MappingColumn() { PropertyName = propertyName, DbColumnName = dbColumnName, EntityName = entityName }); + } + public new void Clear() + { + this.RemoveAll(it => true); + } + } + + + public class QueueList : List + { + public void Add(string sql, SugarParameter[] parameters) + { + this.Add(new QueueItem() { Sql = sql, Parameters = parameters }); + } + public void Add(string sql, List parameters) + { + if (parameters == null) + parameters = new List(); + this.Add(new QueueItem() { Sql = sql, Parameters = parameters.ToArray() }); + } + public new void Clear() + { + this.RemoveAll(it => true); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/SugarTerant.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/SugarTerant.cs new file mode 100644 index 000000000..2148b8b24 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Entities/SugarTerant.cs @@ -0,0 +1,8 @@ +namespace SqlSugar +{ + public class SugarTenant + { + public SqlSugarProvider Context { get; set; } + public ConnectionConfig ConnectionConfig { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/ApplyType.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/ApplyType.cs new file mode 100644 index 000000000..a7229f2cd --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/ApplyType.cs @@ -0,0 +1,8 @@ +namespace SqlSugar +{ + public enum ApplyType + { + Cross = 1, + Outer = 2 + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/ConditionalType.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/ConditionalType.cs new file mode 100644 index 000000000..0849f277d --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/ConditionalType.cs @@ -0,0 +1,23 @@ +namespace SqlSugar +{ + public enum ConditionalType + { + Equal = 0, + Like = 1, + GreaterThan = 2, + GreaterThanOrEqual = 3, + LessThan = 4, + LessThanOrEqual = 5, + In = 6, + NotIn = 7, + LikeLeft = 8, + LikeRight = 9, + NoEqual = 10, + IsNullOrEmpty = 11, + IsNot = 12, + NoLike = 13, + EqualNull = 14, + InLike = 15, + Range = 16 + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/DataFilterType.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/DataFilterType.cs new file mode 100644 index 000000000..6aafbcd22 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/DataFilterType.cs @@ -0,0 +1,64 @@ +using System.Reflection; + +namespace SqlSugar +{ + public enum DataFilterType + { + UpdateByObject = 0, + InsertByObject = 1, + DeleteByObject = 2 + } + public class DataFilterModel + { + public DataFilterType OperationType { get; set; } + public EntityColumnInfo EntityColumnInfo { get; set; } + public object EntityValue { get; set; } + public string PropertyName { get { return EntityColumnInfo.PropertyInfo.Name; } } + public string EntityName { get { return EntityColumnInfo.EntityName; } } + + + public void SetValue(object value) + { + try + { + var type = EntityColumnInfo.PropertyInfo.PropertyType; + if (value != null && value.GetType() != type) + { + value = UtilMethods.ChangeType2(value, type); + } + this.EntityColumnInfo.PropertyInfo.SetValue(EntityValue, value); + } + catch (Exception ex) + { + Check.ExceptionEasy($" SetValue error in DataExecuting {EntityName} . {ex.Message}", $" DataExecuting 中 SetValue出错 {EntityName} 。 {ex.Message}"); + } + } + public bool IsAnyAttribute() where T : Attribute + { + return this.EntityColumnInfo.PropertyInfo.GetCustomAttribute() != null; + } + public T GetAttribute() where T : Attribute + { + return this.EntityColumnInfo.PropertyInfo.GetCustomAttribute(); + } + } + public class DataAfterModel + { + + public List EntityColumnInfos { get; set; } + public object EntityValue { get; set; } + public EntityInfo Entity { get; set; } + public object GetValue(string propertyName) + { + var propety = EntityColumnInfos.FirstOrDefault(it => it.PropertyName == propertyName); + Check.ExceptionEasy(propety == null, $"Aop.DataExecuted error . {Entity.EntityName} no property {propertyName}.", $"Aop.DataExecuted 出错 {Entity.EntityName}不存在属性{propertyName}"); + return propety.PropertyInfo.GetValue(EntityValue); + } + public void SetValue(string propertyName, object value) + { + var propety = EntityColumnInfos.FirstOrDefault(it => it.PropertyName == propertyName); + Check.ExceptionEasy(propety == null, $"Aop.DataExecuted error . {Entity.EntityName} no property {propertyName}.", $"Aop.DataExecuted 出错 {Entity.EntityName}不存在属性{propertyName}"); + propety.PropertyInfo.SetValue(EntityValue, value); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/DbLockType.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/DbLockType.cs new file mode 100644 index 000000000..ad52e80d4 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/DbLockType.cs @@ -0,0 +1,8 @@ +namespace SqlSugar +{ + public enum DbLockType + { + Wait = 0, + Error = 1 + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/DbObjectType.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/DbObjectType.cs new file mode 100644 index 000000000..bd9f01148 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/DbObjectType.cs @@ -0,0 +1,9 @@ +namespace SqlSugar +{ + public enum DbObjectType + { + Table = 0, + View = 1, + All = 2 + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/DbType.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/DbType.cs new file mode 100644 index 000000000..7d93449a9 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/DbType.cs @@ -0,0 +1,40 @@ +namespace SqlSugar +{ + public enum DbType + { + MySql, + SqlServer, + Sqlite, + Oracle, + PostgreSQL, + Dm, + Kdbndp, + Oscar, + MySqlConnector, + Access, + OpenGauss, + QuestDB, + HG, + ClickHouse, + GBase, + Odbc, + OceanBaseForOracle, + TDengine, + GaussDB, + OceanBase, + Tidb, + Vastbase, + PolarDB, + Doris, + Xugu, + GoldenDB, + TDSQLForPGODBC, + TDSQL, + HANA, + DB2, + GaussDBNative, + DuckDB, + MongoDb, + Custom = 900 + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/InitKeyType.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/InitKeyType.cs new file mode 100644 index 000000000..a94517b0d --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/InitKeyType.cs @@ -0,0 +1,10 @@ +namespace SqlSugar +{ + public enum InitKeyType + { + /// + /// Init primary key and identity key from the attribute + /// + Attribute = 1 + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/JoinType.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/JoinType.cs new file mode 100644 index 000000000..b454611d2 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/JoinType.cs @@ -0,0 +1,11 @@ +namespace SqlSugar +{ + public enum JoinType + { + Inner = 0, + Left = 1, + Right = 2, + Full = 3, + Cross + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/LanguageType.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/LanguageType.cs new file mode 100644 index 000000000..621f85a9e --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/LanguageType.cs @@ -0,0 +1,9 @@ +namespace SqlSugar +{ + public enum LanguageType + { + Default = 0, + Chinese = 1, + English = 2 + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/NavigatType.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/NavigatType.cs new file mode 100644 index 000000000..a46c01117 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/NavigatType.cs @@ -0,0 +1,11 @@ +namespace SqlSugar +{ + public enum NavigateType + { + OneToOne = 1, + OneToMany = 2, + ManyToOne = 3, + ManyToMany = 4, + Dynamic = 5 + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/OrderByType.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/OrderByType.cs new file mode 100644 index 000000000..8c18840b1 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/OrderByType.cs @@ -0,0 +1,8 @@ +namespace SqlSugar +{ + public enum OrderByType + { + Asc = 0, + Desc = 1 + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/ProperyType.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/ProperyType.cs new file mode 100644 index 000000000..8034eb13d --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/ProperyType.cs @@ -0,0 +1,27 @@ +namespace SqlSugar +{ + public enum CSharpDataType + { + @int, + @bool, + @string, + @DateTime, + @decimal, + @double, + @Guid, + @byte, + @sbyte, + @enum, + @short, + @long, + @object, + @other, + @byteArray, + @float, + @time, + @DateTimeOffset, + @Single, + @TimeSpan, + @char + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/ReportableDateType.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/ReportableDateType.cs new file mode 100644 index 000000000..9e1f89100 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/ReportableDateType.cs @@ -0,0 +1,12 @@ +namespace SqlSugar +{ + public enum ReportableDateType + { + MonthsInLast1years = 0, + MonthsInLast3years = 1, + MonthsInLast10years = 2, + years1 = 3, + years3 = 4, + years10 = 5 + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/SampleByUnit.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/SampleByUnit.cs new file mode 100644 index 000000000..65f7bda33 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/SampleByUnit.cs @@ -0,0 +1,14 @@ +namespace SqlSugar +{ + public enum SampleByUnit + { + Second, + Minute, + Hour, + Day, + Month, + Year, + Millisecond, + Microsecond, + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/SugarActionType.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/SugarActionType.cs new file mode 100644 index 000000000..b7c3826b7 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/SugarActionType.cs @@ -0,0 +1,11 @@ +namespace SqlSugar +{ + public enum SugarActionType + { + Insert = 0, + Update = 1, + Delete = 2, + Query = 3, + UnKnown = -1 + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/SugarDateTimeFormat.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/SugarDateTimeFormat.cs new file mode 100644 index 000000000..71447e8be --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/SugarDateTimeFormat.cs @@ -0,0 +1,8 @@ +namespace SqlSugar +{ + public partial class SugarDateTimeFormat + { + public const string Default = "yyyy-MM-dd HH:mm:ss"; + public const string Date = "yyyy-MM-dd HH"; + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/WhereType.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/WhereType.cs new file mode 100644 index 000000000..61454536c --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Enum/WhereType.cs @@ -0,0 +1,9 @@ +namespace SqlSugar +{ + public enum WhereType + { + And = 0, + Or = 1, + Null = -1 + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/CaseWhen/CaseWhen.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/CaseWhen/CaseWhen.cs new file mode 100644 index 000000000..c0cde3176 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/CaseWhen/CaseWhen.cs @@ -0,0 +1,26 @@ +namespace SqlSugar +{ + public class CaseWhen + { + public CaseThen ElseIF(bool condition) + { + return null; + } + public T End(T defaultValue) + { + return default(T); + } + public T End() + { + return default(T); + } + } + public class CaseThen + { + + public CaseWhen Return(T result) + { + return null; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/CaseWhen/CaseWhenResolve.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/CaseWhen/CaseWhenResolve.cs new file mode 100644 index 000000000..3e742c2d7 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/CaseWhen/CaseWhenResolve.cs @@ -0,0 +1,89 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + + public class CaseWhenResolve + { + List allMethods = new List(); + private ExpressionContext context = null; + public CaseWhenResolve(MethodCallExpression expression, ExpressionContext context, Expression oppsiteExpression) + { + this.context = context; + var currentExpression = expression; + allMethods.Add(currentExpression); + if (context.IsSingle && oppsiteExpression != null && oppsiteExpression is MemberExpression) + { + var childExpression = (oppsiteExpression as MemberExpression).Expression; + if (childExpression is ParameterExpression) + { + this.context.SingleTableNameSubqueryShortName = (childExpression as ParameterExpression).Name; + } + } + else if (context.IsSingle) + { + if (context.Expression is LambdaExpression) + { + this.context.SingleTableNameSubqueryShortName = (context.Expression as LambdaExpression).Parameters.First().Name; + } + } + while (currentExpression != null) + { + var addItem = currentExpression.Object as MethodCallExpression; + if (addItem != null) + allMethods.Add(addItem); + currentExpression = addItem; + } + } + + public string GetSql() + { + allMethods.Reverse(); + List> sqls = new List>(); + foreach (var methodExp in allMethods) + { + var isFirst = allMethods.First() == methodExp; + var isLast = allMethods.Last() == methodExp; + var isIsNegate = false; + if (methodExp.Arguments.Count == 0) + { + sqls.Add(new KeyValuePair(methodExp.Method.Name, "null")); + } + else + { + var exp = methodExp.Arguments[0]; + if (ExpressionTool.IsNegate(exp)) + { + isIsNegate = true; + exp = (exp as UnaryExpression).Operand; + } + if (methodExp.Method.Name.IsIn("Return", "End") && exp.Type == UtilConstants.BoolType && ExpressionTool.IsEqualOrLtOrGt(exp)) + { + exp = ExpressionTool.GetConditionalExpression(exp); + } + else if (methodExp.Method.Name.IsIn("Return", "End") && exp.Type == UtilConstants.BoolType && ExpressionTool.GetMethodName(exp).IsIn("Contains", "StartsWith", "EndsWith")) + { + exp = ExpressionTool.GetConditionalExpression(exp); + } + var sql = SubTools.GetMethodValue(this.context, exp, this.context.IsSingle ? ResolveExpressType.WhereSingle : ResolveExpressType.WhereMultiple); + if (methodExp.Method.Name == "IF") + { + var parameter = this.context.Parameters.FirstOrDefault(it => it.ParameterName == sql.Trim()); + if (parameter?.Value is bool) + { + sql = Convert.ToBoolean(parameter.Value) ? " 1=1 " : " 1=2 "; + this.context.Parameters.Remove(parameter); + } + } + if (isIsNegate) + { + sql = " (" + sql + "*-1) "; + } + sqls.Add(new KeyValuePair(methodExp.Method.Name, sql)); + } + } + var result = this.context.DbMehtods.CaseWhen(sqls); + return result; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/BinaryExpressionInfo.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/BinaryExpressionInfo.cs new file mode 100644 index 000000000..68782f5cd --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/BinaryExpressionInfo.cs @@ -0,0 +1,9 @@ +namespace SqlSugar +{ + public class BinaryExpressionInfo + { + public bool IsLeft { get; set; } + public Type ExpressionType { get; set; } + public object Value { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/CommonTempDataType.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/CommonTempDataType.cs new file mode 100644 index 000000000..1578d090a --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/CommonTempDataType.cs @@ -0,0 +1,10 @@ +namespace SqlSugar +{ + public enum CommonTempDataType + { + Default = 0, + Result = 1, + Append = 2, + Simple = 3 + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/DateType.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/DateType.cs new file mode 100644 index 000000000..e5f37d885 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/DateType.cs @@ -0,0 +1,15 @@ +namespace SqlSugar +{ + public enum DateType + { + Year = 1, + Month = 2, + Day = 3, + Hour = 4, + Second = 5, + Minute = 6, + Millisecond = 7, + Weekday = 8, + Quarter = 9 + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/ErrorMessage.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/ErrorMessage.cs new file mode 100644 index 000000000..68d9a0840 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/ErrorMessage.cs @@ -0,0 +1,51 @@ +namespace SqlSugar +{ + internal static partial class ErrorMessage + { + internal static string OperatorError + { + get + { + return ErrorMessage.GetThrowMessage("Lambda parsing error: {0} does not support the operator to find!", "拉姆达解析出错:不支持{0}此种运算符查找!"); + } + } + internal static string ExpFileldError + { + get + { + return ErrorMessage.GetThrowMessage("Expression format error, correct format: it=>it.fieldName", "表达式格式错误,正确格式: it=>it.fieldName"); + } + } + + internal static string MethodError + { + get + { + return ErrorMessage.GetThrowMessage("Expression parsing does not support the current function {0}. There are many functions available in the SqlFunc class, for example, it=>SqlFunc.HasValue(it.Id)", "拉姆达解析不支持当前函数{0},SqlFunc这个类里面有大量函数可用,也许有你想要的,例如: it=>SqlFunc.HasValue(it.Id)"); + } + } + + public static string ConnnectionOpen + { + get + { + return ErrorMessage.GetThrowMessage("Connection open error . {0} ", " 连接数据库过程中发生错误,检查服务器是否正常连接字符串是否正确,错误信息:{0}."); + } + } + public static string ExpressionCheck + { + get + { + return ErrorMessage.GetThrowMessage("Join {0} needs to be the same as {1} {2}", "多表查询存在别名不一致,请把{1}中的{2}改成{0}就可以了,特殊需求可以使用.Select((x,y)=>new{{ id=x.id,name=y.name}}).MergeTable().Orderby(xxx=>xxx.Id)功能将Select中的多表结果集变成单表,这样就可以不限制别名一样"); + } + } + + public static string WhereIFCheck + { + get + { + return ErrorMessage.GetThrowMessage("Subquery.WhereIF.IsWhere {0} not supported", "Subquery.WhereIF 第一个参数不支持表达式中的变量,只支持外部变量"); + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/ExpResolveAccessory.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/ExpResolveAccessory.cs new file mode 100644 index 000000000..703747702 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/ExpResolveAccessory.cs @@ -0,0 +1,8 @@ +namespace SqlSugar +{ + public class ExpResolveAccessory + { + protected List _Parameters; + protected ExpressionResult _Result; + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/ExpressionConst.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/ExpressionConst.cs new file mode 100644 index 000000000..e6e7e28c6 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/ExpressionConst.cs @@ -0,0 +1,14 @@ +namespace SqlSugar +{ + internal static class ExpressionConst + { + public const string Const = "Const"; + public const string FormatSymbol = "{0}"; + public const string RightParenthesis = ")"; + public const string LeftParenthesis = "("; + public const string MethodConst = "MethodConst"; + public const string SqlFuncFullName = "SqlSugar.SqlFunc"; + public const string BinaryFormatString = " ( {0} {1} {2} ) "; + public const string ExpressionReplace = "46450BDC-77B7-4025-B2A6-3F048CA85AD0"; + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/ExpressionContextCase.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/ExpressionContextCase.cs new file mode 100644 index 000000000..0307b525f --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/ExpressionContextCase.cs @@ -0,0 +1,7 @@ +namespace SqlSugar +{ + public class ExpressionContextCase + { + public bool IsDateString { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/ExpressionItems.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/ExpressionItems.cs new file mode 100644 index 000000000..23d10bb50 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/ExpressionItems.cs @@ -0,0 +1,29 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + internal class ExpressionItems + { + /// + /// 1 memeber, 2 class ,3 method + /// + public int Type { get; set; } + public EntityInfo ParentEntityInfo { get; set; } + public EntityInfo ThisEntityInfo { get; set; } + public Expression Expression { get; set; } + public Navigate Nav + { + get + { + if (Expression is MemberExpression) + { + var name = (Expression as MemberExpression).Member.Name; + var navColumn = ParentEntityInfo.Columns.FirstOrDefault(it => it.PropertyName == name); + return navColumn == null ? null : navColumn.Navigat; + } + return null; + } + } + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/ExpressionOutParameter.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/ExpressionOutParameter.cs new file mode 100644 index 000000000..7e5cb748e --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/ExpressionOutParameter.cs @@ -0,0 +1,8 @@ +namespace SqlSugar +{ + public class ExpressionOutParameter + { + public SqlSugarProvider Context { get; set; } + public QueryBuilder QueryBuilder { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/ExpressionParameter.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/ExpressionParameter.cs new file mode 100644 index 000000000..503cda9b1 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/ExpressionParameter.cs @@ -0,0 +1,42 @@ +using System.Linq.Expressions; +namespace SqlSugar +{ + public class ExpressionParameter + { + public ExpressionContext Context { get; set; } + public ExpressionParameter BaseParameter { get; set; } + public Expression BaseExpression { get; set; } + public Expression ChildExpression { get; set; } + public Expression LeftExpression { get; set; } + public Expression RightExpression { get; set; } + public Expression CurrentExpression { get; set; } + public string OperatorValue { get; set; } + public bool? IsLeft { get; set; } + public int Index { get; set; } + public bool ValueIsNull { get; set; } + public object CommonTempData { get; set; } + public ExpressionResultAppendType AppendType { get; set; } + public void IsAppendResult() + { + this.AppendType = ExpressionResultAppendType.AppendResult; + } + public void IsAppendTempDate() + { + this.AppendType = ExpressionResultAppendType.AppendTempDate; + } + public Expression OppsiteExpression + { + get + { + return this.IsLeft == true ? this.BaseParameter.RightExpression : this.BaseParameter.LeftExpression; + } + } + public bool IsSetTempData + { + get + { + return BaseParameter.CommonTempData.HasValue() && BaseParameter.CommonTempData.Equals(CommonTempDataType.Result); + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/ExpressionResult.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/ExpressionResult.cs new file mode 100644 index 000000000..246d530c0 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/ExpressionResult.cs @@ -0,0 +1,191 @@ +using System.Text; +namespace SqlSugar +{ + public class ExpressionResult + { + public bool IsLockCurrentParameter { get; set; } + public bool IsUpper { get; set; } + public bool IsNavicate { get; set; } + private ExpressionParameter _CurrentParameter; + public ExpressionParameter CurrentParameter + { + get + { + return this._CurrentParameter; + } + set + { + Check.Exception(value != null && IsLockCurrentParameter, "CurrentParameter is locked."); + this._CurrentParameter = value; + this.IsLockCurrentParameter = false; + } + } + #region constructor + private ExpressionResult() + { + } + public ExpressionResult(ResolveExpressType resolveExpressType) + { + this._ResolveExpressType = resolveExpressType; + } + #endregion + + #region Fields + private ResolveExpressType _ResolveExpressType; + private StringBuilder _Result; + #endregion + + #region properties + private StringBuilder Result + { + get + { + if (_Result == null) _Result = new StringBuilder(); + return _Result; + } + + set + { + _Result = value; + } + } + public bool LastCharIsSpace + { + get + { + if (_Result == null || _Result.Length == 0) return true; + return _Result.ToString().Last() == UtilConstants.SpaceChar; + } + } + #endregion + public string GetString() + { + if (_Result == null) return null; + if (IsUpper) + return _Result.ToString().ToUpper().Replace(UtilConstants.ReplaceCommaKey, ",").TrimEnd(','); + else + return _Result.ToString().Replace(UtilConstants.ReplaceCommaKey, ",").TrimEnd(','); + } + #region functions + public string[] GetResultArray() + { + if (this._Result == null) return null; + var reslut = new List(); + + if (IsUpper) + reslut = this.Result.ToString().ToUpper().TrimEnd(',').Split(',').ToList(); + else + reslut = this.Result.ToString().TrimEnd(',').Split(',').ToList(); + + if (this.Result.ToString().Contains(UtilConstants.ReplaceCommaKey)) + { + for (int i = 0; i < reslut.Count; i++) + { + reslut[i] = reslut[i].Replace(UtilConstants.ReplaceCommaKey, ","); + } + } + return reslut.ToArray(); + } + + public string GetResultString() + { + if (this._Result == null) return null; + if (this._ResolveExpressType.IsIn(ResolveExpressType.SelectMultiple, ResolveExpressType.SelectSingle)) + { + return this.Result.ToString().Replace(UtilConstants.ReplaceCommaKey, ",").TrimEnd(','); + } + if (IsUpper) + return this.Result.ToString().Replace(UtilConstants.ReplaceCommaKey, ",").ToUpper(); + else + return this.Result.ToString().Replace(UtilConstants.ReplaceCommaKey, ","); + } + + public void TrimEnd() + { + if (this._Result == null) return; + this.Result = this.Result.Remove(this.Result.Length - 1, 1); + } + + public bool Contains(string value) + { + if (this.Result.Equals(value)) return true; + return (Result.ToString().Contains(value?.ToLower(), StringComparison.CurrentCultureIgnoreCase)); + } + + internal void Insert(int index, string value) + { + if (this.Result == null) this.Result.Append(value); + this.Result.Insert(index, value); + } + + public void Append(object parameter) + { + if (this.CurrentParameter.HasValue() && this.CurrentParameter.AppendType.IsIn(ExpressionResultAppendType.AppendTempDate)) + { + this.CurrentParameter.CommonTempData = parameter; + return; + } + switch (this._ResolveExpressType) + { + case ResolveExpressType.ArraySingle: + case ResolveExpressType.ArrayMultiple: + case ResolveExpressType.SelectSingle: + case ResolveExpressType.SelectMultiple: + case ResolveExpressType.Update: + parameter = parameter + ","; + break; + case ResolveExpressType.WhereSingle: + break; + case ResolveExpressType.WhereMultiple: + break; + case ResolveExpressType.FieldSingle: + break; + case ResolveExpressType.FieldMultiple: + break; + default: + break; + } + if (this.Result.Length > 0 && this.Result[this.Result.Length - 1] == ',') + { + if (parameter?.ToString()?.StartsWith(" AS ") == true && parameter?.ToString()?.EndsWith(" ,") == true) + { + this.Result.Length--; // 直接删掉最后一个字符 + } + } + this.Result.Append(parameter); + } + + public void AppendFormat(string parameter, params object[] orgs) + { + if (this.CurrentParameter.HasValue() && this.CurrentParameter.AppendType.IsIn(ExpressionResultAppendType.AppendTempDate)) + { + this.CurrentParameter.CommonTempData = new KeyValuePair(parameter, orgs); + return; + } + switch (this._ResolveExpressType) + { + case ResolveExpressType.SelectSingle: + case ResolveExpressType.SelectMultiple: + parameter = parameter + ","; + break; + case ResolveExpressType.WhereSingle: + break; + case ResolveExpressType.WhereMultiple: + break; + case ResolveExpressType.FieldSingle: + break; + case ResolveExpressType.FieldMultiple: + break; + default: + break; + } + this.Result.AppendFormat(parameter, orgs); + } + + public void Replace(string parameter, string newValue) + { + this.Result.Replace(parameter, newValue); + } + #endregion + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/ExpressionResultAcceptType.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/ExpressionResultAcceptType.cs new file mode 100644 index 000000000..f372f0d8b --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/ExpressionResultAcceptType.cs @@ -0,0 +1,8 @@ +namespace SqlSugar +{ + public enum ExpressionResultAppendType + { + AppendResult = 0, + AppendTempDate = 1 + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/ExpressionTool.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/ExpressionTool.cs new file mode 100644 index 000000000..52db6f3d9 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/ExpressionTool.cs @@ -0,0 +1,1100 @@ +using System.Collections.ObjectModel; +using System.Linq.Expressions; +using System.Reflection; +namespace SqlSugar +{ + public static class ExpressionTool + { + public static bool ContainsTwoLevelAccess(Expression exp) + { + var result = false; + + if (exp is LambdaExpression lambda && + lambda.Body is MemberInitExpression initExpr) + { + var param = lambda.Parameters[0]; + + foreach (var binding in initExpr.Bindings) + { + if (binding is MemberAssignment assign) + { + if (assign.Expression is MemberExpression outer && + outer.Expression is MemberExpression inner && + inner.Expression == param) + { + result = true; + break; // 已经找到了,就退出循环 + } + } + } + } + + return result; + } + + public static string GetMemberNameByMethod(Expression expression, string name) + { + if (expression is LambdaExpression lambda) + { + if (lambda.Body is MethodCallExpression method) + { + if (method.Method.Name == "ToList") + { + var arg = method.Arguments.FirstOrDefault(); + if (arg != null) + { + if (arg is MemberExpression member) + { + name = member.Member.Name; + } + } + } + } + } + + return name; + } + + internal static string ResolveMemberValue(ExpressionContext context, Expression item, string value) + { + if (item is MemberExpression member) + { + if (member.Expression is ParameterExpression parameterExpression) + { + if (value?.Contains('(') == true && !value.Contains(' ')) + { + var guid = Guid.NewGuid() + ""; + var guid2 = Guid.NewGuid() + ""; + value = value.Replace("(", guid).Replace(")", guid2); + value = context.GetTranslationColumnName(value); + value = value.Replace(guid, "(").Replace(guid2, ")"); + } + } + } + return value; + } + internal static Expression GetConditionalExpression(Expression item) + { + ConstantExpression trueConstant = Expression.Constant(true, typeof(bool)); + ConstantExpression falseConstant = Expression.Constant(false, typeof(bool)); + + // 创建条件表达式:item ? true : false + Expression conditionalExpression = Expression.Condition( + test: item, + ifTrue: trueConstant, + ifFalse: falseConstant + ); + return conditionalExpression; + } + internal static bool IsOwnsOne(ExpressionContext context, Expression member) + { + var isOwnsOne = false; + if (context?.SugarContext?.Context == null) + { + return false; + } + if (member is MemberExpression memberExp) + { + var name = memberExp?.Member?.Name; + if (memberExp?.Expression is MemberExpression parentMemberExp) + { + if (name != null && parentMemberExp?.Expression is ParameterExpression rootExp) + { + var entityInfo = context?.SugarContext?.Context?.EntityMaintenance?.GetEntityInfo(rootExp.Type); + var navColumn = entityInfo?.Columns?.FirstOrDefault(it => it.PropertyName == name); + isOwnsOne = navColumn?.ForOwnsOnePropertyInfo != null; + } + } + } + return isOwnsOne; + } + internal static EntityColumnInfo GetOwnsOneColumnInfo(ExpressionContext context, Expression member) + { + EntityColumnInfo entityColumnInfo = new EntityColumnInfo(); + if (member is MemberExpression memberExp) + { + var name = memberExp?.Member?.Name; + if (memberExp.Expression is MemberExpression parentMemberExp) + { + if (name != null && parentMemberExp.Expression is ParameterExpression) + { + var rootExp = (parentMemberExp.Expression as ParameterExpression); + var entityInfo = context?.SugarContext?.Context?.EntityMaintenance?.GetEntityInfo(rootExp.Type); + var navColumn = entityInfo.Columns.FirstOrDefault(it => it.PropertyName == name); + entityColumnInfo = navColumn; + } + } + } + return entityColumnInfo; + } + + internal static bool IsNavMember(ExpressionContext context, Expression member) + { + var isNav = false; + if (member is MemberExpression && (member as MemberExpression)?.Type?.IsClass() == true) + { + var memberExp = (member as MemberExpression); + var name = memberExp?.Member?.Name; + var type = memberExp?.Type; + if (name != null && type != null && memberExp.Expression is ParameterExpression) + { + var rootExp = (memberExp.Expression as ParameterExpression); + var entityInfo = context?.SugarContext?.Context?.EntityMaintenance?.GetEntityInfo(rootExp.Type); + var navColumn = entityInfo.Columns.FirstOrDefault(it => it.PropertyName == name); + isNav = navColumn?.Navigat != null; + } + } + return isNav; + } + internal static bool IsNavMember(ISqlSugarClient context, Expression member) + { + var isNav = false; + if (member is MemberExpression && (member as MemberExpression)?.Type?.IsClass() == true) + { + var memberExp = (member as MemberExpression); + var name = memberExp?.Member?.Name; + var type = memberExp?.Type; + if (name != null && type != null && memberExp.Expression is ParameterExpression) + { + var rootExp = (memberExp.Expression as ParameterExpression); + var entityInfo = context?.EntityMaintenance?.GetEntityInfo(rootExp.Type); + var navColumn = entityInfo.Columns.FirstOrDefault(it => it.PropertyName == name); + isNav = navColumn?.Navigat != null; + } + else if (name != null && type != null && memberExp.Expression is MemberExpression) + { + var rootExp = (memberExp.Expression as MemberExpression); + if (rootExp.Type.IsClass() && type.IsArray == false && type.FullName.IsCollectionsList() == false) + { + var entityInfo = context?.EntityMaintenance?.GetEntityInfo(rootExp.Type); + var navColumn = entityInfo.Columns.FirstOrDefault(it => it.PropertyName == name); + isNav = navColumn?.Navigat != null; + } + } + } + return isNav; + } + + internal static bool IsSqlParameterDbType(ExpressionContext context, Expression member) + { + var isNav = false; + if (context?.SugarContext != null && member is MemberExpression && (member as MemberExpression)?.Expression is ParameterExpression expression) + { + if (expression != null) + { + var typeEntity = context?.SugarContext.Context.EntityMaintenance.GetEntityInfo(expression.Type); + var columnInfo = typeEntity.Columns.FirstOrDefault(it => it.PropertyName == ExpressionTool.GetMemberName(member)); + if (columnInfo?.SqlParameterDbType is Type) + { + return true; + } + if (columnInfo?.SqlParameterDbType is System.Data.DbType) + { + return true; + } + } + } + return isNav; + } + internal static SugarParameter GetParameterBySqlParameterDbType(int index, object value, ExpressionContext context, Expression member) + { + var expression = (member as MemberExpression)?.Expression; + var typeEntity = context?.SugarContext.Context.EntityMaintenance.GetEntityInfo(expression.Type); + var columnInfo = typeEntity.Columns.FirstOrDefault(it => it.PropertyName == ExpressionTool.GetMemberName(member)); + var columnDbType = columnInfo.SqlParameterDbType as Type; + if (columnDbType != null) + { + var ParameterConverter = columnDbType.GetMethod("ParameterConverter").MakeGenericMethod(columnInfo.PropertyInfo.PropertyType); + var obj = Activator.CreateInstance(columnDbType); + var p = ParameterConverter.Invoke(obj, new object[] { value, index }) as SugarParameter; + return p; + } + else + { + var paramter = new SugarParameter("@Common" + index, value) + { + DbType = (System.Data.DbType)columnInfo.SqlParameterDbType + }; + if (columnInfo.SqlParameterSize?.ObjToInt() > 0) + { + paramter.Size = columnInfo.SqlParameterSize.ObjToInt(); + } + return paramter; + } + } + public static List ExtractMemberNames(Expression expression) + { + var memberNames = new List(); + var currentExpression = (expression as LambdaExpression).Body; + + while (currentExpression != null && currentExpression.NodeType == ExpressionType.MemberAccess) + { + var memberExpression = (MemberExpression)currentExpression; + memberNames.Add(memberExpression.Member.Name); + currentExpression = memberExpression.Expression; + } + + memberNames.Reverse(); // Reverse the list to get the correct order of member names + return memberNames; + } + public static Expression> ChangeLambdaExpression(Expression> exp, string replaceParameterName, string newParameterName) + { + var parameter = Expression.Parameter(typeof(T), newParameterName); + + // 替换Lambda表达式中指定参数名 + var visitor = new ParameterReplacer(replaceParameterName, parameter); + var newBody = visitor.Visit(exp); + + return (Expression>)newBody; + } + public static Expression ChangeLambdaExpression(Expression exp, Type targetType, string replaceParameterName, string newParameterName) + { + var parameter = Expression.Parameter(targetType, newParameterName); + + // 替换Lambda表达式中指定参数名 + var visitor = new ParameterReplacer(replaceParameterName, parameter); + var newBody = visitor.Visit(exp); + + return newBody; + } + + public static List GetNewArrayMembers(NewArrayExpression newArrayExpression) + { + List strings = new List(); + // 获取数组元素的 MemberExpression,并输出属性名 + foreach (var expression in newArrayExpression.Expressions) + { + var memberExpression = expression as MemberExpression; + if (memberExpression != null) + { + strings.Add(memberExpression.Member.Name); + } + else if (expression is ConstantExpression) + { + strings.Add((expression as ConstantExpression).Value + ""); + } + } + return strings; + } + public static List GetTopLevelMethodCalls(Expression expression) + { + var methodCalls = new List(); + GetTopLevelMethodCalls(expression, methodCalls); + return methodCalls; + } + + public static void GetTopLevelMethodCalls(Expression expression, List methodCalls) + { + if (expression is MethodCallExpression methodCallExpression) + { + methodCalls.Add(methodCallExpression.Method.Name); + if (methodCallExpression.Object is MethodCallExpression parentMethodCallExpression) + { + GetTopLevelMethodCalls(parentMethodCallExpression, methodCalls); + } + } + else if (expression is LambdaExpression lambdaExpression) + { + GetTopLevelMethodCalls(lambdaExpression.Body, methodCalls); + } + } + + public static Dictionary GetNewExpressionItemList(Expression lamExp) + { + var caseExp = GetLambdaExpressionBody(lamExp); + if (caseExp is MemberInitExpression) + { + return GetMemberBindingItemList((caseExp as MemberInitExpression).Bindings); + } + var exp = caseExp as NewExpression; + if (exp == null) + { + Check.ExceptionEasy("Use Select(it=>new class(){})", "导航查询请使用Select(it=>new class(){})"); + } + var dict = new Dictionary(); + + for (int i = 0; i < exp.Arguments.Count; i++) + { + var arg = exp.Arguments[i]; + var parameterInfo = exp.Constructor.GetParameters()[i]; + + dict.Add(parameterInfo.Name, arg); + } + + return dict; + } + public static Dictionary GetMemberBindingItemList(ReadOnlyCollection exp) + { + Dictionary dict = new Dictionary(); + // 获取MemberInitExpression中的每一个MemberBinding + foreach (var binding in exp) + { + // 判断是MemberAssignment还是MemberListBinding + if (binding is MemberAssignment assignment) + { + // 获取属性名和属性值 + string propertyName = assignment.Member.Name; + dict.Add(assignment.Member.Name, assignment.Expression); + } + + } + return dict; + } + public static bool ContainsMethodName(BinaryExpression expression, string name) + { + var visitor = new MethodCallExpressionVisitor(name); + var hasMethodCallWithName = visitor.HasMethodCallWithName(expression); + return hasMethodCallWithName; + } + public static bool IsVariable(Expression expr) + { + var ps = new ParameterExpressionVisitor(); + ps.Visit(expr); + return ps.Parameters.Count == 0; + } + public static List GetParameters(Expression expr) + { + var ps = new ParameterExpressionVisitor(); + ps.Visit(expr); + return ps.Parameters; + } + public static bool IsComparisonOperatorBool(BinaryExpression binaryExp) + { + return binaryExp.NodeType.IsIn(ExpressionType.Equal, + ExpressionType.GreaterThan, ExpressionType.GreaterThanOrEqual, + ExpressionType.LessThan, ExpressionType.LessThanOrEqual); + } + + public static string GetOperator(ExpressionType expressiontype) + { + switch (expressiontype) + { + case ExpressionType.And: + return "&"; + case ExpressionType.AndAlso: + return "AND"; + case ExpressionType.Equal: + return "="; + case ExpressionType.GreaterThan: + return ">"; + case ExpressionType.GreaterThanOrEqual: + return ">="; + case ExpressionType.LessThan: + return "<"; + case ExpressionType.LessThanOrEqual: + return "<="; + case ExpressionType.NotEqual: + return "<>"; + case ExpressionType.Or: + return "|"; + case ExpressionType.OrElse: + return "OR"; + case ExpressionType.Add: + case ExpressionType.AddChecked: + return "+"; + case ExpressionType.Subtract: + case ExpressionType.SubtractChecked: + return "-"; + case ExpressionType.Divide: + return "/"; + case ExpressionType.Multiply: + case ExpressionType.MultiplyChecked: + return "*"; + case ExpressionType.Modulo: + return "%"; + case ExpressionType.Coalesce: + throw new Exception("Expression no support ?? ,Use SqlFunc.IsNull"); + default: +#pragma warning disable CA1863 // 使用 "CompositeFormat" + Check.ThrowNotSupportedException(string.Format(ErrorMessage.OperatorError, expressiontype.ToString())); +#pragma warning restore CA1863 // 使用 "CompositeFormat" + return null; + } + } + + + public static void GetOneToOneInfo(SqlSugarProvider context, Expression> LeftObject, out MemberExpression memberExpression, out string navObjectName, out EntityColumnInfo navColumn, out EntityInfo navEntityInfo, out EntityColumnInfo navPkColumn) + { + memberExpression = ((LeftObject as LambdaExpression).Body as MemberExpression); + var listItemType = typeof(T); + var listItemEntity = context.EntityMaintenance.GetEntityInfo(listItemType); + var listPkColumn = listItemEntity.Columns.Where(it => it.IsPrimarykey).FirstOrDefault(); + navObjectName = memberExpression.Member.Name; + var navObjectName2 = navObjectName; + var navObjectNamePropety = listItemType.GetProperty(navObjectName); + var navObjectNameColumnInfo = listItemEntity.Columns.First(it => it.PropertyName == navObjectName2); + Check.ExceptionEasy(navObjectNameColumnInfo.Navigat == null, $"{navObjectName} not [Navigat(..)] ", $"{navObjectName} 没有导航特性 [Navigat(..)] "); + Check.ExceptionEasy(navObjectNameColumnInfo.Navigat.NavigatType != NavigateType.OneToOne, $"IncludeLeftJoin can only be one-on-one ", $"IncludeLeftJoin 只能是一对一 "); + navColumn = listItemEntity.Columns.FirstOrDefault(it => it.PropertyName == navObjectNameColumnInfo.Navigat.Name); + Check.ExceptionEasy(navColumn == null, "OneToOne navigation configuration error", $"OneToOne导航配置错误: 实体{listItemEntity.EntityName} 不存在{navObjectNameColumnInfo.Navigat.Name}"); + var navType = navObjectNamePropety.PropertyType; + navEntityInfo = context.EntityMaintenance.GetEntityInfo(navType); + context.InitMappingInfo(navEntityInfo.Type); + navPkColumn = navEntityInfo.Columns.Where(it => it.IsPrimarykey).FirstOrDefault(); + Check.ExceptionEasy(navPkColumn == null && navObjectNameColumnInfo.Navigat.Name2 == null, navEntityInfo.EntityName + "need primarykey", navEntityInfo.EntityName + " 需要主键"); + if (navObjectNameColumnInfo.Navigat.Name2.HasValue()) + { + navPkColumn = navEntityInfo.Columns.Where(it => it.PropertyName == navObjectNameColumnInfo.Navigat.Name2).FirstOrDefault(); + } + } + + + public static List ExpressionParameters(Expression expression) + { + List parameters = null; + if (expression is LambdaExpression) + { + if ((expression as LambdaExpression).Parameters != null) + { + parameters = (expression as LambdaExpression).Parameters.ToList(); + } + } + return parameters; + } + + public static object GetValue(object value, ExpressionContext context) + { + if (value == null) return value; + var type = value.GetType(); + if (type.IsEnum() && type != typeof(DateType) && type != typeof(JoinType) && type != typeof(OrderByType)) + { + if (context.TableEnumIsString == true) + { + return value.ToString(); + } + else + { + return Convert.ToInt64(value); + } + } + else + return value; + } + + public static Expression GetLambdaExpressionBody(Expression expression) + { + Expression newExp = expression; + if (newExp is LambdaExpression) + { + newExp = (newExp as LambdaExpression).Body; + } + + return newExp; + } + + + public static string GetFirstTypeNameFromExpression(Expression expression) + { + if (expression is LambdaExpression lambda) + { + return GetFirstTypeNameFromExpression(lambda.Body); + } + else if (expression is MemberExpression member) + { + return member.Member.Name; + } + else if (expression is NewExpression newExpr) + { + return newExpr.Type.Name; + } + else if (expression is MethodCallExpression methodCall) + { + return GetFirstTypeNameFromExpression(methodCall.Arguments.FirstOrDefault()); + } + return ""; + } + + public static string GetMethodName(Expression expression) + { + if (expression is MethodCallExpression) + { + return (expression as MethodCallExpression).Method.Name; + } + return null; + } + + public static Type GetMemberInfoType(MemberInfo member) + { + switch (member.MemberType) + { + case MemberTypes.Event: + return ((EventInfo)member).EventHandlerType; + case MemberTypes.Field: + return ((FieldInfo)member).FieldType; + case MemberTypes.Method: + return ((MethodInfo)member).ReturnType; + case MemberTypes.Property: + return ((PropertyInfo)member).PropertyType; + default: + return null; + } + } + public static bool IsLogicOperator(string operatorValue) + { + return operatorValue == "&&" || operatorValue == "||" || operatorValue == "AND" || operatorValue == "OR"; + } + + public static bool IsLogicOperator(Expression expression) + { + return expression.NodeType == ExpressionType.And || + expression.NodeType == ExpressionType.AndAlso || + expression.NodeType == ExpressionType.Or || + expression.NodeType == ExpressionType.OrElse; + } + public static bool IsComparisonOperator(Expression expression) + { + return expression.NodeType != ExpressionType.And && + expression.NodeType != ExpressionType.AndAlso && + expression.NodeType != ExpressionType.Or && + expression.NodeType != ExpressionType.OrElse; + } + public static bool IsEqualOrLtOrGt(Expression expression) + { + return expression.NodeType == ExpressionType.Equal || + expression.NodeType == ExpressionType.GreaterThan || + expression.NodeType == ExpressionType.LessThan || + expression.NodeType == ExpressionType.LessThanOrEqual || + expression.NodeType == ExpressionType.GreaterThanOrEqual + ; + } + public static object GetMemberValue(MemberInfo member, Expression expression) + { + var rootExpression = expression as MemberExpression; + var memberInfos = new Stack(); + var fieldInfo = member as System.Reflection.FieldInfo; + object reval = null; + MemberExpression memberExpr = null; + while (expression is MemberExpression) + { + memberExpr = expression as MemberExpression; + memberInfos.Push(memberExpr.Member); + if (memberExpr.Expression == null) + { + var isProperty = memberExpr.Member.MemberType == MemberTypes.Property; + var isField = memberExpr.Member.MemberType == MemberTypes.Field; + if (isProperty) + { + try + { + reval = GetPropertyValue(memberExpr); + } + catch + { + reval = null; + } + } + else if (isField) + { + reval = GetFiledValue(memberExpr); + } + } + if (memberExpr.Expression == null) + { + + } + expression = memberExpr.Expression; + } + // fetch the root object reference: + var constExpr = expression as ConstantExpression; + if (constExpr == null) + { + return DynamicInvoke(rootExpression); + } + object objReference = constExpr.Value; + // "ascend" back whence we came from and resolve object references along the way: + while (memberInfos.Count > 0) // or some other break condition + { + var mi = memberInfos.Pop(); + if (mi.MemberType == MemberTypes.Property) + { + if (objReference == null) + { + Check.ExceptionEasy($"Expression error {rootExpression?.ToString()} expression, An empty reference appears in the expression to check if the parameter is null ", $"表达式错误 {rootExpression?.ToString()} 表达式中出现了空引用 检查参数是否为null "); + } + var objProp = objReference.GetType().GetProperties().Where(it => it.Name == mi.Name).FirstOrDefault(); + if (objProp == null) + { + objReference = DynamicInvoke(expression, rootExpression == null ? memberExpr : rootExpression); + } + else + { + objReference = objProp.GetValue(objReference, null); + } + } + else if (mi.MemberType == MemberTypes.Field) + { + var objField = objReference.GetType().GetField(mi.Name); + if (objField == null) + { + objReference = DynamicInvoke(expression, rootExpression == null ? memberExpr : rootExpression); + } + else + { + objReference = objField.GetValue(objReference); + } + } + } + reval = objReference; + return reval; + } + + internal static Expression RemoveConvert(Expression item) + { + for (int i = 0; i < 10; i++) + { + if ((item is UnaryExpression) && (item as UnaryExpression).NodeType == ExpressionType.Convert) + { + item = (item as UnaryExpression).Operand; + } + else + { + break; + } + } + return item; + } + internal static Expression RemoveConvertThanOne(Expression item) + { + for (int i = 0; i < 10; i++) + { + if ((item is UnaryExpression) + && (item as UnaryExpression).NodeType == ExpressionType.Convert + && (item as UnaryExpression).Operand is UnaryExpression) + { + item = (item as UnaryExpression).Operand; + } + else + { + break; + } + } + return item; + } + public static string GetMemberName(Expression expression) + { + if (expression is LambdaExpression) + { + expression = (expression as LambdaExpression).Body; + } + if (expression is UnaryExpression) + { + expression = ((UnaryExpression)expression).Operand; + } + var member = (expression as MemberExpression)?.Member?.Name; + return member; + } + internal static object GetExpressionValue(Expression expression) + { + try + { + if (expression is ConstantExpression) + { + return (expression as ConstantExpression).Value; + } + else if (expression is MethodCallExpression) + { + return LambdaExpression.Lambda(expression).Compile().DynamicInvoke(); + } + else + { + return GetMemberValue((expression as MemberExpression).Member, expression); + } + } + catch + { + return LambdaExpression.Lambda(expression).Compile().DynamicInvoke(); + } + } + + public static object GetFiledValue(MemberExpression memberExpr) + { + if (!(memberExpr.Member is FieldInfo)) + { + return DynamicInvoke(memberExpr); + } + object reval = null; + FieldInfo field = (FieldInfo)memberExpr.Member; + Check.Exception(field.IsPrivate, string.Format(" Field \"{0}\" can't be private ", field.Name)); + reval = field.GetValue(memberExpr.Member); + if (reval?.GetType().IsClass() == true && reval.GetType() != UtilConstants.StringType) + { + var fieldName = memberExpr.Member.Name; + var proInfo = reval.GetType().GetProperty(fieldName); + if (proInfo != null) + { + reval = proInfo.GetValue(reval, null); + } + var fieInfo = reval.GetType().GetField(fieldName); + if (fieInfo != null) + { + reval = fieInfo.GetValue(reval); + } + if (fieInfo == null && proInfo == null) + { + Check.Exception(field.IsPrivate, string.Format(" Field \"{0}\" can't be private ", field.Name)); + } + } + return reval; + } + + + public static bool IsConstExpression(MemberExpression memberExpr) + { + var result = false; + while (memberExpr?.Expression != null) + { + var isConst = memberExpr.Expression is ConstantExpression; + if (isConst) + { + result = true; + break; + } + else if (memberExpr.Expression is BinaryExpression && (memberExpr.Expression as BinaryExpression).NodeType == ExpressionType.ArrayIndex) + { + result = true; + break; + } + memberExpr = memberExpr.Expression as MemberExpression; + } + return result; + } + + public static object GetPropertyValue(MemberExpression memberExpr) + { + if (!(memberExpr.Member is PropertyInfo)) + { + return DynamicInvoke(memberExpr); + } + object reval = null; + PropertyInfo pro = (PropertyInfo)memberExpr.Member; + reval = pro.GetValue(memberExpr.Member, null); + if (reval?.GetType().IsClass() == true && reval.GetType() != UtilConstants.StringType) + { + var fieldName = memberExpr.Member.Name; + var proInfo = reval.GetType().GetProperty(fieldName); + if (proInfo != null) + { + reval = proInfo.GetValue(reval, null); + } + var fieInfo = reval.GetType().GetField(fieldName); + if (fieInfo != null) + { + reval = fieInfo.GetValue(reval); + } + if (fieInfo == null && proInfo == null && !reval.GetType().FullName.IsCollectionsList()) + { + Check.Exception(true, string.Format(" Property \"{0}\" can't be private ", pro.Name)); + } + } + return reval; + } + + public static object DynamicInvoke(Expression expression, MemberExpression memberExpression = null) + { + try + { + object value = Expression.Lambda(expression).Compile().DynamicInvoke(); + if (value?.GetType().IsClass() == true && value.GetType() != UtilConstants.StringType && memberExpression != null) + { + value = Expression.Lambda(memberExpression).Compile().DynamicInvoke(); + } + + return value; + } + catch (InvalidOperationException ex) + { + return new MapperExpressionResolve(expression, ex).GetSql(); ; + } + catch (Exception ex) + { + throw new Exception("No support " + expression.ToString() + " " + ex.Message); + } + } + + public static Type GetPropertyOrFieldType(MemberInfo propertyOrField) + { + if (propertyOrField.MemberType == MemberTypes.Property) + return ((PropertyInfo)propertyOrField).PropertyType; + if (propertyOrField.MemberType == MemberTypes.Field) + return ((FieldInfo)propertyOrField).FieldType; + throw new NotSupportedException(); + } + + public static bool IsEntity(Type type) + { + return type.IsClass() && type != UtilConstants.StringType; + } + + public static bool IsValueType(Type type) + { + return !IsEntity(type); + } + + public static bool IsUnConvertExpress(Expression item) + { + return item is UnaryExpression && item.NodeType == ExpressionType.Convert; + } + + internal static List GetNewexpressionInfos(Expression item, ExpressionContext context, BaseResolve baseResolve) + { + List result = new List(); + foreach (MemberBinding binding in ((MemberInitExpression)item).Bindings) + { + if (binding.BindingType != MemberBindingType.Assignment) + { + throw new NotSupportedException(); + } + MemberAssignment memberAssignment = (MemberAssignment)binding; + NewExpressionInfo additem = new NewExpressionInfo(); + if (memberAssignment.Expression is MemberExpression) + { + additem.LeftNameName = memberAssignment.Member.Name; + var member = (memberAssignment.Expression as MemberExpression).Expression; + additem.ShortName = member + ""; + additem.RightName = (memberAssignment.Expression as MemberExpression).Member.Name; + additem.RightDbName = context.GetDbColumnName(member.Type.Name, additem.RightName); + if (ExpressionTool.IsNavMember(context, member)) + { + additem.RightDbName = additem.RightName = baseResolve.GetNewExpressionValue(memberAssignment.Expression); + } + result.Add(additem); + } + else if (memberAssignment.Expression is ConstantExpression) + { + var value = ((ConstantExpression)memberAssignment.Expression).Value; + //var leftInfo = keys[i]; + additem.Type = nameof(ConstantExpression); + additem.RightName = memberAssignment.Member.Name; + additem.ShortName = memberAssignment.Member.Name; + additem.RightName = memberAssignment.Member.Name; + additem.LeftNameName = memberAssignment.Member.Name; + additem.RightDbName = UtilMethods.GetSqlValue(value); + //additem.Value = ""; + result.Add(additem); + } + else if (memberAssignment.Expression is MemberInitExpression || memberAssignment.Expression is NewExpression) + { + + var dic = ExpressionTool.GetNewExpressionItemList(memberAssignment.Expression); + foreach (var kv in dic) + { + additem = new NewExpressionInfo(); + //var leftInfo = keys[i]; + additem.Type = nameof(NewExpression); + additem.RightName = kv.Key; + additem.ShortName = ExpressionTool.GetParameters(kv.Value).First().Name; + additem.RightName = kv.Key; + additem.LeftNameName = memberAssignment.Member.Name + "." + kv.Key; + additem.RightDbName = kv.Key; + //additem.Value = ""; + result.Add(additem); + } + } + else + { + var value = baseResolve.GetNewExpressionValue(memberAssignment.Expression); + //var leftInfo = keys[i]; + additem.Type = nameof(ConstantExpression); + additem.RightName = memberAssignment.Member.Name; + additem.ShortName = memberAssignment.Member.Name; + additem.RightName = memberAssignment.Member.Name; + additem.LeftNameName = memberAssignment.Member.Name; + additem.RightDbName = value; + //additem.Value = ""; + result.Add(additem); + } + + } + return result; + } + internal static List GetNewDynamicexpressionInfos(Expression item, ExpressionContext context, BaseResolve baseResolve) + { + List result = new List(); + int i = 0; + foreach (var binding in ((NewExpression)item).Arguments) + { + NewExpressionInfo additem = new NewExpressionInfo(); + var keys = ((NewExpression)item).Members; + if (binding is MemberExpression) + { + var member = (MemberExpression)binding; + var entityName = member.Expression?.Type?.Name; + //var memberAssignment = binding; + //NewExpressionInfo additem = new NewExpressionInfo(); + additem.RightName = member.Member.Name; + additem.ShortName = member.Expression + ""; + additem.RightName = member.Member.Name; + additem.RightDbName = context.GetDbColumnName(entityName, additem.RightName); + var isNavMember = member.Expression != null + && ExpressionTool.IsNavMember(context, member.Expression); + additem.LeftNameName = member.Member.Name; + if (isNavMember && (context?.SugarContext?.QueryBuilder?.JoinQueryInfos?.Count ?? 0) == 0) + { + var exp = context.GetCopyContextWithMapping(); + exp.Resolve(member, ResolveExpressType.FieldSingle); + var sql = exp.Result.GetResultString(); + if (context.IsSingle && context.CurrentShortName.IsNullOrEmpty()) + { + context.SingleTableNameSubqueryShortName = ExpressionTool.GetParameters(member)?.FirstOrDefault()?.Name; + } + additem.RightDbName = sql; + } + else if (isNavMember && context?.SugarContext?.QueryBuilder?.JoinQueryInfos?.Any(it => it.ShortName?.StartsWith("pnv_" + ExpressionTool.GetMemberName(member.Expression)) == true) == true) + { + additem.ShortName = "pnv_" + ExpressionTool.GetMemberName(member.Expression); + } + //additem.Value = ""; + result.Add(additem); + } + else if (binding is ConstantExpression) + { + var value = ((ConstantExpression)binding).Value; + var leftInfo = keys[i]; + additem.Type = nameof(ConstantExpression); + additem.RightName = leftInfo.Name; + additem.ShortName = leftInfo.Name; + additem.RightName = leftInfo.Name; + additem.LeftNameName = leftInfo.Name; + additem.RightDbName = UtilMethods.GetSqlValue(value); + //additem.Value = ""; + result.Add(additem); + } + else if (binding is MemberInitExpression || binding is NewExpression) + { + + var dic = ExpressionTool.GetNewExpressionItemList(binding); + foreach (var kv in dic) + { + additem = new NewExpressionInfo(); + //var leftInfo = keys[i]; + additem.Type = nameof(NewExpression); + additem.RightName = kv.Key; + additem.ShortName = ExpressionTool.GetParameters(kv.Value).First().Name; + additem.RightName = kv.Key; + additem.LeftNameName = keys[i].Name + "." + kv.Key; + additem.RightDbName = kv.Key; + //additem.Value = ""; + result.Add(additem); + } + } + else + { + var value = baseResolve.GetNewExpressionValue(binding); + var leftInfo = keys[i]; + additem.Type = nameof(ConstantExpression); + additem.RightName = leftInfo.Name; + additem.ShortName = leftInfo.Name; + additem.RightName = leftInfo.Name; + additem.LeftNameName = leftInfo.Name; + additem.RightDbName = value; + //additem.Value = ""; + result.Add(additem); + } + i++; + } + return result; + } + + internal static bool IsSubQuery(Expression it) + { + if (it is MethodCallExpression) + { + var method = (MethodCallExpression)it; + if (method.Object?.Type.Name.StartsWith("Subquery") == true) + { + return true; + } + } + return false; + } + + internal static bool IsIsNullSubQuery(Expression it) + { + if (it is MethodCallExpression) + { + var method = (MethodCallExpression)it; + if (method.Method.Name == "IsNull") + { + if (method.Arguments.Count == 2 && IsSubQuery(method.Arguments[0])) + { + return true; + } + } + } + return false; + } + + internal static bool IsMemberInit(object selectValue) + { + var result = false; + if (selectValue is Expression) + { + if (selectValue is MemberInitExpression) + { + result = true; + } + else if (selectValue is LambdaExpression) + { + var lambda = (LambdaExpression)selectValue; + if (lambda.Body is MemberInitExpression) + { + result = true; + } + } + } + return result; + } + + internal static MemberInitExpression GetMemberInit(object selectValue) + { + MemberInitExpression result = null; + if (selectValue is Expression) + { + if (selectValue is MemberInitExpression) + { + result = (MemberInitExpression)selectValue; + } + else if (selectValue is LambdaExpression) + { + var lambda = (LambdaExpression)selectValue; + if (lambda.Body is MemberInitExpression) + { + result = (MemberInitExpression)lambda.Body; + } + } + } + return result; + } + + + public static bool IsNegate(Expression exp) + { + return exp is UnaryExpression && exp.NodeType == ExpressionType.Negate; + } + + public static bool GetIsLength(Expression item) + { + var isLength = (item is MemberExpression) && ((item as MemberExpression).Member.Name == "Length"); + if (isLength) + { + var exp = (item as MemberExpression).Expression; + if (exp == null) + { + return false; + } + else if (exp.Type == UtilConstants.StringType && item.Type == UtilConstants.IntType) + { + return true; + } + else + { + return false; + } + } + else + { + return false; + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/ListAnyParameter.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/ListAnyParameter.cs new file mode 100644 index 000000000..e8c835898 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/ListAnyParameter.cs @@ -0,0 +1,10 @@ +namespace SqlSugar +{ + internal class ListAnyParameter + { + public string Name { get; internal set; } + public string Sql { get; internal set; } + public List Columns { get; internal set; } + public Func ConvetColumnFunc { get; internal set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/MapperExpression.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/MapperExpression.cs new file mode 100644 index 000000000..aeaea82d6 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/MapperExpression.cs @@ -0,0 +1,21 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public class MapperExpression + { + public MapperExpressionType Type { get; set; } + public Expression FillExpression { get; set; } + public Expression MappingField1Expression { get; set; } + public Expression MappingField2Expression { get; set; } + public SqlSugarProvider Context { get; set; } + public QueryBuilder QueryBuilder { get; set; } + public ISqlBuilder SqlBuilder { get; set; } + } + + public enum MapperExpressionType + { + oneToOne = 1, + oneToN = 2 + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/MapperSql.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/MapperSql.cs new file mode 100644 index 000000000..79ce5c093 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/MapperSql.cs @@ -0,0 +1,15 @@ +namespace SqlSugar +{ + public class MapperSql + { + public string Sql { get; set; } + } + + public class MapperExpressionInfo + { + public Type Type { get; set; } + public EntityInfo EntityInfo { get; set; } + public string FieldName { get; set; } + public string FieldString { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/MethodCallExpressionModel.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/MethodCallExpressionModel.cs new file mode 100644 index 000000000..0b4f13b9a --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/MethodCallExpressionModel.cs @@ -0,0 +1,23 @@ +using System.Linq.Expressions; +namespace SqlSugar +{ + public class MethodCallExpressionModel + { + public List Args { get; set; } + public string Name { get; set; } + public dynamic Data { get; set; } + public object DataObject { get; set; } + public Expression Expression { get; set; } + public Expression BaseExpression { get; set; } + public List Parameters { get; set; } + public ExpressionContext Conext { get; set; } + } + + public class MethodCallExpressionArgs + { + public bool IsMember { get; set; } + public object MemberName { get; set; } + public object MemberValue { get; set; } + public Type Type { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/NewExpressionInfo.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/NewExpressionInfo.cs new file mode 100644 index 000000000..cbeaa0d65 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/NewExpressionInfo.cs @@ -0,0 +1,11 @@ +namespace SqlSugar +{ + internal class NewExpressionInfo + { + public string LeftNameName { get; set; } + public string RightName { get; set; } + public string RightDbName { get; set; } + public string ShortName { get; set; } + public string Type { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/ParameterExpressionVisitor.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/ParameterExpressionVisitor.cs new file mode 100644 index 000000000..99e8f205b --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/ParameterExpressionVisitor.cs @@ -0,0 +1,84 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + internal class ParameterExpressionVisitor : ExpressionVisitor + { + public List Parameters { get; } = new List(); + + protected override Expression VisitParameter(ParameterExpression node) + { + Parameters.Add(node); + return base.VisitParameter(node); + } + } + internal class MethodCallExpressionVisitor : ExpressionVisitor + { + private readonly string _methodName; + private bool _hasMethodCallWithName; + + public MethodCallExpressionVisitor(string methodName) + { + _methodName = methodName; + } + + public bool HasMethodCallWithName(Expression expression) + { + Visit(expression); + return _hasMethodCallWithName; + } + + protected override Expression VisitMethodCall(MethodCallExpression node) + { + if (node.Method.Name.Contains(_methodName)) + { + _hasMethodCallWithName = true; + return node; + } + + return base.VisitMethodCall(node); + } + } + + internal class ExpressionTreeVisitor : ExpressionVisitor + { + private readonly List _nodes = new List(); + //protected override Expression VisitBinary(BinaryExpression node) + //{ + // // 解析二元操作符表达式 + // _nodes.Add(node); + // Visit(node.Left); + // Visit(node.Right); + // return node; + //} + //protected override Expression VisitConstant(ConstantExpression node) + //{ + // // 解析常量表达式 + // _nodes.Add(node); + // return node; + //} + protected override Expression VisitMember(MemberExpression node) + { + // 解析成员访问表达式 + _nodes.Add(node); + return node; + } + protected override Expression VisitMethodCall(MethodCallExpression node) + { + // 解析方法调用表达式 + _nodes.Add(node); + if (node.Arguments.Count != 0) + { + Visit(node.Arguments.First()); + } + return node; + } + public List GetExpressions(Expression expression) + { + Visit(expression); + _nodes.Reverse(); + return _nodes; + } + } + +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/ParameterReplacer.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/ParameterReplacer.cs new file mode 100644 index 000000000..7b7674360 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/ParameterReplacer.cs @@ -0,0 +1,21 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + internal class ParameterReplacer : ExpressionVisitor + { + private readonly string _oldParameterName; + private readonly ParameterExpression _newParameter; + + public ParameterReplacer(string oldParameterName, ParameterExpression newParameter) + { + _oldParameterName = oldParameterName; + _newParameter = newParameter; + } + + protected override Expression VisitParameter(ParameterExpression node) + { + return node.Name == _oldParameterName ? _newParameter : base.VisitParameter(node); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/ResolveExpressType.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/ResolveExpressType.cs new file mode 100644 index 000000000..0ee028cb7 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/ResolveExpressType.cs @@ -0,0 +1,18 @@ +namespace SqlSugar +{ + public enum ResolveExpressType + { + None = 0, + WhereSingle = 1, + WhereMultiple = 2, + SelectSingle = 3, + SelectMultiple = 4, + FieldSingle = 5, + FieldMultiple = 7, + Join = 8, + ArraySingle = 9, + ArrayMultiple = 10, + Update = 11 + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/SugarParameter.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/SugarParameter.cs new file mode 100644 index 000000000..4f828a813 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Common/SugarParameter.cs @@ -0,0 +1,292 @@ +using System.Data; +using System.Data.Common; +namespace SqlSugar +{ + public class SugarParameter : DbParameter + { + public bool IsRefCursor { get; set; } + public bool IsClob { get; set; } + public bool IsNClob { get; set; } + public bool IsNvarchar2 { get; set; } + public SugarParameter(string name, object value) + { + this.Value = value; + this.ParameterName = name; + if (value != null) + { + SettingDataType(value.GetType()); + } + } + public SugarParameter(string name, object value, Type type) + { + this.Value = value; + this.ParameterName = name; + SettingDataType(type); + } + public SugarParameter(string name, object value, Type type, ParameterDirection direction) + { + this.Value = value; + this.ParameterName = name; + this.Direction = direction; + SettingDataType(type); + } + public SugarParameter(string name, object value, Type type, ParameterDirection direction, int size) + { + this.Value = value; + this.ParameterName = name; + this.Direction = direction; + this.Size = size; + SettingDataType(type); + } + + + public SugarParameter(string name, object value, System.Data.DbType type) + { + this.Value = value; + this.ParameterName = name; + this.DbType = type; + } + public SugarParameter(string name, DataTable value, string SqlServerTypeName) + { + this.Value = value; + this.ParameterName = name; + this.TypeName = SqlServerTypeName; + } + public SugarParameter(string name, object value, System.Data.DbType type, ParameterDirection direction) + { + this.Value = value; + this.ParameterName = name; + this.Direction = direction; + this.DbType = type; + } + public SugarParameter(string name, object value, System.Data.DbType type, ParameterDirection direction, int size) + { + this.Value = value; + this.ParameterName = name; + this.Direction = direction; + this.Size = size; + this.DbType = type; + } + + private void SettingDataType(Type type) + { + if (type == UtilConstants.ByteArrayType) + { + this.DbType = System.Data.DbType.Binary; + } + else if (type == UtilConstants.GuidType) + { + this.DbType = System.Data.DbType.Guid; + } + else if (type == UtilConstants.IntType) + { + this.DbType = System.Data.DbType.Int32; + } + else if (type == UtilConstants.ShortType) + { + this.DbType = System.Data.DbType.Int16; + } + else if (type == UtilConstants.LongType) + { + this.DbType = System.Data.DbType.Int64; + } + else if (type == UtilConstants.DateType) + { + this.DbType = System.Data.DbType.DateTime; + } + else if (type == UtilConstants.DobType) + { + this.DbType = System.Data.DbType.Double; + } + else if (type == UtilConstants.DecType) + { + this.DbType = System.Data.DbType.Decimal; + } + else if (type == UtilConstants.ByteType) + { + this.DbType = System.Data.DbType.Byte; + } + else if (type == UtilConstants.SByteType) + { + this.DbType = System.Data.DbType.SByte; + } + else if (type == UtilConstants.FloatType) + { + this.DbType = System.Data.DbType.Single; + } + else if (type == UtilConstants.BoolType) + { + this.DbType = System.Data.DbType.Boolean; + } + else if (type == UtilConstants.StringType) + { + this.DbType = System.Data.DbType.String; + } + else if (type == UtilConstants.DateTimeOffsetType) + { + this.DbType = System.Data.DbType.DateTimeOffset; + } + else if (type == UtilConstants.TimeSpanType) + { + this.DbType = System.Data.DbType.Time; + } + else if (type?.Name == "Geometry") + { + this.DbType = System.Data.DbType.Object; + } + else if (type?.Namespace == "Kdbndp.LegacyPostgis") + { + this.DbType = System.Data.DbType.Object; + } + else if (type?.Namespace == "NetTopologySuite.Geometries") + { + this.DbType = System.Data.DbType.Object; + } + else if (type?.IsEnum() == true) + { + this.DbType = System.Data.DbType.Int64; + if (Value != null) + { + this.Value = Convert.ToInt64(Value); + } + } + else if (type == UtilConstants.UIntType) + { + this.DbType = System.Data.DbType.UInt32; + } + else if (type == UtilConstants.ULongType) + { + this.DbType = System.Data.DbType.UInt64; + } + else if (type == UtilConstants.UShortType) + { + this.DbType = System.Data.DbType.UInt16; + } + else if (type == UtilConstants.ShortType) + { + this.DbType = System.Data.DbType.UInt16; + } + else if (type?.Name == "TimeOnly") + { + this.DbType = System.Data.DbType.Time; + this.Value = UtilMethods.TimeOnlyToTimeSpan(this.Value); + } + else if (type?.Name == "DateOnly") + { + this.DbType = System.Data.DbType.Date; + this.Value = Convert.ToDateTime(UtilMethods.DateOnlyToDateTime(this.Value)); + } + else if (type?.FullName == "Newtonsoft.Json.Linq.JObject" || type?.FullName == "Newtonsoft.Json.Linq.JArray" || type?.FullName == "Newtonsoft.Json.Linq.JValue") + { + this.Value = this.Value == null ? default(string) : this.Value.ObjToString(); + } + + } + public SugarParameter(string name, object value, bool isOutput) + { + this.Value = value; + this.ParameterName = name; + if (isOutput) + { + this.Direction = ParameterDirection.Output; + } + } + public override System.Data.DbType DbType + { + get; set; + } + + public override ParameterDirection Direction + { + get; set; + } + + public override bool IsNullable + { + get; set; + } + + public override string ParameterName + { + get; set; + } + + public override byte Scale + { + get; set; + } + + public int _Size; + + public override int Size + { + get + { + if (_Size == 0 && Value != null) + { + var isByteArray = Value.GetType() == UtilConstants.ByteArrayType; + if (isByteArray) + _Size = -1; + else + { + var length = Value.ToString().Length; + _Size = length < 4000 ? 4000 : -1; + + } + } + if (_Size == 0) + _Size = 4000; + return _Size; + } + set + { + _Size = value; + } + } + + public override string SourceColumn + { + get; set; + } + + public override bool SourceColumnNullMapping + { + get; set; + } + public string UdtTypeName + { + get; + set; + } + + public override object Value + { + get; set; + } + + public Dictionary TempDate + { + get; set; + } + + /// + /// 如果类库是.NET 4.5请删除该属性 + /// If the SqlSugar library is.NET 4.5, delete the property + /// + public override DataRowVersion SourceVersion + { + get; set; + } + + public override void ResetDbType() + { + this.DbType = System.Data.DbType.String; + } + + + public string TypeName { get; set; } + public bool IsJson { get; set; } + public bool IsArray { get; set; } + public object CustomDbType { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/DbMethods/DefaultDbMethod.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/DbMethods/DefaultDbMethod.cs new file mode 100644 index 000000000..3ff030e7b --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/DbMethods/DefaultDbMethod.cs @@ -0,0 +1,1403 @@ +using System.Collections; +using System.Text; +using System.Text.RegularExpressions; +namespace SqlSugar +{ + public partial class DefaultDbMethod : IDbMethods + { + public virtual string ParameterKeyWord { get; set; } = "@"; + public virtual string RowNumber(MethodCallExpressionModel model) + { + if (model.Args.Count == 1) + { + return $"row_number() over(order by {model.Args[0].MemberName.ObjToString().TrimEnd('\'').TrimStart('\'')})"; + } + else + { + return $"row_number() over( partition by {model.Args[1].MemberName.ObjToString().TrimEnd('\'').TrimStart('\'')} order by {model.Args[0].MemberName.ObjToString().TrimEnd('\'').TrimStart('\'')})"; + } + } + public virtual string RowCount(MethodCallExpressionModel model) + { + if (model.Args.Count > 1) + { + return $"COUNT({model.Args[0].MemberName}) over( partition by {model.Args[2].MemberName.ObjToString().TrimEnd('\'').TrimStart('\'')} order by {model.Args[1].MemberName.ObjToString().TrimEnd('\'').TrimStart('\'')})"; + } + return "COUNT(1) over()"; + } + public string RowSum(MethodCallExpressionModel model) + { + if (model.Args.Count > 1) + { + return $"SUM({model.Args[0].MemberName}) over( partition by {model.Args[2].MemberName.ObjToString().TrimEnd('\'').TrimStart('\'')} order by {model.Args[1].MemberName.ObjToString().TrimEnd('\'').TrimStart('\'')})"; + } + return "SUM(" + model.Args[0].MemberName + ") over()"; + } + public string RowAvg(MethodCallExpressionModel model) + { + if (model.Args.Count > 1) + { + return $"AVG({model.Args[0].MemberName}) over( partition by {model.Args[2].MemberName.ObjToString().TrimEnd('\'').TrimStart('\'')} order by {model.Args[1].MemberName.ObjToString().TrimEnd('\'').TrimStart('\'')})"; + } + return "AVG(" + model.Args[0].MemberName + ") over()"; + } + public string RowMin(MethodCallExpressionModel model) + { + if (model.Args.Count > 1) + { + return $"Min({model.Args[0].MemberName}) over( partition by {model.Args[2].MemberName.ObjToString().TrimEnd('\'').TrimStart('\'')} order by {model.Args[1].MemberName.ObjToString().TrimEnd('\'').TrimStart('\'')})"; + } + return "Min(" + model.Args[0].MemberName + ") over()"; + } + public string RowMax(MethodCallExpressionModel model) + { + if (model.Args.Count > 1) + { + return $"Max({model.Args[0].MemberName}) over( partition by {model.Args[2].MemberName.ObjToString().TrimEnd('\'').TrimStart('\'')} order by {model.Args[1].MemberName.ObjToString().TrimEnd('\'').TrimStart('\'')})"; + } + return "Max(" + model.Args[0].MemberName + ") over()"; + } + public virtual string IIF(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + var parameter3 = model.Args[2]; + var ifTrue = parameter2.MemberName.ObjToString(); + var ifFalse = parameter3.MemberName.ObjToString(); + if (ifTrue == ifFalse) + { + return $" {parameter2.MemberName} "; + } + if (model.Parameters != null + && model.Conext != null + && ifTrue.StartsWith(model.Conext?.SqlParameterKeyWord) + && ifFalse.StartsWith(model.Conext?.SqlParameterKeyWord)) + { + var p2 = model.Parameters.Where(it => it.ParameterName != null).FirstOrDefault(it => it.ParameterName.Equals(ifTrue)); + var p3 = model.Parameters.Where(it => it.ParameterName != null).FirstOrDefault(it => it.ParameterName.Equals(ifFalse)); + if (p2 != null && p3 != null) + { + if (p2.Value?.Equals(p3.Value) == true) + { + model.Parameters.Remove(p3); + return $" {parameter2.MemberName} "; + } + } + } + return string.Format("( CASE WHEN {0} THEN {1} ELSE {2} END )", parameter.MemberName, parameter2.MemberName, parameter3.MemberName); + } + + public virtual string IsNullOrEmpty(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format("( {0} IS NULL OR {0}='')", parameter.MemberName); + } + + public virtual string HasValue(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format("({0} IS NOT NULL )", parameter.MemberName); + } + + public virtual string HasNumber(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format("( {0}>0 AND {0} IS NOT NULL )", parameter.MemberName); + } + + + public virtual string ToUpper(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" (UPPER({0})) ", parameter.MemberName); + } + + public virtual string ToLower(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" (LOWER({0})) ", parameter.MemberName); + } + + public virtual string Trim(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" (rtrim(ltrim({0}))) ", parameter.MemberName); + } + + public virtual string Contains(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + return string.Format(" ({0} like '%'+{1}+'%') ", parameter.MemberName, parameter2.MemberName); + } + public virtual string ContainsArray(MethodCallExpressionModel model) + { + var inValueIEnumerable = (IEnumerable)model.Args[0].MemberValue; + List inValues = new List(); + if (inValueIEnumerable != null) + { + foreach (var item in inValueIEnumerable) + { + if (item?.GetType().IsEnum() == true) + { + inValues.Add(Convert.ToInt64(item)); + } + else if (item != null && item.GetType() == UtilConstants.DateType) + { + var inStr = Convert.ToDateTime(item).ToString("yyyy-MM-dd HH:mm:ss.fff"); + inValues.Add(inStr); + } + else if (item?.GetType().FullName == "System.DateOnly") + { + var inStr = UtilMethods.DateOnlyToDateTime(item).ObjToDate().ToString("yyyy-MM-dd"); + inValues.Add(inStr); + } + else if (item != null && item.GetType() == UtilConstants.ByteArrayType) + { + var inStr = Convert.ToHexString((byte[])item); + inValues.Add(inStr); + } + else + { + if (item is string && item.HasValue() && model?.Conext?.SugarContext?.Context?.CurrentConnectionConfig?.DbType == DbType.MySql) + { + var newValue = item.ToString().Replace("\\", "\\\\"); + inValues.Add(newValue); + } + else + { + inValues.Add(item); + } + } + } + } + var value = model.Args[1].MemberName; + string inValueString = null; + var isNvarchar = model.Args.Count == 3; + if (inValues?.Count > 0) + { + if (isNvarchar && model.Args[2].MemberValue.Equals(true)) + { + inValueString = inValues.ToArray().ToJoinSqlInValsN(); + } + else if (inValues.Count != 0 && inValues.FirstOrDefault() is bool && inValues.All(it => it is bool)) + { + inValueString = string.Join(",", inValues.Select(it => Convert.ToBoolean(it) ? 1 : 0)); + } + else + { + inValueString = inValues.ToArray().ToJoinSqlInVals(); + } + } + if (inValueString.IsNullOrEmpty()) + { + return " (1=2) "; + } + else + { + return string.Format(" ({0} IN ({1})) ", value, inValueString); + } + } + + public virtual string ContainsArrayUseSqlParameters(MethodCallExpressionModel model) + { + var inValueIEnumerable = (IEnumerable)model.Args[0].MemberValue; + List inValues = new List(); + if (inValueIEnumerable != null) + { + foreach (var item in inValueIEnumerable) + { + if (item?.GetType().IsEnum() == true) + { + inValues.Add(Convert.ToInt64(item)); + } + else + { + inValues.Add(item); + } + } + } + var value = model.Args[1].MemberName; + string inValueString = null; + if (inValues?.Count > 0) + { + for (int i = 0; i < inValues.Count; i++) + { + inValueString += model.Data + "_" + i + ","; + } + } + if (inValueString.IsNullOrEmpty()) + { + return " (1=2) "; + } + else + { + inValueString = inValueString.TrimEnd(','); + return string.Format(" ({0} IN ({1})) ", value, inValueString); + } + } + + public virtual string Equals(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + return string.Format(" ({0} = {1}) ", parameter.MemberName, parameter2.MemberName); ; + } + + public virtual string EqualsNull(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + if (parameter2.MemberValue == null) + { + return string.Format(" ({0} is null) ", parameter.MemberName, parameter2.MemberName); + } + else + { + return string.Format(" ({0} = {1}) ", parameter.MemberName, parameter2.MemberName); + } + } + public virtual string DateIsSameDay(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + return string.Format(" (DATEDIFF(day,{0},{1})=0) ", parameter.MemberName, parameter2.MemberName); ; + } + + public virtual string DateIsSameByType(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + var parameter3 = model.Args[2]; + return string.Format(" (DATEDIFF({2},{0},{1})=0) ", parameter.MemberName, parameter2.MemberName, parameter3.MemberValue); + } + + public virtual string DateAddByType(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + var parameter3 = model.Args[2]; + return string.Format(" (DATEADD({2},{1},{0})) ", parameter.MemberName, parameter2.MemberName, parameter3.MemberValue); + } + + public virtual string DateAddDay(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + return string.Format(" (DATEADD(day,{1},{0})) ", parameter.MemberName, parameter2.MemberName); + } + + public virtual string Between(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter1 = model.Args[1]; + var parameter2 = model.Args[2]; + return string.Format(" ({0} BETWEEN {1} AND {2}) ", parameter.MemberName, parameter1.MemberName, parameter2.MemberName); + } + + public virtual string StartsWith(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + return string.Format(" ({0} like {1}+'%') ", parameter.MemberName, parameter2.MemberName); + } + + public virtual string EndsWith(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + return string.Format(" ({0} like '%'+{1}) ", parameter.MemberName, parameter2.MemberName); + } + + public virtual string DateValue(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + if (parameter.MemberName != null && parameter.MemberName is DateTime) + { + return string.Format(" DateName({0},'{1}') ", parameter2.MemberValue, parameter.MemberName); + } + else + { + return string.Format(" DateName({0},{1}) ", parameter2.MemberValue, parameter.MemberName); + } + } + + public virtual string GetStringJoinSelector(string result, string separator) + { + return $"string_agg(({result})::text,'{separator}') "; + } + + public virtual string ToInt32(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS INT)", parameter.MemberName); + } + + public virtual string ToInt64(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS BIGINT)", parameter.MemberName); + } + + public virtual string ToString(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS NVARCHAR(MAX))", parameter.MemberName); + } + + public virtual string ToGuid(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS UNIQUEIDENTIFIER)", parameter.MemberName); + } + + public virtual string ToDouble(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS FLOAT)", parameter.MemberName); + } + + public virtual string ToBool(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS BIT)", parameter.MemberName); + } + + public virtual string ToDate(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS DATETIME)", parameter.MemberName); + } + + public virtual string ToDateShort(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS DATE)", parameter.MemberName); + } + + public virtual string ToTime(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS TIME)", parameter.MemberName); + } + + public virtual string ToDecimal(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS MONEY)", parameter.MemberName); + } + public virtual string Substring(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + var parameter3 = model.Args[2]; + return string.Format("SUBSTRING({0},1 + {1},{2})", parameter.MemberName, parameter2.MemberName, parameter3.MemberName); + } + + public virtual string Length(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format("LEN({0})", parameter.MemberName); + } + + public virtual string Replace(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + var parameter3 = model.Args[2]; + return string.Format("REPLACE({0},{1},{2})", parameter.MemberName, parameter2.MemberName, parameter3.MemberName); + } + + public virtual string AggregateSum(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format("SUM({0})", parameter.MemberName); + } + + public virtual string AggregateAvg(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format("AVG({0})", parameter.MemberName); + } + + public virtual string AggregateMin(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format("MIN({0})", parameter.MemberName); + } + + public virtual string AggregateMax(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format("MAX({0})", parameter.MemberName); + } + + public virtual string AggregateCount(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format("COUNT({0})", parameter.MemberName); + } + + public virtual string AggregateDistinctCount(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format("COUNT(DISTINCT {0} )", parameter.MemberName); + } + public virtual string AggregateDistinctSum(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format("SUM(DISTINCT {0} )", parameter.MemberName); + } + + public virtual string MappingColumn(MethodCallExpressionModel model) + { + if (model.Args.Count == 1) + { + return string.Format("{0}", model.Args[0].MemberValue); + } + else + { + var parameter = model.Args[0]; + var parameter1 = model.Args[1]; + return string.Format("{0}", parameter1.MemberValue); + } + } + + public virtual string IsNull(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter1 = model.Args[1]; + return string.Format("ISNULL({0},{1})", parameter.MemberName, parameter1.MemberName); + } + + public virtual string True() + { + return "( 1 = 1 ) "; + } + + public virtual string False() + { + return "( 1 = 2 ) "; + } + public virtual string TrueValue() + { + return "1 "; + } + + public virtual string FalseValue() + { + return "0"; + } + + public string GuidNew() + { + return "'" + Guid.NewGuid() + "' "; + } + + public string GetSelfAndAutoFill(string shortName, bool isSingle) + { + if (isSingle) return "*"; + else + return string.Format("{0}.*", shortName); + } + + public virtual string MergeString(params string[] strings) + { + return string.Join("+", strings); + } + + public virtual string Pack(string sql) + { + return "(" + sql + ")"; + } + + public virtual string EqualTrue(string fieldName) + { + return "( " + fieldName + "=1 )"; + } + + public virtual string Null() + { + return "NULL"; + } + + public virtual string GetDate() + { + return "GETDATE()"; + } + + public virtual string GetRandom() + { + return "NEWID()"; + } + + public virtual string CaseWhen(List> sqls) + { + StringBuilder reslut = new StringBuilder(); + foreach (var item in sqls) + { + if (item.Key == "IF") + { + reslut.AppendFormat(" ( CASE WHEN {0} ", item.Value); + } + else if (item.Key == "End") + { + reslut.AppendFormat("ELSE {0} END )", item.Value); + } + else if (item.Key == "Return") + { + reslut.AppendFormat(" THEN {0} ", item.Value); + } + else + { + reslut.AppendFormat(" WHEN {0} ", item.Value); + } + } + return reslut.ToString(); + } + public virtual string CharIndex(MethodCallExpressionModel model) + { + return string.Format("CHARINDEX ({0},{1})", model.Args[0].MemberName, model.Args[1].MemberName); + } + public virtual string CharIndexNew(MethodCallExpressionModel model) + { + return CharIndex(model); + } + + public virtual string ToVarchar(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS VARCHAR(MAX))", parameter.MemberName); + } + public virtual string BitwiseAnd(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + return string.Format(" ({0} & {1}) ", parameter.MemberName, parameter2.MemberName); ; + } + public virtual string BitwiseInclusiveOR(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + return string.Format(" ({0} | {1}) ", parameter.MemberName, parameter2.MemberName); ; + } + + public string Oracle_ToDate(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + return string.Format(" to_date({0},{1}) ", parameter.MemberName, parameter2.MemberName); ; + } + public string Oracle_ToChar(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + return string.Format("to_char({0},{1}) ", parameter.MemberName, parameter2.MemberName); ; + } + public string SqlServer_DateDiff(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + var parameter3 = model.Args[2]; + return string.Format(" DATEDIFF({0},{1},{2}) ", parameter.MemberValue?.ToString().ToSqlFilter(), parameter2.MemberName, parameter3.MemberName); ; + } + + public virtual string FormatRowNumber(MethodCallExpressionModel model) + { + var str = model.Args[0].MemberValue.ObjToString(); + var array = model.Args.Skip(1).Select(it => it.IsMember ? it.MemberName : it.MemberValue).ToArray(); + if (array.Length == 1 && array[0] is string[]) + { + return string.Format("'" + str + "'", array[0] as string[]); ; + } + else + { + return string.Format("'" + str + "'", array); + } + } + public virtual string Format(MethodCallExpressionModel model) + { + + var str = "'" + model.Args[0].MemberValue.ObjToString() + "'"; + if (model.Args[0].MemberValue.ObjToString().StartsWith('\'') && model.Args[0].MemberValue.ObjToString().EndsWith('\'')) + { + str = model.Args[0].MemberValue.ObjToString(); + } + var revalue = MergeString("'", "$1", "'"); + if (revalue.Contains("concat(")) + { + return FormatConcat(model); + } + if (model.Args.Count == 2 && model.Args[1].MemberValue is string[]) + { + List args = GetStringFormatArgs(str, model.Args[1].MemberValue as string[]); + return Format(new MethodCallExpressionModel() + { + Args = args + }); ; + } + str = Regex.Replace(str, @"(\{\d+?\})", revalue); + var array = model.Args.Skip(1).Select(it => it.IsMember ? it.MemberName : (it.MemberValue == null ? "''" : it.MemberValue.ToSqlValue())) + .Select(it => ToString(new MethodCallExpressionModel() + { + Args = new List() { + new MethodCallExpressionArgs(){ IsMember=true, MemberName=it } + } + })).ToArray(); + return string.Format("" + str + "", array); + } + private string FormatConcat(MethodCallExpressionModel model) + { + + var str = "concat('" + model.Args[0].MemberValue.ObjToString() + "')"; + if (model.Args.Count == 2 && model.Args[1].MemberValue is string[]) + { + List args = GetStringFormatArgs(str, model.Args[1].MemberValue as string[]); + return Format(new MethodCallExpressionModel() + { + Args = args + }); ; + } + str = Regex.Replace(str, @"(\{\d+?\})", "',$1,'"); + var array = model.Args.Skip(1).Select(it => it.IsMember ? it.MemberName : (it.MemberValue == null ? "''" : it.MemberValue.ToSqlValue())) + .Select(it => ToString(new MethodCallExpressionModel() + { + Args = new List() { + new MethodCallExpressionArgs(){ IsMember=true, MemberName=it } + } + })).ToArray(); + return string.Format("" + str + "", array); + } + + public virtual string Abs(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" ABS({0}) ", parameter.MemberName); + } + + public virtual string Round(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + return string.Format(" ROUND({0},{1}) ", parameter.MemberName, parameter2.MemberName); + } + + public virtual string DateDiff(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + var parameter3 = model.Args[2]; + return string.Format(" DATEDIFF({0},{1},{2}) ", parameter.MemberValue?.ToString().ToSqlFilter(), parameter2.MemberName, parameter3.MemberName); + } + public virtual string GreaterThan(MethodCallExpressionModel model) + { + //> + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + return string.Format(" ({0} > {1}) ", parameter.MemberName, parameter2.MemberName); + } + public virtual string GreaterThanOrEqual(MethodCallExpressionModel model) + { + //>= + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + return string.Format(" ({0} >= {1}) ", parameter.MemberName, parameter2.MemberName); + } + public virtual string LessThan(MethodCallExpressionModel model) + { + //< + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + return string.Format(" ({0} < {1}) ", parameter.MemberName, parameter2.MemberName); + } + public virtual string LessThanOrEqual(MethodCallExpressionModel model) + { + //<= + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + return string.Format(" ({0} <= {1}) ", parameter.MemberName, parameter2.MemberName); + } + + public virtual string Asc(MethodCallExpressionModel model) + { + return model.Args[0].MemberName + " ASC "; + } + public virtual string Desc(MethodCallExpressionModel model) + { + return model.Args[0].MemberName + " DESC "; + } + public virtual string Stuff(MethodCallExpressionModel model) + { + var parameter1 = model.Args[0]; + var parameter2 = model.Args[1]; + var parameter3 = model.Args[2]; + var parameter4 = model.Args[3]; + return $" STUFF ({parameter1.MemberName}, {parameter2.MemberName}, {parameter3.MemberName}, {parameter4.MemberName}) "; + } + public virtual string Exists(MethodCallExpressionModel model) + { + var parameter1 = model.Args[0]; + if (model.Args.Count > 1) + { + var parameter2 = model.Args[1]; + if (UtilMethods.IsParentheses(parameter1.MemberName)) + { + parameter1.MemberName = $" {parameter1.MemberName.ObjToString().Trim().TrimEnd(')')} AND {parameter2.MemberName}) "; + } + else + { + parameter1.MemberName = $" {parameter1.MemberName} AND {parameter2.MemberName} "; + } + } + if (UtilMethods.IsParentheses(parameter1.MemberName)) + { + return $" Exists{parameter1.MemberName} "; + } + else + { + return $" Exists({parameter1.MemberName}) "; + } + } + + public virtual string GetDateString(string dateValue, string format) + { + return null; + } + public virtual string GetForXmlPath() + { + return null; + } + public virtual string JsonIndex(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter1 = model.Args[1]; + return $"({parameter.MemberName}::json ->> {parameter1.MemberValue})"; + } + + + public virtual string JsonField(MethodCallExpressionModel model) + { + throw new NotImplementedException("Current database no support"); + } + + public virtual string JsonContainsFieldName(MethodCallExpressionModel model) + { + throw new NotImplementedException("Current database no support"); + } + + public virtual string JsonArrayLength(MethodCallExpressionModel model) + { + throw new NotImplementedException("Current database no support"); + } + + public virtual string JsonParse(MethodCallExpressionModel model) + { + throw new NotImplementedException("Current database no support"); + } + public virtual string JsonLike(MethodCallExpressionModel model) + { + model.Args[0].MemberName = ToString(model); + return Contains(model); + } + public virtual string Collate(MethodCallExpressionModel model) + { + var name = model.Args[0].MemberName; + return $" {name} collate Chinese_PRC_CS_AS "; + } + public virtual string AggregateSumNoNull(MethodCallExpressionModel model) + { + model.Args.Add(new MethodCallExpressionArgs() { MemberValue = 0, MemberName = 0 }); + var name = IsNull(model); + model.Args[0].MemberName = name; + return AggregateSum(model); + } + public virtual string AggregateAvgNoNull(MethodCallExpressionModel model) + { + model.Args.Add(new MethodCallExpressionArgs() { MemberValue = 0, MemberName = 0 }); + var name = IsNull(model); + model.Args[0].MemberName = name; + return AggregateAvg(model); + } + public virtual string JsonListObjectAny(MethodCallExpressionModel model) + { + throw new NotImplementedException("Current database no support"); + } + public virtual string JsonArrayAny(MethodCallExpressionModel model) + { + throw new NotImplementedException("Current database no support"); + } + public virtual string CompareTo(MethodCallExpressionModel model) + { + var parameterNameA = model.Args[0].MemberName; + var parameterNameB = model.Args[1].MemberName; + return $"(case when {parameterNameA}>{parameterNameB} then 1 when {parameterNameA}={parameterNameB} then 0 else -1 end)"; + } + public virtual string SplitIn(MethodCallExpressionModel model) + { + var fullString = model.Args[0].MemberName + ""; + var value = model.Args[1].MemberName + ""; + var value1 = MergeString(value, "','"); + var value2 = MergeString("','", value); + var value3 = MergeString("','", value, "','"); + if (model.Args.Count == 3) + { + value1 = value1.Replace("','", model.Args[2].MemberName + ""); + value2 = value2.Replace("','", model.Args[2].MemberName + ""); + value3 = value3.Replace("','", model.Args[2].MemberName + ""); + } + var likeString1 = + StartsWith(new MethodCallExpressionModel() + { + Args = new List() { + new MethodCallExpressionArgs(){ IsMember=true, MemberName=fullString }, + new MethodCallExpressionArgs(){ IsMember=true, MemberName=value1 } + } + }); + var likeString2 = + EndsWith(new MethodCallExpressionModel() + { + Args = new List() { + new MethodCallExpressionArgs(){ IsMember=true, MemberName=fullString }, + new MethodCallExpressionArgs(){ IsMember=true, MemberName=value2 } + } + }); + var likeString3 = + Contains(new MethodCallExpressionModel() + { + Args = new List() { + new MethodCallExpressionArgs(){ IsMember=true, MemberName=fullString }, + new MethodCallExpressionArgs(){ IsMember=true, MemberName=value3 } + } + }); + return $" ({likeString1} or {likeString2} or {likeString3} or {fullString}={value} ) "; + } + + public string Like(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + return string.Format(" ({0} like {1} ) ", parameter.MemberName, parameter2.MemberName); + } + public string ToSingle(MethodCallExpressionModel model) + { + return ToDecimal(model); + } + public string ListAny(MethodCallExpressionModel model) + { + if (IsArrayAnyParameter(model)) + { + return ListArrayAny(model); + } + StringBuilder sb = new StringBuilder(); + if (model.Args[0].MemberValue != null && (model.Args[0].MemberValue as IList).Count > 0) + { + sb.Append(" ( "); + var listPar = model.Args[1].MemberValue as ListAnyParameter; + foreach (var item in (model.Args[0].MemberValue as IList)) + { + var sql = listPar.Sql; + if (sb.Length > 3) + { + sb.Append("OR"); + } + foreach (var columnInfo in listPar.Columns) + { + var replace = listPar.ConvetColumnFunc($"{listPar.Name}.{columnInfo.DbColumnName}"); + if (sql.Contains(replace)) + { + var value = columnInfo.PropertyInfo.GetValue(item); + var newValue = "null"; + if (value != null) + { + if (UtilMethods.IsNumber(columnInfo.UnderType.Name)) + { + newValue = value.ToString(); + } + else if (value is Enum) + { + newValue = Convert.ToInt64(value) + ""; + } + else if (columnInfo.UnderType == SqlSugar.UtilConstants.GuidType) + { + newValue = ToGuid(new MethodCallExpressionModel() + { + Args = new List() + { + new MethodCallExpressionArgs(){ + MemberValue=value.ToSqlValue(), + MemberName=value.ToSqlValue() + } + } + }); + } + else if (columnInfo.UnderType == SqlSugar.UtilConstants.DateType) + { + newValue = ToDate(new MethodCallExpressionModel() + { + Args = new List() + { + new MethodCallExpressionArgs(){ + MemberValue=UtilMethods.GetConvertValue( value).ToSqlValue(), + MemberName=UtilMethods.GetConvertValue( value).ToSqlValue() + } + } + }); + } + else + { + newValue = value.ToSqlValue(); + } + } + if (columnInfo.UnderType == UtilConstants.StringType && model.Conext?.SugarContext?.Context?.CurrentConnectionConfig?.DbType == DbType.SqlServer) + { + if (model.Conext?.SugarContext?.Context?.CurrentConnectionConfig?.MoreSettings?.DisableNvarchar != true) + { + if (columnInfo.SqlParameterDbType is System.Data.DbType type && type == System.Data.DbType.AnsiString) + { + + } + else + { + newValue = "N" + newValue; + } + } + } + sql = sql.Replace(replace, newValue); + } + } + sb.Append(sql); + } + sb.Append(" ) "); + } + var result = sb.ToString(); + if (result.IsNullOrEmpty()) + { + return " 1=2 "; + } + else + { + return result; + } + } + public string ListAll(MethodCallExpressionModel model) + { + if (IsArrayAnyParameter(model)) + { + return ListArrayAll(model); + } + StringBuilder sb = new StringBuilder(); + if (model.Args[0].MemberValue != null && (model.Args[0].MemberValue as IList).Count > 0) + { + sb.Append(" ( "); + var listPar = model.Args[1].MemberValue as ListAnyParameter; + foreach (var item in (model.Args[0].MemberValue as IList)) + { + var sql = listPar.Sql; + if (sb.Length > 3) + { + sb.Append("AND"); + } + foreach (var columnInfo in listPar.Columns) + { + var replace = listPar.ConvetColumnFunc($"{listPar.Name}.{columnInfo.DbColumnName}"); + if (sql.Contains(replace)) + { + var value = columnInfo.PropertyInfo.GetValue(item); + var newValue = "null"; + if (value != null) + { + if (UtilMethods.IsNumber(columnInfo.UnderType.Name)) + { + newValue = value.ToString(); + } + else if (columnInfo.UnderType == SqlSugar.UtilConstants.GuidType) + { + newValue = ToGuid(new MethodCallExpressionModel() + { + Args = new List() + { + new MethodCallExpressionArgs(){ + MemberValue=value.ToSqlValue(), + MemberName=value.ToSqlValue() + } + } + }); + } + else if (columnInfo.UnderType == SqlSugar.UtilConstants.DateType) + { + newValue = ToDate(new MethodCallExpressionModel() + { + Args = new List() + { + new MethodCallExpressionArgs(){ + MemberValue=UtilMethods.GetConvertValue( value).ToSqlValue(), + MemberName=UtilMethods.GetConvertValue( value).ToSqlValue() + } + } + }); + } + else + { + newValue = value.ToSqlValue(); + } + } + sql = sql.Replace(replace, newValue); + } + } + sb.Append(sql); + } + sb.Append(" ) "); + } + var result = sb.ToString(); + if (result.IsNullOrEmpty()) + { + return " 1=2 "; + } + else + { + return result; + } + } + public virtual string GetTableWithDataBase(string dataBaseName, string tableName) + { + return $"{dataBaseName}.{tableName}"; + } + + public virtual string Modulo(MethodCallExpressionModel model) + { + return "(" + model.Args[0].MemberName + " % " + model.Args[1].MemberName + ")"; + } + + private static bool IsArrayAnyParameter(MethodCallExpressionModel model) + { + var memberValue = model?.Args?.FirstOrDefault()?.MemberValue; + return UtilMethods.IsValueTypeArray(memberValue); + } + + private string ListArrayAny(MethodCallExpressionModel model) + { + StringBuilder sb = new StringBuilder(); + if (model.Args[0].MemberValue != null && (model.Args[0].MemberValue as IList).Count > 0) + { + sb.Append(" ( "); + var listPar = model.Args[1].MemberValue as ListAnyParameter; + foreach (var item in (model.Args[0].MemberValue as IList)) + { + var sql = listPar.Sql; + if (sb.Length > 3) + { + sb.Append("OR"); + } + foreach (var columnInfo in listPar.Columns) + { + var value = item; + var newValue = "null"; + if (value != null) + { + if (columnInfo.DbTableName != "String" && UtilMethods.IsNumber(columnInfo.UnderType.Name)) + { + newValue = value.ToString(); + } + else if (columnInfo.UnderType == SqlSugar.UtilConstants.GuidType) + { + newValue = ToGuid(new MethodCallExpressionModel() + { + Args = new List() + { + new MethodCallExpressionArgs(){ + MemberValue=value.ToSqlValue(), + MemberName=value.ToSqlValue() + } + } + }); + } + else if (columnInfo.UnderType == SqlSugar.UtilConstants.DateType) + { + newValue = ToDate(new MethodCallExpressionModel() + { + Args = new List() + { + new MethodCallExpressionArgs(){ + MemberValue=UtilMethods.GetConvertValue( value).ToSqlValue(), + MemberName=UtilMethods.GetConvertValue( value).ToSqlValue() + } + } + }); + } + else + { + newValue = value.ToSqlValue(); + if (columnInfo.EntityName == "String" && model.Conext?.SugarContext?.Context?.CurrentConnectionConfig?.DbType == DbType.SqlServer) + { + if (model.Conext?.SugarContext?.Context?.CurrentConnectionConfig?.MoreSettings?.DisableNvarchar != true) + { + if (model.DataObject is EntityColumnInfo dc && dc.SqlParameterDbType is System.Data.DbType type && type == System.Data.DbType.AnsiString) + { + + } + else + { + newValue = "N" + newValue; + } + } + } + } + } + //Regex regex = new Regex("\@"); + if (!sql.Contains(ParameterKeyWord)) + { + sql = sql.Replace(" =)", $" = {newValue})"); + if (!sql.Contains(newValue)) + { + sql = sql.Replace(" )", $" = {newValue})"); + } + } + else + { + Regex reg = new Regex(ParameterKeyWord + @"MethodConst\d+"); + sql = reg.Replace(sql, it => + { + return " " + newValue + " "; + }); + } + + } + sb.Append(sql); + } + sb.Append(" ) "); + } + var result = sb.ToString(); + result = result.Replace(" = null)", " is null)"); + if (result.IsNullOrEmpty()) + { + return " 1=2 "; + } + else + { + return result; + } + } + + private string ListArrayAll(MethodCallExpressionModel model) + { + StringBuilder sb = new StringBuilder(); + if (model.Args[0].MemberValue != null && (model.Args[0].MemberValue as IList).Count > 0) + { + sb.Append(" ( "); + var listPar = model.Args[1].MemberValue as ListAnyParameter; + foreach (var item in (model.Args[0].MemberValue as IList)) + { + var sql = listPar.Sql; + if (sb.Length > 3) + { + sb.Append("AND"); + } + foreach (var columnInfo in listPar.Columns) + { + var value = item; + var newValue = "null"; + if (value != null) + { + if (columnInfo.DbTableName != "String" && UtilMethods.IsNumber(columnInfo.UnderType.Name)) + { + newValue = value.ToString(); + } + else if (columnInfo.UnderType == SqlSugar.UtilConstants.GuidType) + { + newValue = ToGuid(new MethodCallExpressionModel() + { + Args = new List() + { + new MethodCallExpressionArgs(){ + MemberValue=value.ToSqlValue(), + MemberName=value.ToSqlValue() + } + } + }); + } + else if (columnInfo.UnderType == SqlSugar.UtilConstants.DateType) + { + newValue = ToDate(new MethodCallExpressionModel() + { + Args = new List() + { + new MethodCallExpressionArgs(){ + MemberValue=UtilMethods.GetConvertValue( value).ToSqlValue(), + MemberName=UtilMethods.GetConvertValue( value).ToSqlValue() + } + } + }); + } + else + { + newValue = value.ToSqlValue(); + } + } + //Regex regex = new Regex("\@"); + if (!sql.Contains(ParameterKeyWord)) + { + sql = sql.Replace(" =)", $" = {newValue})"); + if (!sql.Contains(newValue)) + { + sql = sql.Replace(" )", $" = {newValue})"); + } + } + else + { + Regex reg = new Regex(ParameterKeyWord + @"MethodConst\d+"); + sql = reg.Replace(sql, it => + { + return " " + newValue + " "; + }); + } + + } + sb.Append(sql); + } + sb.Append(" ) "); + } + var result = sb.ToString(); + result = result.Replace(" = null)", " is null)"); + if (result.IsNullOrEmpty()) + { + return " 1=2 "; + } + else + { + return result; + } + } + + + private static List GetStringFormatArgs(string str, object array) + { + var args = new List() + { + new MethodCallExpressionArgs(){ + MemberName=str, + MemberValue=str + } + }; + args.AddRange((array as string[]).Select(it => new MethodCallExpressionArgs() + { + MemberValue = it, + MemberName = it, + IsMember = (it?.StartsWith('[') == true || it?.StartsWith('`') == true || it?.StartsWith('\"') == true) + && + (it?.EndsWith(']') == true || it?.EndsWith('`') == true || it?.EndsWith('\"') == true) + })); + return args; + } + + public virtual string WeekOfYear(MethodCallExpressionModel mode) + { + var parameterNameA = mode.Args[0].MemberName; + return $" DATE_PART('week', {parameterNameA})+1 "; + } + + public virtual string TrimEnd(MethodCallExpressionModel mode) + { + var parameterNameA = mode.Args[0].MemberName; + var parameterNameB = mode.Args[1].MemberName; + return $" CASE WHEN RIGHT({parameterNameA}, 1) = {parameterNameB} THEN LEFT({parameterNameA}, LENGTH({parameterNameA}) - 1) ELSE {parameterNameA} END "; + } + public virtual string TrimStart(MethodCallExpressionModel mode) + { + + var parameterNameA = mode.Args[0].MemberName; + var parameterNameB = mode.Args[1].MemberName; + return $" CASE WHEN LEFT({parameterNameA}, 1) = {parameterNameB} THEN RIGHT({parameterNameA}, LEN({parameterNameA}) - 1) ELSE {parameterNameA} END "; + } + + public virtual string Left(MethodCallExpressionModel mode) + { + var parameterNameA = mode.Args[0].MemberName; + var parameterNameB = mode.Args[1].MemberName; + return $" LEFT({parameterNameA},{parameterNameB}) "; + } + public virtual string Right(MethodCallExpressionModel mode) + { + var parameterNameA = mode.Args[0].MemberName; + var parameterNameB = mode.Args[1].MemberName; + return $" RIGHT({parameterNameA},{parameterNameB}) "; + } + public virtual string PadLeft(MethodCallExpressionModel mode) + { + var parameterNameA = mode.Args[0].MemberName; + var parameterNameB = mode.Args[1].MemberName; + var parameterNameC = mode.Args[2].MemberName; + return $" LPAD({parameterNameA},{parameterNameB},{parameterNameC}) "; + } + + public virtual string Floor(MethodCallExpressionModel mode) + { + var parameterNameA = mode.Args[0].MemberName; + return $" FLOOR({parameterNameA})"; + } + public virtual string Ceil(MethodCallExpressionModel mode) + { + var parameterNameA = mode.Args[0].MemberName; + return $" CEILING({parameterNameA}) "; + } + public virtual string NewUid(MethodCallExpressionModel mode) + { + return $" uuid_generate_v4() "; + } + public virtual string Coalesce(MethodCallExpressionModel mode) + { + var parameterNameA = mode.Args[0].MemberName; + var parameterNameB = mode.Args[1].MemberName; + return $" COALESCE({parameterNameA},{parameterNameB}) "; + } + public virtual string FullTextContains(MethodCallExpressionModel mode) + { + var columns = mode.Args[0].MemberName; + if (mode.Args[0].MemberValue is List) + { + columns = string.Join("|| ' ' ||", mode.Args[0].MemberValue as List); + } + var searchWord = mode.Args[1].MemberName; + return $"to_tsvector('chinese', {columns}) @@ to_tsquery('chinese', {searchWord})"; + } + + public virtual string PgsqlArrayContains(MethodCallExpressionModel model) + { + // 如果model.Args[1]是一个复杂类型,你可能需要将其转换为字符串或适当的格式 + // 在这里,我们假设它是一个可以直接转换为字符串的值 + string valueToFind = model.Args[1].MemberValue.ToString(); // 或者使用适当的转换方法 + var type = "text"; + if (model.Args[1].MemberValue is int) + { + type = "int4"; + } + else if (model.Args[1].MemberValue is long) + { + type = "int8"; + } + else if (model.Args[1].MemberValue is short) + { + type = "int2"; + } + if (!UtilMethods.IsNumber(model.Args[1].MemberValue.GetType().Name)) + { + valueToFind = $"'{valueToFind}'"; + } + // PostgreSQL查询字符串 + string queryCondition = $"{model.Args[0].MemberName}::{type}[] @> ARRAY[{valueToFind}]"; + + // 如果需要处理NULL值或其他复杂情况,请在这里添加逻辑 + + return queryCondition; + } + public virtual string SelectFields(MethodCallExpressionModel model) + { + return string.Join(",", model.Args.Select(it => it.MemberName)); + } + public virtual string UNIX_TIMESTAMP(MethodCallExpressionModel model) + { + var parameterNameA = model.Args[0].MemberName; + return $" UNIX_TIMESTAMP({parameterNameA}) "; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/DbMethods/IDbMethods.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/DbMethods/IDbMethods.cs new file mode 100644 index 000000000..02274b9dd --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/DbMethods/IDbMethods.cs @@ -0,0 +1,127 @@ +namespace SqlSugar +{ + public interface IDbMethods + { + string IIF(MethodCallExpressionModel model); + string HasNumber(MethodCallExpressionModel model); + string HasValue(MethodCallExpressionModel model); + string IsNullOrEmpty(MethodCallExpressionModel model); + string ToLower(MethodCallExpressionModel model); + string ToUpper(MethodCallExpressionModel model); + string Trim(MethodCallExpressionModel model); + string Contains(MethodCallExpressionModel model); + string ContainsArray(MethodCallExpressionModel model); + string ContainsArrayUseSqlParameters(MethodCallExpressionModel model); + string Equals(MethodCallExpressionModel model); + string EqualsNull(MethodCallExpressionModel model); + string DateIsSameDay(MethodCallExpressionModel model); + string DateIsSameByType(MethodCallExpressionModel model); + string DateAddByType(MethodCallExpressionModel model); + + string DateValue(MethodCallExpressionModel model); + string DateAddDay(MethodCallExpressionModel model); + string Between(MethodCallExpressionModel model); + string StartsWith(MethodCallExpressionModel model); + string EndsWith(MethodCallExpressionModel model); + string ToInt32(MethodCallExpressionModel model); + string GetStringJoinSelector(string result, string separator); + string ToInt64(MethodCallExpressionModel model); + string ToString(MethodCallExpressionModel model); + string ToVarchar(MethodCallExpressionModel model); + string ToGuid(MethodCallExpressionModel model); + string ToDouble(MethodCallExpressionModel model); + string ToBool(MethodCallExpressionModel model); + string CaseWhen(List> sqls); + string Substring(MethodCallExpressionModel model); + string ToDate(MethodCallExpressionModel model); + string ToDateShort(MethodCallExpressionModel model); + string ToTime(MethodCallExpressionModel model); + string ToDecimal(MethodCallExpressionModel model); + string Length(MethodCallExpressionModel model); + string Replace(MethodCallExpressionModel model); + string AggregateSum(MethodCallExpressionModel model); + string AggregateAvg(MethodCallExpressionModel model); + string AggregateMin(MethodCallExpressionModel model); + string AggregateMax(MethodCallExpressionModel model); + string AggregateCount(MethodCallExpressionModel model); + string AggregateDistinctCount(MethodCallExpressionModel model); + string AggregateDistinctSum(MethodCallExpressionModel model); + string MappingColumn(MethodCallExpressionModel model); + string IsNull(MethodCallExpressionModel model); + string GetSelfAndAutoFill(string shortName, bool isSingle); + string True(); + string False(); + string TrueValue(); + string FalseValue(); + string GuidNew(); + string MergeString(params string[] strings); + string EqualTrue(string value); + string Pack(string sql); + string Null(); + string GetDate(); + string GetRandom(); + string CharIndex(MethodCallExpressionModel model); + string CharIndexNew(MethodCallExpressionModel model); + string BitwiseAnd(MethodCallExpressionModel model); + string BitwiseInclusiveOR(MethodCallExpressionModel model); + + string Oracle_ToDate(MethodCallExpressionModel model); + string Oracle_ToChar(MethodCallExpressionModel model); + string SqlServer_DateDiff(MethodCallExpressionModel model); + string Format(MethodCallExpressionModel model); + string FormatRowNumber(MethodCallExpressionModel model); + string Abs(MethodCallExpressionModel model); + string Round(MethodCallExpressionModel model); + + string DateDiff(MethodCallExpressionModel model); + string GreaterThan(MethodCallExpressionModel model); + string GreaterThanOrEqual(MethodCallExpressionModel model); + string LessThan(MethodCallExpressionModel model); + string LessThanOrEqual(MethodCallExpressionModel model); + string Asc(MethodCallExpressionModel model); + string Desc(MethodCallExpressionModel model); + string Stuff(MethodCallExpressionModel model); + string RowNumber(MethodCallExpressionModel model); + string RowCount(MethodCallExpressionModel model); + string RowSum(MethodCallExpressionModel model); + string RowMin(MethodCallExpressionModel model); + string RowMax(MethodCallExpressionModel model); + string RowAvg(MethodCallExpressionModel model); + string Exists(MethodCallExpressionModel model); + string GetDateString(string dateValue, string format); + string GetForXmlPath(); + string JsonIndex(MethodCallExpressionModel model); + string JsonField(MethodCallExpressionModel model); + string JsonContainsFieldName(MethodCallExpressionModel model); + string JsonArrayLength(MethodCallExpressionModel model); + string JsonParse(MethodCallExpressionModel model); + string JsonLike(MethodCallExpressionModel model); + string Collate(MethodCallExpressionModel model); + string AggregateSumNoNull(MethodCallExpressionModel model); + string AggregateAvgNoNull(MethodCallExpressionModel model); + string JsonListObjectAny(MethodCallExpressionModel model); + string JsonArrayAny(MethodCallExpressionModel model); + string CompareTo(MethodCallExpressionModel model); + string SplitIn(MethodCallExpressionModel model); + string ListAny(MethodCallExpressionModel model); + string ListAll(MethodCallExpressionModel model); + string GetTableWithDataBase(string databaseName, string tableName); + string Modulo(MethodCallExpressionModel mode); + string Like(MethodCallExpressionModel mode); + string ToSingle(MethodCallExpressionModel mode); + string WeekOfYear(MethodCallExpressionModel mode); + string TrimEnd(MethodCallExpressionModel mode); + string TrimStart(MethodCallExpressionModel mode); + string Left(MethodCallExpressionModel mode); + string Right(MethodCallExpressionModel mode); + string PadLeft(MethodCallExpressionModel mode); + string Floor(MethodCallExpressionModel mode); + string Ceil(MethodCallExpressionModel mode); + string NewUid(MethodCallExpressionModel mode); + string FullTextContains(MethodCallExpressionModel mode); + string PgsqlArrayContains(MethodCallExpressionModel model); + string SelectFields(MethodCallExpressionModel model); + string Coalesce(MethodCallExpressionModel model); + string UNIX_TIMESTAMP(MethodCallExpressionModel model); + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/DbMethods/SqlFunc.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/DbMethods/SqlFunc.cs new file mode 100644 index 000000000..3d241362d --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/DbMethods/SqlFunc.cs @@ -0,0 +1,459 @@ +using System.Linq.Expressions; +using System.Reflection; + +namespace SqlSugar +{ + public partial class SqlFunc + { + public static long UNIX_TIMESTAMP(DateTime dateTime) + { + throw new NotSupportedException("Can only be used in expressions"); + } + public static T Coalesce(T value1, T value2) + { + throw new NotSupportedException("Can only be used in expressions"); + } + public static bool FullTextContains(string[] columnNames, string keyword) + { + throw new NotSupportedException("Can only be used in expressions"); + } + public static bool FullTextContains(string columnName, string keyword) + { + throw new NotSupportedException("Can only be used in expressions"); + } + public static int Floor(object value) + { + throw new NotSupportedException("Can only be used in expressions"); + } + public static int Ceil(object value) + { + throw new NotSupportedException("Can only be used in expressions"); + } + public static int WeekOfYear(DateTime fieldName) + { + throw new NotSupportedException("Can only be used in expressions"); + } + public static string Left(string value, int number) + { + throw new NotSupportedException("Can only be used in expressions"); + } + public static string Right(string value, int number) + { + throw new NotSupportedException("Can only be used in expressions"); + } + public static string PadLeft(string value, int number, char padChar) + { + throw new NotSupportedException("Can only be used in expressions"); + } + public static bool Like(string fieldName, string likeValue) + { + throw new NotSupportedException("Can only be used in expressions"); + } + public static int Modulo(decimal numA, decimal numB) + { + throw new NotSupportedException("Can only be used in expressions"); + } + public static int CompareTo(decimal numA, decimal numB) + { + throw new NotSupportedException("Can only be used in expressions"); + } + public static int CompareTo(int numA, int numB) + { + throw new NotSupportedException("Can only be used in expressions"); + } + public static int CompareTo(string strA, string strB) + { + throw new NotSupportedException("Can only be used in expressions"); + } + public static int RowNumber(object orderByField, object partitionBy) + { + throw new NotSupportedException("Can only be used in expressions"); + } + public static int RowNumber(object orderByField) + { + throw new NotSupportedException("Can only be used in expressions"); + } + + public static int Rank(object orderByField, object partitionBy) + { + throw new NotSupportedException("Can only be used in expressions"); + } + public static int Rank(object orderByField) + { + throw new NotSupportedException("Can only be used in expressions"); + } + public static int DenseRank(object orderByField, object partitionBy) + { + throw new NotSupportedException("Can only be used in expressions"); + } + public static int DenseRank(object orderByField) + { + throw new NotSupportedException("Can only be used in expressions"); + } + + public static int RowCount(object countFiledName, object orderByField, object partitionBy) + { + throw new NotSupportedException("Can only be used in expressions"); + } + public static int RowCount() + { + throw new NotSupportedException("Can only be used in expressions"); + } + public static TRestult RowSum(TRestult filedName) + { + throw new NotSupportedException("Can only be used in expressions"); + } + public static TRestult RowSum(TRestult filedName, object orderByField, object partitionBy) + { + throw new NotSupportedException("Can only be used in expressions"); + } + public static TRestult RowMax(TRestult filedName) + { + throw new NotSupportedException("Can only be used in expressions"); + } + public static TRestult RowMax(TRestult filedNameobject, object orderByField, object partitionBy) + { + throw new NotSupportedException("Can only be used in expressions"); + } + public static TRestult RowMin(TRestult filedName) + { + throw new NotSupportedException("Can only be used in expressions"); + } + public static TRestult RowMin(TRestult filedName, object orderByField, object partitionBy) + { + throw new NotSupportedException("Can only be used in expressions"); + } + public static TRestult RowAvg(TRestult filedName) + { + throw new NotSupportedException("Can only be used in expressions"); + } + public static TRestult RowAvg(TRestult filedName, object orderByField, object partitionBy) + { + throw new NotSupportedException("Can only be used in expressions"); + } + public static string JsonField(object json, string fieldName) + { + throw new NotSupportedException("Can only be used in expressions"); + } + public static string JsonIndex(object json, int jsonIndex) + { + throw new NotSupportedException("Can only be used in expressions"); + } + public static string JsonField(object json, string fieldName, string includeFieldName) + { + throw new NotSupportedException("Can only be used in expressions"); + } + + public static string JsonField(object json, string fieldName, string includeFieldName, string ThenIncludeFieldName) + { + throw new NotSupportedException("Can only be used in expressions"); + } + public static string JsonField(object json, string fieldName, string includeFieldName, string ThenIncludeFieldName, string ThenIncludeFieldName2) + { + throw new NotSupportedException("Can only be used in expressions"); + } + public static string JsonField(object json, string fieldName, string includeFieldName, string ThenIncludeFieldName, string ThenIncludeFieldName2, string ThenIncludeFieldName3) + { + throw new NotSupportedException("Can only be used in expressions"); + } + public static bool JsonContainsFieldName(object json, string fieldName) + { + throw new NotSupportedException("Can only be used in expressions"); + } + + public static int JsonArrayLength(object json) + { + throw new NotSupportedException("Can only be used in expressions"); + } + + public static string JsonParse(object json) + { + throw new NotSupportedException("Can only be used in expressions"); + } + + public static bool JsonLike(object json, string likeStr) + { + throw new NotSupportedException("Can only be used in expressions"); + } + + public static T Desc(T value) + { + throw new NotSupportedException("Can only be used in expressions"); + } + public static T Asc(T value) + { + throw new NotSupportedException("Can only be used in expressions"); + } + public static int DateDiff(DateType dateType, DateTime littleTime, DateTime bigTime) + { + throw new NotSupportedException("Can only be used in expressions"); + } + public static bool GreaterThan(object thisValue, object gtValue) + { + throw new NotSupportedException("Can only be used in expressions"); + } + + public static string Stuff(string sourceString, int start, int length, string AddString) + { + throw new NotSupportedException("Can only be used in expressions"); + } + + public static bool GreaterThanOrEqual(object thisValue, object gtValue) + { + throw new NotSupportedException("Can only be used in expressions"); + } + public static bool GreaterThan_LinqDynamicCore(object thisValue, object ltValue) + { + throw new NotSupportedException("Can only be used in expressions"); + } + public static bool LessThan(object thisValue, object ltValue) + { + throw new NotSupportedException("Can only be used in expressions"); + } + + public static bool LessThan_LinqDynamicCore(object thisValue, object ltValue) + { + throw new NotSupportedException("Can only be used in expressions"); + } + public static bool LessThanOrEqual(object thisValue, object ltValue) + { + throw new NotSupportedException("Can only be used in expressions"); + } + public static bool HasNumber(object thisValue) + { + return thisValue.ObjToInt() > 0; + } + public static bool HasValue(object thisValue) + { + return thisValue.HasValue(); + } + public static bool IsNullOrEmpty(object thisValue) + { + return thisValue.IsNullOrEmpty(); + } + public static string ToLower(object thisValue) + { + return thisValue == null ? null : thisValue.ToString().ToLower(); + } + public static string ToUpper(object thisValue) + { + return thisValue == null ? null : thisValue.ToString().ToUpper(); + } + public static string Trim(object thisValue) + { + return thisValue == null ? null : thisValue.ToString().Trim(); + } + public static string TrimEnd(object thisValue, string trimChar) + { + throw new NotSupportedException("Can only be used in expressions"); + } + public static string TrimStart(object thisValue, string trimChar) + { + throw new NotSupportedException("Can only be used in expressions"); + } + public static bool Contains(string thisValue, string parameterValue) + { + return thisValue.Contains(parameterValue); + } + public static bool ContainsArray(T[] thisValue, object InField) + { + return thisValue.Contains((T)InField); + } + public static bool ContainsArray(List thisValue, object InField) + { + return thisValue.Contains((T)InField); + } + public static bool ContainsArrayUseSqlParameters(List thisValue, object InField) + { + return thisValue.Contains((T)InField); + } + public static bool ContainsArrayUseSqlParameters(T[] thisValue, object InField) + { + return thisValue.Contains((T)InField); + } + public static bool StartsWith(string thisValue, string parameterValue) + { + return thisValue.StartsWith(parameterValue); + } + public static bool EndsWith(string thisValue, string parameterValue) + { + return thisValue.EndsWith(parameterValue); + } + public new static bool Equals(object thisValue, object parameterValue) + { + return thisValue.Equals(parameterValue); + } + public static bool EqualsNull(object thisValue, object parameterValue) + { + return thisValue.Equals(parameterValue); + } + + public static bool Exists(string subQueryableName_Or_OneToOnePropertyName) + { + throw new NotSupportedException("Can only be used in expressions"); + } + public static bool Exists(valueType subQueryableName_Or_OneToOnePropertyName) where valueType : struct + { + throw new NotSupportedException("Can only be used in expressions"); + } + public static bool Exists(string subQueryableName_Or_OneToOnePropertyName, List conditionalModels) + { + throw new NotSupportedException("Can only be used in expressions"); + } + public static bool Exists(valueType subQueryableName_Or_OneToOnePropertyName, List conditionalModels) where valueType : struct + { + throw new NotSupportedException("Can only be used in expressions"); + } + public static bool DateIsSame(DateTime date1, DateTime date2) + { + return date1.ToString("yyyy-MM-dd") == date2.ToString("yyyy-MM-dd"); + } + public static bool DateIsSame(DateTime? date1, DateTime? date2) + { + return ((DateTime)date1).ToString("yyyy-MM-dd") == ((DateTime)date2).ToString("yyyy-MM-dd"); + } + public static bool DateIsSame(DateTime date1, DateTime date2, DateType dataType) { throw new NotSupportedException("Can only be used in expressions"); } + public static DateTime DateAdd(DateTime date, int addValue, DateType dataType) { throw new NotSupportedException("Can only be used in expressions"); } + public static DateTimeOffset DateAdd(DateTimeOffset date, int addValue, DateType dataType) { throw new NotSupportedException("Can only be used in expressions"); } + public static DateTime DateAdd(DateTime date, int addValue) { throw new NotSupportedException("Can only be used in expressions"); } + public static DateTimeOffset DateAdd(DateTimeOffset date, int addValue) { throw new NotSupportedException("Can only be used in expressions"); } + public static int DateValue(DateTime date, DateType dataType) { throw new NotSupportedException("Can only be used in expressions"); } + public static int DateValue(DateTimeOffset date, DateTimeOffset dataType) { throw new NotSupportedException("Can only be used in expressions"); } + public static bool Between(object value, object start, object end) { throw new NotSupportedException("Can only be used in expressions"); } + public static TResult IIF(bool isTrue, TResult thenValue, TResult elseValue) { return isTrue ? thenValue : elseValue; } + public static TResult IsNull(TResult thisValue, TResult ifNullValue) { throw new NotSupportedException("Can only be used in expressions"); } + public static string MergeString(string value1, string value2) { throw new NotSupportedException("Can only be used in expressions"); } + public static string MergeString(string value1, string value2, string value3) { throw new NotSupportedException("Can only be used in expressions"); } + public static string MergeString(string value1, string value2, string value3, string value4) { throw new NotSupportedException("Can only be used in expressions"); } + public static string MergeString(string value1, string value2, string value3, string value4, string value5) { throw new NotSupportedException("Can only be used in expressions"); } + public static string MergeString(string value1, string value2, string value3, string value4, string value5, string value6) { throw new NotSupportedException("Can only be used in expressions"); } + public static string MergeString(string value1, string value2, string value3, string value4, string value5, string value6, string value7) { throw new NotSupportedException("Can only be used in expressions"); } + public static int ToInt32(object value) { return value.ObjToInt(); } + public static float ToSingle(object value) { return Convert.ToSingle(value); } + public static long ToInt64(object value) { return Convert.ToInt64(value); } + /// + /// yyyy-MM-dd HH:mm:ss.fff + /// + /// + /// + public static DateTime ToDate(object value) { return value.ObjToDate(); } + public static DateTime ToDateShort(object value) { return value.ObjToDate(); } + /// + ///HH:mm:ss + /// + /// + /// + public static TimeSpan ToTime(object value) { throw new NotSupportedException("Can only be used in expressions"); } + public static string ToString(object value) { return value.ObjToString(); } + public static string ToVarchar(object value) { return value.ObjToString(); } + public static decimal ToDecimal(object value) { return value.ObjToDecimal(); } + public static Guid ToGuid(object value) { return Guid.Parse(value.ObjToString()); } + public static Guid NewUid() { throw new NotSupportedException("Can only be used in expressions"); } + public static double ToDouble(object value) { return value.ObjToMoney(); } + public static bool ToBool(object value) { return value.ObjToBool(); } + public static string Substring(object value, int index, int length) { return value.ObjToString().Substring(index, length); } + public static string Replace(object value, string oldChar, string newChar) { return value.ObjToString().Replace(oldChar, newChar); } + public static int Length(object value) { return value.ObjToString().Length; } + public static TResult AggregateSum(TResult thisValue) { throw new NotSupportedException("Can only be used in expressions"); } + public static TResult AggregateSumNoNull(TResult thisValue) { throw new NotSupportedException("Can only be used in expressions"); } + public static string Collate(string thisValue) { throw new NotSupportedException("Can only be used in expressions"); } + public static TResult AggregateAvg(TResult thisValue) { throw new NotSupportedException("Can only be used in expressions"); } + public static TResult AggregateAvgNoNull(TResult thisValue) { throw new NotSupportedException("Can only be used in expressions"); } + public static TResult AggregateMin(TResult thisValue) { throw new NotSupportedException("Can only be used in expressions"); } + public static TResult AggregateMax(TResult thisValue) { throw new NotSupportedException("Can only be used in expressions"); } + public static int AggregateCount(TResult thisValue) { throw new NotSupportedException("Can only be used in expressions"); } + public static int AggregateDistinctCount(TResult thisValue) { throw new NotSupportedException("Can only be used in expressions"); } + public static int AggregateDistinctSum(TResult thisValue) { throw new NotSupportedException("Can only be used in expressions"); } + public static TResult MappingColumn(TResult type, string newColumnName) { throw new NotSupportedException("Can only be used in expressions"); } + public static TResult MappingColumn(string newColumnName) { throw new NotSupportedException("Can only be used in expressions"); } + /// + ///Example: new NewT(){name=SqlFunc.GetSelfAndAutoFill(it)} Generated SQL it.* + /// + /// + /// + /// + public static TResult GetSelfAndAutoFill(TResult value) { throw new NotSupportedException("Can only be used in expressions"); } + public static DateTime GetDate() { throw new NotSupportedException("Can only be used in expressions"); } + public static string GetRandom() { throw new NotSupportedException("Can only be used in expressions"); } + + + public static T Abs(T value) { throw new NotSupportedException("Can only be used in expressions"); } + public static T Round(T value, int precision) { throw new NotSupportedException("Can only be used in expressions"); } + + /// + /// Subquery + /// + /// + /// + public static Subqueryable Subqueryable() where T : class, new() { throw new NotSupportedException("Can only be used in expressions"); } + public static CaseThen IF(bool condition) { throw new NotSupportedException("Can only be used in expressions"); } + [Obsolete("多库下参数顺序不一至,为了保证多库下更好体验请使用 SqlFunc.CharIndexNew")] + public static int CharIndex(string findChar, string searchValue) { throw new NotSupportedException("Can only be used in expressions"); } + public static int CharIndexNew(string stringValue, string charValue) { throw new NotSupportedException("Can only be used in expressions"); } + public static int BitwiseAnd(int left, int right) { throw new NotSupportedException("Can only be used in expressions"); } + public static int BitwiseInclusiveOR(int left, int right) { throw new NotSupportedException("Can only be used in expressions"); } + public static int BitwiseAnd(long left, long right) { throw new NotSupportedException("Can only be used in expressions"); } + public static int BitwiseInclusiveOR(long left, long right) { throw new NotSupportedException("Can only be used in expressions"); } + public static DateTime Oracle_ToDate(string date, string format) { throw new NotSupportedException("Can only be used in expressions"); } + public static string Oracle_ToChar(DateTime date, string format) { throw new NotSupportedException("Can only be used in expressions"); } + public static string Oracle_ToChar(object objValue, string format) { throw new NotSupportedException("Can only be used in expressions"); } + public static int SqlServer_DateDiff(string dateType, DateTime date1, DateTime date2) { throw new NotSupportedException("Can only be used in expressions"); } + + public static bool JsonListObjectAny(object jsonListObject, string fieldName, object value) + { + throw new NotSupportedException("Can only be used in expressions"); + } + public static bool PgsqlArrayContains(object jsonArray, object arrayValue) + { + throw new NotSupportedException("Can only be used in expressions"); + } + public static bool JsonArrayAny(object jsonArray, object arrayValue) + { + throw new NotSupportedException("Can only be used in expressions"); + } + + public static bool SplitIn(string CommaSegmentationString, string inValue) + { + throw new NotSupportedException("Can only be used in expressions"); + } + + public static bool SplitIn(string CommaSegmentationString, string inValue, char splitChar) + { + throw new NotSupportedException("Can only be used in expressions"); + } + + public static bool ListAny(List listConstant, Expression> expression) + { + throw new NotSupportedException("Can only be used in expressions"); + } + public static bool ListAll(List listConstant, Expression> expression) + { + throw new NotSupportedException("Can only be used in expressions"); + } + + public static string OnlyInSelectConvertToString(string stringValue, MethodInfo methodInfo) + { + throw new NotSupportedException("Can only be used in expressions"); + } + public static void SelectFields(string fieldName1) + { + throw new NotSupportedException("Can only be used in expressions"); + } + public static void SelectFields(string fieldName1, string fieldName2) + { + throw new NotSupportedException("Can only be used in expressions"); + } + public static void SelectFields(string fieldName1, string fieldName2, string fieldName3) + { + throw new NotSupportedException("Can only be used in expressions"); + } + public static void SelectFields(string fieldName1, string fieldName2, string fieldName3, string fieldName4) + { + throw new NotSupportedException("Can only be used in expressions"); + } + public static void SelectFields(string fieldName1, string fieldName2, string fieldName3, string fieldName4, string fieldName5) + { + throw new NotSupportedException("Can only be used in expressions"); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/DbMethods/SqlFuncExtendsion.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/DbMethods/SqlFuncExtendsion.cs new file mode 100644 index 000000000..85f8c3a63 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/DbMethods/SqlFuncExtendsion.cs @@ -0,0 +1,24 @@ +namespace SqlSugar +{ + public static class SqlFuncExtendsion + { + internal static List TableInfos = new List(); + public static string GetConfigValue(this object field) + { + throw new NotSupportedException("Can only be used in expressions"); + } + public static string GetConfigValue(this object field, string uniqueCode) + { + throw new NotSupportedException("Can only be used in expressions"); + } + + public static FieldType SelectAll(this FieldType field) + { + throw new NotSupportedException("Can only be used in expressions"); + } + public static FieldType SelectAll(this FieldType field, string singleTableQueryShortName) + { + throw new NotSupportedException("Can only be used in expressions"); + } + } +} \ No newline at end of file diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/DbMethods/SqlFuncExternal.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/DbMethods/SqlFuncExternal.cs new file mode 100644 index 000000000..59b73f514 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/DbMethods/SqlFuncExternal.cs @@ -0,0 +1,8 @@ +namespace SqlSugar +{ + public class SqlFuncExternal + { + public string UniqueMethodName { get; set; } + public Func MethodValue { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ExpressionContext.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ExpressionContext.cs new file mode 100644 index 000000000..737040363 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ExpressionContext.cs @@ -0,0 +1,266 @@ +using System.Linq.Expressions; +namespace SqlSugar +{ + public class ExpressionContext : ExpResolveAccessory + { + #region Fields + private bool _IsSingle = true; + private IDbMethods _DbMehtods { get; set; } + #endregion + + #region Properties + public virtual ExpressionContextCase Case { get; set; } + public ExpressionOutParameter SugarContext { get; set; } + public IDbMethods DbMehtods + { + get + { + if (_DbMehtods == null) + { + _DbMehtods = new DefaultDbMethod(); + } + return _DbMehtods; + } + set + { + _DbMehtods = value; + } + } + public int SubQueryIndex { get; set; } + public int JoinIndex { get; set; } + public bool IsAsAttr { get; set; } + public int Index { get; set; } + public int ParameterIndex { get; set; } + public string SingleTableNameSubqueryShortName { get; set; } + public string CurrentShortName { get; set; } + public MappingColumnList MappingColumns { get; set; } + public MappingTableList MappingTables { get; set; } + public IgnoreColumnList IgnoreComumnList { get; set; } + public bool PgSqlIsAutoToLower { get; set; } + public bool? TableEnumIsString { get; set; } + public List SqlFuncServices { get; set; } + public Expression RootExpression { get; set; } + public bool IsSingle + { + get + { + return _IsSingle; + } + set + { + _IsSingle = value; + } + } + public bool IsJoin + { + get + { + return !IsSingle; + } + } + public List JoinQueryInfos { get; set; } + public ResolveExpressType ResolveType { get; set; } + public Expression Expression { get; set; } + public ExpressionResult Result + { + get + { + if (base._Result == null) + { + this.Result = new ExpressionResult(this.ResolveType); + } + return base._Result; + } + set + { + this._Result = value; + } + } + public List Parameters + { + get + { + if (base._Parameters == null) + base._Parameters = new List(); + return base._Parameters; + } + set + { + base._Parameters = value; + } + } + public virtual string SqlParameterKeyWord + { + get + { + return "@"; + } + } + public virtual string SqlTranslationLeft { get { return "["; } } + public virtual string SqlTranslationRight { get { return "]"; } } + public virtual Action InitMappingInfo { get; set; } + public virtual Action RefreshMapping { get; set; } + public virtual Type SubTableType { get; set; } + public string MethodName { get; set; } + #endregion + + #region Core methods + public void Resolve(Expression expression, ResolveExpressType resolveType) + { + this.ResolveType = resolveType; + this.Expression = expression; + BaseResolve resolve = new BaseResolve(new ExpressionParameter() { CurrentExpression = this.Expression, Context = this }); + resolve.Start(); + } + public void Clear() + { + base._Result = null; + base._Parameters = new List(); + } + public ExpressionContext GetCopyContext() + { + ExpressionContext copyContext = (ExpressionContext)Activator.CreateInstance(this.GetType(), true); + copyContext.Index = this.Index; + copyContext.InitMappingInfo = this.InitMappingInfo; + copyContext.RefreshMapping = this.RefreshMapping; + copyContext.ParameterIndex = this.ParameterIndex; + copyContext.PgSqlIsAutoToLower = this.PgSqlIsAutoToLower; + copyContext.IsSingle = this.IsSingle; + copyContext.RootExpression = this.RootExpression; + copyContext.TableEnumIsString = this.TableEnumIsString; + copyContext.SugarContext = this.SugarContext; + return copyContext; + } + public ExpressionContext GetCopyContextWithMapping() + { + ExpressionContext copyContext = (ExpressionContext)Activator.CreateInstance(this.GetType(), true); + copyContext.Index = this.Index; + copyContext.ParameterIndex = this.ParameterIndex; + copyContext.MappingColumns = this.MappingColumns; + copyContext.MappingTables = this.MappingTables; + copyContext.IgnoreComumnList = this.IgnoreComumnList; + copyContext.SqlFuncServices = this.SqlFuncServices; + copyContext.InitMappingInfo = this.InitMappingInfo; + copyContext.RefreshMapping = this.RefreshMapping; + copyContext.PgSqlIsAutoToLower = this.PgSqlIsAutoToLower; + copyContext.TableEnumIsString = this.TableEnumIsString; + copyContext.IsSingle = this.IsSingle; + copyContext.RootExpression = this.RootExpression; + copyContext.SugarContext = this.SugarContext; + return copyContext; + } + #endregion + + #region Override methods + public virtual string GetLimit() { return null; } + public virtual string GetTranslationTableName(string entityName, bool isMapping = true) + { + Check.ArgumentNullException(entityName, string.Format(null, ErrorMessage.ObjNotExistCompositeFormat, "Table Name")); + if (IsTranslationText(entityName)) return entityName; + isMapping = isMapping && this.MappingTables.HasValue(); + var isComplex = entityName.Contains(UtilConstants.Dot); + if (isMapping && isComplex) + { + var columnInfo = entityName.Split(UtilConstants.DotChar); + var mappingInfo = this.MappingTables.FirstOrDefault(it => it.EntityName.Equals(columnInfo.Last(), StringComparison.CurrentCultureIgnoreCase)); + if (mappingInfo != null) + { + columnInfo[columnInfo.Length - 1] = mappingInfo.EntityName; + } + return string.Join(UtilConstants.Dot, columnInfo.Select(it => GetTranslationText(it))); + } + else if (isMapping) + { + var mappingInfo = this.MappingTables.FirstOrDefault(it => it.EntityName.Equals(entityName, StringComparison.CurrentCultureIgnoreCase)); + var name = (mappingInfo == null ? entityName : mappingInfo.DbTableName); + if (name.Contains(SqlTranslationLeft) && name.Contains(SqlTranslationRight)) + { + return name; + } + else if (name.Contains('.')) + { + return string.Join(".", name.Split('.').Select(it => SqlTranslationLeft + it + SqlTranslationRight)); + } + else + { + return SqlTranslationLeft + name + SqlTranslationRight; + } + } + else if (isComplex) + { + return string.Join(UtilConstants.Dot, entityName.Split(UtilConstants.DotChar).Select(it => GetTranslationText(it))); + } + else + { + return GetTranslationText(entityName); + } + } + public virtual string GetTranslationColumnName(string columnName) + { + Check.ArgumentNullException(columnName, string.Format(null, ErrorMessage.ObjNotExistCompositeFormat, "Column Name")); + if (columnName.Substring(0, 1) == this.SqlParameterKeyWord || columnName.Substring(0, 1) == "@") + { + return columnName; + } + if (this.SugarContext?.Context?.CurrentConnectionConfig?.MoreSettings?.IsCorrectErrorSqlParameterName == true) + { + if (IsTranslationText(columnName.Replace(" ", ""))) return columnName; + else + return GetTranslationText(columnName); + } + if (IsTranslationText(columnName)) return columnName; + if (columnName.Contains(UtilConstants.Dot)) + { + return string.Join(UtilConstants.Dot, columnName.Split(UtilConstants.DotChar).Select(it => GetTranslationText(it))); + } + else + { + return GetTranslationText(columnName); + } + } + public virtual string GetDbColumnName(string entityName, string propertyName) + { + if (this.MappingColumns.HasValue()) + { + var mappingInfo = this.MappingColumns.SingleOrDefault(it => it.EntityName == entityName && it.PropertyName == propertyName); + return mappingInfo == null ? propertyName : mappingInfo.DbColumnName; + } + else + { + return propertyName; + } + } + public virtual bool IsTranslationText(string name) + { + var result = name.IsContainsIn(SqlTranslationLeft, SqlTranslationRight, UtilConstants.Space, ExpressionConst.LeftParenthesis, ExpressionConst.RightParenthesis); + return result; + } + public virtual string GetTranslationText(string name) + { + return SqlTranslationLeft + name + SqlTranslationRight; + } + public virtual string GetAsString(string asName, string fieldValue) + { + if (fieldValue.Contains(".*") || fieldValue == "*") return fieldValue; + return string.Format(" {0} {1} {2} ", GetTranslationColumnName(fieldValue), "AS", GetTranslationColumnName(asName)); + } + + public virtual string GetAsString2(string asName, string fieldValue) + { + if (fieldValue.Contains(".*") || fieldValue == "*") return fieldValue; + return string.Format(" {0} {1} {2} ", fieldValue, "AS", GetTranslationColumnName(asName)); + } + + public virtual string GetEqString(string eqName, string fieldValue) + { + return string.Format(" {0} {1} {2} ", GetTranslationColumnName(eqName), "=", GetTranslationColumnName(fieldValue)); + } + + public virtual string GetAsString(string asName, string fieldValue, string fieldShortName) + { + if (fieldValue.Contains(".*") || fieldValue == "*") return fieldValue; + return string.Format(" {0} {1} {2} ", GetTranslationColumnName(fieldShortName + "." + fieldValue), "AS", GetTranslationColumnName(asName)); + } + #endregion + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/BaseResolve.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/BaseResolve.cs new file mode 100644 index 000000000..8cdb73c1f --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/BaseResolve.cs @@ -0,0 +1,97 @@ +using System.Linq.Expressions; +namespace SqlSugar +{ + /// + /// BaseResolve-Append + /// + public partial class BaseResolve + { + + private BaseResolve() + { + + } + public BaseResolve(ExpressionParameter parameter) + { + this.Expression = parameter.CurrentExpression; + this.Context = parameter.Context; + this.BaseParameter = parameter; + } + + public BaseResolve Start() + { + Expression expression; + ExpressionParameter parameter; + SetParameter(out expression, out parameter); + if (expression is LambdaExpression) + { + return new LambdaExpressionResolve(parameter); + } + else if (expression is BinaryExpression && expression.NodeType == ExpressionType.Coalesce) + { + return new CoalesceResolveItems(parameter); + } + else if (expression is BinaryExpression) + { + return new BinaryExpressionResolve(parameter); + } + else if (expression is BlockExpression) + { + Check.ThrowNotSupportedException("BlockExpression"); + } + else if (expression is ConditionalExpression) + { + return new ConditionalExpressionResolve(parameter); + } + else if (expression is MethodCallExpression) + { + return new MethodCallExpressionResolve(parameter); + } + else if (expression is MemberExpression && ((MemberExpression)expression).Expression == null) + { + return new MemberNoExpressionResolve(parameter); + } + else if (expression is MemberExpression && ((MemberExpression)expression).Expression.NodeType == ExpressionType.Constant) + { + return new MemberConstExpressionResolve(parameter); + } + else if (expression is MemberExpression && ((MemberExpression)expression).Expression.NodeType == ExpressionType.New) + { + return new MemberNewExpressionResolve(parameter); + } + else if (expression is ConstantExpression) + { + return new ConstantExpressionResolve(parameter); + } + else if (expression is MemberExpression) + { + return new MemberExpressionResolve(parameter); + } + else if (expression is UnaryExpression) + { + return new UnaryExpressionResolve(parameter); + } + else if (expression is MemberInitExpression) + { + return new MemberInitExpressionResolve(parameter); + } + else if (expression is NewExpression) + { + return new NewExpressionResolve(parameter); + } + else if (expression is NewArrayExpression) + { + return new NewArrayExpessionResolve(parameter); + } + else if (expression is ParameterExpression) + { + return new TypeParameterExpressionReolve(parameter); + } + else if (expression?.NodeType.IsIn(ExpressionType.NewArrayBounds) == true) + { + Check.ThrowNotSupportedException("ExpressionType.NewArrayBounds"); + } + return null; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/BaseResolve_Append.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/BaseResolve_Append.cs new file mode 100644 index 000000000..2bd080fd8 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/BaseResolve_Append.cs @@ -0,0 +1,296 @@ +using System.Linq.Expressions; +using System.Reflection; +namespace SqlSugar +{ + /// + /// BaseResolve-Append + /// + public partial class BaseResolve + { + protected void AppendMember(ExpressionParameter parameter, bool? isLeft, object appendValue) + { + + Context.ParameterIndex++; + if (isLeft == true) + { + appendValue += ExpressionConst.ExpressionReplace + parameter.BaseParameter.Index; + } + if (this.Context.Result.Contains(ExpressionConst.FormatSymbol)) + { + this.Context.Result.Replace(ExpressionConst.FormatSymbol, appendValue.ObjToString()); + } + else + { + this.Context.Result.Append(appendValue); + } + } + protected void AppendValue(ExpressionParameter parameter, bool? isLeft, object value) + { + if (parameter.BaseExpression is BinaryExpression || parameter.BaseExpression == null) + { + var oppoSiteExpression = isLeft == true ? parameter.BaseParameter.RightExpression : parameter.BaseParameter.LeftExpression; + + if (value is MapperSql) + { + ApppendMapperSql(parameter, isLeft, value); + } + else if (parameter.CurrentExpression is MethodCallExpression || parameter.CurrentExpression is ConditionalExpression || parameter.CurrentExpression.NodeType == ExpressionType.Coalesce) + { + AppendMethod(parameter, isLeft, value); + } + else if (oppoSiteExpression is MemberExpression) + { + AppendMember(parameter, isLeft, value, oppoSiteExpression); + } + else if (ExpressionTool.RemoveConvert(oppoSiteExpression) is MemberExpression) + { + AppendMember(parameter, isLeft, value, ExpressionTool.RemoveConvert(oppoSiteExpression)); + } + else if ((oppoSiteExpression is UnaryExpression && (oppoSiteExpression as UnaryExpression).Operand is MemberExpression)) + { + value = AppendUnaryExp(parameter, isLeft, value, oppoSiteExpression); + } + else + { + value = AppendOther(parameter, isLeft, value); + } + } + } + private object AppendOther(ExpressionParameter parameter, bool? isLeft, object value) + { + var appendValue = this.Context.SqlParameterKeyWord + ExpressionConst.Const + Context.ParameterIndex; + Context.ParameterIndex++; + if (value?.GetType().IsEnum() == true) + { + if (this.Context.TableEnumIsString == true) + { + value = value.ToString(); + } + else + { + value = Convert.ToInt64(value); + } + } + this.Context.Parameters.Add(new SugarParameter(appendValue, value)); + appendValue = string.Format(" {0} ", appendValue); + if (isLeft == true) + { + appendValue += ExpressionConst.ExpressionReplace + parameter.BaseParameter.Index; + } + if (this.Context.Result.Contains(ExpressionConst.FormatSymbol)) + { + this.Context.Result.Replace(ExpressionConst.FormatSymbol, appendValue); + } + else + { + this.Context.Result.Append(appendValue); + } + + return value; + } + private object AppendUnaryExp(ExpressionParameter parameter, bool? isLeft, object value, Expression oppoSiteExpression) + { + string appendValue = Context.SqlParameterKeyWord + + ((MemberExpression)(oppoSiteExpression as UnaryExpression).Operand).Member.Name + + Context.ParameterIndex; + if (value.ObjToString() != "NULL" && !parameter.ValueIsNull) + { + value = this.Context.TableEnumIsString == true ? value.ToString() : value; + this.Context.Parameters.Add(new SugarParameter(appendValue, value)); + } + else + { + appendValue = value.ObjToString(); + } + Context.ParameterIndex++; + appendValue = string.Format(" {0} ", appendValue); + if (isLeft == true) + { + appendValue += ExpressionConst.ExpressionReplace + parameter.BaseParameter.Index; + } + if (this.Context.Result.Contains(ExpressionConst.FormatSymbol)) + { + this.Context.Result.Replace(ExpressionConst.FormatSymbol, appendValue); + } + else + { + this.Context.Result.Append(appendValue); + } + + return value; + } + private void AppendMember(ExpressionParameter parameter, bool? isLeft, object value, Expression oppoSiteExpression) + { + string appendValue = Context.SqlParameterKeyWord + + ((MemberExpression)oppoSiteExpression).Member.Name + + Context.ParameterIndex; + if (IsNullValue(parameter, value)) + { + appendValue = $" NULL "; + parameter.BaseParameter.ValueIsNull = true; + } + else if (value.ObjToString() != "NULL" && !parameter.ValueIsNull) + { + EntityColumnInfo columnInfo = GetColumnInfo(oppoSiteExpression); + if (columnInfo?.SqlParameterDbType != null && columnInfo.SqlParameterDbType is System.Data.DbType) + { + var p = new SugarParameter(appendValue, value, (System.Data.DbType)columnInfo.SqlParameterDbType); + if (columnInfo.SqlParameterSize != null) + { + p.Size = columnInfo.SqlParameterSize.ObjToInt(); + } + this.Context.Parameters.Add(p); + } + else if (UtilMethods.IsParameterConverter(columnInfo)) + { + SugarParameter p = UtilMethods.GetParameterConverter(this.Context.ParameterIndex, this.Context.SugarContext.Context, value, oppoSiteExpression, columnInfo); + appendValue = p.ParameterName; + this.Context.Parameters.Add(p); + } + else if (parameter?.BaseParameter?.CommonTempData.ObjToString() == "IsJson=true") + { + this.Context.Parameters.Add(new SugarParameter(appendValue, new SerializeService().SerializeObject(value)) { IsJson = true }); + } + else if (parameter?.BaseParameter?.CommonTempData.ObjToString() == "IsArray=true") + { + this.Context.Parameters.Add(new SugarParameter(appendValue, value) { IsArray = true }); + } + else if (value != null && (value is Enum) && this.Context?.SugarContext?.Context?.CurrentConnectionConfig?.MoreSettings?.TableEnumIsString == true) + { + this.Context.Parameters.Add(new SugarParameter(appendValue, Convert.ToString(value))); + } + else if (this.Context + ?.SugarContext + ?.Context + ?.CurrentConnectionConfig + ?.MoreSettings + ?.IsCorrectErrorSqlParameterName == true + && columnInfo?.PropertyName != null + && !columnInfo.PropertyName.IsRegexWNoContainsChinese()) + { + appendValue = (Context.SqlParameterKeyWord + "p" + appendValue.GetHashCode() + "no" + Context.ParameterIndex) + .Replace("-", ""); + this.Context.Parameters.Add(new SugarParameter(appendValue, value)); + } + else + { + var p = new SugarParameter(appendValue, value); + if (p.DbType == System.Data.DbType.String && this.Context?.SugarContext?.Context?.CurrentConnectionConfig?.MoreSettings?.DisableNvarchar == true) + { + p.DbType = System.Data.DbType.AnsiString; + } + this.Context.Parameters.Add(p); + } + } + else + { + appendValue = value.ObjToString(); + } + Context.ParameterIndex++; + appendValue = string.Format(" {0} ", appendValue); + if (isLeft == true) + { + appendValue += ExpressionConst.ExpressionReplace + parameter.BaseParameter.Index; + } + if (this.Context.Result.Contains(ExpressionConst.FormatSymbol)) + { + this.Context.Result.Replace(ExpressionConst.FormatSymbol, appendValue); + } + else + { + this.Context.Result.Append(appendValue); + } + } + private void AppendMethod(ExpressionParameter parameter, bool? isLeft, object value) + { + var appendValue = value; + if (this.Context.Result.Contains(ExpressionConst.FormatSymbol)) + { + this.Context.Result.Replace(ExpressionConst.FormatSymbol, appendValue.ObjToString()); + } + else + { + this.Context.Result.Append(appendValue); + } + this.AppendOpreator(parameter, isLeft); + } + private void ApppendMapperSql(ExpressionParameter parameter, bool? isLeft, object value) + { + var sql = ((MapperSql)value).Sql; + if (isLeft == true) + { + sql += ExpressionConst.ExpressionReplace + parameter.BaseParameter.Index; + } + if (this.Context.Result.Contains(ExpressionConst.FormatSymbol)) + { + this.Context.Result.Replace(ExpressionConst.FormatSymbol, sql); + } + else + { + this.Context.Result.Append(sql); + } + } + protected void AppendOpreator(ExpressionParameter parameter, bool? isLeft) + { + if (isLeft == true) + { + this.Context.Result.Append(" " + ExpressionConst.ExpressionReplace + parameter.BaseParameter.Index); + } + } + protected string AppendParameter(object paramterValue) + { + string parameterName = this.Context.SqlParameterKeyWord + "constant" + this.Context.ParameterIndex; + this.Context.ParameterIndex++; ; + this.Context.Parameters.Add(new SugarParameter(parameterName, paramterValue)); + return parameterName; + } + protected string AppendParameter(SugarParameter p) + { + p.ParameterName = p.ParameterName + this.Context.ParameterIndex; + this.Context.ParameterIndex++; ; + this.Context.Parameters.Add(p); + return p.ParameterName; + } + protected void AppendNot(object Value) + { + var isAppend = !this.Context.Result.Contains(ExpressionConst.FormatSymbol); + var lastCharIsSpace = this.Context.Result.LastCharIsSpace; + if (isAppend) + { + this.Context.Result.Append(lastCharIsSpace ? "NOT" : " NOT"); + } + else + { + this.Context.Result.Replace(ExpressionConst.FormatSymbol, "NOT"); + } + } + protected void AppendNegate(object Value) + { + var isAppend = !this.Context.Result.Contains(ExpressionConst.FormatSymbol); + var lastCharIsSpace = this.Context.Result.LastCharIsSpace; + if (isAppend) + { + this.Context.Result.Append(lastCharIsSpace ? "-" : " -"); + } + else + { + this.Context.Result.Replace(ExpressionConst.FormatSymbol, "-"); + } + } + private void AppendOnlyInSelectConvertToString(ExpressionParameter parameter, Expression item, string asName) + { + var name = GetNewExpressionValue((item as MethodCallExpression)?.Arguments[0]); + var methodInfo = ExpressionTool.DynamicInvoke(((item as MethodCallExpression)?.Arguments[1])); + if (this.Context.SugarContext.QueryBuilder.QueryableFormats == null) + this.Context.SugarContext.QueryBuilder.QueryableFormats = new List(); + this.Context.SugarContext.QueryBuilder.QueryableFormats.Add(new QueryableFormat() + { + Format = "", + PropertyName = asName, + MethodName = "OnlyInSelectConvertToString", + MethodInfo = (MethodInfo)methodInfo + }); + parameter.Context.Result.Append(this.Context.GetAsString2(asName, name)); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/BaseResolve_Helper.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/BaseResolve_Helper.cs new file mode 100644 index 000000000..1d280804c --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/BaseResolve_Helper.cs @@ -0,0 +1,207 @@ +using System.Linq.Expressions; +using System.Reflection; +namespace SqlSugar +{ + /// + /// BaseResolve-Helper + /// + public partial class BaseResolve + { + #region Set Method + protected void SetNavigateResult() + { + if (this.Context != null) + { + if (this.Context.Result != null) + { + this.Context.Result.IsNavicate = true; + } + } + } + private void SetParameter(out Expression expression, out ExpressionParameter parameter) + { + Context.Index++; + expression = this.Expression; + parameter = new ExpressionParameter() + { + Context = this.Context, + CurrentExpression = expression, + IsLeft = this.IsLeft, + BaseExpression = this.ExactExpression, + BaseParameter = this.BaseParameter, + Index = Context.Index + }; + } + + #endregion + + #region Get Mehtod + protected object GetMemberValue(object value, Expression exp) + { + if (exp is MemberExpression) + { + var member = (exp as MemberExpression); + var memberParent = member.Expression; + if (memberParent != null && this.Context?.SugarContext?.Context != null) + { + var entity = this.Context.SugarContext.Context.EntityMaintenance.GetEntityInfo(memberParent.Type); + var columnInfo = entity.Columns.FirstOrDefault(it => it.PropertyName == member.Member.Name); + if (columnInfo?.SqlParameterDbType is Type) + { + var type = columnInfo.SqlParameterDbType as Type; + var ParameterConverter = type.GetMethod("ParameterConverter").MakeGenericMethod(columnInfo.PropertyInfo.PropertyType); + var obj = Activator.CreateInstance(type); + var p = ParameterConverter.Invoke(obj, new object[] { value, 100 + this.Context.ParameterIndex }) as SugarParameter; + value = p.Value; + } + } + } + + return value; + } + private string GetAsName(Expression item, object shortName, PropertyInfo property) + { + string asName; + var propertyName = property.Name; + var dbColumnName = propertyName; + var mappingInfo = this.Context.MappingColumns.FirstOrDefault(it => it.EntityName == item.Type.Name && it.PropertyName.Equals(propertyName, StringComparison.CurrentCultureIgnoreCase)); + if (mappingInfo.HasValue()) + { + dbColumnName = mappingInfo.DbColumnName; + } + asName = this.Context.GetTranslationText(item.Type.Name + "." + propertyName); + if (Context.IsJoin) + { + this.Context.Result.Append(Context.GetAsString(asName, dbColumnName, shortName.ObjToString())); + } + else + { + this.Context.Result.Append(Context.GetAsString(asName, dbColumnName)); + } + + return asName; + } + private string GetAsNameAndShortName(Expression item, object shortName, PropertyInfo property) + { + string asName; + var propertyName = property.Name; + var dbColumnName = propertyName; + var mappingInfo = this.Context.MappingColumns.FirstOrDefault(it => it.EntityName == item.Type.Name && it.PropertyName.Equals(propertyName, StringComparison.CurrentCultureIgnoreCase)); + if (mappingInfo.HasValue()) + { + dbColumnName = mappingInfo.DbColumnName; + } + if (shortName?.ObjToString().Contains(this.Context.SqlTranslationLeft) == true && this.Context.IsSingle) + { + asName = this.Context.GetTranslationText(item.Type.Name + "." + propertyName); + } + else + { + asName = this.Context.GetTranslationText(shortName + "." + item.Type.Name + "." + propertyName); + } + if (Context.IsJoin) + { + this.Context.Result.Append(Context.GetAsString(asName, dbColumnName, shortName.ObjToString())); + } + else + { + this.Context.Result.Append(Context.GetAsString(asName, dbColumnName)); + } + + return asName; + } + private EntityColumnInfo GetColumnInfo(Expression oppoSiteExpression) + { + var oppsite = (oppoSiteExpression as MemberExpression); + if (oppsite == null) return null; + if (this.Context.SugarContext == null) return null; + if (this.Context.SugarContext.Context == null) return null; + if (oppsite.Expression == null) return null; + var columnInfo = this.Context.SugarContext.Context.EntityMaintenance + .GetEntityInfo(oppsite.Expression.Type).Columns.FirstOrDefault(it => it.PropertyName == oppsite.Member.Name); + return columnInfo; + } + protected MethodCallExpressionArgs GetMethodCallArgs(ExpressionParameter parameter, Expression item, string name = null) + { + var newContext = this.Context.GetCopyContext(); + newContext.MappingColumns = this.Context.MappingColumns; + newContext.MappingTables = this.Context.MappingTables; + newContext.IgnoreComumnList = this.Context.IgnoreComumnList; + newContext.IsSingle = this.Context.IsSingle; + newContext.SqlFuncServices = this.Context.SqlFuncServices; + newContext.MethodName = name; + newContext.Resolve(item, this.Context.IsJoin ? ResolveExpressType.WhereMultiple : ResolveExpressType.WhereSingle); + this.Context.Index = newContext.Index; + this.Context.ParameterIndex = newContext.ParameterIndex; + if (newContext.Parameters.HasValue()) + { + this.Context.Parameters.AddRange(newContext.Parameters); + } + if (newContext.SingleTableNameSubqueryShortName.HasValue()) + { + this.Context.SingleTableNameSubqueryShortName = newContext.SingleTableNameSubqueryShortName; + } + var methodCallExpressionArgs = new MethodCallExpressionArgs() + { + IsMember = true, + MemberName = newContext.Result.GetResultString() + }; + return methodCallExpressionArgs; + } + private string GetAsNameResolveAnObject(ExpressionParameter parameter, Expression item, string asName, bool isSameType) + { + this.Start(); + var shortName = parameter.CommonTempData; + var listProperties = item.Type.GetProperties().Cast().ToList(); + foreach (var property in listProperties) + { + var hasIgnore = this.Context.IgnoreComumnList?.Any(it => it.EntityName.Equals(item.Type.Name, StringComparison.CurrentCultureIgnoreCase) && it.PropertyName.Equals(property.Name, StringComparison.CurrentCultureIgnoreCase)) == true; + if (hasIgnore) + { + continue; + } + if (property.PropertyType.IsClass()) + { + var comumnInfo = property.GetCustomAttribute(); + if (comumnInfo?.IsJson == true && isSameType) + { + asName = GetAsNameAndShortName(item, shortName, property); + } + else if (comumnInfo?.IsJson == true) + { + asName = GetAsName(item, shortName, property); + } + else if (comumnInfo != null && this.Context.SugarContext?.Context != null) + { + var entityInfo = this.Context.SugarContext.Context.EntityMaintenance.GetEntityInfo(item.Type); + var entityColumn = entityInfo.Columns.FirstOrDefault(it => it.PropertyName == property.Name); + if (entityColumn?.IsJson == true) + { + asName = GetAsName(item, shortName, property); + } + } + } + else if (isSameType) + { + asName = GetAsNameAndShortName(item, shortName, property); + } + else + { + asName = GetAsName(item, shortName, property); + } + } + + return asName; + } + public object GetAsNamePackIfElse(object methodValue) + { + methodValue = this.Context.DbMehtods.CaseWhen(new List>() { + new KeyValuePair("IF",methodValue.ObjToString()), + new KeyValuePair("Return","1"), + new KeyValuePair("End","0") + }); + return methodValue; + } + #endregion + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/BaseResolve_Item.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/BaseResolve_Item.cs new file mode 100644 index 000000000..f88443126 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/BaseResolve_Item.cs @@ -0,0 +1,471 @@ +using System.Linq.Expressions; +namespace SqlSugar +{ + /// + ///BaseResolve New Expression + /// + public partial class BaseResolve + { + private void ResloveOtherMUC(ExpressionParameter parameter, Expression item, string asName) + { + if (ExpressionTool.GetMethodName(item) == "NewGuid") + { + parameter.Context.Result.Append(this.Context.GetAsString2(asName, this.Context.DbMehtods.NewUid(null))); + return; + } + else if (ExpressionTool.GetMethodName(item) == "OnlyInSelectConvertToString") + { + AppendOnlyInSelectConvertToString(parameter, item, asName); + return; + } + else if (ExpressionTool.GetMethodName(item) == "ToString" + && (item as MethodCallExpression)?.Arguments?.Count == 1 + && (item as MethodCallExpression)?.Object?.Type != UtilConstants.DateType + && this.Context?.SugarContext?.QueryBuilder != null + && (item as MethodCallExpression)?.Method?.ReflectedType?.Name != "SqlFunc" + && (item as MethodCallExpression)?.Method?.ReflectedType?.Name != "Convert" + ) + { + var format = ExpressionTool.GetExpressionValue((item as MethodCallExpression)?.Arguments[0]); + var childExpression = (item as MethodCallExpression)?.Object; + var type = childExpression.Type; + if (this.Context.SugarContext.QueryBuilder.QueryableFormats == null) + { + this.Context.SugarContext.QueryBuilder.QueryableFormats = new List(); + } + this.Context.SugarContext.QueryBuilder.QueryableFormats.Add(new QueryableFormat() + { + Format = format + "", + PropertyName = asName, + Type = type, + TypeString = type.FullName, + MethodName = "ToString" + }); + parameter.Context.Result.Append(this.Context.GetAsString2(asName, GetNewExpressionValue(childExpression))); + return; + } + else if (ExpressionTool.GetMethodName(item) == "ToString" + && (item as MethodCallExpression)?.Arguments?.Count == 0 + && (item as MethodCallExpression)?.Object?.Type?.IsEnum == true + && this.Context?.SugarContext?.QueryBuilder != null) + { + var childExpression = (item as MethodCallExpression)?.Object; + var type = childExpression.Type; + if (this.Context.SugarContext.QueryBuilder.QueryableFormats == null) + { + this.Context.SugarContext.QueryBuilder.QueryableFormats = new List(); + } + this.Context.SugarContext.QueryBuilder.QueryableFormats.Add(new QueryableFormat() + { + PropertyName = asName, + Type = type, + TypeString = "Enum", + MethodName = "ToString" + }); + parameter.Context.Result.Append(this.Context.GetAsString2(asName, GetNewExpressionValue(childExpression))); + return; + } + else if (ExpressionTool.GetMethodName(item) == "IsNull" + && this.Context.SingleTableNameSubqueryShortName == null + && this.BaseParameter?.CurrentExpression is NewExpression + && (item as MethodCallExpression)?.Arguments?.FirstOrDefault() is MethodCallExpression + && item?.ToString()?.Contains("Join") == true + && ExpressionTool.GetParameters(this.BaseParameter?.CurrentExpression).Count > 1) + { + var ps = ExpressionTool.GetParameters(this.BaseParameter?.CurrentExpression); + this.Expression = item; + this.Start(); + parameter.Context.Result.Append(this.Context.GetAsString2(asName, parameter.CommonTempData.ObjToString())); + this.Context.SingleTableNameSubqueryShortName = ps.FirstOrDefault().Name; + return; + } + else if (item is MethodCallExpression && ExpressionTool.IsVariable(item)) + { + var p = GetNewExpressionValue(item); + parameter.Context.Result.Append(this.Context.GetAsString2(asName, p)); + return; + } + else if (item is ConditionalExpression && ExpressionTool.GetParameters(item).Count > 0) + { + var p = GetNewExpressionValue(item); + parameter.Context.Result.Append(this.Context.GetAsString2(asName, p)); + return; + } + this.Expression = item; + var negateString = string.Empty; + if (item.NodeType == ExpressionType.Negate) + { + negateString = " -1*"; + this.Expression = (this.Expression as UnaryExpression).Operand; + } + this.Start(); + if (ExpressionTool.GetMethodName(item) == "MappingColumn") + { + parameter.Context.Result.Append(negateString + this.Context.GetAsString2(asName, parameter.CommonTempData.ObjToString())); + } + else if (parameter.CommonTempData?.Equals(CommonTempDataType.Append) == true) + { + if (item.NodeType == ExpressionType.Negate) + { + negateString = "*-1 "; + } + else + { + negateString = null; + } + parameter.Context.Result.TrimEnd(); + parameter.Context.Result.Append(negateString + " AS " + this.Context.GetTranslationColumnName(asName)); + } + else + { + parameter.Context.Result.Append(negateString + this.Context.GetAsString2(asName, parameter.CommonTempData.ObjToString())); + } + } + + private void ResloveCountAny(ExpressionParameter parameter, Expression item, string asName) + { + if (this.Context.IsSingle && this.Context.SingleTableNameSubqueryShortName == null) + { + this.Context.SingleTableNameSubqueryShortName = item.ToString().Split('.').First(); + } + parameter.Context.Result.Append(this.Context.GetAsString(asName, GetNewExpressionValue(item))); + } + + private void ResloveNot(ExpressionParameter parameter, Expression item, string asName) + { + var asValue = GetAsNamePackIfElse(GetNewExpressionValue(item)).ObjToString(); + parameter.Context.Result.Append(this.Context.GetAsString(asName, asValue)); + } + + private void ResloveBoolMethod(ExpressionParameter parameter, Expression item, string asName) + { + this.Expression = item; + if (ExpressionTool.GetMethodName(item) == "Any" && !ExpressionTool.GetTopLevelMethodCalls(item).Contains("Subqueryable")) + { + parameter.CommonTempData = GetNewExpressionValue(item); + } + else if (ExpressionTool.GetMethodName(item) == "All" && !ExpressionTool.GetTopLevelMethodCalls(item).Contains("Subqueryable")) + { + parameter.CommonTempData = GetNewExpressionValue(item); + } + else + { + this.Start(); + } + var sql = this.Context.DbMehtods.IIF(new MethodCallExpressionModel() + { + Args = new List() { + new MethodCallExpressionArgs() { + IsMember=true, + MemberName=parameter.CommonTempData.ObjToString() + }, + new MethodCallExpressionArgs() { + IsMember=true, + MemberName=1 + }, + new MethodCallExpressionArgs() { + IsMember=true, + MemberName=0 + } + } + }); + parameter.Context.Result.Append(this.Context.GetAsString(asName, sql)); + } + + private string ResolveClass(ExpressionParameter parameter, Expression item, string asName) + { + var mappingKeys = GetMappingColumns(parameter.CurrentExpression); + var isSameType = mappingKeys.Keys.Count > 0; + this.Context.SugarContext.QueryBuilder.MappingKeys = mappingKeys; + this.Expression = item; + if (this.Context.IsJoin && (item is MemberInitExpression || item is NewExpression)) + { + List newExpressionInfos = new List(); + if (item is MemberInitExpression) + { + newExpressionInfos = ExpressionTool.GetNewexpressionInfos(item, this.Context, this); + var ignorePropertyNames = item.Type.GetProperties().Where(it => it.PropertyType.IsClass() && !it.PropertyType.Name.StartsWith("System.")) + .Select(it => it.PropertyType.Name).ToList(); + if (ignorePropertyNames.Count > 0) + { + var names = new List() { }; + foreach (MemberBinding binding in ((MemberInitExpression)item).Bindings) + { + names.Add(binding.Member.Name); + } + ignorePropertyNames = ignorePropertyNames.Where(it => !names.Contains(it)).ToList(); + var q = this.Context?.SugarContext?.QueryBuilder; + if (q != null) + { + foreach (var ignorePropertyName in ignorePropertyNames) + { + if (q.SelectNewIgnoreColumns == null) + { + q.SelectNewIgnoreColumns = new List>(); + } + var addItem = new KeyValuePair(ignorePropertyName, item.Type.Name); + q.SelectNewIgnoreColumns.Add(addItem); + } + } + } + } + else + { + newExpressionInfos = ExpressionTool.GetNewDynamicexpressionInfos(item, this.Context, this); + } + foreach (NewExpressionInfo newExpressionInfo in newExpressionInfos) + { + //var property=item.Type.GetProperties().Where(it => it.Name == newExpressionInfo.l).First(); + //asName = GetAsName(item, newExpressionInfo.ShortName, property); + if (newExpressionInfo.Type == nameof(ConstantExpression)) + { + parameter.Context.Result.Append( + newExpressionInfo.RightDbName + " AS " + + this.Context.SqlTranslationLeft + asName + "." + newExpressionInfo.LeftNameName + this.Context.SqlTranslationRight + + ); + } + else + { + parameter.Context.Result.Append(this.Context.GetAsString( + this.Context.SqlTranslationLeft + asName + "." + newExpressionInfo.LeftNameName + this.Context.SqlTranslationRight, + newExpressionInfo.ShortName + "." + newExpressionInfo.RightDbName + )); + } + } + } + else if (!this.Context.IsJoin && (item is MemberInitExpression || item is NewExpression)) + { + List newExpressionInfos = new List(); + if (item is MemberInitExpression) + { + newExpressionInfos = ExpressionTool.GetNewexpressionInfos(item, this.Context, this); + } + else + { + newExpressionInfos = ExpressionTool.GetNewDynamicexpressionInfos(item, this.Context, this); + } + //mappingKeys = new Dictionary(); + foreach (NewExpressionInfo newExpressionInfo in newExpressionInfos) + { + //var property=item.Type.GetProperties().Where(it => it.Name == newExpressionInfo.l).First(); + //asName = GetAsName(item, newExpressionInfo.ShortName, property); + mappingKeys.Add("Single_" + newExpressionInfo.LeftNameName, asName + "." + newExpressionInfo.LeftNameName); + if (newExpressionInfo.Type == nameof(ConstantExpression)) + { + this.Context.SugarContext.QueryBuilder.MappingKeys = mappingKeys; + parameter.Context.Result.Append($" {newExpressionInfo.RightDbName} AS {this.Context.SqlTranslationLeft}{asName}.{newExpressionInfo.LeftNameName}{this.Context.SqlTranslationRight} "); + } + else + { + this.Context.SugarContext.QueryBuilder.MappingKeys = mappingKeys; + parameter.Context.Result.Append(this.Context.GetAsString( + this.Context.SqlTranslationLeft + asName + "." + newExpressionInfo.LeftNameName + this.Context.SqlTranslationRight, + newExpressionInfo.RightDbName + )); + } + } + } + else if (IsExtSqlFuncObj(item)) + { + var value = GetNewExpressionValue(item); + parameter.Context.Result.Append($" {value} AS {asName} "); + } + else if (IsSubToList(item)) + { + var value = GetNewExpressionValue(item); + if (this.Context.SugarContext.QueryBuilder.SubToListParameters == null) + this.Context.SugarContext.QueryBuilder.SubToListParameters = new Dictionary(); + if (!this.Context.SugarContext.QueryBuilder.SubToListParameters.ContainsKey(asName)) + { + this.Context.SugarContext.QueryBuilder.SubToListParameters.Add(asName, value); + } + //throw new Exception("子查询ToList开发中.."); + } + else if (ExpressionTool.GetMethodName(item) == nameof(SqlFunc.MappingColumn)) + { + var value = GetNewExpressionValue(item); + parameter.Context.Result.Append($" {value} AS {asName} "); + } + else if (item is ConditionalExpression) + { + var value = GetNewExpressionValue(item); + parameter.Context.Result.Append($" {value} AS {asName} "); + } + else if (ExpressionTool.GetMethodName(item) == nameof(SqlFunc.IIF)) + { + var value = GetNewExpressionValue(item); + parameter.Context.Result.Append($" {value} AS {asName} "); + } + else + { + asName = GetAsNameResolveAnObject(parameter, item, asName, isSameType); + } + + return asName; + } + + private void ResolveBinary(Expression item, string asName) + { + if (this.Context.Result.IsLockCurrentParameter == false) + { + var newContext = this.Context.GetCopyContextWithMapping(); + var resolveExpressType = this.Context.IsSingle ? ResolveExpressType.WhereSingle : ResolveExpressType.WhereMultiple; + if (resolveExpressType == ResolveExpressType.WhereSingle && item is BinaryExpression) + { + var binaryExp = (item as BinaryExpression); + if (ExpressionTool.ContainsMethodName(binaryExp, "Subquery")) + { + resolveExpressType = ResolveExpressType.WhereMultiple; + } + if (this.Context.Expression != null && this.Context.SingleTableNameSubqueryShortName.IsNullOrEmpty()) + { + this.Context.SingleTableNameSubqueryShortName = (this.Context.Expression as LambdaExpression)?.Parameters?.FirstOrDefault()?.Name; + } + } + newContext.Resolve(item, resolveExpressType); + this.Context.Index = newContext.Index; + this.Context.ParameterIndex = newContext.ParameterIndex; + if (newContext.Parameters.HasValue()) + { + this.Context.Parameters.AddRange(newContext.Parameters); + } + if (ExpressionTool.IsEqualOrLtOrGt(item)) + { + var sql = newContext.Result.GetString(); + var pTrue = AppendParameter(true); + var pFalse = AppendParameter(false); + sql = this.Context.DbMehtods.IIF(new MethodCallExpressionModel() + { + Args = new List() + { + new MethodCallExpressionArgs(){ MemberName=sql,MemberValue=sql,IsMember=true } , + new MethodCallExpressionArgs(){ MemberName=pTrue,MemberValue=pTrue,IsMember=true }, + new MethodCallExpressionArgs(){ MemberName=pFalse,MemberValue=pFalse,IsMember=true } + } + }); + this.Context.Result.Append(this.Context.GetAsString(asName, sql)); + } + else + { + this.Context.Result.Append(this.Context.GetAsString(asName, newContext.Result.GetString())); + } + this.Context.Result.CurrentParameter = null; + if (this.Context.SingleTableNameSubqueryShortName.IsNullOrEmpty() && newContext.SingleTableNameSubqueryShortName.HasValue()) + { + this.Context.SingleTableNameSubqueryShortName = newContext.SingleTableNameSubqueryShortName; + } + } + } + + private void ResolveUnaryExpConst(ExpressionParameter parameter, Expression item, string asName) + { + if (this.Context.Result.IsLockCurrentParameter == false) + { + this.Expression = ((UnaryExpression)item).Operand; + this.Start(); + string parameterName = this.Context.SqlParameterKeyWord + "constant" + this.Context.ParameterIndex; + this.Context.ParameterIndex++; + parameter.Context.Result.Append(this.Context.GetAsString(asName, parameterName)); + this.Context.Parameters.Add(new SugarParameter(parameterName, parameter.CommonTempData)); + } + } + + private void ResolveUnaryExpMem(ExpressionParameter parameter, Expression item, string asName) + { + if (this.Context.Result.IsLockCurrentParameter == false) + { + var expression = ((UnaryExpression)item).Operand as MemberExpression; + var negateString = string.Empty; + if (item.NodeType == ExpressionType.Negate) + { + negateString = " -1*"; + } + var isDateTimeNow = ((UnaryExpression)item).Operand.ToString() == "DateTime.Now"; + if (expression.Expression == null && !isDateTimeNow) + { + this.Context.Result.CurrentParameter = parameter; + this.Context.Result.IsLockCurrentParameter = true; + parameter.IsAppendTempDate(); + this.Expression = item; + this.Start(); + parameter.IsAppendResult(); + this.Context.Result.Append(negateString + this.Context.GetAsString(asName, parameter.CommonTempData.ObjToString())); + this.Context.Result.CurrentParameter = null; + } + else if (expression.Expression is ConstantExpression || isDateTimeNow) + { + string parameterName = this.Context.SqlParameterKeyWord + "constant" + this.Context.ParameterIndex; + this.Context.ParameterIndex++; + parameter.Context.Result.Append(negateString + this.Context.GetAsString(asName, parameterName)); + this.Context.Parameters.Add(new SugarParameter(parameterName, ExpressionTool.GetMemberValue(expression.Member, expression))); + } + else + { + this.Context.Result.CurrentParameter = parameter; + this.Context.Result.IsLockCurrentParameter = true; + parameter.IsAppendTempDate(); + this.Expression = expression; + this.Start(); + parameter.IsAppendResult(); + this.Context.Result.Append(negateString + this.Context.GetAsString(asName, parameter.CommonTempData.ObjToString())); + this.Context.Result.CurrentParameter = null; + } + } + } + + private void ResolveMemberOther(ExpressionParameter parameter, Expression item, string asName) + { + if (this.Context.Result.IsLockCurrentParameter == false) + { + this.Context.Result.CurrentParameter = parameter; + this.Context.Result.IsLockCurrentParameter = true; + parameter.IsAppendTempDate(); + this.Expression = item; + if (IsBoolValue(item)) + { + this.Expression = (item as MemberExpression).Expression; + } + this.Start(); + parameter.IsAppendResult(); + var value = parameter.CommonTempData.ObjToString(); + if (this.Context?.SugarContext?.Context?.CurrentConnectionConfig?.MoreSettings?.IsCorrectErrorSqlParameterName == true) + { + value = ExpressionTool.ResolveMemberValue(this.Context, item, value); + } + this.Context.Result.Append(this.Context.GetAsString2(asName, value)); + this.Context.Result.CurrentParameter = null; + } + } + + private void ResolveMemberConst(ExpressionParameter parameter, Expression item, string asName) + { + this.Expression = item; + this.Start(); + string parameterName = this.Context.SqlParameterKeyWord + "constant" + this.Context.ParameterIndex; + this.Context.ParameterIndex++; + parameter.Context.Result.Append(this.Context.GetAsString(asName, parameterName)); + this.Context.Parameters.Add(new SugarParameter(parameterName, parameter.CommonTempData)); + } + + private void ResolveMember(ExpressionParameter parameter, Expression item, string asName) + { + var paramterValue = ExpressionTool.GetPropertyValue(item as MemberExpression); + string parameterName = this.Context.SqlParameterKeyWord + "constant" + this.Context.ParameterIndex; + this.Context.ParameterIndex++; + parameter.Context.Result.Append(this.Context.GetAsString(asName, parameterName)); + this.Context.Parameters.Add(new SugarParameter(parameterName, paramterValue)); + } + + private void ResolveConst(ExpressionParameter parameter, Expression item, string asName) + { + this.Expression = item; + this.Start(); + string parameterName = this.Context.SqlParameterKeyWord + "constant" + this.Context.ParameterIndex; + this.Context.ParameterIndex++; + parameter.Context.Result.Append(this.Context.GetAsString(asName, parameterName)); + this.Context.Parameters.Add(new SugarParameter(parameterName, parameter.CommonTempData)); + } + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/BaseResolve_NewExp.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/BaseResolve_NewExp.cs new file mode 100644 index 000000000..be1a6d272 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/BaseResolve_NewExp.cs @@ -0,0 +1,110 @@ +using System.Linq.Expressions; +namespace SqlSugar +{ + /// + ///BaseResolve New Expression + /// + public partial class BaseResolve + { + public string GetNewExpressionValue(Expression item) + { + var newContext = this.Context.GetCopyContextWithMapping(); + newContext.SugarContext = this.Context.SugarContext; + newContext.Resolve(item, this.Context.IsJoin ? ResolveExpressType.WhereMultiple : ResolveExpressType.WhereSingle); + this.Context.Index = newContext.Index; + this.Context.ParameterIndex = newContext.ParameterIndex; + if (newContext.Parameters.HasValue()) + { + foreach (var p in newContext.Parameters) + { + if (!this.Context.Parameters.Any(it => it.ParameterName == p.ParameterName)) + { + this.Context.Parameters.Add(p); + } + } + } + if (this.Context.SingleTableNameSubqueryShortName == "Subqueryable()") + { + this.Context.SingleTableNameSubqueryShortName = newContext.SingleTableNameSubqueryShortName; + } + else if (newContext.SingleTableNameSubqueryShortName != null && newContext.Result?.Contains(this.Context.SqlTranslationLeft + newContext.SingleTableNameSubqueryShortName + this.Context.SqlTranslationRight) == true) + { + this.Context.SingleTableNameSubqueryShortName = newContext.SingleTableNameSubqueryShortName; + } + return newContext.Result.GetResultString(); + } + + public string GetNewExpressionValue(Expression item, ResolveExpressType type) + { + var newContext = this.Context.GetCopyContextWithMapping(); + newContext.SugarContext = this.Context.SugarContext; + newContext.Resolve(item, type); + this.Context.Index = newContext.Index; + this.Context.ParameterIndex = newContext.ParameterIndex; + if (newContext.Parameters.HasValue()) + { + this.Context.Parameters.AddRange(newContext.Parameters); + } + return newContext.Result.GetResultString(); + } + + protected void ResolveNewExpressions(ExpressionParameter parameter, Expression item, string asName) + { + if (item is ConstantExpression) + { + ResolveConst(parameter, item, asName); + } + else if ((item is MemberExpression) && ((MemberExpression)item).Expression == null) + { + ResolveMember(parameter, item, asName); + } + else if ((item is MemberExpression) && ((MemberExpression)item).Expression.NodeType == ExpressionType.Constant) + { + ResolveMemberConst(parameter, item, asName); + } + else if (item is MemberExpression) + { + ResolveMemberOther(parameter, item, asName); + } + else if (item is UnaryExpression && ((UnaryExpression)item).Operand is MemberExpression) + { + ResolveUnaryExpMem(parameter, item, asName); + } + else if (item is UnaryExpression && ((UnaryExpression)item).Operand is ConstantExpression) + { + ResolveUnaryExpConst(parameter, item, asName); + } + else if (ExpressionTool.RemoveConvert(item) is BinaryExpression) + { + ResolveBinary(item, asName); + } + else if (item.Type.IsClass()) + { + asName = ResolveClass(parameter, item, asName); + } + else if (item.Type == UtilConstants.BoolType && item is MethodCallExpression && IsNotCaseExpression(item)) + { + ResloveBoolMethod(parameter, item, asName); + } + else if (item.NodeType == ExpressionType.Not + && (item as UnaryExpression).Operand is MethodCallExpression + && ((item as UnaryExpression).Operand as MethodCallExpression).Method.Name.IsIn("IsNullOrEmpty", "IsNullOrWhiteSpace")) + { + ResloveNot(parameter, item, asName); + } + else if (item is MethodCallExpression && (item as MethodCallExpression).Method.Name.IsIn("Count", "Any") && !item.ToString().StartsWith("Subqueryable")) + { + ResloveCountAny(parameter, item, asName); + } + else if (item is MethodCallExpression || item is UnaryExpression || item is ConditionalExpression || item.NodeType == ExpressionType.Coalesce) + { + ResloveOtherMUC(parameter, item, asName); + } + else + { + Check.ThrowNotSupportedException(item.GetType().Name); + } + } + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/BaseResolve_Property.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/BaseResolve_Property.cs new file mode 100644 index 000000000..3f5e2eb8e --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/BaseResolve_Property.cs @@ -0,0 +1,109 @@ +using System.Linq.Expressions; +namespace SqlSugar +{ + public partial class BaseResolve + { + #region Property + protected Expression Expression { get; set; } + protected Expression ExactExpression { get; set; } + public ExpressionContext Context { get; set; } + public bool? IsLeft { get; set; } + public int ContentIndex { get { return this.Context.Index; } } + public int Index { get; set; } + public ExpressionParameter BaseParameter { get; set; } + #endregion + + #region Dictionary + private Dictionary GetMappingColumns(Expression currentExpression) + { + Dictionary result = new Dictionary(); + if (currentExpression == null) + { + return result; + } + List types = new List(); + int i = 0; + if (currentExpression is NewExpression) + { + i = (currentExpression as NewExpression).Arguments.Count; + foreach (var item in (currentExpression as NewExpression).Arguments) + { + if (item.Type.IsClass()) + { + types.Add(item.Type); + } + } + } + else if (currentExpression is MemberInitExpression) + { + i = (currentExpression as MemberInitExpression).Bindings.Count; + foreach (var item in (currentExpression as MemberInitExpression).Bindings) + { + MemberAssignment memberAssignment = (MemberAssignment)item; + if (memberAssignment.Expression.Type.IsClass()) + { + types.Add(memberAssignment.Expression.Type); + } + } + } + if (types.Count == i && (types.Count == types.Distinct().Count())) + { + return result; + } + var array = currentExpression.ToString().Split(','); + foreach (var item in array) + { + var itemArray = item.Split('=').ToArray(); + var last = itemArray.Last().Trim().Split('.').First().TrimEnd(')').TrimEnd('}'); + var first = itemArray.First().Trim(); + if (first.Contains('{')) + { + first = first.Split('{').Last().Trim(); + } + if (first.Contains('(')) + { + first = first.Split('(').Last().Trim(); + } + if (!result.TryAdd(first, last)) + { + //future + } + } + return result; ; + } + protected static Dictionary MethodMapping = new Dictionary() { + { "ToString","ToString"}, + { "ToInt32","ToInt32"}, + { "ToInt16","ToInt32"}, + { "ToInt64","ToInt64"}, + { "ToDecimal","ToDecimal"}, + { "ToDateTime","ToDate"}, + { "ToBoolean","ToBool"}, + { "ToDouble","ToDouble"}, + { "Length","Length"}, + { "Replace","Replace"}, + { "Contains","Contains"}, + { "ContainsArray","ContainsArray"}, + { "EndsWith","EndsWith"}, + { "StartsWith","StartsWith"}, + { "HasValue","HasValue"}, + { "Trim","Trim"}, + { "Equals","Equals"}, + { "ToLower","ToLower"}, + { "ToUpper","ToUpper"}, + { "Substring","Substring"}, + { "DateAdd","DateAdd"}, + { "CompareTo","CompareTo"} + }; + protected static Dictionary MethodTimeMapping = new Dictionary() { + { "AddYears",DateType.Year}, + { "AddMonths",DateType.Month}, + { "AddDays",DateType.Day}, + { "AddHours",DateType.Hour}, + { "AddMinutes",DateType.Minute}, + { "AddSeconds",DateType.Second}, + { "AddMilliseconds",DateType.Millisecond} + }; + #endregion + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/BaseResolve_Validate.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/BaseResolve_Validate.cs new file mode 100644 index 000000000..b2ec28feb --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/BaseResolve_Validate.cs @@ -0,0 +1,114 @@ +using System.Linq.Expressions; +namespace SqlSugar +{ + /// + /// BaseResolve-Validate + /// + public partial class BaseResolve + { + + private static bool IsSubToList(Expression item) + { + return ExpressionTool.GetMethodName(item).IsIn("ToList", "First") && IsSubquery(item); + } + + private static bool IsSubquery(Expression item) + { + var method = (item as MethodCallExpression); + if (method == null) + return false; + if (method.Object == null) + return false; + return method.Object.Type.Name.StartsWith("Subquery"); + } + + private bool IsExtSqlFuncObj(Expression item) + { + return this.Context.SqlFuncServices != null && item is MethodCallExpression && this.Context.SqlFuncServices.Any(it => it.UniqueMethodName == ExpressionTool.GetMethodName(item)); + } + private bool IsNullValue(ExpressionParameter parameter, object value) + { + return value == null + && !parameter.ValueIsNull + && parameter.BaseParameter?.OperatorValue.IsIn("=", "<>") == true + && this.Context.ResolveType.IsIn(ResolveExpressType.WhereMultiple, ResolveExpressType.WhereSingle); + } + private static bool IsNotCaseExpression(Expression item) + { + if ((item as MethodCallExpression).Method.Name == "IIF") + { + return false; + } + else if ((item as MethodCallExpression).Method.Name == "IsNull") + { + return false; + } + else if ((item as MethodCallExpression).Method.Name == "End" && item.ToString().Contains("IF(")) + { + return false; + } + else if ((item as MethodCallExpression).Method.Name == "AggregateMax") + { + return false; + } + else if ((item as MethodCallExpression).Method.Name == "AggregateMin") + { + return false; + } + else if ((item as MethodCallExpression).Method.Name == "AggregateSum") + { + return false; + } + else if ((item as MethodCallExpression).Method.Name == "ToBool") + { + return false; + } + else if ((item as MethodCallExpression).Method.Name == "ToBoolean") + { + return false; + } + else if ((item as MethodCallExpression).Method.Name == "Select" && item.ToString().Contains("Subqueryable()")) + { + return false; + } + else + { + return true; + } + } + private static bool IsBoolValue(Expression item) + { + return item.Type == UtilConstants.BoolType && + (item is MemberExpression) && + (item as MemberExpression).Expression != null && + (item as MemberExpression).Expression.Type == typeof(bool?) && + (item as MemberExpression).Member.Name == "Value"; + } + protected static bool IsConvert(Expression item) + { + return item is UnaryExpression && item.NodeType == ExpressionType.Convert; + } + protected static bool IsNotMember(Expression item) + { + return item is UnaryExpression && + item.Type == UtilConstants.BoolType && + (item as UnaryExpression).NodeType == ExpressionType.Not && + (item as UnaryExpression).Operand is MemberExpression && + ((item as UnaryExpression).Operand as MemberExpression).Expression != null && + ((item as UnaryExpression).Operand as MemberExpression).Expression.NodeType == ExpressionType.Parameter; + } + protected static bool IsNotParameter(Expression item) + { + return item is UnaryExpression && + item.Type == UtilConstants.BoolType && + (item as UnaryExpression).NodeType == ExpressionType.Not && + (item as UnaryExpression).Operand is MemberExpression && + ((item as UnaryExpression).Operand as MemberExpression).Expression != null && + ((item as UnaryExpression).Operand as MemberExpression).Expression.NodeType == ExpressionType.MemberAccess; + } + protected bool IsSubMethod(MethodCallExpression express) + { + return SubTools.SubItemsConst.Any(it => express.Object?.Type.Name.StartsWith("Subqueryable`") == true); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/BinaryExpressionResolve.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/BinaryExpressionResolve.cs new file mode 100644 index 000000000..5f0ae058a --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/BinaryExpressionResolve.cs @@ -0,0 +1,407 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public class BinaryExpressionResolve : BaseResolve + { + public BinaryExpressionResolve(ExpressionParameter parameter) : base(parameter) + { + switch (parameter.Context.ResolveType) + { + case ResolveExpressType.FieldSingle: + case ResolveExpressType.FieldMultiple: + var sql = base.GetNewExpressionValue(this.Expression); + this.Context.Result.Append(sql); + break; + default: + Other(parameter); + break; + } + } + + private void Other(ExpressionParameter parameter) + { + var expression = this.Expression as BinaryExpression; + if (expression.NodeType == ExpressionType.ArrayIndex) + { + var parameterName = AppendParameter(ExpressionTool.DynamicInvoke(expression)); + if (this.BaseParameter?.IsLeft == true) + { + base.Context.Result.Append($" {BaseParameter.BaseParameter.OperatorValue} {parameterName} "); + } + else + { + base.Context.Result.Append($" {parameterName} "); + } + return; + } + var operatorValue = parameter.OperatorValue = ExpressionTool.GetOperator(expression.NodeType); + var isSubGroup = IsGroupSubquery(expression.Right, operatorValue); + if (isSubGroup) + { + SubGroup(expression, operatorValue); + } + else if (IsJoinString(expression, operatorValue)) + { + JoinString(parameter, expression); + } + else if (IsUpdateJson(parameter, expression, operatorValue)) + { + parameter.CommonTempData = "IsJson=true"; + DefaultBinary(parameter, expression, operatorValue); + parameter.CommonTempData = null; + } + else if (IsUpdateArray(parameter, expression, operatorValue)) + { + parameter.CommonTempData = "IsArray=true"; + DefaultBinary(parameter, expression, operatorValue); + parameter.CommonTempData = null; + } + else if (IsBinaryGroup(operatorValue, expression)) + { + var isComparisonOperator = ExpressionTool.IsComparisonOperator(expression); + var getLeft = GetNewExpressionValue(expression.Left); + var getRight = GetNewExpressionValue(expression.Right); + base.Context.Result.Append($"( {getLeft} {operatorValue} {getRight} )"); + } + else + { + DefaultBinary(parameter, expression, operatorValue); + } + } + + private bool IsBinaryGroup(string operatorValue, BinaryExpression expression) + { + var left = ExpressionTool.RemoveConvert(expression.Left); + var right = ExpressionTool.RemoveConvert(expression.Right); + if (operatorValue?.IsIn("AND", "OR") == true && left is BinaryExpression && right is BinaryExpression) + { + + var leftChild = ExpressionTool.RemoveConvert((left as BinaryExpression).Right); + var rightChild = ExpressionTool.RemoveConvert((right as BinaryExpression).Right); + var isLeftSelect = ExpressionTool.GetMethodName(leftChild) == "Select" || leftChild is BinaryExpression; + var isRightSelect = ExpressionTool.GetMethodName(rightChild) == "Select" || rightChild is BinaryExpression; + var isLeftGroup = ExpressionTool.ContainsMethodName(left as BinaryExpression, "Group"); + var isRightGroup = ExpressionTool.ContainsMethodName(right as BinaryExpression, "Group"); + if ( + (isLeftSelect && isLeftGroup) + || + (isRightSelect && isRightGroup) + ) + { + return true; + } + } + return false; + } + + private bool IsUpdateArray(ExpressionParameter parameter, BinaryExpression expression, string operatorValue) + { + var isOk = parameter.Context.ResolveType == ResolveExpressType.WhereSingle && operatorValue == "=" && (expression.Left is MemberExpression) && expression.Left.Type.IsClass(); + if (isOk && this.Context.SugarContext != null) + { + var member = (expression.Left as MemberExpression); + if (member.Expression != null) + { + var entity = this.Context.SugarContext.Context.EntityMaintenance.GetEntityInfo(member.Expression.Type); + var jsonColumn = entity.Columns.FirstOrDefault(it => it.IsArray && it.PropertyName == ExpressionTool.GetMemberName(expression.Left)); + if (jsonColumn != null) + { + return true; + } + } + } + return false; + } + + private bool IsUpdateJson(ExpressionParameter parameter, BinaryExpression expression, string operatorValue) + { + var isOk = parameter.Context.ResolveType == ResolveExpressType.WhereSingle && operatorValue == "=" && (expression.Left is MemberExpression) && expression.Left.Type.IsClass(); + if (isOk && this.Context.SugarContext != null) + { + var member = (expression.Left as MemberExpression); + if (member.Expression != null) + { + var entity = this.Context.SugarContext.Context.EntityMaintenance.GetEntityInfo(member.Expression.Type); + var jsonColumn = entity.Columns.FirstOrDefault(it => it.IsJson && it.PropertyName == ExpressionTool.GetMemberName(expression.Left)); + if (jsonColumn != null) + { + return true; + } + } + } + return false; + } + + private void JoinString(ExpressionParameter parameter, BinaryExpression expression) + { + var leftString = GetNewExpressionValue(expression.Left); + var RightString = GetNewExpressionValue(expression.Right); + if (leftString == null && base.BaseParameter?.BaseExpression == null && expression.Left is ParameterExpression parameterExpression) + { + leftString = this.Context.SqlParameterKeyWord + "MethodConst1"; + } + var joinString = this.Context.DbMehtods.MergeString(leftString, RightString); + if (this.Context is KdbndpExpressionContext && this.Context?.SugarContext?.Context?.CurrentConnectionConfig?.MoreSettings?.DatabaseModel == DbType.SqlServer) + { + joinString = new SqlServerMethod().MergeString(leftString, RightString); + } + if (this.Context.Result.Contains(ExpressionConst.FormatSymbol)) + { + base.Context.Result.Replace("{0}", $" {joinString} "); + base.Context.Result.Append(" " + ExpressionConst.ExpressionReplace + parameter.BaseParameter.Index + " "); + } + else + { + base.Context.Result.Append($" {joinString} "); + } + } + + + + private void DefaultBinary(ExpressionParameter parameter, BinaryExpression expression, string operatorValue) + { + var isEqual = expression.NodeType == ExpressionType.Equal; + var isComparisonOperator = ExpressionTool.IsComparisonOperator(expression); + base.ExactExpression = expression; + var leftExpression = expression.Left; + var rightExpression = expression.Right; + if (operatorValue.IsIn("AND", "OR") && leftExpression is BinaryExpression exp) + { + if (exp?.Left is BinaryExpression expChild) + { + if (ExpressionTool.GetMethodName(expChild?.Right) == "Select" && ExpressionTool.ContainsMethodName(expChild, "GroupBy")) + { + var childLeft = GetNewExpressionValue(expChild.Left); + var childRight = GetNewExpressionValue(expChild.Right); + var right = GetNewExpressionValue(exp.Right); + var ov = ExpressionTool.GetOperator(exp.NodeType); + base.Context.Result.Append($" (({childLeft + " IN " + childRight}) {operatorValue} {GetNewExpressionValue(rightExpression)}) {ov} {right} "); + return; + } + } + } + if (operatorValue == "=" && ExpressionTool.RemoveConvert(leftExpression) is ConstantExpression) + { + leftExpression = expression.Right; + rightExpression = expression.Left; + } + if (RightIsHasValue(leftExpression, rightExpression, ExpressionTool.IsLogicOperator(expression))) + { + Expression trueValue = Expression.Constant(true); + rightExpression = ExpressionBuilderHelper.CreateExpression(rightExpression, trueValue, ExpressionType.Equal); + } + var leftIsBinary = leftExpression is BinaryExpression; + var rightBinary = rightExpression is BinaryExpression; + var lbrs = leftIsBinary && !rightBinary; + var lsrb = !leftIsBinary && rightBinary; + var lbrb = rightBinary && leftIsBinary; + var lsbs = !leftIsBinary && !rightBinary; + var isAppend = !base.Context.Result.Contains(ExpressionConst.FormatSymbol); + ConvertExpression(ref leftExpression, ref rightExpression, isAppend); + parameter.LeftExpression = leftExpression; + parameter.RightExpression = rightExpression; + Left(expression, leftExpression); + Right(parameter, operatorValue, isEqual, rightExpression, lsbs); + } + + private void SubGroup(BinaryExpression expression, string operatorValue) + { + if (ExpressionTool.IsUnConvertExpress(expression.Right)) + { + InSubGroupByConvertExpress(expression); + } + else + { + InSubGroupBy(expression, operatorValue == "<>" ? "NOT" : ""); + } + } + + private void ConvertExpression(ref Expression leftExpression, ref Expression rightExpression, bool isAppend) + { + if (isAppend) + { + base.Context.Result.Append(ExpressionConst.LeftParenthesis); + base.Context.Result.Append(ExpressionConst.FormatSymbol); + } + else + { + base.Context.Result.Replace(ExpressionConst.FormatSymbol, ExpressionConst.LeftParenthesis + ExpressionConst.FormatSymbol); + } + if (leftExpression is UnaryExpression && (leftExpression as UnaryExpression).Operand is UnaryExpression && (leftExpression as UnaryExpression).NodeType == ExpressionType.Convert) + { + leftExpression = (leftExpression as UnaryExpression).Operand; + } + if (leftExpression is UnaryExpression && (leftExpression as UnaryExpression).Operand.Type == UtilConstants.BoolType && (leftExpression as UnaryExpression).NodeType == ExpressionType.Convert && rightExpression.Type == UtilConstants.BoolTypeNull) + { + leftExpression = (leftExpression as UnaryExpression).Operand; + } + if (rightExpression is UnaryExpression && (rightExpression as UnaryExpression).NodeType == ExpressionType.Convert) + { + rightExpression = (rightExpression as UnaryExpression).Operand; + } + } + + private void Right(ExpressionParameter parameter, string operatorValue, bool isEqual, Expression rightExpression, bool lsbs) + { + base.IsLeft = false; + base.Expression = rightExpression; + base.Start(); + base.IsLeft = null; + if (lsbs && parameter.ValueIsNull) + { + base.Context.Result.Replace(ExpressionConst.ExpressionReplace + parameter.Index, isEqual ? "IS" : "IS NOT"); + base.Context.Result.Replace(ExpressionConst.ExpressionReplace + (parameter.Index + 1), isEqual ? "IS" : "IS NOT"); + } + else + { + base.Context.Result.Replace(ExpressionConst.ExpressionReplace + parameter.Index, operatorValue); + base.Context.Result.Replace(ExpressionConst.ExpressionReplace + (parameter.Index + 1), operatorValue); + } + base.Context.Result.Append(ExpressionConst.RightParenthesis); + if (parameter.BaseExpression is BinaryExpression && parameter.IsLeft == true) + { + base.Context.Result.Append(" " + ExpressionConst.ExpressionReplace + parameter.BaseParameter.Index + " "); + } + } + + private void Left(BinaryExpression expression, Expression leftExpression) + { + base.Expression = leftExpression; + base.IsLeft = true; + base.Start(); + if (leftExpression is UnaryExpression && leftExpression.Type == UtilConstants.BoolType && !this.Context.Result.Contains(ExpressionConst.ExpressionReplace)) + { + this.Context.Result.AppendFormat(" {0} ", ExpressionTool.GetOperator(expression.NodeType)); + } + else if (leftExpression is UnaryExpression && ExpressionTool.RemoveConvert(leftExpression) is BinaryExpression && !this.Context.Result.Contains(ExpressionConst.ExpressionReplace)) + { + this.Context.Result.AppendFormat(" {0} ", ExpressionTool.GetOperator(expression.NodeType)); + } + } + + private void InSubGroupByConvertExpress(BinaryExpression expression) + { + var leftSql = GetNewExpressionValue(expression.Left); + var rightExpression = (expression.Right as UnaryExpression).Operand as MethodCallExpression; + var selector = GetNewExpressionValue(rightExpression.Arguments[0]); + var rightSql = GetNewExpressionValue(rightExpression.Object).Replace("SELECT FROM", $"SELECT {selector} FROM"); + if (this.Context.IsSingle && this.Context.SingleTableNameSubqueryShortName == null) + { + var leftExp = expression.Left; + if (leftExp is UnaryExpression) + { + leftExp = (leftExp as UnaryExpression).Operand; + } + var p = (leftExp as MemberExpression); + this.Context.SingleTableNameSubqueryShortName = p.Expression.ToString(); + } + base.Context.Result.Append($" {leftSql} in ({rightSql}) "); + } + private void InSubGroupBy(BinaryExpression expression, string not) + { + var leftSql = GetNewExpressionValue(ExpressionTool.RemoveConvert(expression.Left)); + var rightExpression = expression.Right as MethodCallExpression; + if (rightExpression.Arguments[0] is LambdaExpression) + { + if ((rightExpression.Arguments[0] as LambdaExpression).Parameters?.Count > 0) + { + foreach (var item in (rightExpression.Arguments[0] as LambdaExpression).Parameters) + { + if (this.Context.InitMappingInfo != null) + { + this.Context.InitMappingInfo(item.Type); + this.Context.RefreshMapping(); + } + } + } + } + var selector = ""; + bool hasMethodCallWithName = ExpressionTool.ContainsMethodName(expression, "Join"); + if (hasMethodCallWithName) + { + selector = GetNewExpressionValue(rightExpression.Arguments[0], ResolveExpressType.WhereMultiple); + } + else + { + selector = GetNewExpressionValue(rightExpression.Arguments[0]); + } + var selectorExp = rightExpression.Arguments[0]; + if (hasMethodCallWithName == false && selector.Contains('.') && selectorExp is LambdaExpression) + { + var selectorExpLam = (selectorExp as LambdaExpression); + var name = (selectorExpLam.Parameters[0] as ParameterExpression).Name; + selector = selector.Replace(this.Context.GetTranslationColumnName(name) + ".", ""); + } + var rightSql = GetNewExpressionValue(rightExpression.Object).Replace("SELECT FROM", $"SELECT {selector} FROM"); + if (this.Context.IsSingle && this.Context.SingleTableNameSubqueryShortName == null) + { + var leftExp = expression.Left; + if (leftExp is UnaryExpression) + { + leftExp = (leftExp as UnaryExpression).Operand; + } + var p = (leftExp as MemberExpression); + this.Context.SingleTableNameSubqueryShortName = p.Expression.ToString(); + } + if (UtilMethods.IsParentheses(rightSql + "")) + { + base.Context.Result.Append($" {leftSql} {not} in {rightSql} "); + } + else + { + base.Context.Result.Append($" {leftSql} {not} in ({rightSql}) "); + } + } + + private bool IsGroupSubquery(Expression rightExpression, string operatorValue) + { + if (operatorValue != "=" && operatorValue != "<>") + { + return false; + } + if (rightExpression == null) + { + return false; + } + if (ExpressionTool.IsUnConvertExpress(rightExpression)) + { + rightExpression = (rightExpression as UnaryExpression).Operand; + } + if ((rightExpression is MethodCallExpression) == false) + { + return false; + } + var method = (rightExpression as MethodCallExpression); + if (method.Method.Name != "Select") + { + return false; + } + var topMethods = ExpressionTool.GetTopLevelMethodCalls(method); + if (!topMethods.Contains("Subqueryable")) + { + return false; + } + if (!topMethods.Contains("GroupBy")) + { + return false; + } + return true; + } + private static bool IsJoinString(BinaryExpression expression, string operatorValue) + { + return operatorValue == "+" + && expression.Right.Type == UtilConstants.StringType + && expression.Left.Type == UtilConstants.StringType; + } + private static bool RightIsHasValue(Expression leftExpression, Expression rightExpression, bool isLogic) + { + return isLogic && + leftExpression.Type == UtilConstants.BoolType && + rightExpression.Type == UtilConstants.BoolType && + rightExpression is MethodCallExpression && + (rightExpression as MethodCallExpression).Method.Name == "HasValue"; + } + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/BlockExpressionResolve.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/BlockExpressionResolve.cs new file mode 100644 index 000000000..4891cf87e --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/BlockExpressionResolve.cs @@ -0,0 +1,10 @@ +namespace SqlSugar +{ + public class BlockExpressionResolve : BaseResolve + { + public BlockExpressionResolve(ExpressionParameter parameter) : base(parameter) + { + + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/CoalesceResolveItems.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/CoalesceResolveItems.cs new file mode 100644 index 000000000..335a2d2ea --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/CoalesceResolveItems.cs @@ -0,0 +1,39 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public class CoalesceResolveItems : MethodCallExpressionResolve + { + public CoalesceResolveItems(ExpressionParameter parameter) : base(parameter) + { + string name = "IsNull"; + var express = base.Expression as BinaryExpression; + var args = new List() { + express.Left, + express.Right + }; + var isLeft = parameter.IsLeft; + MethodCallExpressionModel model = new MethodCallExpressionModel(); + model.Args = new List(); + switch (this.Context.ResolveType) + { + case ResolveExpressType.WhereSingle: + case ResolveExpressType.WhereMultiple: + Check.Exception(name == "GetSelfAndAutoFill", "SqlFunc.GetSelfAndAutoFill can only be used in Select."); + base.Where(parameter, isLeft, name, args, model); + break; + case ResolveExpressType.SelectSingle: + case ResolveExpressType.SelectMultiple: + case ResolveExpressType.Update: + base.Select(parameter, isLeft, name, args, model); + break; + case ResolveExpressType.FieldSingle: + case ResolveExpressType.FieldMultiple: + base.Field(parameter, isLeft, name, args, model); + break; + default: + break; + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/ConditionalExpressionResolve.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/ConditionalExpressionResolve.cs new file mode 100644 index 000000000..12fb81b7f --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/ConditionalExpressionResolve.cs @@ -0,0 +1,93 @@ +using System.Linq.Expressions; +namespace SqlSugar +{ + public class ConditionalExpressionResolve : MethodCallExpressionResolve + { + public ConditionalExpressionResolve(ExpressionParameter parameter) : base(parameter) + { + + string name = "IIF"; + var express = base.Expression as ConditionalExpression; + var args = new List() { + express.Test, + express.IfTrue, + express.IfFalse + }; + if (ExpressionTool.GetParameters(express.Test).Count == 0) + { + while (express != null) + { + var ps = ExpressionTool.GetParameters(express.Test); + if (ps?.Count == 0) + { + var value = ExpressionTool.DynamicInvoke(express.Test); + if (value is bool boolValue) + { + args[0] = boolValue ? UtilConstants.ExpTrue : UtilConstants.ExpFalse; + // 根据结果选择分支 + var next = boolValue ? express.IfTrue : express.IfFalse; + args[1] = next; + args[2] = next; + + // 如果选择的分支还是一个条件表达式,就继续展开 + if (ExpressionTool.RemoveConvert(next) is ConditionalExpression childConditional) + { + express = childConditional; + args = new List() { + express.Test, + express.IfTrue, + express.IfFalse + }; + continue; + } + else + { + break; // 到底了,不是条件表达式,跳出循环 + } + } + else + { + break; // 不是bool,无法判断,退出 + } + } + else + { + break; // 有参数,不能动态执行,退出 + } + } + } + if (IsBoolMember(express)) + { + Expression trueValue = Expression.Constant(true); + args[0] = ExpressionBuilderHelper.CreateExpression(express.Test, trueValue, ExpressionType.Equal); + } + var isLeft = parameter.IsLeft; + MethodCallExpressionModel model = new MethodCallExpressionModel(); + model.Args = new List(); + switch (this.Context.ResolveType) + { + case ResolveExpressType.WhereSingle: + case ResolveExpressType.WhereMultiple: + Check.Exception(name == "GetSelfAndAutoFill", "SqlFunc.GetSelfAndAutoFill can only be used in Select."); + base.Where(parameter, isLeft, name, args, model); + break; + case ResolveExpressType.SelectSingle: + case ResolveExpressType.SelectMultiple: + case ResolveExpressType.Update: + base.Select(parameter, isLeft, name, args, model); + break; + case ResolveExpressType.FieldSingle: + case ResolveExpressType.FieldMultiple: + base.Field(parameter, isLeft, name, args, model); + break; + default: + break; + } + } + + private static bool IsBoolMember(ConditionalExpression express) + { + return express.Test is MemberExpression && (express.Test as MemberExpression).Expression is ParameterExpression; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/ConstantExpressionResolve.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/ConstantExpressionResolve.cs new file mode 100644 index 000000000..5ab826e46 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/ConstantExpressionResolve.cs @@ -0,0 +1,166 @@ +using System.Linq.Expressions; +namespace SqlSugar +{ + public class ConstantExpressionResolve : BaseResolve + { + public ConstantExpressionResolve(ExpressionParameter parameter) : base(parameter) + { + var expression = base.Expression as ConstantExpression; + string customParameter = GetCustomParameter(parameter, expression); + if (customParameter == null) + { + DefaultConstant(parameter, expression); + } + else + { + CustomConstant(parameter, customParameter); + } + } + + private void CustomConstant(ExpressionParameter parameter, string customParameter) + { + var baseParameter = parameter.BaseParameter; + var isSetTempData = baseParameter.CommonTempData.HasValue() && baseParameter.CommonTempData.Equals(CommonTempDataType.Result); + if (isSetTempData) + { + baseParameter.CommonTempData = customParameter; + } + else + { + AppendMember(parameter, parameter.IsLeft, customParameter); + } + } + + private string GetCustomParameter(ExpressionParameter parameter, ConstantExpression expression) + { + string customParameter = null; + if (parameter.OppsiteExpression != null) + { + var exp = ExpressionTool.RemoveConvert(parameter.OppsiteExpression); + if (exp is MemberExpression) + { + var member = (exp as MemberExpression); + var memberParent = member.Expression; + if (memberParent != null && this.Context?.SugarContext?.Context != null) + { + var entity = this.Context.SugarContext.Context.EntityMaintenance.GetEntityInfo(memberParent.Type); + var columnInfo = entity.Columns.FirstOrDefault(it => it.PropertyName == member.Member.Name); + if (columnInfo?.SqlParameterDbType is Type) + { + var type = columnInfo.SqlParameterDbType as Type; + var ParameterConverter = type.GetMethod("ParameterConverter").MakeGenericMethod(columnInfo.PropertyInfo.PropertyType); + var obj = Activator.CreateInstance(type); + var p = ParameterConverter.Invoke(obj, new object[] { expression.Value, 100 }) as SugarParameter; + customParameter = base.AppendParameter(p); + + } + } + } + } + return customParameter; + } + + private void DefaultConstant(ExpressionParameter parameter, ConstantExpression expression) + { + var isLeft = parameter.IsLeft; + object value = ExpressionTool.GetValue(expression.Value, this.Context); + var isNullStr = value?.ObjToString() == "NULL"; + value = ConvetValue(parameter, expression, value); + if (IsEnumString(value)) + value = ConvertEnum(value); + var baseParameter = parameter.BaseParameter; + baseParameter.ChildExpression = expression; + var isSetTempData = baseParameter.CommonTempData.HasValue() && baseParameter.CommonTempData.Equals(CommonTempDataType.Result); + switch (parameter.Context.ResolveType) + { + case ResolveExpressType.SelectSingle: + case ResolveExpressType.Update: + case ResolveExpressType.SelectMultiple: + baseParameter.CommonTempData = value; + break; + case ResolveExpressType.WhereSingle: + case ResolveExpressType.WhereMultiple: + if (isSetTempData) + { + baseParameter.CommonTempData = value; + } + else + { + var parentIsBinary = parameter.BaseParameter.CurrentExpression is BinaryExpression; + var parentIsRoot = parameter.BaseParameter.CurrentExpression is LambdaExpression; + var isBool = value != null && value.GetType() == UtilConstants.BoolType; + if (parentIsRoot && isBool) + { + this.Context.Result.Append(value.ObjToBool() ? this.Context.DbMehtods.True() : this.Context.DbMehtods.False()); + break; + } + if (parentIsBinary && isBool) + { + var isLogicOperator = + parameter.BaseExpression.NodeType == ExpressionType.And || + parameter.BaseExpression.NodeType == ExpressionType.AndAlso || + parameter.BaseExpression.NodeType == ExpressionType.Or || + parameter.BaseExpression.NodeType == ExpressionType.OrElse; + if (isLogicOperator) + { + AppendMember(parameter, isLeft, (value.ObjToBool() ? this.Context.DbMehtods.True() : this.Context.DbMehtods.False())); + break; + } + } + if (value == null && parentIsBinary) + { + parameter.BaseParameter.ValueIsNull = true; + value = this.Context.DbMehtods.Null(); + } + if (isNullStr) + { + this.Context.Result.Append(AppendParameter(value)); + break; + } + AppendValue(parameter, isLeft, value); + } + break; + case ResolveExpressType.FieldSingle: + case ResolveExpressType.FieldMultiple: + default: + break; + } + } + + private object ConvetValue(ExpressionParameter parameter, ConstantExpression expression, object value) + { + if (expression.Type == UtilConstants.IntType && parameter.OppsiteExpression != null && ExpressionTool.IsUnConvertExpress(parameter.OppsiteExpression)) + { + var exp = ExpressionTool.RemoveConvert(parameter.OppsiteExpression); + if (exp.Type == typeof(char) && value is int) + { + value = Convert.ToChar(Convert.ToInt32(value)); + } + } + + return value; + } + + private object ConvertEnum(object value) + { + if (base.BaseParameter?.OppsiteExpression is UnaryExpression) + { + var oppsiteExpression = base.BaseParameter?.OppsiteExpression as UnaryExpression; + var oppsiteValue = oppsiteExpression.Operand; + if (oppsiteValue.Type.IsEnum()) + { + value = UtilMethods.ChangeType2(value, oppsiteValue.Type).ToString(); + } + } + + return value; + } + + private bool IsEnumString(object value) + { + return this.Context.TableEnumIsString == true + && value?.IsInt() == true + && base.BaseParameter?.OppsiteExpression != null; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/LambdaExpressionResolve.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/LambdaExpressionResolve.cs new file mode 100644 index 000000000..c455aeea0 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/LambdaExpressionResolve.cs @@ -0,0 +1,18 @@ +using System.Linq.Expressions; +namespace SqlSugar +{ + public class LambdaExpressionResolve : BaseResolve + { + public LambdaExpressionResolve(ExpressionParameter parameter) : base(parameter) + { + LambdaExpression lambda = base.Expression as LambdaExpression; + var expression = lambda.Body; + base.Expression = expression; + if (parameter.Context.ResolveType.IsIn(ResolveExpressType.FieldMultiple, ResolveExpressType.FieldSingle)) + { + parameter.CommonTempData = CommonTempDataType.Append; + } + base.Start(); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/MapperExpressionResolve.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/MapperExpressionResolve.cs new file mode 100644 index 000000000..d68ac6629 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/MapperExpressionResolve.cs @@ -0,0 +1,325 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public class MapperExpressionResolve + { + private Expression expression; + private List mappers; + private InvalidOperationException ex; + private SqlSugarProvider context; + private QueryBuilder querybuiler; + private ISqlBuilder sqlBuilder; + private string sql; + public MapperExpressionResolve(Expression expression, InvalidOperationException ex) + { + this.expression = expression; + this.ex = ex; + this.mappers = CallContext.MapperExpression.Value; + Error01(); + var isMember = expression is MemberExpression; + if (isMember) + { + ResolveMember(); + } + else + { + ResolveList(); + } + } + + private void ResolveList() + { + var methodExpression = expression as MethodCallExpression; + var callName = methodExpression.Method.Name; + var exp = methodExpression.Arguments[0] as MemberExpression; + ThrowTrue(exp == null); + var childExpression = exp; + MapperExpression mapper = GetMapperMany(exp); + var fillInfo = GetFillInfoMany(childExpression, mapper); + var mappingFild1Info = GetMappingFild1ManyInfo(childExpression, mapper); + var mappingFild1Info2 = GetMappingFild2Info(childExpression, mapper); + //var SelectInfo = GetSelectInfo(expression); + this.context.InitMappingInfo(childExpression.Expression.Type); + var entity = this.context.EntityMaintenance.GetEntityInfo(childExpression.Expression.Type); + oneToMany(methodExpression, callName, entity, childExpression.Expression.ToString(), fillInfo, mappingFild1Info, mappingFild1Info2); + } + + private void ResolveMember() + { + var exp = expression as MemberExpression; + ThrowTrue(exp.Expression == null); + var childExpression = exp.Expression; + MapperExpression mapper = GetMapper(exp); + var fillInfo = GetFillInfo(childExpression, mapper); + var mappingFild1Info = GetMappingFild1Info(childExpression, mapper); + var mappingFild1Info2 = GetMappingFild2Info(childExpression, mapper); + var SelectInfo = GetSelectInfo(expression); + var entity = this.context.EntityMaintenance.GetEntityInfo(childExpression.Type); + + var isExMapper = mappingFild1Info2 != null; + var isFillFild1SameType = fillInfo.Type == mappingFild1Info.Type; + var isSameProperty = false; + + if (isExMapper) + { + ExtMapper(fillInfo, mappingFild1Info, mappingFild1Info2, SelectInfo); + } + else if (isSameProperty) + { + + } + else if (isFillFild1SameType) + { + throw new NotSupportedException(expression.ToString()); + } + else + { + oneToOne(fillInfo, mappingFild1Info, mappingFild1Info2, SelectInfo); + } + } + + + private void oneToOne(MapperExpressionInfo fillInfo, MapperExpressionInfo mappingFild1Info, MapperExpressionInfo mappingFild1Info2, MapperExpressionInfo selectInfo) + { + var pkColumn = selectInfo.EntityInfo.Columns.Where(it => it.IsPrimarykey == true).FirstOrDefault(); + if (pkColumn == null) + { + pkColumn = selectInfo.EntityInfo.Columns.First(); + } + var tableName = sqlBuilder.GetTranslationTableName(fillInfo.EntityInfo.DbTableName); + var whereLeft = sqlBuilder.GetTranslationColumnName(pkColumn.DbColumnName); + var whereRight = sqlBuilder.GetTranslationColumnName(mappingFild1Info.FieldString); + this.sql = this.context.Queryable() + .AS(tableName) + .Where(string.Format(" {0}={1} ", whereLeft, whereRight)) + .Select(sqlBuilder.GetTranslationColumnName(selectInfo.FieldName)).ToSql().Key; + } + + private void oneToMany(MethodCallExpression methodCallExpression, string methodName, EntityInfo mainEntity, string shortName, MapperExpressionInfo fillInfo, MapperExpressionInfo mappingFild1Info, MapperExpressionInfo mappingFild1Info2) + { + var pkColumn = mainEntity.Columns.FirstOrDefault(it => it.IsPrimarykey == true); + if (pkColumn == null) + { + pkColumn = mainEntity.Columns.FirstOrDefault(); + } + var tableName = sqlBuilder.GetTranslationTableName(fillInfo.EntityInfo.DbTableName); + var whereLeft = sqlBuilder.GetTranslationColumnName(mappingFild1Info.FieldString); + var whereRight = sqlBuilder.GetTranslationColumnName(shortName + "." + pkColumn.DbColumnName); + string whereExpression = GetWhereExpression(methodCallExpression); + if (methodName == "Any") + { + this.sql = " (" + this.context.Queryable() + .AS(tableName) + .Where(string.Format(" {0}={1} ", whereLeft, whereRight)) + .WhereIF(!string.IsNullOrEmpty(whereExpression), whereExpression) + .Select("COUNT(1)").ToSql().Key + ")>0 "; + } + else + { + this.sql = this.context.Queryable() + .AS(tableName) + .Where(string.Format(" {0}={1} ", whereLeft, whereRight)) + .WhereIF(!string.IsNullOrEmpty(whereExpression), whereExpression) + .Select("COUNT(1)").ToSql().Key; + } + } + + private string GetWhereExpression(MethodCallExpression methodCallExpression) + { + if (methodCallExpression.Arguments.Count <= 1) + return null; + var exp = methodCallExpression.Arguments[1]; + var querybuiler = InstanceFactory.GetQueryBuilder(this.context.CurrentConnectionConfig); + querybuiler.LambdaExpressions = InstanceFactory.GetLambdaExpressions(this.context.CurrentConnectionConfig); + querybuiler.Builder = InstanceFactory.GetSqlbuilder(this.context.CurrentConnectionConfig); + querybuiler.Builder.Context = querybuiler.Context; + querybuiler.Builder.QueryBuilder = querybuiler; + querybuiler.Context = this.context; + var expValue = querybuiler.GetExpressionValue(exp, ResolveExpressType.WhereMultiple); + var paramterName = (exp as LambdaExpression).Parameters[0].Name; + var sql = expValue.GetResultString(); + sql = sql.Replace(querybuiler.Builder.GetTranslationColumnName(paramterName) + ".", ""); + if (querybuiler.Parameters?.Count > 0) + { + foreach (var item in querybuiler.Parameters) + { + sql = sql.Replace(item.ParameterName, item.Value.ObjToString().ToSqlValue()); + } + } + return sql; + } + + private MapperExpressionInfo GetSelectInfo(Expression expression) + { + + var field = expression; + if (field is UnaryExpression) + { + field = (field as UnaryExpression).Operand; + } + var type = ((field as MemberExpression).Expression).Type; + this.context.InitMappingInfo(type); + var name = (field as MemberExpression).Member.Name; + var entity = this.context.EntityMaintenance.GetEntityInfo(type); + var fieldName = entity.Columns.First(it => it.PropertyName == name).DbColumnName; + return new MapperExpressionInfo() + { + Type = type, + FieldName = fieldName, + EntityInfo = entity + }; + } + + private MapperExpressionInfo GetMappingFild2Info(Expression childExpression, MapperExpression mapper) + { + if (mapper.MappingField2Expression == null) + return null; + var exp = mapper.MappingField2Expression; + var field = (exp as LambdaExpression).Body; + if (field is UnaryExpression) + { + field = (field as UnaryExpression).Operand; + } + var type = ((field as MemberExpression).Expression).Type; + this.context.InitMappingInfo(type); + var name = (field as MemberExpression).Member.Name; + var entity = this.context.EntityMaintenance.GetEntityInfo(type); + var fieldName = entity.Columns.First(it => it.PropertyName == name).DbColumnName; + return new MapperExpressionInfo() + { + Type = type, + FieldName = fieldName + }; + } + + private MapperExpressionInfo GetMappingFild1Info(Expression childExpression, MapperExpression mapper) + { + var exp = mapper.MappingField1Expression; + var field = (exp as LambdaExpression).Body; + if (field is UnaryExpression) + { + field = (field as UnaryExpression).Operand; + } + var type = ((field as MemberExpression).Expression).Type; + this.context.InitMappingInfo(type); + var name = (field as MemberExpression).Member.Name; + var entity = this.context.EntityMaintenance.GetEntityInfo(type); + var fieldName = entity.Columns.First(it => it.PropertyName == name).DbColumnName; + var array = (field as MemberExpression).ToString().Split('.').ToList(); + array[array.Count - 1] = fieldName; + var filedString = string.Join(".", array); + return new MapperExpressionInfo() + { + Type = type, + FieldName = fieldName, + FieldString = filedString, + EntityInfo = entity + }; + } + + private MapperExpressionInfo GetFillInfo(Expression childExpression, MapperExpression mapper) + { + this.querybuiler = mapper.QueryBuilder; + this.context = mapper.Context; + this.sqlBuilder = mapper.SqlBuilder; + if (this.querybuiler.TableShortName.IsNullOrEmpty()) + { + this.querybuiler.TableShortName = (childExpression as MemberExpression).Expression.ToString(); + } + this.context.InitMappingInfo(childExpression.Type); + return new MapperExpressionInfo() + { + EntityInfo = this.context.EntityMaintenance.GetEntityInfo(childExpression.Type) + }; + } + + private MapperExpression GetMapper(MemberExpression exp) + { + var mapper = mappers.Where(it => it.Type == MapperExpressionType.oneToOne) + .Reverse() + .Where(it => (it.FillExpression as LambdaExpression).Body.ToString() == exp.Expression.ToString()).FirstOrDefault(); + ThrowTrue(mapper == null); + return mapper; + } + + public string GetMemberName(MemberExpression memberExpression) + { + return ""; + } + + private void ExtMapper(MapperExpressionInfo fillInfo, MapperExpressionInfo mappingFild1Info, MapperExpressionInfo mappingFild1Info2, MapperExpressionInfo selectInfo) + { + var tableName = sqlBuilder.GetTranslationTableName(fillInfo.EntityInfo.DbTableName); + var whereLeft = sqlBuilder.GetTranslationColumnName(mappingFild1Info2.FieldName); + var whereRight = sqlBuilder.GetTranslationColumnName(mappingFild1Info.FieldString); + this.sql = this.context.Queryable() + .AS(tableName) + .Where(string.Format(" {0}={1} ", whereLeft, whereRight)) + .Select(sqlBuilder.GetTranslationColumnName(selectInfo.FieldName)).ToSql().Key; + } + + public MapperSql GetSql() + { + return new MapperSql() { Sql = " (" + this.sql + ") " }; + } + + private MapperExpression GetMapperMany(MemberExpression exp) + { + var mapper = mappers.Where(it => it.Type == MapperExpressionType.oneToN) + .Reverse() + .Where(it => (it.FillExpression as LambdaExpression).Body.ToString() == exp.ToString()).FirstOrDefault(); + ThrowTrue(mapper == null); + return mapper; + } + private MapperExpressionInfo GetFillInfoMany(Expression childExpression, MapperExpression mapper) + { + this.querybuiler = mapper.QueryBuilder; + this.context = mapper.Context; + this.sqlBuilder = mapper.SqlBuilder; + if (this.querybuiler.TableShortName.IsNullOrEmpty()) + { + this.querybuiler.TableShortName = (childExpression as MemberExpression).Expression.ToString(); + } + var type = (childExpression as MemberExpression).Type.GetGenericArguments()[0]; + this.context.InitMappingInfo(type); + return new MapperExpressionInfo() + { + EntityInfo = this.context.EntityMaintenance.GetEntityInfo(type) + }; + } + private MapperExpressionInfo GetMappingFild1ManyInfo(Expression childExpression, MapperExpression mapper) + { + var exp = mapper.MappingField1Expression; + var field = (exp as LambdaExpression).Body; + if (field is UnaryExpression) + { + field = (field as UnaryExpression).Operand; + } + var type = ((field as MemberExpression).Expression).Type; + this.context.InitMappingInfo(type); + var name = (field as MemberExpression).Member.Name; + var entity = this.context.EntityMaintenance.GetEntityInfo(type); + var fieldName = entity.Columns.First(it => it.PropertyName == name).DbColumnName; + //var array = (field as MemberExpression).ToString().Split('.').ToList(); + //array[array.Count() - 1] = fieldName; + //var filedString = string.Join(".", array); + return new MapperExpressionInfo() + { + Type = type, + FieldName = fieldName, + FieldString = fieldName, + EntityInfo = entity + }; + } + + void Error01() + { + Check.Exception(mappers == null, ErrorMessage.GetThrowMessage(expression.ToString() + "no support Check if the navigation is configured correctly or Includes() is missing", "当前表达式" + expression.ToString() + " 不支持,查看导航是否配置正确等或者缺少Includes() ")); + } + void ThrowTrue(bool isError) + { + Check.Exception(isError, ErrorMessage.GetThrowMessage(expression.ToString() + "no support Check if the navigation is configured correctly or Includes() is missing", "不支持表达式" + expression.ToString() + " ,查看导航是否配置正确等或者缺少Includes() ")); + } + } +} \ No newline at end of file diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/MemberConstExpressionResolve.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/MemberConstExpressionResolve.cs new file mode 100644 index 000000000..e0d819ea7 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/MemberConstExpressionResolve.cs @@ -0,0 +1,69 @@ +using System.Linq.Expressions; +namespace SqlSugar +{ + public class MemberConstExpressionResolve : BaseResolve + { + public MemberConstExpressionResolve(ExpressionParameter parameter) : base(parameter) + { + var expression = base.Expression as MemberExpression; + var isLeft = parameter.IsLeft; + object value = ExpressionTool.GetMemberValue(expression.Member, expression); + var baseParameter = parameter.BaseParameter; + var isSetTempData = baseParameter.CommonTempData.HasValue() && baseParameter.CommonTempData.Equals(CommonTempDataType.Result); + switch (parameter.Context.ResolveType) + { + case ResolveExpressType.Update: + case ResolveExpressType.SelectSingle: + case ResolveExpressType.SelectMultiple: + value = Select(parameter, value); + break; + case ResolveExpressType.WhereSingle: + case ResolveExpressType.WhereMultiple: + Where(parameter, isLeft, value, baseParameter, isSetTempData); + break; + case ResolveExpressType.FieldSingle: + case ResolveExpressType.FieldMultiple: + break; + } + } + + private void Where(ExpressionParameter parameter, bool? isLeft, object value, ExpressionParameter baseParameter, bool isSetTempData) + { + if (parameter.OppsiteExpression != null) + { + var exp = ExpressionTool.RemoveConvert(parameter.OppsiteExpression); + value = GetMemberValue(value, exp); + var valueFullName = value?.GetType()?.FullName; + if (valueFullName == "Microsoft.Extensions.Primitives.StringValues") + { + value = value.ToString(); + } + } + if (isSetTempData) + { + baseParameter.CommonTempData = value; + } + else + { + AppendValue(parameter, isLeft, value); + } + } + + private object Select(ExpressionParameter parameter, object value) + { + if (value?.GetType().IsEnum() == true) + { + if (this.Context?.SugarContext?.Context?.CurrentConnectionConfig?.MoreSettings?.TableEnumIsString == true) + { + value = Convert.ToString(value); + } + else + { + value = Convert.ToInt64(value); + } + } + parameter.BaseParameter.CommonTempData = value; + return value; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/MemberExpressionResolve.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/MemberExpressionResolve.cs new file mode 100644 index 000000000..75ebf1956 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/MemberExpressionResolve.cs @@ -0,0 +1,869 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public class MemberExpressionResolve : BaseResolve + { + public ExpressionParameter Parameter { get; set; } + + public MemberExpressionResolve(ExpressionParameter parameter) : base(parameter) + { + ExpressionParameter baseParameter; + MemberExpression expression; + bool? isLeft; + bool isSetTempData, isValue, isValueBool, isLength, isDateValue, isHasValue, isDateDate, isMemberValue, isSingle, fieldIsBool, isSelectField, isField; + SettingParameters(parameter, out baseParameter, out expression, out isLeft, out isSetTempData, out isValue, out isValueBool, out isLength, out isDateValue, out isHasValue, out isDateDate, out isMemberValue, out isSingle, out fieldIsBool, out isSelectField, out isField); + baseParameter.ChildExpression = expression; + ProcessNavigationMemberAndUpdateExpression(ref expression, ref isValue); + if (isLength) + { + ResolveLength(parameter, isLeft, expression); + } + else if (IsDateDiff(expression)) + { + ResolveDateDiff(parameter, isLeft, expression); + } + else if (expression.Member.Name == "DayOfWeek" && expression.Type == typeof(DayOfWeek)) + { + ResolveDayOfWeek(parameter, isLeft, expression); + } + else if (isHasValue) + { + ResolveHasValue(parameter, expression); + } + else if (isDateValue) + { + ResolveDateValue(parameter, isLeft, expression); + } + else if (isValueBool) + { + ResolveValueBool(parameter, baseParameter, expression, isLeft, isSingle); + } + else if (isValue && expression.Expression != null && expression.Expression is MethodCallExpression) + { + ResolveCallValue(parameter, baseParameter, expression, isLeft, isSetTempData, isSingle); + } + else if (isValue & IsNavValue(expression)) + { + expression = expression.Expression as MemberExpression; + ResolveMemberValue(parameter, baseParameter, expression, isLeft, isSetTempData); + } + else if (isValue) + { + ResolveValue(parameter, baseParameter, expression, isLeft, isSetTempData, isSingle); + } + else if (expression.Expression != null && expression.Expression.Type == UtilConstants.DateType && expression is MemberExpression && expression.Expression is MethodCallExpression) + { + ResolveDateDateByCall(parameter, isLeft, expression); + } + else if (isDateDate) + { + ResolveDateDate(parameter, isLeft, expression); + } + else if (IsConvertMemberName(expression)) + { + ResolveConvertMemberName(parameter, expression, isLeft); + } + else if (isMemberValue) + { + ResolveMemberValue(parameter, baseParameter, expression, isLeft, isSetTempData); + } + else if (fieldIsBool && !isField && !isSelectField) + { + ResolvefieldIsBool(parameter, baseParameter, isLeft, isSetTempData, expression, isSingle); + } + else + { + ResolveDefault(parameter, baseParameter, expression, isLeft, isSetTempData, isSingle); + } + } + + private void ProcessNavigationMemberAndUpdateExpression(ref MemberExpression expression, ref bool isValue) + { + if (isValue && expression.Expression is MemberExpression childMemExp) + { + if (childMemExp.Expression is MemberExpression navMemExp) + { + if (ExpressionTool.IsNavMember(this.Context, navMemExp)) + { + expression = childMemExp; + isValue = false; + } + } + } + } + + + + #region Navigate + private static bool IsNavValue(MemberExpression expression) + { + var isDateMember = expression.Type == UtilConstants.DateType && expression.Expression is MemberExpression; + return isDateMember && + (expression.Expression as MemberExpression)?.Expression is MemberExpression; + } + private void DefaultOneToOneN(ExpressionParameter parameter, ExpressionParameter baseParameter, bool? isLeft, bool isSetTempData, OneToOneNavgateExpressionN navN) + { + var value = navN.GetMemberSql(); + SetNavigateResult(); + this.Context.SingleTableNameSubqueryShortName = navN.shorName; + if (isSetTempData) + { + baseParameter.CommonTempData = value; + } + else + { + AppendValue(parameter, isLeft, value); + } + } + + private void DefaultOneToOne(ExpressionParameter parameter, ExpressionParameter baseParameter, bool? isLeft, bool isSetTempData, OneToOneNavgateExpression nav) + { + var value = nav.GetSql(); + SetNavigateResult(); + this.Context.SingleTableNameSubqueryShortName = nav.ShorName; + if (isSetTempData) + { + baseParameter.CommonTempData = value; + } + else + { + AppendValue(parameter, isLeft, value); + } + } + + #endregion + + #region Resolve default + private void ResolveDefault(ExpressionParameter parameter, ExpressionParameter baseParameter, MemberExpression expression, bool? isLeft, bool isSetTempData, bool isSingle) + { + string fieldName = string.Empty; + switch (parameter.Context.ResolveType) + { + case ResolveExpressType.Update: + case ResolveExpressType.SelectSingle: + fieldName = GetSingleName(parameter, expression, isLeft); + if (isSetTempData) + baseParameter.CommonTempData = fieldName; + else + base.Context.Result.Append(fieldName); + break; + case ResolveExpressType.SelectMultiple: + fieldName = GetMultipleName(parameter, expression, isLeft); + if (isSetTempData) + baseParameter.CommonTempData = fieldName; + else + base.Context.Result.Append(fieldName); + break; + case ResolveExpressType.WhereSingle: + case ResolveExpressType.WhereMultiple: + ResolveWhereLogic(parameter, baseParameter, expression, isLeft, isSetTempData, isSingle); + break; + case ResolveExpressType.FieldSingle: + fieldName = GetSingleName(parameter, expression, isLeft); + var fieldIsCommonTemp = IsFieldIsCommonTemp(isSetTempData, parameter); + if (fieldIsCommonTemp) + { + baseParameter.CommonTempData = fieldName; + } + else + { + base.Context.Result.Append(fieldName); + } + break; + case ResolveExpressType.FieldMultiple: + fieldName = GetMultipleName(parameter, expression, isLeft); + var fieldIsCommonTemp2 = IsFieldIsCommonTemp(isSetTempData, parameter); + if (fieldIsCommonTemp2) + { + baseParameter.CommonTempData = fieldName; + } + else + { + base.Context.Result.Append(fieldName); + } + break; + case ResolveExpressType.ArrayMultiple: + case ResolveExpressType.ArraySingle: + fieldName = GetName(parameter, expression, isLeft, parameter.Context.ResolveType == ResolveExpressType.ArraySingle); + var fieldIsCommonTemp3 = IsFieldIsCommonTemp(isSetTempData, parameter); + if (fieldIsCommonTemp3) + { + baseParameter.CommonTempData = fieldName; + } + else + { + base.Context.Result.Append(fieldName); + } + break; + default: + break; + } + } + + private bool IsFieldIsCommonTemp(bool isSetTempData, ExpressionParameter parameter) + { + if (parameter.BaseParameter == null) + return false; + var childExpression = parameter.BaseParameter.ChildExpression.ObjToString(); + var expression = parameter.BaseParameter.CurrentExpression.ObjToString(); + var datevaluelist = UtilConstants.DateTypeStringList.Select(it => childExpression + "." + it); + return isSetTempData && datevaluelist.Contains(expression); + } + + #endregion + + #region Resolve Where + private void ResolveBoolLogic(ExpressionParameter parameter, ExpressionParameter baseParameter, MemberExpression expression, bool? isLeft, bool isSetTempData, bool isSingle) + { + string fieldName = string.Empty; + if (isSetTempData) + { + if (ExpressionTool.IsConstExpression(expression)) + { + var value = ExpressionTool.GetMemberValue(expression.Member, expression); + baseParameter.CommonTempData = value + "=1 "; + } + else + { + fieldName = GetName(parameter, expression, null, isSingle); + baseParameter.CommonTempData = fieldName + "=1 "; + } + } + else + { + if (ExpressionTool.IsConstExpression(expression)) + { + var value = ExpressionTool.GetMemberValue(expression.Member, expression); + base.AppendValue(parameter, isLeft, value + "=1 "); + } + else + { + fieldName = GetName(parameter, expression, isLeft, isSingle); + AppendMember(parameter, isLeft, fieldName + "=1 "); + } + } + } + + private void ResolveWhereLogic(ExpressionParameter parameter, ExpressionParameter baseParameter, MemberExpression expression, bool? isLeft, bool isSetTempData, bool isSingle) + { + string fieldName = string.Empty; + if (isSetTempData) + { + if (ExpressionTool.IsConstExpression(expression)) + { + var value = ExpressionTool.GetMemberValue(expression.Member, expression); + baseParameter.CommonTempData = value; + } + else + { + fieldName = GetName(parameter, expression, null, isSingle); + baseParameter.CommonTempData = fieldName; + } + } + else + { + if (ExpressionTool.IsConstExpression(expression)) + { + var value = ExpressionTool.GetMemberValue(expression.Member, expression); + base.AppendValue(parameter, isLeft, value); + } + else + { + fieldName = GetName(parameter, expression, isLeft, isSingle); + AppendMember(parameter, isLeft, fieldName); + } + } + } + #endregion + + #region Resolve special member + + private void ResolveMemberValue(ExpressionParameter parameter, ExpressionParameter baseParameter, MemberExpression expression, bool? isLeft, bool isSetTempData) + { + var nav = new OneToOneNavgateExpression(this.Context?.SugarContext?.Context, this); + nav.ExpContext = this.Context; + var navN = new OneToOneNavgateExpressionN(this.Context?.SugarContext?.Context); + if (nav.IsNavgate(expression)) + { + if (this.Context?.SugarContext?.QueryBuilder?.JoinQueryInfos != null) + { + var p = expression.Expression.ObjToString(); + var querybuilder = this.Context?.SugarContext?.QueryBuilder; + var joinInfos = querybuilder.JoinQueryInfos; + var joinInfo = joinInfos.FirstOrDefault(it => $"{querybuilder.TableShortName}.{it.ShortName.Replace("pnv_", "")}" == p); + if (joinInfo != null) + { + var columnInfo = nav.ProPertyEntity.Columns.FirstOrDefault(it => it.PropertyName == nav.MemberName); + var value = new MapperSql() { Sql = querybuilder.Builder.GetTranslationColumnName(joinInfo.ShortName) + "." + querybuilder.Builder.GetTranslationColumnName(columnInfo.DbColumnName) }; + + if (isSetTempData) + { + baseParameter.CommonTempData = value; + } + else + { + AppendValue(parameter, isLeft, value); + } + } + else + { + DefaultOneToOne(parameter, baseParameter, isLeft, isSetTempData, nav); + } + } + else + { + DefaultOneToOne(parameter, baseParameter, isLeft, isSetTempData, nav); + } + } + else if (navN.IsNavgate(expression)) + { + DefaultOneToOneN(parameter, baseParameter, isLeft, isSetTempData, navN); + } + else + { + ResolveMemberValue(parameter, baseParameter, isLeft, isSetTempData, expression); + } + } + + private void ResolveConvertMemberName(ExpressionParameter parameter, MemberExpression expression, bool? isLeft) + { + var memParameter = (expression.Expression as UnaryExpression).Operand as ParameterExpression; + var name = ExpressionTool.GetMemberName(expression); + if (this.Context.IsSingle) + { + AppendMember(parameter, isLeft, this.Context.GetTranslationColumnName(name)); + } + else + { + AppendMember(parameter, isLeft, this.Context.GetTranslationColumnName(memParameter.Name + "." + name)); + } + } + + private void ResolveDayOfWeek(ExpressionParameter parameter, bool? isLeft, MemberExpression expression) + { + var exp = expression.Expression; + var value = GetNewExpressionValue(exp); + var result = this.Context.DbMehtods.DateValue(new MethodCallExpressionModel() + { + Args = new List() { + + new MethodCallExpressionArgs(){ + MemberName=value, + MemberValue=value + }, + new MethodCallExpressionArgs(){ + MemberName=DateType.Weekday, + MemberValue=DateType.Weekday + } + } + }); ; + base.AppendMember(parameter, isLeft, result); + } + + + private void ResolveDateDiff(ExpressionParameter parameter, bool? isLeft, MemberExpression expression) + { + var binaryExp = expression.Expression as BinaryExpression; + var beginExp = binaryExp.Right; + var endExp = binaryExp.Left; + + var dateType = DateType.Day; + var begin = GetNewExpressionValue(beginExp); + var end = GetNewExpressionValue(endExp); + + foreach (var item in UtilMethods.EnumToDictionary()) + { + if (expression.Member.Name.Contains(item.Key, StringComparison.CurrentCultureIgnoreCase)) + { + dateType = item.Value; + break; + } + } + var result = this.Context.DbMehtods.DateDiff(new MethodCallExpressionModel() + { + Args = new List() { + new MethodCallExpressionArgs(){ + MemberName=dateType, + MemberValue=dateType + }, + new MethodCallExpressionArgs(){ + MemberName=begin, + MemberValue=begin + }, + new MethodCallExpressionArgs(){ + MemberName=end, + MemberValue=end + } + } + }); ; + base.AppendMember(parameter, isLeft, result); + } + private void ResolveDateDateByCall(ExpressionParameter parameter, bool? isLeft, MemberExpression expression) + { + var value = GetNewExpressionValue(expression.Expression); + if (expression.Member.Name == "Date") + { + AppendMember(parameter, isLeft, GetToDateShort(value)); + } + else + { + foreach (int myCode in Enum.GetValues(typeof(DateType))) + { + string strName = Enum.GetName(typeof(DateType), myCode);//获取名称 + if (expression.Member.Name == strName) + { + AppendMember(parameter, isLeft, this.Context.DbMehtods.MergeString(this.GetDateValue(value, (DateType)(myCode)))); + } + } + } + } + private void ResolveCallValue(ExpressionParameter parameter, ExpressionParameter baseParameter, MemberExpression expression, bool? isLeft, bool isSetTempData, bool isSingle) + { + try + { + baseParameter.ChildExpression = expression; + string fieldName = string.Empty; + if (isSetTempData) + { + var value = ExpressionTool.DynamicInvoke(expression); + baseParameter.CommonTempData = value; + } + else + { + var value = ExpressionTool.DynamicInvoke(expression); + base.AppendValue(parameter, isLeft, value); + } + } + catch + { + Check.Exception(true, "Not Support {0}", expression.ToString()); + } + } + + private MemberExpression ResolveValue(ExpressionParameter parameter, ExpressionParameter baseParameter, MemberExpression expression, bool? isLeft, bool isSetTempData, bool isSingle) + { + expression = expression.Expression as MemberExpression; + baseParameter.ChildExpression = expression; + if (UtilMethods.GetUnderType(expression.Type) == UtilConstants.BoolType && parameter.BaseExpression != null && ExpressionTool.IsLogicOperator(parameter.BaseExpression)) + { + ResolveBoolLogic(parameter, baseParameter, expression, isLeft, isSetTempData, isSingle); + } + else + { + ResolveWhereLogic(parameter, baseParameter, expression, isLeft, isSetTempData, isSingle); + } + return expression; + } + + private void ResolveValueBool(ExpressionParameter parameter, ExpressionParameter baseParameter, MemberExpression expression, bool? isLeft, bool isSingle) + { + string fieldName = GetName(parameter, expression.Expression as MemberExpression, isLeft, isSingle); + if (expression.Type == UtilConstants.BoolType && baseParameter.OperatorValue.IsNullOrEmpty()) + { + fieldName = this.Context.DbMehtods.EqualTrue(fieldName); + } + AppendMember(parameter, isLeft, fieldName); + } + + private void ResolveMemberValue(ExpressionParameter parameter, ExpressionParameter baseParameter, bool? isLeft, bool isSetTempData, MemberExpression expression) + { + if (ExpressionTool.IsOwnsOne(this.Context, expression)) + { + var column = ExpressionTool.GetOwnsOneColumnInfo(this.Context, expression); + var columnName = column.DbColumnName; + if (this.Context.IsJoin) + { + var expParameterInfo = ExpressionTool.GetParameters(expression).First(); + columnName = $"{expParameterInfo.Name}.{columnName}"; + } + columnName = this.Context.GetTranslationColumnName(columnName); + if (isSetTempData) + { + baseParameter.CommonTempData = columnName; + } + else + { + AppendMember(parameter, isLeft, columnName); + } + return; + } + var value = ExpressionTool.GetMemberValue(expression.Member, expression); + if (isSetTempData) + { + if (value is MapperSql) + { + value = (value as MapperSql).Sql; + } + baseParameter.CommonTempData = value; + } + else + { + if (parameter?.OppsiteExpression != null) + { + var exp = ExpressionTool.RemoveConvert(parameter?.OppsiteExpression); + if (exp is MemberExpression) + { + var member = (exp as MemberExpression); + var memberParent = member.Expression; + if (memberParent != null && this.Context?.SugarContext?.Context != null) + { + var entity = this.Context.SugarContext.Context.EntityMaintenance.GetEntityInfo(memberParent.Type); + var columnInfo = entity.Columns.FirstOrDefault(it => it.PropertyName == member.Member.Name); + if (columnInfo?.SqlParameterDbType is Type) + { + var type = columnInfo.SqlParameterDbType as Type; + var ParameterConverter = type.GetMethod("ParameterConverter").MakeGenericMethod(columnInfo.PropertyInfo.PropertyType); + var obj = Activator.CreateInstance(type); + var p = ParameterConverter.Invoke(obj, new object[] { value, 100 + this.ContentIndex }) as SugarParameter; + value = p.Value; + } + } + } + } + AppendValue(parameter, isLeft, value); + } + } + private void ResolvefieldIsBool(ExpressionParameter parameter, ExpressionParameter baseParameter, bool? isLeft, bool isSetTempData, MemberExpression expression, bool isSingle) + { + var fieldName = GetName(parameter, expression, isLeft, isSingle); + if (isSetTempData) + { + baseParameter.CommonTempData = fieldName; + } + else + { + fieldName = this.Context.DbMehtods.EqualTrue(fieldName.ObjToString()); + AppendMember(parameter, isLeft, fieldName); + } + } + + private void ResolveDateDate(ExpressionParameter parameter, bool? isLeft, MemberExpression expression) + { + var name = expression.Member.Name; + var oldCommonTempDate = parameter.CommonTempData; + parameter.CommonTempData = CommonTempDataType.Result; + this.Expression = expression.Expression; + this.Start(); + var isConst = parameter.CommonTempData.GetType() == UtilConstants.DateType; + if (isConst) + { + if (this.Context?.Case?.IsDateString == true) + { + AppendMember(parameter, isLeft, GetToDateShort("'" + parameter.CommonTempData.ObjToDate().Date.ToString("yyyy-MM-dd") + "'")); + } + else + { + AppendValue(parameter, isLeft, parameter.CommonTempData.ObjToDate().Date); + } + } + else + { + var GetYear = new MethodCallExpressionModel() + { + Args = new List() { + new MethodCallExpressionArgs() { IsMember=true, MemberName=parameter.CommonTempData, MemberValue=parameter.CommonTempData }, + new MethodCallExpressionArgs() { MemberName=DateType.Year, MemberValue=DateType.Year} + } + }; + if (parameter.CommonTempData is MapperSql) + { + parameter.CommonTempData = ((MapperSql)parameter.CommonTempData).Sql; + } + AppendMember(parameter, isLeft, GetToDateShort(parameter.CommonTempData.ObjToString())); + } + parameter.CommonTempData = oldCommonTempDate; + } + + private void ResolveDateValue(ExpressionParameter parameter, bool? isLeft, MemberExpression expression) + { + var name = expression.Member.Name; + var oldCommonTempDate = parameter.CommonTempData; + parameter.CommonTempData = CommonTempDataType.Result; + this.Expression = expression.Expression; + var isConst = this.Expression is ConstantExpression; + var isDateTimeNowDateStartWith = expression.ObjToString().StartsWith("DateTime.Now.Date."); + var isDateContains = expression.ObjToString().Contains(".Date."); + if (this.Expression.Type == UtilConstants.DateType && this.Expression.ToString() == "DateTime.Now") + { + this.Expression = expression; + var parameterName = base.AppendParameter(ExpressionTool.GetMemberValue(expression.Member, expression)); + base.AppendMember(parameter, isLeft, parameterName); + } + else if (isDateTimeNowDateStartWith) + { + this.Expression = expression; + var parameterName = base.AppendParameter(ExpressionTool.GetMemberValue(expression.Member, expression)); + base.AppendMember(parameter, isLeft, parameterName); + } + else if (isDateContains) + { + parameter.CommonTempData = base.GetNewExpressionValue(this.Expression); + var result = this.Context.DbMehtods.DateValue(new MethodCallExpressionModel() + { + Conext = this.Context, + Args = new List() { + new MethodCallExpressionArgs() { IsMember = !isConst, MemberName = parameter.CommonTempData, MemberValue = null }, + new MethodCallExpressionArgs() { IsMember = true, MemberName = name, MemberValue = name } + } + }); + base.AppendMember(parameter, isLeft, result); + } + else + { + this.Start(); + if (parameter.CommonTempData != null && parameter.CommonTempData is DateTime) + { + parameter.CommonTempData = base.AppendParameter(parameter.CommonTempData); + } + else if (parameter.CommonTempData != null && parameter.CommonTempData?.GetType()?.FullName == "System.DateOnly") + { + parameter.CommonTempData = base.AppendParameter(parameter.CommonTempData); + } + var result = this.Context.DbMehtods.DateValue(new MethodCallExpressionModel() + { + Conext = this.Context, + Args = new List() { + new MethodCallExpressionArgs() { IsMember = !isConst, MemberName = parameter.CommonTempData, MemberValue = null }, + new MethodCallExpressionArgs() { IsMember = true, MemberName = name, MemberValue = name } + } + }); + base.AppendMember(parameter, isLeft, result); + } + parameter.CommonTempData = oldCommonTempDate; + } + + private void ResolveHasValue(ExpressionParameter parameter, MemberExpression expression) + { + parameter.CommonTempData = CommonTempDataType.Result; + this.Expression = expression.Expression; + this.Start(); + var methodParamter = new MethodCallExpressionArgs() { IsMember = true, MemberName = parameter.CommonTempData, MemberValue = null }; + if (expression.Expression?.Type != null) + { + methodParamter.Type = UtilMethods.GetUnderType(expression.Expression?.Type); + } + var result = this.Context.DbMehtods.HasValue(new MethodCallExpressionModel() + { + Args = new List() { + methodParamter + } + }); + if (parameter.BaseExpression != null && ExpressionTool.IsLogicOperator(parameter.BaseExpression) && parameter.IsLeft == true) + { + if (base.Context.Result.Contains(ExpressionConst.FormatSymbol)) + { + base.Context.Result.Replace(ExpressionConst.FormatSymbol, ""); + } + this.Context.Result.Append(result + " " + ExpressionTool.GetOperator(parameter.BaseExpression.NodeType) + " "); + } + else + { + this.Context.Result.Append(result); + } + parameter.CommonTempData = null; + } + + private void ResolveLength(ExpressionParameter parameter, bool? isLeft, MemberExpression expression) + { + var ps = ExpressionTool.GetParameters(expression); + if (expression.Expression != null && ps.Count == 0) + { + var p = base.AppendParameter(ExpressionTool.DynamicInvoke(expression.Expression)); + var methodParamter2 = new MethodCallExpressionArgs() { IsMember = true, MemberName = p, MemberValue = null }; + var result2 = this.Context.DbMehtods.Length(new MethodCallExpressionModel() + { + Args = new List() { + methodParamter2 + } + }); + base.AppendMember(parameter, isLeft, result2); + return; + } + if (parameter.Context.ResolveType == ResolveExpressType.FieldSingle) + { + parameter.Context.ResolveType = ResolveExpressType.WhereSingle; + } + if (parameter.Context.ResolveType == ResolveExpressType.FieldMultiple) + { + parameter.Context.ResolveType = ResolveExpressType.WhereMultiple; + } + var oldCommonTempDate = parameter.CommonTempData; + parameter.CommonTempData = CommonTempDataType.Result; + this.Expression = expression.Expression; + var isConst = this.Expression is ConstantExpression; + this.Start(); + var methodParamter = new MethodCallExpressionArgs() { IsMember = !isConst, MemberName = parameter.CommonTempData, MemberValue = null }; + var result = this.Context.DbMehtods.Length(new MethodCallExpressionModel() + { + Args = new List() { + methodParamter + } + }); + base.AppendMember(parameter, isLeft, result); + parameter.CommonTempData = oldCommonTempDate; + } + #endregion + + #region Helper + private static bool IsConvertMemberName(MemberExpression expression) + { + return expression.Expression is UnaryExpression && (expression.Expression as UnaryExpression).Operand is ParameterExpression; + } + + private static bool IsDateDiff(MemberExpression expression) + { + if (expression.Expression != null && + expression.Expression is BinaryExpression) + { + var binExp = (expression.Expression as BinaryExpression); + var nodeType = binExp.NodeType; + if (nodeType == ExpressionType.Subtract && binExp.Left.Type == UtilConstants.DateType && binExp.Right.Type == UtilConstants.DateType) + { + return true; + } + } + return + expression.Expression != null && + expression.Expression is BinaryExpression && + expression.Expression.Type == UtilConstants.TimeSpanType && + expression.Member.Name.StartsWith("Total") && + expression.Member.Name.EndsWith('s') + ; + } + + private string AppendMember(ExpressionParameter parameter, bool? isLeft, string fieldName) + { + if (parameter.BaseExpression is BinaryExpression || (parameter.BaseParameter.CommonTempData?.Equals(CommonTempDataType.Append) == true)) + { + fieldName = string.Format(" {0} ", fieldName); + if (isLeft == true) + { + fieldName += ExpressionConst.ExpressionReplace + parameter.BaseParameter.Index; + } + if (base.Context.Result.Contains(ExpressionConst.FormatSymbol)) + { + base.Context.Result.Replace(ExpressionConst.FormatSymbol, fieldName); + } + else + { + base.Context.Result.Append(fieldName); + } + } + else + { + base.Context.Result.Append(fieldName); + } + + return fieldName; + } + + private string GetName(ExpressionParameter parameter, MemberExpression expression, bool? isLeft, bool isSingle) + { + if (isSingle) + { + return GetSingleName(parameter, expression, IsLeft); + } + else + { + return GetMultipleName(parameter, expression, IsLeft); + } + } + + private string GetMultipleName(ExpressionParameter parameter, MemberExpression expression, bool? isLeft) + { + string shortName = expression.Expression.ToString(); + string fieldName = expression.Member.Name; + fieldName = this.Context.GetDbColumnName(expression.Expression.Type.Name, fieldName); + if (UtilMethods.GetMoreSetting(this.Context).IsCorrectErrorSqlParameterName) + { + fieldName = Context.GetTranslationColumnName(shortName) + UtilConstants.Dot + Context.GetTranslationColumnName(fieldName); + } + else + { + fieldName = Context.GetTranslationColumnName(shortName + UtilConstants.Dot + fieldName); + } + return fieldName; + } + + private string GetSingleName(ExpressionParameter parameter, MemberExpression expression, bool? isLeft) + { + string fieldName = expression.Member.Name; + fieldName = this.Context.GetDbColumnName(expression.Expression.Type.Name, fieldName); + + var isSpace = fieldName.Contains(UtilConstants.Space); + var guid = string.Empty; + if (isSpace) + { + guid = SnowFlakeSingle.Instance.NextId().ToString(); + fieldName = fieldName.Replace(UtilConstants.Space, guid); + } + fieldName = Context.GetTranslationColumnName(fieldName); + if (this.Context?.SugarContext?.Context?.CurrentConnectionConfig?.MoreSettings?.IsCorrectErrorSqlParameterName == true && fieldName?.Contains(ExpressionConst.LeftParenthesis) == true) + { + fieldName = Context.GetTranslationText(fieldName); + } + if (isSpace) + { + fieldName = fieldName.Replace(guid, UtilConstants.Space); + } + return fieldName; + } + + private string GetDateValue(object value, DateType type) + { + var pars = new MethodCallExpressionModel() + { + Conext = this.Context, + Args = new List() { + new MethodCallExpressionArgs() { IsMember=true, MemberName=value, MemberValue=value }, + new MethodCallExpressionArgs() { MemberName=type, MemberValue=type} + } + }; + return this.Context.DbMehtods.DateValue(pars); + } + + private string GetToDateShort(string value) + { + var pars = new MethodCallExpressionModel() + { + Args = new List() { + new MethodCallExpressionArgs() { MemberName=value, MemberValue=value }, + } + }; + return this.Context.DbMehtods.ToDateShort(pars); + } + + private void SettingParameters(ExpressionParameter parameter, out ExpressionParameter baseParameter, out MemberExpression expression, out bool? isLeft, out bool isSetTempData, out bool isValue, out bool isValueBool, out bool isLength, out bool isDateValue, out bool isHasValue, out bool isDateDate, out bool isMemberValue, out bool isSingle, out bool fieldIsBool, out bool isSelectField, out bool isField) + { + baseParameter = parameter.BaseParameter; + expression = base.Expression as MemberExpression; + var childExpression = expression.Expression as MemberExpression; + var memberName = expression.Member.Name; + var childIsMember = childExpression != null; + var isRoot = parameter.BaseExpression == null; + isLeft = parameter.IsLeft; + isSetTempData = parameter.IsSetTempData; + isValue = memberName == "Value" && expression.Member.DeclaringType.Name == "Nullable`1"; + var isBool = expression.Type == UtilConstants.BoolType; + isValueBool = isValue && isBool && isRoot; + isLength = memberName == "Length" && childIsMember && childExpression.Type == UtilConstants.StringType; + isDateValue = memberName.IsIn(Enum.GetNames(typeof(DateType))) && (childIsMember && childExpression.Type == UtilConstants.DateType); + if (isDateValue == false && childIsMember && childExpression?.Type?.FullName == "System.DateOnly" && memberName.IsIn(Enum.GetNames(typeof(DateType)))) + { + isDateValue = true; + } + var isLogicOperator = ExpressionTool.IsLogicOperator(baseParameter.OperatorValue) || baseParameter.OperatorValue.IsNullOrEmpty(); + isHasValue = isLogicOperator && memberName == "HasValue" && expression.Expression != null && expression.NodeType == ExpressionType.MemberAccess; + isDateDate = memberName == "Date" && expression.Expression.Type == UtilConstants.DateType; + isMemberValue = expression.Expression != null && expression.Expression.NodeType != ExpressionType.Parameter && !isValueBool; + isSingle = parameter.Context.ResolveType.IsIn(ResolveExpressType.WhereSingle, ResolveExpressType.SelectSingle, ResolveExpressType.FieldSingle, ResolveExpressType.ArraySingle); + fieldIsBool = isBool && isLogicOperator && (parameter.BaseParameter == null || !(parameter.BaseParameter.CurrentExpression is MemberInitExpression || parameter.BaseParameter.CurrentExpression is NewExpression)); + var isSelect = this.Context.ResolveType.IsIn(ResolveExpressType.SelectSingle, ResolveExpressType.SelectMultiple); + isSelectField = isSelect && isRoot; + isField = this.Context.ResolveType.IsIn(ResolveExpressType.FieldSingle, ResolveExpressType.FieldMultiple); + } + + #endregion + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/MemberInitExpressionResolve.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/MemberInitExpressionResolve.cs new file mode 100644 index 000000000..d304ffb45 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/MemberInitExpressionResolve.cs @@ -0,0 +1,385 @@ +using System.Linq.Expressions; +using System.Reflection; +namespace SqlSugar +{ + public class MemberInitExpressionResolve : BaseResolve + { + public MemberInitExpressionResolve(ExpressionParameter parameter) : base(parameter) + { + var expression = base.Expression as MemberInitExpression; + switch (parameter.Context.ResolveType) + { + case ResolveExpressType.WhereSingle: + break; + case ResolveExpressType.WhereMultiple: + break; + case ResolveExpressType.ArraySingle: + case ResolveExpressType.SelectSingle: + Select(expression, parameter, true); + break; + case ResolveExpressType.SelectMultiple: + Select(expression, parameter, false); + break; + case ResolveExpressType.FieldSingle: + break; + case ResolveExpressType.FieldMultiple: + break; + case ResolveExpressType.Update: + Update(expression, parameter); + break; + default: + break; + } + } + + private void Update(MemberInitExpression expression, ExpressionParameter parameter) + { + int i = 0; + var entityMaintenance = this.Context?.SugarContext?.Context?.EntityMaintenance; + foreach (MemberBinding binding in expression.Bindings) + { + ++i; + if (binding.BindingType != MemberBindingType.Assignment) + { + throw new NotSupportedException(); + } + MemberAssignment memberAssignment = (MemberAssignment)binding; + var type = expression.Type; + var memberName = this.Context.GetDbColumnName(type.Name, memberAssignment.Member.Name); + var item = memberAssignment.Expression; + item = ExpressionTool.RemoveConvert(item); + //Column IsJson Handler + if (memberAssignment.Member.CustomAttributes != null) + { + var customAttribute = memberAssignment.Member.GetCustomAttribute(); + + if (customAttribute?.IsJson ?? false) + { + var paramterValue = ExpressionTool.DynamicInvoke(item); + var parameterName = AppendParameter(new SerializeService().SerializeObject(paramterValue)); + var parameterObj = this.Context.Parameters.FirstOrDefault(it => it.ParameterName == parameterName); + if (parameterObj != null) + { + parameterObj.IsJson = true; + } + this.Context.Result.Append(base.Context.GetEqString(memberName, parameterName)); + + continue; + } + } + + if ((item is MemberExpression) && ((MemberExpression)item).Expression == null) + { + var paramterValue = ExpressionTool.DynamicInvoke(item); + string parameterName = AppendParameter(paramterValue); + this.Context.Result.Append(base.Context.GetEqString(memberName, parameterName)); + } + else if (entityMaintenance?.GetEntityInfo(type).Columns.Any(it => it.SqlParameterDbType is Type + && it.PropertyInfo.Name == memberName) == true + && IsConstNew(ExpressionTool.RemoveConvertThanOne(item))) + { + var columnInfo = entityMaintenance.GetEntityInfo(expression.Type).Columns.First(it => it.SqlParameterDbType is Type && it.PropertyInfo.Name == memberName); + var columnDbType = columnInfo.SqlParameterDbType as Type; + var ParameterConverter = columnDbType.GetMethod("ParameterConverter").MakeGenericMethod(columnInfo.PropertyInfo.PropertyType); + var obj = Activator.CreateInstance(columnDbType); + var value = ExpressionTool.DynamicInvoke(item); + var p = ParameterConverter.Invoke(obj, new object[] { value, 100 + i }) as SugarParameter; + parameter.Context.Result.Append(base.Context.GetEqString(memberName, p.ParameterName)); + this.Context.Parameters.Add(p); + } + else if (IsNotMember(item)) + { + if (base.Context.Result.IsLockCurrentParameter == false) + { + base.Context.Result.CurrentParameter = parameter; + base.Context.Result.IsLockCurrentParameter = true; + parameter.IsAppendTempDate(); + base.Expression = item; + base.Expression = (item as UnaryExpression).Operand; + base.Start(); + parameter.IsAppendResult(); + var result = this.Context.DbMehtods.IIF(new MethodCallExpressionModel() + { + Args = new List() { + new MethodCallExpressionArgs(){ IsMember=true, MemberName=parameter.CommonTempData.ObjToString()+"=1",Type=UtilConstants.BoolType }, + new MethodCallExpressionArgs(){ IsMember=true,MemberName=AppendParameter(0) }, + new MethodCallExpressionArgs(){ IsMember=true, MemberName=AppendParameter(1) } + } + }); + parameter.Context.Result.Append(base.Context.GetEqString(memberName, result)); + base.Context.Result.CurrentParameter = null; + } + } + else if (IsNotParameter(item)) + { + try + { + parameter.Context.Result.Append(base.Context.GetEqString(memberName, AppendParameter(ExpressionTool.DynamicInvoke(item).ObjToBool()))); + } + catch + { + throw new NotSupportedException(item.ToString()); + } + } + else if (IsMethod(item)) + { + if (item is UnaryExpression) + item = (item as UnaryExpression).Operand; + var callMethod = item as MethodCallExpression; + if (MethodTimeMapping.Any(it => it.Key == callMethod.Method.Name) || MethodMapping.Any(it => it.Key == callMethod.Method.Name) || IsExtMethod(callMethod.Method.Name) || IsSubMethod(callMethod) || callMethod.Method.DeclaringType.FullName.StartsWith(UtilConstants.AssemblyName + UtilConstants.Dot)) + { + MethodCall(parameter, memberName, item); + } + else + { + var paramterValue = ExpressionTool.DynamicInvoke(item); + string parameterName = AppendParameter(paramterValue); + this.Context.Result.Append(base.Context.GetEqString(memberName, parameterName)); + } + } + else if (IsConst(item) && IsConvert(item) && UtilMethods.IsNullable(item.Type) && UtilMethods.GetUnderType(item.Type) == UtilConstants.BoolType) + { + item = (item as UnaryExpression).Operand; + parameter.Context.Result.Append(base.Context.GetEqString(memberName, GetNewExpressionValue(item))); + } + else if (IsConst(item)) + { + base.Expression = ExpressionTool.RemoveConvertThanOne(item); + base.Start(); + string parameterName = this.Context.SqlParameterKeyWord + ExpressionConst.Const + this.Context.ParameterIndex; + parameter.Context.Result.Append(base.Context.GetEqString(memberName, parameterName)); + var addItem = new SugarParameter(parameterName, parameter.CommonTempData); + if (addItem.Value == null && item.Type?.Name == "Nullable`1") + { + var genericType = item.Type?.GenericTypeArguments?.FirstOrDefault(); + if (genericType != null) + { + addItem.DbType = new SugarParameter(parameterName, UtilMethods.GetDefaultValue(genericType)).DbType; + } + } + ConvertParameterTypeByType(item, addItem); + + this.Context.Parameters.Add(addItem); + this.Context.ParameterIndex++; + } + else if (item is MemberExpression) + { + if (base.Context.Result.IsLockCurrentParameter == false) + { + base.Context.Result.CurrentParameter = parameter; + base.Context.Result.IsLockCurrentParameter = true; + parameter.IsAppendTempDate(); + base.Expression = item; + base.Start(); + parameter.IsAppendResult(); + parameter.Context.Result.Append(base.Context.GetEqString(memberName, parameter.CommonTempData.ObjToString().Replace(",", UtilConstants.ReplaceCommaKey))); + + if (this.Context.Parameters != null) + { + var memberParameter = this.Context.Parameters?.FirstOrDefault(it => it.Value == null && it.ParameterName == parameter.CommonTempData.ObjToString()); + if (memberParameter != null) + { + ConvertParameterTypeByType(item, memberParameter); + } + } + + base.Context.Result.CurrentParameter = null; + } + } + else if (item is BinaryExpression) + { + var result = GetNewExpressionValue(item); + if (result.HasValue()) + { + result = result.Replace(",", UtilConstants.ReplaceCommaKey); + } + this.Context.Result.Append(base.Context.GetEqString(memberName, result)); + } + else if (item is MemberInitExpression) + { + try + { + var value = ExpressionTool.DynamicInvoke(item); + var parameterName = AppendParameter(value); + parameter.Context.Result.Append(base.Context.GetEqString(memberName, parameterName)); + } + catch (Exception ex) + { + throw new NotSupportedException("Not Supported " + item.ToString() + " " + ex.Message); + } + } + else if (item is NewExpression) + { + try + { + var value = ExpressionTool.DynamicInvoke(item); + var parameterName = AppendParameter(value); + parameter.Context.Result.Append(base.Context.GetEqString(memberName, parameterName)); + } + catch (Exception ex) + { + throw new NotSupportedException("Not Supported " + item.ToString() + " " + ex.Message); + } + } + else if (item is ConditionalExpression) + { + var result = GetNewExpressionValue(item); + this.Context.Result.Append(base.Context.GetEqString(memberName, result)); + } + } + } + + private static void ConvertParameterTypeByType(Expression item, SugarParameter addItem) + { + var dataType = UtilMethods.GetUnderType(item.Type); + if (addItem.Value == null && dataType == UtilConstants.DateType) + { + addItem.DbType = System.Data.DbType.Date; + } + if (addItem.Value == null && dataType.IsIn(UtilConstants.ULongType, UtilConstants.UIntType, UtilConstants.FloatType, UtilConstants.IntType, UtilConstants.LongType, UtilConstants.DecType, UtilConstants.DobType)) + { + addItem.DbType = System.Data.DbType.Int32; + } + if (addItem.Value == null && dataType == UtilConstants.BoolType) + { + addItem.DbType = System.Data.DbType.Boolean; + } + } + + private static bool IsConst(Expression item) + { + return item is UnaryExpression || item.NodeType == ExpressionType.Constant || (item is MemberExpression) && ((MemberExpression)item).Expression.NodeType == ExpressionType.Constant; + } + private static bool IsConstNew(Expression item) + { + if (item != null) + { + if (ExpressionTool.GetParameters(item).Count == 0) + { + return true; + } + } + return item is UnaryExpression || item.NodeType == ExpressionType.Constant || (item is MemberExpression) && ((MemberExpression)item).Expression.NodeType == ExpressionType.Constant; + } + + private static bool IsMethod(Expression item) + { + return item is MethodCallExpression || (item is UnaryExpression && (item as UnaryExpression).Operand is MethodCallExpression); + } + + private void MethodCall(ExpressionParameter parameter, string memberName, Expression item) + { + if (IsSubMethod(item as MethodCallExpression)) + { + UtilMethods.GetOldValue(parameter.CommonTempData, () => + { + parameter.CommonTempData = CommonTempDataType.Result; + base.Expression = item; + base.Start(); + var subSql = base.Context.GetEqString(memberName, parameter.CommonTempData.ObjToString()); + var isSubJoin = subSql.Contains(" JOIN ") && subSql.Contains(" ON "); + if (subSql.Contains(',')) + { + subSql = subSql.Replace(",", UtilConstants.ReplaceCommaKey); + } + if (ResolveExpressType.Update == this.Context.ResolveType) + { + string name = this.Context.GetTranslationTableName(parameter.CurrentExpression.Type.Name, true); + if (name.Contains('.')) + { + + } + else if (isSubJoin) + { + var shortName = (base.BaseParameter.BaseParameter.CurrentExpression as LambdaExpression).Parameters[0].Name; + subSql = subSql.Replace(this.Context.GetTranslationColumnName(shortName), name); + } + else + { + var p = (base.BaseParameter?.BaseParameter?.CurrentExpression as LambdaExpression)?.Parameters[0].Name; + subSql = subSql.Replace(this.Context.SqlTranslationLeft + p + this.Context.SqlTranslationRight + ".", name + "."); + subSql = subSql.Replace(this.Context.SqlTranslationLeft + p.ToUpper() + this.Context.SqlTranslationRight + ".", name + "."); + subSql = subSql.Replace(this.Context.SqlTranslationLeft + p.ToLower() + this.Context.SqlTranslationRight + ".", name + "."); + } + } + parameter.Context.Result.Append(subSql); + }); + } + else + { + base.Expression = item; + base.Start(); + parameter.Context.Result.Append(base.Context.GetEqString(memberName, parameter.CommonTempData.ObjToString().Replace(",", UtilConstants.ReplaceCommaKey))); + } + } + + private void Select(MemberInitExpression expression, ExpressionParameter parameter, bool isSingle) + { + foreach (MemberBinding binding in expression.Bindings) + { + if (binding.BindingType != MemberBindingType.Assignment) + { + throw new NotSupportedException(); + } + MemberAssignment memberAssignment = (MemberAssignment)binding; + var memberName = memberAssignment.Member.Name; + if (this.Context?.SugarContext?.QueryBuilder?.AppendNavInfo?.MappingNavProperties?.ContainsKey(memberName) == true) + { + continue; + } + var item = memberAssignment.Expression; + if (item.Type.IsClass() && item is MemberExpression && (item as MemberExpression).Expression is ParameterExpression) + { + var rootType = ((item as MemberExpression).Expression as ParameterExpression).Type; + if (this.Context.SugarContext != null) + { + var navColumn = this.Context.SugarContext.Context.EntityMaintenance.GetEntityInfo(rootType) + .Columns.FirstOrDefault(x => x.PropertyName == memberName); + if (navColumn?.Navigat != null) + { + break; + } + } + } + if (IsNullable(item) && item is UnaryExpression) + { + var memtype = ExpressionTool.GetMemberInfoType(memberAssignment.Member); + if (IsNullable(memtype) && UtilMethods.GetUnderType(memtype) == UtilMethods.GetUnderType(item.Type)) + { + item = (item as UnaryExpression).Operand; + } + } + ResolveNewExpressions(parameter, item, memberName); + } + } + + private static bool IsNullable(Type memtype) + { + return memtype.Name == "Nullable`1"; + } + + private static bool IsNullable(Expression item) + { + return item.Type.Name == "Nullable`1"; + } + + //private bool IsSubMethod(MethodCallExpression express) + //{ + // return SubTools.SubItemsConst.Any(it =>express.Object != null && express.Object.Type.Name == "Subqueryable`1"); + //} + private bool IsExtMethod(string methodName) + { + if (this.Context.SqlFuncServices == null) return false; + return this.Context.SqlFuncServices.Select(it => it.UniqueMethodName).Contains(methodName); + } + private bool CheckMethod(MethodCallExpression expression) + { + if (IsExtMethod(expression.Method.Name)) + return true; + if (expression.Method.ReflectedType().FullName != ExpressionConst.SqlFuncFullName) + return false; + else + return true; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/MemberNewExpressionResolve.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/MemberNewExpressionResolve.cs new file mode 100644 index 000000000..081e6d7ff --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/MemberNewExpressionResolve.cs @@ -0,0 +1,14 @@ +using System.Linq.Expressions; +namespace SqlSugar +{ + public class MemberNewExpressionResolve : BaseResolve + { + public MemberNewExpressionResolve(ExpressionParameter parameter) : base(parameter) + { + var expression = base.Expression as MemberExpression; + var isLeft = parameter.IsLeft; + object value = null; + value = ExpressionTool.DynamicInvoke(expression); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/MemberNoExpressionResolve.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/MemberNoExpressionResolve.cs new file mode 100644 index 000000000..b6ee7fea4 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/MemberNoExpressionResolve.cs @@ -0,0 +1,61 @@ +using System.Linq.Expressions; +namespace SqlSugar +{ + public class MemberNoExpressionResolve : BaseResolve + { + public MemberNoExpressionResolve(ExpressionParameter parameter) : base(parameter) + { + var expression = base.Expression as MemberExpression; + var isLeft = parameter.IsLeft; + var isField = expression.Member is System.Reflection.FieldInfo; + var isProperty = expression.Member is System.Reflection.PropertyInfo; + var baseParameter = parameter.BaseParameter; + var isSetTempData = baseParameter.CommonTempData.HasValue() && baseParameter.CommonTempData.Equals(CommonTempDataType.Result); + object value = GetValue(expression, isField, isProperty); + switch (base.Context.ResolveType) + { + case ResolveExpressType.WhereSingle: + case ResolveExpressType.WhereMultiple: + case ResolveExpressType.SelectSingle: + case ResolveExpressType.SelectMultiple: + case ResolveExpressType.Update: + Update(parameter, isLeft, baseParameter, isSetTempData, value); + break; + case ResolveExpressType.FieldSingle: + break; + case ResolveExpressType.FieldMultiple: + break; + default: + break; + } + } + + private void Update(ExpressionParameter parameter, bool? isLeft, ExpressionParameter baseParameter, bool isSetTempData, object value) + { + if (isSetTempData) + { + baseParameter.CommonTempData = value; + } + else + { + AppendValue(parameter, isLeft, value); + } + } + + private static object GetValue(MemberExpression expression, bool isField, bool isProperty) + { + object value = null; + if (isField) + { + value = ExpressionTool.GetFiledValue(expression); + } + else if (isProperty) + { + value = ExpressionTool.GetPropertyValue(expression); + } + + return value; + } + } + +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/MethodCallExpressionResolve.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/MethodCallExpressionResolve.cs new file mode 100644 index 000000000..fa40e16c4 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/MethodCallExpressionResolve.cs @@ -0,0 +1,339 @@ +using SqlSugar; + +using System.Linq.Expressions; + +namespace SqlSugar +{ + public partial class MethodCallExpressionResolve : BaseResolve + { + int contextIndex = 0; + + public MethodCallExpressionResolve(ExpressionParameter parameter) : base(parameter) + { + contextIndex = this.Context.Index; + var express = base.Expression as MethodCallExpression; + if (express == null) return; + var isLeft = parameter.IsLeft; + string methodName = express.Method.Name; + var isValidNativeMethod = IsValidNativeMethod(express, methodName); + List appendArgs = null; + if (MethodTimeMapping.TryGetValue(methodName, out DateType dateType)) + { + appendArgs = new List(); + string paramterName = this.Context.SqlParameterKeyWord + ExpressionConst.Const + this.Context.ParameterIndex; + appendArgs.Add(new MethodCallExpressionArgs() { IsMember = false, MemberName = paramterName, MemberValue = dateType }); + this.Context.Parameters.Add(new SugarParameter(paramterName, dateType.ToString())); + this.Context.ParameterIndex++; + methodName = "DateAdd"; + isValidNativeMethod = true; + } + else if (methodName == "get_Item") + { + string paramterName = this.Context.SqlParameterKeyWord + ExpressionConst.Const + this.Context.ParameterIndex; + this.Context.Parameters.Add(new SugarParameter(paramterName, ExpressionTool.DynamicInvoke(express))); + this.Context.Result.Append(string.Format(" {0} ", paramterName)); + this.Context.ParameterIndex++; + return; + } + else if (methodName == "NewGuid") + { + this.Context.Result.Append(this.Context.DbMehtods.NewUid(null)); + return; + } + else if (methodName == "GetConfigValue") + { + GetConfigValue(express, parameter); + return; + } + else if (IsSubMethod(express, methodName)) + { + //Check.Exception(!(parameter.BaseExpression is BinaryExpression), "Current expressions are not supported"); + SubResolve subResolve = new SubResolve(express, this.Context, parameter.OppsiteExpression); + var appendSql = subResolve.GetSql(); + if (this.Context.ResolveType.IsIn(ResolveExpressType.SelectMultiple, ResolveExpressType.SelectSingle) || (parameter.BaseParameter != null && parameter.BaseParameter.CommonTempData?.Equals(CommonTempDataType.Result) == true)) + { + parameter.BaseParameter.CommonTempData = appendSql; + } + else + { + base.AppendValue(parameter, isLeft, appendSql); + } + return; + } + else if (IsIfElse(express, methodName)) + { + CaseWhenResolve caseResole = new CaseWhenResolve(express, this.Context, parameter.OppsiteExpression); + var appendSql = caseResole.GetSql(); + var isRoot = contextIndex == 2 && parameter.BaseExpression == null; + if (isRoot || (parameter.BaseExpression != null && ExpressionTool.IsLogicOperator(parameter.BaseExpression))) + { + appendSql = appendSql + "=1 "; + } + if (this.Context.ResolveType.IsIn(ResolveExpressType.SelectMultiple, ResolveExpressType.SelectSingle, ResolveExpressType.Update)) + { + parameter.BaseParameter.CommonTempData = appendSql; + } + else + { + base.AppendValue(parameter, isLeft, appendSql); + } + return; + } + if (IsContainsArray(express, methodName, isValidNativeMethod)) + { + methodName = "ContainsArray"; + isValidNativeMethod = true; + } + if (isValidNativeMethod) + { + NativeExtensionMethod(parameter, express, isLeft, MethodMapping[methodName], appendArgs); + } + else + { + SqlFuncMethod(parameter, express, isLeft); + } + } + + private void NativeExtensionMethod(ExpressionParameter parameter, MethodCallExpression express, bool? isLeft, string name, List appendArgs = null) + { + var method = express.Method; + var args = express.Arguments.Cast().ToList(); + MethodCallExpressionModel model = new MethodCallExpressionModel(); + model.Name = name; + model.Args = new List(); + switch (this.Context.ResolveType) + { + case ResolveExpressType.WhereSingle: + case ResolveExpressType.WhereMultiple: + if (express.Object != null) + args.Insert(0, express.Object); + Where(parameter, isLeft, name, args, model, appendArgs); + break; + case ResolveExpressType.SelectSingle: + case ResolveExpressType.SelectMultiple: + case ResolveExpressType.Update: + if (express.Object != null) + args.Insert(0, express.Object); + Select(parameter, isLeft, name, args, model, appendArgs); + break; + case ResolveExpressType.FieldSingle: + case ResolveExpressType.FieldMultiple: + if (express.Method.Name == "ToString" && express.Object != null && express.Object?.Type == UtilConstants.DateType) + { + var format = (args[0] as ConstantExpression)?.Value + ""; + if (string.IsNullOrEmpty(format) && args[0] is MemberExpression) + { + format = ExpressionTool.GetExpressionValue(args[0]) + ""; + } + var value = GetNewExpressionValue(express.Object); + var dateString2 = this.Context.DbMehtods.GetDateString(value, format); + if (IsSqlServerModel()) + { + dateString2 = string.Format("FORMAT({0},'{1}','en-US')", value, format); + } + if (dateString2 == null) + { + var dateString = GeDateFormat(format, value); + base.AppendValue(parameter, isLeft, dateString); + } + else + { + base.AppendValue(parameter, isLeft, dateString2); + } + } + else + { + var value = GetNewExpressionValue(express, this.Context.IsJoin ? ResolveExpressType.WhereMultiple : ResolveExpressType.WhereSingle); + base.AppendValue(parameter, isLeft, value); + } + break; + default: + break; + } + } + protected void SqlFuncMethod(ExpressionParameter parameter, MethodCallExpression express, bool? isLeft) + { + if (!CheckMethod(express)) + { + CusMethod(parameter, express, isLeft); + } + else + { + var method = express.Method; + string name = method.Name; + if (name == "Any" && ExpressionTool.IsVariable(express.Arguments[0])) + { + name = "ListAny"; + } + else if (name == "All" && ExpressionTool.IsVariable(express.Arguments[0])) + { + name = "ListAll"; + } + else if (name == "IndexOf") + { + name = "CharIndexNew"; + } + var args = express.Arguments.Cast().ToList(); + MethodCallExpressionModel model = new MethodCallExpressionModel(); + model.Args = new List(); + switch (this.Context.ResolveType) + { + case ResolveExpressType.WhereSingle: + case ResolveExpressType.WhereMultiple: + Check.Exception(name == "GetSelfAndAutoFill", "SqlFunc.GetSelfAndAutoFill can only be used in Select."); + if (name == "CharIndexNew" && args.Count == 1) + { + args.Insert(0, express.Object); + } + Where(parameter, isLeft, name, args, model); + break; + case ResolveExpressType.SelectSingle: + case ResolveExpressType.SelectMultiple: + case ResolveExpressType.Update: + Select(parameter, isLeft, name, args, model); + break; + case ResolveExpressType.FieldSingle: + case ResolveExpressType.FieldMultiple: + Field(parameter, isLeft, name, args, model); + break; + default: + break; + } + } + } + + + protected void Field(ExpressionParameter parameter, bool? isLeft, string name, IEnumerable args, MethodCallExpressionModel model, List appendArgs = null) + { + if (this.Context.ResolveType == ResolveExpressType.FieldSingle) + { + this.Context.ResolveType = ResolveExpressType.WhereSingle; + } + else + { + this.Context.ResolveType = ResolveExpressType.WhereMultiple; + } + Where(parameter, isLeft, name, args, model); + } + protected void Select(ExpressionParameter parameter, bool? isLeft, string name, IEnumerable args, MethodCallExpressionModel model, List appendArgs = null) + { + if (name.IsIn("GetSelfAndAutoFill", "SelectAll")) + { + var memberValue = (args.First() as MemberExpression)?.Expression?.ToString(); + if (memberValue == null && args.First() is ParameterExpression) + { + memberValue = (args.First() as ParameterExpression).Type.GetProperties().First().Name; + } + var data = new MethodCallExpressionArgs() { MemberValue = memberValue, IsMember = true, MemberName = memberValue }; + model.Args.Add(data); + if (args.Count() == 2) + { + data.MemberName = (args.Last()).ToString().Replace("\"", ""); + data.MemberValue = "."; + } + } + else + { + foreach (var item in args) + { + if (name == "IIF" && item == args.First() && item is MemberExpression) + { + Expression trueValue = Expression.Constant(true); + var newItem = ExpressionBuilderHelper.CreateExpression(item, trueValue, ExpressionType.Equal); + var member = (item as MemberExpression); + if (member.Member.Name == "HasValue") + { + newItem = ExpressionBuilderHelper.CreateExpression(member.Expression, Expression.Constant(null), ExpressionType.NotEqual); + } + AppendItem(parameter, name, new List() { newItem }, model, newItem); + } + else + { + AppendItem(parameter, name, args, model, item); + } + } + if (appendArgs != null) + { + model.Args.AddRange(appendArgs); + } + } + if (parameter.BaseParameter.BaseParameter.BaseParameter == null) + { + this.Context.Result.Append(GetMethodValue(name, model)); + } + else + { + parameter.BaseParameter.CommonTempData = GetMethodValue(name, model); + } + } + protected void Where(ExpressionParameter parameter, bool? isLeft, string name, IEnumerable args, MethodCallExpressionModel model, List appendArgs = null) + { + foreach (var item in args) + { + var expItem = item; + if (name == "IIF" && item is UnaryExpression) + { + expItem = ExpressionTool.RemoveConvert(expItem); + } + else if (item is UnaryExpression) + { + expItem = (item as UnaryExpression).Operand; + } + else if (item is MethodCallExpression callExpression && callExpression.Method.Name == "op_Implicit") + { + expItem = callExpression.Arguments[0]; + } + AppendItem(parameter, name, args, model, expItem); + } + if (appendArgs != null) + { + model.Args.AddRange(appendArgs); + } + var methodValue = GetMethodValue(name, model); + if (parameter.BaseExpression is BinaryExpression && parameter.OppsiteExpression.Type == UtilConstants.BoolType && name == "HasValue" && !(parameter.OppsiteExpression is BinaryExpression) && !(parameter.OppsiteExpression is MethodCallExpression && parameter.OppsiteExpression.Type == UtilConstants.BoolType)) + { + methodValue = packIfElse(methodValue); + } + if (parameter.OppsiteExpression != null && name == "IsNullOrEmpty" && parameter.OppsiteExpression.Type == UtilConstants.BoolType && parameter.OppsiteExpression is ConstantExpression) + { + methodValue = packIfElse(methodValue); + } + var isRoot = contextIndex == 2; + if (isRoot && parameter.BaseExpression == null && this.Context.ResolveType.IsIn(ResolveExpressType.WhereMultiple, ResolveExpressType.WhereSingle) && (parameter.CurrentExpression is MethodCallExpression) && ((parameter.CurrentExpression as MethodCallExpression).Method.Name.IsIn("ToBool", "ToBoolean"))) + { + methodValue = methodValue + "=1 "; + ; + } + if (isRoot && parameter.BaseExpression == null && this.Context.ResolveType.IsIn(ResolveExpressType.WhereMultiple, ResolveExpressType.WhereSingle) && (parameter.CurrentExpression is ConditionalExpression) && ((parameter.CurrentExpression as ConditionalExpression).Type == UtilConstants.BoolType)) + { + var isContainsTrue = MethodValueIsTrue(methodValue); + if (isContainsTrue) + { + methodValue = methodValue + "=true "; + } + else + { + methodValue = methodValue + "=1 "; + } + } + if (isRoot && parameter.BaseExpression == null && this.Context.ResolveType.IsIn(ResolveExpressType.WhereMultiple, ResolveExpressType.WhereSingle) && (parameter.CurrentExpression is MethodCallExpression) && ((parameter.CurrentExpression as MethodCallExpression).Method.Name.IsIn("IIF")) && (parameter.CurrentExpression as MethodCallExpression).Method.ReturnType == UtilConstants.BoolType) + { + methodValue = methodValue + "=1 "; + } + if (parameter.BaseExpression != null && ExpressionTool.IsLogicOperator(parameter.BaseExpression) && this.Context.ResolveType.IsIn(ResolveExpressType.WhereMultiple, ResolveExpressType.WhereSingle) && (parameter.CurrentExpression is ConditionalExpression) && ((parameter.CurrentExpression as ConditionalExpression).Type == UtilConstants.BoolType)) + { + methodValue = methodValue + "=1 "; + } + if (parameter.BaseExpression != null && ExpressionTool.IsLogicOperator(parameter.BaseExpression) && this.Context.ResolveType.IsIn(ResolveExpressType.WhereMultiple, ResolveExpressType.WhereSingle) && (parameter.CurrentExpression is MethodCallExpression) && ((parameter.CurrentExpression as MethodCallExpression).Method.Name.IsIn("IIF")) && (parameter.CurrentExpression as MethodCallExpression).Method.ReturnType == UtilConstants.BoolType) + { + methodValue = methodValue + "=1 "; + } + if (parameter.BaseExpression != null && ExpressionTool.IsLogicOperator(parameter.BaseExpression) && this.Context.ResolveType.IsIn(ResolveExpressType.WhereMultiple, ResolveExpressType.WhereSingle) && (parameter.CurrentExpression is MethodCallExpression) && ((parameter.CurrentExpression as MethodCallExpression).Method.Name.IsIn("ToBool", "ToBoolean"))) + { + methodValue = methodValue + "=1 "; + } + base.AppendValue(parameter, isLeft, methodValue); + } + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/MethodCallExpressionResolve_BaseDateFomat.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/MethodCallExpressionResolve_BaseDateFomat.cs new file mode 100644 index 000000000..4d532ac85 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/MethodCallExpressionResolve_BaseDateFomat.cs @@ -0,0 +1,284 @@ +using System.Text.RegularExpressions; + +namespace SqlSugar +{ + /// + /// MethodCall base DateFomat + /// + public partial class MethodCallExpressionResolve : BaseResolve + { + public string GeDateFormat(string formatString, string value) + { + if (IsOracle() && formatString == "yyyy-MM-dd HH:mm:ss") + { + return $"to_char({value},'yyyy-MM-dd HH24:mi:ss') "; + } + else if (IsOracle() || IsPg()) + { + if (this.Context?.SugarContext?.Context?.CurrentConnectionConfig?.MoreSettings?.DatabaseModel == DbType.SqlServer) + { + return string.Format("FORMAT({0},'{1}','en-US')", value, formatString); + } + if (!(formatString?.Contains("24") == true)) + { + formatString = formatString.Replace("HH", "hh24"); + if (!(formatString?.Contains("24") == true)) + { + formatString = formatString.Replace("hh", "hh24"); + } + } + formatString = formatString.Replace("mm", "mi"); + //if (formatString.HasValue() && formatString.Contains("hh:mm")) + //{ + // formatString = formatString.Replace("hh:mm", "hh:mi"); + //} + //else if (formatString.HasValue() && formatString.Contains("hhmm")) + //{ + // formatString = formatString.Replace("hhmm", "hhmi"); + //} + //else if (formatString.HasValue() && formatString.Contains("HH:mm")) + //{ + // formatString = formatString.Replace("HH:mm", "HH:mi"); + //} + //else if (formatString.HasValue() && formatString.Contains("HHmm")) + //{ + // formatString = formatString.Replace("HHmm", "HHmi"); + //} + return $"to_char({value},'{formatString}') "; + } + else if (IsSqlite() && formatString == "yyyy-MM-dd") + { + return $"strftime('%Y-%m-%d', {value})"; + } + else if (IsSqlite() && formatString == "yyyy-MM-dd HH:mm:ss") + { + return $"strftime('%Y-%m-%d %H:%M:%S', {value})"; + } + else if (IsSqlite() && formatString == "yyyy-MM-dd hh:mm:ss") + { + return $"strftime('%Y-%m-%d %H:%M:%S', {value})"; + } + else if (IsSqlite() && formatString == "yyyy-MM") + { + return $"strftime('%Y-%m', {value})"; + } + else if (IsSqlite() && formatString == "yyyyMM") + { + return $"strftime('%Y%m', {value})"; + } + else if (IsSqlite() && formatString == "yyyyMMdd") + { + return $"strftime('%Y%m%d', {value})"; + } + else if (IsSqlite() && formatString.Contains('%')) + { + return $"strftime('{formatString}', {value})"; + } + else if (IsMySql() && formatString == "yyyy-MM-dd") + { + return $"DATE_FORMAT({value}, '%Y-%m-%d')"; + } + else if (IsMySql() && formatString == "yyyy-MM") + { + return $"DATE_FORMAT({value}, '%Y-%m')"; + } + else if (IsMySql() && formatString == "yyyyMM") + { + return $"DATE_FORMAT({value}, '%Y%m')"; + } + else if (IsMySql() && formatString == "yyyyMMdd") + { + return $"DATE_FORMAT({value}, '%Y%m%d')"; + } + else if (IsMySql() && formatString == "yyyy-MM-dd HH:mm:ss") + { + return $"DATE_FORMAT({value}, '%Y-%m-%d %H:%i:%S')"; + } + else if (IsMySql() && formatString == "yyyy-MM-dd hh:mm:ss") + { + return $"DATE_FORMAT({value}, '%Y-%m-%d %H:%i:%S')"; + } + else if (IsMySql() && formatString.Contains('%')) + { + return $"DATE_FORMAT({value}, '{formatString}')"; + } + else if (formatString == "yyyy-MM-dd" && IsSqlServer()) + { + return $"CONVERT(varchar(100),convert(datetime,{value}), 23)"; + } + else if (formatString == "yyyy-MM" && IsSqlServer()) + { + return $"CONVERT(varchar(7),convert(datetime,{value}), 23)"; + } + else if (formatString == "yyyy-MM-dd HH:mm:ss" && IsSqlServer()) + { + return $"CONVERT(varchar(100),convert(datetime,{value}), 120)"; + } + else if (formatString == "yyyy-MM-dd hh:mm:ss" && IsSqlServer()) + { + return $"CONVERT(varchar(100),convert(datetime,{value}), 120)"; + } + else if (formatString == "yyyy-MM-dd HH:mm" && IsSqlServer()) + { + return $"CONVERT(varchar(16),convert(datetime,{value}), 120)"; + } + else if (formatString == "yyyy-MM-dd hh:mm" && IsSqlServer()) + { + return $"CONVERT(varchar(16),convert(datetime,{value}), 120)"; + } + else if (formatString == "yyyy-MM-dd hh:mm:ss.ms" && IsSqlServer()) + { + return $"CONVERT(varchar(100),convert(datetime,{value}), 121)"; + } + else if (IsSqlServer() && formatString?.IsInt() == true) + { + return string.Format("CONVERT(varchar(100),convert(datetime,{0}), {1})", value, formatString); + } + else if (IsSqlServer()) + { + return string.Format("FORMAT({0},'{1}','en-US')", value, formatString); + } + else if (IsMySql() && !formatString.Contains('%')) + { + var newFormt = formatString + .Replace("yyyy", "%Y") + .Replace("yy", "%Y") + .Replace("MM", "%m") + .Replace("M", "%m") + .Replace("dd", "%d") + .Replace("HH", "%H") + .Replace("hh", "%h") + .Replace("mm", "%i") + .Replace("ss", "%s") + .Replace("fff", "%f"); + return $"DATE_FORMAT({value}, '{newFormt}')"; + } + else if (IsSqlite() && !formatString.Contains('%')) + { + var newFormt = formatString + .Replace("yyyy", "%Y") + .Replace("yy", "%Y") + .Replace("MM", "%m") + .Replace("M", "%m") + .Replace("dd", "%d") + .Replace("HH", "%H") + .Replace("hh", "%h") + .Replace("mm", "%M") + .Replace("ss", "%S") + .Replace("fff", "%f"); + return $"strftime('{newFormt}',{value})"; + } + var parameter = new MethodCallExpressionArgs() { IsMember = true, MemberValue = DateType.Year }; + var parameter2 = new MethodCallExpressionArgs() { IsMember = true, MemberName = value }; + var parameters = new MethodCallExpressionModel() { Args = new List() { parameter2, parameter } }; + var begin = @"^"; + var end = @"$"; + formatString = formatString.Replace("yyyy", begin + UtilMethods.ConvertStringToNumbers(this.GetMethodValue("DateValue", parameters).ObjToString()) + end); + formatString = formatString.Replace("yy", begin + UtilMethods.ConvertStringToNumbers(this.GetMethodValue("DateValue", parameters).ObjToString()) + end); + + parameters.Args.Last().MemberValue = DateType.Month; + if (IsMySql()) + { + formatString = formatString.Replace("MM", begin + UtilMethods.ConvertStringToNumbers("LPAD(" + this.GetMethodValue("DateValue", parameters).ObjToString() + ",2,0)") + end); + } + else if (IsSqlite()) + { + formatString = formatString.Replace("MM", begin + UtilMethods.ConvertStringToNumbers("SUBSTR('00' ||" + this.GetMethodValue("DateValue", parameters).ObjToString() + ", -2, 2)") + end); + } + else if (IsPg()) + { + formatString = formatString.Replace("MM", begin + UtilMethods.ConvertStringToNumbers("lpad(cast(" + this.GetMethodValue("DateValue", parameters).ObjToString() + " as varchar(20)),2,'0')") + end); + } + else if (IsOracle()) + { + formatString = formatString.Replace("MM", begin + UtilMethods.ConvertStringToNumbers("lpad(cast(" + this.GetMethodValue("DateValue", parameters).ObjToString() + " as varchar(20)),2,'0')") + end); + } + else + { + formatString = formatString.Replace("MM", begin + UtilMethods.ConvertStringToNumbers(this.GetMethodValue("DateValue", parameters).ObjToString()) + end); + } + formatString = formatString.Replace("M", begin + UtilMethods.ConvertStringToNumbers(this.GetMethodValue("DateValue", parameters).ObjToString()) + end); + + parameters.Args.Last().MemberValue = DateType.Day; + if (IsSqlServer()) + { + formatString = formatString.Replace("dd", begin + UtilMethods.ConvertStringToNumbers(string.Format("CASE WHEN LEN({0})=1 THEN '0'+ {0} else {0} end", this.GetMethodValue("DateValue", parameters))) + end); + } + formatString = formatString.Replace("dd", begin + UtilMethods.ConvertStringToNumbers(this.GetMethodValue("DateValue", parameters).ObjToString()) + end); + formatString = formatString.Replace("d", begin + UtilMethods.ConvertStringToNumbers(this.GetMethodValue("DateValue", parameters).ObjToString()) + end); + + parameters.Args.Last().MemberValue = DateType.Hour; + formatString = Regex.Replace(formatString, "hh", begin + UtilMethods.ConvertStringToNumbers(this.GetMethodValue("DateValue", parameters).ObjToString()) + end, RegexOptions.IgnoreCase); + formatString = Regex.Replace(formatString, "h", begin + UtilMethods.ConvertStringToNumbers(this.GetMethodValue("DateValue", parameters).ObjToString()) + end, RegexOptions.IgnoreCase); + + parameters.Args.Last().MemberValue = DateType.Minute; + formatString = formatString.Replace("mm", begin + UtilMethods.ConvertStringToNumbers(this.GetMethodValue("DateValue", parameters).ObjToString()) + end); + formatString = formatString.Replace("m", begin + UtilMethods.ConvertStringToNumbers(this.GetMethodValue("DateValue", parameters).ObjToString()) + end); + + parameters.Args.Last().MemberValue = DateType.Second; + formatString = formatString.Replace("ss", begin + UtilMethods.ConvertStringToNumbers(this.GetMethodValue("DateValue", parameters).ObjToString()) + end); + formatString = formatString.Replace("s", begin + UtilMethods.ConvertStringToNumbers(this.GetMethodValue("DateValue", parameters).ObjToString()) + end); + + if (!IsSqlite()) + { + parameters.Args.Last().MemberValue = DateType.Millisecond; + formatString = formatString.Replace("ms", begin + UtilMethods.ConvertStringToNumbers(this.GetMethodValue("DateValue", parameters).ObjToString()) + end); + } + + var items = Regex.Matches(formatString, @"\^\d+\$").Cast().ToList(); + foreach (var item in items) + { + formatString = formatString.Replace(item.Value, "$@" + UtilMethods.ConvertNumbersToString(item.Value.TrimStart('^').TrimEnd('$')) + "$"); + } + var strings = formatString.TrimStart('$').TrimEnd('$').Split('$'); + var joinStringParameter = new MethodCallExpressionModel() + { + Args = new List() + }; + foreach (var r in strings) + { + if (!string.IsNullOrEmpty(r) && r.Substring(0, 1) == "@") + { + joinStringParameter.Args.Add(new MethodCallExpressionArgs() + { + MemberName = r.TrimStart('@') + }); + } + else + { + + var name = base.AppendParameter(r); + joinStringParameter.Args.Add(new MethodCallExpressionArgs() + { + MemberName = name + }); + } + } + return this.GetMethodValue("MergeString", joinStringParameter).ObjToString(); + } + private bool IsSqlServer() + { + return this.Context is SqlServerExpressionContext; + } + private bool IsMySql() + { + var name = this.Context.GetType().Name; + var result = (name == "MySqlExpressionContext"); + return result; + } + private bool IsSqlite() + { + return this.Context is SqliteExpressionContext; + } + private bool IsPg() + { + return this.Context is PostgreSQLExpressionContext + || this.Context is KdbndpExpressionContext; + } + private bool IsOracle() + { + return this.Context is OracleExpressionContext + || this.Context is DmExpressionContext; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/MethodCallExpressionResolve_Helper.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/MethodCallExpressionResolve_Helper.cs new file mode 100644 index 000000000..d7830c62d --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/MethodCallExpressionResolve_Helper.cs @@ -0,0 +1,1207 @@ +using System.Collections; +using System.Linq.Expressions; +using System.Text.RegularExpressions; + +namespace SqlSugar +{ + /// + ///MethodCall Helper + /// + public partial class MethodCallExpressionResolve : BaseResolve + { + private void CusMethod(ExpressionParameter parameter, MethodCallExpression express, bool? isLeft) + { + try + { + OneToManyNavgateExpression nav = new OneToManyNavgateExpression(this.Context?.SugarContext?.Context, this); + nav.ParameterIndex = this.Context.ParameterIndex; + this.Context.ParameterIndex++; + if (nav.IsNavgate(express)) + { + var sql = nav.GetSql(); + SetNavigateResult(); + this.Context.SingleTableNameSubqueryShortName = nav.ShorName; + base.AppendValue(parameter, isLeft, sql); + return; + } + + OneToManyNavgateExpressionN nav2 = new OneToManyNavgateExpressionN(this.Context?.SugarContext?.Context, this); + if (nav2.IsNavgate(express)) + { + var sql = nav2.GetSql(); + SetNavigateResult(); + this.Context.SingleTableNameSubqueryShortName = nav2.shorName; + base.AppendValue(parameter, isLeft, sql); + return; + } + + var constValue = ExpressionTool.DynamicInvoke(express); + if (constValue is MapperSql) + { + constValue = (constValue as MapperSql).Sql; + base.AppendValue(parameter, isLeft, constValue); + return; + } + parameter.BaseParameter.CommonTempData = constValue; + var parameterName = base.AppendParameter(constValue); + if (parameter.BaseParameter.CommonTempData?.Equals(CommonTempDataType.Result) == true) + { + this.Context.Result.Append(parameterName); + } + else + { + base.AppendValue(parameter, isLeft, parameterName); + } + } + catch (Exception ex) + { + if (ex is SqlSugarException) + { + Check.Exception(true, string.Format(ex.Message, express.Method.Name)); + } + else + { + Check.Exception(true, ErrorMessage.MethodError, express.Method.Name); + } + } + } + private static bool MethodValueIsTrue(object methodValue) + { + return methodValue?.ToString().Contains("THEN true ELSE false END") == true; + } + private object packIfElse(object methodValue) + { + methodValue = this.Context.DbMehtods.CaseWhen(new List>() { + new KeyValuePair("IF",methodValue.ObjToString()), + new KeyValuePair("Return", this.Context.DbMehtods.TrueValue()), + new KeyValuePair("End", this.Context.DbMehtods.FalseValue()) + }); + return methodValue; + } + private void SetShortName(Expression exp) + { + var lamExp = (exp as LambdaExpression); + if (lamExp.Parameters?.Count == 1) + { + if (this.Context.SingleTableNameSubqueryShortName == null) + { + this.Context.SingleTableNameSubqueryShortName = lamExp.Parameters.First().Name; + } + } + } + + + private void AppendItem(ExpressionParameter parameter, string name, IEnumerable args, MethodCallExpressionModel model, Expression item) + { + if (ExpressionTool.IsUnConvertExpress(item)) + { + item = (item as UnaryExpression).Operand; + } + if (this.Context.IsSingle && args.Any(it => ExpressionTool.IsSubQuery(it)) && base.BaseParameter?.BaseParameter?.BaseParameter?.CurrentExpression != null) + { + var exp = base.BaseParameter?.BaseParameter?.BaseParameter?.CurrentExpression; + if (exp is LambdaExpression) + { + SetShortName(exp); + } + else if (exp is UnaryExpression) + { + exp = base.BaseParameter?.BaseParameter?.BaseParameter?.BaseParameter?.CurrentExpression; + if (exp is LambdaExpression) + { + SetShortName(exp); + } + } + } + else if (this.Context.IsSingle && args.Any(it => ExpressionTool.IsIsNullSubQuery(it))) + { + var exp = base.BaseParameter?.BaseParameter?.BaseParameter?.CurrentExpression; + if (exp is LambdaExpression) + { + SetShortName(exp); + } + else if (exp is UnaryExpression) + { + exp = base.BaseParameter?.BaseParameter?.BaseParameter?.BaseParameter?.CurrentExpression; + if (exp is LambdaExpression) + { + SetShortName(exp); + } + } + } + var isBinaryExpression = item is BinaryExpression || item is MethodCallExpression; + var isConst = item is ConstantExpression; + var isIIF = name == "IIF"; + var isSubIIF = (isIIF && item.ToString().StartsWith("IIF")); + var isIFFBoolMember = isIIF && (item is MemberExpression) && (item as MemberExpression).Type == UtilConstants.BoolType; + var isIFFUnary = isIIF && (item is UnaryExpression) && (item as UnaryExpression).Operand.Type == UtilConstants.BoolType; + var isIFFBoolBinary = isIIF && (item is BinaryExpression) && (item as BinaryExpression).Type == UtilConstants.BoolType; + var isIFFBoolMethod = isIIF && (item is MethodCallExpression) && (item as MethodCallExpression).Type == UtilConstants.BoolType; + var isFirst = item == args.First(); + var isBoolValue = item.Type == UtilConstants.BoolType && item.ToString().StartsWith("value("); + var isLength = ExpressionTool.GetIsLength(item); + if (isFirst && isIIF && isConst) + { + var value = (item as ConstantExpression).Value.ObjToBool() ? this.Context.DbMehtods.True() : this.Context.DbMehtods.False(); + var methodCallExpressionArgs = new MethodCallExpressionArgs() + { + IsMember = true, + MemberName = value, + MemberValue = value + }; + model.Args.Add(methodCallExpressionArgs); + } + else if (isFirst && isIIF && isIFFBoolMember && (item as MemberExpression)?.Member?.Name == "HasValue") + { + var value = base.GetNewExpressionValue(item); + var methodCallExpressionArgs = new MethodCallExpressionArgs() + { + IsMember = true, + MemberName = value, + MemberValue = value + }; + model.Args.Add(methodCallExpressionArgs); + } + else if (name != null && name != "MappingColumn" && !name.StartsWith("Row") && ExpressionTool.GetMethodName(item) == "Format" && ExpressionTool.GetParameters(item).Count == 0) + { + var value = ExpressionTool.DynamicInvoke(item); + var p = AppendParameter(value); + var methodCallExpressionArgs = new MethodCallExpressionArgs() + { + IsMember = true, + MemberName = p, + MemberValue = p + }; + model.Args.Add(methodCallExpressionArgs); + } + else if (isLength) + { + var value = GetNewExpressionValue(item); + + var methodCallExpressionArgs = new MethodCallExpressionArgs() + { + IsMember = true, + MemberName = value, + MemberValue = value + }; + model.Args.Add(methodCallExpressionArgs); + } + else if (isIFFUnary && !isFirst) + { + AppendModelByIIFMember(parameter, model, (item as UnaryExpression).Operand); + } + else if (isIFFBoolMember && !isFirst) + { + AppendModelByIIFMember(parameter, model, item); + + } + else if (isIFFBoolBinary && !isFirst) + { + var binaryExp = item as BinaryExpression; + var binaryExpEqual = binaryExp != null && ExpressionTool.IsComparisonOperatorBool(binaryExp); + if (binaryExpEqual) + { + var expValue = GetNewExpressionValue(item); + expValue = this.Context.DbMehtods.IIF(new MethodCallExpressionModel() + { + Name = "IIF", + Args = new List() + { + new MethodCallExpressionArgs(){ + IsMember=true, + MemberName=expValue + }, + new MethodCallExpressionArgs(){ + IsMember=true, + MemberName= Context.DbMehtods.TrueValue() + }, + new MethodCallExpressionArgs(){ + IsMember=true, + MemberName= Context.DbMehtods.FalseValue() + } + } + }); + model.Args.Add(new MethodCallExpressionArgs() + { + IsMember = false, + MemberName = expValue, + MemberValue = expValue + }); + } + else + { + AppendModelByIIFBinary(parameter, model, item); + } + + } + else if (isIFFBoolMethod && !isFirst) + { + AppendModelByIIFMethod(parameter, model, item); + } + else if (isBinaryExpression) + { + model.Args.Add(GetMethodCallArgs(parameter, item, name)); + } + else if (isSubIIF) + { + model.Args.Add(GetMethodCallArgs(parameter, item)); + } + else if (isBoolValue && !isIIF && item is MemberExpression) + { + model.Args.Add(GetMethodCallArgs(parameter, (item as MemberExpression).Expression)); + } + else if (isBoolValue && isIIF && item is MemberExpression && ExpressionTool.GetParameters(item).Count == 0) + { + var expValue = AppendParameter(ExpressionTool.DynamicInvoke(item)); + expValue = this.Context.DbMehtods.Equals(new MethodCallExpressionModel() + { + Name = "Equals", + Args = new List() + { + new MethodCallExpressionArgs(){ + IsMember=true, + MemberName=expValue + }, + new MethodCallExpressionArgs(){ + IsMember=true, + MemberName= Context.DbMehtods.TrueValue() + } + } + }); + model.Args.Add(new MethodCallExpressionArgs() + { + IsMember = false, + MemberName = expValue, + MemberValue = expValue + }); + } + else if (isBoolValue && isIIF && item is MemberExpression) + { + var argItem = GetMethodCallArgs(parameter, (item as MemberExpression).Expression); + if (argItem.IsMember) + { + var pName = this.Context.SqlParameterKeyWord + "true_0"; + if (!this.Context.Parameters.Any(it => it.ParameterName == pName)) + this.Context.Parameters.Add(new SugarParameter(pName, true)); + argItem.MemberName = $" {argItem.MemberName}={pName} "; + } + model.Args.Add(argItem); + } + else if (name.IsIn("ListAny", "ListAll") && item is LambdaExpression) + { + var sql = GetNewExpressionValue(item, ResolveExpressType.WhereMultiple); + var lamExp = (item as LambdaExpression); + var pExp = lamExp.Parameters[0]; + var pname = pExp.Name; + var columns = this.Context.SugarContext.Context.EntityMaintenance.GetEntityInfo(pExp.Type).Columns; + if (columns.Count == 0 && pExp.Type.IsValueType && pExp.Type != typeof(string)) + { + columns = new List() { new EntityColumnInfo() { UnderType = UtilMethods.GetUnderType(pExp.Type), PropertyName = pExp.Type.Name, DbTableName = pExp.Type.Name } }; + } + model.Args.Add(new MethodCallExpressionArgs() + { + MemberValue = new ListAnyParameter() + { + Sql = sql, + Name = pname, + Columns = columns, + ConvetColumnFunc = this.Context.GetTranslationColumnName + } + }); + if (lamExp.Body is MethodCallExpression callExpression) + { + var callObject = callExpression.Object; + + if (callObject is MemberExpression memberExpression && memberExpression?.Expression is ParameterExpression parameterExpression) + { + var entity = this.Context.SugarContext.Context.EntityMaintenance.GetEntityInfo(parameterExpression.Type); + var columnInfo = entity.Columns.FirstOrDefault(it => it.PropertyName == memberExpression.Member.Name); + model.DataObject = columnInfo; + } + } + if (this.Context.IsSingle && this.Context.SingleTableNameSubqueryShortName == null) + { + ParameterExpressionVisitor visitor = new ParameterExpressionVisitor(); + visitor.Visit(lamExp); + var tableParamter = visitor.Parameters.FirstOrDefault(it => it.Name != pname); + if (tableParamter != null) + { + this.Context.SingleTableNameSubqueryShortName = tableParamter.Name; + } + } + } + else + { + AppendModel(parameter, model, item, name, args); + } + } + + + private void AppendModelByIIFMember(ExpressionParameter parameter, MethodCallExpressionModel model, Expression item) + { + parameter.CommonTempData = CommonTempDataType.Result; + base.Expression = item; + base.Start(); + var methodCallExpressionArgs = new MethodCallExpressionArgs() + { + IsMember = parameter.ChildExpression is MemberExpression, + MemberName = parameter.CommonTempData + }; + if (methodCallExpressionArgs.IsMember && parameter.ChildExpression?.ToString() == "DateTime.Now") + { + methodCallExpressionArgs.IsMember = false; + } + var value = methodCallExpressionArgs.MemberName; + if (methodCallExpressionArgs.IsMember) + { + var childExpression = parameter.ChildExpression as MemberExpression; + if (childExpression.Expression != null && childExpression.Expression is ConstantExpression) + { + methodCallExpressionArgs.IsMember = false; + } + } + if (methodCallExpressionArgs.IsMember == false) + { + var parameterName = this.Context.SqlParameterKeyWord + ExpressionConst.MethodConst + this.Context.ParameterIndex; + this.Context.ParameterIndex++; + methodCallExpressionArgs.MemberName = parameterName; + methodCallExpressionArgs.MemberValue = value; + this.Context.Parameters.Add(new SugarParameter(parameterName, value)); + } + model.Args.Add(methodCallExpressionArgs); + parameter.ChildExpression = null; + } + private void AppendModelByIIFBinary(ExpressionParameter parameter, MethodCallExpressionModel model, Expression item) + { + Check.Exception(true, "The SqlFunc.IIF(arg1,arg2,arg3) , {0} argument do not support ", item.ToString()); + } + private void AppendModelByIIFMethod(ExpressionParameter parameter, MethodCallExpressionModel model, Expression item) + { + var methodExpression = item as MethodCallExpression; + if (methodExpression.Method.Name.IsIn("ToBool", "ToBoolean", "IIF")) + { + model.Args.Add(base.GetMethodCallArgs(parameter, item)); + } + else if (methodExpression.Method.Name.IsIn("Contains", "EndsWith", "StartsWith")) + { + Expression conditionalExpression = ExpressionTool.GetConditionalExpression(item); + model.Args.Add(base.GetMethodCallArgs(parameter, conditionalExpression)); + } + else + { + Check.Exception(true, "The SqlFunc.IIF(arg1,arg2,arg3) , {0} argument do not support ", item.ToString()); + } + } + + private void AppendModel(ExpressionParameter parameter, MethodCallExpressionModel model, Expression item, string name, IEnumerable args) + { + parameter.CommonTempData = CommonTempDataType.Result; + base.Expression = item; + var isRemoveParamter = false; + var isNegate = false; + if (item.Type == UtilConstants.DateType && parameter.CommonTempData.ObjToString() == CommonTempDataType.Result.ToString() && item.ToString() == "DateTime.Now.Date") + { + parameter.CommonTempData = DateTime.Now.Date; + } + else if (item is ConditionalExpression) + { + parameter.CommonTempData = GetNewExpressionValue(item); + } + else if (IsNot(item)) + { + parameter.CommonTempData = GetNewExpressionValue(item); + } + else if (IsDateDate(item)) + { + parameter.CommonTempData = GetNewExpressionValue(item); + } + else if (IsDateValue(item)) + { + parameter.CommonTempData = GetNewExpressionValue(item); + } + else if (model.Name == "ToString" && item is ConstantExpression && (item as ConstantExpression).Type.IsEnum()) + { + parameter.CommonTempData = item.ToString(); + } + else if (IsDateItemValue(item)) + { + parameter.CommonTempData = GetNewExpressionValue(item); + } + else if (name == "Format" && item is NewArrayExpression) + { + var exps = (item as NewArrayExpression).Expressions; + parameter.CommonTempData = exps.Select(it => + { + var res = GetNewExpressionValue(ExpressionTool.RemoveConvert(it)); + return res; + }).ToArray(); + } + else if (name == "Format" && item is ConstantExpression) + { + parameter.CommonTempData = ExpressionTool.GetExpressionValue(item); + } + else if (name == "FullTextContains" && item is NewArrayExpression) + { + var array = ExpressionTool.GetNewArrayMembers(item as NewArrayExpression); + parameter.CommonTempData = array.Select(it => this.Context.GetTranslationColumnName(it)).ToList(); + isRemoveParamter = true; + } + else if (ExpressionTool.IsNegate(item) && (item as UnaryExpression)?.Operand is MemberExpression) + { + var exp = (item as UnaryExpression)?.Operand; + parameter.CommonTempData = GetNewExpressionValue(exp) + " * -1 "; + isRemoveParamter = true; + isNegate = true; + } + else + { + base.Start(); + } + var methodCallExpressionArgs = new MethodCallExpressionArgs() + { + IsMember = parameter.ChildExpression is MemberExpression && !ExpressionTool.IsConstExpression(parameter.ChildExpression as MemberExpression), + MemberName = parameter.CommonTempData + }; + if (this.Context?.SugarContext?.Context?.CurrentConnectionConfig?.MoreSettings?.IsCorrectErrorSqlParameterName == true) + { + if (methodCallExpressionArgs.MemberName is string) + { + methodCallExpressionArgs.MemberName = ExpressionTool.ResolveMemberValue(this.Context, item, methodCallExpressionArgs.MemberName?.ToString()); + } + } + if (methodCallExpressionArgs.MemberName is MapperSql) + { + methodCallExpressionArgs.MemberName = (methodCallExpressionArgs.MemberName as MapperSql).Sql; + } + if (methodCallExpressionArgs.IsMember && parameter.ChildExpression?.ToString() == "DateTime.Now") + { + methodCallExpressionArgs.IsMember = false; + } + var value = methodCallExpressionArgs.MemberName; + if (methodCallExpressionArgs.IsMember) + { + var childExpression = parameter.ChildExpression as MemberExpression; + if (childExpression.Expression != null && childExpression.Expression is ConstantExpression) + { + methodCallExpressionArgs.IsMember = false; + } + } + if (IsDateDate(item) || IsDateValue(item) || IsDateItemValue(item) || item is ConditionalExpression || IsNot(item)) + { + methodCallExpressionArgs.IsMember = true; + } + if (methodCallExpressionArgs.IsMember == false && (item is MethodCallExpression && item.ToString() == "GetDate()") || (item is UnaryExpression && ((UnaryExpression)item).Operand.ToString() == "GetDate()")) + { + var parameterName = this.Context.SqlParameterKeyWord + ExpressionConst.MethodConst + this.Context.ParameterIndex; + this.Context.ParameterIndex++; + methodCallExpressionArgs.MemberName = value; + methodCallExpressionArgs.MemberValue = null; + } + else if (methodCallExpressionArgs.IsMember == false && isNegate == false) + { + var parameterName = this.Context.SqlParameterKeyWord + ExpressionConst.MethodConst + this.Context.ParameterIndex; + this.Context.ParameterIndex++; + methodCallExpressionArgs.MemberName = parameterName; + if (name == "ToString" && UtilMethods.GetUnderType(base.Expression.Type).IsEnum()) + { + value = value?.ToString(); + } + else if (name == "ContainsArray" && args.Count() == 2 && value != null && value is IList) + { + List result = new List(); + foreach (var memItem in (value as IList)) + { + result.Add(GetMemberValue(memItem, args.Last())); + } + value = result; + } + else if (!(item is ParameterExpression) && name.IsIn("Contains", "StartsWith", "EndsWith") && item == args.Last() && ExpressionTool.IsSqlParameterDbType(this.Context, args.First())) + { + var myvalue = ExpressionTool.DynamicInvoke(args.Last()); + var parametre = ExpressionTool.GetParameterBySqlParameterDbType(this.Context.ParameterIndex, myvalue, this.Context, args.First()); + this.Context.Parameters.Add(parametre); + methodCallExpressionArgs.MemberName = parametre.ParameterName; + methodCallExpressionArgs.MemberValue = parametre.Value; + methodCallExpressionArgs.IsMember = true; + isRemoveParamter = true; + this.Context.ParameterIndex++; + } + methodCallExpressionArgs.MemberValue = value; + if (isRemoveParamter != true) + { + if (value == null && item != null) + { + this.Context.Parameters.Add(new SugarParameter(parameterName, value, UtilMethods.GetUnderType(item.Type))); + } + else + { + this.Context.Parameters.Add(new SugarParameter(parameterName, value)); + } + } + } + model.Args.Add(methodCallExpressionArgs); + parameter.ChildExpression = null; + } + + + private void GetConfigValue(MethodCallExpression express, ExpressionParameter parameter) + { + var exp = express.Arguments[0]; + var name = Regex.Match(express.Method.ToString(), @"GetConfigValue\[(.+)\]").Groups[1].Value; + string code = null; + if (express.Arguments.Count > 1) + { + code = ExpressionTool.GetExpressionValue(express.Arguments[1]) + ""; + } + var entityDb = SqlFuncExtendsion.TableInfos.FirstOrDefault(y => y.Type.Name == name && y.Code == code); + Check.Exception(entityDb == null, string.Format("GetConfigValue no configuration Entity={0} UniqueCode={1}", name, code)); + var entity = new ConfigTableInfo() + { + Code = entityDb.Code, + TableName = entityDb.TableName, + Key = entityDb.Key, + Parameter = new List(), + Type = entityDb.Type, + Value = entityDb.Value, + Where = entityDb.Where + }; + if (entityDb.Parameter != null && entityDb.Parameter.Count != 0) + { + foreach (var item in entityDb.Parameter) + { + entity.Parameter.Add(new SugarParameter("", null) + { + DbType = item.DbType, + Direction = item.Direction, + IsArray = item.IsArray, + IsJson = item.IsJson, + IsNullable = item.IsNullable, + IsRefCursor = item.IsRefCursor, + ParameterName = item.ParameterName, + Size = item.Size, + SourceColumn = item.SourceColumn, + SourceColumnNullMapping = item.SourceColumnNullMapping, + SourceVersion = item.SourceVersion, + TempDate = item.TempDate, + TypeName = item.TypeName, + Value = item.Value, + _Size = item._Size + + }); + } + } + string sql = " (SELECT {0} FROM {1} WHERE {2}={3}"; + if (ExpressionTool.IsUnConvertExpress(exp)) + { + exp = (exp as UnaryExpression).Operand; + } + var member = exp as MemberExpression; + var it = member.Expression; + var type = it.Type; + var properyName = member.Member.Name; + var eqName = string.Format("{0}.{1}", this.Context.GetTranslationColumnName(it.ToString()), this.Context.GetDbColumnName(type.Name, properyName)); + if (this.Context.IsSingle) + { + this.Context.SingleTableNameSubqueryShortName = it.ToString(); + } + sql = string.Format(sql, entity.Value, this.Context.GetTranslationColumnName(entity.TableName), entity.Key, eqName); + if (entity.Parameter != null) + { + foreach (var item in entity.Parameter) + { + var oldName = item.ParameterName; + item.ParameterName = Regex.Split(oldName, "_con_").First() + "_con_" + this.Context.ParameterIndex; + entity.Where = entity.Where.Replace(oldName, item.ParameterName); + } + this.Context.ParameterIndex++; + this.Context.Parameters.AddRange(entity.Parameter); + } + if (entity.Where.HasValue()) + { + sql += " AND " + entity.Where; + } + sql += " )"; + if (this.Context.ResolveType.IsIn(ResolveExpressType.SelectMultiple, ResolveExpressType.SelectSingle, ResolveExpressType.Update)) + { + parameter.BaseParameter.CommonTempData = sql; + } + else + { + AppendMember(parameter, parameter.IsLeft, sql); + } + } + private object GetMethodValue(string name, MethodCallExpressionModel model) + { + model.Parameters = this.Context.Parameters; + if (IsExtMethod(name)) + { + model.Expression = this.Expression; + model.BaseExpression = this.BaseParameter.CurrentExpression; + DbType type = DbType.SqlServer; + if (this.Context is SqlServerExpressionContext) + type = DbType.SqlServer; + else if (this.Context is MySqlExpressionContext) + type = DbType.MySql; + else if (this.Context is SqliteExpressionContext) + type = DbType.Sqlite; + else if (this.Context is OracleExpressionContext) + type = DbType.Oracle; + else if (this.Context is PostgreSQLExpressionContext) + type = DbType.PostgreSQL; + else if (this.Context.GetType().Name.StartsWith("MySql")) + { + type = DbType.MySql; + } + else + { + type = GetType(this.Context.GetType().Name); + } + return this.Context.SqlFuncServices.First(it => it.UniqueMethodName == name).MethodValue(model, type, this.Context); + } + else + { + var iLike = this.Context?.SugarContext?.Context?.CurrentConnectionConfig?.MoreSettings?.EnableILike == true; + if (name == "Parse" && TempParseType.IsIn(UtilConstants.GuidType) && model.Args?.Count > 1) + { + name = "Equals"; + } + else if (name == "Parse") + { + name = "To" + TempParseType.Name; + } + else if (name == "IsNullOrWhiteSpace") + { + name = "IsNullOrEmpty"; + } + if (model.Conext == null) + { + model.Conext = this.Context; + } + switch (name) + { + case "IIF": + return this.Context.DbMehtods.IIF(model); + case "HasNumber": + return this.Context.DbMehtods.HasNumber(model); + case "HasValue": + return this.Context.DbMehtods.HasValue(model); + case "IsNullOrEmpty": + model.Conext = this.Context; + return this.Context.DbMehtods.IsNullOrEmpty(model); + case "ToLower": + return this.Context.DbMehtods.ToLower(model); + case "ToUpper": + return this.Context.DbMehtods.ToUpper(model); + case "Trim": + return this.Context.DbMehtods.Trim(model); + case "Contains": + return GetLike(this.Context.DbMehtods.Contains(model), iLike); + case "ContainsArray": + if (model.Args[0].MemberValue == null) + { + var first = this.Context.Parameters.FirstOrDefault(it => it.ParameterName == model.Args[0].MemberName.ObjToString()); + if (first.HasValue()) + { + model.Args[0].MemberValue = first.Value; + } + } + if (this.Context.TableEnumIsString == true) + { + List enumStringList = new List(); + foreach (var inItem in (model.Args[0].MemberValue as IEnumerable)) + { + if (inItem != null) + { + if (UtilMethods.GetUnderType(inItem.GetType()).IsEnum()) + { + enumStringList.Add(inItem.ToString()); + } + } + } + if (enumStringList.Count != 0) + { + model.Args[0].MemberValue = enumStringList; + } + } + var caResult = this.Context.DbMehtods.ContainsArray(model); + this.Context.Parameters.RemoveAll(it => it.ParameterName == model.Args[0].MemberName.ObjToString()); + return caResult; + case "ContainsArrayUseSqlParameters": + if (model.Args[0].MemberValue == null) + { + var first = this.Context.Parameters.FirstOrDefault(it => it.ParameterName == model.Args[0].MemberName.ObjToString()); + if (first.HasValue()) + { + model.Args[0].MemberValue = first.Value; + } + } + model.Data = this.Context.SqlParameterKeyWord + "INP_" + this.Context.ParameterIndex; + this.Context.ParameterIndex++; + if (model.Args[0].MemberValue.HasValue()) + { + var inValueIEnumerable = (IEnumerable)model.Args[0].MemberValue; + int i = 0; + foreach (var item in inValueIEnumerable) + { + this.Context.Parameters.Add(new SugarParameter(model.Data + "_" + i, item)); + i++; + } + } + var caResult2 = this.Context.DbMehtods.ContainsArrayUseSqlParameters(model); + this.Context.Parameters.RemoveAll(it => it.ParameterName == model.Args[0].MemberName.ObjToString()); + return caResult2; + case "Equals": + return this.Context.DbMehtods.Equals(model); + case "EqualsNull": + return this.Context.DbMehtods.EqualsNull(model); + case "DateIsSame": + if (model.Args.Count == 2) + return this.Context.DbMehtods.DateIsSameDay(model); + else + { + var dsResult = this.Context.DbMehtods.DateIsSameByType(model); + this.Context.Parameters.RemoveAll(it => it.ParameterName == model.Args[2].MemberName.ObjToString()); + return dsResult; + } + case "DateAdd": + model.Conext = this.Context; + if (model.Args.Count == 2) + return this.Context.DbMehtods.DateAddDay(model); + else + { + var daResult = this.Context.DbMehtods.DateAddByType(model); + this.Context.Parameters.RemoveAll(it => it.ParameterName == model.Args[2].MemberName.ObjToString()); + return daResult; + } + case "DateValue": + var dvResult = this.Context.DbMehtods.DateValue(model); + this.Context.Parameters.RemoveAll(it => it.ParameterName == model.Args[1].MemberName.ObjToString()); + return dvResult; + case "Between": + return this.Context.DbMehtods.Between(model); + case "StartsWith": + return GetLike(this.Context.DbMehtods.StartsWith(model), iLike); + case "EndsWith": + return GetLike(this.Context.DbMehtods.EndsWith(model), iLike); + case "ToInt32": + return this.Context.DbMehtods.ToInt32(model); + case "ToInt64": + return this.Context.DbMehtods.ToInt64(model); + case "ToDate": + return this.Context.DbMehtods.ToDate(model); + case "ToDateTime": + return this.Context.DbMehtods.ToDate(model); + case "ToTime": + return this.Context.DbMehtods.ToTime(model); + case "ToString": + if (model.Args.Count > 1) + { + var dateString2 = this.Context.DbMehtods.GetDateString(model.Args.First().MemberName.ObjToString(), model.Args.Last().MemberValue.ObjToString()); + if (IsSqlServerModel()) + { + return string.Format("FORMAT({0},'{1}','en-US')", model.Args.First().MemberName.ObjToString(), model.Args.Last().MemberValue.ObjToString()); + } + if (dateString2 != null) return dateString2; + return GeDateFormat(model.Args.Last().MemberValue.ObjToString(), model.Args.First().MemberName.ObjToString()); + } + //Check.Exception(model.Args.Count > 1, "ToString (Format) is not supported, Use ToString().If time formatting can be used it.Date.Year+\"-\"+it.Data.Month+\"-\"+it.Date.Day "); + return this.Context.DbMehtods.ToString(model); + case "ToVarchar": + return this.Context.DbMehtods.ToVarchar(model); + case "ToDecimal": + return this.Context.DbMehtods.ToDecimal(model); + case "ToGuid": + return this.Context.DbMehtods.ToGuid(model); + case "ToDouble": + return this.Context.DbMehtods.ToDouble(model); + case "ToBool": + return this.Context.DbMehtods.ToBool(model); + case "ToBoolean": + return this.Context.DbMehtods.ToBool(model); + case "Substring": + if (model.Args.Count == 2) + { + model.Args.Add(new MethodCallExpressionArgs() + { + MemberName = "100000", + IsMember = true, + MemberValue = "100000", + }); + } + model.Conext = this.Context; + return this.Context.DbMehtods.Substring(model); + case "Replace": + return this.Context.DbMehtods.Replace(model); + case "Length": + return this.Context.DbMehtods.Length(model); + case "AggregateSum": + return this.Context.DbMehtods.AggregateSum(model); + case "AggregateAvg": + return this.Context.DbMehtods.AggregateAvg(model); + case "AggregateMin": + return this.Context.DbMehtods.AggregateMin(model); + case "AggregateMax": + return this.Context.DbMehtods.AggregateMax(model); + case "AggregateCount": + return this.Context.DbMehtods.AggregateCount(model); + case "AggregateDistinctCount": + return this.Context.DbMehtods.AggregateDistinctCount(model); + case "MappingColumn": + var mappingColumnResult = this.Context.DbMehtods.MappingColumn(model); + if (model.Args.Count == 1 && mappingColumnResult.IsNullOrEmpty()) + { + return model.Args[0].MemberName.ObjToString().TrimStart('\'').TrimEnd('\''); + } + var isValid = model.Args[0].IsMember && model.Args[1].IsMember == false; + //Check.Exception(!isValid, "SqlFunc.MappingColumn parameters error, The property name on the left, string value on the right"); + if (model.Args.Count > 1) + { + this.Context.Parameters.RemoveAll(it => it.ParameterName == model.Args[1].MemberName.ObjToString()); + } + else + { + this.Context.Parameters.RemoveAll(it => it.ParameterName == model.Args[0].MemberName.ObjToString()); + } + if (string.IsNullOrEmpty(mappingColumnResult)) + { + return model.Args[1].MemberName.ObjToString().TrimStart('\'').TrimEnd('\''); + } + return mappingColumnResult; + case "IsNull": + return this.Context.DbMehtods.IsNull(model); + case "MergeString": + return this.Context.DbMehtods.MergeString(model.Args.Select(it => it.MemberName.ObjToString()).ToArray()); + case "SelectAll": + case "GetSelfAndAutoFill": + this.Context.Parameters.RemoveAll(it => it.ParameterName == model.Args[0].MemberName.ObjToString()); + var result1 = this.Context.DbMehtods.GetSelfAndAutoFill(this.Context.GetTranslationColumnName(model.Args[0].MemberValue.ObjToString()), this.Context.IsSingle); + if ((model.Args[0].MemberValue + "") == "." && this.Context.IsSingle) + { + result1 = this.Context.GetTranslationTableName(model.Args[0].MemberName + "", false) + ".*/**/" + result1; + } + return result1; + case "GetDate": + if (this.Context?.SugarContext?.Context?.CurrentConnectionConfig?.MoreSettings?.DatabaseModel == DbType.SqlServer) + { + return "GetDate()"; + } + return this.Context.DbMehtods.GetDate(); + case "GetRandom": + return this.Context.DbMehtods.GetRandom(); + case "CharIndex": + return this.Context.DbMehtods.CharIndex(model); + case "BitwiseAnd": + return this.Context.DbMehtods.BitwiseAnd(model); + case "BitwiseInclusiveOR": + return this.Context.DbMehtods.BitwiseInclusiveOR(model); + case "ToDateShort": + return this.Context.DbMehtods.ToDateShort(model); + case "Oracle_ToChar": + return this.Context.DbMehtods.Oracle_ToChar(model); + case "Oracle_ToDate": + return this.Context.DbMehtods.Oracle_ToDate(model); + case "SqlServer_DateDiff": + return this.Context.DbMehtods.SqlServer_DateDiff(model); + case "Format": + var xx = base.BaseParameter; + var result = this.Context.DbMehtods.Format(model); + if (this.Context.MethodName == "MappingColumn" || this.Context.MethodName?.StartsWith("Row") == true) + { + result = this.Context.DbMehtods.FormatRowNumber(model); + } + this.Context.Parameters.RemoveAll(it => model.Args.Select(x => x.MemberName.ObjToString()).Contains(it.ParameterName)); + return result; + case "Abs": + return this.Context.DbMehtods.Abs(model); + case "Round": + return this.Context.DbMehtods.Round(model); + case "DateDiff": + return this.Context.DbMehtods.DateDiff(model); + case "GreaterThan": + return this.Context.DbMehtods.GreaterThan(model); + case "GreaterThanOrEqual": + return this.Context.DbMehtods.GreaterThanOrEqual(model); + case "LessThan": + return this.Context.DbMehtods.LessThan(model); + case "LessThanOrEqual": + return this.Context.DbMehtods.LessThanOrEqual(model); + case "Asc": + return this.Context.DbMehtods.Asc(model); + case "Desc": + return this.Context.DbMehtods.Desc(model); + case "Stuff": + return this.Context.DbMehtods.Stuff(model); + case "RowNumber": + return this.Context.DbMehtods.RowNumber(model); + case "Rank": + return this.Context.DbMehtods.RowNumber(model).Replace("row_number() over(", " rank() over("); + case "DenseRank": + return this.Context.DbMehtods.RowNumber(model).Replace("row_number() over(", " dense_rank() over("); + case "RowCount": + return this.Context.DbMehtods.RowCount(model); + case "RowSum": + return this.Context.DbMehtods.RowSum(model); + case "RowMax": + return this.Context.DbMehtods.RowMax(model); + case "RowMin": + return this.Context.DbMehtods.RowMin(model); + case "RowAvg": + return this.Context.DbMehtods.RowAvg(model); + case "Exists": + if (model.Args.Count > 1) + { + this.Context.Parameters.RemoveAll(it => model.Args[1].MemberName.ObjToString().Contains(it.ParameterName)); + List conditionalModels = (List)model.Args[1].MemberValue; + var sqlObj = this.Context.SugarContext.Context.Queryable().SqlBuilder.ConditionalModelToSql(conditionalModels, 0); + var sql = sqlObj.Key; + UtilMethods.RepairReplicationParameters(ref sql, sqlObj.Value, 0, "_" + this.Context.ParameterIndex + "_B"); + model.Args[1].MemberName = sql; + if (sqlObj.Value != null) + { + this.Context.Parameters.AddRange(sqlObj.Value); + } + else + { + return " 1=1 "; + } + } + return this.Context.DbMehtods.Exists(model); + + case "JsonField": + return this.Context.DbMehtods.JsonField(model); + case "JsonArrayLength": + return this.Context.DbMehtods.JsonArrayLength(model); + case "JsonContainsFieldName": + return this.Context.DbMehtods.JsonContainsFieldName(model); + case "JsonParse": + return this.Context.DbMehtods.JsonParse(model); + case "JsonLike": + return this.Context.DbMehtods.JsonLike(model); + case "Collate": + return this.Context.DbMehtods.Collate(model); + case "AggregateSumNoNull": + return this.Context.DbMehtods.AggregateSumNoNull(model); + case "JsonListObjectAny": + return this.Context.DbMehtods.JsonListObjectAny(model); + case "JsonArrayAny": + return this.Context.DbMehtods.JsonArrayAny(model); + case "CompareTo": + return this.Context.DbMehtods.CompareTo(model); + case "SplitIn": + return this.Context.DbMehtods.SplitIn(model); + case "ListAny": + this.Context.Result.IsNavicate = true; + this.Context.Parameters.RemoveAll(it => model.Args[0].MemberName.ObjToString().Contains(it.ParameterName)); + return this.Context.DbMehtods.ListAny(model); + case "ListAll": + this.Context.Result.IsNavicate = true; + this.Context.Parameters.RemoveAll(it => model.Args[0].MemberName.ObjToString().Contains(it.ParameterName)); + return this.Context.DbMehtods.ListAll(model); + case "Modulo": + return this.Context.DbMehtods.Modulo(model); + case "Like": + return GetLike(this.Context.DbMehtods.Like(model), iLike); + case "ToSingle": + return this.Context.DbMehtods.ToSingle(model); + case "GreaterThan_LinqDynamicCore": + return this.Context.DbMehtods.GreaterThan(model); + case "LessThan_LinqDynamicCore": + return this.Context.DbMehtods.LessThan(model); + default: + if (typeof(IDbMethods).GetMethods().Any(it => it.Name == name)) + { + return this.Context.DbMehtods.GetType().GetMethod(name).Invoke(this.Context.DbMehtods, new object[] { model }); + } + break; + } + } + return null; + } + + private bool IsSqlServerModel() + { + return this.Context?.SugarContext?.Context?.CurrentConnectionConfig?.MoreSettings?.DatabaseModel == DbType.SqlServer; + } + + private string GetLike(string result, bool iLike) + { + if (iLike) + { + result = result.Replace(" like ", " ilike "); + } + return result; + } + + private DbType GetType(string name) + { + DbType result = DbType.SqlServer; + foreach (var item in UtilMethods.EnumToDictionary()) + { + if (name.StartsWith(item.Value.ToString())) + { + result = item.Value; + break; + } + } + return result; + } + + private bool IsContainsArray(MethodCallExpression express, string methodName, bool isValidNativeMethod) + { + if (isMemoryExtensionsContainsArray(express, methodName)) + { + return true; + } + return !isValidNativeMethod && express.Method.DeclaringType.Namespace.IsIn("System.Collections", "System.Linq", "System.Collections.Generic") && methodName == "Contains"; + } + private bool isMemoryExtensionsContainsArray(MethodCallExpression express, string methodName) + { + var isMemoryExtensionsContainsArray = false; + if (express.Method.DeclaringType.Name == "MemoryExtensions" && methodName == "Contains") + { + if (express.Arguments.Count == 2) + { + if (express.Arguments.First() is MethodCallExpression callExpression) + { + if (callExpression.Method.Name == "op_Implicit") + { + isMemoryExtensionsContainsArray = true; + } + } + } + } + + return isMemoryExtensionsContainsArray; + } + + private bool IsSubMethod(MethodCallExpression express, string methodName) + { + return SubTools.SubItemsConst.Any(it => it.Name == methodName) && (express.Object?.Type.Name.StartsWith("Subqueryable`") == true); + } + private bool CheckMethod(MethodCallExpression expression) + { + if (expression?.Object?.Type?.Name?.StartsWith("ISugarQueryable`") == true) + { + Check.ExceptionEasy("Sublookup is implemented using SqlFunc.Subquery(); Queryable objects cannot be used", "子查请使用SqlFunc.Subquery()来实现,不能用Queryable对象"); + } + if (expression.Method.Name == "SelectAll") + { + return true; + } + if (expression.Method.Name == "IndexOf") + { + return true; + } + if (expression.Method.Name == "CompareTo") + { + return true; + } + if (expression.Method.Name == "Any" && expression.Arguments.Count > 0 && ExpressionTool.IsVariable(expression.Arguments[0])) + { + return true; + } + if (expression.Method.Name == "All" && expression.Arguments.Count > 0 && ExpressionTool.IsVariable(expression.Arguments[0])) + { + return true; + } + if (expression.Method.Name == "Format" && expression.Method.DeclaringType == UtilConstants.StringType) + { + return true; + } + if (IsExtMethod(expression.Method.Name)) + return true; + if (IsParseMethod(expression)) + return true; + if (expression.Method.Name == "IsNullOrEmpty" && expression.Method.DeclaringType == UtilConstants.StringType) + { + return true; + } + if (expression.Method.Name == "IsNullOrWhiteSpace" && expression.Method.DeclaringType == UtilConstants.StringType) + { + return true; + } + if (expression.Method.ReflectedType().FullName != ExpressionConst.SqlFuncFullName) + return false; + else + return true; + } + private Type TempParseType; + public bool IsParseMethod(MethodCallExpression expression) + { + if (expression.Method.Name == "Parse" && expression.Method.DeclaringType.IsIn( + UtilConstants.DecType, + UtilConstants.DateType, + UtilConstants.DobType, + UtilConstants.GuidType, + UtilConstants.FloatType, + UtilConstants.ShortType, + UtilConstants.LongType, + UtilConstants.IntType, + UtilConstants.BoolType)) + { + TempParseType = expression.Method.DeclaringType; + return true; + } + return false; + } + private static bool IsNot(Expression item) + { + return item is UnaryExpression && (item as UnaryExpression).NodeType == ExpressionType.Not; + } + private bool IsDateItemValue(Expression item) + { + var result = false; + if (item is MemberExpression) + { + var memberExp = item as MemberExpression; + if (memberExp?.Expression != null && memberExp.Expression.Type == UtilConstants.DateType) + { + foreach (var dateType in UtilMethods.EnumToDictionary()) + { + if (memberExp.Member.Name.EqualCase(dateType.Key)) + { + result = true; + break; + } + else if (memberExp.Member.Name == "DayOfWeek") + { + result = true; + break; + } + } + } + } + + return result; + } + private static bool IsDateDate(Expression item) + { + return item.Type == UtilConstants.DateType && item is MemberExpression && (item as MemberExpression).Member.Name == "Date" && item.ToString() != "DateTime.Now.Date"; + } + private static bool IsDateValue(Expression item) + { + return item.Type == UtilConstants.IntType && + item is MemberExpression && + (item as MemberExpression).Expression != null && + (item as MemberExpression).Expression.Type == UtilConstants.DateType && + (item as MemberExpression).Expression is MemberExpression && + ((item as MemberExpression).Expression as MemberExpression).Member.Name == "Value"; + } + private bool IsValidNativeMethod(MethodCallExpression express, string methodName) + { + return MethodMapping.ContainsKey(methodName) && express.Method.DeclaringType.Namespace == ("System"); + } + private bool IsExtMethod(string methodName) + { + if (this.Context.SqlFuncServices == null) return false; + return this.Context.SqlFuncServices.Select(it => it.UniqueMethodName).Contains(methodName); + } + private bool IsIfElse(MethodCallExpression express, string methodName) + { + if (methodName == "End" && express.Object.Type == typeof(CaseWhen)) + return true; + else + return false; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/NewArrayExpessionResolve.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/NewArrayExpessionResolve.cs new file mode 100644 index 000000000..fb92e2903 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/NewArrayExpessionResolve.cs @@ -0,0 +1,112 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public class NewArrayExpessionResolve : BaseResolve + { + public NewArrayExpessionResolve(ExpressionParameter parameter) : base(parameter) + { + var expression = (NewArrayExpression)base.Expression; + switch (base.Context.ResolveType) + { + case ResolveExpressType.WhereSingle: + case ResolveExpressType.WhereMultiple: + case ResolveExpressType.SelectSingle: + case ResolveExpressType.SelectMultiple: + case ResolveExpressType.FieldSingle: + case ResolveExpressType.FieldMultiple: + #region Filed + try + { + var value = ExpressionTool.DynamicInvoke(expression); + var isLeft = parameter.IsLeft; + var baseParameter = parameter.BaseParameter; + var isSetTempData = baseParameter.CommonTempData.HasValue() && baseParameter.CommonTempData.Equals(CommonTempDataType.Result); + if (isSetTempData) + { + baseParameter.CommonTempData = value; + } + else + { + var parentIsBinary = parameter.BaseParameter.CurrentExpression is BinaryExpression; + var parentIsRoot = parameter.BaseParameter.CurrentExpression is LambdaExpression; + var isBool = value != null && value.GetType() == UtilConstants.BoolType; + if (parentIsRoot && isBool) + { + this.Context.Result.Append(value.ObjToBool() ? this.Context.DbMehtods.True() : this.Context.DbMehtods.False()); + break; + } + if (parentIsBinary && isBool) + { + var isLogicOperator = + parameter.BaseExpression.NodeType == ExpressionType.And || + parameter.BaseExpression.NodeType == ExpressionType.AndAlso || + parameter.BaseExpression.NodeType == ExpressionType.Or || + parameter.BaseExpression.NodeType == ExpressionType.OrElse; + if (isLogicOperator) + { + AppendMember(parameter, isLeft, (value.ObjToBool() ? this.Context.DbMehtods.True() : this.Context.DbMehtods.False())); + break; + } + } + if (value == null && parentIsBinary) + { + parameter.BaseParameter.ValueIsNull = true; + value = this.Context.DbMehtods.Null(); + } + AppendValue(parameter, isLeft, value); + } + } + catch (Exception) + { + Check.ThrowNotSupportedException("NewArrayExpression"); + } + #endregion + break; + case ResolveExpressType.ArraySingle: + ArraySingle(expression); + break; + case ResolveExpressType.Join: + Join(parameter, expression); + break; + default: + break; + } + } + + private void Join(ExpressionParameter parameter, NewArrayExpression expression) + { + base.Context.ResolveType = ResolveExpressType.WhereMultiple; + int i = 0; + foreach (var item in expression.Expressions) + { + if (item is UnaryExpression) + { + base.Expression = item; + base.Start(); + if (parameter.CommonTempData is JoinType) + { + if (i > 0) + { + base.Context.Result.Append("," + parameter.CommonTempData.ObjToString().Replace(",", UtilConstants.ReplaceCommaKey) + ","); + } + else + { + base.Context.Result.Append(parameter.CommonTempData.ObjToString().Replace(",", UtilConstants.ReplaceCommaKey) + ","); + } + ++i; + } + } + } + } + + private void ArraySingle(NewArrayExpression expression) + { + foreach (var item in expression.Expressions) + { + base.Expression = item; + base.Start(); + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/NewExpressionResolve.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/NewExpressionResolve.cs new file mode 100644 index 000000000..4b2c3a2bd --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/NewExpressionResolve.cs @@ -0,0 +1,174 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public class NewExpressionResolve : BaseResolve + { + public NewExpressionResolve(ExpressionParameter parameter) : base(parameter) + { + var expression = base.Expression as NewExpression; + if (expression.Type.IsIn(UtilConstants.DateType, UtilConstants.GuidType)) + { + NewValueType(parameter, expression); + return; + } + switch (parameter.Context.ResolveType) + { + case ResolveExpressType.WhereSingle: + Check.ThrowNotSupportedException(expression.ToString()); + break; + case ResolveExpressType.WhereMultiple: + Check.ThrowNotSupportedException(expression.ToString()); + break; + case ResolveExpressType.SelectSingle: + Check.Exception(expression.Type == UtilConstants.DateType, "ThrowNotSupportedException {0} ", expression.ToString()); + Select(expression, parameter, true); + break; + case ResolveExpressType.SelectMultiple: + Check.Exception(expression.Type == UtilConstants.DateType, "ThrowNotSupportedException {0} ", expression.ToString()); + Select(expression, parameter, false); + break; + case ResolveExpressType.FieldSingle: + Check.ThrowNotSupportedException(expression.ToString()); + break; + case ResolveExpressType.FieldMultiple: + case ResolveExpressType.ArrayMultiple: + case ResolveExpressType.ArraySingle: + ArraySingle(expression); + break; + case ResolveExpressType.Join: + Join(expression); + break; + default: + break; + } + } + + private void Join(NewExpression expression) + { + base.Context.ResolveType = ResolveExpressType.WhereMultiple; + int i = 0; + foreach (var item in expression.Arguments) + { + if (item.Type != typeof(JoinType)) + { + base.Expression = item; + base.Start(); + } + if (item.Type == typeof(JoinType)) + { + var joinValue = item.ObjToString(); + if (joinValue.Contains('(')) + { + joinValue = ExpressionTool.DynamicInvoke(item).ObjToString(); + } + if (i > 0) + { + base.Context.Result.Append("," + joinValue + ","); + } + else + { + base.Context.Result.Append(joinValue + ","); + } + ++i; + } + } + } + + private void ArraySingle(NewExpression expression) + { + foreach (var item in expression.Arguments) + { + if (IsDateValue(item)) + { + var value = GetNewExpressionValue(item); + base.Context.Result.Append(value); + } + else + { + base.Expression = item; + base.Start(); + } + } + } + + private bool IsDateValue(Expression item) + { + var isMember = item is MemberExpression; + if (isMember) + { + var m = (item as MemberExpression); + var isInt = m.Type == UtilConstants.IntType; + if (m.Expression != null && isInt && m.Expression is MemberExpression) + { + var mm = (m.Expression as MemberExpression); + if (m.Member.Name.IsIn("Year", "Day", "Month") && mm.Type == UtilConstants.DateType) + { + return true; + } + } + } + return false; + } + + private void NewValueType(ExpressionParameter parameter, NewExpression expression) + { + try + { + var value = ExpressionTool.DynamicInvoke(expression); + var isSetTempData = parameter.CommonTempData.HasValue() && parameter.CommonTempData.Equals(CommonTempDataType.Result); + if (isSetTempData) + { + parameter.CommonTempData = value; + } + else + { + AppendValue(parameter, parameter.IsLeft, value); + } + } + catch (Exception ex) + { + Check.Exception(expression.Type == UtilConstants.DateType, "ThrowNotSupportedException {0} ", ex.ToString()); + } + } + + private void Select(NewExpression expression, ExpressionParameter parameter, bool isSingle) + { + if (expression.Arguments != null) + { + int i = 0; + foreach (var item in expression.Arguments) + { + + string memberName = expression.Members?[i]?.Name; + if (memberName == null && expression.Members == null && item is MemberExpression member) + { + memberName = member.Member.Name; + this.Context.SugarContext.QueryBuilder.IsParameterizedConstructor = true; + } + if (this.Context?.SugarContext?.QueryBuilder?.AppendNavInfo?.MappingNavProperties?.ContainsKey(memberName) == true) + { + ++i; + continue; + } + ++i; + if (item is ParameterExpression) + { + var itemType = item.Type; + var ignoreProperty = itemType.GetProperties().FirstOrDefault(it => it.PropertyType == itemType); + if (ignoreProperty != null && ignoreProperty.Name != memberName) + { + if (this.Context.SugarContext.QueryBuilder.SelectNewIgnoreColumns == null) + { + this.Context.SugarContext.QueryBuilder.SelectNewIgnoreColumns = new List>(); + } + this.Context.SugarContext.QueryBuilder.SelectNewIgnoreColumns.Add(new KeyValuePair(ignoreProperty.Name, itemType.Name)); + } + } + ResolveNewExpressions(parameter, item, memberName); + } + } + } + } +} + diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/OneToManyNavgateExpression.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/OneToManyNavgateExpression.cs new file mode 100644 index 000000000..cb01caa25 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/OneToManyNavgateExpression.cs @@ -0,0 +1,337 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + internal class OneToManyNavgateExpression + { + private SqlSugarProvider context; + private EntityInfo EntityInfo; + private EntityInfo ProPertyEntity; + private Navigate Navigat; + public string ShorName; + public string PropertyShortName; + private string MemberName; + private string MethodName; + private string whereSql; + public int ParameterIndex = 0; + private MethodCallExpressionResolve methodCallExpressionResolve; + public OneToManyNavgateExpression(SqlSugarProvider context, MethodCallExpressionResolve methodCallExpressionResolve) + { + this.context = context; + this.methodCallExpressionResolve = methodCallExpressionResolve; + } + + internal bool IsNavgate(Expression expression) + { + var result = false; + var exp = expression; + if (exp is UnaryExpression) + { + exp = (exp as UnaryExpression).Operand; + } + if (exp is MethodCallExpression) + { + var memberExp = exp as MethodCallExpression; + MethodName = memberExp.Method.Name; + if (memberExp.Method.Name.IsIn("Any", "Count") && memberExp.Arguments.Count > 0 && memberExp.Arguments[0] is MemberExpression) + { + result = ValidateNav(result, memberExp.Arguments[0] as MemberExpression, memberExp.Arguments[0]); + if (memberExp.Arguments.Count > 1) + { + var pars = ExpressionTool.ExpressionParameters(memberExp.Arguments.Last()); + if (pars != null && ProPertyEntity != null && pars.Any(z => z.Type == ProPertyEntity.Type)) + { + PropertyShortName = pars.First(z => z.Type == ProPertyEntity.Type).Name; + } + whereSql = GetWhereSql(memberExp); + } + } + } + return result; + } + + private string GetWhereSql(MethodCallExpression memberExp) + { + var whereExp = memberExp.Arguments[1]; + if (PropertyShortName.HasValue() && Navigat != null && Navigat.NavigatType == NavigateType.OneToMany) + { + InitType(whereExp); + var result = this.methodCallExpressionResolve.GetNewExpressionValue(whereExp, ResolveExpressType.WhereMultiple); + return result; + } + else if (whereExp != null && whereExp.Type == typeof(List)) + { + var value = ExpressionTool.GetExpressionValue(whereExp) as List; + //this.context.Utilities.Context.Queryable().Where(value).ToList(); + if (value.HasValue()) + { + var sqlbuilder = this.context.Queryable().SqlBuilder; + var sqlObj = sqlbuilder.ConditionalModelToSql(value, 0); + var sql = sqlObj.Key; + var count = methodCallExpressionResolve?.Context?.SugarContext?.QueryBuilder?.Parameters?.Count ?? 0; + UtilMethods.RepairReplicationParameters(ref sql, sqlObj.Value, 0, "_" + count + "_"); + methodCallExpressionResolve.Context.Parameters.AddRange(sqlObj.Value); + return sql; + } + else + { + return " 1=1 "; + } + } + else + { + InitType(whereExp); + var result = this.methodCallExpressionResolve.GetNewExpressionValue(whereExp); + return result; + } + } + + private void InitType(Expression whereExp) + { + if (whereExp is LambdaExpression) + { + var parameters = (whereExp as LambdaExpression).Parameters; + if (parameters?.Count > 0) + { + foreach (var item in parameters) + { + this.context.InitMappingInfo(item.Type); + } + } + } + } + + private bool ValidateNav(bool result, MemberExpression memberExp, Expression childExpression) + { + if (childExpression != null && childExpression is MemberExpression) + { + var child2Expression = (childExpression as MemberExpression).Expression; + if (child2Expression.Type.IsClass() && child2Expression is ParameterExpression) + { + var rootType = child2Expression.Type; + var rootEntity = this.context.EntityMaintenance.GetEntityInfo(rootType); + var type = childExpression.Type.GetGenericArguments()[0]; + var entity = this.context.EntityMaintenance.GetEntityInfo(type); + if (rootEntity.Columns.Any(x => x.PropertyName == (childExpression as MemberExpression).Member.Name && x.Navigat != null)) + { + EntityInfo = rootEntity; + ShorName = child2Expression.ToString(); + MemberName = memberExp.Member.Name; + ProPertyEntity = entity; + Navigat = rootEntity.Columns.FirstOrDefault(x => x.PropertyName == (childExpression as MemberExpression).Member.Name).Navigat; + result = true; + } + } + } + + return result; + } + internal MapperSql GetSql() + { + if (Navigat.NavigatType == NavigateType.OneToMany) + { + return GetOneToManySql(); + } + else if (Navigat.NavigatType == NavigateType.Dynamic) + { + return GetDynamicSql(); + } + else + { + return GetManyToManySql(); + } + + } + + private MapperSql GetDynamicSql() + { + if (Navigat.Name == null) + { + Check.ExceptionEasy( + true, + " NavigateType.Dynamic User-defined navigation objects need to be configured with json to be used in expressions . " + this.ProPertyEntity.Type.Name, + " NavigateType.Dynamic 自定义导航对象需要配置json才能在表达式中使用。 " + this.ProPertyEntity.Type.Name); + } + MapperSql mapper = new MapperSql(); + var queryable = this.context.Queryable(); + //selectName = queryable.QueryBuilder.Builder.GetTranslationColumnName(selectName); + if (PropertyShortName.HasValue()) + { + queryable.QueryBuilder.TableShortName = PropertyShortName; + } + queryable.QueryBuilder.LambdaExpressions.ParameterIndex = 500; + var isClearFilter = false; + Type[] clearTypes = null; + if (this.methodCallExpressionResolve?.Context?.SugarContext?.QueryBuilder != null) + { + queryable.QueryBuilder.LambdaExpressions.ParameterIndex = 500 + this.methodCallExpressionResolve.Context.SugarContext.QueryBuilder.LambdaExpressions.ParameterIndex; + this.methodCallExpressionResolve.Context.SugarContext.QueryBuilder.LambdaExpressions.ParameterIndex++; + isClearFilter = this.methodCallExpressionResolve.Context.SugarContext.QueryBuilder.IsDisabledGobalFilter; + clearTypes = this.methodCallExpressionResolve.Context.SugarContext.QueryBuilder.RemoveFilters; + } + queryable + .AS(this.ProPertyEntity.DbTableName) + .ClearFilter(clearTypes) + .Filter(isClearFilter ? null : this.ProPertyEntity.Type) + .WhereIF(!string.IsNullOrEmpty(whereSql), whereSql); + var json = Newtonsoft.Json.Linq.JArray.Parse(Navigat.Name); + foreach (var item in json) + { + string m = item["m"] + ""; + string c = item["c"] + ""; + var leftName = this.EntityInfo.Columns.First(it => it.PropertyName == m).DbColumnName; + var rightName = this.ProPertyEntity.Columns.First(it => it.PropertyName == c).DbColumnName; + queryable.Where($" {queryable.SqlBuilder.GetTranslationColumnName(ShorName)}.{queryable.SqlBuilder.GetTranslationColumnName(leftName)}={queryable.SqlBuilder.GetTranslationColumnName(rightName)} "); + } + var sqlObj = queryable.ToSql(); + // .Where($" {name}={queryable.QueryBuilder.Builder.GetTranslationColumnName(ShorName)}.{pk} ").Select(MethodName == "Any" ? "1" : " COUNT(1) ").ToSql(); + if (sqlObj.Value?.Count > 0) + { + foreach (var item in sqlObj.Value) + { + if (!this.methodCallExpressionResolve.Context.Parameters.Any(it => it.ParameterName == item.ParameterName)) + { + this.methodCallExpressionResolve.Context.Parameters.Add(item); + } + } + } + mapper.Sql = sqlObj.Key; + mapper.Sql = $" ({mapper.Sql}) "; + mapper.Sql = GetMethodSql(mapper.Sql); + return mapper; + } + + private MapperSql GetManyToManySql() + { + + var bPk = this.ProPertyEntity.Columns.FirstOrDefault(it => it.IsPrimarykey == true)?.DbColumnName; + var aPk = this.EntityInfo.Columns.FirstOrDefault(it => it.IsPrimarykey == true)?.DbColumnName; + if (Navigat.BClassId.HasValue()) + { + bPk = this.ProPertyEntity.Columns.FirstOrDefault(it => it.PropertyName == Navigat.BClassId)?.DbColumnName; + } + if (Navigat.AClassId.HasValue()) + { + aPk = this.EntityInfo.Columns.FirstOrDefault(it => it.PropertyName == Navigat.AClassId)?.DbColumnName; + } + Check.ExceptionEasy(aPk.IsNullOrEmpty(), $"{this.EntityInfo.EntityName}need primary key", $"{this.EntityInfo.EntityName}需要主键"); + Check.ExceptionEasy(bPk.IsNullOrEmpty(), $"{this.ProPertyEntity.EntityName}need primary key", $"{this.ProPertyEntity.EntityName}需要主键"); + MapperSql mapper = new MapperSql(); + var queryable = this.context.Queryable(); + bPk = queryable.QueryBuilder.Builder.GetTranslationColumnName(bPk); + aPk = queryable.QueryBuilder.Builder.GetTranslationColumnName(aPk); + var mappingType = Navigat.MappingType; + Check.ExceptionEasy(mappingType == null, "ManyToMany misconfiguration", "多对多配置错误"); + var mappingEntity = this.context.EntityMaintenance.GetEntityInfo(mappingType); + var mappingTableName = queryable.QueryBuilder.Builder.GetTranslationTableName(mappingEntity.DbTableName); + var mappingA = mappingEntity.Columns.First(it => it.PropertyName == Navigat.MappingAId).DbColumnName; + var mappingB = mappingEntity.Columns.First(it => it.PropertyName == Navigat.MappingBId).DbColumnName; + mappingA = queryable.QueryBuilder.Builder.GetTranslationColumnName(mappingA); + mappingB = queryable.QueryBuilder.Builder.GetTranslationColumnName(mappingB); + var bTableName = queryable.QueryBuilder.Builder.GetTranslationTableName(this.ProPertyEntity.DbTableName); + this.context.InitMappingInfo(mappingType); + var queryBuilerAB = this.context.Queryable().QueryBuilder; + queryBuilerAB.LambdaExpressions.ParameterIndex = 100 + this.ParameterIndex; + var filters = queryBuilerAB.GetFilters(mappingType); + if (filters.HasValue() && this.methodCallExpressionResolve?.Context?.SugarContext?.QueryBuilder?.IsDisabledGobalFilter != true) + { + aPk += " AND " + filters; + if (queryBuilerAB.Parameters != null) + { + this.methodCallExpressionResolve.Context.Parameters.AddRange(queryBuilerAB.Parameters); + } + } + + mapper.Sql = $" (select {(MethodName == "Any" ? "1" : " COUNT(1) ")} from {bTableName} {this.ProPertyEntity.DbTableName}_1 where {this.ProPertyEntity.DbTableName}_1.{bPk} in (select {mappingB} from {mappingTableName} where {mappingA} = {ShorName}.{aPk} ) )"; + if (this.whereSql.HasValue()) + { + mapper.Sql = mapper.Sql.TrimEnd(')'); + if (this.whereSql.Contains($" {PropertyShortName}.")) + { + this.whereSql = this.whereSql.Replace($" {PropertyShortName}.", $" {this.ProPertyEntity.DbTableName}_1."); + } + else if (this.whereSql.Contains($" {queryable.QueryBuilder.Builder.GetTranslationColumnName(PropertyShortName)}.")) + { + this.whereSql = this.whereSql.Replace($" {queryable.QueryBuilder.Builder.GetTranslationColumnName(PropertyShortName)}.", $" {this.ProPertyEntity.DbTableName}_1."); + } + else if (this.whereSql.Contains($"({queryable.QueryBuilder.Builder.GetTranslationColumnName(PropertyShortName)}.")) + { + this.whereSql = this.whereSql.Replace($"({queryable.QueryBuilder.Builder.GetTranslationColumnName(PropertyShortName)}.", $"({this.ProPertyEntity.DbTableName}_1."); + } + mapper.Sql = mapper.Sql + " AND " + this.whereSql + ")"; + } + if (MethodName == "Any") + { + mapper.Sql = $" {mapper.Sql} "; + } + else + { + mapper.Sql = $" ({mapper.Sql}) "; + } + mapper.Sql = GetMethodSql(mapper.Sql); + return mapper; + } + private MapperSql GetOneToManySql() + { + var pkColumn = this.EntityInfo.Columns.FirstOrDefault(it => it.IsPrimarykey == true); + if (Navigat.Name2.HasValue()) + { + pkColumn = this.EntityInfo.Columns.FirstOrDefault(it => it.PropertyName == Navigat.Name2); + } + Check.ExceptionEasy(pkColumn == null, $"{this.EntityInfo.EntityName} need primary key ", + $"导航属性 {this.EntityInfo.EntityName}需要主键"); + var pk = pkColumn.DbColumnName; + var name = this.ProPertyEntity.Columns.First(it => it.PropertyName == Navigat.Name).DbColumnName; + //var selectName = this.ProPertyEntity.Columns.First(it => it.PropertyName == MemberName).DbColumnName; + MapperSql mapper = new MapperSql(); + var queryable = this.context.Queryable(); + pk = queryable.QueryBuilder.Builder.GetTranslationColumnName(pk); + name = queryable.QueryBuilder.Builder.GetTranslationColumnName(name); + //selectName = queryable.QueryBuilder.Builder.GetTranslationColumnName(selectName); + if (PropertyShortName.HasValue()) + { + queryable.QueryBuilder.TableShortName = PropertyShortName; + } + queryable.QueryBuilder.LambdaExpressions.ParameterIndex = 500; + var isClearFilter = false; + Type[] clearTypes = null; + if (this.methodCallExpressionResolve?.Context?.SugarContext?.QueryBuilder != null) + { + queryable.QueryBuilder.LambdaExpressions.ParameterIndex = 500 + this.methodCallExpressionResolve.Context.SugarContext.QueryBuilder.LambdaExpressions.ParameterIndex; + this.methodCallExpressionResolve.Context.SugarContext.QueryBuilder.LambdaExpressions.ParameterIndex++; + isClearFilter = this.methodCallExpressionResolve.Context.SugarContext.QueryBuilder.IsDisabledGobalFilter; + clearTypes = this.methodCallExpressionResolve.Context.SugarContext.QueryBuilder.RemoveFilters; + } + var sqlObj = queryable + .AS(this.ProPertyEntity.DbTableName) + .ClearFilter(clearTypes) + .Filter(isClearFilter ? null : this.ProPertyEntity.Type) + .WhereIF(!string.IsNullOrEmpty(whereSql), whereSql) + .Where($" {name}={queryable.QueryBuilder.Builder.GetTranslationColumnName(ShorName)}.{pk} ").Select(MethodName == "Any" ? "1" : " COUNT(1) ").ToSql(); + if (sqlObj.Value?.Count > 0) + { + foreach (var item in sqlObj.Value) + { + if (!this.methodCallExpressionResolve.Context.Parameters.Any(it => it.ParameterName == item.ParameterName)) + { + this.methodCallExpressionResolve.Context.Parameters.Add(item); + } + } + } + mapper.Sql = sqlObj.Key; + mapper.Sql = $" ({mapper.Sql}) "; + mapper.Sql = GetMethodSql(mapper.Sql); + return mapper; + } + + private string GetMethodSql(string sql) + { + if (MethodName == "Any") + { + return $" ( EXISTS {sql} ) "; + } + return sql; + } + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/OneToManyNavgateExpressionN.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/OneToManyNavgateExpressionN.cs new file mode 100644 index 000000000..d43827b40 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/OneToManyNavgateExpressionN.cs @@ -0,0 +1,311 @@ +using System.Linq.Expressions; +using System.Text.RegularExpressions; + +namespace SqlSugar +{ + internal class OneToManyNavgateExpressionN + { + #region Constructor + public SqlSugarProvider context; + public string shorName; + public EntityInfo entityInfo; + public List items; + public string whereSql; + public MethodCallExpressionResolve methodCallExpressionResolve; + public OneToManyNavgateExpressionN(SqlSugarProvider context, MethodCallExpressionResolve methodCallExpressionResolve) + { + this.context = context; + this.methodCallExpressionResolve = methodCallExpressionResolve; + } + #endregion + + internal bool IsNavgate(Expression expression) + { + var result = false; + var exp = expression; + if (exp is UnaryExpression) + { + exp = (exp as UnaryExpression).Operand; + } + if (exp is MethodCallExpression) + { + var memberExp = exp as MethodCallExpression; + if (memberExp.Method.Name.IsIn("Any", "Count") && memberExp.Arguments.Count > 0 && memberExp.Arguments[0] is MemberExpression) + { + result = ValiteOneManyCall(result, memberExp, memberExp.Arguments[0] as MemberExpression, memberExp.Arguments[0]); + if (memberExp.Arguments.Count > 1) + { + whereSql = GetWhereSql(memberExp); + } + } + } + return result; + } + private bool ValiteOneManyCall(bool result, MethodCallExpression callExpression, MemberExpression memberExp, Expression childExpression) + { + if (childExpression != null && childExpression is MemberExpression) + { + var oldChildExpression = childExpression; + var child2Expression = (childExpression as MemberExpression).Expression; + if (child2Expression == null || (child2Expression is ConstantExpression)) + { + return false; + } + items = new List(); + var childType = oldChildExpression.Type; + if (!childType.FullName.IsCollectionsList()) + { + return false; + } + childType = childType.GetGenericArguments()[0]; + items.Add(new ExpressionItems() { Type = 3, Expression = callExpression, ParentEntityInfo = this.context.EntityMaintenance.GetEntityInfo(childType) }); + items.Add(new ExpressionItems() { Type = 2, Expression = oldChildExpression, ThisEntityInfo = this.context.EntityMaintenance.GetEntityInfo(childType), ParentEntityInfo = this.context.EntityMaintenance.GetEntityInfo(child2Expression.Type) }); + if (items.Any(it => it.Type == 2 && it.Nav == null)) + { + return false; + } + while (child2Expression != null) + { + if (IsClass(child2Expression)) + { + items.Add(new ExpressionItems() { Type = 2, Expression = child2Expression, ThisEntityInfo = this.context.EntityMaintenance.GetEntityInfo(child2Expression.Type), ParentEntityInfo = this.context.EntityMaintenance.GetEntityInfo(GetMemberExpression(child2Expression).Type) }); + child2Expression = GetMemberExpression(child2Expression); + + } + else if (IsParameter(child2Expression)) + { + shorName = child2Expression.ToString(); + entityInfo = this.context.EntityMaintenance.GetEntityInfo(child2Expression.Type); + break; + } + else + { + break; + } + } + if (!items.Any(it => it.Type == 2 && it.Nav == null)) + { + return true; + } + } + return result; + } + public object GetSql() + { + MapperSql MapperSql = new MapperSql(); + var memberInfo = this.items.Where(it => it.Type == 3).First(); + var subInfos = this.items.Where(it => it.Type == 2).Reverse().ToList(); + var formInfo = subInfos.First(); + var joinInfos = subInfos.Skip(1).ToList(); + var i = 0; + var masterShortName = formInfo.ThisEntityInfo.DbTableName + i; + var queryable = this.context.Queryable(masterShortName).AS(formInfo.ThisEntityInfo.DbTableName); + i++; + var lastShortName = ""; + var index = 0; + foreach (var item in joinInfos) + { + if (item.Nav.NavigatType == NavigateType.OneToMany) + { + lastShortName = OneToMany(ref formInfo, ref i, queryable, ref index, item); + } + else if (item.Nav.NavigatType == NavigateType.OneToOne) + { + lastShortName = OneToOne(ref formInfo, ref i, queryable, ref index, item); + } + else + { + lastShortName = ManyToMany(ref formInfo, ref i, queryable, ref index, item); + } + } + var isAny = (memberInfo.Expression as MethodCallExpression).Method.Name == "Any"; + queryable.Select(isAny ? "1" : " COUNT(1) "); + var last = subInfos.First(); + var FirstPkColumn = last.ThisEntityInfo.Columns.FirstOrDefault(it => it.IsPrimarykey); + FirstPkColumn = GetFirstPkColumn(last, FirstPkColumn); + Check.ExceptionEasy(FirstPkColumn == null, $"{last.ThisEntityInfo.EntityName} need PrimayKey", $"使用导航属性{last.ThisEntityInfo.EntityName} 缺少主键"); + var PkColumn = last.ParentEntityInfo.Columns.FirstOrDefault(it => it.PropertyName == last.Nav.Name); + Check.ExceptionEasy(PkColumn == null, $"{last.ParentEntityInfo.EntityName} no found {last.Nav.Name}", $"{last.ParentEntityInfo.EntityName} 不存在 {last.Nav.Name}"); + queryable.Where($" {queryable.SqlBuilder.GetTranslationColumnName(this.shorName)}.{queryable.SqlBuilder.GetTranslationColumnName(PkColumn.DbColumnName)} = {queryable.SqlBuilder.GetTranslationColumnName(masterShortName)}.{queryable.SqlBuilder.GetTranslationColumnName(FirstPkColumn.DbColumnName)} "); + queryable.WhereIF(this.whereSql.HasValue(), GetWhereSql1(this.whereSql, lastShortName, joinInfos, queryable.SqlBuilder)); + MapperSql.Sql = $"( {queryable.ToSql().Key} ) "; + if (isAny) + { + MapperSql.Sql = $" EXISTS( {MapperSql.Sql}) "; + + } + return MapperSql; + } + + private static EntityColumnInfo GetFirstPkColumn(ExpressionItems last, EntityColumnInfo FirstPkColumn) + { + if (last.Nav.NavigatType == NavigateType.OneToOne && last.Nav.Name2.HasValue()) + { + var name2 = last.ThisEntityInfo.Columns.FirstOrDefault(it => it.PropertyName == last.Nav.Name2); + if (name2 != null) + { + FirstPkColumn = name2; + } + } + return FirstPkColumn; + } + + private static string OneToMany(ref ExpressionItems formInfo, ref int i, ISugarQueryable queryable, ref int index, ExpressionItems item) + { + string lastShortName; + var shortName = item.ThisEntityInfo.DbTableName + i; + EntityColumnInfo pkColumn; + EntityColumnInfo navColum; + //if (index == 0) + //{ + pkColumn = item.ThisEntityInfo.Columns.FirstOrDefault(it => it.PropertyName == item.Nav.Name); + navColum = item.ParentEntityInfo.Columns.FirstOrDefault(it => it.IsPrimarykey); + if (item.Nav.Name2.HasValue()) + { + navColum = item.ParentEntityInfo.Columns.FirstOrDefault(it => it.PropertyName == item.Nav.Name2); + } + //} + //else + //{ + // pkColumn = item.ThisEntityInfo.Columns.FirstOrDefault(it => it.IsPrimarykey); + // navColum = item.ParentEntityInfo.Columns.FirstOrDefault(it => it.PropertyName == item.Nav.Name); + //} + Check.ExceptionEasy(pkColumn == null, $"{item.ThisEntityInfo.EntityName} need PrimayKey", $"使用导航属性{item.ThisEntityInfo.EntityName} 缺少主键"); + var on = $" {queryable.SqlBuilder.GetTranslationColumnName(shortName)}.{queryable.SqlBuilder.GetTranslationColumnName(pkColumn.DbColumnName)}={queryable.SqlBuilder.GetTranslationColumnName(formInfo.ThisEntityInfo.DbTableName + (i - 1))}.{queryable.SqlBuilder.GetTranslationColumnName(navColum.DbColumnName)}"; + queryable.AddJoinInfo(item.ThisEntityInfo.DbTableName, shortName, on, JoinType.Inner); + ++i; + index++; + lastShortName = shortName; + formInfo = item; + return lastShortName; + } + + private static string OneToOne(ref ExpressionItems formInfo, ref int i, ISugarQueryable queryable, ref int index, ExpressionItems item) + { + string lastShortName; + var shortName = item.ThisEntityInfo.DbTableName + i; + EntityColumnInfo pkColumn; + EntityColumnInfo navColum; + //if (index == 0) + //{ + // pkColumn = item.ThisEntityInfo.Columns.FirstOrDefault(it => it.PropertyName == item.Nav.Name); + // navColum = item.ParentEntityInfo.Columns.FirstOrDefault(it => it.IsPrimarykey); + //} + //else + //{ + pkColumn = item.ThisEntityInfo.Columns.FirstOrDefault(it => it.IsPrimarykey); + navColum = item.ParentEntityInfo.Columns.FirstOrDefault(it => it.PropertyName == item.Nav.Name); + if (item.Nav.Name2.HasValue()) + { + pkColumn = item.ThisEntityInfo.Columns.FirstOrDefault(it => it.PropertyName == item.Nav.Name2); + } + //} + Check.ExceptionEasy(pkColumn == null, $"{item.ThisEntityInfo.EntityName} need PrimayKey", $"使用导航属性{item.ThisEntityInfo.EntityName} 缺少主键"); + var on = $" {shortName}.{queryable.SqlBuilder.GetTranslationColumnName(pkColumn.DbColumnName)}={formInfo.ThisEntityInfo.DbTableName + (i - 1)}.{queryable.SqlBuilder.GetTranslationColumnName(navColum.DbColumnName)}"; + queryable.AddJoinInfo(item.ThisEntityInfo.DbTableName, shortName, on, JoinType.Inner); + ++i; + index++; + lastShortName = shortName; + formInfo = item; + return lastShortName; + } + + + private string ManyToMany(ref ExpressionItems formInfo, ref int i, ISugarQueryable queryable, ref int index, ExpressionItems item) + { + string lastShortName; + var bshortName = item.ThisEntityInfo.DbTableName + i; + EntityColumnInfo AidColumn; + EntityColumnInfo BidColumn; + + BidColumn = item.ThisEntityInfo.Columns.FirstOrDefault(it => it.IsPrimarykey); + AidColumn = item.ParentEntityInfo.Columns.FirstOrDefault(it => it.IsPrimarykey); + + var abEntity = this.context.EntityMaintenance.GetEntityInfo(item.Nav.MappingType); + var Ab_Aid = abEntity.Columns.FirstOrDefault(it => item.Nav.MappingAId == it.PropertyName); + var Ab_Bid = abEntity.Columns.FirstOrDefault(it => item.Nav.MappingBId == it.PropertyName); + + Check.ExceptionEasy(AidColumn == null, $" {AidColumn.EntityName} need primary key ", $"{AidColumn.EntityName}需要主键"); + Check.ExceptionEasy(AidColumn == null, $" {BidColumn.EntityName} need primary key ", $"{BidColumn.EntityName}需要主键"); + + var abShort = abEntity.EntityName + "_1"; + var abOn = $" {queryable.SqlBuilder.GetTranslationColumnName(abShort)}.{queryable.SqlBuilder.GetTranslationColumnName(Ab_Aid.DbColumnName)}={formInfo.ThisEntityInfo.DbTableName + (i - 1)}.{queryable.SqlBuilder.GetTranslationColumnName(AidColumn.DbColumnName)}"; + queryable.AddJoinInfo(abEntity.DbTableName, abShort, abOn, JoinType.Inner); + var On = $" {bshortName}.{queryable.SqlBuilder.GetTranslationColumnName(BidColumn.DbColumnName)}={abShort}.{queryable.SqlBuilder.GetTranslationColumnName(Ab_Bid.DbColumnName)}"; + queryable.AddJoinInfo(BidColumn.DbTableName, bshortName, On, JoinType.Inner); + ++i; + index++; + lastShortName = bshortName; + formInfo = item; + return lastShortName; + } + + #region Helper + private string GetWhereSql1(string wheresql, string lastShortName, List joinInfos, ISqlBuilder sqlBuilder) + { + var sql = wheresql; + if (sql == null) return sql; + joinInfos.Last().ThisEntityInfo.Columns.ForEach(it => + { + if (it.DbColumnName != null) + { + if (this.whereSql.Contains("." + sqlBuilder.GetTranslationColumnName(it.DbColumnName))) + { + var regex = @"\w+\." + sqlBuilder.GetTranslationColumnName(it.DbColumnName) + .Replace(sqlBuilder.SqlTranslationLeft, "\\" + sqlBuilder.SqlTranslationLeft) + .Replace(sqlBuilder.SqlTranslationRight, "\\" + sqlBuilder.SqlTranslationRight) + .Replace("\\\\", "\\"); + + if (!regex.IsMatch(this.whereSql)) + { + regex = $@"\{sqlBuilder.SqlTranslationLeft}\w+\{sqlBuilder.SqlTranslationRight}\." + sqlBuilder.GetTranslationColumnName(it.DbColumnName) + .Replace(sqlBuilder.SqlTranslationLeft, "\\" + sqlBuilder.SqlTranslationLeft) + .Replace(sqlBuilder.SqlTranslationRight, "\\" + sqlBuilder.SqlTranslationRight) + .Replace("\\\\", "\\"); + } + + this.whereSql = Regex.Replace(this.whereSql, regex, + sqlBuilder.GetTranslationColumnName(lastShortName) + "." + sqlBuilder.GetTranslationColumnName(it.DbColumnName)); + } + else + { + var oldWhere = this.whereSql; + var newWhere = this.whereSql.Replace(sqlBuilder.GetTranslationColumnName(it.DbColumnName), + sqlBuilder.GetTranslationColumnName(lastShortName) + "." + sqlBuilder.GetTranslationColumnName(it.DbColumnName)); + if (oldWhere != newWhere && !oldWhere.Contains($" {sqlBuilder.GetTranslationColumnName(it.DbColumnName)}")) + { + + } + else + { + this.whereSql = newWhere; + } + } + } + }); + return this.whereSql; + } + + private string GetWhereSql(MethodCallExpression memberExp) + { + var whereExp = memberExp.Arguments[1]; + var result = this.methodCallExpressionResolve.GetNewExpressionValue(whereExp); + return result; + } + private static bool IsParameter(Expression child2Expression) + { + return child2Expression.Type.IsClass() && child2Expression is ParameterExpression; + } + + private static Expression GetMemberExpression(Expression child2Expression) + { + return (child2Expression as MemberExpression).Expression; + } + + private static bool IsClass(Expression child2Expression) + { + return child2Expression.Type.IsClass() && child2Expression is MemberExpression; + } + #endregion + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/OneToOneNavgateExpression.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/OneToOneNavgateExpression.cs new file mode 100644 index 000000000..d69c341e0 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/OneToOneNavgateExpression.cs @@ -0,0 +1,203 @@ +using System.Linq.Expressions; +using System.Reflection; + +namespace SqlSugar +{ + internal class OneToOneNavgateExpression + { + public ExpressionContext ExpContext; + private SqlSugarProvider context; + internal EntityInfo EntityInfo; + internal EntityInfo ProPertyEntity; + private Navigate Navigat; + public string ShorName; + internal string MemberName; + private MemberExpressionResolve _memberExpressionResolve; + public OneToOneNavgateExpression(SqlSugarProvider context, MemberExpressionResolve memberExpressionResolve) + { + this.context = context; + _memberExpressionResolve = memberExpressionResolve; + } + + internal bool IsNavgate(Expression expression) + { + var result = false; + var exp = expression; + if (exp is UnaryExpression) + { + exp = (exp as UnaryExpression).Operand; + } + if (exp is MemberExpression) + { + var memberExp = exp as MemberExpression; + var childExpression = memberExp.Expression; + result = ValidateNav(result, memberExp, childExpression); + } + return result; + } + + private bool ValidateNav(bool result, MemberExpression memberExp, Expression childExpression) + { + if (childExpression != null && childExpression is MemberExpression) + { + var child2Expression = (childExpression as MemberExpression).Expression; + if (child2Expression == null) + { + return false; + } + if (child2Expression.Type.IsClass() && child2Expression is ParameterExpression) + { + var entity = this.context.EntityMaintenance.GetEntityInfo(child2Expression.Type); + if (entity.Columns.Any(x => x.PropertyName == (childExpression as MemberExpression).Member.Name && x.Navigat != null)) + { + EntityInfo = entity; + ShorName = child2Expression.ToString(); + MemberName = memberExp.Member.Name; + ProPertyEntity = this.context.EntityMaintenance.GetEntityInfo(childExpression.Type); + Navigat = entity.Columns.FirstOrDefault(x => x.PropertyName == (childExpression as MemberExpression).Member.Name).Navigat; + result = true; + } + } + } + + return result; + } + + internal MapperSql GetSql() + { + if (this.ProPertyEntity.Type.Name.StartsWith("List`")) + { + Check.ExceptionEasy(true, " expression error ", "导航查询出错,比如.Count要改成 .Count()"); + } + else if (Navigat.NavigatType == NavigateType.Dynamic) + { + return NavigatDynamicSql(); + } + var pk = this.ProPertyEntity.Columns.FirstOrDefault(it => it.IsPrimarykey == true)?.DbColumnName; + if (Navigat.Name2.HasValue()) + { + pk = this.ProPertyEntity.Columns.FirstOrDefault(it => it.PropertyName == Navigat.Name2)?.DbColumnName; + } + if (pk == null) + { + Check.ExceptionEasy( + true, + $"{this.ProPertyEntity.EntityName} naviate config error", + $"{this.ProPertyEntity.EntityName} 导航配置错误"); + } + var name = this.EntityInfo.Columns.First(it => it.PropertyName == Navigat.Name).DbColumnName; + var selectName = this.ProPertyEntity.Columns.First(it => it.PropertyName == MemberName).DbColumnName; + MapperSql mapper = new MapperSql(); + var queryable = this.context.Queryable(); + pk = queryable.QueryBuilder.Builder.GetTranslationColumnName(pk); + name = queryable.QueryBuilder.Builder.GetTranslationColumnName(name); + selectName = queryable.QueryBuilder.Builder.GetTranslationColumnName(selectName); + var tableName = this.ProPertyEntity.DbTableName; + if (ExpContext?.SugarContext?.QueryBuilder?.IsCrossQueryWithAttr == true) + { + var attr = this.ProPertyEntity.Type.GetCustomAttribute(); + var configId = ((object)this.context.CurrentConnectionConfig.ConfigId).ObjToString(); + if (attr != null && configId != attr.configId.ObjToString()) + { + var context = this.context.Root.GetConnection(attr.configId); + var dbName = context.Ado.Connection.Database; + if (context.CurrentConnectionConfig.DbLinkName.HasValue()) + { + tableName = UtilMethods.GetTableByDbLink(context, tableName, tableName, attr); + } + else + { + tableName = queryable.QueryBuilder.LambdaExpressions.DbMehtods.GetTableWithDataBase + (queryable.QueryBuilder.Builder.GetTranslationColumnName(dbName), queryable.QueryBuilder.Builder.GetTranslationColumnName(tableName)); + } + } + } + Type[] clearTypes = null; + var isClearFilter = false; + if (this._memberExpressionResolve?.Context?.SugarContext?.QueryBuilder != null) + { + queryable.QueryBuilder.LambdaExpressions.ParameterIndex = 500 + this._memberExpressionResolve.Context.SugarContext.QueryBuilder.LambdaExpressions.ParameterIndex; + this._memberExpressionResolve.Context.SugarContext.QueryBuilder.LambdaExpressions.ParameterIndex++; + isClearFilter = this._memberExpressionResolve.Context.SugarContext.QueryBuilder.IsDisabledGobalFilter; + clearTypes = this._memberExpressionResolve.Context.SugarContext.QueryBuilder.RemoveFilters; + } + var type = this.ProPertyEntity.Columns.Count(it => it.IsPrimarykey) > 1 ? this.ProPertyEntity.Type : null; + if (isClearFilter) + { + type = null; + } + var sqlObj = queryable + .AS(tableName) + .ClearFilter(clearTypes) + .Filter(type) + .WhereIF(Navigat.WhereSql.HasValue(), Navigat.WhereSql) + .Where($" {queryable.SqlBuilder.GetTranslationColumnName(ShorName)}.{name}={pk} ").Select(selectName).ToSql(); + mapper.Sql = sqlObj.Key; + mapper.Sql = $" ({mapper.Sql}) "; + + if (type != null & sqlObj.Value?.Count > 0) + { + foreach (var item in sqlObj.Value) + { + if (!this._memberExpressionResolve.Context.Parameters.Any(it => it.ParameterName == item.ParameterName)) + { + this._memberExpressionResolve.Context.Parameters.Add(item); + } + } + } + + return mapper; + } + + private MapperSql NavigatDynamicSql() + { + if (Navigat.Name == null) + { + Check.ExceptionEasy( + true, + " NavigateType.Dynamic User-defined navigation objects need to be configured with json to be used in expressions . " + this.ProPertyEntity.Type.Name, + " NavigateType.Dynamic 自定义导航对象需要配置json才能在表达式中使用。 " + this.ProPertyEntity.Type.Name); + } + MapperSql mapperSql = new MapperSql(); + //var name = this.EntityInfo.Columns.First(it => it.PropertyName == Navigat.Name).DbColumnName; + var selectName = this.ProPertyEntity.Columns.First(it => it.PropertyName == MemberName).DbColumnName; + MapperSql mapper = new MapperSql(); + var queryable = this.context.Queryable(); + var tableName = this.ProPertyEntity.DbTableName; + Type[] clearTypes = null; + var isClearFilter = false; + if (this._memberExpressionResolve?.Context?.SugarContext?.QueryBuilder != null) + { + queryable.QueryBuilder.LambdaExpressions.ParameterIndex = 500 + this._memberExpressionResolve.Context.SugarContext.QueryBuilder.LambdaExpressions.ParameterIndex; + this._memberExpressionResolve.Context.SugarContext.QueryBuilder.LambdaExpressions.ParameterIndex++; + isClearFilter = this._memberExpressionResolve.Context.SugarContext.QueryBuilder.IsDisabledGobalFilter; + clearTypes = this._memberExpressionResolve.Context.SugarContext.QueryBuilder.RemoveFilters; + } + var type = this.ProPertyEntity.Columns.Count(it => it.IsPrimarykey) > 1 ? this.ProPertyEntity.Type : null; + if (isClearFilter) + { + type = null; + } + queryable + .AS(tableName) + .Take(1) + .ClearFilter(clearTypes) + .Filter(type) + .WhereIF(Navigat.WhereSql.HasValue(), Navigat.WhereSql) + .Select(selectName); + var json = Newtonsoft.Json.Linq.JArray.Parse(Navigat.Name); + foreach (var item in json) + { + string m = item["m"] + ""; + string c = item["c"] + ""; + var leftName = this.EntityInfo.Columns.First(it => it.PropertyName == m).DbColumnName; + var rightName = this.ProPertyEntity.Columns.First(it => it.PropertyName == c).DbColumnName; + queryable.Where($" {queryable.SqlBuilder.GetTranslationColumnName(ShorName)}.{queryable.SqlBuilder.GetTranslationColumnName(leftName)}={queryable.SqlBuilder.GetTranslationColumnName(rightName)} "); + } + var sqlObj = queryable.ToSql(); + mapper.Sql = sqlObj.Key; + mapper.Sql = $" ({mapper.Sql}) "; + return mapper; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/OneToOneNavgateExpressionN.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/OneToOneNavgateExpressionN.cs new file mode 100644 index 000000000..4c9eea535 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/OneToOneNavgateExpressionN.cs @@ -0,0 +1,187 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + internal class OneToOneNavgateExpressionN + { + #region Constructor + public string shorName { get; set; } + public EntityInfo entityInfo; + public List items; + public SqlSugarProvider context; + public ISqlBuilder builder; + public OneToOneNavgateExpressionN(SqlSugarProvider context) + { + this.context = context; + } + #endregion + + #region Api&Core + + public bool IsNavgate(Expression expression) + { + if (this.context == null) return false; + var result = false; + var exp = expression; + if (exp is UnaryExpression) + { + exp = (exp as UnaryExpression).Operand; + } + if (exp is MemberExpression) + { + var memberExp = exp as MemberExpression; + var childExpression = memberExp.Expression; + result = ValidateIsJoinMember(result, memberExp, childExpression); + } + return result; + } + public MapperSql GetMemberSql() + { + MapperSql MapperSql = new MapperSql(); + var memberInfo = this.items.Where(it => it.Type == 1).First(); + var subInfos = this.items.Where(it => it.Type == 2).Reverse().ToList(); + var formInfo = subInfos.First(); + var rootWhereSql = formInfo?.Nav?.WhereSql; + var joinInfos = subInfos.Skip(1).ToList(); + var i = 0; + var masterShortName = formInfo.ThisEntityInfo.DbTableName + i; + var queryable = this.context.Queryable(ToShortName(masterShortName)).AS(formInfo.ThisEntityInfo.DbTableName).Filter(null, true); + builder = queryable.SqlBuilder; + i++; + var lastShortName = ""; + foreach (var item in joinInfos) + { + var shortName = item.ThisEntityInfo.DbTableName + i; + var pkColumn = item.ThisEntityInfo.Columns.FirstOrDefault(it => it.IsPrimarykey); + if (item.Nav.Name2.HasValue()) + { + var nav2NameColumn = item.ThisEntityInfo.Columns.FirstOrDefault(it => it.PropertyName == item.Nav.Name2); + if (nav2NameColumn != null) + { + pkColumn = nav2NameColumn; + } + } + var navColum = item.ParentEntityInfo.Columns.FirstOrDefault(it => it.PropertyName == item.Nav.Name); + Check.ExceptionEasy(pkColumn == null, $"{item.ThisEntityInfo.EntityName} need PrimayKey", $"使用导航属性{item.ThisEntityInfo.EntityName} 缺少主键"); + var on = $" {ToShortName(shortName)}.{queryable.SqlBuilder.GetTranslationColumnName(pkColumn.DbColumnName)}={ToShortName(formInfo.ThisEntityInfo.DbTableName + (i - 1))}.{queryable.SqlBuilder.GetTranslationColumnName(navColum.DbColumnName)}"; + if (item.Nav.WhereSql.HasValue()) + { + on = (on + " AND " + item.Nav.WhereSql); + } + queryable.AddJoinInfo(item.ThisEntityInfo.DbTableName, ToShortName(shortName), on, JoinType.Inner); + ++i; + lastShortName = shortName; + formInfo = item; + } + var selectProperyInfo = ExpressionTool.GetMemberName(memberInfo.Expression); + var selectColumnInfo = memberInfo.ParentEntityInfo.Columns.First(it => it.PropertyName == selectProperyInfo); + if (rootWhereSql?.HasValue() == true) + { + queryable.Where(rootWhereSql); + } + queryable.Select($" {ToShortName(lastShortName)}.{queryable.SqlBuilder.GetTranslationColumnName(selectColumnInfo.DbColumnName)}"); + var last = subInfos.First(); + var FirstPkColumn = last.ThisEntityInfo.Columns.FirstOrDefault(it => it.IsPrimarykey); + if (last.Nav.Name2.HasValue()) + { + var nav2 = last.ThisEntityInfo.Columns.FirstOrDefault(it => it.PropertyName == last.Nav.Name2); + if (nav2 != null) + { + FirstPkColumn = nav2; + } + } + Check.ExceptionEasy(FirstPkColumn == null, $"{last.ThisEntityInfo.EntityName} need PrimayKey", $"使用导航属性{last.ThisEntityInfo.EntityName} 缺少主键"); + var PkColumn = last.ParentEntityInfo.Columns.FirstOrDefault(it => it.PropertyName == last.Nav.Name); + Check.ExceptionEasy(PkColumn == null, $"{last.ParentEntityInfo.EntityName} no found {last.Nav.Name}", $"{last.ParentEntityInfo.EntityName} 不存在 {last.Nav.Name}"); + queryable.Where($" {ToShortName(this.shorName)}.{queryable.SqlBuilder.GetTranslationColumnName(PkColumn.DbColumnName)} = {ToShortName(masterShortName)}.{queryable.SqlBuilder.GetTranslationColumnName(FirstPkColumn.DbColumnName)} "); + MapperSql.Sql = "( " + queryable.ToSql().Key + " ) "; + return MapperSql; + } + private bool ValidateIsJoinMember(bool result, MemberExpression memberExp, Expression childExpression) + { + if (childExpression != null && childExpression is MemberExpression) + { + var oldChildExpression = childExpression; + var child2Expression = (childExpression as MemberExpression).Expression; + if (child2Expression == null || (child2Expression is ConstantExpression)) + { + return false; + } + items = new List(); + items.Add(new ExpressionItems() { Type = 1, Expression = memberExp, ParentEntityInfo = this.context.EntityMaintenance.GetEntityInfo(oldChildExpression.Type) }); + items.Add(new ExpressionItems() { Type = 2, Expression = oldChildExpression, ThisEntityInfo = this.context.EntityMaintenance.GetEntityInfo(oldChildExpression.Type), ParentEntityInfo = this.context.EntityMaintenance.GetEntityInfo(child2Expression.Type) }); + if (items.Any(it => it.Type == 2 && it.Nav == null)) + { + return false; + } + while (child2Expression != null) + { + if (IsClass(child2Expression)) + { + items.Add(new ExpressionItems() { Type = 2, Expression = child2Expression, ThisEntityInfo = this.context.EntityMaintenance.GetEntityInfo(child2Expression.Type), ParentEntityInfo = this.context.EntityMaintenance.GetEntityInfo(GetMemberExpression(child2Expression).Type) }); + child2Expression = GetMemberExpression(child2Expression); + + } + else if (IsParameter(child2Expression)) + { + shorName = child2Expression.ToString(); + entityInfo = this.context.EntityMaintenance.GetEntityInfo(child2Expression.Type); + break; + } + else + { + break; + } + } + if (!items.Any(it => it.Type == 2 && it.Nav == null)) + { + return true; + } + } + return result; + } + #endregion + + #region Helper + private string ToShortName(string name) + { + var result = ""; + if (name.ObjToString().Contains('.')) + { + if (this.context != null) + { + var sqlBuilder = this.context.Queryable().SqlBuilder; + result = sqlBuilder.SqlTranslationLeft + name.Replace(".", "_") + sqlBuilder.SqlTranslationRight; + return result; + } + else + { + result = name.Replace(".", "_"); + } + } + else + { + result = name; + } + if (builder == null) return name; + return builder.GetTranslationColumnName(name); + } + + private static bool IsParameter(Expression child2Expression) + { + return child2Expression.Type.IsClass() && child2Expression is ParameterExpression; + } + + private static Expression GetMemberExpression(Expression child2Expression) + { + return (child2Expression as MemberExpression).Expression; + } + + private static bool IsClass(Expression child2Expression) + { + return child2Expression.Type.IsClass() && child2Expression is MemberExpression; + } + #endregion + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/TypeParameterExpressionReolve.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/TypeParameterExpressionReolve.cs new file mode 100644 index 000000000..b309898b4 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/TypeParameterExpressionReolve.cs @@ -0,0 +1,48 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public class TypeParameterExpressionReolve : BaseResolve + { + public TypeParameterExpressionReolve(ExpressionParameter parameter) : base(parameter) + { + var expression = (ParameterExpression)base.Expression; + switch (parameter.Context.ResolveType) + { + case ResolveExpressType.WhereSingle: + break; + case ResolveExpressType.WhereMultiple: + break; + case ResolveExpressType.Update: + parameter.BaseParameter.CommonTempData = expression.Name; + break; + case ResolveExpressType.SelectSingle: + case ResolveExpressType.SelectMultiple: + if (parameter.BaseParameter != null && parameter.BaseParameter.CurrentExpression.NodeType == ExpressionType.Lambda) + { + //if (this.Context.PgSqlIsAutoToLower == false&&this.Context is PostgreSQLExpressionContext) + //{ + this.Context.Result.Append(this.Context.GetTranslationColumnName(expression.Name) + ".*"); + //} + //else + //{ + // this.Context.Result.Append(expression.Name + ".*"); + //} + } + else + { + parameter.BaseParameter.CommonTempData = expression.Name; + } + break; + case ResolveExpressType.FieldSingle: + break; + case ResolveExpressType.FieldMultiple: + break; + case ResolveExpressType.Join: + break; + default: + break; + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/UnaryExpressionResolve.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/UnaryExpressionResolve.cs new file mode 100644 index 000000000..69cb3f89a --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/ResolveItems/UnaryExpressionResolve.cs @@ -0,0 +1,190 @@ +using System.Linq.Expressions; +namespace SqlSugar +{ + public class UnaryExpressionResolve : BaseResolve + { + public UnaryExpressionResolve(ExpressionParameter parameter) : base(parameter) + { + var oldExpression = base.Expression; + var expression = base.Expression as UnaryExpression; + var baseParameter = parameter.BaseParameter; + switch (this.Context.ResolveType) + { + case ResolveExpressType.WhereSingle: + case ResolveExpressType.WhereMultiple: + case ResolveExpressType.FieldSingle: + case ResolveExpressType.FieldMultiple: + case ResolveExpressType.SelectSingle: + case ResolveExpressType.SelectMultiple: + case ResolveExpressType.ArraySingle: + case ResolveExpressType.ArrayMultiple: + case ResolveExpressType.Update: + var nodeType = expression.NodeType; + base.Expression = expression.Operand; + var isMember = expression.Operand is MemberExpression; + var isConst = expression.Operand is ConstantExpression; + var offsetIsNull = (parameter.OppsiteExpression is ConstantExpression) + && (parameter.OppsiteExpression as ConstantExpression).Value == null + && ExpressionTool.IsComparisonOperator(expression.Operand); + if (isMember && offsetIsNull) + { + Append(parameter, nodeType); + } + else if (baseParameter.CurrentExpression is NewArrayExpression) + { + Result(parameter, nodeType); + } + else if (baseParameter.OperatorValue == "=" && IsNotMember(oldExpression)) + { + AppendNotMember(parameter, nodeType); + } + else if (baseParameter.OperatorValue == "=" && IsNotParameter(oldExpression)) + { + AppendNotParameter(parameter, nodeType); + } + else if (base.Expression is BinaryExpression || parameter.BaseExpression is BinaryExpression || baseParameter.CommonTempData.ObjToString() == CommonTempDataType.Append.ToString()) + { + Append(parameter, nodeType); + } + else if (isMember) + { + MemberLogic(parameter, baseParameter, nodeType); + } + else if (isConst) + { + Result(parameter, nodeType); + } + else + { + Append(parameter, nodeType); + } + break; + default: + break; + } + } + + private void MemberLogic(ExpressionParameter parameter, ExpressionParameter baseParameter, ExpressionType nodeType) + { + var memberExpression = (base.Expression as MemberExpression); + var isLogicOperator = ExpressionTool.IsLogicOperator(baseParameter.OperatorValue) || baseParameter.OperatorValue.IsNullOrEmpty(); + var isHasValue = isLogicOperator && memberExpression.Member.Name == "HasValue" && memberExpression.Expression != null && memberExpression.NodeType == ExpressionType.MemberAccess; + if (isHasValue) + { + var member = memberExpression.Expression as MemberExpression; + parameter.CommonTempData = CommonTempDataType.Result; + var isConst = member.Expression != null && member.Expression is ConstantExpression; + if (isConst) + { + var paramterValue = ExpressionTool.DynamicInvoke(member); + var paramterName = base.AppendParameter(paramterValue); + var result = this.Context.DbMehtods.HasValue(new MethodCallExpressionModel() + { + Args = new List() { + new MethodCallExpressionArgs() { IsMember=false, MemberName=paramterName, MemberValue=paramterValue } } + }); + this.Context.Result.Append(result); + } + else + { + this.Expression = isConst ? member.Expression : member; + this.Start(); + var methodParamter = isConst ? new MethodCallExpressionArgs() { IsMember = false } : new MethodCallExpressionArgs() { IsMember = true, MemberName = parameter.CommonTempData, MemberValue = null }; + var result = this.Context.DbMehtods.HasValue(new MethodCallExpressionModel() + { + Args = new List() { + methodParamter + } + }); + if (nodeType == ExpressionType.Not && memberExpression.Member.Name == "HasValue") + { + this.Context.Result.Append("NOT" + result); + } + else + { + this.Context.Result.Append(result); + } + parameter.CommonTempData = null; + } + } + else if (memberExpression.Type == UtilConstants.BoolType && isLogicOperator) + { + Append(parameter, nodeType); + } + else + { + Result(parameter, nodeType); + } + } + + private void Result(ExpressionParameter parameter, ExpressionType nodeType) + { + BaseParameter.ChildExpression = base.Expression; + parameter.CommonTempData = CommonTempDataType.Result; + if (nodeType == ExpressionType.Not) + AppendNot(parameter.CommonTempData); + base.Start(); + parameter.BaseParameter.CommonTempData = parameter.CommonTempData; + if (nodeType == ExpressionType.Negate && parameter.BaseParameter.CommonTempData is int) + parameter.BaseParameter.CommonTempData = Convert.ToInt32(parameter.BaseParameter.CommonTempData) * -1; + parameter.BaseParameter.ChildExpression = base.Expression; + parameter.CommonTempData = null; + } + + private void Append(ExpressionParameter parameter, ExpressionType nodeType) + { + BaseParameter.ChildExpression = base.Expression; + this.IsLeft = parameter.IsLeft; + parameter.CommonTempData = CommonTempDataType.Append; + if (nodeType == ExpressionType.Not) + AppendNot(parameter.CommonTempData); + if (nodeType == ExpressionType.Negate) + AppendNegate(parameter.CommonTempData); + base.Start(); + parameter.BaseParameter.CommonTempData = parameter.CommonTempData; + parameter.BaseParameter.ChildExpression = base.Expression; + parameter.CommonTempData = null; + } + + + private void AppendNotMember(ExpressionParameter parameter, ExpressionType nodeType) + { + BaseParameter.ChildExpression = base.Expression; + this.IsLeft = parameter.IsLeft; + parameter.CommonTempData = CommonTempDataType.Result; + base.Start(); + var result = this.Context.DbMehtods.IIF(new MethodCallExpressionModel() + { + Args = new List() { + new MethodCallExpressionArgs(){ IsMember=true, MemberName=parameter.CommonTempData.ObjToString()+"=1",Type=UtilConstants.BoolType }, + new MethodCallExpressionArgs(){ IsMember=true,MemberName=AppendParameter(0) }, + new MethodCallExpressionArgs(){ IsMember=true, MemberName=AppendParameter(1) } + } + }); + this.Context.Result.Append(result); + parameter.BaseParameter.ChildExpression = base.Expression; + parameter.CommonTempData = null; + } + + + private void AppendNotParameter(ExpressionParameter parameter, ExpressionType nodeType) + { + BaseParameter.ChildExpression = base.Expression; + this.IsLeft = parameter.IsLeft; + parameter.CommonTempData = CommonTempDataType.Result; + base.Start(); + var result = this.Context.DbMehtods.IIF(new MethodCallExpressionModel() + { + Args = new List() { + new MethodCallExpressionArgs(){ IsMember=true, MemberName=AppendParameter(parameter.CommonTempData)+"=1" }, + new MethodCallExpressionArgs(){ IsMember=true,MemberName=AppendParameter(0) }, + new MethodCallExpressionArgs(){ IsMember=true, MemberName=AppendParameter(1) } + } + }); + this.Context.Result.Append(result); + parameter.BaseParameter.ChildExpression = base.Expression; + parameter.CommonTempData = null; + } + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/ISubOperation.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/ISubOperation.cs new file mode 100644 index 000000000..d26a1e291 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/ISubOperation.cs @@ -0,0 +1,14 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public interface ISubOperation + { + ExpressionContext Context { get; set; } + string Name { get; } + string GetValue(Expression expression); + int Sort { get; } + Expression Expression { get; set; } + bool HasWhere { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubAnd.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubAnd.cs new file mode 100644 index 000000000..d6485aeb7 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubAnd.cs @@ -0,0 +1,112 @@ +using System.Linq.Expressions; +using System.Text.RegularExpressions; + +namespace SqlSugar +{ + public class SubAnd : ISubOperation + { + public string Name + { + get { return "And"; } + } + + public Expression Expression + { + get; set; + } + + public int Sort + { + get + { + return 401; + } + } + + public ExpressionContext Context + { + get; set; + } + + public bool HasWhere + { + get; set; + } + + public string GetValue(Expression expression) + { + var exp = expression as MethodCallExpression; + var argExp = exp.Arguments[0]; + var copyContext = this.Context; + + var pars = ExpressionTool.GetParameters(expression).Distinct(); + if (this.Context.JoinIndex > 0 || pars.Count() > 1) + { + copyContext = this.Context.GetCopyContextWithMapping(); + copyContext.IsSingle = false; + } + if (ExpressionTool.GetMethodName(argExp) == "ToExpression") + { + argExp = ExpressionTool.DynamicInvoke(argExp) as Expression; + } + var result = "AND " + SubTools.GetMethodValue(copyContext, argExp, ResolveExpressType.WhereMultiple); + if (argExp.Type == typeof(List)) + { + var p = this.Context.Parameters.Last(); + this.Context.Parameters.Remove(p); + var cols = p.Value as List; + var sqlObj = this.Context.SugarContext.QueryBuilder.Builder.ConditionalModelToSql(cols, this.Context.ParameterIndex * 100); + this.Context.ParameterIndex = this.Context.ParameterIndex + this.Context.ParameterIndex * 100; + result = "AND " + sqlObj.Key; + this.Context.Parameters.AddRange(sqlObj.Value); + return result; + } + if (this.Context.JoinIndex > 0 || pars.Count() > 1) + { + this.Context.Parameters.AddRange(copyContext.Parameters); + this.Context.Index = copyContext.Index; + this.Context.ParameterIndex = copyContext.ParameterIndex; + } + + var regex = @"^AND (\@Const\d+) $"; + if (this.Context is OracleExpressionContext) + { + regex = @"^AND (\:Const\d+) $"; + } + if (this.Context is DmExpressionContext) + { + regex = @"^AND (\:Const\d+) $"; + } + if (Regex.IsMatch(result, regex)) + { + var value = GetValue(result, regex); + if (value is Expression) + { + var p = this.Context.Parameters.First(it => it.ParameterName == Regex.Match(result, regex).Groups[1].Value); + result = "AND " + SubTools.GetMethodValue(Context, value as Expression, ResolveExpressType.WhereMultiple); + argExp = value as Expression; + this.Context.Parameters.Remove(p); + } + else + { + result = "AND " + value; + return result; + } + } + + var selfParameterName = this.Context.GetTranslationColumnName((argExp as LambdaExpression).Parameters.First().Name) + UtilConstants.Dot; + if (this.Context.JoinIndex == 0 && result.Contains(" FROM ")) + { + this.Context.CurrentShortName = selfParameterName.TrimEnd('.'); + } + else if (this.Context.JoinIndex == 0 && this.Context.CurrentShortName != selfParameterName.TrimEnd('.')) + result = result.Replace(selfParameterName, SubTools.GetSubReplace(this.Context)); + return result; + } + + private object GetValue(string result, string regex) + { + return this.Context.Parameters.First(it => it.ParameterName == Regex.Match(result, regex).Groups[1].Value).Value; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubAndIF.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubAndIF.cs new file mode 100644 index 000000000..587056d1f --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubAndIF.cs @@ -0,0 +1,74 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public class SubAndIF : ISubOperation + { + public bool HasWhere + { + get; set; + } + + public string Name + { + get { return "WhereIF"; } + } + + public Expression Expression + { + get; set; + } + + public int Sort + { + get + { + return 400; + } + } + + public ExpressionContext Context + { + get; set; + } + + public string GetValue(Expression expression) + { + var exp = expression as MethodCallExpression; + object value = null; + try + { + value = ExpressionTool.DynamicInvoke(exp.Arguments[0]); + } + catch + { + Check.Exception(true, ErrorMessage.WhereIFCheck, exp.Arguments[0].ToString()); + } + var isWhere = Convert.ToBoolean(value); + if (!Convert.ToBoolean(isWhere)) + { + return ""; + } + var argExp = exp.Arguments[1]; + var copyContext = this.Context; + if (this.Context.JoinIndex > 0) + { + copyContext = this.Context.GetCopyContextWithMapping(); + copyContext.IsSingle = false; + } + var result = "AND " + SubTools.GetMethodValue(copyContext, argExp, ResolveExpressType.WhereMultiple); ; + if (this.Context.JoinIndex > 0) + { + this.Context.Parameters.AddRange(copyContext.Parameters); + this.Context.Index = copyContext.Index; + this.Context.ParameterIndex = copyContext.ParameterIndex; + } + var selfParameterName = Context.GetTranslationColumnName((argExp as LambdaExpression).Parameters.First().Name) + UtilConstants.Dot; + if (this.Context.JoinIndex == 0) + { + result = result.Replace(selfParameterName, SubTools.GetSubReplace(this.Context)); + } + return result; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubAny.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubAny.cs new file mode 100644 index 000000000..28b667d8a --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubAny.cs @@ -0,0 +1,43 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public class SubAny : ISubOperation + { + public ExpressionContext Context + { + get; set; + } + + public Expression Expression + { + get; set; + } + + public bool HasWhere + { + get; set; + } + + public string Name + { + get + { + return "Any"; + } + } + + public int Sort + { + get + { + return 0; + } + } + + public string GetValue(Expression expression) + { + return "EXISTS"; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubAs.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubAs.cs new file mode 100644 index 000000000..24f520586 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubAs.cs @@ -0,0 +1,52 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public class SubAs : ISubOperation + { + public bool HasWhere + { + get; set; + } + + public string Name + { + get + { + return "AS"; + } + } + + public Expression Expression + { + get; set; + } + + + public int Sort + { + get + { + return 200; + } + } + + public ExpressionContext Context + { + get; set; + } + + public string GetValue(Expression expression = null) + { + var exp = expression as MethodCallExpression; + var arg = exp.Arguments[0]; + if (arg is MethodCallExpression) + { + arg = Expression.Constant(ExpressionTool.DynamicInvoke(arg)); + } + var expString = SubTools.GetMethodValue(this.Context, arg, ResolveExpressType.WhereSingle)?.Trim(); + var result = this.Context.Parameters.First(it => it.ParameterName == expString).Value + ""; + return "$SubAs:" + result; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubAsWithAttr.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubAsWithAttr.cs new file mode 100644 index 000000000..785835315 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubAsWithAttr.cs @@ -0,0 +1,51 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public class SubAsWithAttr : ISubOperation + { + public bool HasWhere + { + get; set; + } + + public string Name + { + get + { + return "AsWithAttr"; + } + } + + public Expression Expression + { + get; set; + } + + + public int Sort + { + get + { + return 200; + } + } + + public ExpressionContext Context + { + get; set; + } + + public string GetValue(Expression expression = null) + { + var exp = expression as MethodCallExpression; + var type = exp.Type.GetGenericArguments()[0]; + var db = this.Context.SugarContext.Context; + var entityInfo = db.EntityMaintenance.GetEntityInfo(type); + var tableName = entityInfo.DbTableName; + var queryable = ((QueryableProvider)(db.Queryable())); + var expString = queryable.GetTableName(entityInfo, tableName); + return "$SubAs:" + expString; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubAvg.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubAvg.cs new file mode 100644 index 000000000..628746acf --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubAvg.cs @@ -0,0 +1,45 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public class SubAvg : ISubOperation + { + public bool HasWhere + { + get; set; + } + + public string Name + { + get + { + return "Avg"; + } + } + + public Expression Expression + { + get; set; + } + + + public int Sort + { + get + { + return 200; + } + } + + public ExpressionContext Context + { + get; set; + } + + public string GetValue(Expression expression = null) + { + var exp = expression as MethodCallExpression; + return "AVG(" + SubTools.GetMethodValue(this.Context, exp.Arguments[0], ResolveExpressType.FieldSingle) + ")"; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubBegin.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubBegin.cs new file mode 100644 index 000000000..8a737deee --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubBegin.cs @@ -0,0 +1,43 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public class SubBegin : ISubOperation + { + public bool HasWhere + { + get; set; + } + + public string Name + { + get + { + return "Begin"; + } + } + + public Expression Expression + { + get; set; + } + + public int Sort + { + get + { + return 100; + } + } + + public ExpressionContext Context + { + get; set; + } + + public string GetValue(Expression expression) + { + return "SELECT"; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubCount.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubCount.cs new file mode 100644 index 000000000..81ec7a819 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubCount.cs @@ -0,0 +1,44 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public class SubCount : ISubOperation + { + public bool HasWhere + { + get; set; + } + + public string Name + { + get + { + return "Count"; + } + } + + public Expression Expression + { + get; set; + } + + + public int Sort + { + get + { + return 200; + } + } + + public ExpressionContext Context + { + get; set; + } + + public string GetValue(Expression expression) + { + return "COUNT(*)"; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubDistinctCount.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubDistinctCount.cs new file mode 100644 index 000000000..1b28bf993 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubDistinctCount.cs @@ -0,0 +1,77 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public class SubDistinctCount : ISubOperation + { + public bool HasWhere + { + get; set; + } + + public string Name + { + get + { + return "DistinctCount"; + } + } + + public Expression Expression + { + get; set; + } + + + public int Sort + { + get + { + return 200; + } + } + + public ExpressionContext Context + { + get; set; + } + + public string GetValue(Expression expression = null) + { + var exp = expression as MethodCallExpression; + var argExp = exp.Arguments[0]; + InitType(exp); + var parametres = (argExp as LambdaExpression).Parameters; + if ((argExp as LambdaExpression).Body is UnaryExpression) + { + argExp = ((argExp as LambdaExpression).Body as UnaryExpression).Operand; + } + var argLambda = argExp as LambdaExpression; + if (this.Context.InitMappingInfo != null && argLambda?.Parameters.Count > 0) + { + foreach (var item in argLambda.Parameters) + { + this.Context.InitMappingInfo(item.Type); + } + this.Context.RefreshMapping(); + } + var result = "COUNT(DISTINCT " + SubTools.GetMethodValue(Context, argExp, ResolveExpressType.WhereMultiple) + ")"; + var selfParameterName = Context.GetTranslationColumnName(parametres.First().Name) + UtilConstants.Dot; + if (this.Context.JoinIndex == 0) + result = result.Replace(selfParameterName, SubTools.GetSubReplace(this.Context)); + return result; + } + private void InitType(MethodCallExpression exp) + { + foreach (var arg in (exp.Arguments[0] as LambdaExpression).Parameters) + { + if (this.Context.InitMappingInfo != null) + { + this.Context.InitMappingInfo(arg.Type); + this.Context.RefreshMapping(); + } + } + } + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubEnableTableFilter.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubEnableTableFilter.cs new file mode 100644 index 000000000..ab0abc7ac --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubEnableTableFilter.cs @@ -0,0 +1,72 @@ +using System.Linq.Expressions; +using System.Reflection; + +namespace SqlSugar +{ + public class SubEnableTableFilter : ISubOperation + { + public ExpressionContext Context + { + get; set; + } + + public Expression Expression + { + get; set; + } + + public bool HasWhere + { + get; set; + } + + public string Name + { + get + { + return "EnableTableFilter"; + } + } + + public int Sort + { + get + { + return 402; + } + } + + public string GetValue(Expression expression) + { + var result = ""; + if (this.Context.SugarContext != null) + { + var db = this.Context.SugarContext.Context; + BindingFlags flag = BindingFlags.Instance | BindingFlags.NonPublic; + Type type = this.Context.SubTableType; + var isWhere = HasWhere; + if (db.QueryFilter.GetFilterList != null) + { + foreach (var item in db.QueryFilter.GetFilterList) + { + PropertyInfo field = item.GetType().GetProperty("exp", flag); + if (field != null) + { + Type ChildType = item.GetType().GetProperty("type", flag).GetValue(item, null) as Type; + if (ChildType == type || (ChildType.IsInterface && type.GetInterfaces().Contains(ChildType))) + { + var entityInfo = db.EntityMaintenance.GetEntityInfo(ChildType); + this.Context.InitMappingInfo(ChildType); + var exp = field.GetValue(item, null) as Expression; + var whereStr = isWhere ? " AND " : " WHERE "; + isWhere = true; + result += (whereStr + SubTools.GetMethodValue(Context, exp, ResolveExpressType.WhereSingle)); + } + } + } + } + } + return result; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubFirst.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubFirst.cs new file mode 100644 index 000000000..398dfccf0 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubFirst.cs @@ -0,0 +1,195 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public class SubFirst : ISubOperation + { + public bool HasWhere + { + get; set; + } + + public string Name + { + get + { + return "First"; + } + } + + public Expression Expression + { + get; set; + } + + + public int Sort + { + get + { + return 200; + } + } + + public ExpressionContext Context + { + get; set; + } + + public string GetValue(Expression expression = null) + { + var exp = expression as MethodCallExpression; + if (IsAutoGeneric(exp)) return GetValueByAuto(exp); + if (IsAutoSelect(exp)) return GetValueByAuto(exp); + InitType(exp); + var type = expression.Type; + if (type.FullName.IsCollectionsList() + && exp.Arguments.Count == 0 && type.GenericTypeArguments.Length > 0 + && this.Context.SugarContext?.QueryBuilder.IsSelectNoAll == true) + { + var entity = type.GenericTypeArguments[0]; + var columnNames = this.Context.SugarContext.Context.EntityMaintenance.GetEntityInfo(entity).Columns; + var columnsString = string.Join(",", columnNames + .Where(it => it.IsIgnore == false) + .Where(it => it.DbColumnName.HasValue()) + .Select(it => this.Context.GetTranslationColumnName(it.DbColumnName))); + return $"{columnsString},@sugarIndex as sugarIndex"; + } + else if (exp.Arguments.Count == 0) + { + return "*,@sugarIndex as sugarIndex"; + } + var argExp = exp.Arguments[0]; + var parametres = (argExp as LambdaExpression).Parameters; + if ((argExp as LambdaExpression).Body is UnaryExpression) + { + argExp = ((argExp as LambdaExpression).Body as UnaryExpression).Operand; + } + var argLambda = argExp as LambdaExpression; + var copyContext = this.Context.GetCopyContextWithMapping(); + copyContext.Resolve(argLambda, ResolveExpressType.SelectMultiple); + var select = copyContext.Result.GetString(); + this.Context.Parameters.AddRange(copyContext.Parameters); + this.Context.Index = copyContext.Index; + this.Context.ParameterIndex = copyContext.ParameterIndex; + SetShortName(exp, null); + return select + ",@sugarIndex as sugarIndex"; + } + + private void InitType(MethodCallExpression exp) + { + if (exp.Arguments.Count > 0) + { + foreach (var arg in (exp.Arguments[0] as LambdaExpression).Parameters) + { + if (this.Context.InitMappingInfo != null) + { + this.Context.InitMappingInfo(arg.Type); + this.Context.RefreshMapping(); + } + } + } + } + + public void SetShortName(MethodCallExpression exp, string result) + { + if (exp.Arguments.Count != 0 && exp.Arguments[0] is LambdaExpression) + { + var parameters = (exp.Arguments[0] as LambdaExpression).Parameters; + if (parameters?.Count > 0) + { + this.Context.CurrentShortName = this.Context.GetTranslationColumnName(parameters[0].ObjToString()); + } + + } + } + public void SetShortNameNext(MethodCallExpression exp, string result) + { + if (exp.Arguments.Count > 1 && exp.Arguments[1] is LambdaExpression) + { + var parameters = (exp.Arguments[1] as LambdaExpression).Parameters; + if (parameters?.Count > 0) + { + this.Context.CurrentShortName = this.Context.GetTranslationColumnName(parameters[0].ObjToString()); + } + + } + } + + private string GetValueByAuto(MethodCallExpression exp) + { + var selectExp = exp.Arguments.FirstOrDefault(); + if (selectExp == null) + { + var type = exp.Type; + var parameter = Expression.Parameter(type, "it"); + + // 构造返回值表达式 + var body = Expression.MemberInit(Expression.New(type)); + + // 将返回值表达式作为lambda表达式的主体 + selectExp = Expression.Lambda(body, parameter); + + } + var bodyExp = ExpressionTool.GetLambdaExpressionBody(selectExp); + var newMemExp = (bodyExp as MemberInitExpression); + var parameters = ExpressionTool.GetParameters(exp); + InitType(exp); + if (parameters.Count != 0) + { + this.Context.CurrentShortName = this.Context.GetTranslationColumnName(parameters.FirstOrDefault().Name); + } + Check.ExceptionEasy(newMemExp == null, $"Subquery ToList(exp,true) expression {exp.ToString()} can only be it=>new class(){{Id = it.id}}", $"子查询ToList(exp,true)表达式{exp.ToString()}只能是it=>new class(){{ id=it.Id}}"); + var dic = ExpressionTool.GetMemberBindingItemList(newMemExp.Bindings); + var db = this.Context.SugarContext.Context; + var builder = this.Context.SugarContext.QueryBuilder.Builder; + var columnInfos = db.EntityMaintenance.GetEntityInfo(bodyExp.Type); + var autoColumns = columnInfos.Columns + .Where(it => !dic.ContainsKey(it.PropertyName)) + .Where(it => it.IsIgnore == false) + .ToList(); + List appendColumns = new List(); + List completeColumnColumns = new List(); + foreach (var item in autoColumns) + { + + foreach (var parameter in parameters) + { + var parameterColumns = db.EntityMaintenance.GetEntityInfo(parameter.Type).Columns; + if (!completeColumnColumns.Any(it => it.EqualCase(item.PropertyName)) && parameterColumns.Any(it => it.PropertyName.EqualCase(item.PropertyName))) + { + var completeColumn = parameterColumns.First(it => it.PropertyName == item.PropertyName); + var shortName = builder.GetTranslationColumnName(parameter.Name); + var columnName = builder.GetTranslationColumnName(completeColumn.DbColumnName); + var asName = builder.SqlTranslationLeft + item.PropertyName + builder.SqlTranslationRight; + appendColumns.Add($"{shortName}.{columnName} as {asName}"); + completeColumnColumns.Add(completeColumn.PropertyName); + } + } + } + var copyContext = this.Context.GetCopyContextWithMapping(); + copyContext.Resolve(bodyExp, ResolveExpressType.SelectMultiple); + var select = copyContext.Result.GetString(); + if (dic.Count > 0 && appendColumns.Count == 0) + { + return select + ",@sugarIndex as sugarIndex"; ; + } + else if (dic.Count > 0 && appendColumns.Count > 0) + { + return select + "," + string.Join(",", appendColumns) + ",@sugarIndex as sugarIndex"; ; + } + else + { + return string.Join(",", appendColumns) + ",@sugarIndex as sugarIndex"; + } + } + private static bool IsAutoSelect(MethodCallExpression exp) + { + return exp.Arguments.Count == 2 && exp.Arguments.Last().Type == UtilConstants.BoolType; + } + private static bool IsAutoGeneric(MethodCallExpression exp) + { + return exp.Arguments.Count == 0 && exp.Method.GetGenericArguments().Length == 1; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubFromTable.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubFromTable.cs new file mode 100644 index 000000000..2d97adb4e --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubFromTable.cs @@ -0,0 +1,58 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public class SubFromTable : ISubOperation + { + public bool HasWhere + { + get; set; + } + + public string Name + { + get + { + return "Subqueryable"; + } + } + + public Expression Expression + { + get; set; + } + + public int Sort + { + get + { + return 300; + } + } + + public ExpressionContext Context + { + get; set; + } + + public string GetValue(Expression expression) + { + var exp = expression as MethodCallExpression; + var resType = exp.Method.ReturnType; + var entityType = resType.GetGenericArguments().First(); + this.Context.SubTableType = entityType; + var name = entityType.Name; + if (this.Context.InitMappingInfo != null) + { + this.Context.InitMappingInfo(entityType); + this.Context.RefreshMapping(); + } + var result = "FROM " + this.Context.GetTranslationTableName(name, true); + if (this.Context.SubQueryIndex > 0) + { + result += " subTableIndex" + this.Context.SubQueryIndex; + } + return result; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubGroupBy.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubGroupBy.cs new file mode 100644 index 000000000..f4ad58c2c --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubGroupBy.cs @@ -0,0 +1,76 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public class SubGroupBy : ISubOperation + { + public bool HasWhere + { + get; set; + } + + public string Name + { + get { return "GroupBy"; } + } + + public Expression Expression + { + get; set; + } + + public int Sort + { + get + { + return 479; + } + } + + public ExpressionContext Context + { + get; set; + } + + public string GetValue(Expression expression) + { + var exp = expression as MethodCallExpression; + var argExp = exp.Arguments[0]; + var type = ResolveExpressType.FieldSingle; + if ((argExp as LambdaExpression).Body is NewExpression) + { + type = ResolveExpressType.ArraySingle; + } + var result = "GROUP BY "; + if (this.Context.JoinIndex == 0) + { + result = result + SubTools.GetMethodValue(this.Context, argExp, type); + } + else + { + if (type == ResolveExpressType.ArraySingle) + { + type = ResolveExpressType.ArrayMultiple; + } + else if (type == ResolveExpressType.FieldSingle) + { + type = ResolveExpressType.FieldMultiple; + } + else if (type == ResolveExpressType.WhereSingle) + { + type = ResolveExpressType.WhereMultiple; + } + result = result + SubTools.GetMethodValueSubJoin(this.Context, argExp, type); + } + result = result.TrimEnd(','); + var selfParameterName = this.Context.GetTranslationColumnName((argExp as LambdaExpression).Parameters.First().Name) + UtilConstants.Dot; + if (this.Context.JoinIndex == 0) + result = result.Replace(selfParameterName, SubTools.GetSubReplace(this.Context)); + if (this.Context.CurrentShortName == null) + { + this.Context.CurrentShortName = this.Context.GetTranslationColumnName(ExpressionTool.GetParameters(exp).FirstOrDefault().Name); + } + return result; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubHaving.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubHaving.cs new file mode 100644 index 000000000..44e0a7e47 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubHaving.cs @@ -0,0 +1,46 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public class SubHaving : ISubOperation + { + public bool HasWhere + { + get; set; + } + + public string Name + { + get { return "Having"; } + } + + public Expression Expression + { + get; set; + } + + public int Sort + { + get + { + return 480; + } + } + + public ExpressionContext Context + { + get; set; + } + + public string GetValue(Expression expression) + { + var exp = expression as MethodCallExpression; + var argExp = exp.Arguments[0]; + var result = "Having " + SubTools.GetMethodValue(Context, argExp, ResolveExpressType.WhereMultiple); + var selfParameterName = Context.GetTranslationColumnName((argExp as LambdaExpression).Parameters.First().Name) + UtilConstants.Dot; + if (this.Context.JoinIndex == 0) + result = result.Replace(selfParameterName, SubTools.GetSubReplace(this.Context)); + return result; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubInnerJoin.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubInnerJoin.cs new file mode 100644 index 000000000..11d2532fe --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubInnerJoin.cs @@ -0,0 +1,75 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public class SubInnerJoin : ISubOperation + { + + public bool HasWhere + { + get; set; + } + + public string Name + { + get { return "InnerJoin"; } + } + + public Expression Expression + { + get; set; + } + + public int Sort + { + get + { + return 302; + } + } + + public ExpressionContext Context + { + get; set; + } + + public string GetValue(Expression expression) + { + var exp = expression as MethodCallExpression; + var argExp = exp.Arguments[0]; + var name = this.Context.GetTranslationColumnName((argExp as LambdaExpression).Parameters[0].Name); + var parameter = (argExp as LambdaExpression).Parameters.Last(); + foreach (var item in (argExp as LambdaExpression).Parameters) + { + Context.InitMappingInfo(item.Type); + } + this.Context.RefreshMapping(); + var tableName = Context.GetTranslationTableName(parameter.Type.Name, true); + if (this.Context.IsAsAttr == true) + { + var db = this.Context.SugarContext.Context; + var entityInfo = db.EntityMaintenance.GetEntityInfo(parameter.Type); + var queryable = ((QueryableProvider)(db.Queryable())); + tableName = queryable.GetTableName(entityInfo, tableName); + } + if (exp.Arguments.Count == 2 && exp.Arguments.Last().HasValue()) + { + tableName = Context.GetTranslationTableName(ExpressionTool.DynamicInvoke(exp.Arguments.Last()) + ""); + } + var joinString = string.Format(" {2} INNER JOIN {1} {0} ", + this.Context.GetTranslationColumnName(parameter.Name), + tableName, + null); + if (this.Context?.SugarContext?.Context?.CurrentConnectionConfig?.DbType == DbType.SqlServer && this.Context?.SugarContext?.Context?.CurrentConnectionConfig?.MoreSettings?.IsWithNoLockSubquery == true) + { + joinString = $"{joinString} {SqlWith.NoLock} "; + } + var result = joinString + "ON " + SubTools.GetMethodValue(Context, argExp, ResolveExpressType.WhereMultiple); + //var selfParameterName = Context.GetTranslationColumnName((argExp as LambdaExpression).Parameters.First().Name) + UtilConstants.Dot; + this.Context.JoinIndex++; + new SubSelect() { Context = this.Context }.SetShortName(exp, "+"); + //result = result.Replace(selfParameterName, SubTools.GetSubReplace(this.Context)); + return result; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubLeftBracket.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubLeftBracket.cs new file mode 100644 index 000000000..2fa0c9f32 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubLeftBracket.cs @@ -0,0 +1,43 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public class SubLeftBracket : ISubOperation + { + public bool HasWhere + { + get; set; + } + + public ExpressionContext Context + { + get; set; + } + + public Expression Expression + { + get; set; + } + + public string Name + { + get + { + return "LeftBracket"; + } + } + + public int Sort + { + get + { + return 50; + } + } + + public string GetValue(Expression expression) + { + return "("; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubLeftJoin.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubLeftJoin.cs new file mode 100644 index 000000000..3c2e71e90 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubLeftJoin.cs @@ -0,0 +1,75 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public class SubLeftJoin : ISubOperation + { + + public bool HasWhere + { + get; set; + } + + public string Name + { + get { return "LeftJoin"; } + } + + public Expression Expression + { + get; set; + } + + public int Sort + { + get + { + return 302; + } + } + + public ExpressionContext Context + { + get; set; + } + + public string GetValue(Expression expression) + { + var exp = expression as MethodCallExpression; + var argExp = exp.Arguments[0]; + var name = this.Context.GetTranslationColumnName((argExp as LambdaExpression).Parameters[0].Name); + var parameter = (argExp as LambdaExpression).Parameters.Last(); + foreach (var item in (argExp as LambdaExpression).Parameters) + { + Context.InitMappingInfo(item.Type); + } + this.Context.RefreshMapping(); + var tableName = Context.GetTranslationTableName(parameter.Type.Name, true); + if (this.Context.IsAsAttr == true) + { + var db = this.Context.SugarContext.Context; + var entityInfo = db.EntityMaintenance.GetEntityInfo(parameter.Type); + var queryable = ((QueryableProvider)(db.Queryable())); + tableName = queryable.GetTableName(entityInfo, tableName); + } + if (exp.Arguments.Count == 2 && exp.Arguments.Last().HasValue()) + { + tableName = Context.GetTranslationTableName(ExpressionTool.DynamicInvoke(exp.Arguments.Last()) + ""); + } + var joinString = string.Format(" {2} LEFT JOIN {1} {0} ", + this.Context.GetTranslationColumnName(parameter.Name), + tableName, + null); + if (this.Context?.SugarContext?.Context?.CurrentConnectionConfig?.DbType == DbType.SqlServer && this.Context?.SugarContext?.Context?.CurrentConnectionConfig?.MoreSettings?.IsWithNoLockSubquery == true) + { + joinString = $"{joinString} {SqlWith.NoLock} "; + } + var result = joinString + "ON " + SubTools.GetMethodValue(Context, argExp, ResolveExpressType.WhereMultiple); + //var selfParameterName = Context.GetTranslationColumnName((argExp as LambdaExpression).Parameters.First().Name) + UtilConstants.Dot; + this.Context.JoinIndex++; + new SubSelect() { Context = this.Context }.SetShortName(exp, "+"); + //result = result.Replace(selfParameterName, SubTools.GetSubReplace(this.Context)); + return result; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubMax.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubMax.cs new file mode 100644 index 000000000..e0d9c4324 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubMax.cs @@ -0,0 +1,64 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public class SubMax : ISubOperation + { + public bool HasWhere + { + get; set; + } + + public string Name + { + get + { + return "Max"; + } + } + + public Expression Expression + { + get; set; + } + + + public int Sort + { + get + { + return 200; + } + } + + public ExpressionContext Context + { + get; set; + } + + public string GetValue(Expression expression = null) + { + var exp = expression as MethodCallExpression; + var argExp = exp.Arguments[0]; + var parametres = (argExp as LambdaExpression).Parameters; + if ((argExp as LambdaExpression).Body is UnaryExpression) + { + argExp = ((argExp as LambdaExpression).Body as UnaryExpression).Operand; + } + var argLambda = argExp as LambdaExpression; + if (this.Context.InitMappingInfo != null && argLambda?.Parameters.Count > 0) + { + foreach (var item in argLambda.Parameters) + { + this.Context.InitMappingInfo(item.Type); + } + this.Context.RefreshMapping(); + } + var result = "MAX(" + SubTools.GetMethodValue(Context, argExp, ResolveExpressType.WhereMultiple) + ")"; + var selfParameterName = Context.GetTranslationColumnName(parametres.First().Name) + UtilConstants.Dot; + if (this.Context.JoinIndex == 0) + result = result.Replace(selfParameterName, SubTools.GetSubReplace(this.Context)); + return result; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubMin.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubMin.cs new file mode 100644 index 000000000..eb7a058bb --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubMin.cs @@ -0,0 +1,64 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public class SubMin : ISubOperation + { + public bool HasWhere + { + get; set; + } + + public string Name + { + get + { + return "Min"; + } + } + + public Expression Expression + { + get; set; + } + + + public int Sort + { + get + { + return 200; + } + } + + public ExpressionContext Context + { + get; set; + } + + public string GetValue(Expression expression = null) + { + var exp = expression as MethodCallExpression; + var argExp = exp.Arguments[0]; + var parametres = (argExp as LambdaExpression).Parameters; + if ((argExp as LambdaExpression).Body is UnaryExpression) + { + argExp = ((argExp as LambdaExpression).Body as UnaryExpression).Operand; + } + var argLambda = argExp as LambdaExpression; + if (this.Context.InitMappingInfo != null && argLambda?.Parameters.Count > 0) + { + foreach (var item in argLambda.Parameters) + { + this.Context.InitMappingInfo(item.Type); + } + this.Context.RefreshMapping(); + } + var result = "MIN(" + SubTools.GetMethodValue(Context, argExp, ResolveExpressType.WhereMultiple) + ")"; + var selfParameterName = Context.GetTranslationColumnName(parametres.First().Name) + UtilConstants.Dot; + if (this.Context.JoinIndex == 0) + result = result.Replace(selfParameterName, SubTools.GetSubReplace(this.Context)); + return result; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubNotAny.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubNotAny.cs new file mode 100644 index 000000000..a99d4949a --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubNotAny.cs @@ -0,0 +1,43 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public class SubNotAny : ISubOperation + { + public bool HasWhere + { + get; set; + } + + public ExpressionContext Context + { + get; set; + } + + public Expression Expression + { + get; set; + } + + public string Name + { + get + { + return "NotAny"; + } + } + + public int Sort + { + get + { + return 0; + } + } + + public string GetValue(Expression expression) + { + return "NOT EXISTS"; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubOrderBy.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubOrderBy.cs new file mode 100644 index 000000000..a6ab0986f --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubOrderBy.cs @@ -0,0 +1,113 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public class SubOrderBy : ISubOperation + { + public int OrderIndex { get; set; } = 0; + public bool HasWhere + { + get; set; + } + + public string Name + { + get { return "OrderBy"; } + } + + public Expression Expression + { + get; set; + } + + public int Sort + { + get + { + return 480 + OrderIndex; + } + } + + public ExpressionContext Context + { + get; set; + } + + public string GetValue(Expression expression) + { + if (this.Context is OracleExpressionContext) + { + throw new Exception("Oracle Subquery can't OrderBy"); + } + var exp = expression as MethodCallExpression; + var argExp = exp.Arguments[0]; + var result = ""; + if (this.Context.JoinIndex == 0) + { + result = (OrderIndex == 0 ? "ORDER BY " : ",") + SubTools.GetMethodValue(this.Context, argExp, ResolveExpressType.FieldSingle); + } + else + { + result = (OrderIndex == 0 ? "ORDER BY " : ",") + SubTools.GetMethodValueSubJoin(this.Context, argExp, ResolveExpressType.FieldMultiple); + } + var selfParameterName = this.Context.GetTranslationColumnName((argExp as LambdaExpression).Parameters.First().Name) + UtilConstants.Dot; + if (this.Context.JoinIndex == 0) + { + result = result.Replace(selfParameterName, SubTools.GetSubReplace(this.Context)); + } + return result; + } + } + public class SubOrderByDesc : ISubOperation + { + public int OrderIndex { get; set; } = 0; + public bool HasWhere + { + get; set; + } + + public string Name + { + get { return "OrderByDesc"; } + } + + public Expression Expression + { + get; set; + } + + public int Sort + { + get + { + return 480 + OrderIndex; + } + } + + public ExpressionContext Context + { + get; set; + } + + public string GetValue(Expression expression) + { + var exp = expression as MethodCallExpression; + var argExp = exp.Arguments[0]; + var result = ""; + if (this.Context.JoinIndex == 0) + { + result = (OrderIndex == 0 ? "ORDER BY " : ",") + SubTools.GetMethodValue(this.Context, argExp, ResolveExpressType.FieldSingle) + " DESC"; + } + else + { + result = (OrderIndex == 0 ? "ORDER BY " : ",") + SubTools.GetMethodValueSubJoin(this.Context, argExp, ResolveExpressType.FieldMultiple) + " DESC"; + } + var selfParameterName = this.Context.GetTranslationColumnName((argExp as LambdaExpression).Parameters.First().Name) + UtilConstants.Dot; + if (this.Context.JoinIndex == 0) + { + result = result.Replace(selfParameterName, string.Empty); + } + return result; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubRightBracket.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubRightBracket.cs new file mode 100644 index 000000000..9eac6ea92 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubRightBracket.cs @@ -0,0 +1,43 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public class SubRightBracket : ISubOperation + { + public bool HasWhere + { + get; set; + } + + public ExpressionContext Context + { + get; set; + } + + public Expression Expression + { + get; set; + } + + public string Name + { + get + { + return "RightBracket"; + } + } + + public int Sort + { + get + { + return 500; + } + } + + public string GetValue(Expression expression) + { + return ")"; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubSelect.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubSelect.cs new file mode 100644 index 000000000..8bbc71e9b --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubSelect.cs @@ -0,0 +1,92 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public class SubSelect : ISubOperation + { + public bool HasWhere + { + get; set; + } + + public string Name + { + get + { + return "Select"; + } + } + + public Expression Expression + { + get; set; + } + + + public int Sort + { + get + { + return 200; + } + } + + public ExpressionContext Context + { + get; set; + } + + public string GetValue(Expression expression = null) + { + var exp = expression as MethodCallExpression; + InitType(exp); + var result = ""; + + var oldIsSingle = this.Context.IsSingle; + this.Context.IsSingle = false; + result = SubTools.GetMethodValue(this.Context, exp.Arguments[0], ResolveExpressType.FieldMultiple); + this.Context.IsSingle = oldIsSingle; + + SetShortName(exp, result); + + return result; + } + + private void InitType(MethodCallExpression exp) + { + foreach (var arg in (exp.Arguments[0] as LambdaExpression).Parameters) + { + if (this.Context.InitMappingInfo != null) + { + this.Context.InitMappingInfo(arg.Type); + this.Context.RefreshMapping(); + } + } + } + + public void SetShortName(MethodCallExpression exp, string result) + { + if (exp.Arguments[0] is LambdaExpression) + { + var parameters = (exp.Arguments[0] as LambdaExpression).Parameters; + if (parameters?.Count > 0) + { + this.Context.CurrentShortName = this.Context.GetTranslationColumnName(parameters[0].ObjToString()); + } + + } + } + public void SetShortNameNext(MethodCallExpression exp, string result) + { + if (exp.Arguments.Count > 1 && exp.Arguments[1] is LambdaExpression) + { + var parameters = (exp.Arguments[1] as LambdaExpression).Parameters; + if (parameters?.Count > 0) + { + this.Context.CurrentShortName = this.Context.GetTranslationColumnName(parameters[0].ObjToString()); + } + + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubSelectDefault.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubSelectDefault.cs new file mode 100644 index 000000000..3fb9acac1 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubSelectDefault.cs @@ -0,0 +1,43 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public class SubSelectDefault : ISubOperation + { + public bool HasWhere + { + get; set; + } + + public ExpressionContext Context + { + get; set; + } + + public Expression Expression + { + get; set; + } + + public string Name + { + get + { + return "SelectDefault"; + } + } + + public int Sort + { + get + { + return 250; + } + } + + public string GetValue(Expression expression) + { + return "*"; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubSelectStringJoin.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubSelectStringJoin.cs new file mode 100644 index 000000000..8615a7dae --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubSelectStringJoin.cs @@ -0,0 +1,104 @@ +using System.Linq.Expressions; +namespace SqlSugar +{ + public class SubSelectStringJoin : ISubOperation + { + public bool HasWhere + { + get; set; + } + + public string Name + { + get + { + return "SelectStringJoin"; + } + } + + public Expression Expression + { + get; set; + } + + + public int Sort + { + get + { + return 200; + } + } + + public ExpressionContext Context + { + get; set; + } + + public string GetValue(Expression expression = null) + { + var exp = expression as MethodCallExpression; + //var entityType = (exp.Arguments[0] as LambdaExpression).Parameters[0].Type; + //if (this.Context.InitMappingInfo != null) + //{ + // this.Context.InitMappingInfo(entityType); + // this.Context.RefreshMapping(); + //} + InitType(exp); + var result = ""; + if (this.Context.JoinIndex == 0) + result = SubTools.GetMethodValue(this.Context, exp.Arguments[0], ResolveExpressType.FieldSingle); + else + result = SubTools.GetMethodValueSubJoin(this.Context, exp.Arguments[0], ResolveExpressType.FieldMultiple); + + SetShortName(exp, result); + + if (result == null && ExpressionTool.GetLambdaExpressionBody(exp.Arguments[0]) is ConstantExpression) + { + var constant = ExpressionTool.GetLambdaExpressionBody(exp.Arguments[0]) as ConstantExpression; + if (constant.Value?.ToString()?.Contains(',') == true) + { + result = string.Join(",", (constant.Value + "").Split(',').Select(it => this.Context.GetTranslationTableName(it))); + } + } + result = this.Context.DbMehtods.GetStringJoinSelector(result, ExpressionTool.GetExpressionValue(exp.Arguments[1]) + ""); + + return result; + } + private void InitType(MethodCallExpression exp) + { + foreach (var arg in (exp.Arguments[0] as LambdaExpression).Parameters) + { + if (this.Context.InitMappingInfo != null) + { + this.Context.InitMappingInfo(arg.Type); + this.Context.RefreshMapping(); + } + } + } + public void SetShortName(MethodCallExpression exp, string result) + { + if (exp.Arguments[0] is LambdaExpression) + { + var parameters = (exp.Arguments[0] as LambdaExpression).Parameters; + if (parameters?.Count > 0) + { + this.Context.CurrentShortName = this.Context.GetTranslationColumnName(parameters[0].ObjToString()); + } + + } + } + public void SetShortNameNext(MethodCallExpression exp, string result) + { + if (exp.Arguments.Count > 1 && exp.Arguments[1] is LambdaExpression) + { + var parameters = (exp.Arguments[1] as LambdaExpression).Parameters; + if (parameters?.Count > 0) + { + this.Context.CurrentShortName = this.Context.GetTranslationColumnName(parameters[0].ObjToString()); + } + + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubSum.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubSum.cs new file mode 100644 index 000000000..c7b290edc --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubSum.cs @@ -0,0 +1,64 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public class SubSum : ISubOperation + { + public bool HasWhere + { + get; set; + } + + public string Name + { + get + { + return "Sum"; + } + } + + public Expression Expression + { + get; set; + } + + + public int Sort + { + get + { + return 200; + } + } + + public ExpressionContext Context + { + get; set; + } + + public string GetValue(Expression expression = null) + { + var exp = expression as MethodCallExpression; + var argExp = exp.Arguments[0]; + var parametres = (argExp as LambdaExpression).Parameters; + if ((argExp as LambdaExpression).Body is UnaryExpression) + { + argExp = ((argExp as LambdaExpression).Body as UnaryExpression).Operand; + } + var argLambda = argExp as LambdaExpression; + if (this.Context.InitMappingInfo != null && argLambda?.Parameters.Count > 0) + { + foreach (var item in argLambda.Parameters) + { + this.Context.InitMappingInfo(item.Type); + } + this.Context.RefreshMapping(); + } + var result = "SUM(" + SubTools.GetMethodValue(Context, argExp, ResolveExpressType.WhereMultiple) + ")"; + var selfParameterName = Context.GetTranslationColumnName(parametres.First().Name) + UtilConstants.Dot; + if (this.Context.JoinIndex == 0) + result = result.Replace(selfParameterName, SubTools.GetSubReplace(this.Context)); + return result; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubTake.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubTake.cs new file mode 100644 index 000000000..a5c6cc2e5 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubTake.cs @@ -0,0 +1,89 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public class SubTake : ISubOperation + { + public bool HasWhere + { + get; set; + } + + public ExpressionContext Context + { + get; set; + } + + public Expression Expression + { + get; set; + } + + public string Name + { + get + { + return "Take"; + } + } + + public int Sort + { + get + { + if (this.Context is SqlServerExpressionContext || this.Context.GetType().Name.Contains("Access")) + { + return 150; + } + else if (this.Context is OracleExpressionContext) + { + + return 401; + } + else + { + return 490; + } + } + } + + + public string GetValue(Expression expression) + { + var numExp = (expression as MethodCallExpression).Arguments[0]; + var num = 1; + if (ExpressionTool.GetParameters(numExp).Count != 0) + { + var copyContext = this.Context.GetCopyContextWithMapping(); + copyContext.IsSingle = false; + copyContext.Resolve(numExp, ResolveExpressType.WhereMultiple); + copyContext.Result.GetString(); + } + else + { + num = ExpressionTool.DynamicInvoke(numExp).ObjToInt(); + } + var take = (expression as MethodCallExpression); + if (this.Context is SqlServerExpressionContext || this.Context.GetType().Name.Contains("Access")) + { + return "TOP " + num; + } + else if (this.Context is OracleExpressionContext) + { + return (HasWhere ? "AND" : "WHERE") + " ROWNUM<=" + num; + } + else if (this.Context is PostgreSQLExpressionContext || this.Context?.SugarContext?.Context?.CurrentConnectionConfig?.MoreSettings?.DatabaseModel == DbType.PostgreSQL) + { + return "limit " + num; + } + else if (this.Context.GetLimit() != null) + { + return this.Context.GetLimit(); + } + else + { + return "limit " + num; + } + } + } +} \ No newline at end of file diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubToList.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubToList.cs new file mode 100644 index 000000000..e9ce548f6 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubToList.cs @@ -0,0 +1,210 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public class SubToList : ISubOperation + { + public bool HasWhere + { + get; set; + } + + public string Name + { + get + { + return "ToList"; + } + } + + public Expression Expression + { + get; set; + } + + + public int Sort + { + get + { + return 200; + } + } + + public ExpressionContext Context + { + get; set; + } + + public string GetValue(Expression expression = null) + { + var exp = expression as MethodCallExpression; + if (IsAutoGeneric(exp)) return GetValueByAuto(exp); + if (IsAutoSelect(exp)) return GetValueByAuto(exp); + InitType(exp); + var type = expression.Type; + if (type.FullName.IsCollectionsList() + && exp.Arguments.Count == 0 && type.GenericTypeArguments.Length > 0 + && this.Context.SugarContext?.QueryBuilder.IsSelectNoAll == true) + { + var entity = type.GenericTypeArguments[0]; + var columnNames = this.Context.SugarContext.Context.EntityMaintenance.GetEntityInfo(entity).Columns.Where(it => it.IsIgnore == false); + var columnsString = string.Join(",", columnNames + .Where(it => it.IsIgnore == false) + .Where(it => it.DbColumnName.HasValue()) + .Select(it => this.Context.GetTranslationColumnName(it.DbColumnName))); + return $"{columnsString},@sugarIndex as sugarIndex"; + } + else if (exp.Arguments.Count == 0) + { + return "*,@sugarIndex as sugarIndex"; + } + var argExp = exp.Arguments[0]; + var parametres = (argExp as LambdaExpression).Parameters; + if ((argExp as LambdaExpression).Body is UnaryExpression) + { + argExp = ((argExp as LambdaExpression).Body as UnaryExpression).Operand; + } + var argLambda = argExp as LambdaExpression; + var copyContext = this.Context.GetCopyContextWithMapping(); + copyContext.IsSingle = false; + copyContext.Resolve(argLambda, ResolveExpressType.SelectMultiple); + var select = copyContext.Result.GetString(); + this.Context.Parameters.AddRange(copyContext.Parameters); + this.Context.Index = copyContext.Index; + this.Context.ParameterIndex = copyContext.ParameterIndex; + SetShortName(exp, null); + return select + ",@sugarIndex as sugarIndex"; + } + + private void InitType(MethodCallExpression exp) + { + if (exp.Arguments.Count > 0) + { + foreach (var arg in (exp.Arguments[0] as LambdaExpression).Parameters) + { + if (this.Context.InitMappingInfo != null) + { + this.Context.InitMappingInfo(arg.Type); + this.Context.RefreshMapping(); + } + } + } + } + + public void SetShortName(MethodCallExpression exp, string result) + { + if (exp.Arguments.Count != 0 && exp.Arguments[0] is LambdaExpression) + { + var parameters = (exp.Arguments[0] as LambdaExpression).Parameters; + if (parameters?.Count > 0) + { + this.Context.CurrentShortName = this.Context.GetTranslationColumnName(parameters[0].ObjToString()); + } + + } + } + public void SetShortNameNext(MethodCallExpression exp, string result) + { + if (exp.Arguments.Count > 1 && exp.Arguments[1] is LambdaExpression) + { + var parameters = (exp.Arguments[1] as LambdaExpression).Parameters; + if (parameters?.Count > 0) + { + this.Context.CurrentShortName = this.Context.GetTranslationColumnName(parameters[0].ObjToString()); + } + + } + } + + private string GetValueByAuto(MethodCallExpression exp) + { + var selectExp = exp.Arguments.FirstOrDefault(); + if (selectExp == null) + { + var type = exp.Type.GenericTypeArguments[0]; + var parameter = Expression.Parameter(type, "it"); + + // 构造返回值表达式 + var body = Expression.MemberInit(Expression.New(type)); + + // 将返回值表达式作为lambda表达式的主体 + selectExp = Expression.Lambda(body, parameter); + + } + var bodyExp = ExpressionTool.GetLambdaExpressionBody(selectExp); + var newMemExp = (bodyExp as MemberInitExpression); + var parameters = ExpressionTool.GetParameters(exp); + InitType(exp); + if (parameters.Count != 0) + { + this.Context.CurrentShortName = this.Context.GetTranslationColumnName(parameters.FirstOrDefault().Name); + } + Check.ExceptionEasy(newMemExp == null, $"Subquery ToList(exp,true) expression {exp.ToString()} can only be it=>new class(){{Id = it.id}}", $"子查询ToList(exp,true)表达式{exp.ToString()}只能是it=>new class(){{ id=it.Id}}"); + var dic = ExpressionTool.GetMemberBindingItemList(newMemExp.Bindings); + var db = this.Context.SugarContext.Context; + var builder = this.Context.SugarContext.QueryBuilder.Builder; + var columnInfos = db.EntityMaintenance.GetEntityInfo(bodyExp.Type); + var autoColumns = columnInfos.Columns + .Where(it => !dic.ContainsKey(it.PropertyName)) + .Where(it => it.IsIgnore == false) + .ToList(); + List appendColumns = new List(); + List completeColumnColumns = new List(); + foreach (var item in autoColumns) + { + + foreach (var parameter in parameters) + { + var parameterColumns = db.EntityMaintenance.GetEntityInfo(parameter.Type).Columns.Where(it => it.IsIgnore == false); + if (!completeColumnColumns.Any(it => it.EqualCase(item.PropertyName)) && parameterColumns.Any(it => it.PropertyName.EqualCase(item.PropertyName))) + { + var completeColumn = parameterColumns.First(it => it.PropertyName.EqualCase(item.PropertyName)); + var shortName = builder.GetTranslationColumnName(parameter.Name); + var columnName = builder.GetTranslationColumnName(completeColumn.DbColumnName); + var asName = builder.SqlTranslationLeft + item.PropertyName + builder.SqlTranslationRight; + appendColumns.Add($"{shortName}.{columnName} as {asName}"); + completeColumnColumns.Add(completeColumn.PropertyName); + } + } + } + var copyContext = this.Context.GetCopyContextWithMapping(); + copyContext.IsSingle = false; + copyContext.Resolve(bodyExp, ResolveExpressType.SelectMultiple); + var select = copyContext.Result.GetString(); + if (dic.Count > 0 && appendColumns.Count == 0) + { + select = AppendParameter(copyContext, select); + return select + ",@sugarIndex as sugarIndex"; ; + } + else if (dic.Count > 0 && appendColumns.Count > 0) + { + select = AppendParameter(copyContext, select); + return select + "," + string.Join(",", appendColumns) + ",@sugarIndex as sugarIndex"; ; + } + else + { + return string.Join(",", appendColumns) + ",@sugarIndex as sugarIndex"; + } + } + + private string AppendParameter(ExpressionContext copyContext, string select) + { + if (copyContext.Parameters?.Count > 0) + { + this.Context.Parameters.AddRange(copyContext.Parameters); + select = select.Replace("), AS", ") AS"); + } + return select; + } + + private static bool IsAutoSelect(MethodCallExpression exp) + { + return exp.Arguments.Count == 2 && exp.Arguments.Last().Type == UtilConstants.BoolType; + } + private static bool IsAutoGeneric(MethodCallExpression exp) + { + return exp.Arguments.Count == 0 && exp.Method.GetGenericArguments().Length == 1; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubTop.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubTop.cs new file mode 100644 index 000000000..892da2092 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubTop.cs @@ -0,0 +1,75 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public class SubTop : ISubOperation + { + public bool HasWhere + { + get; set; + } + + public ExpressionContext Context + { + get; set; + } + + public Expression Expression + { + get; set; + } + + public string Name + { + get + { + return "Top"; + } + } + + public int Sort + { + get + { + if (this.Context is SqlServerExpressionContext || this.Context.GetType().Name.Contains("Access")) + { + return 150; + } + else if (this.Context is OracleExpressionContext) + { + + return 401; + } + else + { + return 490; + } + } + } + + + public string GetValue(Expression expression) + { + if (this.Context is SqlServerExpressionContext || this.Context.GetType().Name.Contains("Access")) + { + return "TOP 1"; + } + else if (this.Context is OracleExpressionContext) + { + return (HasWhere ? "AND" : "WHERE") + " ROWNUM=1"; + } + else if (this.Context is PostgreSQLExpressionContext || this.Context?.SugarContext?.Context?.CurrentConnectionConfig?.MoreSettings?.DatabaseModel == DbType.PostgreSQL) + { + return "limit 1"; + } + else if (this.Context.GetLimit() != null) + { + return this.Context.GetLimit(); + } + else + { + return "limit 0,1"; + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubWhere.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubWhere.cs new file mode 100644 index 000000000..e62eba212 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubWhere.cs @@ -0,0 +1,128 @@ +using System.Linq.Expressions; +using System.Text.RegularExpressions; + +namespace SqlSugar +{ + public class SubWhere : ISubOperation + { + public bool HasWhere + { + get; set; + } + + public string Name + { + get { return "Where"; } + } + + public Expression Expression + { + get; set; + } + + public int Sort + { + get + { + return 400; + } + } + + public ExpressionContext Context + { + get; set; + } + + public string GetValue(Expression expression) + { + var exp = expression as MethodCallExpression; + if (Regex.Matches(expression.ToString(), "Subqueryable").Count >= 2) + { + new SubSelect() { Context = this.Context }.SetShortName(exp, "+"); + } + var argExp = exp.Arguments[0]; + if (ExpressionTool.GetMethodName(argExp) == "ToExpression") + { + argExp = ExpressionTool.DynamicInvoke(argExp) as Expression; + } + var copyContext = this.Context; + var pars = ExpressionTool.GetParameters(expression).Distinct(); + if (this.Context.JoinIndex > 0 || pars.Count() > 1) + { + copyContext = this.Context.GetCopyContextWithMapping(); + copyContext.IsSingle = false; + } + var result = "WHERE " + SubTools.GetMethodValue(copyContext, argExp, ResolveExpressType.WhereMultiple); + if (argExp.Type == typeof(List) && this.Context.Parameters.Count != 0) + { + var p = this.Context.Parameters.Last(); + this.Context.Parameters.Remove(p); + var cols = p.Value as List; + var sqlObj = this.Context.SugarContext.QueryBuilder.Builder.ConditionalModelToSql(cols, this.Context.ParameterIndex * 100); + this.Context.ParameterIndex = this.Context.ParameterIndex + this.Context.ParameterIndex * 100; + result = "WHERE " + sqlObj.Key; + this.Context.Parameters.AddRange(sqlObj.Value); + return result; + } + else if (argExp.Type == typeof(List) && this.Context.Parameters.Count == 0) + { + var cols = ExpressionTool.DynamicInvoke(((expression as MethodCallExpression).Arguments[0])) as List; + var sqlObj = this.Context.SugarContext.QueryBuilder.Builder.ConditionalModelToSql(cols, this.Context.ParameterIndex * 100); + this.Context.ParameterIndex = this.Context.ParameterIndex + this.Context.ParameterIndex * 100; + result = "WHERE " + sqlObj.Key; + this.Context.Parameters.AddRange(sqlObj.Value); + return result; + } + if (this.Context.JoinIndex > 0 || pars.Count() > 1) + { + this.Context.Parameters.AddRange(copyContext.Parameters); + this.Context.Index = copyContext.Index; + this.Context.ParameterIndex = copyContext.ParameterIndex; + } + + var regex = @"^WHERE (\@Const\d+) $"; + if (this.Context is OracleExpressionContext) + { + regex = @"^WHERE (\:Const\d+) $"; + } + if (this.Context is DmExpressionContext) + { + regex = @"^WHERE (\:Const\d+) $"; + } + if (Regex.IsMatch(result, regex)) + { + var value = GetValue(result, regex); + if (value is Expression) + { + var p = this.Context.Parameters.First(it => it.ParameterName == Regex.Match(result, regex).Groups[1].Value); + result = "WHERE " + SubTools.GetMethodValue(Context, value as Expression, ResolveExpressType.WhereMultiple); + argExp = value as Expression; + this.Context.Parameters.Remove(p); + } + else + { + result = "WHERE " + value; + return result; + } + } + + var selfParameterName = Context.GetTranslationColumnName((argExp as LambdaExpression).Parameters.First().Name) + UtilConstants.Dot; + if (this.Context.JoinIndex == 0 && result.Contains(" FROM ")) + { + this.Context.CurrentShortName = selfParameterName.ObjToString().TrimEnd('.'); + } + else if (this.Context.JoinIndex == 0 && this.Context.CurrentShortName != selfParameterName.TrimEnd('.')) + result = result.Replace(selfParameterName, SubTools.GetSubReplace(this.Context)); + if (!string.IsNullOrEmpty(selfParameterName) && this.Context.IsSingle && this.Context.JoinIndex == 0) + { + this.Context.CurrentShortName = selfParameterName.TrimEnd('.'); + } + return result; + } + + private object GetValue(string result, string regex) + { + return this.Context.Parameters.First(it => it.ParameterName == Regex.Match(result, regex).Groups[1].Value).Value; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubWhereIF.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubWhereIF.cs new file mode 100644 index 000000000..736690d2d --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubWhereIF.cs @@ -0,0 +1,81 @@ +using System.Linq.Expressions; +using System.Text.RegularExpressions; + +namespace SqlSugar +{ + public class SubWhereIF : ISubOperation + { + public bool HasWhere + { + get; set; + } + + public string Name + { + get { return "WhereIF"; } + } + + public Expression Expression + { + get; set; + } + + public int Sort + { + get + { + return 400; + } + } + + public ExpressionContext Context + { + get; set; + } + + public string GetValue(Expression expression) + { + var exp = expression as MethodCallExpression; + object value = null; + try + { + value = ExpressionTool.DynamicInvoke(exp.Arguments[0]); + } + catch + { + Check.Exception(true, ErrorMessage.WhereIFCheck, exp.Arguments[0].ToString()); + } + if (Regex.Matches(expression.ToString(), "Subqueryable").Count >= 2) + { + new SubSelect() { Context = this.Context }.SetShortNameNext(exp, "+"); + } + var isWhere = Convert.ToBoolean(value); + if (!Convert.ToBoolean(isWhere)) + { + return "WHERE 1=1 "; + } + var argExp = exp.Arguments[1]; + var copyContext = this.Context; + if (this.Context.JoinIndex > 0) + { + copyContext = this.Context.GetCopyContextWithMapping(); + copyContext.IsSingle = false; + } + var result = "WHERE " + SubTools.GetMethodValue(copyContext, argExp, ResolveExpressType.WhereMultiple); + if (this.Context.JoinIndex > 0) + { + this.Context.Parameters.AddRange(copyContext.Parameters); + this.Context.Index = copyContext.Index; + this.Context.ParameterIndex = copyContext.ParameterIndex; + } + var selfParameterName = Context.GetTranslationColumnName((argExp as LambdaExpression).Parameters.First().Name) + UtilConstants.Dot; + if (this.Context.JoinIndex == 0) + result = result.Replace(selfParameterName, SubTools.GetSubReplace(this.Context)); + if (!string.IsNullOrEmpty(selfParameterName) && this.Context.IsSingle && this.Context.JoinIndex == 0) + { + this.Context.CurrentShortName = selfParameterName.TrimEnd('.'); + } + return result; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubWithNoLock.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubWithNoLock.cs new file mode 100644 index 000000000..9222972a2 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Items/SubWithNoLock.cs @@ -0,0 +1,50 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public class SubWithNolock : ISubOperation + { + public ExpressionContext Context + { + get; set; + } + + public Expression Expression + { + get; set; + } + + public bool HasWhere + { + get; set; + } + + public string Name + { + get + { + return "WithNoLock"; + } + } + + public int Sort + { + get + { + return 301; + } + } + + public string GetValue(Expression expression) + { + if (Context is SqlServerExpressionContext) + { + return SqlWith.NoLock; + } + else + { + return ""; + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/SubResolve.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/SubResolve.cs new file mode 100644 index 000000000..ea98fbcfd --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/SubResolve.cs @@ -0,0 +1,326 @@ +using System.Linq.Expressions; +using System.Text.RegularExpressions; + +namespace SqlSugar +{ + /// + /// ** description:Get subquery sql + /// ** author:sunkaixuan + /// ** date:2017/9/17 + /// ** email:610262374@qq.com + /// + public class SubResolve + { + List allMethods = new List(); + private ExpressionContext context = null; + private string subKey = "$SubAs:"; + private bool hasWhere; + private bool isXmlPath = false; + private bool isAsAttr = false; + public SubResolve(MethodCallExpression expression, ExpressionContext context, Expression oppsiteExpression) + { + this.context = context; + var currentExpression = expression; + allMethods.Add(currentExpression); + if (context.IsSingle && oppsiteExpression != null && oppsiteExpression is MemberExpression) + { + var childExpression = (oppsiteExpression as MemberExpression).Expression; + if (childExpression is ParameterExpression) + this.context.SingleTableNameSubqueryShortName = (childExpression as ParameterExpression).Name; + else + { + this.context.SingleTableNameSubqueryShortName = (context.Expression as LambdaExpression).Parameters.First().Name; + } + } + else if (context.IsSingle && ExpressionTool.GetMethodName(currentExpression) != "ToList") + { + if (context.Expression is LambdaExpression) + { + this.context.SingleTableNameSubqueryShortName = (context.Expression as LambdaExpression).Parameters.First().Name; + } + else if (context.Expression is MethodCallExpression) + { + var expArgs = ((context.Expression as MethodCallExpression).Object as MethodCallExpression).Arguments; + if (expArgs != null && expArgs.Count != 0) + { + var meExp = expArgs[0] as LambdaExpression; + if (meExp != null) + { + var selfParameterName = meExp.Parameters.First().Name; + if ((meExp.Body is BinaryExpression)) + { + context.SingleTableNameSubqueryShortName = (((meExp.Body as BinaryExpression).Left as MemberExpression)?.Expression as ParameterExpression)?.Name; + } + if (ExpressionTool.GetMethodName(context.Expression).IsContainsIn("ToList") && meExp.Parameters.Any(it => it.Name == selfParameterName)) + { + if (meExp.Body is BinaryExpression) + { + context.SingleTableNameSubqueryShortName = (((meExp.Body as BinaryExpression).Right as MemberExpression)?.Expression as ParameterExpression)?.Name; + } + } + if (context.SingleTableNameSubqueryShortName == selfParameterName) + { + if (meExp.Body is BinaryExpression) + { + context.SingleTableNameSubqueryShortName = (((meExp.Body as BinaryExpression).Right as MemberExpression)?.Expression as ParameterExpression)?.Name; + } + } + } + } + } + else if (context.Expression.GetType().Name == "MethodBinaryExpression") + { + + var subExp = (context.Expression as BinaryExpression).Left is MethodCallExpression ? (context.Expression as BinaryExpression).Left : (context.Expression as BinaryExpression).Right; + if (subExp is MethodCallExpression) + { + var meExp = ((subExp as MethodCallExpression).Object as MethodCallExpression).Arguments[0] as LambdaExpression; + var selfParameterName = meExp.Parameters.First().Name; + context.SingleTableNameSubqueryShortName = (((meExp.Body as BinaryExpression).Left as MemberExpression).Expression as ParameterExpression).Name; + if (context.SingleTableNameSubqueryShortName == selfParameterName) + { + context.SingleTableNameSubqueryShortName = (((meExp.Body as BinaryExpression).Right as MemberExpression).Expression as ParameterExpression).Name; + } + } + } + else if (context.RootExpression != null && context.Expression.GetType().Name == "SimpleBinaryExpression") + { + var name = (this.context.RootExpression as LambdaExpression).Parameters[0].Name; + context.SingleTableNameSubqueryShortName = name; + } + else if (context.Expression is BinaryExpression) + { + var subExp = (context.Expression as BinaryExpression).Left is MethodCallExpression ? (context.Expression as BinaryExpression).Left : (context.Expression as BinaryExpression).Right; + if (subExp is MethodCallExpression) + { + var argus = ((subExp as MethodCallExpression).Object as MethodCallExpression).Arguments; + if (argus.Count > 0) + { + var meExp = argus[0] as LambdaExpression; + var selfParameterName = meExp.Parameters.First().Name; + context.SingleTableNameSubqueryShortName = (((meExp.Body as BinaryExpression).Left as MemberExpression)?.Expression as ParameterExpression)?.Name; + if (context.SingleTableNameSubqueryShortName == selfParameterName) + { + context.SingleTableNameSubqueryShortName = (((meExp.Body as BinaryExpression).Right as MemberExpression).Expression as ParameterExpression).Name; + } + } + } + } + else if (context.Expression is MemberInitExpression memberInitExpression) + { + var getParameters = ExpressionTool.GetParameters(context.Expression).Select(it => it.Name).Distinct().ToList(); + if (getParameters?.Count > 1) + { + context.SingleTableNameSubqueryShortName = getParameters.First(); + } + } + else + { + Check.ExceptionEasy("I'm sorry I can't parse the current expression", "不支持当前表达式"); + } + } + var subIndex = this.context.SubQueryIndex; + while (currentExpression != null) + { + var addItem = currentExpression.Object as MethodCallExpression; + if (addItem != null) + allMethods.Add(addItem); + if (subIndex == this.context.SubQueryIndex && addItem?.Arguments.HasValue() == true && addItem.Arguments.Any(it => it.ToString().Contains("Subqueryable()"))) + { + this.context.SubQueryIndex++; + } + currentExpression = addItem; + } + } + public string GetSql() + { + List subItems = GetSubItems(); + var sqlItems = subItems.Where(it => !it.StartsWith(subKey)).ToList(); + var asItems = subItems.Where(it => it.StartsWith(subKey)).ToList(); + if (asItems.Count != 0) + { + GetSubAs(sqlItems, asItems); + } + if (this.context.CurrentShortName.HasValue()) + { + GetShortName(sqlItems); + } + var sql = ""; + + if (sqlItems.Count(it => IsJoin(it)) > 1) + { + var index = sqlItems.IndexOf(sqlItems.First(x => IsJoin(x))); + var joinitems = sqlItems.Where(it => IsJoin(it)).ToList(); + joinitems.Reverse(); + var items = sqlItems.Where(it => !IsJoin(it)).ToList(); + items.InsertRange(index, joinitems); + sql = string.Join(UtilConstants.Space, items); + } + else + { + sql = string.Join(UtilConstants.Space, sqlItems); + } + if (isXmlPath) + { + var xmlPath = context.DbMehtods.GetForXmlPath(); + if (xmlPath.HasValue()) + { + sql = sql + xmlPath; + } + } + return this.context.DbMehtods.Pack(sql); + } + + private static bool IsJoin(string it) + { + return it.StartsWith(" INNER JOIN") || it.StartsWith(" LEFT JOIN"); + } + + private void GetSubAs(List sqlItems, List asItems) + { + for (int i = 0; i < sqlItems.Count; i++) + { + if (sqlItems[i].StartsWith("FROM " + this.context.SqlTranslationLeft)) + { + var asName = this.context.GetTranslationTableName(asItems.First().Replace(subKey, ""), false); + var repKey = $"\\{this.context.SqlTranslationLeft}.+\\{this.context.SqlTranslationRight}"; + if (this.context.IsSingle && this.context.JoinIndex == 0 && this.context.CurrentShortName.HasValue() && isAsAttr && !asName.Contains(this.context.CurrentShortName)) + { + asName = asName + " " + this.context.CurrentShortName + " "; + } + sqlItems[i] = Regex.Replace(sqlItems[i], repKey, asName); + } + } + } + private void GetShortName(List sqlItems) + { + for (int i = 0; i < sqlItems.Count; i++) + { + if (sqlItems[i].StartsWith("FROM " + this.context.SqlTranslationLeft)) + { + if (isAsAttr && sqlItems[i].EndsWith(this.context.CurrentShortName + " ")) + { + } + else + { + sqlItems[i] = sqlItems[i] + " " + this.context.CurrentShortName + " "; + } + } + } + } + + private List GetSubItems() + { + var isSubSubQuery = this.allMethods.Select(it => it.ToString()).Any(it => Regex.Matches(it, "Subquery").Count > 1); + var isubList = this.allMethods.Select(exp => + { + if (isSubSubQuery) + { + this.context.JoinIndex = 1; + this.context.SubQueryIndex = 0; + } + var methodName = exp.Method.Name; + var items = SubTools.SubItems(this.context); + var item = items.First(s => s.Name == methodName); + if (item is SubWhere && hasWhere == false) + { + hasWhere = true; + } + else if (item is SubWhere) + { + item = items.First(s => s is SubAnd); + } + + if (item is SubWhereIF && hasWhere == false) + { + hasWhere = true; + } + else if (item is SubWhereIF) + { + item = items.First(s => s is SubAndIF); + } + else if (item is SubSelectStringJoin) + { + isXmlPath = true; + } + else if (item is SubAsWithAttr) + { + isAsAttr = true; + } + + item.Context = this.context; + item.Expression = exp; + return item; + }).ToList(); + SetOrderByIndex(isubList); + isubList.Insert(0, new SubBegin()); + if (isubList.Any(it => it is SubSelect || it is SubFirst)) + { + isubList.Add(new SubTop() { Context = this.context }); + } + if (isubList.Any(it => it is SubAny || it is SubNotAny)) + { + isubList.Add(new SubLeftBracket()); + isubList.Add(new SubRightBracket()); + isubList.Add(new SubSelectDefault()); + } + var db = this.context?.SugarContext?.Context; + if (db != null && db?.CurrentConnectionConfig?.DbType == DbType.SqlServer) + { + if (db.CurrentConnectionConfig?.MoreSettings?.IsWithNoLockSubquery == true) + { + if (!isubList.Any(it => it is SubWithNolock)) + { + isubList.Add(new SubWithNolock() { Context = this.context }); + } + } + } + isubList = isubList.OrderBy(it => it.Sort).ToList(); + var isHasWhere = isubList.Where(it => it is SubWhere).Any(); + var isJoin = isubList.Any(it => it is SubInnerJoin || it is SubLeftJoin); + if (isJoin) + { + this.context.JoinIndex++; + } + if (isAsAttr) + { + this.context.IsAsAttr = true; + } + if (isubList.Any(it => it is SubSelect) && isubList.Any(it => it is SubTake)) + { + isubList.RemoveAll(it => it is SubTake); + } + List result = isubList.Select(it => + { + it.HasWhere = isHasWhere; + return it.GetValue(it.Expression); + }).ToList(); + this.context.JoinIndex = 0; + this.context.IsAsAttr = false; + return result; + } + + private static void SetOrderByIndex(List isubList) + { + var orderByIndex = 0; + var orderByList = isubList.Where(it => it is SubOrderBy || it is SubOrderByDesc).ToList(); + if (orderByList.Count > 1) + { + orderByList.Reverse(); + foreach (var item in orderByList) + { + if (item is SubOrderBy) + { + (item as SubOrderBy).OrderIndex = orderByIndex; + orderByIndex++; + } + else if (item is SubOrderByDesc) + { + (item as SubOrderByDesc).OrderIndex = orderByIndex; + orderByIndex++; + } + } + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/SubTemplate.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/SubTemplate.cs new file mode 100644 index 000000000..9f59b55cb --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/SubTemplate.cs @@ -0,0 +1,40 @@ +//@{ +// var count = 9; +// var T = ""; +// var Tn = ""; +// for (int i = 0; i < count; i++) +// { +// T += "T" + (i + 1) + ","; +// } +// Tn = T + "JoinType"; +// T = T.TrimEnd(','); +//} +//public class Subqueryable<@T> : Subqueryable where T1 : class, new() +//{ +// public Subqueryable<@Tn> InnerJoin(Func<@Tn, bool> expression) +// { +// return new Subqueryable<@Tn>(); +// } +// public Subqueryable<@Tn> LeftJoin(Func<@Tn, bool> expression) +// { +// return new Subqueryable<@Tn>(); +// } +// @for(int i = 0; i Where(Func<@wtn, bool> expression) +// @:{ +// @:return this; +// @:} + +//} +//} + diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/SubTools.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/SubTools.cs new file mode 100644 index 000000000..0cccecf90 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/SubTools.cs @@ -0,0 +1,91 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public static class SubTools + { + public static List SubItems(ExpressionContext Context) + { + + return new List() + { + new SubSelect() { Context=Context }, + new SubWhere(){ Context=Context }, + new SubWhereIF(){ Context=Context }, + new SubLeftJoin(){ Context=Context }, + new SubInnerJoin(){ Context=Context }, + new SubAnd(){ Context=Context }, + new SubAndIF(){ Context=Context }, + new SubAny(){ Context=Context }, + new SubNotAny(){ Context=Context }, + new SubBegin(){ Context=Context }, + new SubFromTable(){ Context=Context }, + new SubCount(){ Context=Context }, + new SubMax(){ Context=Context }, + new SubMin(){ Context=Context }, + new SubSum(){ Context=Context }, + new SubAvg(){ Context=Context }, + new SubOrderBy(){ Context=Context }, + new SubOrderByDesc(){ Context=Context }, + new SubGroupBy(){ Context=Context}, + new SubAs(){Context=Context}, + new SubHaving(){ Context=Context}, + new SubWithNolock(){ Context=Context }, + new SubEnableTableFilter(){ Context=Context }, + new SubSelectStringJoin{ Context=Context }, + new SubDistinctCount{ Context=Context }, + new SubToList{ Context=Context}, + new SubFirst(){ Context=Context }, + new SubAsWithAttr(){ Context=Context }, + new SubTake(){ Context=Context } + }; + } + + public static string GetSubReplace(ExpressionContext context) + { + if (context.SubQueryIndex == 0) + return string.Empty; + else + return "subTableIndex" + context.SubQueryIndex + "."; + } + + public static List SubItemsConst = SubItems(null); + + public static string GetMethodValue(ExpressionContext context, Expression item, ResolveExpressType type) + { + var newContext = context.GetCopyContext(); + newContext.MappingColumns = context.MappingColumns; + newContext.MappingTables = context.MappingTables; + newContext.InitMappingInfo = context.InitMappingInfo; + newContext.RefreshMapping = context.RefreshMapping; + newContext.IgnoreComumnList = context.IgnoreComumnList; + newContext.SqlFuncServices = context.SqlFuncServices; + newContext.Resolve(item, type); + context.Index = newContext.Index; + context.ParameterIndex = newContext.ParameterIndex; + if (newContext.Parameters.HasValue()) + context.Parameters.AddRange(newContext.Parameters); + return newContext.Result.GetResultString(); + } + public static string GetMethodValueSubJoin(ExpressionContext context, Expression item, ResolveExpressType type) + { + var newContext = context.GetCopyContext(); + newContext.MappingColumns = context.MappingColumns; + newContext.MappingTables = context.MappingTables; + newContext.InitMappingInfo = context.InitMappingInfo; + newContext.RefreshMapping = context.RefreshMapping; + newContext.IgnoreComumnList = context.IgnoreComumnList; + newContext.SqlFuncServices = context.SqlFuncServices; + if (type == ResolveExpressType.WhereMultiple || type == ResolveExpressType.FieldMultiple) + { + newContext.IsSingle = false; + } + newContext.Resolve(item, type); + context.Index = newContext.Index; + context.ParameterIndex = newContext.ParameterIndex; + if (newContext.Parameters.HasValue()) + context.Parameters.AddRange(newContext.Parameters); + return newContext.Result.GetResultString(); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Subquerable.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Subquerable.cs new file mode 100644 index 000000000..7d62a74be --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/Subquerable.cs @@ -0,0 +1,229 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public partial class Subqueryable where T : class, new() + { + + public Subqueryable AS(string tableName) + { + return this; + } + public Subqueryable InnerJoin(Func expression) + { + return new Subqueryable(); + } + + public Subqueryable InnerJoin(Func expression, string tableName) + { + return new Subqueryable(); + } + + public Subqueryable LeftJoin(Func expression) + { + return new Subqueryable(); + } + + public Subqueryable LeftJoin(Func expression, string tableName) + { + return new Subqueryable(); + } + + + public Subqueryable Where(string where) + { + return this; + } + public Subqueryable Where(List conditionals) + { + return this; + } + public Subqueryable Where(Expression exp) + { + return this; + } + public Subqueryable Where(Func expression) + { + return this; + } + public Subqueryable Having(Func expression) + { + return this; + } + public Subqueryable Where(Func expression) + { + return this; + } + public Subqueryable Where(Func expression) + { + return this; + } + public Subqueryable Where(Func expression) + { + return this; + } + public Subqueryable Where(Func expression) + { + return this; + } + public Subqueryable WhereIF(bool isWhere, Func expression) + { + return this; + } + public Subqueryable OrderBy(Func expression) + { + return this; + } + public Subqueryable GroupBy(Func expression) + { + return this; + } + public Subqueryable OrderByDesc(Func expression) + { + return this; + } + + public TResult Select(Func expression) + { + return default(TResult); + } + public Byte[] Select(Func expression) + { + return null; + } + public string Select(Func expression) + { + return default(string); + } + + public string SelectStringJoin(Func expression, string separator) + { + return default(string); + } + + public TResult Max(Func expression) + { + return default(TResult); + } + public Byte[] Max(Func expression) + { + return null; + } + public string Max(Func expression) + { + return default(string); + } + + public string Min(Func expression) + { + return default(string); + } + public TResult Min(Func expression) + { + return default(TResult); + } + public Byte[] Min(Func expression) + { + return null; + } + + + public string Sum(Func expression) + { + return default(string); + } + + public int DistinctCount(Func expression) + { + return default(int); + } + public TResult Sum(Func expression) + { + return default(TResult); + } + public Byte[] Sum(Func expression) + { + return null; + } + + public string Avg(Func expression) + { + return default(string); + } + public TResult Avg(Func expression) where TResult : struct + { + return default(TResult); + } + public Byte[] Avg(Func expression) + { + return null; + } + + public bool Any() + { + return default(bool); + } + + public bool NotAny() + { + return default(bool); + } + + public int Count() + { + return default(int); + } + + public Subqueryable WithNoLock() + { + return this; + } + public Subqueryable EnableTableFilter() + { + return this; + } + + public List ToList() + { + return new List(); + } + public List ToList(Func selector) + { + return null; + } + public List ToList() + { + return null; + } + public List ToList(Func selector, bool isAutoDto) where TResult : class, new() + { + return null; + } + public T First() + { + return default(T); + } + public TResult First(Func selector) where TResult : class, new() + { + return default(TResult); + } + public TResult First(Func selector, bool isAutoDto) where TResult : class, new() + { + return default(TResult); + } + public TResult First() where TResult : class, new() + { + return default(TResult); + } + + public Subqueryable AsWithAttr() + { + return this; + } + + public Subqueryable Take(int takeNum) + { + return this; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/SubqueryableN.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/SubqueryableN.cs new file mode 100644 index 000000000..25a43bfc4 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExpressionsToSql/Subquery/SubqueryableN.cs @@ -0,0 +1,695 @@ +namespace SqlSugar +{ + public class Subqueryable : Subqueryable where T1 : class, new() + { } + public class Subqueryable : Subqueryable where T1 : class, new() + { + public new Subqueryable AsWithAttr() + { + return this; + } + public Subqueryable InnerJoin(Func expression) + { + return new Subqueryable(); + } + public Subqueryable LeftJoin(Func expression) + { + return new Subqueryable(); + } + public Subqueryable InnerJoin(Func expression, string tableName) + { + return new Subqueryable(); + } + public Subqueryable LeftJoin(Func expression, string tableName) + { + return new Subqueryable(); + } + public new Subqueryable Where(Func expression) + { + return this; + } + public Subqueryable Where(Func expression) + { + return this; + } + public Subqueryable Where(Func expression) + { + return this; + } + public Subqueryable Where(Func expression) + { + return this; + } + public Subqueryable Where(Func expression) + { + return this; + } + public Subqueryable Where(Func expression) + { + return this; + } + public Subqueryable Where(Func expression) + { + return this; + } + public Subqueryable Where(Func expression) + { + return this; + } + public Subqueryable Where(Func expression) + { + return this; + } + public List ToList(Func selector) + { + return null; + } + public new Subqueryable Take(int takeNum) + { + return this; + } + } + public class Subqueryable : Subqueryable where T1 : class, new() + { + public new Subqueryable AsWithAttr() + { + return this; + } + public Subqueryable InnerJoin(Func expression) + { + return new Subqueryable(); + } + public Subqueryable LeftJoin(Func expression) + { + return new Subqueryable(); + } + public Subqueryable InnerJoin(Func expression, string tableName) + { + return new Subqueryable(); + } + public Subqueryable LeftJoin(Func expression, string tableName) + { + return new Subqueryable(); + } + public new Subqueryable Where(Func expression) + { + return this; + } + public Subqueryable Where(Func expression) + { + return this; + } + public Subqueryable Where(Func expression) + { + return this; + } + public Subqueryable Where(Func expression) + { + return this; + } + public Subqueryable Where(Func expression) + { + return this; + } + public Subqueryable Where(Func expression) + { + return this; + } + public Subqueryable Where(Func expression) + { + return this; + } + public Subqueryable Where(Func expression) + { + return this; + } + public List ToList(Func selector) + { + return null; + } + public new Subqueryable Take(int takeNum) + { + return this; + } + } + public class Subqueryable : Subqueryable where T1 : class, new() + { + public new Subqueryable AsWithAttr() + { + return this; + } + public Subqueryable InnerJoin(Func expression) + { + return new Subqueryable(); + } + public Subqueryable LeftJoin(Func expression) + { + return new Subqueryable(); + } + public Subqueryable InnerJoin(Func expression, string tableName) + { + return new Subqueryable(); + } + public Subqueryable LeftJoin(Func expression, string tableName) + { + return new Subqueryable(); + } + public new Subqueryable Where(Func expression) + { + return this; + } + public Subqueryable Where(Func expression) + { + return this; + } + public Subqueryable Where(Func expression) + { + return this; + } + public Subqueryable Where(Func expression) + { + return this; + } + public Subqueryable Where(Func expression) + { + return this; + } + public Subqueryable Where(Func expression) + { + return this; + } + public Subqueryable Where(Func expression) + { + return this; + } + public List ToList(Func selector) + { + return null; + } + public new Subqueryable Take(int takeNum) + { + return this; + } + } + public class Subqueryable : Subqueryable where T1 : class, new() + { + public string SelectStringJoin(Func expression, string separator) + { + return default(string); + } + public new Subqueryable AsWithAttr() + { + return this; + } + public new Subqueryable Where(List conditionals) + { + return this; + } + public Subqueryable InnerJoin(Func expression) + { + return new Subqueryable(); + } + public Subqueryable LeftJoin(Func expression) + { + return new Subqueryable(); + } + + public Subqueryable InnerJoin(Func expression, string tableName) + { + return new Subqueryable(); + } + public Subqueryable LeftJoin(Func expression, string tableName) + { + return new Subqueryable(); + } + public new Subqueryable Where(Func expression) + { + return this; + } + public Subqueryable Where(Func expression) + { + return this; + } + public Subqueryable Where(Func expression) + { + return this; + } + public Subqueryable Where(Func expression) + { + return this; + } + public Subqueryable Where(Func expression) + { + return this; + } + public Subqueryable Where(Func expression) + { + return this; + } + public List ToList(Func selector) + { + return null; + } + public new Subqueryable Take(int takeNum) + { + return this; + } + } + public class Subqueryable : Subqueryable where T1 : class, new() + { + public string SelectStringJoin(Func expression, string separator) + { + return default(string); + } + public new Subqueryable AsWithAttr() + { + return this; + } + public new Subqueryable Where(List conditionals) + { + return this; + } + public Subqueryable InnerJoin(Func expression) + { + return new Subqueryable(); + } + public Subqueryable LeftJoin(Func expression) + { + return new Subqueryable(); + } + + public Subqueryable InnerJoin(Func expression, string tableName) + { + return new Subqueryable(); + } + public Subqueryable LeftJoin(Func expression, string tableName) + { + return new Subqueryable(); + } + public new Subqueryable Where(Func expression) + { + return this; + } + public Subqueryable Where(Func expression) + { + return this; + } + public Subqueryable Where(Func expression) + { + return this; + } + public Subqueryable Where(Func expression) + { + return this; + } + public Subqueryable Where(Func expression) + { + return this; + } + public List ToList(Func selector) + { + return null; + } + public new Subqueryable Take(int takeNum) + { + return this; + } + } + public class Subqueryable : Subqueryable where T1 : class, new() + { + public new Subqueryable AsWithAttr() + { + return this; + } + public new Subqueryable Where(List conditionals) + { + return this; + } + public Subqueryable InnerJoin(Func expression) + { + return new Subqueryable(); + } + public Subqueryable LeftJoin(Func expression) + { + return new Subqueryable(); + } + public Subqueryable InnerJoin(Func expression, string tableName) + { + return new Subqueryable(); + } + public Subqueryable LeftJoin(Func expression, string tableName) + { + return new Subqueryable(); + } + public new Subqueryable Where(Func expression) + { + return this; + } + public Subqueryable Where(Func expression) + { + return this; + } + public Subqueryable Where(Func expression) + { + return this; + } + public Subqueryable Where(Func expression) + { + return this; + } + public Subqueryable WhereIF(bool isWhere, Func expression) + { + return this; + } + public TResult Select(Func expression) where TResult : struct + { + return default(TResult); + } + public string Select(Func expression) + { + return default(string); + } + public Subqueryable OrderBy(Func expression) + { + return this; + } + public Subqueryable OrderByDesc(Func expression) + { + return this; + } + public Subqueryable GroupBy(Func expression) + { + return this; + } + public string SelectStringJoin(Func expression, string separator) + { + return default(string); + } + public List ToList(Func selector) + { + return null; + } + public List ToList(Func selector, bool isAutoDto) where TResult : class, new() + { + return null; + } + public TResult First(Func selector, bool isAutoDto) where TResult : class, new() + { + return default(TResult); + } + public TResult Max(Func expression) + { + return default(TResult); + } + public Byte[] Max(Func expression) + { + return null; + } + public string Max(Func expression) + { + return default(string); + } + + public string Min(Func expression) + { + return default(string); + } + public TResult Min(Func expression) + { + return default(TResult); + } + public Byte[] Min(Func expression) + { + return null; + } + public string Sum(Func expression) + { + return default(string); + } + public TResult Sum(Func expression) + { + return default(TResult); + } + + public new Subqueryable Take(int takeNum) + { + return this; + } + } + public class Subqueryable : Subqueryable where T1 : class, new() + { + public new Subqueryable AsWithAttr() + { + return this; + } + public new Subqueryable Where(List conditionals) + { + return this; + } + public Subqueryable InnerJoin(Func expression) + { + return new Subqueryable(); + } + public Subqueryable LeftJoin(Func expression) + { + return new Subqueryable(); + } + public new Subqueryable Where(Func expression) + { + return this; + } + public Subqueryable Where(Func expression) + { + return this; + } + public Subqueryable Where(Func expression) + { + return this; + } + public Subqueryable WhereIF(bool isWhere, Func expression) + { + return this; + } + public Subqueryable OrderBy(Func expression) + { + return this; + } + public Subqueryable OrderBy(Func expression) + { + return this; + } + public new Subqueryable OrderBy(Func expression) + { + return this; + } + public Subqueryable OrderByDesc(Func expression) + { + return this; + } + public Subqueryable OrderByDesc(Func expression) + { + return this; + } + public new Subqueryable OrderByDesc(Func expression) + { + return this; + } + public Subqueryable GroupBy(Func expression) + { + return this; + } + public Subqueryable GroupBy(Func expression) + { + return this; + } + public new Subqueryable GroupBy(Func expression) + { + return this; + } + public TResult Select(Func expression) where TResult : struct + { + return default(TResult); + } + public string Select(Func expression) + { + return default(string); + } + public string SelectStringJoin(Func expression, string separator) + { + return default(string); + } + public List ToList(Func selector) + { + return null; + } + public List ToList(Func selector, bool isAutoDto) where TResult : class, new() + { + return null; + } + public TResult First(Func selector, bool isAutoDto) where TResult : class, new() + { + return default(TResult); + } + public TResult Max(Func expression) + { + return default(TResult); + } + public Byte[] Max(Func expression) + { + return null; + } + public string Max(Func expression) + { + return default(string); + } + + public string Min(Func expression) + { + return default(string); + } + public TResult Min(Func expression) + { + return default(TResult); + } + public Byte[] Min(Func expression) + { + return null; + } + public string Sum(Func expression) + { + return default(string); + } + public TResult Sum(Func expression) + { + return default(TResult); + } + public new Subqueryable Take(int takeNum) + { + return this; + } + } + public class Subqueryable : Subqueryable where T1 : class, new() + { + public new Subqueryable AsWithAttr() + { + return this; + } + public new Subqueryable Where(List conditionals) + { + return this; + } + public Subqueryable InnerJoin(Func expression) + { + return new Subqueryable(); + } + public Subqueryable LeftJoin(Func expression) + { + return new Subqueryable(); + } + public Subqueryable InnerJoin(Func expression, string tableName) + { + return new Subqueryable(); + } + public Subqueryable LeftJoin(Func expression, string tableName) + { + return new Subqueryable(); + } + public new Subqueryable Where(Func expression) + { + return this; + } + public Subqueryable Where(Func expression) + { + return this; + } + public Subqueryable OrderBy(Func expression) + { + return this; + } + public new Subqueryable OrderBy(Func expression) + { + return this; + } + public Subqueryable OrderByDesc(Func expression) + { + return this; + } + public new Subqueryable OrderByDesc(Func expression) + { + return this; + } + public Subqueryable GroupBy(Func expression) + { + return this; + } + public new Subqueryable GroupBy(Func expression) + { + return this; + } + public Subqueryable WhereIF(bool isWhere, Func expression) + { + return this; + } + public TResult Select(Func expression) where TResult : struct + { + return default(TResult); + } + public string Select(Func expression) + { + return default(string); + } + public string SelectStringJoin(Func expression, string separator) + { + return default(string); + } + public List ToList(Func selector) + { + return null; + } + public List ToList(Func selector, bool isAutoDto) where TResult : class, new() + { + return null; + } + public TResult First(Func selector, bool isAutoDto) where TResult : class, new() + { + return default(TResult); + } + public TResult Max(Func expression) + { + return default(TResult); + } + public Byte[] Max(Func expression) + { + return null; + } + public string Max(Func expression) + { + return default(string); + } + + public string Min(Func expression) + { + return default(string); + } + public TResult Min(Func expression) + { + return default(TResult); + } + public Byte[] Min(Func expression) + { + return null; + } + public string Sum(Func expression) + { + return default(string); + } + public TResult Sum(Func expression) + { + return default(TResult); + } + public new Subqueryable Take(int takeNum) + { + return this; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExternalServiceInterface/ICacheService.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExternalServiceInterface/ICacheService.cs new file mode 100644 index 000000000..753a8a801 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExternalServiceInterface/ICacheService.cs @@ -0,0 +1,13 @@ +namespace SqlSugar +{ + public interface ICacheService + { + void Add(string key, V value); + void Add(string key, V value, int cacheDurationInSeconds); + bool ContainsKey(string key); + V Get(string key); + IEnumerable GetAllKey(); + void Remove(string key); + V GetOrCreate(string cacheKey, Func create, int cacheDurationInSeconds = int.MaxValue); + } +} \ No newline at end of file diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExternalServiceInterface/IRazorService.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExternalServiceInterface/IRazorService.cs new file mode 100644 index 000000000..7f79bbad5 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExternalServiceInterface/IRazorService.cs @@ -0,0 +1,7 @@ +namespace SqlSugar +{ + public interface IRazorService + { + List> GetClassStringList(string razorTemplate, List model); + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExternalServiceInterface/ISerializeService.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExternalServiceInterface/ISerializeService.cs new file mode 100644 index 000000000..c86448bf8 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExternalServiceInterface/ISerializeService.cs @@ -0,0 +1,9 @@ +namespace SqlSugar +{ + public interface ISerializeService + { + string SerializeObject(object value); + string SugarSerializeObject(object value); + T DeserializeObject(string value); + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/ExternalServiceInterface/ISplitTableService.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExternalServiceInterface/ISplitTableService.cs new file mode 100644 index 000000000..1cfcd9e48 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/ExternalServiceInterface/ISplitTableService.cs @@ -0,0 +1,11 @@ +namespace SqlSugar +{ + public interface ISplitTableService + { + List GetAllTables(ISqlSugarClient db, EntityInfo EntityInfo, List tableInfos); + string GetTableName(ISqlSugarClient db, EntityInfo EntityInfo); + string GetTableName(ISqlSugarClient db, EntityInfo EntityInfo, SplitType type); + string GetTableName(ISqlSugarClient db, EntityInfo entityInfo, SplitType splitType, object fieldValue); + object GetFieldValue(ISqlSugarClient db, EntityInfo entityInfo, SplitType splitType, object entityValue); + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Infrastructure/ContextMethods.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Infrastructure/ContextMethods.cs new file mode 100644 index 000000000..4471ade7d --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Infrastructure/ContextMethods.cs @@ -0,0 +1,1282 @@ +using Newtonsoft.Json.Linq; + +using System.Collections; +using System.Data; +using System.Data.Common; +using System.Dynamic; +using System.Linq.Expressions; +using System.Reflection; +using System.Text.RegularExpressions; +namespace SqlSugar +{ + public partial class ContextMethods : IContextMethods + { + public SqlSugarProvider Context { get; set; } + public QueryBuilder QueryBuilder { get; set; } + + #region DataReader + public List DataReaderToValueTupleType(IDataReader reader) + { + var result = new List(); + + // Get the property names and types dynamically + var propertyNames = Enumerable.Range(0, reader.FieldCount) + .Select(reader.GetName).ToList(); + var propertyTypes = Enumerable.Range(0, reader.FieldCount) + .Select(reader.GetFieldType).ToList(); + + using (reader) + { + while (reader.Read()) + { + // Create a new instance of the tuple type using the property types + var tupleType = typeof(T); + var tupleInstance = Activator.CreateInstance(tupleType); + + // Set the property values dynamically + for (int i = 0; i < reader.FieldCount; i++) + { + var propertyName = propertyNames[i]; + var propertyValue = reader.GetValue(i); + var propertyType = propertyTypes[i]; + + var propertyInfo = tupleType.GetFields()[i]; + propertyInfo.SetValue(tupleInstance, UtilMethods.ChangeType2(propertyValue, propertyInfo.FieldType)); + } + + // Add the tuple instance to the result list + result.Add((T)tupleInstance); + } + } + return result; + } + public async Task> DataReaderToValueTupleTypeAsync(IDataReader reader) + { + var result = new List(); + + // Get the property names and types dynamically + var propertyNames = Enumerable.Range(0, reader.FieldCount) + .Select(reader.GetName).ToList(); + var propertyTypes = Enumerable.Range(0, reader.FieldCount) + .Select(reader.GetFieldType).ToList(); + + using (reader) + { + while (await ((DbDataReader)reader).ReadAsync().ConfigureAwait(false)) + { + // Create a new instance of the tuple type using the property types + var tupleType = typeof(T); + var tupleInstance = Activator.CreateInstance(tupleType); + + // Set the property values dynamically + for (int i = 0; i < reader.FieldCount; i++) + { + var propertyName = propertyNames[i]; + var propertyValue = reader.GetValue(i); + var propertyType = propertyTypes[i]; + + var propertyInfo = tupleType.GetFields()[i]; + propertyInfo.SetValue(tupleInstance, Convert.ChangeType(propertyValue, propertyType)); + } + + // Add the tuple instance to the result list + result.Add((T)tupleInstance); + } + } + return result; + } + + + /// + ///DataReader to Dynamic + /// + /// + /// + public ExpandoObject DataReaderToExpandoObject(IDataReader reader) + { + ExpandoObject result = new ExpandoObject(); + var dic = ((IDictionary)result); + for (int i = 0; i < reader.FieldCount; i++) + { + try + { + var addItem = reader.GetValue(i); + if (addItem == DBNull.Value) + addItem = null; + dic.Add(reader.GetName(i), addItem); + } + catch + { + dic.Add(reader.GetName(i), null); + } + } + return result; + } + + /// + ///DataReader to Dynamic List + /// + /// + /// + public List DataReaderToExpandoObjectList(IDataReader reader) + { + using (reader) + { + List result = new List(); + if (reader?.IsClosed == false) + { + while (reader.Read()) + { + result.Add(DataReaderToExpandoObject(reader)); + } + } + return result; + } + } + /// + ///DataReader to Dynamic List + /// + /// + /// + public async Task> DataReaderToExpandoObjectListAsync(IDataReader reader) + { + using (reader) + { + List result = new List(); + if (reader?.IsClosed == false) + { + while (await ((DbDataReader)reader).ReadAsync().ConfigureAwait(false)) + { + result.Add(DataReaderToExpandoObject(reader)); + } + } + return result; + } + } + + + /// + ///DataReader to Dynamic List + /// + /// + /// + public List DataReaderToExpandoObjectListNoUsing(IDataReader reader) + { + List result = new List(); + if (reader?.IsClosed == false) + { + while (reader.Read()) + { + result.Add(DataReaderToExpandoObject(reader)); + } + } + return result; + } + + /// + ///DataReader to Dynamic List + /// + /// + /// + public async Task> DataReaderToExpandoObjectListAsyncNoUsing(IDataReader reader) + { + List result = new List(); + if (reader?.IsClosed == false) + { + while (await ((DbDataReader)reader).ReadAsync().ConfigureAwait(false)) + { + result.Add(DataReaderToExpandoObject(reader)); + } + } + return result; + } + + + /// + ///DataReader to DataReaderToDictionary + /// + /// + /// + public Dictionary DataReaderToDictionary(IDataReader reader) + { + Dictionary result = new Dictionary(); + for (int i = 0; i < reader.FieldCount; i++) + { + try + { + var addItem = reader.GetValue(i); + if (addItem == DBNull.Value) + addItem = null; + result.Add(reader.GetName(i), addItem); + } + catch + { + result.Add(reader.GetName(i), null); + } + } + return result; + } + + /// + ///DataReader to DataReaderToDictionary + /// + /// + /// + /// + public Dictionary DataReaderToDictionary(IDataReader reader, Type type) + { + Dictionary result = new Dictionary(); + for (int i = 0; i < reader.FieldCount; i++) + { + string name = reader.GetName(i); + try + { + name = this.Context.EntityMaintenance.GetPropertyName(name, type); + var addItem = reader.GetValue(i); + if (addItem == DBNull.Value) + addItem = null; + result.Add(name, addItem); + } + catch + { + if (!result.ContainsKey(name)) + { + result.Add(name, null); + } + } + } + return result; + } + + /// + /// DataReaderToList + /// + /// + /// + /// + public List DataReaderToList(IDataReader reader) + { + using (reader) + { + var tType = typeof(T); + var classProperties = tType.GetProperties() + .Where(p => p.GetIndexParameters().Length == 0).ToList(); + var reval = new List(); + if (reader?.IsClosed == false) + { + while (reader.Read()) + { + Dictionary result = DataReaderToList(reader, tType, classProperties, reval); + var stringValue = SerializeObject(result); + reval.Add((T)DeserializeObject(stringValue)); + SetAppendColumns(reader); + } + } + return reval; + } + } + + + public List DataReaderToSelectJsonList(IDataReader dataReader) + { + List result = new List(); + using (dataReader) + { + while (dataReader.Read()) + { + var value = dataReader.GetValue(0); + if (value == null || value == DBNull.Value) + { + result.Add(default(T)); + } + else + { + result.Add(Context.Utilities.DeserializeObject(value.ToString())); + } + } + } + + return result; + } + + public List DataReaderToSelectArrayList(IDataReader dataReader) + { + List result = new List(); + using (dataReader) + { + while (dataReader.Read()) + { + var value = dataReader.GetValue(0); + if (value == null || value == DBNull.Value) + { + result.Add(default(T)); + } + else + { + result.Add((T)value); + } + } + } + + return result; + } + /// + /// DataReaderToList + /// + /// + /// + /// + public List DataReaderToListNoUsing(IDataReader reader) + { + var tType = typeof(T); + var classProperties = tType.GetProperties() + .Where(p => p.GetIndexParameters().Length == 0).ToList(); + var reval = new List(); + if (reader?.IsClosed == false) + { + while (reader.Read()) + { + Dictionary result = DataReaderToList(reader, tType, classProperties, reval); + var stringValue = SerializeObject(result); + reval.Add((T)DeserializeObject(stringValue)); + SetAppendColumns(reader); + } + } + return reval; + } + /// + /// DataReaderToList + /// + /// + /// + /// + public async Task> DataReaderToListAsync(IDataReader reader) + { + using (reader) + { + var tType = typeof(T); + var classProperties = tType.GetProperties().Where(p => p.GetIndexParameters().Length == 0).ToList(); + var reval = new List(); + if (reader?.IsClosed == false) + { + while (await ((DbDataReader)reader).ReadAsync().ConfigureAwait(false)) + { + Dictionary result = DataReaderToList(reader, tType, classProperties, reval); + var stringValue = SerializeObject(result); + reval.Add((T)DeserializeObject(stringValue)); + SetAppendColumns(reader); + } + } + return reval; + } + } + + + public async Task> DataReaderToSelectJsonListAsync(IDataReader dataReader) + { + List result = new List(); + using (dataReader) + { + while (await ((DbDataReader)dataReader).ReadAsync().ConfigureAwait(false)) + { + var value = dataReader.GetValue(0); + if (value == null || value == DBNull.Value) + { + result.Add(default(T)); + } + else + { + result.Add(Context.Utilities.DeserializeObject(value.ToString())); + } + } + } + return result; + } + public async Task> DataReaderToSelectArrayListAsync(IDataReader dataReader) + { + List result = new List(); + using (dataReader) + { + while (await ((DbDataReader)dataReader).ReadAsync().ConfigureAwait(false)) + { + var value = dataReader.GetValue(0); + if (value == null || value == DBNull.Value) + { + result.Add(default(T)); + } + else + { + result.Add((T)value); + } + } + } + return result; + } + + /// + /// DataReaderToList + /// + /// + /// + /// + public async Task> DataReaderToListAsyncNoUsing(IDataReader reader) + { + var tType = typeof(T); + var classProperties = tType.GetProperties().Where(p => p.GetIndexParameters().Length == 0).ToList(); + var reval = new List(); + if (reader?.IsClosed == false) + { + while (await ((DbDataReader)reader).ReadAsync().ConfigureAwait(false)) + { + Dictionary result = DataReaderToList(reader, tType, classProperties, reval); + var stringValue = SerializeObject(result); + reval.Add((T)DeserializeObject(stringValue)); + } + } + return reval; + } + + private Dictionary DataReaderToList(IDataReader reader, Type tType, List classProperties, List reval) + { + var readerValues = DataReaderToDictionary(reader, tType); + var mappingKeys = this.QueryBuilder?.MappingKeys; + var result = new Dictionary(); + if (UtilMethods.IsTuple(tType, classProperties)) + { + var index = 0; + foreach (var item in classProperties) + { + result.Add("Item" + (index + 1), reader.GetValue(index)); + index++; + } + return result; + } + foreach (var item in classProperties) + { + var name = item.Name; + var typeName = tType.Name; + if (item.PropertyType.IsClass()) + { + if (item.PropertyType.FullName == "Newtonsoft.Json.Linq.JObject") + { + result.Add(name, DeserializeObject(readerValues[item.Name].ToString())); + } + else if (IsJsonItem(readerValues, name)) + { + result.Add(name, DeserializeObject>(readerValues.First(it => it.Key.EqualCase(name)).Value.ObjToString())); + } + else if (IsJsonList(readerValues, item)) + { + var json = readerValues.First(y => y.Key.EqualCase(item.Name)).Value.ToString(); + result.Add(name, DeserializeObject>>(json)); + } + else if (IsBytes(readerValues, item)) + { + result.Add(name, (byte[])readerValues[item.Name.ToLower()]); + } + else if (StaticConfig.EnableAot && tType == typeof(DbColumnInfo) && item.PropertyType == typeof(object)) + { + //No add + } + else if (item.PropertyType == typeof(object)) + { + result.Add(name, readerValues[item.Name.ToLower()]); + } + else if (IsArrayItem(readerValues, item)) + { + var json = readerValues.First(y => y.Key.EqualCase(item.Name)).Value + ""; + if (json.StartsWith("[{")) + { + result.Add(name, DeserializeObject(json)); + } + else + { + result.Add(name, DeserializeObject(json)); + } + } + else if (item.PropertyType?.IsArray == true && readerValues?.Any(y => y.Key.EqualCase(item.Name)) == true && readerValues?.FirstOrDefault(y => y.Key.EqualCase(item.Name)).Value is Array value) + { + result.Add(name, value); + } + else if (StaticConfig.EnableAot && item.PropertyType == typeof(Type)) + { + //No Add + } + else + { + List ignorePropertyNames = null; + if (this.QueryBuilder?.SelectNewIgnoreColumns?.Count > 0) + { + var ignoreColumns = this.QueryBuilder.SelectNewIgnoreColumns.Where(it => it.Value == item.PropertyType.Name).ToList(); + if (ignoreColumns.Count != 0) + { + ignorePropertyNames = ignoreColumns.Select(it => it.Key).ToList(); + } + } + result.Add(name, DataReaderToDynamicList_Part(readerValues, item, reval, mappingKeys, ignorePropertyNames)); + } + } + else + { + if (readerValues.Any(it => it.Key.Equals(name, StringComparison.CurrentCultureIgnoreCase))) + { + var addValue = readerValues.TryGetValue(name, out object? value) ? value : readerValues.First(it => it.Key.Equals(name, StringComparison.CurrentCultureIgnoreCase)).Value; + if (addValue != null && this.QueryBuilder?.QueryableFormats?.Any(it => it.PropertyName == name) == true) + { + var valueFomatInfo = this.QueryBuilder?.QueryableFormats?.First(it => it.PropertyName == name); + addValue = UtilMethods.GetFormatValue(addValue, valueFomatInfo); + + } + var type = UtilMethods.GetUnderType(item.PropertyType); + if (addValue == DBNull.Value || addValue == null) + { + if (item.PropertyType.IsIn(UtilConstants.IntType, UtilConstants.LongType, UtilConstants.DecType, UtilConstants.DobType, UtilConstants.ByteType)) + { + addValue = 0; + } + else if (item.PropertyType == UtilConstants.GuidType) + { + addValue = Guid.Empty; + } + else if (item.PropertyType == UtilConstants.DateType) + { + addValue = DateTime.MinValue; + } + else if (item.PropertyType == UtilConstants.StringType) + { + addValue = null; + } + else + { + addValue = null; + } + } + else if (type == UtilConstants.IntType) + { + addValue = Convert.ToInt32(addValue); + } + else if (type == UtilConstants.LongType) + { + addValue = Convert.ToInt64(addValue); + } + else if (type.IsEnum() && addValue is decimal) + { + addValue = Convert.ToInt64(addValue); + } + else if (type.FullName == "System.DateOnly") + { + addValue = Convert.ToDateTime(addValue).ToString("yyyy-MM-dd"); + } + result.Add(name, addValue); + } + } + } + + return result; + } + + + private void SetAppendColumns(IDataReader dataReader) + { + if (QueryBuilder?.AppendColumns != null && QueryBuilder.AppendColumns.Count != 0) + { + if (QueryBuilder.AppendValues == null) + QueryBuilder.AppendValues = new List>(); + List addItems = new List(); + foreach (var item in QueryBuilder.AppendColumns) + { + var vi = dataReader.GetOrdinal(item.AsName); + var value = dataReader.GetValue(vi); + addItems.Add(new QueryableAppendColumn() + { + Name = item.Name, + AsName = item.AsName, + Value = value + }); + } + QueryBuilder.AppendValues.Add(addItems); + } + if (QueryBuilder?.AppendNavInfo != null) + { + var navResult = new AppendNavResult(); + foreach (var item in QueryBuilder?.AppendNavInfo.AppendProperties) + { + var vi = dataReader.GetOrdinal("SugarNav_" + item.Key); + var value = dataReader.GetValue(vi); + navResult.result.Add("SugarNav_" + item.Key, value); + } + QueryBuilder?.AppendNavInfo.Result.Add(navResult); + } + } + private static bool IsBytes(Dictionary readerValues, PropertyInfo item) + { + return item.PropertyType == UtilConstants.ByteArrayType && + readerValues.ContainsKey(item.Name.ToLower()) && + (readerValues[item.Name.ToLower()] == null || + readerValues[item.Name.ToLower()].GetType() == UtilConstants.ByteArrayType); + } + + private static bool IsJsonItem(Dictionary readerValuesOld, string name) + { + Dictionary readerValues = new Dictionary(); + if (readerValuesOld.Any(it => it.Key.EqualCase(name))) + { + var data = readerValuesOld.First(it => it.Key.EqualCase(name)); + readerValues.Add(data.Key, data.Value); + } + return readerValues?.Count == 1 && + readerValues.First().Key == name && + readerValues.First().Value != null && + readerValues.First().Value.GetType() == UtilConstants.StringType && + Regex.IsMatch(readerValues.First().Value.ObjToString(), @"^\{.+\}$"); + } + + + private static bool IsArrayItem(Dictionary readerValues, PropertyInfo item) + { + var isArray = item.PropertyType.IsArray && readerValues.Any(y => y.Key.EqualCase(item.Name)) && readerValues.FirstOrDefault(y => y.Key.EqualCase(item.Name)).Value is string; + var isListItem = item.PropertyType.FullName.IsCollectionsList() && + item.PropertyType.GenericTypeArguments.Length == 1 && + item.PropertyType.GenericTypeArguments.First().IsClass() == false && readerValues.FirstOrDefault(y => y.Key.EqualCase(item.Name)).Value is string; + return isArray || isListItem; + } + + private static bool IsJsonList(Dictionary readerValues, PropertyInfo item) + { + return item.PropertyType.FullName.IsCollectionsList() && + readerValues.Any(y => y.Key.EqualCase(item.Name)) && + readerValues.First(y => y.Key.EqualCase(item.Name)).Value != null && + readerValues.First(y => y.Key.EqualCase(item.Name)).Value.GetType() == UtilConstants.StringType && + Regex.IsMatch(readerValues.First(y => y.Key.EqualCase(item.Name)).Value.ToString(), @"^\[{.+\}]$"); + } + + private Dictionary DataReaderToDynamicList_Part(Dictionary readerValues, PropertyInfo item, List reval, Dictionary mappingKeys = null, List ignoreColumns = null) + { + Dictionary result = new Dictionary(); + var type = item.PropertyType; + if (UtilConstants.SugarType == type) + { + return result; + } + if (type.FullName.IsCollectionsList()) + { + return null; + } + if (type == typeof(string[])) + { + return null; + } + var classProperties = type.GetProperties().ToList(); + if (type.Name.StartsWith("Dictionary`")) + { + return null; + } + var columns = this.Context.EntityMaintenance.GetEntityInfo(type).Columns; + foreach (var prop in classProperties) + { + var name = prop.Name; + var typeName = type.Name; + if (prop.PropertyType.IsClass()) + { + if (ignoreColumns?.Contains(name) == true) + { + continue; + } + var suagrColumn = prop.GetCustomAttribute(); + if (suagrColumn?.IsJson == true) + { + + if (mappingKeys != null && mappingKeys.TryGetValue(item.Name, out string? key)) + { + Json(readerValues, result, name, typeName, key, item); + } + else + { + Json(readerValues, result, name, typeName, item); + } + } + else if (columns.Any(it => it.IsJson)) + { + var column = columns.FirstOrDefault(it => it.PropertyName == name); + if (column?.IsJson == true) + { + Json(readerValues, result, name, typeName); + } + } + else + { + result.Add(name, DataReaderToDynamicList_Part(readerValues, prop, reval)); + } + } + else + { + var key = typeName + "." + name; + var info = readerValues.Select(it => it.Key).FirstOrDefault(it => it.Equals(key, StringComparison.CurrentCultureIgnoreCase)); + if (info == null) + { + key = item.Name + "." + name; + info = readerValues.Select(it => it.Key).FirstOrDefault(it => it.Equals(key, StringComparison.CurrentCultureIgnoreCase)); + if (info == null) + { + info = readerValues.Select(it => it.Key).FirstOrDefault(it => it.ToLower().EndsWith("." + key.ToLower())); + } + } + var oldInfo = info; + if (mappingKeys != null && mappingKeys.TryGetValue(item.Name, out string? value)) + { + key = value + "." + typeName + "." + name; + info = readerValues.Select(it => it.Key).FirstOrDefault(it => it.Equals(key, StringComparison.CurrentCultureIgnoreCase)); + if (info == null) + { + var key2 = item.Name + "." + name; + info = readerValues.Select(it => it.Key).FirstOrDefault(it => it.Equals(key2, StringComparison.CurrentCultureIgnoreCase)); + } + } + else if (mappingKeys?.ContainsKey("Single_" + name) == true) + { + key = mappingKeys["Single_" + name]; + info = readerValues.Select(it => it.Key).FirstOrDefault(it => it.Equals(key, StringComparison.CurrentCultureIgnoreCase)); + } + if (info == null && oldInfo != null) + { + info = oldInfo; + } + if (info != null) + { + var addItem = readerValues[info]; + if (addItem == DBNull.Value) + addItem = null; + var underType = UtilMethods.GetUnderType(prop.PropertyType); + if (prop.PropertyType == UtilConstants.IntType) + { + addItem = addItem.ObjToInt(); + } + else if (prop.PropertyType == UtilConstants.ShortType) + { + addItem = Convert.ToInt16(addItem); + } + else if (prop.PropertyType == UtilConstants.LongType) + { + addItem = Convert.ToInt64(addItem); + } + else if (UtilMethods.GetUnderType(prop.PropertyType) == UtilConstants.IntType && addItem != null) + { + addItem = addItem.ObjToInt(); + } + else if (addItem != null && underType?.FullName == "System.DateOnly") + { + addItem = Convert.ToDateTime(addItem).ToString("yyyy-MM-dd"); + } + else if (UtilMethods.GetUnderType(prop.PropertyType).IsEnum() && addItem is decimal) + { + if (prop.PropertyType.IsEnum() == false && addItem == null) + { + //Future + } + else + { + addItem = Convert.ToInt64(addItem); + } + } + result.Add(name, addItem); + } + } + } + return result; + } + + private void Json(Dictionary readerValues, Dictionary result, string name, string typeName, string shortName = null, PropertyInfo item = null) + { + var key = (typeName + "." + name).ToLower(); + if (readerValues.Any(it => it.Key.EqualCase(key))) + { + var jsonString = readerValues.First(it => it.Key.EqualCase(key)).Value; + AddJson(result, name, jsonString); + } + else + { + key = (shortName + "." + typeName + "." + name).ToLower(); + if (readerValues.Any(it => it.Key.EqualCase(key))) + { + var jsonString = readerValues.First(it => it.Key.EqualCase(key)).Value; + AddJson(result, name, jsonString); + } + else if (item != null) + { + if (readerValues.Any(it => it.Key.EqualCase(item.Name + "." + name))) + { + var jsonString = readerValues.First(it => it.Key.EqualCase(item.Name + "." + name)).Value; + AddJson(result, name, jsonString); + + } + } + } + } + private void Json(Dictionary readerValues, Dictionary result, string name, string typeName, PropertyInfo item) + { + var key = (typeName + "." + name).ToLower(); + if (readerValues.Any(it => it.Key.EqualCase(key))) + { + var jsonString = readerValues.First(it => it.Key.EqualCase(key)).Value; + AddJson(result, name, jsonString); + } + else + { + key = (item.Name + "." + name).ToLower(); + if (readerValues.Any(it => it.Key.EqualCase(key))) + { + var jsonString = readerValues.First(it => it.Key.EqualCase(key)).Value; + AddJson(result, name, jsonString); + } + } + } + + private void AddJson(Dictionary result, string name, object jsonString) + { + if (jsonString != null) + { + if (jsonString.ToString().First() == '{' && jsonString.ToString().Last() == '}') + { + result.Add(name, this.DeserializeObject>(jsonString + "")); + } + else if (jsonString.ToString().Replace(" ", "") != "[]" && !jsonString.ToString().Contains('{') && !jsonString.ToString().Contains('}')) + { + result.Add(name, this.DeserializeObject(jsonString + "")); + } + else + { + result.Add(name, this.DeserializeObject>>(jsonString + "")); + + } + } + } + #endregion + + #region Serialize + /// + /// Serialize Object + /// + /// + /// + public string SerializeObject(object value) + { + DependencyManagement.TryJsonNet(); + return Context.CurrentConnectionConfig.ConfigureExternalServices.SerializeService.SerializeObject(value); + } + public string SerializeObject(object value, Type type) + { + DependencyManagement.TryJsonNet(); + if (type.IsAnonymousType()) + { + return Context.CurrentConnectionConfig.ConfigureExternalServices.SerializeService.SerializeObject(value); + } + else + { + var isSugar = this.Context.EntityMaintenance.GetEntityInfo(type).Columns.Any(it => it.NoSerialize || it.SerializeDateTimeFormat.HasValue()); + if (isSugar) + { + return Context.CurrentConnectionConfig.ConfigureExternalServices.SerializeService.SugarSerializeObject(value); + } + else + { + return Context.CurrentConnectionConfig.ConfigureExternalServices.SerializeService.SerializeObject(value); + } + } + } + + + /// + /// Serialize Object + /// + /// + /// + public T DeserializeObject(string value) + { + DependencyManagement.TryJsonNet(); + return Context.CurrentConnectionConfig.ConfigureExternalServices.SerializeService.DeserializeObject(value); + } + #endregion + + #region Copy Object + /// + /// Copy new Object + /// + /// + /// + /// + public T TranslateCopy(T sourceObject) + { + if (sourceObject == null) return default(T); + else + { + var jsonString = SerializeObject(sourceObject); + return DeserializeObject(jsonString); + } + } + public SqlSugarProvider CopyContext(bool isCopyEvents = false) + { + var newClient = new SqlSugarProvider(this.TranslateCopy(Context.CurrentConnectionConfig)); + newClient.CurrentConnectionConfig.ConfigureExternalServices = Context.CurrentConnectionConfig.ConfigureExternalServices; + newClient.MappingColumns = this.TranslateCopy(Context.MappingColumns); + newClient.MappingTables = this.TranslateCopy(Context.MappingTables); + newClient.IgnoreColumns = this.TranslateCopy(Context.IgnoreColumns); + newClient.IgnoreInsertColumns = this.TranslateCopy(Context.IgnoreInsertColumns); + if (isCopyEvents) + { + newClient.QueryFilter = Context.QueryFilter; + newClient.CurrentConnectionConfig.AopEvents = Context.CurrentConnectionConfig.AopEvents; + } + return newClient; + } + #endregion + + #region DataTable + public DataTable DictionaryListToDataTable(List> list) + { + DataTable result = new DataTable(); + if (list.Count == 0) + return result; + + var columnNames = list.First(); + foreach (var item in columnNames) + { + result.Columns.Add(item.Key, item.Value == null ? typeof(object) : item.Value.GetType()); + } + foreach (var item in list) + { + var row = result.NewRow(); + foreach (var key in item.Keys) + { + if (item[key] == null) + { + row[key] = DBNull.Value; + } + else + { + row[key] = item[key]; + } + } + + result.Rows.Add(row); + } + + return result; + } + public dynamic DataTableToDynamic(DataTable table) + { + List> deserializeObject = new List>(); + Dictionary childRow; + foreach (DataRow row in table.Rows) + { + childRow = new Dictionary(); + foreach (DataColumn col in table.Columns) + { + var addItem = row[col]; + if (addItem == DBNull.Value) + addItem = null; + childRow.Add(col.ColumnName, addItem); + } + deserializeObject.Add(childRow); + } + return this.DeserializeObject(this.SerializeObject(deserializeObject)); + + } + public List DataTableToList(DataTable table) + { + List> deserializeObject = new List>(); + Dictionary childRow; + foreach (DataRow row in table.Rows) + { + childRow = new Dictionary(); + foreach (DataColumn col in table.Columns) + { + var addItem = row[col]; + if (addItem == DBNull.Value) + addItem = null; + childRow.Add(col.ColumnName, addItem); + } + deserializeObject.Add(childRow); + } + return this.DeserializeObject>(this.SerializeObject(deserializeObject)); + } + public DataTable ListToDataTable(List list) + { + DataTable result = new DataTable(); + if (list?.Count > 0) + { + PropertyInfo[] propertys = list[0].GetType().GetProperties(); + foreach (PropertyInfo pi in propertys) + { + //获取类型 + Type colType = pi.PropertyType; + //当类型为Nullable<>时 + if ((colType.IsGenericType) && (colType.GetGenericTypeDefinition() == typeof(Nullable<>))) + { + colType = colType.GetGenericArguments()[0]; + } + result.Columns.Add(pi.Name, colType); + } + for (int i = 0; i < list.Count; i++) + { + ArrayList tempList = new ArrayList(); + foreach (PropertyInfo pi in propertys) + { + object obj = pi.GetValue(list[i], null); + tempList.Add(obj); + } + object[] array = tempList.ToArray(); + result.LoadDataRow(array, true); + } + } + return result; + } + + public DataTable ListToDataTableWithAttr(List list) + { + var entityInfo = this.Context.EntityMaintenance.GetEntityInfo(); + DataTable result = new DataTable(); + result.TableName = entityInfo.DbTableName; + if (list?.Count > 0) + { + var colimnInfos = entityInfo.Columns.Where(it => it.IsIgnore == false); + foreach (var pi in colimnInfos) + { + //获取类型 + Type colType = pi.PropertyInfo.PropertyType; + //当类型为Nullable<>时 + if ((colType.IsGenericType) && (colType.GetGenericTypeDefinition() == typeof(Nullable<>))) + { + colType = colType.GetGenericArguments()[0]; + } + result.Columns.Add(pi.DbColumnName, colType); + } + for (int i = 0; i < list.Count; i++) + { + ArrayList tempList = new ArrayList(); + foreach (var pi in colimnInfos) + { + object obj = pi.PropertyInfo.GetValue(list[i], null); + tempList.Add(obj); + } + object[] array = tempList.ToArray(); + result.LoadDataRow(array, true); + } + } + return result; + } + public Dictionary DataTableToDictionary(DataTable table) + { + return table.Rows.Cast().ToDictionary(x => x[0].ToString(), x => x[1]); + } + + public List> DataTableToDictionaryList(DataTable dt) + { + List> result = new List>(); + if (dt?.Rows.Count > 0) + { + foreach (DataRow dr in dt.Rows) + { + Dictionary dic = new Dictionary(); + for (int i = 0; i < dr.Table.Columns.Count; i++) + { + var value = dr[dr.Table.Columns[i].ColumnName]; + if (value == DBNull.Value) + { + value = null; + } + dic.Add(dr.Table.Columns[i].ColumnName.ToString(), value); + } + result.Add(dic); + } + } + return result; + } + + #endregion + + #region Cache + public ICacheService GetReflectionInoCacheInstance() + { + return Context.CurrentConnectionConfig.ConfigureExternalServices.ReflectionInoCacheService; + } + + public void RemoveCacheAll() + { + ReflectionInoHelper.RemoveAllCache(); + InstanceFactory.RemoveCache(); + } + + public void RemoveCacheAll() + { + ReflectionInoCore.GetInstance().RemoveAllCache(); + } + + public void RemoveCache(string key) + { + ReflectionInoCore.GetInstance().Remove(key); + } + public void RemoveCacheByLikeKey(string key) + { + foreach (var item in ReflectionInoCore.GetInstance().GetAllKey()) + { + if (item != null && key != null && item.Contains(key)) + { + ReflectionInoCore.GetInstance().Remove(item); + } + } + } + #endregion + + #region Page Each + public void PageEach(IEnumerable pageItems, int pageSize, Action> action) + { + if (pageItems?.Any() == true) + { + int totalRecord = pageItems.Count(); + int pageCount = (totalRecord + pageSize - 1) / pageSize; + for (int i = 1; i <= pageCount; i++) + { + var list = pageItems.Skip((i - 1) * pageSize).Take(pageSize).ToList(); + action(list); + } + } + } + + public async Task PageEachAsync(IEnumerable pageItems, int pageSize, Func, Task> action) + { + if (pageItems?.Any() == true) + { + int totalRecord = pageItems.Count(); + int pageCount = (totalRecord + pageSize - 1) / pageSize; + for (int i = 1; i <= pageCount; i++) + { + var list = pageItems.Skip((i - 1) * pageSize).Take(pageSize).ToList(); + await action(list).ConfigureAwait(false); + } + } + } + public async Task PageEachAsync(IEnumerable pageItems, int pageSize, Func, Task> action) + { + if (pageItems?.Any() == true) + { + int totalRecord = pageItems.Count(); + int pageCount = (totalRecord + pageSize - 1) / pageSize; + for (int i = 1; i <= pageCount; i++) + { + var list = pageItems.Skip((i - 1) * pageSize).Take(pageSize).ToList(); + await action(list).ConfigureAwait(false); + } + } + } + + #endregion + + #region Conditional + public KeyValuePair ConditionalModelsToSql(List conditionalModels, int beginIndex = 0) + { + var sqlBuilder = InstanceFactory.GetSqlBuilderWithContext(this.Context); + var result = sqlBuilder.ConditionalModelToSql(conditionalModels, beginIndex); + return result; + } + public List JsonToConditionalModels(string json) + { + List conditionalModels = new List(); + var jarray = this.Context.Utilities.DeserializeObject(json); + foreach (var item in jarray) + { + + if (item.Any()) + { + if (item.ToString().Contains("ConditionalList")) + { + IConditionalModel model = new ConditionalTree() + { + ConditionalList = GetConditionalList(item) + }; + conditionalModels.Add(model); + } + else + { + var typeValue = item["ConditionalType"].Value(); + + ConditionalModel conditionalModel = new ConditionalModel() + { + // ConditionalType = (ConditionalType)Convert.ToInt32(), + FieldName = item["FieldName"] + "", + CSharpTypeName = item["CSharpTypeName"].ObjToString().IsNullOrEmpty() ? null : item["CSharpTypeName"].ObjToString(), + FieldValue = item["FieldValue"].Value() == null ? null : item["FieldValue"].ToString() + }; + if (typeValue.IsInt()) + { + conditionalModel.ConditionalType = (ConditionalType)Convert.ToInt32(typeValue); + } + else + { + conditionalModel.ConditionalType = (ConditionalType)Enum.Parse(typeof(ConditionalType), typeValue.ObjToString()); + } + conditionalModels.Add(conditionalModel); + } + } + } + return conditionalModels; + } + private static List> GetConditionalList(JToken item) + { + List> result = new List>(); + var values = item.Values().First(); + foreach (var jToken in values) + { + WhereType type = (WhereType)Convert.ToInt32(jToken["Key"].Value()); + IConditionalModel conditionalModel = null; + var value = jToken["Value"]; + if (value.ToString().Contains("ConditionalList")) + { + conditionalModel = new ConditionalTree() + { + ConditionalList = GetConditionalList(value) + }; + } + else + { + conditionalModel = new ConditionalModel() + { + ConditionalType = GetConditionalType(value), + FieldName = value["FieldName"] + "", + CSharpTypeName = value["CSharpTypeName"].ObjToString().IsNullOrEmpty() ? null : value["CSharpTypeName"].ObjToString(), + FieldValue = value["FieldValue"].Value() == null ? null : value["FieldValue"].ToString() + }; + } + result.Add(new KeyValuePair(type, conditionalModel)); + } + return result; + } + private static ConditionalType GetConditionalType(JToken value) + { + if (value["ConditionalType"].Type == JTokenType.String) + { + var stringValue = value["ConditionalType"].Value(); + if (!stringValue.IsInt()) + { + return (ConditionalType)Enum.Parse(typeof(ConditionalType), stringValue); + } + } + return (ConditionalType)Convert.ToInt32(value["ConditionalType"].Value()); + } + #endregion + + #region Tree + public List ToTree(List list, Expression>> childListExpression, Expression> parentIdExpression, Expression> pkExpression, object rootValue) + { + var pk = ExpressionTool.GetMemberName(pkExpression); + return (this.Context.Queryable() as QueryableProvider).GetTreeRoot(childListExpression, parentIdExpression, pk, list, rootValue); + } + #endregion + + #region Other + public string EscapeLikeValue(string value, char wildcard = '%') + { + return UtilMethods.EscapeLikeValue(this.Context, value, wildcard); + } + #endregion + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Infrastructure/DependencyManagement.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Infrastructure/DependencyManagement.cs new file mode 100644 index 000000000..050029e88 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Infrastructure/DependencyManagement.cs @@ -0,0 +1,166 @@ +namespace SqlSugar +{ + internal static class DependencyManagement + { + private static bool IsTryJsonNet = false; + private static bool IsTryMySqlData = false; + private static bool IsTrySqlite = false; + private static bool IsTryOracle = false; + private static bool IsTryPgSql = false; + private static bool IsTryDm = false; + private static bool IsTryKd = false; + private static bool IsTryOscar = false; + public static void TryJsonNet() + { + if (!IsTryJsonNet) + { + try + { + new SerializeService().SerializeObject(new { }); + IsTryJsonNet = true; + } + catch + { + var message = ErrorMessage.GetThrowMessage( + " SqlSugar Some functions are used in newtonsoft ,Nuget references Newtonsoft.Json 9.0.0.1 + .", + " SqlSugar 部分功能用到Newtonsoft.Json.dll,需要在Nuget上安装 Newtonsoft.Json 9.0.0.1及以上版本,如果有版本兼容问题请先删除原有引用"); + throw new Exception(message); + } + } + } + + public static void TryMySqlData() + { + if (!IsTryMySqlData) + { + try + { + MySqlProvider db = new MySqlProvider(); + var conn = db.GetAdapter(); + IsTryMySqlData = true; + } + catch + { + var message = ErrorMessage.GetThrowMessage( + "You need to refer to MySql.Data.dll", + "需要引用MySql.Data.dll,请在Nuget安装最新稳定版本,如果有版本兼容问题请先删除原有引用"); + throw new Exception(message); + } + } + } + + public static void TryPostgreSQL() + { + if (!IsTryPgSql) + { + try + { + PostgreSQLProvider db = new PostgreSQLProvider(); + var conn = db.GetAdapter(); + IsTryPgSql = true; + } + catch + { + var message = ErrorMessage.GetThrowMessage( + "You need to refer to Npgsql 3.2.7", + "你需要引用 Npgsql 3.2.7及以上版本"); + throw new Exception(message); + } + } + } + + public static void TryOracle() + { + if (!IsTryOracle) + { + try + { + OracleProvider db = new OracleProvider(); + var conn = db.GetAdapter(); + IsTryOracle = true; + } + catch + { + var message = ErrorMessage.GetThrowMessage( + "You need to refer to Oracle.ManagedDataAccess.Core", + "你需要引用 Oracle.ManagedDataAccess.Core"); + throw new Exception(message); + } + } + } + + public static void TrySqlite() + { + if (!IsTrySqlite) + { + try + { + SqliteProvider db = new SqliteProvider(); + var conn = db.GetAdapter(); + IsTrySqlite = true; + } + catch (Exception ex) + { + var message = ErrorMessage.GetThrowMessage( + "You need to refer to Microsoft.Data.Sqlite." + ex.Message, + "你需要引用Microsoft.Data.Sqlite,如果有版本兼容问题请先删除原有引用"); + throw new Exception(message); + } + } + } + + public static void TryKdbndb() + { + if (!IsTryKd) + { + try + { + KdbndpProvider db = new KdbndpProvider(); + var conn = db.GetAdapter(); + IsTryKd = true; + } + catch + { + var message = "需要引用Kdbndp.dll,Github搜索sqlsugar源码里面有"; + throw new Exception(message); + } + } + } + + public static void TryDm() + { + if (!IsTryDm) + { + try + { + DmProvider db = new DmProvider(); + var conn = db.GetAdapter(); + IsTryDm = true; + } + catch + { + var message = "需要引用DmProvider.dll,Github搜索sqlsugar源码里面有"; + throw new Exception(message); + } + } + } + + public static void TryOscar() + { + if (!IsTryOscar) + { + try + { + OscarProvider db = new OscarProvider(); + db.GetAdapter(); + IsTryOscar = true; + } + catch + { + var message = "需要引用Oscar.Data.SqlClient.dll,Github搜索sqlsugar源码里面有"; + throw new Exception(message); + } + } + } + } +} \ No newline at end of file diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Infrastructure/InstanceFactory.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Infrastructure/InstanceFactory.cs new file mode 100644 index 000000000..b1e70f1e3 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Infrastructure/InstanceFactory.cs @@ -0,0 +1,823 @@ +using System.Reflection; +namespace SqlSugar +{ + + public static class InstanceFactory + { + static Assembly assembly = Assembly.GetExecutingAssembly(); + static Dictionary typeCache = new Dictionary(); + private static string _CustomDllName = ""; + private static List CustomDlls = new List(); + public static Assembly[] CustomAssemblies = Array.Empty(); + public static string CustomDllName + { + get { return _CustomDllName; } + set + { + if (!CustomDlls.Contains(value)) + { + CustomDlls.Add(value); + } + _CustomDllName = value; + } + } + public static string CustomDbName = ""; + public static string CustomNamespace = ""; + public static bool NoCache = false; + public static void RemoveCache() + { + typeCache = new Dictionary(); + } + + #region Queryable + public static ISugarQueryable GetQueryable(ConnectionConfig currentConnectionConfig) + { + if (currentConnectionConfig.DbType == DbType.SqlServer) + { + return new SqlServerQueryable(); + } + else if (currentConnectionConfig.DbType == DbType.MySql) + { + return new MySqlQueryable(); + } + else if (currentConnectionConfig.DbType == DbType.Sqlite) + { + return new SqliteQueryable(); + } + else if (currentConnectionConfig.DbType == DbType.PostgreSQL) + { + return new PostgreSQLQueryable(); + } + else + { + string className = "Queryable"; + className = GetClassName(currentConnectionConfig.DbType.ToString(), className); + ISugarQueryable result = CreateInstance>(className); + return result; + } + } + public static ISugarQueryable GetQueryable(ConnectionConfig currentConnectionConfig) + { + if (currentConnectionConfig.DbType == DbType.SqlServer) + { + return new SqlServerQueryable(); + } + else + { + string className = "Queryable"; + className = GetClassName(currentConnectionConfig.DbType.ToString(), className); + ISugarQueryable result = CreateInstance>(className); + return result; + } + } + public static ISugarQueryable GetQueryable(ConnectionConfig currentConnectionConfig) + { + if (currentConnectionConfig.DbType == DbType.SqlServer) + { + return new SqlServerQueryable(); + } + else + { + string className = "Queryable"; + className = GetClassName(currentConnectionConfig.DbType.ToString(), className); + ISugarQueryable result = CreateInstance>(className); + return result; + } + } + public static ISugarQueryable GetQueryable(ConnectionConfig currentConnectionConfig) + { + string className = "Queryable"; + className = GetClassName(currentConnectionConfig.DbType.ToString(), className); + ISugarQueryable result = CreateInstance>(className); + return result; + } + public static ISugarQueryable GetQueryable(ConnectionConfig currentConnectionConfig) + { + string className = "Queryable"; + className = GetClassName(currentConnectionConfig.DbType.ToString(), className); + ISugarQueryable result = CreateInstance>(className); + return result; + } + public static ISugarQueryable GetQueryable(ConnectionConfig currentConnectionConfig) + { + string className = "Queryable"; + className = GetClassName(currentConnectionConfig.DbType.ToString(), className); + ISugarQueryable result = CreateInstance>(className); + return result; + } + public static ISugarQueryable GetQueryable(ConnectionConfig currentConnectionConfig) + { + string className = "Queryable"; + className = GetClassName(currentConnectionConfig.DbType.ToString(), className); + ISugarQueryable result = CreateInstance>(className); + return result; + } + public static ISugarQueryable GetQueryable(ConnectionConfig currentConnectionConfig) + { + string className = "Queryable"; + className = GetClassName(currentConnectionConfig.DbType.ToString(), className); + ISugarQueryable result = CreateInstance>(className); + return result; + } + + #region 9-12 + public static ISugarQueryable GetQueryable(ConnectionConfig currentConnectionConfig) + { + string className = "Queryable"; + className = GetClassName(currentConnectionConfig.DbType.ToString(), className); + ISugarQueryable result = CreateInstance>(className); + return result; + } + public static ISugarQueryable GetQueryable(ConnectionConfig currentConnectionConfig) + { + string className = "Queryable"; + className = GetClassName(currentConnectionConfig.DbType.ToString(), className); + ISugarQueryable result = CreateInstance>(className); + return result; + } + public static ISugarQueryable GetQueryable(ConnectionConfig currentConnectionConfig) + { + string className = "Queryable"; + className = GetClassName(currentConnectionConfig.DbType.ToString(), className); + ISugarQueryable result = CreateInstance>(className); + return result; + } + public static ISugarQueryable GetQueryable(ConnectionConfig currentConnectionConfig) + { + string className = "Queryable"; + className = GetClassName(currentConnectionConfig.DbType.ToString(), className); + ISugarQueryable result = CreateInstance>(className); + return result; + } + #endregion + + #endregion + + public static QueryBuilder GetQueryBuilderWithContext(ISqlSugarClient db) + { + if (db is SqlSugarClient) + { + db = (db as SqlSugarClient).Context; + } + else if (db is SqlSugarScope) + { + db = (db as SqlSugarScope).ScopedContext.Context; + } + if (!(db is SqlSugarProvider)) + { + db = new SqlSugarClient(db.CurrentConnectionConfig).Context; + } + var QueryBuilder = InstanceFactory.GetQueryBuilder(db.CurrentConnectionConfig); + QueryBuilder.Context = (SqlSugarProvider)db; + QueryBuilder.Builder = InstanceFactory.GetSqlbuilder(db.CurrentConnectionConfig); + QueryBuilder.Builder.Context = (SqlSugarProvider)db; + return QueryBuilder; + } + + public static QueryBuilder GetQueryBuilder(ConnectionConfig currentConnectionConfig) + { + if (currentConnectionConfig.DbType == DbType.SqlServer) + { + return new SqlServerQueryBuilder(); + } + else if (currentConnectionConfig.DbType == DbType.MySql) + { + return new MySqlQueryBuilder(); + } + else + { + QueryBuilder result = CreateInstance(GetClassName(currentConnectionConfig.DbType.ToString(), "QueryBuilder")); + return result; + } + } + public static InsertBuilder GetInsertBuilder(ConnectionConfig currentConnectionConfig) + { + InsertBuilder result = CreateInstance(GetClassName(currentConnectionConfig.DbType.ToString(), "InsertBuilder")); + return result; + } + public static UpdateBuilder GetUpdateBuilder(ConnectionConfig currentConnectionConfig) + { + UpdateBuilder result = CreateInstance(GetClassName(currentConnectionConfig.DbType.ToString(), "UpdateBuilder")); + return result; + } + public static DeleteBuilder GetDeleteBuilder(ConnectionConfig currentConnectionConfig) + { + DeleteBuilder result = CreateInstance(GetClassName(currentConnectionConfig.DbType.ToString(), "DeleteBuilder")); + return result; + } + + public static ILambdaExpressions GetLambdaExpressions(ConnectionConfig currentConnectionConfig) + { + if (currentConnectionConfig.DbType == DbType.SqlServer) + { + return new SqlServerExpressionContext(); + } + else if (currentConnectionConfig.DbType == DbType.MySql) + { + return new MySqlExpressionContext(); + } + else + { + ILambdaExpressions result = CreateInstance(GetClassName(currentConnectionConfig.DbType.ToString(), "ExpressionContext")); + return result; + } + } + public static ISqlBuilder GetSqlBuilderWithContext(ISqlSugarClient db) + { + var result = GetQueryBuilderWithContext(db).Builder; + return result; + } + public static ISqlBuilder GetSqlbuilder(ConnectionConfig currentConnectionConfig) + { + if (currentConnectionConfig.DbType == DbType.SqlServer) + { + return new SqlServerBuilder(); + } + else if (currentConnectionConfig.DbType == DbType.MySql) + { + return new MySqlBuilder(); + } + else + { + ISqlBuilder result = CreateInstance(GetClassName(currentConnectionConfig.DbType.ToString(), "Builder")); + return result; + } + } + + public static UpdateableProvider GetUpdateableProvider(ConnectionConfig currentConnectionConfig) where T : class, new() + { + if (currentConnectionConfig.DbType == DbType.Oracle) + { + return new OracleUpdateable(); + } + else if (IsCustomDb(currentConnectionConfig)) + { + var name = + "SqlSugar." + currentConnectionConfig.DbType + + "." + currentConnectionConfig.DbType + + "Updateable`1"; + var type = GetCustomTypeByClass(name); + if (type == null) + { + name = + InstanceFactory.CustomNamespace + + "." + InstanceFactory.CustomDbName + + "Updateable`1"; + type = GetCustomTypeByClass(name); + } + if (type == null) + { + return new UpdateableProvider(); + } + else + { + return (UpdateableProvider)Activator.CreateInstance(type, true); + } + } + else + { + return new UpdateableProvider(); + } + } + + public static DeleteableProvider GetDeleteableProvider(ConnectionConfig currentConnectionConfig) where T : class, new() + { + if (currentConnectionConfig.DbType == DbType.Oracle) + { + return new OracleDeleteable(); + } + else if (IsCustomDb(currentConnectionConfig)) + { + var name = + "SqlSugar." + currentConnectionConfig.DbType + + "." + currentConnectionConfig.DbType + + "Deleteable`1"; + var type = GetCustomTypeByClass(name); + if (type == null) + { + name = + InstanceFactory.CustomNamespace + + "." + InstanceFactory.CustomDbName + + "Deleteable`1"; + type = GetCustomTypeByClass(name); + } + if (type == null) + { + return new DeleteableProvider(); + } + else + { + return (DeleteableProvider)Activator.CreateInstance(type, true); + } + } + else + { + return new DeleteableProvider(); + } + } + + public static InsertableProvider GetInsertableProvider(ConnectionConfig currentConnectionConfig) where T : class, new() + { + if (currentConnectionConfig.DbType == DbType.Oracle) + { + return new OracleInsertable(); + } + else if (currentConnectionConfig.DbType == DbType.PostgreSQL) + { + return new PostgreSQLInserttable(); + } + else if (currentConnectionConfig.DbType == DbType.Kdbndp) + { + return new KdbndpInserttable(); + } + else if (currentConnectionConfig.DbType == DbType.Oscar) + { + return new KdbndpInserttable(); + } + else if (IsCustomDb(currentConnectionConfig)) + { + var name = + "SqlSugar." + currentConnectionConfig.DbType + + "." + currentConnectionConfig.DbType + + "Insertable`1"; + var type = GetCustomTypeByClass(name); + if (type == null) + { + name = + InstanceFactory.CustomNamespace + + "." + InstanceFactory.CustomDbName + + "Insertable`1"; + type = GetCustomTypeByClass(name); + } + if (type == null) + { + return new InsertableProvider(); + } + else + { + return (InsertableProvider)Activator.CreateInstance(type, true); + } + } + else + { + return new InsertableProvider(); + } + } + + private static bool IsCustomDb(ConnectionConfig currentConnectionConfig) + { + if (currentConnectionConfig.DbType == DbType.Custom) + { + return true; + } + return currentConnectionConfig.DbType != DbType.SqlServer && + currentConnectionConfig.DbType != DbType.Dm && + currentConnectionConfig.DbType != DbType.Oscar && + currentConnectionConfig.DbType != DbType.Access && + currentConnectionConfig.DbType != DbType.QuestDB && + currentConnectionConfig.DbType != DbType.MySql && + currentConnectionConfig.DbType != DbType.Oracle && + currentConnectionConfig.DbType != DbType.PostgreSQL && + currentConnectionConfig.DbType != DbType.ClickHouse && + currentConnectionConfig.DbType != DbType.GBase && + currentConnectionConfig.DbType != DbType.Sqlite && + GetCustomTypeByClass("SqlSugar." + currentConnectionConfig.DbType + "." + currentConnectionConfig.DbType + "Provider") != null; + + } + + public static IDbBind GetDbBind(ConnectionConfig currentConnectionConfig) + { + if (currentConnectionConfig.DbType == DbType.SqlServer) + { + return new SqlServerDbBind(); + } + else if (currentConnectionConfig.DbType == DbType.MySql) + { + return new MySqlDbBind(); + } + else + { + IDbBind result = CreateInstance(GetClassName(currentConnectionConfig.DbType.ToString(), "DbBind")); + return result; + } + } + + public static IDbMaintenance GetDbMaintenance(ConnectionConfig currentConnectionConfig) + { + IDbMaintenance result = CreateInstance(GetClassName(currentConnectionConfig.DbType.ToString(), "DbMaintenance")); + return result; + } + + public static IDbFirst GetDbFirst(ConnectionConfig currentConnectionConfig) + { + IDbFirst result = CreateInstance(GetClassName(currentConnectionConfig.DbType.ToString(), "DbFirst")); + return result; + } + + public static ICodeFirst GetCodeFirst(ConnectionConfig currentConnectionConfig) + { + ICodeFirst result = CreateInstance(GetClassName(currentConnectionConfig.DbType.ToString(), "CodeFirst")); + return result; + } + + public static IAdo GetAdo(ConnectionConfig currentConnectionConfig) + { + if (currentConnectionConfig.DbType == DbType.SqlServer) + { + return new SqlServerProvider(); + } + else + { + IAdo result = CreateInstance(GetClassName(currentConnectionConfig.DbType.ToString(), "Provider")); + return result; + } + } + + private static string GetClassName(string type, string name) + { + if (type == "MySqlConnector") + { + return "SqlSugar.MySqlConnector.MySql" + name; + } + else if (type == "Access") + { + return "SqlSugar.Access.Access" + name; + } + else if (type == "ClickHouse") + { + return "SqlSugar.ClickHouse.ClickHouse" + name; + } + else if (type == "GBase") + { + return "SqlSugar.GBase.GBase" + name; + } + else if (type == "Odbc") + { + return "SqlSugar.Odbc.Odbc" + name; + } + else if (type == "Custom") + { + return CustomNamespace + "." + CustomDbName + name; + } + else if (type == "HANA") + { + return InstanceFactory.CustomDllName + "." + type + name; + } + else if (type == "DB2") + { + return "SqlSugar.DB2." + type + name; + } + else if (type == "GaussDBNative") + { + return "SqlSugar.GaussDB.GaussDB" + name; + } + else + { + //if (!string.IsNullOrEmpty(CustomDllName)) + //{ + // type = CustomDllName; + //} + return UtilConstants.AssemblyName + "." + type + name; + } + } + + #region CreateInstance + private static Restult CreateInstance(string className) + { + return CreateInstance(className, typeof(T)); + } + private static Restult CreateInstance(string className) + { + return CreateInstance(className, typeof(T), typeof(T2)); + } + private static Restult CreateInstance(string className) + { + return CreateInstance(className, typeof(T), typeof(T2), typeof(T3)); + } + private static Restult CreateInstance(string className) + { + return CreateInstance(className, typeof(T), typeof(T2), typeof(T3), typeof(T4)); + } + private static Restult CreateInstance(string className) + { + return CreateInstance(className, typeof(T), typeof(T2), typeof(T3), typeof(T4), typeof(T5)); + } + private static Restult CreateInstance(string className) + { + return CreateInstance(className, typeof(T), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6)); + } + private static Restult CreateInstance(string className) + { + return CreateInstance(className, typeof(T), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7)); + } + private static Restult CreateInstance(string className) + { + return CreateInstance(className, typeof(T), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8)); + } + + #region 9-12 + private static Restult CreateInstance(string className) + { + return CreateInstance(className, typeof(T), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9)); + } + private static Restult CreateInstance(string className) + { + return CreateInstance(className, typeof(T), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9), typeof(T10)); + } + private static Restult CreateInstance(string className) + { + return CreateInstance(className, typeof(T), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9), typeof(T10), typeof(T11)); + } + private static Restult CreateInstance(string className) + { + return CreateInstance(className, typeof(T), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9), typeof(T10), typeof(T11), typeof(T12)); + } + #endregion + + private static Restult CreateInstance(string className, params Type[] types) + { + try + { + if (NoCache) + { + return NoCacheGetCacheInstance(className, types); + } + else + { + return GetCacheInstance(className, types); + } + } + catch + { + NoCache = true; + return NoCacheGetCacheInstance(className, types); + } + } + + private static Restult GetCacheInstance(string className, Type[] types) + { + var cacheKey = className + string.Join(",", types.Select(it => it.FullName)); + Type type = null; + if (typeCache.TryGetValue(cacheKey, out Type? value)) + { + type = value; + } + else + { + lock (typeCache) + { + if (string.IsNullOrEmpty(CustomDllName)) + { + type = Type.GetType(className + '`' + types.Length, true).MakeGenericType(types); + } + else + { + var custom = GetCustomTypeByClass(className + '`' + types.Length); + if (custom != null) + { + type = custom.MakeGenericType(types); + } + if (type == null) + { + type = Type.GetType(className + '`' + types.Length, true).MakeGenericType(types); + } + } + Check.ArgumentNullException(type, string.Format(null, ErrorMessage.ObjNotExistCompositeFormat, className)); + typeCache.TryAdd(cacheKey, type); + } + } + var result = (Restult)Activator.CreateInstance(type, true); + return result; + } + private static Restult NoCacheGetCacheInstance(string className, Type[] types) + { + + Type type = null; + if (string.IsNullOrEmpty(CustomDllName)) + { + type = Type.GetType(className + '`' + types.Length, true).MakeGenericType(types); + } + else + { + var custom = GetCustomTypeByClass(className + '`' + types.Length); + if (custom != null) + { + type = custom.MakeGenericType(types); + } + if (type == null) + { + type = Type.GetType(className + '`' + types.Length)?.MakeGenericType(types); + if (type == null) + { + type = GetCustomDbType(className + '`' + types.Length, type).MakeGenericType(types); + } + } + } + var result = (Restult)Activator.CreateInstance(type, true); + return result; + } + + public static T CreateInstance(string className) + { + try + { + if (NoCache) + { + return NoCacheGetCacheInstance(className); + } + else + { + return GetCacheInstance(className); + } + } + catch + { + return NoCacheGetCacheInstance(className); + } + } + + private static T GetCacheInstance(string className) + { + Type type; + if (typeCache.TryGetValue(className, out Type? value)) + { + type = value; + } + else + { + lock (typeCache) + { + if (string.IsNullOrEmpty(CustomDllName)) + { + type = assembly.GetType(className); + } + else + { + type = GetCustomTypeByClass(className); + if (type == null) + { + type = assembly.GetType(className); + } + } + if (type == null) + { + type = GetCustomDbType(className, type); + } + Check.ArgumentNullException(type, string.Format(null, ErrorMessage.ObjNotExistCompositeFormat, className)); + typeCache.TryAdd(className, type); + } + } + var result = (T)Activator.CreateInstance(type, true); + return result; + } + private static T NoCacheGetCacheInstance(string className) + { + Type type = null; + if (string.IsNullOrEmpty(CustomDllName)) + { + type = assembly.GetType(className); + } + else + { + type = GetCustomTypeByClass(className); + } + if (type == null) + { + type = GetCustomDbType(className, type); + } + var result = (T)Activator.CreateInstance(type, true); + return result; + } + + private static Type GetCustomDbType(string className, Type type) + { + if (className.Replace(".", "").Length + 1 == className.Length) + { + var array = className.Split('.'); + foreach (var item in UtilMethods.EnumToDictionary()) + { + if (array.Last().StartsWith(item.Value.ToString())) + { + + var newName = array.First() + "." + item.Value.ToString() + "." + array.Last(); + type = GetCustomTypeByClass(newName); + break; + } + } + + } + + return type; + } + + internal static Type GetCustomTypeByClass(string className) + { + Type type = null; + foreach (var item in CustomDlls.ToArray()) + { + if (type == null) + { + type = GetCustomTypeByClass(className, item); + } + if (type != null) + { + break; + } + } + return type; + } + internal static Type GetCustomTypeByClass(string className, string customDllName) + { + var key = "Assembly_" + customDllName + assembly.GetHashCode(); + var newAssembly = new ReflectionInoCacheService().GetOrCreate(key, () => + { + try + { + if (CustomAssemblies?.Any(it => it.FullName.StartsWith(customDllName)) == true) + { + return CustomAssemblies?.First(it => it.FullName.StartsWith(customDllName)); + } + var path = Assembly.GetExecutingAssembly().Location; + if (path.HasValue()) + { + path = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(path), customDllName + ".dll"); + } + if (path.HasValue() && FileHelper.IsExistFile(path)) + { + return Assembly.LoadFrom(path); + } + else + { + return Assembly.LoadFrom(customDllName + ".dll"); + } + } + catch + { + var message = "Not Found " + customDllName + ".dll"; + Check.Exception(true, message); + return null; + } + }); + Type type = newAssembly.GetType(className); + if (type == null) + { + type = assembly.GetType(className); + } + return type; + } + internal static Type GetCustomTypeByClass(string className) + { + Type type = null; + foreach (var item in CustomDlls.ToArray()) + { + if (type == null) + { + type = GetCustomTypeByClass(className, item); + } + if (type != null) + { + break; + } + } + return type; + } + internal static Type GetCustomTypeByClass(string className, string customDllName) + { + var key = "Assembly_" + customDllName + assembly.GetHashCode(); + var newAssembly = new ReflectionInoCacheService().GetOrCreate(key, () => + { + try + { + if (CustomAssemblies?.Any(it => it.FullName.StartsWith(customDllName)) == true) + { + return CustomAssemblies?.First(it => it.FullName.StartsWith(customDllName)); + } + var path = Assembly.GetExecutingAssembly().Location; + if (path.HasValue()) + { + path = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(path), customDllName + ".dll"); + } + if (path.HasValue() && FileHelper.IsExistFile(path)) + { + return Assembly.LoadFrom(path); + } + else + { + return Assembly.LoadFrom(customDllName + ".dll"); + } + } + catch + { + var message = "Not Found " + customDllName + ".dll"; + Check.Exception(true, message); + return null; + } + }); + Type typeArgument = typeof(T); + string fullTypeName = className + "[[" + typeArgument.FullName + "," + typeArgument.Assembly.FullName + "]]"; + Type type = newAssembly.GetType(fullTypeName); + return type; + } + #endregion + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Infrastructure/Mapper.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Infrastructure/Mapper.cs new file mode 100644 index 000000000..05c07ade7 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Infrastructure/Mapper.cs @@ -0,0 +1,73 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public class SugarMapper + { + private ISqlSugarClient _context; + public SugarMapper(ISqlSugarClient context) + { + _context = context; + } + + public string GetSelectValue(QueryBuilder queryBuilder) + { + string result = string.Empty; + var veiwModel = _context.EntityMaintenance.GetEntityInfo(); + var exp = (queryBuilder.JoinExpression as LambdaExpression); + List> selectItems = new List>(); + var exParsmeters = exp.Parameters.Select(it => new { shortName = it.Name, type = it.Type }).ToList(); + foreach (var viewColumns in veiwModel.Columns) + { + var isbreak = false; + foreach (var expPars in exParsmeters) + { + if (isbreak) + { + break; + } + var entityInfo = _context.EntityMaintenance.GetEntityInfo(expPars.type); + var columns = entityInfo.Columns.Where(it => it.IsIgnore == false); + var list = columns.Select(it => + { + var array = new string[] + { + it.PropertyName, + it.DbColumnName, + + expPars.type.Name+it.PropertyName, + expPars.type.Name+it.DbColumnName, + + expPars.type.Name+"_"+it.PropertyName, + expPars.type.Name+"_"+it.DbColumnName, + + expPars.shortName+it.PropertyName, + expPars.shortName+it.DbColumnName, + }; + return new { it, array }; + }).ToList(); + var columnInfo = list.FirstOrDefault(y => y.array.Select(z => z.ToLower()).Contains(viewColumns.PropertyName.ToLower())); + if (columnInfo != null) + { + JoinMapper joinMapper = new JoinMapper() + { + AsName = viewColumns.PropertyName, + DbName = columnInfo.it.DbColumnName + }; + //var isNoPgAuto = queryBuilder.Context.CurrentConnectionConfig.MoreSettings?.PgSqlIsAutoToLower == false; + //var isNoUpper = this._context.CurrentConnectionConfig?.MoreSettings?.IsAutoToUpper == false; + var shortName = queryBuilder.Builder.GetTranslationColumnName(expPars.shortName); + selectItems.Add(new KeyValuePair(shortName, joinMapper)); + isbreak = true; + } + } + } + result = queryBuilder.GetSelectByItems(selectItems); + //if (_context.CurrentConnectionConfig.DbType == DbType.PostgreSQL) + //{ + // result = result.ToLower(); + //} + return result; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Infrastructure/StaticConfig.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Infrastructure/StaticConfig.cs new file mode 100644 index 000000000..4346c089c --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Infrastructure/StaticConfig.cs @@ -0,0 +1,38 @@ +namespace SqlSugar +{ + public static class StaticConfig + { + public static bool EnableAot { get; set; } + public static Func Encode { get; set; } + public static Func Decode { get; set; } + public static bool AppContext_ConvertInfinityDateTime { get; set; } + + public const string CodeFirst_BigString = "varcharmax,longtext,text,clob"; + public static string CodeFirst_MySqlCollate { get; set; } + public static string CodeFirst_MySqlTableEngine { get; set; } + public static Type Backup_MySqlBackupType { get; set; } + + public static Func CustomSnowFlakeFunc; + public static Func CustomSnowFlakeTimeErrorFunc; + public static Func CustomGuidFunc; + public static Func CustomGuidByValueFunc; + + public static Action CompleteQueryableFunc; + public static Action CompleteInsertableFunc; + public static Action CompleteUpdateableFunc; + public static Action CompleteDeleteableFunc; + public static Action CompleteDbFunc; + + public static Func> SplitTableGetTablesFunc; + public static Action SplitTableCreateTableFunc; + + public static bool Check_StringIdentity = true; + public static bool EnableAllWhereIF = false; + public static Func Check_FieldFunc; + public static Type DynamicExpressionParserType; + public static object DynamicExpressionParsingConfig; + public static Action CacheRemoveByLikeStringFunc { get; set; } + public static Guid TableQuerySqlKey { get; set; } + public static string BulkCopy_MySqlCsvPath { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/IntegrationServices/CacheService.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/IntegrationServices/CacheService.cs new file mode 100644 index 000000000..3fa84aee8 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/IntegrationServices/CacheService.cs @@ -0,0 +1,139 @@ +namespace SqlSugar +{ + public class ReflectionInoCacheService : ICacheService + { + public void Add(string key, V value) + { + ReflectionInoCore.GetInstance().Add(key, value); + } + public void Add(string key, V value, int cacheDurationInSeconds) + { + ReflectionInoCore.GetInstance().Add(key, value, cacheDurationInSeconds); + } + + public bool ContainsKey(string key) + { + return ReflectionInoCore.GetInstance().ContainsKey(key); + } + + public V Get(string key) + { + return ReflectionInoCore.GetInstance().Get(key); + } + + public IEnumerable GetAllKey() + { + return ReflectionInoCore.GetInstance().GetAllKey(); + } + + public V GetOrCreate(string cacheKey, Func create, int cacheDurationInSeconds = int.MaxValue) + { + return ReflectionInoCore.GetInstance().GetOrCreate(cacheKey, create); + } + + public void Remove(string key) + { + ReflectionInoCore.GetInstance().Remove(key); + } + } + public class ReflectionInoCore + { + readonly System.Collections.Concurrent.ConcurrentDictionary InstanceCache = new System.Collections.Concurrent.ConcurrentDictionary(); + private static ReflectionInoCore _instance = null; + private static readonly object _instanceLock = new object(); + private ReflectionInoCore() { } + + public V this[string key] + { + get + { + return this.Get(key); + } + } + + public bool ContainsKey(string key) + { + return this.InstanceCache.ContainsKey(key); + } + + public V Get(string key) + { + if (this.ContainsKey(key)) + return this.InstanceCache[key]; + else + return default(V); + } + + public static ReflectionInoCore GetInstance() + { + if (_instance == null) + lock (_instanceLock) + if (_instance == null) + { + _instance = new ReflectionInoCore(); + Action addItem = () => { ReflectionInoCore.GetInstance().RemoveAllCache(); }; + ReflectionInoHelper.AddRemoveFunc(addItem); + } + return _instance; + } + + public void Add(string key, V value) + { + this.InstanceCache.GetOrAdd(key, value); + } + + public void Add(string key, V value, int cacheDurationInSeconds) + { + Check.ThrowNotSupportedException("ReflectionInoCache.Add(string key, V value, int cacheDurationInSeconds)"); + } + + public void Remove(string key) + { + V val; + this.InstanceCache.TryRemove(key, out val); + } + + public void RemoveAllCache() + { + foreach (var key in GetAllKey()) + { + this.Remove(key); + } + } + + public IEnumerable GetAllKey() + { + return this.InstanceCache.Keys; + } + + public V GetOrCreate(string cacheKey, Func create) + { + if (this.ContainsKey(cacheKey)) return Get(cacheKey); + else + { + var reval = create(); + this.Add(cacheKey, reval); + return reval; + } + } + } + internal static class ReflectionInoHelper + { + private static List removeActions = new List(); + internal static void AddRemoveFunc(Action removeAction) + { + removeActions.Add(removeAction); + } + + public static void RemoveAllCache() + { + lock (removeActions) + { + foreach (var item in removeActions) + { + item(); + } + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/IntegrationServices/SerializeService.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/IntegrationServices/SerializeService.cs new file mode 100644 index 000000000..d1f152321 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/IntegrationServices/SerializeService.cs @@ -0,0 +1,105 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Serialization; + +namespace SqlSugar +{ + public class SerializeService : ISerializeService + { + public string SerializeObject(object value) + { + if (value?.GetType().FullName.StartsWith("System.Text.Json.") == true) + { + // 动态创建一个 JsonSerializer 实例 + Type serializerType = value.GetType().Assembly.GetType("System.Text.Json.JsonSerializer"); + + var methods = serializerType + .GetMyMethod("Serialize", 2); + + // 调用 SerializeObject 方法序列化对象 + string json = (string)methods.MakeGenericMethod(value.GetType()) + .Invoke(null, new object[] { value, null }); + return json; + } + return JsonConvert.SerializeObject(value); + } + + public string SugarSerializeObject(object value) + { + return JsonConvert.SerializeObject(value, new JsonSerializerSettings() + { + ContractResolver = new MyContractResolver() + }); + } + + public T DeserializeObject(string value) + { + if (typeof(T).FullName.StartsWith("System.Text.Json.")) + { + // 动态创建一个 JsonSerializer 实例 + Type serializerType = typeof(T).Assembly.GetType("System.Text.Json.JsonSerializer"); + + var methods = serializerType + .GetMethods().Where(it => it.Name == "Deserialize") + .Where(it => it.GetParameters().Any(z => z.ParameterType == typeof(string))).First(); + + // 调用 SerializeObject 方法序列化对象 + T json = (T)methods.MakeGenericMethod(typeof(T)) + .Invoke(null, new object[] { value, null }); + return json; + } + var jSetting = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }; + return JsonConvert.DeserializeObject(value, jSetting); + } + } + public class MyContractResolver : Newtonsoft.Json.Serialization.DefaultContractResolver + { + + + public MyContractResolver() + { + + } + + protected override IList CreateProperties(Type type, MemberSerialization memberSerialization) + { + if (type.IsAnonymousType() || type == UtilConstants.ObjType || type.Namespace == "SqlSugar" || type.IsClass() == false) + { + return base.CreateProperties(type, memberSerialization); + } + else + { + var list = type.GetProperties() + .Where(x => !x.GetCustomAttributes(true).Any(a => (a is SugarColumn) && ((SugarColumn)a).NoSerialize == true)) + .Select(p => new JsonProperty() + { + PropertyName = p.Name, + PropertyType = p.PropertyType, + Readable = true, + Writable = true, + ValueProvider = base.CreateMemberValueProvider(p) + }).ToList(); + foreach (var item in list) + { + if (UtilMethods.GetUnderType(item.PropertyType) == UtilConstants.DateType) + { + CreateDateProperty(type, item); + } + } + return list; + } + } + + private static void CreateDateProperty(Type type, JsonProperty item) + { + var property = type.GetProperties().Where(it => it.Name == item.PropertyName).First(); + var itemType = UtilMethods.GetUnderType(property); + if (property.GetCustomAttributes(true).Any(it => it is SugarColumn)) + { + var sugarAttribute = (SugarColumn)property.GetCustomAttributes(true).First(it => it is SugarColumn); + item.Converter = new IsoDateTimeConverter() { DateTimeFormat = sugarAttribute.SerializeDateTimeFormat }; + } + } + } + +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/IntegrationServices/SplitTableService.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/IntegrationServices/SplitTableService.cs new file mode 100644 index 000000000..ff0aeb7aa --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/IntegrationServices/SplitTableService.cs @@ -0,0 +1,274 @@ +using System.Globalization; +using System.Reflection; +using System.Text.RegularExpressions; + +namespace SqlSugar +{ + public class DateSplitTableService : ISplitTableService + { + #region Core + public virtual List GetAllTables(ISqlSugarClient db, EntityInfo EntityInfo, List tableInfos) + { + CheckTableName(EntityInfo.DbTableName); + var regex = "^" + EntityInfo.DbTableName.Replace("{year}", "([0-9]{2,4})").Replace("{day}", "([0-9]{1,2})").Replace("{month}", "([0-9]{1,2})"); + var currentTables = tableInfos.Where(it => Regex.IsMatch(it.Name, regex, RegexOptions.IgnoreCase)).Select(it => it.Name).Reverse().ToList(); + List result = new List(); + foreach (var item in currentTables) + { + SplitTableInfo tableInfo = new SplitTableInfo(); + tableInfo.TableName = item; + var math = Regex.Match(item, regex, RegexOptions.IgnoreCase); + var group1 = math.Groups[1].Value; + var group2 = math.Groups[2].Value; + var group3 = math.Groups[3].Value; + tableInfo.Date = GetDate(group1, group2, group3, EntityInfo.DbTableName); + //tableInfo.String = null; Time table, it doesn't work + //tableInfo.Long = null; Time table, it doesn't work + result.Add(tableInfo); + } + result = result.OrderByDescending(it => it.Date).ToList(); + return result; + } + public virtual string GetTableName(ISqlSugarClient db, EntityInfo EntityInfo) + { + var splitTableAttribute = EntityInfo.Type.GetCustomAttribute(); + if (splitTableAttribute != null) + { + var type = (splitTableAttribute as SplitTableAttribute).SplitType; + return GetTableName(db, EntityInfo, type); + } + else + { + return GetTableName(db, EntityInfo, SplitType.Day); + } + } + public virtual string GetTableName(ISqlSugarClient db, EntityInfo EntityInfo, SplitType splitType) + { + var date = db.GetDate(); + return GetTableNameByDate(EntityInfo, splitType, date); + } + public virtual string GetTableName(ISqlSugarClient db, EntityInfo entityInfo, SplitType splitType, object fieldValue) + { + var value = Convert.ToDateTime(fieldValue); + return GetTableNameByDate(entityInfo, splitType, value); + } + public virtual object GetFieldValue(ISqlSugarClient db, EntityInfo entityInfo, SplitType splitType, object entityValue) + { + var splitColumn = entityInfo.Columns.FirstOrDefault(it => it.PropertyInfo.GetCustomAttribute() != null); + if (splitColumn == null) + { + return db.GetDate(); + } + else + { + if (entityValue == null) + { + return null; + } + var value = splitColumn.PropertyInfo.GetValue(entityValue, null); + if (value == null) + { + return db.GetDate(); + } + else if (value is DateTimeOffset) + { + return ((DateTimeOffset)value).DateTime; + } + else if (UtilMethods.GetUnderType(value.GetType()) != UtilConstants.DateType) + { + throw new Exception($"DateSplitTableService Split column {splitColumn.PropertyName} not DateTime " + splitType.ToString()); + } + else if (Convert.ToDateTime(value) == DateTime.MinValue) + { + return db.GetDate(); + } + else + { + return value; + } + } + } + public void VerifySplitType(SplitType splitType) + { + switch (splitType) + { + case SplitType.Day: + break; + case SplitType.Week: + break; + case SplitType.Month: + break; + case SplitType.Season: + break; + case SplitType.Year: + break; + case SplitType.Month_6: + break; + default: + throw new Exception("DateSplitTableService no support " + splitType.ToString()); + } + } + + #endregion + + #region Common Helper + private string GetTableNameByDate(EntityInfo EntityInfo, SplitType splitType, DateTime date) + { + date = ConvertDateBySplitType(date, splitType); + return EntityInfo.DbTableName.Replace("{year}", date.Year + "").Replace("{day}", PadLeft2(date.Day + "")).Replace("{month}", PadLeft2(date.Month + "")); + } + + private DateTime GetDate(string group1, string group2, string group3, string dbTableName) + { + var yearIndex = dbTableName.IndexOf("{year}"); + var dayIndex = dbTableName.IndexOf("{day}"); + var monthIndex = dbTableName.IndexOf("{month}"); + List tables = new List(); + tables.Add(new SplitTableSort() { Name = "{year}", Sort = yearIndex }); + tables.Add(new SplitTableSort() { Name = "{day}", Sort = dayIndex }); + tables.Add(new SplitTableSort() { Name = "{month}", Sort = monthIndex }); + tables = tables.OrderBy(it => it.Sort).ToList(); + var year = ""; + var month = ""; + var day = ""; + if (tables[0].Name == "{year}") + { + year = group1; + } + if (tables[1].Name == "{year}") + { + year = group2; + } + if (tables[2].Name == "{year}") + { + year = group3; + } + if (tables[0].Name == "{month}") + { + month = group1; + } + if (tables[1].Name == "{month}") + { + month = group2; + } + if (tables[2].Name == "{month}") + { + month = group3; + } + if (tables[0].Name == "{day}") + { + day = group1; + } + if (tables[1].Name == "{day}") + { + day = group2; + } + if (tables[2].Name == "{day}") + { + day = group3; + } + return Convert.ToDateTime($"{year}-{month}-{day}", CultureInfo.InvariantCulture); + } + + private string PadLeft2(string str) + { + if (str.Length < 2) + { + return str.PadLeft(2, '0'); + } + else + { + return str; + } + } + + private static void CheckTableName(string dbTableName) + { + Check.Exception(!dbTableName.Contains("{year}"), ErrorMessage.GetThrowMessage("table name need {{year}}", "分表表名需要占位符 {{year}}")); + Check.Exception(!dbTableName.Contains("{month}"), ErrorMessage.GetThrowMessage("table name need {{month}}", "分表表名需要占位符 {{month}} ")); + Check.Exception(!dbTableName.Contains("{day}"), ErrorMessage.GetThrowMessage("table name need {{day}}", "分表表名需要占位符{{day}}")); + Check.Exception(Regex.Matches(dbTableName, @"\{year\}").Count > 1, ErrorMessage.GetThrowMessage(" There can only be one {{year}}", " 只能有一个 {{year}}")); + Check.Exception(Regex.Matches(dbTableName, @"\{month\}").Count > 1, ErrorMessage.GetThrowMessage("There can only be one {{month}}", "只能有一个 {{month}} ")); + Check.Exception(Regex.Matches(dbTableName, @"\{day\}").Count > 1, ErrorMessage.GetThrowMessage("There can only be one {{day}}", "只能有一个{{day}}")); + Check.Exception(Regex.IsMatch(dbTableName, @"\d\{|\}\d"), ErrorMessage.GetThrowMessage(" '{{' or '}}' can't be numbers nearby", "占位符相令一位不能是数字,比如 : 1{{day}}2 错误 , 正确: 1_{{day}}_2")); + } + #endregion + + #region Date Helper + private DateTime ConvertDateBySplitType(DateTime time, SplitType type) + { + switch (type) + { + case SplitType.Day: + return Convert.ToDateTime(time.ToString("yyyy-MM-dd")); + case SplitType.Week: + return GetMondayDate(time); + case SplitType.Month: + return Convert.ToDateTime(time.ToString("yyyy-MM-01")); + case SplitType.Season: + if (time.Month <= 3) + { + return Convert.ToDateTime(time.ToString("yyyy-01-01")); + } + else if (time.Month <= 6) + { + return Convert.ToDateTime(time.ToString("yyyy-04-01")); + } + else if (time.Month <= 9) + { + return Convert.ToDateTime(time.ToString("yyyy-07-01")); + } + else + { + return Convert.ToDateTime(time.ToString("yyyy-10-01")); + } + case SplitType.Year: + return Convert.ToDateTime(time.ToString("yyyy-01-01")); + case SplitType.Month_6: + if (time.Month <= 6) + { + return Convert.ToDateTime(time.ToString("yyyy-01-01")); + } + else + { + return Convert.ToDateTime(time.ToString("yyyy-07-01")); + } + default: + throw new Exception($"SplitType parameter error "); + } + } + private DateTime GetMondayDate() + { + return GetMondayDate(DateTime.Now); + } + private DateTime GetSundayDate() + { + return GetSundayDate(DateTime.Now); + } + private DateTime GetMondayDate(DateTime someDate) + { + int i = someDate.DayOfWeek - DayOfWeek.Monday; + if (i == -1) i = 6; + TimeSpan ts = new TimeSpan(i, 0, 0, 0); + return someDate.Subtract(ts); + } + private DateTime GetSundayDate(DateTime someDate) + { + int i = someDate.DayOfWeek - DayOfWeek.Sunday; + if (i != 0) i = 7 - i; + TimeSpan ts = new TimeSpan(i, 0, 0, 0); + return someDate.Add(ts); + } + + #endregion + + #region Private Models + internal class SplitTableSort + { + public string Name { get; set; } + public int Sort { get; set; } + } + #endregion + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IAdo.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IAdo.cs new file mode 100644 index 000000000..b0de6f662 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IAdo.cs @@ -0,0 +1,198 @@ +using System.Data; +using System.Data.Common; +using System.Reflection; + +namespace SqlSugar +{ + public partial interface IAdo + { + string SqlParameterKeyWord { get; } + IDbConnection Connection { get; set; } + IDbTransaction Transaction { get; set; } + IDataParameter[] ToIDbDataParameter(params SugarParameter[] pars); + SugarParameter[] GetParameters(object obj, PropertyInfo[] propertyInfo = null); + SqlSugarProvider Context { get; set; } + void CheckConnectionAfter(IDbConnection Connection); + void CheckConnectionBefore(IDbConnection Connection); + void ExecuteBefore(string sql, SugarParameter[] pars); + void ExecuteAfter(string sql, SugarParameter[] pars); + void GetDataBefore(string sql, SugarParameter[] parameters); + void GetDataAfter(string sql, SugarParameter[] parameters); + bool IsAnyTran(); + bool IsNoTran(); + bool IsEnableLogEvent { get; set; } + StackTraceInfo SqlStackTrace { get; } + IDataParameterCollection DataReaderParameters { get; set; } + CommandType CommandType { get; set; } + CancellationToken? CancellationToken { get; set; } + bool IsDisableMasterSlaveSeparation { get; set; } + bool IsClearParameters { get; set; } + int CommandTimeOut { get; set; } + TimeSpan SqlExecutionTime { get; } + TimeSpan ConnectionExecutionTime { get; } + int SqlExecuteCount { get; } + SugarActionType SqlExecuteType { get; } + IDbBind DbBind { get; } + void SetCommandToAdapter(IDataAdapter adapter, DbCommand command); + IDataAdapter GetAdapter(); + DbCommand GetCommand(string sql, SugarParameter[] parameters); + + + DataTable GetDataTable(string sql, object parameters); + DataTable GetDataTable(string sql, params SugarParameter[] parameters); + DataTable GetDataTable(string sql, List parameters); + + Task GetDataTableAsync(string sql, object parameters); + Task GetDataTableAsync(string sql, params SugarParameter[] parameters); + Task GetDataTableAsync(string sql, List parameters); + + DataSet GetDataSetAll(string sql, object parameters); + DataSet GetDataSetAll(string sql, params SugarParameter[] parameters); + DataSet GetDataSetAll(string sql, List parameters); + + Task GetDataSetAllAsync(string sql, object parameters); + Task GetDataSetAllAsync(string sql, params SugarParameter[] parameters); + Task GetDataSetAllAsync(string sql, List parameters); + + IDataReader GetDataReader(string sql, object parameters); + IDataReader GetDataReader(string sql, params SugarParameter[] parameters); + IDataReader GetDataReader(string sql, List parameters); + + + Task GetDataReaderAsync(string sql, object parameters); + Task GetDataReaderAsync(string sql, params SugarParameter[] parameters); + Task GetDataReaderAsync(string sql, List parameters); + + + object GetScalar(string sql, object parameters); + object GetScalar(string sql, params SugarParameter[] parameters); + object GetScalar(string sql, List parameters); + + Task GetScalarAsync(string sql, object parameters); + Task GetScalarAsync(string sql, params SugarParameter[] parameters); + Task GetScalarAsync(string sql, List parameters); + + int ExecuteCommandWithGo(string sql, params SugarParameter[] parameters); + int ExecuteCommand(string sql, object parameters); + int ExecuteCommand(string sql, params SugarParameter[] parameters); + int ExecuteCommand(string sql, List parameters); + + Task ExecuteCommandAsync(string sql, params SugarParameter[] parameters); + Task ExecuteCommandAsync(string sql, object parameters); + Task ExecuteCommandAsync(string sql, object parameters, CancellationToken cancellationToken); + Task ExecuteCommandAsync(string sql, List parameters); + + string GetString(string sql, object parameters); + string GetString(string sql, params SugarParameter[] parameters); + string GetString(string sql, List parameters); + Task GetStringAsync(string sql, object parameters); + Task GetStringAsync(string sql, params SugarParameter[] parameters); + Task GetStringAsync(string sql, List parameters); + + + int GetInt(string sql, object pars); + int GetInt(string sql, params SugarParameter[] parameters); + int GetInt(string sql, List parameters); + + Task GetIntAsync(string sql, object pars); + Task GetIntAsync(string sql, params SugarParameter[] parameters); + Task GetIntAsync(string sql, List parameters); + + + long GetLong(string sql, object pars = null); + + Task GetLongAsync(string sql, object pars = null); + + + Double GetDouble(string sql, object parameters); + Double GetDouble(string sql, params SugarParameter[] parameters); + Double GetDouble(string sql, List parameters); + + + Task GetDoubleAsync(string sql, object parameters); + Task GetDoubleAsync(string sql, params SugarParameter[] parameters); + Task GetDoubleAsync(string sql, List parameters); + + + decimal GetDecimal(string sql, object parameters); + decimal GetDecimal(string sql, params SugarParameter[] parameters); + decimal GetDecimal(string sql, List parameters); + + Task GetDecimalAsync(string sql, object parameters); + Task GetDecimalAsync(string sql, params SugarParameter[] parameters); + Task GetDecimalAsync(string sql, List parameters); + + + DateTime GetDateTime(string sql, object parameters); + DateTime GetDateTime(string sql, params SugarParameter[] parameters); + DateTime GetDateTime(string sql, List parameters); + + Task GetDateTimeAsync(string sql, object parameters); + Task GetDateTimeAsync(string sql, params SugarParameter[] parameters); + Task GetDateTimeAsync(string sql, List parameters); + + + Tuple, List> SqlQuery(string sql, object parameters = null); + Tuple, List, List> SqlQuery(string sql, object parameters = null); + Tuple, List, List, List> SqlQuery(string sql, object parameters = null); + Tuple, List, List, List, List> SqlQuery(string sql, object parameters = null); + Tuple, List, List, List, List, List> SqlQuery(string sql, object parameters = null); + Tuple, List, List, List, List, List, List> SqlQuery(string sql, object parameters = null); + + Task, List>> SqlQueryAsync(string sql, object parameters = null); + Task, List, List>> SqlQueryAsync(string sql, object parameters = null); + Task, List, List, List>> SqlQueryAsync(string sql, object parameters = null); + Task, List, List, List, List>> SqlQueryAsync(string sql, object parameters = null); + Task, List, List, List, List, List>> SqlQueryAsync(string sql, object parameters = null); + Task, List, List, List, List, List, List>> SqlQueryAsync(string sql, object parameters = null); + + List SqlQuery(string sql, object parameters = null); + List SqlQuery(string sql, params SugarParameter[] parameters); + List SqlQuery(string sql, List parameters); + Task> MasterSqlQueryAasync(string sql, object parameters = null); + List MasterSqlQuery(string sql, object parameters = null); + + Task> SqlQueryAsync(string sql, object parameters = null); + Task> SqlQueryAsync(string sql, object parameters, CancellationToken token); + Task> SqlQueryAsync(string sql, List parameters); + Task> SqlQueryAsync(string sql, params SugarParameter[] parameters); + + T SqlQuerySingle(string sql, object whereObj = null); + T SqlQuerySingle(string sql, params SugarParameter[] parameters); + T SqlQuerySingle(string sql, List parameters); + + Task SqlQuerySingleAsync(string sql, object whereObj = null); + Task SqlQuerySingleAsync(string sql, params SugarParameter[] parameters); + Task SqlQuerySingleAsync(string sql, List parameters); + + void RemoveCancellationToken(); + + void Dispose(); + void Close(); + Task CloseAsync(); + void Open(); + Task OpenAsync(); + SugarConnection OpenAlways(); + bool IsValidConnection(); + bool IsValidConnectionNoClose(); + void CheckConnection(); + + void BeginTran(); + Task BeginTranAsync(); + Task BeginTranAsync(IsolationLevel iso); + void BeginTran(IsolationLevel iso); + void BeginTran(string transactionName); + void BeginTran(IsolationLevel iso, string transactionName); + void RollbackTran(); + Task RollbackTranAsync(); + void CommitTran(); + Task CommitTranAsync(); + SqlSugarTransactionAdo UseTran(); + DbResult UseTran(Action action, Action errorCallBack = null); + DbResult UseTran(Func action, Action errorCallBack = null); + Task> UseTranAsync(Func action, Action errorCallBack = null); + Task> UseTranAsync(Func> action, Action errorCallBack = null); + IAdo UseStoredProcedure(); + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/ICodeFirst.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/ICodeFirst.cs new file mode 100644 index 000000000..22132da6e --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/ICodeFirst.cs @@ -0,0 +1,26 @@ +namespace SqlSugar +{ + public partial interface ICodeFirst + { + SqlSugarProvider Context { get; set; } + ICodeFirst BackupTable(int maxBackupDataRows = int.MaxValue); + ICodeFirst SetStringDefaultLength(int length); + + ICodeFirst As(Type type, string newTableName); + ICodeFirst As(string newTableName); + + void InitTables(string entitiesNamespace); + void InitTables(string[] entitiesNamespaces); + void InitTables(params Type[] entityTypes); + void InitTablesWithAttr(params Type[] entityTypes); + void InitTables(Type entityType); + void InitTables(); + void InitTables(); + void InitTables(); + void InitTables(); + void InitTables(); + SplitCodeFirstProvider SplitTables(); + TableDifferenceProvider GetDifferenceTables(); + TableDifferenceProvider GetDifferenceTables(params Type[] types); + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IContextMethods.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IContextMethods.cs new file mode 100644 index 000000000..3384c9163 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IContextMethods.cs @@ -0,0 +1,51 @@ +using System.Data; +using System.Dynamic; +using System.Linq.Expressions; + +namespace SqlSugar +{ + public partial interface IContextMethods + { + SqlSugarProvider Context { get; set; } + QueryBuilder QueryBuilder { get; set; } + List DataReaderToValueTupleType(IDataReader reader); + Task> DataReaderToValueTupleTypeAsync(IDataReader reader); + ExpandoObject DataReaderToExpandoObject(IDataReader reader); + List DataReaderToExpandoObjectList(IDataReader reader); + Task> DataReaderToExpandoObjectListAsync(IDataReader dataReader); + List DataReaderToExpandoObjectListNoUsing(IDataReader reader); + Task> DataReaderToExpandoObjectListAsyncNoUsing(IDataReader dataReader); + List DataReaderToList(IDataReader reader); + List DataReaderToSelectJsonList(IDataReader reader); + List DataReaderToSelectArrayList(IDataReader reader); + Task> DataReaderToSelectArrayListAsync(IDataReader reader); + Task> DataReaderToSelectJsonListAsync(IDataReader reader); + List DataReaderToListNoUsing(IDataReader reader); + Task> DataReaderToListAsync(IDataReader dataReader); + Task> DataReaderToListAsyncNoUsing(IDataReader dataReader); + string SerializeObject(object value); + string SerializeObject(object value, Type type); + T DeserializeObject(string value); + T TranslateCopy(T sourceObject); + SqlSugarProvider CopyContext(bool isCopyEvents = false); + dynamic DataTableToDynamic(DataTable table); + List DataTableToList(DataTable table); + DataTable ListToDataTable(List list); + DataTable ListToDataTableWithAttr(List list); + Dictionary DataTableToDictionary(DataTable table); + List> DataTableToDictionaryList(DataTable table); + ICacheService GetReflectionInoCacheInstance(); + void RemoveCacheAll(); + void RemoveCacheAll(); + void RemoveCacheByLikeKey(string key); + void RemoveCache(string key); + void PageEach(IEnumerable pageItems, int pageSize, Action> action); + Task PageEachAsync(IEnumerable pageItems, int pageSize, Func, Task> action); + Task PageEachAsync(IEnumerable pageItems, int pageSize, Func, Task> action); + List JsonToConditionalModels(string json); + DataTable DictionaryListToDataTable(List> dictionaryList); + List ToTree(List list, Expression>> childListExpression, Expression> parentIdExpression, Expression> pkExpression, object rootValue); + KeyValuePair ConditionalModelsToSql(List conditionalModels, int beginIndex = 0); + string EscapeLikeValue(string value, char wildcard = '%'); + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/ICustomConditionalFunc.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/ICustomConditionalFunc.cs new file mode 100644 index 000000000..83065438e --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/ICustomConditionalFunc.cs @@ -0,0 +1,7 @@ +namespace SqlSugar +{ + public interface ICustomConditionalFunc + { + KeyValuePair GetConditionalSql(ConditionalModel json, int index); + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IDMLBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IDMLBuilder.cs new file mode 100644 index 000000000..cfb5f2a6d --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IDMLBuilder.cs @@ -0,0 +1,14 @@ +using System.Text; + +namespace SqlSugar +{ + public partial interface IDMLBuilder + { + string SqlTemplate { get; } + List Parameters { get; set; } + SqlSugarProvider Context { get; set; } + StringBuilder sql { get; set; } + string ToSqlString(); + void Clear(); + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IDbBind.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IDbBind.cs new file mode 100644 index 000000000..aabdb9e64 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IDbBind.cs @@ -0,0 +1,28 @@ +using System.Data; + +namespace SqlSugar +{ + public partial interface IDbBind + { + QueryBuilder QueryBuilder { get; set; } + SqlSugarProvider Context { get; set; } + List GuidThrow { get; } + List IntThrow { get; } + List StringThrow { get; } + List DecimalThrow { get; } + List DoubleThrow { get; } + List DateThrow { get; } + List ShortThrow { get; } + string GetPropertyTypeName(string dbTypeName); + string GetConvertString(string dbTypeName); + string GetDbTypeName(string csharpTypeName); + string GetCsharpTypeName(string dbTypeName); + string GetCsharpTypeNameByDbTypeName(string dbTypeName); + List> MappingTypes { get; } + List DataReaderToList(Type type, IDataReader reader); + Task> DataReaderToListAsync(Type entityType, IDataReader dataReader); + List DataReaderToListNoUsing(Type type, IDataReader reader); + Task> DataReaderToListNoUsingAsync(Type type, IDataReader reader); + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IDbFirst.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IDbFirst.cs new file mode 100644 index 000000000..bf3890135 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IDbFirst.cs @@ -0,0 +1,29 @@ +namespace SqlSugar +{ + public partial interface IDbFirst + { + ISqlSugarClient Context { get; set; } + IDbFirst SettingClassTemplate(Func func); + IDbFirst SettingClassDescriptionTemplate(Func func); + IDbFirst SettingPropertyTemplate(Func func); + IDbFirst SettingPropertyTemplate(Func func); + IDbFirst SettingPropertyDescriptionTemplate(Func func); + IDbFirst SettingConstructorTemplate(Func func); + IDbFirst SettingNamespaceTemplate(Func func); + RazorFirst UseRazorAnalysis(string razorClassString, string classNamespace = "Models"); + IDbFirst IsCreateAttribute(bool isCreateAttribute = true); + IDbFirst IsCreateDefaultValue(bool isCreateDefaultValue = true); + IDbFirst Where(params string[] objectNames); + IDbFirst Where(Func func); + IDbFirst WhereColumns(Func func); + IDbFirst Where(DbObjectType dbObjectType); + void CreateClassFile(string directoryPath, string nameSpace = "Models"); + Dictionary ToClassStringList(string nameSpace = "Models"); + void Init(); + IDbFirst FormatFileName(Func formatFileNameFunc); + IDbFirst FormatClassName(Func formatClassNameFunc); + IDbFirst FormatPropertyName(Func formatPropertyNameFunc); + IDbFirst StringNullable(); + IDbFirst CreatedReplaceClassString(Func replaceClassStringFunc); + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IDbMaintenance.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IDbMaintenance.cs new file mode 100644 index 000000000..e047bf591 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IDbMaintenance.cs @@ -0,0 +1,113 @@ +namespace SqlSugar +{ + public partial interface IDbMaintenance + { + SqlSugarProvider Context { get; set; } + + #region DML + List GetDataBaseList(SqlSugarClient db); + List GetDataBaseList(); + List GetViewInfoList(bool isCache = true); + List GetTableInfoList(bool isCache = true); + List GetTableInfoList(Func getChangeSqlFunc); + List GetColumnInfosByTableName(string tableName, bool isCache = true); + List GetColumnInfosByTableName(string tableName, Func getChangeSqlFunc); + List GetIsIdentities(string tableName); + List GetPrimaries(string tableName); + List GetProcList(string dbName); + List GetProcList(); + List GetIndexList(string tableName); + List GetFuncList(); + List GetTriggerNames(string tableName); + List GetDbTypes(); + #endregion + + #region Check + bool IsAnyTable(); + bool IsAnyTable(string tableName, bool isCache = true); + bool IsAnyColumn(string tableName, string column, bool isCache = true); + bool IsPrimaryKey(string tableName, string column); + bool IsPrimaryKey(string tableName, string column, bool isCache = true); + bool IsIdentity(string tableName, string column); + bool IsAnyConstraint(string ConstraintName); + bool IsAnySystemTablePermissions(); + bool IsAnyProcedure(string procName); + #endregion + + #region DDL + bool AddDefaultValue(string tableName, string columnName, string defaultValue); + bool CreateIndex(string tableName, string[] columnNames, bool isUnique = false); + bool CreateIndex(string tableName, string[] columnNames, string IndexName, bool isUnique = false); + bool DropTable(string tableName); + bool DropView(string viewName); + bool DropIndex(string indexName); + bool DropIndex(string indexName, string tableName); + bool DropFunction(string funcName); + bool DropProc(string procName); + bool DropTable(params string[] tableName); + bool DropTable(params Type[] tableEntityTypes); + bool DropTable(); + bool DropTable(); + bool DropTable(); + bool DropTable(); + bool TruncateTable(string tableName); + bool TruncateTable(params string[] tableName); + bool TruncateTable(params Type[] tableEntityType); + bool TruncateTable(); + bool TruncateTable(); + bool TruncateTable(); + bool TruncateTable(); + bool TruncateTable(); + bool CreateTable(string tableName, List columns, bool isCreatePrimaryKey = true); + bool AddColumn(string tableName, DbColumnInfo column); + bool UpdateColumn(string tableName, DbColumnInfo column); + bool AddPrimaryKey(string tableName, string columnName); + bool AddPrimaryKeys(string tableName, string[] columnNames); + bool AddPrimaryKeys(string tableName, string[] columnNames, string pkName); + bool DropConstraint(string tableName, string constraintName); + bool BackupDataBase(string databaseName, string fullFileName); + bool BackupTable(string oldTableName, string newTableName, int maxBackupDataRows = int.MaxValue); + bool DropColumn(string tableName, string columnName); + bool RenameColumn(string tableName, string oldColumnName, string newColumnName); + bool AddRemark(EntityInfo entity); + void AddIndex(EntityInfo entityInfo); + void AddDefaultValue(EntityInfo entityInfo); + bool IsAnyDefaultValue(string tableName, string columnName); + bool IsAnyIndex(string indexName); + bool AddColumnRemark(string columnName, string tableName, string description); + bool DeleteColumnRemark(string columnName, string tableName); + bool IsAnyColumnRemark(string columnName, string tableName); + bool AddTableRemark(string tableName, string description); + bool DeleteTableRemark(string tableName); + bool IsAnyTableRemark(string tableName); + bool RenameTable(string oldTableName, string newTableName); + /// + ///by current connection string + /// + /// + /// + bool CreateDatabase(string databaseDirectory = null); + /// + /// by databaseName + /// + /// + /// + /// + bool CreateDatabase(string databaseName, string databaseDirectory = null); + /// + /// setAuto incrementInitial value + /// + /// + /// + /// + bool SetAutoIncrementInitialValue(Type type, int initialValue); + /// + /// setAuto incrementInitial value + /// + /// + /// + /// + bool SetAutoIncrementInitialValue(string tableName, int initialValue); + #endregion + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IDeleteable.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IDeleteable.cs new file mode 100644 index 000000000..072d6babb --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IDeleteable.cs @@ -0,0 +1,52 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public interface IDeleteable where T : class, new() + { + DeleteBuilder DeleteBuilder { get; set; } + int ExecuteCommand(); + bool ExecuteCommandHasChange(); + Task ExecuteCommandAsync(); + Task ExecuteCommandAsync(CancellationToken token); + Task ExecuteCommandHasChangeAsync(); + IDeleteable AS(string tableName); + IDeleteable AsType(Type tableNameType); + IDeleteable With(string lockString); + IDeleteable Where(T deleteObj); + IDeleteable WhereIF(bool isWhere, Expression> expression); + IDeleteable Where(Expression> expression); + IDeleteable Where(List deleteObjs); + DeleteablePage PageSize(int pageSize); + IDeleteable In(PkType primaryKeyValue); + IDeleteable In(PkType[] primaryKeyValues); + IDeleteable In(List primaryKeyValues); + IDeleteable In(Expression> inField, PkType primaryKeyValue); + IDeleteable In(Expression> inField, PkType[] primaryKeyValues); + IDeleteable In(Expression> inField, List primaryKeyValues); + IDeleteable In(Expression> inField, ISugarQueryable childQueryExpression); + + IDeleteable In(string inField, List primaryKeyValues); + IDeleteable Where(string whereString, object parameters = null); + IDeleteable Where(string whereString, SugarParameter parameter); + IDeleteable Where(string whereString, SugarParameter[] parameters); + IDeleteable Where(string whereString, List parameters); + IDeleteable WhereColumns(T data, Expression> columns); + IDeleteable WhereColumns(List list, Expression> columns); + IDeleteable WhereColumns(List> columns); + IDeleteable Where(List conditionalModels); + IDeleteable Where(List conditionalModels, bool isWrap); + IDeleteable EnableDiffLogEventIF(bool isEnableDiffLogEvent, object businessData = null); + IDeleteable EnableDiffLogEvent(object businessData = null); + IDeleteable RemoveDataCache(); + IDeleteable RemoveDataCache(string likeString); + KeyValuePair> ToSql(); + string ToSqlString(); + IDeleteable EnableQueryFilter(); + IDeleteable EnableQueryFilter(Type type); + SplitTableDeleteProvider SplitTable(Func, IEnumerable> getTableNamesFunc); + SplitTableDeleteByObjectProvider SplitTable(); + LogicDeleteProvider IsLogic(); + void AddQueue(); + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IFastBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IFastBuilder.cs new file mode 100644 index 000000000..3691e4230 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IFastBuilder.cs @@ -0,0 +1,18 @@ +using System.Data; + +namespace SqlSugar +{ + public interface IFastBuilder + { + EntityInfo FastEntityInfo { get; set; } + bool IsActionUpdateColumns { get; set; } + DbFastestProperties DbFastestProperties { get; set; } + SqlSugarProvider Context { get; set; } + string CharacterSet { get; set; } + Task UpdateByTempAsync(string tableName, string tempName, string[] updateColumns, string[] whereColumns); + Task ExecuteBulkCopyAsync(DataTable dt); + Task CreateTempAsync(DataTable dt) where T : class, new(); + void CloseDb(); + Task Merge(string tableName, DataTable dt, EntityInfo entityInfo, string[] whereColumns, string[] updateColumns, List datas) where T : class, new(); + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IFastest.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IFastest.cs new file mode 100644 index 000000000..6c67b09d4 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IFastest.cs @@ -0,0 +1,45 @@ +using System.Data; + +namespace SqlSugar +{ + public interface IFastest where T : class, new() + { + IFastest RemoveDataCache(); + IFastest RemoveDataCache(string cacheKey); + IFastest AS(string tableName); + IFastest PageSize(int Size); + IFastest OffIdentity(); + IFastest SetCharacterSet(string CharacterSet); + IFastest EnableDataAop(); + int BulkCopy(List datas); + Task BulkCopyAsync(List datas); + int BulkCopy(string tableName, DataTable dataTable); + int BulkCopy(DataTable dataTable); + Task BulkCopyAsync(string tableName, DataTable dataTable); + Task BulkCopyAsync(DataTable dataTable); + + int BulkUpdate(List datas); + Task BulkUpdateAsync(List datas); + int BulkUpdate(List datas, string[] whereColumns, string[] updateColumns); + int BulkUpdate(List datas, string[] whereColumns); + Task BulkUpdateAsync(List datas, string[] whereColumns); + Task BulkUpdateAsync(List datas, string[] whereColumns, string[] updateColumns); + int BulkUpdate(string tableName, DataTable dataTable, string[] whereColumns, string[] updateColumns); + int BulkUpdate(DataTable dataTable, string[] whereColumns, string[] updateColumns); + int BulkUpdate(DataTable dataTable, string[] whereColumns); + Task BulkUpdateAsync(string tableName, DataTable dataTable, string[] whereColumns, string[] updateColumns); + Task BulkUpdateAsync(DataTable dataTable, string[] whereColumns); + SplitFastest SplitTable(); + Task BulkMergeAsync(List datas); + int BulkMerge(List datas); + int BulkMerge(DataTable dataTable, string[] whereColumns, bool isIdentity); + Task BulkMergeAsync(DataTable dataTable, string[] whereColumns, bool isIdentity); + int BulkMerge(DataTable dataTable, string[] whereColumns, string[] updateColumns, bool isIdentity); + Task BulkMergeAsync(DataTable dataTable, string[] whereColumns, string[] updateColumns, bool isIdentity); + Task BulkMergeAsync(List datas, string[] whereColumns); + int BulkMerge(List datas, string[] whereColumns); + Task BulkMergeAsync(List datas, string[] whereColumns, string[] updateColumns); + int BulkMerge(List datas, string[] whereColumns, string[] updateColumns); + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IFilter.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IFilter.cs new file mode 100644 index 000000000..376899211 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IFilter.cs @@ -0,0 +1,9 @@ +namespace SqlSugar +{ + public interface IFilter + { + IFilter Add(SqlFilterItem filter); + void Remove(string filterName); + List GetFilterList { get; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IIncludes.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IIncludes.cs new file mode 100644 index 000000000..eefb14e99 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IIncludes.cs @@ -0,0 +1,286 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + /// + /// Includes + /// + /// + public partial interface ISugarQueryable + { + NavISugarQueryable AsNavQueryable(); + ISugarQueryable IncludesByExpression2(Expression include1, Expression include2); + ISugarQueryable IncludesByExpression(Expression include1); + ISugarQueryable IncludesByNameString(string navMemberName); + ISugarQueryable IncludesByNameString(string navMemberName, string thenNavMemberName2); + ISugarQueryable IncludesByNameString(string navMemberName, string thenNavMemberName2, string thenNavMemberName3); + ISugarQueryable IncludesByNameString(string navMemberName, string thenNavMemberName2, string thenNavMemberName3, string thenNavMemberName4); + ISugarQueryable IncludesByNameString(string navMemberName, string thenNavMemberName2, string thenNavMemberName3, string thenNavMemberName4, string thenNavMemberName5); + ISugarQueryable IncludesAllFirstLayer(params string[] ignoreProperyNameList); + ISugarQueryable IncludesAllSecondLayer(Expression> expression, params string[] ignoreProperyNameList); + ISugarQueryable Includes(Expression>> include1); + ISugarQueryable Includes(Expression> include1); + ISugarQueryable Includes(Expression>> include1, Expression>> include2); + ISugarQueryable Includes(Expression>> include1, Expression> include2); + ISugarQueryable Includes(Expression> include1, Expression> include2); + ISugarQueryable Includes(Expression> include1, Expression>> include2); + NavISugarQueryable Includes(Expression>> include1, Expression>> include2, Expression>> include3); + NavISugarQueryable Includes(Expression>> include1, Expression>> include2, Expression> include3); + NavISugarQueryable Includes(Expression> include1, Expression>> include2, Expression> include3); + NavISugarQueryable Includes(Expression> include1, Expression> include2, Expression> include3); + NavISugarQueryable Includes(Expression>> include1, Expression> include2, Expression>> include3); + NavISugarQueryable Includes(Expression> include1, Expression> include2, Expression>> include3); + NavISugarQueryable Includes(Expression>> include1, Expression> include2, Expression> include3); + NavISugarQueryable Includes(Expression> include1, Expression>> include2, Expression>> include3); + + } + + /// + /// Includes + /// + /// + public partial interface NavISugarQueryable : ISugarQueryable + { + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression>> include4); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression>> include4); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression> include4); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression> include4); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression> include4); + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression> include4); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression>> include4); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression>> include4); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression> include4); + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression>> include4); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression>> include4); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression> include4); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression>> include4); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression> include4); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression>> include4); + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression> include4); + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression>> include4, Expression> include5); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression>> include4, Expression> include5); + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression>> include4, Expression>> include5); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression>> include4, Expression>> include5); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression> include4, Expression>> include5); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression> include4, Expression> include5); + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression>> include4, Expression>> include5); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression>> include4, Expression>> include5); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression>> include4, Expression>> include5); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression> include4, Expression> include5); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression>> include4, Expression> include5); + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression>> include4, Expression> include5); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression>> include4, Expression> include5); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression>> include4, Expression> include5); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression> include4, Expression> include5); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression> include4, Expression> include5); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression>> include4, Expression>> include5); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression>> include4, Expression> include5); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression>> include4, Expression>> include5); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression> include4, Expression>> include5); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression> include4, Expression>> include5); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression> include4, Expression>> include5); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression>> include4, Expression> include5); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression> include4, Expression> include5); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression> include4, Expression> include5); + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression> include4, Expression> include5); + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression> include4, Expression>> include5); + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression> include4, Expression> include5); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression> include4, Expression>> include5); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression>> include4, Expression>> include5); + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression> include4, Expression>> include5); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression> include4, Expression>> include5); + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression> include4, Expression>> include5, Expression>> include6); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression> include4, Expression>> include5, Expression>> include6); + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression>> include4, Expression> include5, Expression> include6); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression>> include4, Expression> include5, Expression> include6); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression> include4, Expression>> include5, Expression> include6); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression> include4, Expression>> include5, Expression>> include6); + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression> include4, Expression> include5, Expression>> include6); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression>> include4, Expression>> include5, Expression>> include6); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression> include4, Expression>> include5, Expression>> include6); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression> include4, Expression>> include5, Expression> include6); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression>> include4, Expression> include5, Expression>> include6); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression> include4, Expression> include5, Expression> include6); + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression> include4, Expression>> include5, Expression> include6); + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression>> include4, Expression> include5, Expression>> include6); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression> include4, Expression>> include5, Expression>> include6); + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression> include4, Expression> include5, Expression> include6); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression> include4, Expression>> include5, Expression>> include6); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression>> include4, Expression>> include5, Expression> include6); + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression>> include4, Expression> include5, Expression> include6); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression> include4, Expression> include5, Expression> include6); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression> include4, Expression> include5, Expression> include6); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression>> include4, Expression> include5, Expression>> include6); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression>> include4, Expression> include5, Expression>> include6); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression> include4, Expression> include5, Expression> include6); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression>> include4, Expression>> include5, Expression>> include6); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression>> include4, Expression>> include5, Expression> include6); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression>> include4, Expression> include5, Expression> include6); + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression> include4, Expression>> include5, Expression> include6); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression>> include4, Expression> include5, Expression> include6); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression> include4, Expression> include5, Expression>> include6); + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression>> include4, Expression> include5, Expression>> include6); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression> include4, Expression> include5, Expression> include6); + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression>> include4, Expression>> include5, Expression>> include6); + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression>> include4, Expression>> include5, Expression> include6); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression>> include4, Expression> include5, Expression> include6); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression>> include4, Expression>> include5, Expression>> include6); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression> include4, Expression>> include5, Expression> include6); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression> include4, Expression> include5, Expression>> include6); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression> include4, Expression>> include5, Expression> include6); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression>> include4, Expression>> include5, Expression> include6); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression>> include4, Expression>> include5, Expression>> include6); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression> include4, Expression> include5, Expression>> include6); + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression> include4, Expression>> include5, Expression>> include6); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression> include4, Expression> include5, Expression>> include6); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression>> include4, Expression> include5, Expression> include6); + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression>> include4, Expression>> include5, Expression>> include6); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression>> include4, Expression>> include5, Expression> include6); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression>> include4, Expression>> include5, Expression>> include6); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression>> include4, Expression> include5, Expression>> include6); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression>> include4, Expression> include5, Expression>> include6); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression>> include4, Expression>> include5, Expression> include6); + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression> include4, Expression> include5, Expression>> include6); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression> include4, Expression> include5, Expression> include6); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression> include4, Expression>> include5, Expression> include6); + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression> include4, Expression> include5, Expression> include6); + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression>> include4, Expression>> include5, Expression> include6); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression>> include4, Expression> include5, Expression> include6); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression>> include4, Expression>> include5, Expression>> include6); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression> include4, Expression> include5, Expression>> include6); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression> include4, Expression> include5, Expression>> include6); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression>> include4, Expression> include5, Expression>> include6); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression> include4, Expression>> include5, Expression> include6); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression> include4, Expression>> include5, Expression>> include6); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression>> include4, Expression>> include5, Expression> include6); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression>> include4, Expression>> include5, Expression> include6, Expression>> include7); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression> include4, Expression>> include5, Expression>> include6, Expression> include7); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression>> include4, Expression>> include5, Expression>> include6, Expression>> include7); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression>> include4, Expression> include5, Expression> include6, Expression> include7); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression>> include4, Expression>> include5, Expression>> include6, Expression> include7); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression>> include4, Expression> include5, Expression> include6, Expression> include7); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression> include4, Expression>> include5, Expression>> include6, Expression> include7); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression>> include4, Expression>> include5, Expression> include6, Expression> include7); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression>> include4, Expression> include5, Expression>> include6, Expression>> include7); + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression> include4, Expression> include5, Expression>> include6, Expression> include7); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression> include4, Expression> include5, Expression>> include6, Expression> include7); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression>> include4, Expression> include5, Expression> include6, Expression> include7); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression> include4, Expression>> include5, Expression> include6, Expression>> include7); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression> include4, Expression>> include5, Expression>> include6, Expression>> include7); + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression> include4, Expression> include5, Expression> include6, Expression>> include7); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression>> include4, Expression>> include5, Expression>> include6, Expression> include7); + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression>> include4, Expression>> include5, Expression> include6, Expression> include7); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression>> include4, Expression>> include5, Expression>> include6, Expression>> include7); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression>> include4, Expression>> include5, Expression> include6, Expression>> include7); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression> include4, Expression> include5, Expression>> include6, Expression> include7); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression>> include4, Expression>> include5, Expression>> include6, Expression> include7); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression>> include4, Expression>> include5, Expression> include6, Expression>> include7); + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression>> include4, Expression> include5, Expression>> include6, Expression> include7); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression>> include4, Expression>> include5, Expression> include6, Expression> include7); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression>> include4, Expression>> include5, Expression> include6, Expression> include7); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression> include4, Expression> include5, Expression>> include6, Expression>> include7); + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression> include4, Expression>> include5, Expression> include6, Expression> include7); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression>> include4, Expression>> include5, Expression>> include6, Expression> include7); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression> include4, Expression> include5, Expression> include6, Expression>> include7); + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression>> include4, Expression>> include5, Expression> include6, Expression>> include7); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression> include4, Expression> include5, Expression>> include6, Expression> include7); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression> include4, Expression> include5, Expression>> include6, Expression> include7); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression> include4, Expression>> include5, Expression> include6, Expression> include7); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression> include4, Expression>> include5, Expression>> include6, Expression> include7); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression> include4, Expression> include5, Expression>> include6, Expression> include7); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression> include4, Expression> include5, Expression>> include6, Expression>> include7); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression>> include4, Expression> include5, Expression> include6, Expression>> include7); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression> include4, Expression>> include5, Expression> include6, Expression> include7); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression>> include4, Expression> include5, Expression>> include6, Expression>> include7); + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression>> include4, Expression>> include5, Expression>> include6, Expression>> include7); + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression> include4, Expression> include5, Expression> include6, Expression>> include7); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression>> include4, Expression> include5, Expression>> include6, Expression> include7); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression>> include4, Expression> include5, Expression> include6, Expression>> include7); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression> include4, Expression>> include5, Expression>> include6, Expression> include7); + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression> include4, Expression>> include5, Expression>> include6, Expression> include7); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression>> include4, Expression>> include5, Expression>> include6, Expression>> include7); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression>> include4, Expression> include5, Expression>> include6, Expression>> include7); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression> include4, Expression> include5, Expression>> include6, Expression>> include7); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression> include4, Expression>> include5, Expression>> include6, Expression> include7); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression> include4, Expression> include5, Expression>> include6, Expression>> include7); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression> include4, Expression> include5, Expression>> include6, Expression>> include7); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression>> include4, Expression> include5, Expression>> include6, Expression> include7); + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression> include4, Expression> include5, Expression>> include6, Expression>> include7); + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression> include4, Expression>> include5, Expression> include6, Expression>> include7); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression>> include4, Expression>> include5, Expression>> include6, Expression> include7); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression> include4, Expression>> include5, Expression> include6, Expression>> include7); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression>> include4, Expression> include5, Expression> include6, Expression> include7); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression>> include4, Expression> include5, Expression> include6, Expression>> include7); + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression> include4, Expression> include5, Expression>> include6, Expression> include7); + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression>> include4, Expression>> include5, Expression> include6, Expression>> include7); + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression>> include4, Expression>> include5, Expression>> include6, Expression>> include7); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression>> include4, Expression> include5, Expression> include6, Expression> include7); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression> include4, Expression> include5, Expression> include6, Expression>> include7); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression> include4, Expression> include5, Expression> include6, Expression> include7); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression> include4, Expression> include5, Expression> include6, Expression>> include7); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression> include4, Expression> include5, Expression> include6, Expression> include7); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression> include4, Expression>> include5, Expression>> include6, Expression>> include7); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression>> include4, Expression>> include5, Expression> include6, Expression> include7); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression> include4, Expression> include5, Expression> include6, Expression>> include7); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression> include4, Expression>> include5, Expression>> include6, Expression>> include7); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression>> include4, Expression> include5, Expression>> include6, Expression>> include7); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression>> include4, Expression>> include5, Expression> include6, Expression>> include7); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression> include4, Expression>> include5, Expression> include6, Expression>> include7); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression>> include4, Expression>> include5, Expression> include6, Expression>> include7); + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression> include4, Expression>> include5, Expression>> include6, Expression> include7); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression> include4, Expression> include5, Expression> include6, Expression> include7); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression>> include4, Expression>> include5, Expression> include6, Expression>> include7); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression>> include4, Expression> include5, Expression>> include6, Expression> include7); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression>> include4, Expression>> include5, Expression> include6, Expression> include7); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression> include4, Expression>> include5, Expression>> include6, Expression> include7); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression> include4, Expression>> include5, Expression> include6, Expression> include7); + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression> include4, Expression>> include5, Expression>> include6, Expression>> include7); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression> include4, Expression>> include5, Expression>> include6, Expression>> include7); + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression> include4, Expression> include5, Expression> include6, Expression> include7); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression> include4, Expression>> include5, Expression> include6, Expression> include7); + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression>> include4, Expression>> include5, Expression> include6, Expression> include7); + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression> include4, Expression> include5, Expression>> include6, Expression>> include7); + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression>> include4, Expression> include5, Expression>> include6, Expression>> include7); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression>> include4, Expression> include5, Expression>> include6, Expression> include7); + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression>> include4, Expression>> include5, Expression>> include6, Expression> include7); + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression>> include4, Expression> include5, Expression> include6, Expression>> include7); + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression> include4, Expression>> include5, Expression> include6, Expression> include7); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression>> include4, Expression> include5, Expression> include6, Expression> include7); + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression>> include4, Expression> include5, Expression> include6, Expression> include7); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression> include4, Expression> include5, Expression> include6, Expression> include7); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression> include4, Expression>> include5, Expression> include6, Expression>> include7); + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression>> include4, Expression> include5, Expression> include6, Expression>> include7); + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression>> include4, Expression> include5, Expression>> include6, Expression>> include7); + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression> include4, Expression>> include5, Expression>> include6, Expression>> include7); + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression>> include4, Expression> include5, Expression>> include6, Expression> include7); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression>> include4, Expression>> include5, Expression>> include6, Expression>> include7); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression> include4, Expression>> include5, Expression> include6, Expression>> include7); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression> include4, Expression> include5, Expression>> include6, Expression>> include7); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression> include4, Expression> include5, Expression> include6, Expression> include7); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression> include4, Expression> include5, Expression>> include6, Expression> include7); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression> include4, Expression> include5, Expression> include6, Expression>> include7); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression>> include4, Expression>> include5, Expression>> include6, Expression> include7); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression> include4, Expression> include5, Expression> include6, Expression>> include7); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression>> include4, Expression>> include5, Expression> include6, Expression> include7); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression>> include4, Expression> include5, Expression>> include6, Expression>> include7); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression>> include4, Expression>> include5, Expression>> include6, Expression>> include7); + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression> include3, Expression>> include4, Expression>> include5, Expression>> include6, Expression> include7); + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression> include4, Expression> include5, Expression> include6, Expression> include7); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression>> include4, Expression> include5, Expression> include6, Expression>> include7); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression> include3, Expression> include4, Expression>> include5, Expression>> include6, Expression>> include7); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression> include3, Expression> include4, Expression>> include5, Expression> include6, Expression> include7); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression>> include4, Expression> include5, Expression>> include6, Expression> include7); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression>> include4, Expression> include5, Expression>> include6, Expression>> include7); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression> include3, Expression>> include4, Expression> include5, Expression> include6, Expression>> include7); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression>> include4, Expression>> include5, Expression>> include6, Expression>> include7); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression> include4, Expression>> include5, Expression> include6, Expression>> include7); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression>> include4, Expression> include5, Expression> include6, Expression>> include7); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression> include4, Expression>> include5, Expression> include6, Expression> include7); + NavQueryableProvider Includes(Expression> include1, Expression> include2, Expression>> include3, Expression> include4, Expression>> include5, Expression>> include6, Expression>> include7); + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression> include4, Expression>> include5, Expression> include6, Expression>> include7); + NavQueryableProvider Includes(Expression>> include1, Expression> include2, Expression>> include3, Expression>> include4, Expression> include5, Expression> include6, Expression> include7); + NavQueryableProvider Includes(Expression>> include1, Expression>> include2, Expression>> include3, Expression> include4, Expression> include5, Expression> include6, Expression> include7); + NavQueryableProvider Includes(Expression> include1, Expression>> include2, Expression>> include3, Expression>> include4, Expression> include5, Expression>> include6, Expression> include7); + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IIncludes.txt b/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IIncludes.txt new file mode 100644 index 000000000..df013e689 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IIncludes.txt @@ -0,0 +1,106 @@ + +List sourceNumbers = new List { 2, 4, 8, 16, 32, 64, 128 }; +var ram = new Random(); +foreach (var source in sourceNumbers) +{ + var index = sourceNumbers.IndexOf(source) + 1; + //Console.WriteLine("index=" + index); + + List> result = new List>(); + + string sb = ""; + List sb2 = new List(); + for (int i = 1; i <= source; i++) + { + + List test = new List(); + + List test2 = new List(); + for (int j = 1; j <= index; j++) + { + + test2.Add(new Test() { i = j, b = ram.Next(1, 222) % 2 == 0 }); + + + sb += $"[{test2.Last().b}:{test2.Last().i}]"; + //sb2 += $"[{test2.Last().b}:{test2.Last().i}]"; + if (j == index) + { + if (sb2.Count > 0 && sb2.Contains(sb)) + { + j = 0; + sb = ""; + test2 = new List(); + } + else + { + test.AddRange(test2.Select(x => new Test { b = x.b, i = x.i })); + sb2.Add(sb); + sb = ""; + } + } + + } + + test = test.OrderBy(it => it.i).ToList(); + result.Add(test); + + } + foreach (var test in result) + { + List res = new List(); + List res2 = new List(); + + foreach (var item in test) + { + if (item.i == 1) + { + if (item.b == false) + { + res.Add($"Expression>> include{item.i}"); + } + else + { + res.Add($"Expression> include{item.i}"); + } + + } + else + { + if (item.b == false) + { + res.Add($"Expression>> include{item.i}"); + } + else + { + res.Add($"Expression> include{item.i}"); + } + } + res2.Add("TReturn" + (item.i)); + } + + Console.WriteLine($"ISugarQueryable Includes<{string.Join(",", res2)}>({string.Join(",", res)});"); + } + //Console.WriteLine("--"); +} + +Console.ReadKey(); + +public class Test +{ + public bool b { get; set; } + public int i { get; set; } +} + + +//1:1 true + +//2: +// 1 true , 2 true +// 1 false , 2 true + +//3: +// 1 true , 2 true 3 true +// 1 true , 2 false 3 true +// 1 false , 2 false 3 true +// 1 false , 2 true 3 true diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/ILambdaExpressions.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/ILambdaExpressions.cs new file mode 100644 index 000000000..b5a304e3e --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/ILambdaExpressions.cs @@ -0,0 +1,33 @@ +using System.Linq.Expressions; +namespace SqlSugar +{ + public partial interface ILambdaExpressions + { + MappingColumnList MappingColumns { get; set; } + MappingTableList MappingTables { get; set; } + IgnoreColumnList IgnoreComumnList { get; set; } + List SqlFuncServices { get; set; } + + List JoinQueryInfos { get; set; } + bool IsSingle { get; set; } + SqlSugarProvider Context { get; set; } + IDbMethods DbMehtods { get; set; } + Expression Expression { get; set; } + int Index { get; set; } + int ParameterIndex { get; set; } + List Parameters { get; set; } + ExpressionResult Result { get; set; } + string SqlParameterKeyWord { get; } + string SingleTableNameSubqueryShortName { get; set; } + Action InitMappingInfo { get; set; } + Action RefreshMapping { get; set; } + bool PgSqlIsAutoToLower { get; set; } + Expression RootExpression { get; set; } + ExpressionOutParameter SugarContext { get; set; } + bool? TableEnumIsString { get; set; } + + string GetAsString(string fieldName, string fieldValue); + void Resolve(Expression expression, ResolveExpressType resolveType); + void Clear(); + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IParameterInsertable.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IParameterInsertable.cs new file mode 100644 index 000000000..5a6c42a7b --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IParameterInsertable.cs @@ -0,0 +1,8 @@ +namespace SqlSugar +{ + public interface IParameterInsertable + { + int ExecuteCommand(); + Task ExecuteCommandAsync(); + } +} \ No newline at end of file diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IQueryable.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IQueryable.cs new file mode 100644 index 000000000..d94a9ef0d --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IQueryable.cs @@ -0,0 +1,1970 @@ +using System.Data; +using System.Linq.Expressions; + +namespace SqlSugar +{ + public partial interface ISugarQueryable + { + SqlSugarProvider Context { get; set; } + ISqlBuilder SqlBuilder { get; set; } + QueryBuilder QueryBuilder { get; set; } + bool IsCache { get; set; } + int CacheTime { get; set; } + ISugarQueryable Clone(); + ISugarQueryable Hints(string hints); + ISugarQueryable AS(string tableName); + ISugarQueryable AS(string tableName); + ISugarQueryable IF(bool condition, Action> action); + ISugarQueryable AsWithAttr(); + ISugarQueryable AsType(Type tableNameType); + ISugarQueryable Cast(); + ISugarQueryable With(string withString); + //ISugarQueryable CrossQueryWithAttr(); + ISugarQueryable CrossQuery(string configId); + ISugarQueryable CrossQuery(Type type, string configId); + ISugarQueryable IncludeLeftJoin(Expression> leftObjectExp); + ISugarQueryable IncludeInnerJoin(Expression> innerObjectExp); + ISugarQueryable IncludeRightJoin(Expression> rightObjectExp); + ISugarQueryable IncludeFullJoin(Expression> fullObjectExp); + ISugarQueryable LeftJoin(ISugarQueryable joinQueryable, Expression> joinExpression); + ISugarQueryable LeftJoinIF(bool isJoin, ISugarQueryable joinQueryable, Expression> joinExpression); + ISugarQueryable InnerJoinIF(bool isJoin, ISugarQueryable joinQueryable, Expression> joinExpression); + ISugarQueryable InnerJoin(ISugarQueryable joinQueryable, Expression> joinExpression); + ISugarQueryable RightJoin(ISugarQueryable joinQueryable, Expression> joinExpression); + ISugarQueryable FullJoin(ISugarQueryable joinQueryable, Expression> joinExpression); + ISugarQueryable LeftJoin(Expression> joinExpression); + ISugarQueryable LeftJoinIF(bool isLeftJoin, Expression> joinExpression); + ISugarQueryable InnerJoinIF(bool isJoin, Expression> joinExpression); + ISugarQueryable LeftJoin(Expression> joinExpression, string tableName); + ISugarQueryable FullJoin(Expression> joinExpression); + ISugarQueryable FullJoin(Expression> joinExpression, string tableName); + ISugarQueryable InnerJoin(Expression> joinExpression); + ISugarQueryable InnerJoin(Expression> joinExpression, string tableName); + ISugarQueryable RightJoin(Expression> joinExpression); + ISugarQueryable RightJoin(Expression> joinExpression, string tableName); + ISugarQueryable Filter(string FilterName, bool isDisabledGobalFilter = false); + ISugarQueryable ClearFilter(params Type[] types); + ISugarQueryable ClearFilter(); + ISugarQueryable ClearFilter(); + ISugarQueryable ClearFilter(); + ISugarQueryable ClearFilter(); + ISugarQueryable Filter(Type type); + ISugarQueryable Mapper(Action mapperAction); + ISugarQueryable Mapper(Expression> expression); + ISugarQueryable Mapper(Action> mapperAction); + ISugarQueryable Mapper(Expression> mapperObject, Expression> mainField, Expression> childField); + ISugarQueryable Mapper(Expression>> mapperObject, Expression> mainField, Expression> childField); + ISugarQueryable Mapper(Expression> mapperObject, Expression> mapperField); + ISugarQueryable Mapper(Expression>> mapperObject, Expression> mapperField); + ISugarQueryable AddParameters(object parameters); + ISugarQueryable AddParameters(SugarParameter[] parameters); + ISugarQueryable AddParameters(List parameters); + ISugarQueryable AddJoinInfo(string tableName, string shortName, string joinWhere, JoinType type = JoinType.Left); + ISugarQueryable AddJoinInfo(Type JoinEntityType, string shortName, string joinWhere, JoinType type = JoinType.Left); + ISugarQueryable AddJoinInfo(Type JoinEntityType, Dictionary keyIsShortName_ValueIsType_Dictionary, FormattableString onExpString, JoinType type = JoinType.Left); + /// + /// if a property that is not empty is a condition + /// + /// + /// + /// + ISugarQueryable WhereClass(ClassType whereClass, bool ignoreDefaultValue = false) where ClassType : class, new(); + /// + /// if a property that is not empty is a condition + /// + /// + /// + /// + ISugarQueryable WhereClass(List whereClassList, bool ignoreDefaultValue = false) where ClassType : class, new(); + ISugarQueryable WhereClassByPrimaryKey(List list); + ISugarQueryable WhereClassByWhereColumns(List list, string[] whereColumns); + ISugarQueryable WhereClassByPrimaryKey(T data); + ISugarQueryable WhereColumns(List> columns); + ISugarQueryable WhereColumns(Dictionary columns, bool ignoreDefaultValue); + ISugarQueryable WhereColumns(Dictionary columns); + ISugarQueryable TranLock(DbLockType? LockType = DbLockType.Wait); + ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(string expShortName, FormattableString expressionString); + ISugarQueryable Where(Dictionary keyIsShortName_ValueIsType_Dictionary, FormattableString expressionString); + ISugarQueryable Where(string whereString, object parameters = null); + ISugarQueryable Where(IFuncModel funcModel); + ISugarQueryable Where(List conditionalModels); + ISugarQueryable Where(List conditionalModels, bool isWrap); + ISugarQueryable Where(string fieldName, string conditionalType, object fieldValue); + ISugarQueryable Having(Expression> expression); + ISugarQueryable HavingIF(bool isHaving, Expression> expression); + ISugarQueryable Having(string whereString, object parameters = null); + + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, string whereString, object parameters = null); + + T InSingle(object pkValue); + Task InSingleAsync(object pkValue); + ISugarQueryable In(params TParamter[] pkValues); + ISugarQueryable InIF(bool isIn, string fieldName, params TParamter[] pkValues); + ISugarQueryable InIF(bool isIn, params TParamter[] pkValues); + ISugarQueryable In(string InFieldName, params FieldType[] inValues); + ISugarQueryable In(Expression> expression, params FieldType[] inValues); + ISugarQueryable In(List pkValues); + ISugarQueryable In(string InFieldName, List inValues); + ISugarQueryable In(Expression> expression, List inValues); + ISugarQueryable In(Expression> expression, ISugarQueryable childQueryExpression); + ISugarQueryable InIF(bool isWhere, Expression> expression, ISugarQueryable childQueryExpression); + + ISugarQueryable OrderBy(string orderByFields); + ISugarQueryable OrderByPropertyName(string orderPropertyName, OrderByType? orderByType = null); + ISugarQueryable OrderByPropertyNameIF(bool isOrderBy, string orderPropertyName, OrderByType? orderByType = null); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(string expShortName, FormattableString expOrderBy, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByDescending(Expression> expression); + ISugarQueryable OrderByIF(bool isOrderBy, string orderByFields); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable SampleBy(int timeNumber, SampleByUnit timeType); + ISugarQueryable SampleBy(int timeNumber, string timeType); + + + ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(string groupFileds); + ISugarQueryable GroupByIF(bool isGroupBy, Expression> expression); + + ISugarQueryable GroupByIF(bool isGroupBy, string groupFields); + + ISugarQueryable PartitionBy(Expression> expression); + ISugarQueryable PartitionBy(string groupFileds); + + ISugarQueryable Skip(int index); + ISugarQueryable Take(int num); + ISugarQueryable Distinct(); + + T Single(); + Task SingleAsync(); + T Single(Expression> expression); + Task SingleAsync(Expression> expression); + + T First(); + Task FirstAsync(); + Task FirstAsync(CancellationToken token); + T First(Expression> expression); + Task FirstAsync(Expression> expression); + Task FirstAsync(Expression> expression, CancellationToken token); + + bool Any(Expression> expression); + Task AnyAsync(Expression> expression); + Task AnyAsync(Expression> expression, CancellationToken token); + bool Any(); + Task AnyAsync(); + ISugarQueryable Select(string expShortName, FormattableString expSelect, Type resultType); + ISugarQueryable Select(Dictionary keyIsShortName_ValueIsType_Dictionary, FormattableString expSelect, Type resultType); + ISugarQueryable Select(string expShortName, FormattableString expSelect, Type EntityType, Type resultType); + ISugarQueryable Select(string expShortName, FormattableString expSelect, Type resultType); + ISugarQueryable Select(Expression expression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable SelectIF(bool condition, Expression> trueSelectExpression, Expression> falseSelectExpression); + ISugarQueryable Select(Expression> expression, bool isAutoFill); + ISugarQueryable Select(); + ISugarQueryable Select(string select); + ISugarQueryable Select(string select); + ISugarQueryable SelectMergeTable(Expression> expression); + ISugarQueryable MergeTable(); + void ForEachDataReader(Action action); + Task ForEachDataReaderAsync(Action action); + void ForEach(Action action, int singleMaxReads = 300, System.Threading.CancellationTokenSource cancellationTokenSource = null); + Task ForEachAsync(Action action, int singleMaxReads = 300, System.Threading.CancellationTokenSource cancellationTokenSource = null); + void ForEachByPage(Action action, int pageIndex, int pageSize, ref int totalNumber, int singleMaxReads = 300, System.Threading.CancellationTokenSource cancellationTokenSource = null); + Task ForEachByPageAsync(Action action, int pageIndex, int pageSize, RefAsync totalNumber, int singleMaxReads = 300, System.Threading.CancellationTokenSource cancellationTokenSource = null); + int Count(); + Task CountAsync(); + Task CountAsync(CancellationToken token); + int Count(Expression> expression); + Task CountAsync(Expression> expression); + Task CountAsync(Expression> expression, CancellationToken token); + TResult Max(string maxField); + Task MaxAsync(string maxField); + Task MaxAsync(string maxField, CancellationToken token); + TResult Max(Expression> expression); + Task MaxAsync(Expression> expression); + Task MaxAsync(Expression> expression, CancellationToken token); + TResult Min(string minField); + Task MinAsync(string minField); + TResult Min(Expression> expression); + Task MinAsync(Expression> expression); + TResult Sum(string sumField); + Task SumAsync(string sumField); + TResult Sum(Expression> expression); + Task SumAsync(Expression> expression); + TResult Avg(string avgField); + Task AvgAsync(string avgField); + TResult Avg(Expression> expression); + Task AvgAsync(Expression> expression); + List ToList(Expression> expression); + Task> ToListAsync(Expression> expression); + List ToList(); + + int IntoTable(); + int IntoTable(Type TableEntityType); + int IntoTable(string tableName); + int IntoTable(Type TableEntityType, string tableName); + + Task IntoTableAsync(CancellationToken cancellationToken = default); + Task IntoTableAsync(Type TableEntityType, CancellationToken cancellationToken = default); + Task IntoTableAsync(string tableName, CancellationToken cancellationToken = default); + Task IntoTableAsync(Type TableEntityType, string tableName, CancellationToken cancellationToken = default); + + //bool IntoTable(Type TableEntityType, params string[] columnNameList); + //bool IntoTable(params string[] columnNameList); + List SetContext(Expression> whereExpression, ParameterT parameter); + List SetContext(Expression> thisFiled, Expression> mappingFiled, ParameterT parameter); + List SetContext(Expression> thisFiled1, Expression> mappingFiled1, Expression> thisFiled2, Expression> mappingFiled2, ParameterT parameter); + Task> SetContextAsync(Expression> thisFiled, Expression> mappingFiled, ParameterT parameter); + Task> SetContextAsync(Expression> thisFiled1, Expression> mappingFiled1, Expression> thisFiled2, Expression> mappingFiled2, ParameterT parameter); + Dictionary ToDictionary(Expression> key, Expression> value); + Dictionary ToDictionary(Expression> key, Expression> value); + Task> ToDictionaryAsync(Expression> key, Expression> value); + Task> ToDictionaryAsync(Expression> key, Expression> value); + List> ToDictionaryList(); + Task>> ToDictionaryListAsync(); + + T[] ToArray(); + Task ToArrayAsync(); + Task> ToListAsync(); + Task> ToListAsync(CancellationToken token); + + string ToJson(); + Task ToJsonAsync(); + string ToJsonPage(int pageIndex, int pageSize); + Task ToJsonPageAsync(int pageIndex, int pageSize); + string ToJsonPage(int pageIndex, int pageSize, ref int totalNumber); + Task ToJsonPageAsync(int pageIndex, int pageSize, RefAsync totalNumber); + KeyValuePair> ToSql(); + string ToSqlString(); + List ToChildList(Expression> parentIdExpression, object primaryKeyValue, bool isContainOneself = true); + List ToChildList(Expression> parentIdExpression, object[] primaryKeyValues, bool isContainOneself = true); + Task> ToChildListAsync(Expression> parentIdExpression, object primaryKeyValue, bool isContainOneself = true); + Task> ToChildListAsync(Expression> parentIdExpression, object[] primaryKeyValues, bool isContainOneself = true); + List ToParentList(Expression> parentIdExpression, object primaryKeyValue); + List ToParentList(Expression> parentIdExpression, object primaryKeyValue, Expression> parentWhereExpression); + + Task> ToParentListAsync(Expression> parentIdExpression, object primaryKeyValue); + Task> ToParentListAsync(Expression> parentIdExpression, object primaryKeyValue, Expression> parentWhereExpression); + List ToTree(string childPropertyName, string parentIdPropertyName, object rootValue, string primaryKeyPropertyName); + List ToTree(Expression>> childListExpression, Expression> parentIdExpression, object rootValue); + List ToTree(Expression>> childListExpression, Expression> parentIdExpression, object rootValue, Expression> primaryKeyExpression); + Task> ToTreeAsync(string childPropertyName, string parentIdPropertyName, object rootValue, string primaryKeyPropertyName); + Task> ToTreeAsync(Expression>> childListExpression, Expression> parentIdExpression, object rootValue); + Task> ToTreeAsync(Expression>> childListExpression, Expression> parentIdExpression, object rootValue, Expression> primaryKeyExpression); + List ToTree(Expression>> childListExpression, Expression> parentIdExpression, object rootValue, object[] childIds); + List ToTree(Expression>> childListExpression, Expression> parentIdExpression, object rootValue, object[] childIds, Expression> primaryKeyExpression); + Task> ToTreeAsync(Expression>> childListExpression, Expression> parentIdExpression, object rootValue, object[] childIds); + Task> ToTreeAsync(Expression>> childListExpression, Expression> parentIdExpression, object rootValue, object[] childIds, Expression> primaryKeyExpression); + DataTable ToDataTable(); + DataTable ToDataTableByEntity(); + Task ToDataTableAsync(); + Task ToDataTableByEntityAsync(); + DataTable ToDataTablePage(int pageNumber, int pageSize); + Task ToDataTablePageAsync(int pageNumber, int pageSize); + DataTable ToDataTablePage(int pageNumber, int pageSize, ref int totalNumber); + DataTable ToDataTableByEntityPage(int pageNumber, int pageSize, ref int totalNumber); + DataTable ToDataTablePage(int pageNumber, int pageSize, ref int totalNumber, ref int totalPage); + Task ToDataTablePageAsync(int pageNumber, int pageSize, RefAsync totalNumber); + Task ToDataTableByEntityPageAsync(int pageNumber, int pageSize, RefAsync totalNumber); + + + DataTable ToOffsetDataTablePage(int pageNumber, int pageSize); + Task ToOffsetDataTablePageAsync(int pageNumber, int pageSize); + DataTable ToOffsetDataTablePage(int pageNumber, int pageSize, ref int totalNumber); + DataTable ToOffsetDataTableByEntityPage(int pageNumber, int pageSize, ref int totalNumber); + DataTable ToOffsetDataTablePage(int pageNumber, int pageSize, ref int totalNumber, ref int totalPage); + Task ToOffsetDataTablePageAsync(int pageNumber, int pageSize, RefAsync totalNumber); + Task ToOffsetDataTableByEntityPageAsync(int pageNumber, int pageSize, RefAsync totalNumber); + + + List ToOffsetPage(int pageNumber, int pageSize); + List ToOffsetPage(int pageNumber, int pageSize, ref int totalNumber); + List ToOffsetPage(int pageNumber, int pageSize, ref int totalNumber, ref int totalPage); + Task> ToOffsetPageAsync(int pageNumber, int pageSize); + Task> ToOffsetPageAsync(int pageNumber, int pageSize, RefAsync totalNumber); + Task> ToOffsetPageAsync(int pageNumber, int pageSize, RefAsync totalNumber, RefAsync totalPage); + Task> ToOffsetPageAsync(int pageNumber, int pageSize, RefAsync totalNumber, CancellationToken token); + Task> ToOffsetPageAsync(int pageNumber, int pageSize, RefAsync totalNumber, RefAsync totalPage, CancellationToken token); + List ToPageList(int pageNumber, int pageSize); + Task> ToPageListAsync(int pageNumber, int pageSize); + Task> ToPageListAsync(int pageNumber, int pageSize, CancellationToken token); + List ToPageList(int pageNumber, int pageSize, ref int totalNumber); + List ToPageList(int pageNumber, int pageSize, ref int totalNumber, Expression> expression); + List ToPageList(int pageNumber, int pageSize, ref int totalNumber, ref int totalPage); + Task> ToPageListAsync(int pageNumber, int pageSize, RefAsync totalNumber); + Task> ToPageListAsync(int pageNumber, int pageSize, RefAsync totalNumber, CancellationToken token); + Task> ToPageListAsync(int pageNumber, int pageSize, RefAsync totalNumber, Expression> expression); + Task> ToPageListAsync(int pageNumber, int pageSize, RefAsync totalNumber, RefAsync totalPage); + Task> ToPageListAsync(int pageNumber, int pageSize, RefAsync totalNumber, RefAsync totalPage, CancellationToken token); + ISugarQueryable WithCache(string cacheKey, int cacheDurationInSeconds = int.MaxValue); + ISugarQueryable WithCache(int cacheDurationInSeconds = int.MaxValue); + ISugarQueryable WithCacheIF(bool isCache, int cacheDurationInSeconds = int.MaxValue); + string ToClassString(string className); + void Clear(); + void AddQueue(); + ISugarQueryable IgnoreColumns(Expression> columns); + ISugarQueryable IgnoreColumns(params string[] columns); + + #region 内存行转列 + + #region 同步 + DataTable ToPivotTable(Func columnSelector, Expression> rowSelector, Func, TData> dataSelector); + List ToPivotList(Func columnSelector, Expression> rowSelector, Func, TData> dataSelector); + IEnumerable ToPivotEnumerable(Func columnSelector, Expression> rowSelector, Func, TData> dataSelector); + string ToPivotJson(Func columnSelector, Expression> rowSelector, Func, TData> dataSelector); + #endregion + + #region 异步 + Task ToPivotTableAsync(Func columnSelector, Expression> rowSelector, Func, TData> dataSelector); + Task> ToPivotListAsync(Func columnSelector, Expression> rowSelector, Func, TData> dataSelector); + Task> ToPivotEnumerableAsync(Func columnSelector, Expression> rowSelector, Func, TData> dataSelector); + Task ToPivotJsonAsync(Func columnSelector, Expression> rowSelector, Func, TData> dataSelector); + #endregion + + #endregion + + ISugarQueryable SplitTable(Func, IEnumerable> getTableNamesFunc); + ISugarQueryable SplitTable(DateTime beginTime, DateTime endTime); + ISugarQueryable SplitTable(); + } + public partial interface ISugarQueryable : ISugarQueryable + { + new ISugarQueryable IF(bool condition, Action> action); + new ISugarQueryable Hints(string hints); + new ISugarQueryable SampleBy(int timeNumber, SampleByUnit timeType); + new ISugarQueryable SampleBy(int timeNumber, string timeType); + ISugarQueryable SelectMergeTable(Expression> expression); + ISugarQueryable LeftJoinIF(bool isLeftJoin, Expression> joinExpression); + ISugarQueryable InnerJoinIF(bool isJoin, Expression> joinExpression); + ISugarQueryable LeftJoin(ISugarQueryable joinQueryable, Expression> joinExpression); + ISugarQueryable LeftJoinIF(bool isJoin, ISugarQueryable joinQueryable, Expression> joinExpression); + ISugarQueryable InnerJoinIF(bool isJoin, ISugarQueryable joinQueryable, Expression> joinExpression); + ISugarQueryable InnerJoin(ISugarQueryable joinQueryable, Expression> joinExpression); + ISugarQueryable RightJoin(ISugarQueryable joinQueryable, Expression> joinExpression); + ISugarQueryable FullJoin(ISugarQueryable joinQueryable, Expression> joinExpression); + ISugarQueryable LeftJoin(Expression> joinExpression); + ISugarQueryable FullJoin(Expression> joinExpression); + ISugarQueryable InnerJoin(Expression> joinExpression); + ISugarQueryable RightJoin(Expression> joinExpression); + + ISugarQueryable LeftJoinIF(bool isJoin, Expression> joinExpression, string tableName); + ISugarQueryable FullJoinIF(bool isJoin, Expression> joinExpression, string tableName); + ISugarQueryable InnerJoinIF(bool isJoin, Expression> joinExpression, string tableName); + ISugarQueryable RightJoinIF(bool isJoin, Expression> joinExpression, string tableName); + + ISugarQueryable LeftJoin(Expression> joinExpression, string tableName); + ISugarQueryable FullJoin(Expression> joinExpression, string tableName); + ISugarQueryable InnerJoin(Expression> joinExpression, string tableName); + ISugarQueryable RightJoin(Expression> joinExpression, string tableName); + #region Where + new ISugarQueryable Where(string expShortName, FormattableString expressionString); + new ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + new ISugarQueryable Where(List conditionalModels); + new ISugarQueryable Where(List conditionalModels, bool isWrap); + new ISugarQueryable Where(IFuncModel funcModel); + + new ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + + new ISugarQueryable Where(string whereString, object whereObj = null); + new ISugarQueryable WhereIF(bool isWhere, string whereString, object whereObj = null); + /// + /// if a property that is not empty is a condition + /// + /// + /// + /// + new ISugarQueryable WhereClass(ClassType whereClass, bool ignoreDefaultValue = false) where ClassType : class, new(); + /// + /// if a property that is not empty is a condition + /// + /// + /// + /// + new ISugarQueryable WhereClass(List whereClassList, bool ignoreDefaultValue = false) where ClassType : class, new(); + #endregion + + #region Select + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression, bool isAutoFill); + ISugarQueryable SelectIF(bool condition, Expression> trueSelectExpression, Expression> falseSelectExpression); + + #endregion + + #region OrderBy + new ISugarQueryable OrderBy(List models); + new ISugarQueryable OrderByPropertyName(string orderPropertyName, OrderByType? orderByType = null); + new ISugarQueryable OrderByPropertyNameIF(bool isOrderBy, string orderPropertyName, OrderByType? orderByType = null); + new ISugarQueryable OrderByDescending(Expression> expression); + ISugarQueryable OrderByDescending(Expression> expression); + new ISugarQueryable OrderBy(string orderByFields); + new ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + new ISugarQueryable OrderByIF(bool isOrderBy, string orderByFields); + new ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + #endregion + + #region GroupBy + new ISugarQueryable PartitionBy(Expression> expression); + ISugarQueryable PartitionBy(Expression> expression); + new ISugarQueryable PartitionBy(string groupFileds); + new ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + new ISugarQueryable GroupByIF(bool isGroupBy, Expression> expression); + ISugarQueryable GroupByIF(bool isGroupBy, Expression> expression); + new ISugarQueryable GroupByIF(bool isGroupBy, string groupFields); + new ISugarQueryable Having(Expression> expression); + ISugarQueryable Having(Expression> expression); + new ISugarQueryable Having(string whereString, object whereObj = null); + new ISugarQueryable HavingIF(bool isHaving, Expression> expression); + ISugarQueryable HavingIF(bool isHaving, Expression> expression); + #endregion + + #region Aggr + TResult Max(Expression> expression); + TResult Min(Expression> expression); + TResult Sum(Expression> expression); + TResult Avg(Expression> expression); + Task MaxAsync(Expression> expression); + Task MinAsync(Expression> expression); + Task SumAsync(Expression> expression); + Task AvgAsync(Expression> expression); + #endregion + + #region In + new ISugarQueryable In(Expression> expression, params FieldType[] inValues); + new ISugarQueryable In(Expression> expression, List inValues); + new ISugarQueryable In(Expression> expression, ISugarQueryable childQueryExpression); + + ISugarQueryable In(Expression> expression, params FieldType[] inValues); + ISugarQueryable In(Expression> expression, List inValues); + ISugarQueryable In(Expression> expression, ISugarQueryable childQueryExpression); + new ISugarQueryable InIF(bool isIn, params TParamter[] pkValues); + new ISugarQueryable InIF(bool isIn, string fieldName, params TParamter[] pkValues); + #endregion + + #region Other + new ISugarQueryable Take(int num); + new ISugarQueryable Clone(); + new ISugarQueryable AS(string tableName); + new ISugarQueryable AS(string tableName); + new ISugarQueryable ClearFilter(); + new ISugarQueryable ClearFilter(params Type[] types); + new ISugarQueryable ClearFilter(); + new ISugarQueryable ClearFilter(); + new ISugarQueryable ClearFilter(); + new ISugarQueryable Filter(string FilterName, bool isDisabledGobalFilter = false); + new ISugarQueryable AddParameters(object parameters); + new ISugarQueryable AddParameters(SugarParameter[] parameters); + new ISugarQueryable AddParameters(List parameters); + new ISugarQueryable AddJoinInfo(string tableName, string shortName, string joinWhere, JoinType type = JoinType.Left); + new ISugarQueryable With(string withString); + new ISugarQueryable WithCache(int cacheDurationInSeconds = int.MaxValue); + new ISugarQueryable WithCacheIF(bool isCache, int cacheDurationInSeconds = int.MaxValue); + new ISugarQueryable Distinct(); + bool Any(Expression> expression); + #endregion + } + public partial interface ISugarQueryable : ISugarQueryable + { + new ISugarQueryable IF(bool condition, Action> action); + new ISugarQueryable Hints(string hints); + new ISugarQueryable SampleBy(int timeNumber, SampleByUnit timeType); + new ISugarQueryable SampleBy(int timeNumber, string timeType); + ISugarQueryable SelectMergeTable(Expression> expression); + ISugarQueryable LeftJoinIF(bool isLeftJoin, Expression> joinExpression); + ISugarQueryable InnerJoinIF(bool isJoin, Expression> joinExpression); + ISugarQueryable LeftJoin(ISugarQueryable joinQueryable, Expression> joinExpression); + ISugarQueryable LeftJoinIF(bool isJoin, ISugarQueryable joinQueryable, Expression> joinExpression); + ISugarQueryable InnerJoinIF(bool isJoin, ISugarQueryable joinQueryable, Expression> joinExpression); + ISugarQueryable InnerJoin(ISugarQueryable joinQueryable, Expression> joinExpression); + ISugarQueryable RightJoin(ISugarQueryable joinQueryable, Expression> joinExpression); + ISugarQueryable FullJoin(ISugarQueryable joinQueryable, Expression> joinExpression); + ISugarQueryable LeftJoin(Expression> joinExpression); + ISugarQueryable FullJoin(Expression> joinExpression); + ISugarQueryable InnerJoin(Expression> joinExpression); + ISugarQueryable RightJoin(Expression> joinExpression); + ISugarQueryable LeftJoin(Expression> joinExpression, string tableName); + ISugarQueryable FullJoin(Expression> joinExpression, string tableName); + ISugarQueryable InnerJoin(Expression> joinExpression, string tableName); + ISugarQueryable RightJoin(Expression> joinExpression, string tableName); + + ISugarQueryable LeftJoinIF(bool isJoin, Expression> joinExpression, string tableName); + ISugarQueryable FullJoinIF(bool isJoin, Expression> joinExpression, string tableName); + ISugarQueryable InnerJoinIF(bool isJoin, Expression> joinExpression, string tableName); + ISugarQueryable RightJoinIF(bool isJoin, Expression> joinExpression, string tableName); + #region Where + new ISugarQueryable Where(string expShortName, FormattableString expressionString); + new ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + new ISugarQueryable Where(List conditionalModels); + new ISugarQueryable Where(List conditionalModels, bool isWrap); + new ISugarQueryable Where(IFuncModel funcModel); + new ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + + new ISugarQueryable Where(string whereString, object parameters = null); + new ISugarQueryable WhereIF(bool isWhere, string whereString, object parameters = null); + + /// + /// if a property that is not empty is a condition + /// + /// + /// + /// + new ISugarQueryable WhereClass(ClassType whereClass, bool ignoreDefaultValue = false) where ClassType : class, new(); + /// + /// if a property that is not empty is a condition + /// + /// + /// + /// + new ISugarQueryable WhereClass(List whereClassList, bool ignoreDefaultValue = false) where ClassType : class, new(); + #endregion + + #region Select + ISugarQueryable Select(Expression> expression, bool isAutoFill); + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable SelectIF(bool condition, Expression> trueSelectExpression, Expression> falseSelectExpression); + #endregion + + #region OrderBy + new ISugarQueryable OrderBy(List models); + new ISugarQueryable OrderByPropertyName(string orderPropertyName, OrderByType? orderByType = null); + new ISugarQueryable OrderByPropertyNameIF(bool isOrderBy, string orderPropertyName, OrderByType? orderByType = null); + new ISugarQueryable OrderByDescending(Expression> expression); + ISugarQueryable OrderByDescending(Expression> expression); + ISugarQueryable OrderByDescending(Expression> expression); + new ISugarQueryable OrderBy(string orderByFields); + new ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + new ISugarQueryable OrderByIF(bool isOrderBy, string orderByFields); + new ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + #endregion + + #region GroupBy + new ISugarQueryable PartitionBy(Expression> expression); + ISugarQueryable PartitionBy(Expression> expression); + ISugarQueryable PartitionBy(Expression> expression); + new ISugarQueryable PartitionBy(string groupFileds); + new ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + new ISugarQueryable GroupByIF(bool isGroupBy, Expression> expression); + ISugarQueryable GroupByIF(bool isGroupBy, Expression> expression); + ISugarQueryable GroupByIF(bool isGroupBy, Expression> expression); + new ISugarQueryable GroupByIF(bool isGroupBy, string groupFields); + new ISugarQueryable Having(Expression> expression); + ISugarQueryable Having(Expression> expression); + ISugarQueryable Having(Expression> expression); + new ISugarQueryable Having(string whereString, object parameters = null); + new ISugarQueryable HavingIF(bool isHaving, Expression> expression); + ISugarQueryable HavingIF(bool isHaving, Expression> expression); + ISugarQueryable HavingIF(bool isHaving, Expression> expression); + #endregion + + #region Aggr + TResult Max(Expression> expression); + TResult Min(Expression> expression); + TResult Sum(Expression> expression); + TResult Avg(Expression> expression); + Task MaxAsync(Expression> expression); + Task MinAsync(Expression> expression); + Task SumAsync(Expression> expression); + Task AvgAsync(Expression> expression); + #endregion + + #region In + new ISugarQueryable In(Expression> expression, params FieldType[] inValues); + new ISugarQueryable In(Expression> expression, List inValues); + new ISugarQueryable In(Expression> expression, ISugarQueryable childQueryExpression); + + ISugarQueryable In(Expression> expression, params FieldType[] inValues); + ISugarQueryable In(Expression> expression, List inValues); + ISugarQueryable In(Expression> expression, ISugarQueryable childQueryExpression); + + ISugarQueryable In(Expression> expression, params FieldType[] inValues); + ISugarQueryable In(Expression> expression, List inValues); + ISugarQueryable In(Expression> expression, ISugarQueryable childQueryExpression); + + new ISugarQueryable InIF(bool isIn, params TParamter[] pkValues); + new ISugarQueryable InIF(bool isIn, string fieldName, params TParamter[] pkValues); + #endregion + + #region Other + new ISugarQueryable Take(int num); + new ISugarQueryable Clone(); + new ISugarQueryable AS(string tableName); + new ISugarQueryable AS(string tableName); + new ISugarQueryable ClearFilter(); + new ISugarQueryable Filter(string FilterName, bool isDisabledGobalFilter = false); + new ISugarQueryable ClearFilter(params Type[] types); + new ISugarQueryable ClearFilter(); + new ISugarQueryable ClearFilter(); + new ISugarQueryable ClearFilter(); + new ISugarQueryable AddParameters(object parameters); + new ISugarQueryable AddParameters(SugarParameter[] parameters); + new ISugarQueryable AddParameters(List parameters); + new ISugarQueryable AddJoinInfo(string tableName, string shortName, string joinWhere, JoinType type = JoinType.Left); + new ISugarQueryable With(string withString); + new ISugarQueryable WithCache(int cacheDurationInSeconds = int.MaxValue); + new ISugarQueryable WithCacheIF(bool isCache, int cacheDurationInSeconds = int.MaxValue); + new ISugarQueryable Distinct(); + bool Any(Expression> expression); + #endregion + } + public partial interface ISugarQueryable : ISugarQueryable + { + new ISugarQueryable IF(bool condition, Action> action); + new ISugarQueryable Hints(string hints); + new ISugarQueryable SampleBy(int timeNumber, SampleByUnit timeType); + new ISugarQueryable SampleBy(int timeNumber, string timeType); + ISugarQueryable SelectMergeTable(Expression> expression); + ISugarQueryable LeftJoinIF(bool isLeftJoin, Expression> joinExpression); + ISugarQueryable InnerJoinIF(bool isJoin, Expression> joinExpression); + ISugarQueryable LeftJoin(ISugarQueryable joinQueryable, Expression> joinExpression); + ISugarQueryable LeftJoinIF(bool isJoin, ISugarQueryable joinQueryable, Expression> joinExpression); + ISugarQueryable InnerJoinIF(bool isJoin, ISugarQueryable joinQueryable, Expression> joinExpression); + ISugarQueryable InnerJoin(ISugarQueryable joinQueryable, Expression> joinExpression); + ISugarQueryable RightJoin(ISugarQueryable joinQueryable, Expression> joinExpression); + ISugarQueryable LeftJoin(Expression> joinExpression); + ISugarQueryable FullJoin(Expression> joinExpression); + ISugarQueryable InnerJoin(Expression> joinExpression); + ISugarQueryable RightJoin(Expression> joinExpression); + ISugarQueryable LeftJoin(Expression> joinExpression, string tableName); + ISugarQueryable FullJoin(Expression> joinExpression, string tableName); + ISugarQueryable InnerJoin(Expression> joinExpression, string tableName); + ISugarQueryable RightJoin(Expression> joinExpression, string tableName); + ISugarQueryable LeftJoinIF(bool isJoin, Expression> joinExpression, string tableName); + ISugarQueryable FullJoinIF(bool isJoin, Expression> joinExpression, string tableName); + ISugarQueryable InnerJoinIF(bool isJoin, Expression> joinExpression, string tableName); + ISugarQueryable RightJoinIF(bool isJoin, Expression> joinExpression, string tableName); + + #region Where + new ISugarQueryable Where(string expShortName, FormattableString expressionString); + new ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + new ISugarQueryable Where(List conditionalModels); + new ISugarQueryable Where(List conditionalModels, bool isWrap); + new ISugarQueryable Where(IFuncModel funcModel); + new ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + + new ISugarQueryable Where(string whereString, object parameters = null); + new ISugarQueryable WhereIF(bool isWhere, string whereString, object parameters = null); + + /// + /// if a property that is not empty is a condition + /// + /// + /// + /// + new ISugarQueryable WhereClass(ClassType whereClass, bool ignoreDefaultValue = false) where ClassType : class, new(); + /// + /// if a property that is not empty is a condition + /// + /// + /// + /// + new ISugarQueryable WhereClass(List whereClassList, bool ignoreDefaultValue = false) where ClassType : class, new(); + #endregion + + #region Select + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable SelectIF(bool condition, Expression> trueSelectExpression, Expression> falseSelectExpression); + ISugarQueryable Select(Expression> expression, bool isAutoFill); + #endregion + + #region OrderBy + new ISugarQueryable OrderBy(List models); + new ISugarQueryable OrderByPropertyName(string orderPropertyName, OrderByType? orderByType = null); + new ISugarQueryable OrderByPropertyNameIF(bool isOrderBy, string orderPropertyName, OrderByType? orderByType = null); + new ISugarQueryable OrderByDescending(Expression> expression); + ISugarQueryable OrderByDescending(Expression> expression); + ISugarQueryable OrderByDescending(Expression> expression); + ISugarQueryable OrderByDescending(Expression> expression); + new ISugarQueryable OrderBy(string orderByFields); + new ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + new ISugarQueryable OrderByIF(bool isOrderBy, string orderByFields); + new ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + #endregion + + #region GroupBy + new ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + new ISugarQueryable GroupByIF(bool isGroupBy, Expression> expression); + ISugarQueryable GroupByIF(bool isGroupBy, Expression> expression); + ISugarQueryable GroupByIF(bool isGroupBy, Expression> expression); + ISugarQueryable GroupByIF(bool isGroupBy, Expression> expression); + new ISugarQueryable GroupByIF(bool isGroupBy, string groupFields); + new ISugarQueryable Having(Expression> expression); + ISugarQueryable Having(Expression> expression); + ISugarQueryable Having(Expression> expression); + ISugarQueryable Having(Expression> expression); + new ISugarQueryable Having(string whereString, object parameters = null); + new ISugarQueryable HavingIF(bool isHaving, Expression> expression); + ISugarQueryable HavingIF(bool isHaving, Expression> expression); + ISugarQueryable HavingIF(bool isHaving, Expression> expression); + ISugarQueryable HavingIF(bool isHaving, Expression> expression); + #endregion + + #region Aggr + TResult Max(Expression> expression); + TResult Min(Expression> expression); + TResult Sum(Expression> expression); + TResult Avg(Expression> expression); + #endregion + + #region In + new ISugarQueryable In(Expression> expression, params FieldType[] inValues); + new ISugarQueryable In(Expression> expression, List inValues); + new ISugarQueryable In(Expression> expression, ISugarQueryable childQueryExpression); + + ISugarQueryable In(Expression> expression, params FieldType[] inValues); + ISugarQueryable In(Expression> expression, List inValues); + ISugarQueryable In(Expression> expression, ISugarQueryable childQueryExpression); + + ISugarQueryable In(Expression> expression, params FieldType[] inValues); + ISugarQueryable In(Expression> expression, List inValues); + ISugarQueryable In(Expression> expression, ISugarQueryable childQueryExpression); + + ISugarQueryable In(Expression> expression, params FieldType[] inValues); + ISugarQueryable In(Expression> expression, List inValues); + ISugarQueryable In(Expression> expression, ISugarQueryable childQueryExpression); + + new ISugarQueryable InIF(bool isIn, params TParamter[] pkValues); + new ISugarQueryable InIF(bool isIn, string fieldName, params TParamter[] pkValues); + #endregion + + #region Other + new ISugarQueryable Take(int num); + new ISugarQueryable Clone(); + new ISugarQueryable AS(string tableName); + new ISugarQueryable AS(string tableName); + new ISugarQueryable ClearFilter(); + new ISugarQueryable Filter(string FilterName, bool isDisabledGobalFilter = false); + new ISugarQueryable ClearFilter(params Type[] types); + new ISugarQueryable ClearFilter(); + new ISugarQueryable ClearFilter(); + new ISugarQueryable ClearFilter(); + new ISugarQueryable AddParameters(object parameters); + new ISugarQueryable AddParameters(SugarParameter[] parameters); + new ISugarQueryable AddParameters(List parameters); + new ISugarQueryable AddJoinInfo(string tableName, string shortName, string joinWhere, JoinType type = JoinType.Left); + new ISugarQueryable With(string withString); + new ISugarQueryable WithCache(int cacheDurationInSeconds = int.MaxValue); + new ISugarQueryable WithCacheIF(bool isCache, int cacheDurationInSeconds = int.MaxValue); + new ISugarQueryable Distinct(); + bool Any(Expression> expression); + #endregion + } + public partial interface ISugarQueryable : ISugarQueryable + { + new ISugarQueryable IF(bool condition, Action> action); + new ISugarQueryable Hints(string hints); + new ISugarQueryable SampleBy(int timeNumber, SampleByUnit timeType); + new ISugarQueryable SampleBy(int timeNumber, string timeType); + ISugarQueryable SelectMergeTable(Expression> expression); + ISugarQueryable LeftJoinIF(bool isLeftJoin, Expression> joinExpression); + ISugarQueryable InnerJoinIF(bool isJoin, Expression> joinExpression); + ISugarQueryable LeftJoin(ISugarQueryable joinQueryable, Expression> joinExpression); + ISugarQueryable LeftJoinIF(bool isJoin, ISugarQueryable joinQueryable, Expression> joinExpression); + ISugarQueryable InnerJoinIF(bool isJoin, ISugarQueryable joinQueryable, Expression> joinExpression); + ISugarQueryable InnerJoin(ISugarQueryable joinQueryable, Expression> joinExpression); + ISugarQueryable RightJoin(ISugarQueryable joinQueryable, Expression> joinExpression); + ISugarQueryable LeftJoin(Expression> joinExpression); + ISugarQueryable FullJoin(Expression> joinExpression); + ISugarQueryable InnerJoin(Expression> joinExpression); + ISugarQueryable RightJoin(Expression> joinExpression); + ISugarQueryable LeftJoin(Expression> joinExpression, string tableName); + ISugarQueryable FullJoin(Expression> joinExpression, string tableName); + ISugarQueryable InnerJoin(Expression> joinExpression, string tableName); + ISugarQueryable RightJoin(Expression> joinExpression, string tableName); + + #region Where + new ISugarQueryable Where(string expShortName, FormattableString expressionString); + new ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + new ISugarQueryable Where(List conditionalModels); + new ISugarQueryable Where(List conditionalModels, bool isWrap); + new ISugarQueryable Where(IFuncModel funcModel); + new ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + + new ISugarQueryable Where(string whereString, object parameters = null); + new ISugarQueryable WhereIF(bool isWhere, string whereString, object parameters = null); + + /// + /// if a property that is not empty is a condition + /// + /// + /// + /// + new ISugarQueryable WhereClass(ClassType whereClass, bool ignoreDefaultValue = false) where ClassType : class, new(); + /// + /// if a property that is not empty is a condition + /// + /// + /// + /// + new ISugarQueryable WhereClass(List whereClassList, bool ignoreDefaultValue = false) where ClassType : class, new(); + #endregion + + #region Select + ISugarQueryable SelectIF(bool condition, Expression> trueSelectExpression, Expression> falseSelectExpression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression, bool isAutoFill); + #endregion + + #region OrderBy + new ISugarQueryable OrderBy(List models); + new ISugarQueryable OrderByPropertyName(string orderPropertyName, OrderByType? orderByType = null); + new ISugarQueryable OrderByPropertyNameIF(bool isOrderBy, string orderPropertyName, OrderByType? orderByType = null); + new ISugarQueryable OrderByDescending(Expression> expression); + ISugarQueryable OrderByDescending(Expression> expression); + ISugarQueryable OrderByDescending(Expression> expression); + ISugarQueryable OrderByDescending(Expression> expression); + ISugarQueryable OrderByDescending(Expression> expression); + new ISugarQueryable OrderBy(string orderByFields); + new ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + new ISugarQueryable OrderByIF(bool isOrderBy, string orderByFields); + new ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + #endregion + + #region GroupBy + new ISugarQueryable GroupByIF(bool isGroupBy, Expression> expression); + ISugarQueryable GroupByIF(bool isGroupBy, Expression> expression); + ISugarQueryable GroupByIF(bool isGroupBy, Expression> expression); + ISugarQueryable GroupByIF(bool isGroupBy, Expression> expression); + ISugarQueryable GroupByIF(bool isGroupBy, Expression> expression); + new ISugarQueryable GroupByIF(bool isGroupBy, string groupFields); + new ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + new ISugarQueryable Having(Expression> expression); + ISugarQueryable Having(Expression> expression); + ISugarQueryable Having(Expression> expression); + ISugarQueryable Having(Expression> expression); + ISugarQueryable Having(Expression> expression); + new ISugarQueryable Having(string whereString, object parameters = null); + new ISugarQueryable HavingIF(bool isHaving, Expression> expression); + ISugarQueryable HavingIF(bool isHaving, Expression> expression); + ISugarQueryable HavingIF(bool isHaving, Expression> expression); + ISugarQueryable HavingIF(bool isHaving, Expression> expression); + ISugarQueryable HavingIF(bool isHaving, Expression> expression); + #endregion + + #region Aggr + TResult Max(Expression> expression); + TResult Min(Expression> expression); + TResult Sum(Expression> expression); + TResult Avg(Expression> expression); + #endregion + + #region In + new ISugarQueryable In(Expression> expression, params FieldType[] inValues); + new ISugarQueryable In(Expression> expression, List inValues); + new ISugarQueryable In(Expression> expression, ISugarQueryable childQueryExpression); + #endregion + + #region Other + new ISugarQueryable Take(int num); + new ISugarQueryable Clone(); + new ISugarQueryable AS(string tableName); + new ISugarQueryable AS(string tableName); + new ISugarQueryable ClearFilter(); + new ISugarQueryable Filter(string FilterName, bool isDisabledGobalFilter = false); + new ISugarQueryable ClearFilter(params Type[] types); + new ISugarQueryable ClearFilter(); + new ISugarQueryable ClearFilter(); + new ISugarQueryable ClearFilter(); + new ISugarQueryable AddParameters(object parameters); + new ISugarQueryable AddParameters(SugarParameter[] parameters); + new ISugarQueryable AddParameters(List parameters); + new ISugarQueryable AddJoinInfo(string tableName, string shortName, string joinWhere, JoinType type = JoinType.Left); + new ISugarQueryable With(string withString); + new ISugarQueryable WithCache(int cacheDurationInSeconds = int.MaxValue); + new ISugarQueryable WithCacheIF(bool isCache, int cacheDurationInSeconds = int.MaxValue); + new ISugarQueryable Distinct(); + bool Any(Expression> expression); + #endregion + } + public partial interface ISugarQueryable : ISugarQueryable + { + new ISugarQueryable IF(bool condition, Action> action); + new ISugarQueryable Hints(string hints); + ISugarQueryable SelectMergeTable(Expression> expression); + ISugarQueryable LeftJoinIF(bool isLeftJoin, Expression> joinExpression); + ISugarQueryable InnerJoinIF(bool isJoin, Expression> joinExpression); + ISugarQueryable LeftJoinIF(bool isJoin, ISugarQueryable joinQueryable, Expression> joinExpression); + ISugarQueryable InnerJoinIF(bool isJoin, ISugarQueryable joinQueryable, Expression> joinExpression); + ISugarQueryable LeftJoin(ISugarQueryable joinQueryable, Expression> joinExpression); + ISugarQueryable InnerJoin(ISugarQueryable joinQueryable, Expression> joinExpression); + ISugarQueryable RightJoin(ISugarQueryable joinQueryable, Expression> joinExpression); + ISugarQueryable LeftJoin(Expression> joinExpression); + ISugarQueryable FullJoin(Expression> joinExpression); + ISugarQueryable InnerJoin(Expression> joinExpression); + ISugarQueryable RightJoin(Expression> joinExpression); + ISugarQueryable LeftJoin(Expression> joinExpression, string tableName); + ISugarQueryable FullJoin(Expression> joinExpression, string tableName); + ISugarQueryable InnerJoin(Expression> joinExpression, string tableName); + ISugarQueryable RightJoin(Expression> joinExpression, string tableName); + + #region Where + new ISugarQueryable Where(string expShortName, FormattableString expressionString); + new ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + new ISugarQueryable Where(List conditionalModels); + new ISugarQueryable Where(List conditionalModels, bool isWrap); + new ISugarQueryable Where(IFuncModel funcModel); + new ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + + new ISugarQueryable Where(string whereString, object parameters = null); + new ISugarQueryable WhereIF(bool isWhere, string whereString, object parameters = null); + + /// + /// if a property that is not empty is a condition + /// + /// + /// + /// + new ISugarQueryable WhereClass(ClassType whereClass, bool ignoreDefaultValue = false) where ClassType : class, new(); + /// + /// if a property that is not empty is a condition + /// + /// + /// + /// + new ISugarQueryable WhereClass(List whereClassList, bool ignoreDefaultValue = false) where ClassType : class, new(); + + #endregion + + #region Select + ISugarQueryable SelectIF(bool condition, Expression> trueSelectExpression, Expression> falseSelectExpression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression, bool isAutoFill); + #endregion + + #region OrderBy + new ISugarQueryable OrderBy(List models); + new ISugarQueryable OrderByPropertyNameIF(bool isOrderBy, string orderPropertyName, OrderByType? orderByType = null); + new ISugarQueryable OrderByPropertyName(string orderPropertyName, OrderByType? orderByType = null); + new ISugarQueryable OrderByDescending(Expression> expression); + ISugarQueryable OrderByDescending(Expression> expression); + ISugarQueryable OrderByDescending(Expression> expression); + ISugarQueryable OrderByDescending(Expression> expression); + ISugarQueryable OrderByDescending(Expression> expression); + ISugarQueryable OrderByDescending(Expression> expression); + new ISugarQueryable OrderBy(string orderByFields); + new ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + new ISugarQueryable OrderByIF(bool isOrderBy, string orderByFields); + new ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + #endregion + + #region GroupBy + new ISugarQueryable GroupByIF(bool isGroupBy, string groupFields); + new ISugarQueryable GroupByIF(bool isGroupBy, Expression> expression); + ISugarQueryable GroupByIF(bool isGroupBy, Expression> expression); + ISugarQueryable GroupByIF(bool isGroupBy, Expression> expression); + ISugarQueryable GroupByIF(bool isGroupBy, Expression> expression); + ISugarQueryable GroupByIF(bool isGroupBy, Expression> expression); + ISugarQueryable GroupByIF(bool isGroupBy, Expression> expression); + new ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + new ISugarQueryable Having(Expression> expression); + ISugarQueryable Having(Expression> expression); + ISugarQueryable Having(Expression> expression); + ISugarQueryable Having(Expression> expression); + ISugarQueryable Having(Expression> expression); + ISugarQueryable Having(Expression> expression); + new ISugarQueryable Having(string whereString, object parameters = null); + new ISugarQueryable HavingIF(bool isHaving, Expression> expression); + ISugarQueryable HavingIF(bool isHaving, Expression> expression); + ISugarQueryable HavingIF(bool isHaving, Expression> expression); + ISugarQueryable HavingIF(bool isHaving, Expression> expression); + ISugarQueryable HavingIF(bool isHaving, Expression> expression); + ISugarQueryable HavingIF(bool isHaving, Expression> expression); + #endregion + + #region Aggr + TResult Max(Expression> expression); + TResult Min(Expression> expression); + TResult Sum(Expression> expression); + TResult Avg(Expression> expression); + #endregion + + #region In + new ISugarQueryable In(Expression> expression, params FieldType[] inValues); + new ISugarQueryable In(Expression> expression, List inValues); + new ISugarQueryable In(Expression> expression, ISugarQueryable childQueryExpression); + #endregion + + #region Other + new ISugarQueryable Take(int num); + new ISugarQueryable Clone(); + new ISugarQueryable AS(string tableName); + new ISugarQueryable AS(string tableName); + new ISugarQueryable ClearFilter(); + new ISugarQueryable Filter(string FilterName, bool isDisabledGobalFilter = false); + new ISugarQueryable ClearFilter(params Type[] types); + new ISugarQueryable ClearFilter(); + new ISugarQueryable ClearFilter(); + new ISugarQueryable ClearFilter(); + new ISugarQueryable AddParameters(object parameters); + new ISugarQueryable AddParameters(SugarParameter[] parameters); + new ISugarQueryable AddParameters(List parameters); + new ISugarQueryable AddJoinInfo(string tableName, string shortName, string joinWhere, JoinType type = JoinType.Left); + new ISugarQueryable With(string withString); + new ISugarQueryable WithCache(int cacheDurationInSeconds = int.MaxValue); + new ISugarQueryable WithCacheIF(bool isCache, int cacheDurationInSeconds = int.MaxValue); + new ISugarQueryable Distinct(); + bool Any(Expression> expression); + #endregion + } + public partial interface ISugarQueryable : ISugarQueryable + { + new ISugarQueryable Hints(string hints); + ISugarQueryable SelectMergeTable(Expression> expression); + ISugarQueryable LeftJoinIF(bool isLeftJoin, Expression> joinExpression); + ISugarQueryable InnerJoinIF(bool isJoin, Expression> joinExpression); + ISugarQueryable LeftJoin(ISugarQueryable joinQueryable, Expression> joinExpression); + ISugarQueryable InnerJoin(ISugarQueryable joinQueryable, Expression> joinExpression); + ISugarQueryable RightJoin(ISugarQueryable joinQueryable, Expression> joinExpression); + ISugarQueryable LeftJoinIF(bool isJoin, ISugarQueryable joinQueryable, Expression> joinExpression); + ISugarQueryable InnerJoinIF(bool isJoin, ISugarQueryable joinQueryable, Expression> joinExpression); + ISugarQueryable LeftJoin(Expression> joinExpression); + ISugarQueryable FullJoin(Expression> joinExpression); + ISugarQueryable InnerJoin(Expression> joinExpression); + ISugarQueryable RightJoin(Expression> joinExpression); + ISugarQueryable LeftJoin(Expression> joinExpression, string tableName); + ISugarQueryable FullJoin(Expression> joinExpression, string tableName); + ISugarQueryable InnerJoin(Expression> joinExpression, string tableName); + ISugarQueryable RightJoin(Expression> joinExpression, string tableName); + + #region Where + new ISugarQueryable Where(string expShortName, FormattableString expressionString); + new ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + new ISugarQueryable Where(List conditionalModels); + new ISugarQueryable Where(List conditionalModels, bool isWrap); + + new ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + + new ISugarQueryable Where(string whereString, object parameters = null); + new ISugarQueryable WhereIF(bool isWhere, string whereString, object parameters = null); + + /// + /// if a property that is not empty is a condition + /// + /// + /// + /// + new ISugarQueryable WhereClass(ClassType whereClass, bool ignoreDefaultValue = false) where ClassType : class, new(); + /// + /// if a property that is not empty is a condition + /// + /// + /// + /// + new ISugarQueryable WhereClass(List whereClassList, bool ignoreDefaultValue = false) where ClassType : class, new(); + + #endregion + + #region Select + ISugarQueryable SelectIF(bool condition, Expression> trueSelectExpression, Expression> falseSelectExpression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression, bool isAutoFill); + #endregion + + #region OrderBy + new ISugarQueryable OrderBy(List models); + new ISugarQueryable OrderByPropertyNameIF(bool isOrderBy, string orderPropertyName, OrderByType? orderByType = null); + new ISugarQueryable OrderByPropertyName(string orderPropertyName, OrderByType? orderByType = null); + new ISugarQueryable OrderByDescending(Expression> expression); + ISugarQueryable OrderByDescending(Expression> expression); + ISugarQueryable OrderByDescending(Expression> expression); + ISugarQueryable OrderByDescending(Expression> expression); + ISugarQueryable OrderByDescending(Expression> expression); + ISugarQueryable OrderByDescending(Expression> expression); + ISugarQueryable OrderByDescending(Expression> expression); + new ISugarQueryable OrderBy(string orderByFields); + new ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + new ISugarQueryable OrderByIF(bool isOrderBy, string orderByFields); + new ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + #endregion + + #region GroupBy + new ISugarQueryable GroupByIF(bool isGroupBy, string groupFields); + new ISugarQueryable GroupByIF(bool isGroupBy, Expression> expression); + ISugarQueryable GroupByIF(bool isGroupBy, Expression> expression); + ISugarQueryable GroupByIF(bool isGroupBy, Expression> expression); + ISugarQueryable GroupByIF(bool isGroupBy, Expression> expression); + ISugarQueryable GroupByIF(bool isGroupBy, Expression> expression); + ISugarQueryable GroupByIF(bool isGroupBy, Expression> expression); + ISugarQueryable GroupByIF(bool isGroupBy, Expression> expression); + new ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + #endregion + + #region Aggr + TResult Max(Expression> expression); + TResult Min(Expression> expression); + TResult Sum(Expression> expression); + TResult Avg(Expression> expression); + #endregion + + #region In + new ISugarQueryable In(Expression> expression, params FieldType[] inValues); + new ISugarQueryable In(Expression> expression, List inValues); + new ISugarQueryable In(Expression> expression, ISugarQueryable childQueryExpression); + #endregion + + #region Other + new ISugarQueryable Take(int num); + new ISugarQueryable Clone(); + new ISugarQueryable AS(string tableName); + new ISugarQueryable AS(string tableName); + new ISugarQueryable ClearFilter(); + new ISugarQueryable Filter(string FilterName, bool isDisabledGobalFilter = false); + new ISugarQueryable ClearFilter(params Type[] types); + new ISugarQueryable ClearFilter(); + new ISugarQueryable ClearFilter(); + new ISugarQueryable ClearFilter(); + new ISugarQueryable AddParameters(object parameters); + new ISugarQueryable AddParameters(SugarParameter[] parameters); + new ISugarQueryable AddParameters(List parameters); + new ISugarQueryable AddJoinInfo(string tableName, string shortName, string joinWhere, JoinType type = JoinType.Left); + new ISugarQueryable With(string withString); + new ISugarQueryable WithCache(int cacheDurationInSeconds = int.MaxValue); + new ISugarQueryable WithCacheIF(bool isCache, int cacheDurationInSeconds = int.MaxValue); + new ISugarQueryable Distinct(); + bool Any(Expression> expression); + #endregion + } + public partial interface ISugarQueryable : ISugarQueryable + { + new ISugarQueryable Hints(string hints); + ISugarQueryable SelectMergeTable(Expression> expression); + ISugarQueryable LeftJoinIF(bool isLeftJoin, Expression> joinExpression); + ISugarQueryable InnerJoinIF(bool isJoin, Expression> joinExpression); + ISugarQueryable LeftJoin(ISugarQueryable joinQueryable, Expression> joinExpression); + ISugarQueryable InnerJoin(ISugarQueryable joinQueryable, Expression> joinExpression); + ISugarQueryable RightJoin(ISugarQueryable joinQueryable, Expression> joinExpression); + ISugarQueryable LeftJoinIF(bool isJoin, ISugarQueryable joinQueryable, Expression> joinExpression); + ISugarQueryable InnerJoinIF(bool isJoin, ISugarQueryable joinQueryable, Expression> joinExpression); + ISugarQueryable LeftJoin(Expression> joinExpression); + ISugarQueryable FullJoin(Expression> joinExpression); + ISugarQueryable InnerJoin(Expression> joinExpression); + ISugarQueryable RightJoin(Expression> joinExpression); + ISugarQueryable LeftJoin(Expression> joinExpression, string tableName); + ISugarQueryable FullJoin(Expression> joinExpression, string tableName); + ISugarQueryable InnerJoin(Expression> joinExpression, string tableName); + ISugarQueryable RightJoin(Expression> joinExpression, string tableName); + + #region Where + new ISugarQueryable Where(string expShortName, FormattableString expressionString); + new ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + new ISugarQueryable Where(List conditionalModels); + new ISugarQueryable Where(List conditionalModels, bool isWrap); + + new ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + + new ISugarQueryable Where(string whereString, object parameters = null); + new ISugarQueryable WhereIF(bool isWhere, string whereString, object parameters = null); + + /// + /// if a property that is not empty is a condition + /// + /// + /// + /// + new ISugarQueryable WhereClass(ClassType whereClass, bool ignoreDefaultValue = false) where ClassType : class, new(); + /// + /// if a property that is not empty is a condition + /// + /// + /// + /// + new ISugarQueryable WhereClass(List whereClassList, bool ignoreDefaultValue = false) where ClassType : class, new(); + + #endregion + + #region Select + ISugarQueryable SelectIF(bool condition, Expression> trueSelectExpression, Expression> falseSelectExpression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression, bool isAutoFill); + #endregion + + #region OrderBy + new ISugarQueryable OrderBy(List models); + new ISugarQueryable OrderByPropertyNameIF(bool isOrderBy, string orderPropertyName, OrderByType? orderByType = null); + new ISugarQueryable OrderByPropertyName(string orderPropertyName, OrderByType? orderByType = null); + new ISugarQueryable OrderByDescending(Expression> expression); + ISugarQueryable OrderByDescending(Expression> expression); + ISugarQueryable OrderByDescending(Expression> expression); + ISugarQueryable OrderByDescending(Expression> expression); + ISugarQueryable OrderByDescending(Expression> expression); + ISugarQueryable OrderByDescending(Expression> expression); + ISugarQueryable OrderByDescending(Expression> expression); + ISugarQueryable OrderByDescending(Expression> expression); + new ISugarQueryable OrderBy(string orderByFields); + new ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + new ISugarQueryable OrderByIF(bool isOrderBy, string orderByFields); + new ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + #endregion + + #region GroupBy + new ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + #endregion + + #region Aggr + TResult Max(Expression> expression); + TResult Min(Expression> expression); + TResult Sum(Expression> expression); + TResult Avg(Expression> expression); + #endregion + + #region In + new ISugarQueryable In(Expression> expression, params FieldType[] inValues); + new ISugarQueryable In(Expression> expression, List inValues); + new ISugarQueryable In(Expression> expression, ISugarQueryable childQueryExpression); + #endregion + + #region Other + new ISugarQueryable Take(int num); + new ISugarQueryable Clone(); + new ISugarQueryable AS(string tableName); + new ISugarQueryable AS(string tableName); + new ISugarQueryable ClearFilter(); + new ISugarQueryable Filter(string FilterName, bool isDisabledGobalFilter = false); + new ISugarQueryable ClearFilter(params Type[] types); + new ISugarQueryable ClearFilter(); + new ISugarQueryable ClearFilter(); + new ISugarQueryable ClearFilter(); + new ISugarQueryable AddParameters(object parameters); + new ISugarQueryable AddParameters(SugarParameter[] parameters); + new ISugarQueryable AddParameters(List parameters); + new ISugarQueryable AddJoinInfo(string tableName, string shortName, string joinWhere, JoinType type = JoinType.Left); + new ISugarQueryable With(string withString); + new ISugarQueryable WithCache(int cacheDurationInSeconds = int.MaxValue); + new ISugarQueryable WithCacheIF(bool isCache, int cacheDurationInSeconds = int.MaxValue); + new ISugarQueryable Distinct(); + bool Any(Expression> expression); + #endregion + } + + #region 9-12 + public partial interface ISugarQueryable : ISugarQueryable + { + new ISugarQueryable Hints(string hints); + ISugarQueryable SelectMergeTable(Expression> expression); + ISugarQueryable LeftJoinIF(bool isLeftJoin, Expression> joinExpression); + ISugarQueryable InnerJoinIF(bool isJoin, Expression> joinExpression); + ISugarQueryable LeftJoin(ISugarQueryable joinQueryable, Expression> joinExpression); + ISugarQueryable InnerJoin(ISugarQueryable joinQueryable, Expression> joinExpression); + ISugarQueryable RightJoin(ISugarQueryable joinQueryable, Expression> joinExpression); + ISugarQueryable LeftJoinIF(bool isJoin, ISugarQueryable joinQueryable, Expression> joinExpression); + ISugarQueryable InnerJoinIF(bool isJoin, ISugarQueryable joinQueryable, Expression> joinExpression); + ISugarQueryable LeftJoin(Expression> joinExpression); + ISugarQueryable FullJoin(Expression> joinExpression); + ISugarQueryable InnerJoin(Expression> joinExpression); + ISugarQueryable RightJoin(Expression> joinExpression); + ISugarQueryable LeftJoin(Expression> joinExpression, string tableName); + ISugarQueryable FullJoin(Expression> joinExpression, string tableName); + ISugarQueryable InnerJoin(Expression> joinExpression, string tableName); + ISugarQueryable RightJoin(Expression> joinExpression, string tableName); + + #region Where + new ISugarQueryable Where(string expShortName, FormattableString expressionString); + new ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + new ISugarQueryable Where(List conditionalModels); + new ISugarQueryable Where(List conditionalModels, bool isWrap); + new ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + + new ISugarQueryable Where(string whereString, object parameters = null); + new ISugarQueryable WhereIF(bool isWhere, string whereString, object parameters = null); + #endregion + + #region Select + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression, bool isAutoFill); + ISugarQueryable SelectIF(bool condition, Expression> trueSelectExpression, Expression> falseSelectExpression); + #endregion + + #region OrderBy + new ISugarQueryable OrderByDescending(Expression> expression); + ISugarQueryable OrderByDescending(Expression> expression); + ISugarQueryable OrderByDescending(Expression> expression); + ISugarQueryable OrderByDescending(Expression> expression); + ISugarQueryable OrderByDescending(Expression> expression); + ISugarQueryable OrderByDescending(Expression> expression); + ISugarQueryable OrderByDescending(Expression> expression); + ISugarQueryable OrderByDescending(Expression> expression); + ISugarQueryable OrderByDescending(Expression> expression); + + new ISugarQueryable OrderBy(List models); + new ISugarQueryable OrderByPropertyNameIF(bool isOrderBy, string orderPropertyName, OrderByType? orderByType = null); + new ISugarQueryable OrderByPropertyName(string orderPropertyName, OrderByType? orderByType = null); + new ISugarQueryable OrderBy(string orderByFields); + new ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + + new ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + #endregion + + #region GroupBy + new ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + #endregion + + #region Aggr + TResult Max(Expression> expression); + TResult Min(Expression> expression); + TResult Sum(Expression> expression); + TResult Avg(Expression> expression); + #endregion + + #region In + new ISugarQueryable In(Expression> expression, params FieldType[] inValues); + new ISugarQueryable In(Expression> expression, List inValues); + new ISugarQueryable In(Expression> expression, ISugarQueryable childQueryExpression); + #endregion + + #region Other + new ISugarQueryable Distinct(); + new ISugarQueryable Take(int num); + new ISugarQueryable Clone(); + new ISugarQueryable AS(string tableName); + new ISugarQueryable AS(string tableName); + new ISugarQueryable ClearFilter(); + new ISugarQueryable Filter(string FilterName, bool isDisabledGobalFilter = false); + new ISugarQueryable ClearFilter(params Type[] types); + new ISugarQueryable ClearFilter(); + new ISugarQueryable ClearFilter(); + new ISugarQueryable ClearFilter(); + new ISugarQueryable AddParameters(object parameters); + new ISugarQueryable AddParameters(SugarParameter[] parameters); + new ISugarQueryable AddParameters(List parameters); + new ISugarQueryable AddJoinInfo(string tableName, string shortName, string joinWhere, JoinType type = JoinType.Left); + new ISugarQueryable With(string withString); + new ISugarQueryable WithCache(int cacheDurationInSeconds = int.MaxValue); + new ISugarQueryable WithCacheIF(bool isCache, int cacheDurationInSeconds = int.MaxValue); + #endregion + } + public partial interface ISugarQueryable : ISugarQueryable + { + new ISugarQueryable Hints(string hints); + ISugarQueryable SelectMergeTable(Expression> expression); + ISugarQueryable LeftJoinIF(bool isLeftJoin, Expression> joinExpression); + ISugarQueryable InnerJoinIF(bool isJoin, Expression> joinExpression); + ISugarQueryable LeftJoin(ISugarQueryable joinQueryable, Expression> joinExpression); + ISugarQueryable InnerJoin(ISugarQueryable joinQueryable, Expression> joinExpression); + ISugarQueryable RightJoin(ISugarQueryable joinQueryable, Expression> joinExpression); + ISugarQueryable LeftJoin(Expression> joinExpression); + ISugarQueryable FullJoin(Expression> joinExpression); + ISugarQueryable InnerJoin(Expression> joinExpression); + ISugarQueryable RightJoin(Expression> joinExpression); + ISugarQueryable LeftJoin(Expression> joinExpression, string tableName); + ISugarQueryable FullJoin(Expression> joinExpression, string tableName); + ISugarQueryable InnerJoin(Expression> joinExpression, string tableName); + ISugarQueryable RightJoin(Expression> joinExpression, string tableName); + + #region Where + new ISugarQueryable Where(string expShortName, FormattableString expressionString); + new ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + new ISugarQueryable Where(List conditionalModels); + new ISugarQueryable Where(List conditionalModels, bool isWrap); + + new ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + + new ISugarQueryable Where(string whereString, object parameters = null); + new ISugarQueryable WhereIF(bool isWhere, string whereString, object parameters = null); + #endregion + + #region Select + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression, bool isAutoFill); + ISugarQueryable SelectIF(bool condition, Expression> trueSelectExpression, Expression> falseSelectExpression); + #endregion + + #region OrderBy + new ISugarQueryable OrderByDescending(Expression> expression); + ISugarQueryable OrderByDescending(Expression> expression); + ISugarQueryable OrderByDescending(Expression> expression); + ISugarQueryable OrderByDescending(Expression> expression); + ISugarQueryable OrderByDescending(Expression> expression); + ISugarQueryable OrderByDescending(Expression> expression); + ISugarQueryable OrderByDescending(Expression> expression); + ISugarQueryable OrderByDescending(Expression> expression); + ISugarQueryable OrderByDescending(Expression> expression); + ISugarQueryable OrderByDescending(Expression> expression); + new ISugarQueryable OrderBy(List models); + new ISugarQueryable OrderByPropertyName(string orderPropertyName, OrderByType? orderByType = null); + new ISugarQueryable OrderByPropertyNameIF(bool isOrderBy, string orderPropertyName, OrderByType? orderByType = null); + new ISugarQueryable OrderBy(string orderByFields); + new ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + + + + new ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + #endregion + + #region GroupBy + new ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + #endregion + + #region Aggr + TResult Max(Expression> expression); + TResult Min(Expression> expression); + TResult Sum(Expression> expression); + TResult Avg(Expression> expression); + #endregion + + #region In + new ISugarQueryable In(Expression> expression, params FieldType[] inValues); + new ISugarQueryable In(Expression> expression, List inValues); + new ISugarQueryable In(Expression> expression, ISugarQueryable childQueryExpression); + #endregion + + #region Other + new ISugarQueryable Distinct(); + new ISugarQueryable Take(int num); + new ISugarQueryable Clone(); + new ISugarQueryable AS(string tableName); + new ISugarQueryable AS(string tableName); + new ISugarQueryable ClearFilter(); + new ISugarQueryable Filter(string FilterName, bool isDisabledGobalFilter = false); + new ISugarQueryable ClearFilter(params Type[] types); + new ISugarQueryable ClearFilter(); + new ISugarQueryable ClearFilter(); + new ISugarQueryable ClearFilter(); + new ISugarQueryable AddParameters(object parameters); + new ISugarQueryable AddParameters(SugarParameter[] parameters); + new ISugarQueryable AddParameters(List parameters); + new ISugarQueryable AddJoinInfo(string tableName, string shortName, string joinWhere, JoinType type = JoinType.Left); + new ISugarQueryable With(string withString); + new ISugarQueryable WithCache(int cacheDurationInSeconds = int.MaxValue); + new ISugarQueryable WithCacheIF(bool isCache, int cacheDurationInSeconds = int.MaxValue); + #endregion + } + public partial interface ISugarQueryable : ISugarQueryable + { + new ISugarQueryable Hints(string hints); + ISugarQueryable SelectMergeTable(Expression> expression); + ISugarQueryable LeftJoinIF(bool isLeftJoin, Expression> joinExpression); + ISugarQueryable InnerJoinIF(bool isJoin, Expression> joinExpression); + ISugarQueryable LeftJoin(ISugarQueryable joinQueryable, Expression> joinExpression); + ISugarQueryable InnerJoin(ISugarQueryable joinQueryable, Expression> joinExpression); + ISugarQueryable RightJoin(ISugarQueryable joinQueryable, Expression> joinExpression); + ISugarQueryable LeftJoinIF(bool isJoin, ISugarQueryable joinQueryable, Expression> joinExpression); + ISugarQueryable InnerJoinIF(bool isJoin, ISugarQueryable joinQueryable, Expression> joinExpression); + ISugarQueryable LeftJoin(Expression> joinExpression); + ISugarQueryable FullJoin(Expression> joinExpression); + ISugarQueryable InnerJoin(Expression> joinExpression); + ISugarQueryable RightJoin(Expression> joinExpression); + ISugarQueryable LeftJoin(Expression> joinExpression, string tableName); + ISugarQueryable FullJoin(Expression> joinExpression, string tableName); + ISugarQueryable InnerJoin(Expression> joinExpression, string tableName); + ISugarQueryable RightJoin(Expression> joinExpression, string tableName); + + #region Where + new ISugarQueryable Where(string expShortName, FormattableString expressionString); + new ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + new ISugarQueryable Where(List conditionalModels); + new ISugarQueryable Where(List conditionalModels, bool isWrap); + new ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + + new ISugarQueryable Where(string whereString, object parameters = null); + new ISugarQueryable WhereIF(bool isWhere, string whereString, object parameters = null); + #endregion + + #region Select + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression, bool isAutoFill); + ISugarQueryable SelectIF(bool condition, Expression> trueSelectExpression, Expression> falseSelectExpression); + #endregion + + #region OrderBy + new ISugarQueryable OrderBy(List models); + new ISugarQueryable OrderByPropertyNameIF(bool isOrderBy, string orderPropertyName, OrderByType? orderByType = null); + new ISugarQueryable OrderByPropertyName(string orderPropertyName, OrderByType? orderByType = null); + new ISugarQueryable OrderBy(string orderByFields); + new ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + + new ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + #endregion + + #region GroupBy + new ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + #endregion + + #region Aggr + TResult Max(Expression> expression); + TResult Min(Expression> expression); + TResult Sum(Expression> expression); + TResult Avg(Expression> expression); + #endregion + + #region In + new ISugarQueryable In(Expression> expression, params FieldType[] inValues); + new ISugarQueryable In(Expression> expression, List inValues); + new ISugarQueryable In(Expression> expression, ISugarQueryable childQueryExpression); + #endregion + + #region Other + new ISugarQueryable Distinct(); + new ISugarQueryable Take(int num); + new ISugarQueryable Clone(); + new ISugarQueryable AS(string tableName); + new ISugarQueryable AS(string tableName); + new ISugarQueryable ClearFilter(); + new ISugarQueryable Filter(string FilterName, bool isDisabledGobalFilter = false); + new ISugarQueryable ClearFilter(params Type[] types); + new ISugarQueryable ClearFilter(); + new ISugarQueryable ClearFilter(); + new ISugarQueryable ClearFilter(); + new ISugarQueryable AddParameters(object parameters); + new ISugarQueryable AddParameters(SugarParameter[] parameters); + new ISugarQueryable AddParameters(List parameters); + new ISugarQueryable AddJoinInfo(string tableName, string shortName, string joinWhere, JoinType type = JoinType.Left); + new ISugarQueryable With(string withString); + new ISugarQueryable WithCache(int cacheDurationInSeconds = int.MaxValue); + new ISugarQueryable WithCacheIF(bool isCache, int cacheDurationInSeconds = int.MaxValue); + #endregion + } + public partial interface ISugarQueryable : ISugarQueryable + { + new ISugarQueryable Hints(string hints); + ISugarQueryable SelectMergeTable(Expression> expression); + #region Where + new ISugarQueryable Where(string expShortName, FormattableString expressionString); + new ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + ISugarQueryable Where(Expression> expression); + new ISugarQueryable Where(List conditionalModels); + new ISugarQueryable Where(List conditionalModels, bool isWrap); + new ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + ISugarQueryable WhereIF(bool isWhere, Expression> expression); + + new ISugarQueryable Where(string whereString, object parameters = null); + new ISugarQueryable WhereIF(bool isWhere, string whereString, object parameters = null); + #endregion + + #region Select + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable SelectIF(bool condition, Expression> trueSelectExpression, Expression> falseSelectExpression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression); + ISugarQueryable Select(Expression> expression, bool isAutoFill); + #endregion + + #region OrderBy + new ISugarQueryable OrderBy(List models); + new ISugarQueryable OrderByPropertyNameIF(bool isOrderBy, string orderPropertyName, OrderByType? orderByType = null); + new ISugarQueryable OrderByPropertyName(string orderPropertyName, OrderByType? orderByType = null); + new ISugarQueryable OrderBy(string orderByFields); + new ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderBy(Expression> expression, OrderByType type = OrderByType.Asc); + + new ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + ISugarQueryable OrderByIF(bool isOrderBy, Expression> expression, OrderByType type = OrderByType.Asc); + #endregion + + #region GroupBy + new ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + ISugarQueryable GroupBy(Expression> expression); + #endregion + + #region Aggr + TResult Max(Expression> expression); + TResult Min(Expression> expression); + TResult Sum(Expression> expression); + TResult Avg(Expression> expression); + #endregion + + #region In + new ISugarQueryable In(Expression> expression, params FieldType[] inValues); + new ISugarQueryable In(Expression> expression, List inValues); + new ISugarQueryable In(Expression> expression, ISugarQueryable childQueryExpression); + #endregion + + #region Other + new ISugarQueryable Distinct(); + new ISugarQueryable Take(int num); + new ISugarQueryable Clone(); + new ISugarQueryable AS(string tableName); + new ISugarQueryable AS(string tableName); + new ISugarQueryable ClearFilter(); + new ISugarQueryable Filter(string FilterName, bool isDisabledGobalFilter = false); + new ISugarQueryable ClearFilter(params Type[] types); + new ISugarQueryable ClearFilter(); + new ISugarQueryable ClearFilter(); + new ISugarQueryable ClearFilter(); + new ISugarQueryable AddParameters(object parameters); + new ISugarQueryable AddParameters(SugarParameter[] parameters); + new ISugarQueryable AddParameters(List parameters); + new ISugarQueryable AddJoinInfo(string tableName, string shortName, string joinWhere, JoinType type = JoinType.Left); + new ISugarQueryable With(string withString); + new ISugarQueryable WithCache(int cacheDurationInSeconds = int.MaxValue); + new ISugarQueryable WithCacheIF(bool isCache, int cacheDurationInSeconds = int.MaxValue); + #endregion + } + #endregion +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IReportable.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IReportable.cs new file mode 100644 index 000000000..0f933239c --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IReportable.cs @@ -0,0 +1,10 @@ +namespace SqlSugar +{ + public interface IReportable + { + //IReportable MakeUp(Func auto); + ISugarQueryable ToQueryable(); + ISugarQueryable> ToQueryable(); + ISugarQueryable> ToQueryable(bool onlySelectEntity); + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/ISaveable.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/ISaveable.cs new file mode 100644 index 000000000..b08502b78 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/ISaveable.cs @@ -0,0 +1,22 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public partial interface ISaveable where T : class, new() + { + Task ExecuteCommandAsync(); + Task ExecuteReturnEntityAsync(); + + Task> ExecuteReturnListAsync(); + int ExecuteCommand(); + T ExecuteReturnEntity(); + List ExecuteReturnList(); + ISaveable InsertColumns(Expression> columns); + ISaveable InsertIgnoreColumns(Expression> columns); + ISaveable UpdateColumns(Expression> columns); + ISaveable UpdateIgnoreColumns(Expression> columns); + ISaveable UpdateWhereColumns(Expression> columns); + ISaveable EnableDiffLogEvent(object businessData = null); + ISaveable RemoveDataCache(); + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/ISimpleClient.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/ISimpleClient.cs new file mode 100644 index 000000000..da80d9605 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/ISimpleClient.cs @@ -0,0 +1,142 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public interface ISimpleClient where T : class, new() + { + SimpleClient CopyNew(); + RepositoryType CopyNew(IServiceProvider serviceProvider) where RepositoryType : ISugarRepository; + RepositoryType CopyNew() where RepositoryType : ISugarRepository; + SimpleClient Change() where ChangeType : class, new(); + RepositoryType ChangeRepository() where RepositoryType : ISugarRepository; + RepositoryType ChangeRepository(IServiceProvider serviceProvider) where RepositoryType : ISugarRepository; + IDeleteable AsDeleteable(); + IInsertable AsInsertable(List insertObjs); + IInsertable AsInsertable(T insertObj); + IInsertable AsInsertable(T[] insertObjs); + ISugarQueryable AsQueryable(); + ISqlSugarClient AsSugarClient(); + ITenant AsTenant(); + IUpdateable AsUpdateable(List updateObjs); + IUpdateable AsUpdateable(T updateObj); + IUpdateable AsUpdateable(); + IUpdateable AsUpdateable(T[] updateObjs); + int Count(Expression> whereExpression); + int Count(List conditionalModels); + bool Delete(Expression> whereExpression); + bool Delete(List conditionalModels); + bool Delete(T deleteObj); + bool Delete(List deleteObjs); + bool DeleteById(dynamic id); + bool DeleteByIds(dynamic[] ids); + T GetById(dynamic id); + List GetList(); + List GetList(Expression> whereExpression); + List GetList(List conditionalList); + List GetList(Expression> whereExpression, List orderByModels); + List GetList(List conditionalList, List orderByModels); + List GetPageList(Expression> whereExpression, PageModel page); + List GetPageList(Expression> whereExpression, PageModel page, List orderByModels); + List GetPageList(Expression> whereExpression, PageModel page, Expression> orderByExpression = null, OrderByType orderByType = OrderByType.Asc); + List GetPageList(List conditionalList, PageModel page); + List GetPageList(List conditionalList, PageModel page, List orderByModels); + List GetPageList(List conditionalList, PageModel page, Expression> orderByExpression = null, OrderByType orderByType = OrderByType.Asc); + T GetSingle(Expression> whereExpression); + T GetSingle(List conditionalModels); + T GetFirst(Expression> whereExpression); + T GetFirst(List conditionalModels); + T GetFirst(List conditionalModels, List orderByModels); + bool Insert(T insertObj); + bool InsertOrUpdate(T data); + bool InsertOrUpdate(List datas); + bool InsertRange(List insertObjs); + bool InsertRange(T[] insertObjs); + int InsertReturnIdentity(T insertObj); + long InsertReturnBigIdentity(T insertObj); + long InsertReturnSnowflakeId(T insertObj); + List InsertReturnSnowflakeId(List insertObjs); + T InsertReturnEntity(T insertObj); + + + bool IsAny(Expression> whereExpression); + bool IsAny(List conditionalModels); + bool Update(Expression> columns, Expression> whereExpression); + bool UpdateSetColumnsTrue(Expression> columns, Expression> whereExpression); + bool Update(T updateObj); + bool UpdateRange(List updateObjs); + bool UpdateRange(T[] updateObjs); + + + + + Task CountAsync(Expression> whereExpression); + Task DeleteAsync(Expression> whereExpression); + Task DeleteAsync(T deleteObj); + Task DeleteAsync(List deleteObjs); + Task DeleteByIdAsync(dynamic id); + Task DeleteByIdsAsync(dynamic[] ids); + Task GetByIdAsync(dynamic id); + Task> GetListAsync(); + Task> GetListAsync(Expression> whereExpression); + Task> GetPageListAsync(Expression> whereExpression, PageModel page); + Task> GetPageListAsync(Expression> whereExpression, PageModel page, Expression> orderByExpression = null, OrderByType orderByType = OrderByType.Asc); + Task> GetPageListAsync(List conditionalList, PageModel page); + Task> GetPageListAsync(List conditionalList, PageModel page, Expression> orderByExpression = null, OrderByType orderByType = OrderByType.Asc); + Task GetSingleAsync(Expression> whereExpression); + Task GetFirstAsync(Expression> whereExpression); + Task InsertAsync(T insertObj); + Task InsertOrUpdateAsync(T data); + Task InsertOrUpdateAsync(List datas); + Task InsertRangeAsync(List insertObjs); + Task InsertRangeAsync(T[] insertObjs); + Task InsertReturnIdentityAsync(T insertObj); + Task InsertReturnBigIdentityAsync(T insertObj); + Task InsertReturnSnowflakeIdAsync(T insertObj); + Task> InsertReturnSnowflakeIdAsync(List insertObjs); + Task InsertReturnEntityAsync(T insertObj); + + Task IsAnyAsync(Expression> whereExpression); + Task UpdateSetColumnsTrueAsync(Expression> columns, Expression> whereExpression); + Task UpdateAsync(Expression> columns, Expression> whereExpression); + Task UpdateAsync(T updateObj); + Task UpdateRangeAsync(List updateObjs); + Task UpdateRangeAsync(T[] updateObjs); + + + + + Task CountAsync(Expression> whereExpression, CancellationToken cancellationToken); + Task DeleteAsync(Expression> whereExpression, CancellationToken cancellationToken); + Task DeleteAsync(T deleteObj, CancellationToken cancellationToken); + Task DeleteAsync(List deleteObjs, CancellationToken cancellationToken); + Task DeleteByIdAsync(dynamic id, CancellationToken cancellationToken); + Task DeleteByIdsAsync(dynamic[] ids, CancellationToken cancellationToken); + Task GetByIdAsync(dynamic id, CancellationToken cancellationToken); + Task> GetListAsync(CancellationToken cancellationToken); + Task> GetListAsync(Expression> whereExpression, CancellationToken cancellationToken); + Task> GetPageListAsync(Expression> whereExpression, PageModel page, CancellationToken cancellationToken); + Task> GetPageListAsync(Expression> whereExpression, PageModel page, Expression> orderByExpression = null, OrderByType orderByType = OrderByType.Asc, CancellationToken cancellationToken = default); + Task> GetPageListAsync(List conditionalList, PageModel page, CancellationToken cancellationToken); + Task> GetPageListAsync(List conditionalList, PageModel page, Expression> orderByExpression = null, OrderByType orderByType = OrderByType.Asc, CancellationToken cancellationToken = default); + Task GetSingleAsync(Expression> whereExpression, CancellationToken cancellationToken); + Task GetFirstAsync(Expression> whereExpression, CancellationToken cancellationToken); + Task InsertAsync(T insertObj, CancellationToken cancellationToken); + Task InsertOrUpdateAsync(T data, CancellationToken cancellationToken); + Task InsertOrUpdateAsync(List datas, CancellationToken cancellationToken); + Task InsertRangeAsync(List insertObjs, CancellationToken cancellationToken); + Task InsertRangeAsync(T[] insertObjs, CancellationToken cancellationToken); + Task InsertReturnIdentityAsync(T insertObj, CancellationToken cancellationToken); + Task InsertReturnBigIdentityAsync(T insertObj, CancellationToken cancellationToken); + Task InsertReturnSnowflakeIdAsync(T insertObj, CancellationToken cancellationToken); + Task> InsertReturnSnowflakeIdAsync(List insertObjs, CancellationToken cancellationToken); + Task InsertReturnEntityAsync(T insertObj, CancellationToken cancellationToken); + + Task IsAnyAsync(Expression> whereExpression, CancellationToken cancellationToken); + Task UpdateSetColumnsTrueAsync(Expression> columns, Expression> whereExpression, CancellationToken cancellationToken); + Task UpdateAsync(Expression> columns, Expression> whereExpression, CancellationToken cancellationToken); + Task UpdateAsync(T updateObj, CancellationToken cancellationToken); + Task UpdateRangeAsync(List updateObjs, CancellationToken cancellationToken); + Task UpdateRangeAsync(T[] updateObjs, CancellationToken cancellationToken); + + } +} \ No newline at end of file diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/ISqlBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/ISqlBuilder.cs new file mode 100644 index 000000000..6c0e82722 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/ISqlBuilder.cs @@ -0,0 +1,49 @@ +using System.Data; +using System.Text; +namespace SqlSugar +{ + public partial interface ISqlBuilder + { + SqlSugarProvider Context { get; set; } + CommandType CommandType { get; set; } + String AppendWhereOrAnd(bool isWhere, string sqlString); + string AppendHaving(string sqlString); + + SqlQueryBuilder SqlQueryBuilder { get; set; } + QueryBuilder QueryBuilder { get; set; } + InsertBuilder InsertBuilder { get; set; } + DeleteBuilder DeleteBuilder { get; set; } + UpdateBuilder UpdateBuilder { get; set; } + + string SqlParameterKeyWord { get; } + string SqlFalse { get; } + string SqlDateNow { get; } + string FullSqlDateNow { get; } + string SqlTranslationLeft { get; } + string SqlTranslationRight { get; } + string SqlSelectAll { get; } + + void ChangeJsonType(SugarParameter paramter); + string GetTranslationTableName(string name); + string GetTranslationColumnName(string entityName, string propertyName); + string GetTranslationColumnName(string propertyName); + string GetNoTranslationColumnName(string name); + string GetPackTable(string sql, string shortName); + string GetDefaultShortName(); + + string GetWhere(string fieldName, string conditionalType, int? parameterIndex = null); + string GetUnionAllSql(List sqlList); + string GetUnionSql(List sqlList); + void RepairReplicationParameters(ref string appendSql, SugarParameter[] parameters, int addIndex); + KeyValuePair ConditionalModelToSql(List models, int beginIndex = 0); + string GetUnionFomatSql(string sql); + Type GetNullType(string tableName, string columnName); + string RemoveParentheses(string sql); + string RemoveN(string sql); + void FormatSaveQueueSql(StringBuilder sqlBuilder); + + bool SupportReadToken { get; set; } + Task GetReaderByToken(IDataReader dataReader, CancellationToken cancellationToken); + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/ISqlSugarClient.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/ISqlSugarClient.cs new file mode 100644 index 000000000..46a6cbda6 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/ISqlSugarClient.cs @@ -0,0 +1,245 @@ +using System.Data; +using System.Dynamic; +using System.Linq.Expressions; + +namespace SqlSugar +{ + public interface ISqlSugarClient : IDisposable + { + MappingTableList MappingTables { get; set; } + MappingColumnList MappingColumns { get; set; } + IgnoreColumnList IgnoreColumns { get; set; } + IgnoreColumnList IgnoreInsertColumns { get; set; } + Dictionary TempItems { get; set; } + ConfigQuery ConfigQuery { get; set; } + + Guid ContextID { get; set; } + ConnectionConfig CurrentConnectionConfig { get; set; } + + + IAdo Ado { get; } + AopProvider Aop { get; } + ICodeFirst CodeFirst { get; } + + + IDbFirst DbFirst { get; } + IDbMaintenance DbMaintenance { get; } + EntityMaintenance EntityMaintenance { get; set; } + QueryFilterProvider QueryFilter { get; set; } + IContextMethods Utilities { get; set; } + SugarActionType SugarActionType { get; set; } + + #region Deleteable + DeleteMethodInfo DeleteableByObject(object singleEntityObjectOrListObject); + IDeleteable Deleteable() where T : class, new(); + IDeleteable Deleteable(dynamic primaryKeyValue) where T : class, new(); + IDeleteable Deleteable(dynamic[] primaryKeyValues) where T : class, new(); + IDeleteable Deleteable(Expression> expression) where T : class, new(); + IDeleteable Deleteable(List pkValue) where T : class, new(); + IDeleteable Deleteable(List deleteObjs) where T : class, new(); + IDeleteable Deleteable(T deleteObj) where T : class, new(); + #endregion + + #region Other methods + Task AsyncLock(int timeOutSeconds = 30); + DynamicBuilder DynamicBuilder(); + void ClearTracking(); + void Tracking(T data) where T : class, new(); + void Tracking(List data) where T : class, new(); + SqlSugarClient CopyNew(); + T CreateContext(bool isTran = true) where T : SugarUnitOfWork, new(); + SugarUnitOfWork CreateContext(bool isTran = true); + SplitTableContext SplitHelper(Type entityType); + SplitTableContext SplitHelper() where T : class, new(); + SplitTableContextResult SplitHelper(T data) where T : class, new(); + SplitTableContextResult SplitHelper(List data) where T : class, new(); + DateTime GetDate(); + //SimpleClient GetSimpleClient(); + SimpleClient GetSimpleClient() where T : class, new(); + RepositoryType GetRepository() where RepositoryType : ISugarRepository, new(); + void InitMappingInfo(Type type); + void InitMappingInfo(); + void Open(); + void Close(); + ITenant AsTenant(); + #endregion + + #region Insertable + IInsertable Insertable(Dictionary columnDictionary) where T : class, new(); + IInsertable Insertable(dynamic insertDynamicObject) where T : class, new(); + IInsertable Insertable(List insertObjs) where T : class, new(); + IInsertable Insertable(T insertObj) where T : class, new(); + IInsertable Insertable(T[] insertObjs) where T : class, new(); + InsertMethodInfo InsertableByObject(object singleEntityObjectOrListObject); + IInsertable> InsertableByDynamic(object insertDynamicObject); + #endregion + + #region Queryable + QueryMethodInfo QueryableByObject(Type entityType, string shortName); + QueryMethodInfo QueryableByObject(Type entityType); + ISugarQueryable MasterQueryable(); + ISugarQueryable SlaveQueryable(); + ISugarQueryable SqlQueryable(string sql) where T : class, new(); + ISugarQueryable Queryable(string tableName, string shortName); + ISugarQueryable Queryable(Expression> joinExpression) where T : class, new(); + ISugarQueryable Queryable(Expression> joinExpression); + ISugarQueryable Queryable(Expression> joinExpression); + ISugarQueryable Queryable(Expression> joinExpression) where T : class, new(); + ISugarQueryable Queryable(Expression> joinExpression); + ISugarQueryable Queryable(Expression> joinExpression); + ISugarQueryable Queryable(Expression> joinExpression) where T : class, new(); + ISugarQueryable Queryable(Expression> joinExpression); + ISugarQueryable Queryable(Expression> joinExpression); + ISugarQueryable Queryable(Expression> joinExpression) where T : class, new(); + ISugarQueryable Queryable(Expression> joinExpression); + ISugarQueryable Queryable(Expression> joinExpression); + ISugarQueryable Queryable(Expression> joinExpression) where T : class, new(); + ISugarQueryable Queryable(Expression> joinExpression); + ISugarQueryable Queryable(Expression> joinExpression); + ISugarQueryable Queryable(Expression> joinExpression) where T : class, new(); + ISugarQueryable Queryable(Expression> joinExpression); + ISugarQueryable Queryable(Expression> joinExpression); + ISugarQueryable Queryable(Expression> joinExpression) where T : class, new(); + ISugarQueryable Queryable(Expression> joinExpression); + ISugarQueryable Queryable(Expression> joinExpression); + ISugarQueryable Queryable(Expression> joinExpression) where T : class, new(); + ISugarQueryable Queryable(Expression> joinExpression); + ISugarQueryable Queryable(Expression> joinExpression); + ISugarQueryable Queryable(Expression> joinExpression) where T : class, new(); + ISugarQueryable Queryable(Expression> joinExpression); + ISugarQueryable Queryable(Expression> joinExpression); + ISugarQueryable Queryable(Expression> joinExpression) where T : class, new(); + ISugarQueryable Queryable(Expression> joinExpression); + ISugarQueryable Queryable(Expression> joinExpression); + ISugarQueryable Queryable(Expression> joinExpression) where T : class, new(); + ISugarQueryable Queryable(Expression> joinExpression); + ISugarQueryable Queryable(Expression> joinExpression); + ISugarQueryable Queryable(ISugarQueryable joinQueryable1, ISugarQueryable joinQueryable2, Expression> joinExpression) + where T : class, new() + where T2 : class, new(); + ISugarQueryable Queryable(ISugarQueryable joinQueryable1, ISugarQueryable joinQueryable2, JoinType joinType, Expression> joinExpression) + where T : class, new() + where T2 : class, new(); + + ISugarQueryable Queryable(ISugarQueryable joinQueryable1, ISugarQueryable joinQueryable2, ISugarQueryable joinQueryable3, + JoinType joinType1, Expression> joinExpression1, + JoinType joinType2, Expression> joinExpression2) + where T : class, new() + where T2 : class, new() + where T3 : class, new(); + + ISugarQueryable Queryable(ISugarQueryable joinQueryable1, ISugarQueryable joinQueryable2, ISugarQueryable joinQueryable3, ISugarQueryable joinQueryable4, + JoinType joinType1, Expression> joinExpression1, + JoinType joinType2, Expression> joinExpression2, + JoinType joinType3, Expression> joinExpression4) + where T : class, new() + where T2 : class, new() + where T3 : class, new() + where T4 : class, new(); + ISugarQueryable Queryable(); + ISugarQueryable Queryable(ISugarQueryable queryable); + ISugarQueryable Queryable(ISugarQueryable queryable, string shortName); + ISugarQueryable Queryable(string shortName); + #endregion + + #region Saveable + GridSaveProvider GridSave(List saveList) where T : class, new(); + GridSaveProvider GridSave(List oldList, List saveList) where T : class, new(); + IStorageable Storageable(T[] dataList) where T : class, new(); + IStorageable Storageable(IList dataList) where T : class, new(); + StorageableDataTable Storageable(List> dictionaryList, string tableName); + StorageableDataTable Storageable(Dictionary dictionary, string tableName); + IStorageable Storageable(List dataList) where T : class, new(); + IStorageable Storageable(T data) where T : class, new(); + StorageableDataTable Storageable(DataTable data); + [Obsolete("use Storageable")] + ISaveable Saveable(List saveObjects) where T : class, new(); + [Obsolete("use Storageable")] + ISaveable Saveable(T saveObject) where T : class, new(); + + StorageableMethodInfo StorageableByObject(object singleEntityObjectOrListObject); + + #endregion + + #region Queue + QueueList Queues { get; set; } + void AddQueue(string sql, object parsmeters = null); + void AddQueue(string sql, List parsmeters); + void AddQueue(string sql, SugarParameter parsmeter); + int SaveQueues(bool isTran = true); + Tuple, List, List, List, List, List, List> SaveQueues(bool isTran = true); + Tuple, List, List, List, List, List> SaveQueues(bool isTran = true); + Tuple, List, List, List, List> SaveQueues(bool isTran = true); + Tuple, List, List, List> SaveQueues(bool isTran = true); + Tuple, List, List> SaveQueues(bool isTran = true); + Tuple, List> SaveQueues(bool isTran = true); + List SaveQueues(bool isTran = true); + Task SaveQueuesAsync(bool isTran = true); + Task, List, List, List, List, List, List>> SaveQueuesAsync(bool isTran = true); + Task, List, List, List, List, List>> SaveQueuesAsync(bool isTran = true); + Task, List, List, List, List>> SaveQueuesAsync(bool isTran = true); + Task, List, List, List>> SaveQueuesAsync(bool isTran = true); + Task, List, List>> SaveQueuesAsync(bool isTran = true); + Task, List>> SaveQueuesAsync(bool isTran = true); + Task> SaveQueuesAsync(bool isTran = true); + #endregion + + #region Union + ISugarQueryable Union(List> queryables) where T : class; + ISugarQueryable Union(params ISugarQueryable[] queryables) where T : class; + ISugarQueryable UnionAll(List> queryables) where T : class; + ISugarQueryable UnionAll(params ISugarQueryable[] queryables) where T : class; + #endregion + + #region Updateable + UpdateMethodInfo UpdateableByObject(object singleEntityObjectOrListObject); + UpdateExpressionMethodInfo UpdateableByObject(Type entityType); + IUpdateable Updateable() where T : class, new(); + IUpdateable Updateable(Dictionary columnDictionary) where T : class, new(); + IUpdateable Updateable(dynamic updateDynamicObject) where T : class, new(); + IUpdateable Updateable(Expression> columns) where T : class, new(); + IUpdateable Updateable(Expression> columns) where T : class, new(); + IUpdateable Updateable(List UpdateObjs) where T : class, new(); + IUpdateable Updateable(T UpdateObj) where T : class, new(); + IUpdateable Updateable(T[] UpdateObjs) where T : class, new(); + IUpdateable> UpdateableByDynamic(object updateDynamicObject); + #endregion + + #region Reportable + IReportable Reportable(T data); + IReportable Reportable(List list); + IReportable Reportable(T[] array); + #endregion + + #region Cache + SugarCacheProvider DataCache { get; } + #endregion + + #region Fastest + IFastest Fastest() where T : class, new(); + #endregion + + #region ThenMapper + void ThenMapper(IEnumerable list, Action action); + Task ThenMapperAsync(IEnumerable list, Func action); + #endregion + + #region Nav CUD + InsertNavTaskInit InsertNav(T data) where T : class, new(); + InsertNavTaskInit InsertNav(List datas) where T : class, new(); + InsertNavTaskInit InsertNav(T data, InsertNavRootOptions rootOptions) where T : class, new(); + InsertNavTaskInit InsertNav(List datas, InsertNavRootOptions rootOptions) where T : class, new(); + DeleteNavTaskInit DeleteNav(T data) where T : class, new(); + DeleteNavTaskInit DeleteNav(List datas) where T : class, new(); + DeleteNavTaskInit DeleteNav(Expression> whereExpression) where T : class, new(); + DeleteNavTaskInit DeleteNav(T data, DeleteNavRootOptions options) where T : class, new(); + DeleteNavTaskInit DeleteNav(List datas, DeleteNavRootOptions options) where T : class, new(); + DeleteNavTaskInit DeleteNav(Expression> whereExpression, DeleteNavRootOptions options) where T : class, new(); + UpdateNavTaskInit UpdateNav(T data) where T : class, new(); + UpdateNavTaskInit UpdateNav(List datas) where T : class, new(); + UpdateNavTaskInit UpdateNav(T data, UpdateNavRootOptions rootOptions) where T : class, new(); + UpdateNavTaskInit UpdateNav(List datas, UpdateNavRootOptions rootOptions) where T : class, new(); + #endregion + + } +} \ No newline at end of file diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IStorageable.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IStorageable.cs new file mode 100644 index 000000000..a0f23a002 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IStorageable.cs @@ -0,0 +1,191 @@ +using System.Linq.Expressions; +namespace SqlSugar +{ + public interface IStorageable where T : class, new() + { + IStorageable TableDataRange(Expression> exp); + IStorageable WhereColumns(Expression> columns); + IStorageable WhereColumns(Expression> columns, Func formatTime); + IStorageable WhereColumns(string[] columns); + IStorageable WhereColumns(string[] columns, Func formatTime); + IStorageable SplitInsert(Func, bool> conditions, string message = null); + IStorageable SplitUpdate(Func, bool> conditions, string message = null); + IStorageable Saveable(string inserMessage = null, string updateMessage = null); + IStorageable SplitError(Func, bool> conditions, string message = null); + IStorageable SplitIgnore(Func, bool> conditions, string message = null); + IStorageable DisableFilters(); + IStorageable TranLock(DbLockType LockType = DbLockType.Wait); + IStorageable TranLock(DbLockType? LockType); + IStorageable SplitDelete(Func, bool> conditions, string message = null); + IStorageable SplitOther(Func, bool> conditions, string message = null); + StorageableResult ToStorage(); + StorageableResult GetStorageableResult(); + Task> ToStorageAsync(); + IStorageable As(string tableName); + int ExecuteCommand(); + T ExecuteReturnEntity(); + Task ExecuteReturnEntityAsync(); + Task ExecuteCommandAsync(); + Task ExecuteCommandAsync(CancellationToken cancellationToken); + int ExecuteSqlBulkCopy(); + Task ExecuteSqlBulkCopyAsync(); + IStorageable DefaultAddElseUpdate(); + StorageableSplitProvider SplitTable(); + StorageablePage PageSize(int PaegSize, Action ActionCallBack = null); + } + + public class StorageableInfo where T : class, new() + { + public T Item { get; set; } + internal List Database { get; set; } + internal string[] PkFields { get; set; } + public bool Any(Func expression) + { + return Database.Any(expression); + } + public bool NotAny(Func expression) + { + return !Database.Any(expression); + } + public bool Any() + { + var list = Database.Where(it => true); + foreach (var pk in PkFields) + { + list = list.Where(it => IsEquals(it, pk)); + } + return list.Any(); + } + + private bool IsEquals(T it, string pk) + { + var leftValue = it.GetType().GetProperty(pk).GetValue(it, null); + var rightValue = Item.GetType().GetProperty(pk).GetValue(Item, null); + var left = leftValue.ObjToString(); + var rigth = rightValue.ObjToString(); + if (leftValue != null && (leftValue is decimal || leftValue is decimal?)) + { + return Convert.ToDecimal(leftValue) == Convert.ToDecimal(rightValue); + } + else + { + return left.EqualCase(rigth); + } + } + + public bool NotAny() + { + return !Any(); + } + } + + public class StorageableMessage : StorageableInfo where T : class, new() + { + public string StorageMessage { get; set; } + public StorageType? StorageType { get; set; } + } + + public enum StorageType + { + Insert = 0, + Update = 1, + Delete = 2, + Error = 3, + Other = 4, + Ignore = 5, + } + internal struct KeyValuePair + { + public TKey key; + public TValue value1; + public TValue2 value2; + public KeyValuePair(TKey key, TValue value1, TValue2 value2) + { + this.key = key; + this.value1 = value1; + this.value2 = value2; + } + } + + public class StorageableResult where T : class, new() + { + public List> TotalList { get; set; } + public List> InsertList { get; set; } + public List> UpdateList { get; set; } + public List> DeleteList { get; set; } + public List> ErrorList { get; set; } + public List> IgnoreList { get; set; } + public List> OtherList { get; set; } + public IInsertable AsInsertable { get; set; } + public IUpdateable AsUpdateable { get; set; } + public IDeleteable AsDeleteable { get; set; } + internal List _WhereColumnList { get; set; } + internal string _AsName { get; set; } + internal SqlSugarProvider _Context { get; set; } + + public int BulkCopy() + { + return this._Context.Fastest().AS(_AsName).BulkCopy(InsertList.Select(it => it.Item).ToList()); + } + public Task BulkCopyAsync() + { + return this._Context.Fastest().AS(_AsName).BulkCopyAsync(InsertList.Select(it => it.Item).ToList()); + } + + public int BulkUpdate() + { + var isWhereColums = _WhereColumnList != null && _WhereColumnList.Count != 0; + if (isWhereColums) + { + var updateColumns = this._Context.EntityMaintenance.GetEntityInfo().Columns.Where(it => !it.IsPrimarykey && !it.IsIdentity && !it.IsOnlyIgnoreUpdate && !it.IsIgnore).Select(it => it.DbColumnName ?? it.PropertyName).ToArray(); + return BulkUpdate(updateColumns); + } + else + { + return this._Context.Fastest().AS(_AsName).BulkUpdate(UpdateList.Select(it => it.Item).ToList()); + } + } + public Task BulkUpdateAsync() + { + var isWhereColums = _WhereColumnList != null && _WhereColumnList.Count != 0; + if (isWhereColums) + { + var updateColumns = this._Context.EntityMaintenance.GetEntityInfo().Columns.Where(it => !it.IsPrimarykey && !it.IsIdentity && !it.IsOnlyIgnoreUpdate && !it.IsIgnore).Select(it => it.DbColumnName ?? it.PropertyName).ToArray(); + return BulkUpdateAsync(updateColumns); + } + else + { + return this._Context.Fastest().AS(_AsName).BulkUpdateAsync(UpdateList.Select(it => it.Item).ToList()); + } + } + public int BulkUpdate(params string[] UpdateColumns) + { + + Check.Exception(UpdateColumns == null, "UpdateColumns is null"); + if (_WhereColumnList != null && _WhereColumnList.Count != 0) + { + return this._Context.Fastest().AS(_AsName).BulkUpdate(UpdateList.Select(it => it.Item).ToList(), _WhereColumnList.Select(it => it.DbColumnName).ToArray(), UpdateColumns); + } + else + { + var pkColumns = this._Context.EntityMaintenance.GetEntityInfo().Columns.Where(it => it.IsPrimarykey).Select(it => it.DbColumnName).ToArray(); + Check.Exception(pkColumns.Length == 0, "need primary key"); + return this._Context.Fastest().AS(_AsName).BulkUpdate(UpdateList.Select(it => it.Item).ToList(), pkColumns, UpdateColumns); + } + } + public async Task BulkUpdateAsync(params string[] UpdateColumns) + { + Check.Exception(UpdateColumns == null, "UpdateColumns is null"); + if (_WhereColumnList != null && _WhereColumnList.Count != 0) + { + return await _Context.Fastest().AS(_AsName).BulkUpdateAsync(UpdateList.Select(it => it.Item).ToList(), _WhereColumnList.Select(it => it.DbColumnName).ToArray(), UpdateColumns).ConfigureAwait(false); + } + else + { + var pkColumns = this._Context.EntityMaintenance.GetEntityInfo().Columns.Where(it => it.IsPrimarykey).Select(it => it.DbColumnName).ToArray(); + Check.Exception(pkColumns.Length == 0, "need primary key"); + return await _Context.Fastest().AS(_AsName).BulkUpdateAsync(UpdateList.Select(it => it.Item).ToList(), pkColumns, UpdateColumns).ConfigureAwait(false); + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/ISubInsertable.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/ISubInsertable.cs new file mode 100644 index 000000000..1ddaaf5e2 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/ISubInsertable.cs @@ -0,0 +1,14 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public interface ISubInsertable + { + ISubInsertable AddSubList(Expression> items); + ISubInsertable AddSubList(Expression> tree); + [Obsolete("use ExecuteCommand")] + object ExecuteReturnPrimaryKey(); + object ExecuteCommand(); + Task ExecuteCommandAsync(); + } +} \ No newline at end of file diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/ISugarDataConverter.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/ISugarDataConverter.cs new file mode 100644 index 000000000..06e372e1e --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/ISugarDataConverter.cs @@ -0,0 +1,11 @@ +using System.Data; + +namespace SqlSugar +{ + public interface ISugarDataConverter + { + SugarParameter ParameterConverter(object columnValue, int columnIndex); + + T QueryConverter(IDataRecord dataRecord, int dataRecordIndex); + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/ISugarRepository.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/ISugarRepository.cs new file mode 100644 index 000000000..235e52f77 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/ISugarRepository.cs @@ -0,0 +1,7 @@ +namespace SqlSugar +{ + public interface ISugarRepository + { + ISqlSugarClient Context { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/ITenant.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/ITenant.cs new file mode 100644 index 000000000..a12386530 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/ITenant.cs @@ -0,0 +1,46 @@ +using System.Data; + +namespace SqlSugar +{ + public interface ITenant + { + SqlSugarClient CopyNew(); + string[] GetCurrentConfigIds(); + void BeginTran(); + void BeginTran(IsolationLevel iso); + void CommitTran(); + void RollbackTran(); + Task BeginTranAsync(); + Task BeginTranAsync(IsolationLevel iso); + Task CommitTranAsync(); + Task RollbackTranAsync(); + void ChangeDatabase(object configId); + void ChangeDatabase(Func changeExpression); + SqlSugarTransaction UseTran(); + DbResult UseTran(Action action, Action errorCallBack = null); + Task> UseTranAsync(Func action, Action errorCallBack = null); + DbResult UseTran(Func action, Action errorCallBack = null); + + Task> UseTranAsync(Func> action, Action errorCallBack = null); + void AddConnection(ConnectionConfig connection); + SqlSugarProvider GetConnection(object configId); + void RemoveConnection(object configId); + SqlSugarScopeProvider GetConnectionScope(object configId); + SqlSugarProvider GetConnectionWithAttr(); + SqlSugarScopeProvider GetConnectionScopeWithAttr(); + ISugarQueryable QueryableWithAttr(); + IInsertable InsertableWithAttr(T insertObj) where T : class, new(); + IInsertable InsertableWithAttr(List insertObjs) where T : class, new(); + IUpdateable UpdateableWithAttr(T updateObj) where T : class, new(); + IUpdateable UpdateableWithAttr() where T : class, new(); + IUpdateable UpdateableWithAttr(List updateObjs) where T : class, new(); + IDeleteable DeleteableWithAttr(T deleteObjs) where T : class, new(); + IDeleteable DeleteableWithAttr(List deleteObjs) where T : class, new(); + IDeleteable DeleteableWithAttr() where T : class, new(); + + bool IsAnyConnection(object configId); + + void Close(); + void Open(); + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IUpdateable.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IUpdateable.cs new file mode 100644 index 000000000..5e040de30 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/IUpdateable.cs @@ -0,0 +1,136 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public interface IUpdateable where T : class, new() + { + UpdateBuilder UpdateBuilder { get; set; } + bool UpdateParameterIsNull { get; set; } + + int ExecuteCommandWithOptLock(bool isThrowError = false); + int ExecuteCommandWithOptLockIF(bool? IsVersionValidation, bool? IsOptLock = null); + Task ExecuteCommandWithOptLockAsync(bool isThrowError = false); + int ExecuteCommand(); + bool ExecuteCommandHasChange(); + Task ExecuteCommandAsync(); + Task ExecuteCommandAsync(CancellationToken token); + Task ExecuteCommandHasChangeAsync(); + Task ExecuteCommandHasChangeAsync(CancellationToken token); + + + IUpdateable AS(string tableName); + IUpdateable AsType(Type tableNameType); + IUpdateable With(string lockString); + IUpdateable In(Expression> inField, ISugarQueryable childQueryExpression); + + IUpdateable Where(Expression> expression); + IUpdateable WhereIF(bool isWhere, Expression> expression); + IUpdateable Where(string whereSql, object parameters = null); + + IUpdateable Where(string fieldName, string conditionalType, object fieldValue); + + + + /// + /// Non primary key entity update function,.WhereColumns(it=>new{ it.Id }) + /// + /// + /// + IUpdateable WhereColumns(Expression> columns); + IUpdateable WhereColumns(string columnName); + IUpdateable WhereColumns(params string[] columnNames); + IUpdateable Where(List conditionalModels); + + /// + /// .UpdateColumns(it=>new{ it.Name,it.Price}) + /// + /// + /// + IUpdateable UpdateColumns(Expression> columns); + IUpdateable UpdateColumns(Expression> columns, bool appendColumnsByDataFilter); + IUpdateable UpdateColumns(params string[] columns); + IUpdateable UpdateColumns(string[] columns, bool appendColumnsByDataFilter); + + + /// + ///.SetColumns(it=>it.Name=="a") + /// + /// + /// + IUpdateable SetColumns(Expression> columns); + /// + /// .SetColumns(it=> new class() { it.Name="a",it.Price=0}) + /// + /// + /// + IUpdateable SetColumns(Expression> columns); + IUpdateable SetColumns(Expression> columns, bool appendColumnsByDataFilter); + IUpdateable SetColumns(string fieldName, object fieldValue); + + IUpdateable SetColumns(Expression> filedNameExpression, object fieldValue); + IUpdateable SetColumns(Expression> filedNameExpression, Expression> valueExpression); + IUpdateable SetColumnsIF(bool isUpdateColumns, Expression> filedNameExpression, object fieldValue); + IUpdateable UpdateColumnsIF(bool isUpdateColumns, Expression> columns); + IUpdateable UpdateColumnsIF(bool isUpdateColumns, params string[] columns); + + + IUpdateable SetColumnsIF(bool isUpdateColumns, Expression> columns); + IUpdateable SetColumnsIF(bool isUpdateColumns, Expression> columns); + + + + IUpdateable IgnoreColumns(bool ignoreAllNullColumns, bool isOffIdentity = false, bool ignoreAllDefaultValue = false); + IUpdateable IgnoreColumns(Expression> columns); + IUpdateable IgnoreColumnsIF(bool isIgnore, Expression> columns); + + IUpdateable IgnoreColumns(params string[] columns); + IUpdateable IgnoreNullColumns(bool isIgnoreNull = true); + + + IUpdateable IsEnableUpdateVersionValidation(); + IUpdateable EnableDiffLogEvent(object businessData = null); + IUpdateable EnableDiffLogEventIF(bool isEnableDiffLog, object businessData = null); + IUpdateable ReSetValue(Action setValueExpression); + IUpdateable PublicSetColumns(Expression> filedNameExpression, string computationalSymbol); + IUpdateable PublicSetColumns(Expression> filedNameExpression, Expression> ValueExpExpression); + IUpdateable RemoveDataCache(); + IUpdateable RemoveDataCache(string likeString); + IUpdateable CallEntityMethod(Expression> method); + KeyValuePair> ToSql(); + string ToSqlString(); + void AddQueue(); + SplitTableUpdateProvider SplitTable(Func, IEnumerable> getTableNamesFunc); + SplitTableUpdateByObjectProvider SplitTable(); + IUpdateable EnableQueryFilter(); + IUpdateable Clone(); + IUpdateable InnerJoin(Expression> joinExpress); + IUpdateable InnerJoin(Expression> joinExpress, string tableName); + IUpdateable InnerJoin(ISugarQueryable queryable, Expression> joinExpress); + UpdateablePage PageSize(int pageSize); + IUpdateable In(object[] ids); + ParameterUpdateable UseParameter(); + } + public interface IUpdateable + { + int ExecuteCommand(); + Task ExecuteCommandAsync(); + IUpdateable InnerJoin(Expression> joinExpress); + IUpdateable SetColumns(Expression> columns); + IUpdateable Where(Expression> whereExpression); + } + public interface IUpdateable + { + IUpdateable InnerJoin(Expression> joinExpress); + int ExecuteCommand(); + Task ExecuteCommandAsync(); + IUpdateable SetColumns(Expression> columns); + IUpdateable Where(Expression> whereExpression); + } + public interface IUpdateable + { + int ExecuteCommand(); + Task ExecuteCommandAsync(); + IUpdateable SetColumns(Expression> columns); + IUpdateable Where(Expression> whereExpression); + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/Insertable.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/Insertable.cs new file mode 100644 index 000000000..1979fc0ce --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Interface/Insertable.cs @@ -0,0 +1,67 @@ +using System.Linq.Expressions; + +namespace SqlSugar +{ + public partial interface IInsertable where T : class, new() + { + InsertBuilder InsertBuilder { get; set; } + int ExecuteCommand(); + Task ExecuteCommandAsync(); + Task ExecuteCommandAsync(CancellationToken token); + List ExecuteReturnPkList(); + Task> ExecuteReturnPkListAsync(); + long ExecuteReturnSnowflakeId(); + List ExecuteReturnSnowflakeIdList(); + Task ExecuteReturnSnowflakeIdAsync(); + Task ExecuteReturnSnowflakeIdAsync(CancellationToken token); + Task> ExecuteReturnSnowflakeIdListAsync(); + Task> ExecuteReturnSnowflakeIdListAsync(CancellationToken token); + int ExecuteReturnIdentity(); + Task ExecuteReturnIdentityAsync(); + Task ExecuteReturnIdentityAsync(CancellationToken token); + T ExecuteReturnEntity(); + T ExecuteReturnEntity(bool isIncludesAllFirstLayer); + Task ExecuteReturnEntityAsync(); + Task ExecuteReturnEntityAsync(bool isIncludesAllFirstLayer); + bool ExecuteCommandIdentityIntoEntity(); + Task ExecuteCommandIdentityIntoEntityAsync(); + long ExecuteReturnBigIdentity(); + Task ExecuteReturnBigIdentityAsync(); + Task ExecuteReturnBigIdentityAsync(CancellationToken token); + IInsertable AS(string tableName); + IInsertable AsType(Type tableNameType); + IInsertable With(string lockString); + IInsertable InsertColumns(Expression> columns); + IInsertable InsertColumns(params string[] columns); + + IInsertable IgnoreColumns(Expression> columns); + IInsertable IgnoreColumns(params string[] columns); + IInsertable IgnoreColumns(bool ignoreNullColumn, bool isOffIdentity = false); + IInsertable IgnoreColumnsNull(bool isIgnoreNull = true); + + ISubInsertable AddSubList(Expression> subForeignKey); + ISubInsertable AddSubList(Expression> tree); + IParameterInsertable UseParameter(); + IInsertable CallEntityMethod(Expression> method); + + IInsertable EnableDiffLogEvent(object businessData = null); + IInsertable EnableDiffLogEventIF(bool isDiffLogEvent, object businessData = null); + IInsertable RemoveDataCache(); + IInsertable RemoveDataCache(string likeString); + KeyValuePair> ToSql(); + string ToSqlString(); + SqlServerBlukCopy UseSqlServer(); + MySqlBlukCopy UseMySql(); + OracleBlukCopy UseOracle(); + + SplitInsertable SplitTable(); + SplitInsertable SplitTable(SplitType splitType); + void AddQueue(); + IInsertable MySqlIgnore(); + IInsertable MySqlIgnore(bool isIgnore); + IInsertable PostgreSQLConflictNothing(string[] columns); + IInsertable OffIdentity(); + IInsertable OffIdentity(bool isSetOn); + InsertablePage PageSize(int pageSize); + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/DynamicLinq/DynamicCoreHelper.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/DynamicLinq/DynamicCoreHelper.cs new file mode 100644 index 000000000..1ce9168ec --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/DynamicLinq/DynamicCoreHelper.cs @@ -0,0 +1,171 @@ +using System.Linq.Expressions; +using System.Runtime.CompilerServices; +using System.Text.RegularExpressions; + +namespace SqlSugar +{ + public static class DynamicCoreHelper + { + public static BuildPropertySelectorResult BuildPropertySelector(string shortName, Type type, List propertyNames, params object[] args) + { + BuildPropertySelectorResult result = new BuildPropertySelectorResult(); + if (type == null) + throw new ArgumentNullException(nameof(type)); + + if (propertyNames == null || propertyNames.Count == 0) + throw new ArgumentNullException(nameof(propertyNames)); + + var parameter = Expression.Parameter(type, shortName); + + // 解析多个属性,生成匿名类型 + var newAnonymousTypeStr = $"new {{ {string.Join(", ", propertyNames)} }}"; + newAnonymousTypeStr = ReplaceFormatParameters(newAnonymousTypeStr); + result.formattableString = FormattableStringFactory.Create(newAnonymousTypeStr, args); + var lambda = SqlSugarDynamicExpressionParser.ParseLambda(new[] { parameter }, null, newAnonymousTypeStr, args); + result.ResultNewType = lambda.Body.Type; + result.ShortName = shortName; + result.Exp = lambda; + return result; + } + + public class BuildPropertySelectorResult + { + public FormattableString formattableString { get; set; } + public string ShortName { get; set; } + public Type ResultNewType { get; set; } + public LambdaExpression Exp { get; internal set; } + } + public static Expression> GetWhere(string shortName, FormattableString whereSql) + { + return (Expression>)GetWhere(typeof(T), shortName, whereSql); + } + public static LambdaExpression GetWhere(Type entityType, string shortName, FormattableString whereSql) + { + var parameter = Expression.Parameter(entityType, shortName); + + // 提取 FormattableString 中的参数值 + var arguments = whereSql.GetArguments(); + + + var sql = ReplaceFormatParameters(whereSql.Format); + + sql = CompatibleDynamicLinqCoreBug(sql); + + // 构建动态表达式,使用常量表达式和 whereSql 中的参数值 + var lambda = SqlSugarDynamicExpressionParser.ParseLambda( + new[] { parameter }, + typeof(bool), + sql, + whereSql.GetArguments() + ); + + return lambda; + } + + private static string CompatibleDynamicLinqCoreBug(string sql) + { + //Compatible DynamicCore.Linq bug + if (sql?.Contains("SqlFunc.") == true) + { + sql = sql.Replace("SqlFunc.LessThan(", "SqlFunc.LessThan_LinqDynamicCore("); + sql = sql.Replace("SqlFunc.LessThan (", "SqlFunc.LessThan_LinqDynamicCore ("); + sql = sql.Replace("SqlFunc.GreaterThan(", "SqlFunc.GreaterThan_LinqDynamicCore("); + sql = sql.Replace("SqlFunc.GreaterThan (", "SqlFunc.GreaterThan_LinqDynamicCore ("); + } + return sql; + } + + public static LambdaExpression GetObject(Type entityType, string shortName, FormattableString whereSql) + { + var parameter = Expression.Parameter(entityType, shortName); + + // 提取 FormattableString 中的参数值 + var arguments = whereSql.GetArguments(); + + + var sql = ReplaceFormatParameters(whereSql.Format); + + // 构建动态表达式,使用常量表达式和 whereSql 中的参数值 + var lambda = SqlSugarDynamicExpressionParser.ParseLambda( + new[] { parameter }, + typeof(object), + sql, + whereSql.GetArguments() + ); + + return lambda; + } + public static LambdaExpression GetWhere(Dictionary parameterDictionary, FormattableString whereSql) + { + var parameters = parameterDictionary.Select(it => Expression.Parameter(it.Value, it.Key)).ToArray(); + + // 提取 FormattableString 中的参数值 + var arguments = whereSql.GetArguments(); + + + var sql = ReplaceFormatParameters(whereSql.Format); + + sql = CompatibleDynamicLinqCoreBug(sql); + + // 构建动态表达式,使用常量表达式和 whereSql 中的参数值 + var lambda = SqlSugarDynamicExpressionParser.ParseLambda( + parameters, + typeof(bool), + sql, + whereSql.GetArguments() + ); + + return lambda; + } + public static LambdaExpression GetMember(Dictionary parameterDictionary, Type propertyType, FormattableString memberSql) + { + var parameters = parameterDictionary.Select(it => Expression.Parameter(it.Value, it.Key)).ToArray(); + + // 提取 FormattableString 中的参数值 + var arguments = memberSql.GetArguments(); + + + var sql = ReplaceFormatParameters(memberSql.Format); + + // 构建动态表达式,使用常量表达式和 whereSql 中的参数值 + var lambda = SqlSugarDynamicExpressionParser.ParseLambda( + parameters, + propertyType, + sql, + memberSql.GetArguments() + ); + + return lambda; + } + public static LambdaExpression GetMember(Type entityType, Type propertyType, string shortName, FormattableString memberSql) + { + var parameter = Expression.Parameter(entityType, shortName); + + // 提取 FormattableString 中的参数值 + var arguments = memberSql.GetArguments(); + + + var sql = ReplaceFormatParameters(memberSql.Format); + + // 构建动态表达式,使用常量表达式和 whereSql 中的参数值 + var lambda = SqlSugarDynamicExpressionParser.ParseLambda( + new[] { parameter }, + propertyType, + sql, + memberSql.GetArguments() + ); + + return lambda; + } + private static string ReplaceFormatParameters(string format) + { + int parameterIndex = 0; // 起始参数索引 + return Regex.Replace(format, @"\{\d+\}", match => + { + string replacement = $"@{parameterIndex}"; + parameterIndex++; + return replacement; + }); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/DynamicLinq/DynamicParameters.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/DynamicLinq/DynamicParameters.cs new file mode 100644 index 000000000..9e4830562 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/DynamicLinq/DynamicParameters.cs @@ -0,0 +1,50 @@ +namespace SqlSugar +{ + public static class DynamicParameters + { + public static Dictionary Create(string parameterName, Type parameterType1, string parameterName2, Type parameterType2) + { + return new Dictionary() { { parameterName, parameterType1 }, { parameterName2, parameterType2 } }; + } + + public static Dictionary Create(string parameterName, Type parameterType1, string parameterName2, Type parameterType2, string parameterName3, Type parameterType3) + { + return new Dictionary() { { parameterName, parameterType1 }, { parameterName2, parameterType2 }, { parameterName3, parameterType3 } }; + } + + public static Dictionary Create(string parameterName, Type parameterType1, string parameterName2, Type parameterType2, string parameterName3, Type parameterType3, string parameterName4, Type parameterType4) + { + return new Dictionary() { { parameterName, parameterType1 }, { parameterName2, parameterType2 }, { parameterName3, parameterType3 }, { parameterName4, parameterType4 } }; + } + + public static Dictionary Create(string parameterName, Type parameterType1, string parameterName2, Type parameterType2, string parameterName3, Type parameterType3, string parameterName4, Type parameterType4, string parameterName5, Type parameterType5) + { + return new Dictionary() { { parameterName, parameterType1 }, { parameterName2, parameterType2 }, { parameterName3, parameterType3 }, { parameterName4, parameterType4 }, { parameterName5, parameterType5 } }; + } + + public static Dictionary Create(string parameterName, Type parameterType1, string parameterName2, Type parameterType2, string parameterName3, Type parameterType3, string parameterName4, Type parameterType4, string parameterName5, Type parameterType5, string parameterName6, Type parameterType6) + { + return new Dictionary() { { parameterName, parameterType1 }, { parameterName2, parameterType2 }, { parameterName3, parameterType3 }, { parameterName4, parameterType4 }, { parameterName5, parameterType5 }, { parameterName6, parameterType6 } }; + } + + public static Dictionary Create(string parameterName, Type parameterType1, string parameterName2, Type parameterType2, string parameterName3, Type parameterType3, string parameterName4, Type parameterType4, string parameterName5, Type parameterType5, string parameterName6, Type parameterType6, string parameterName7, Type parameterType7) + { + return new Dictionary() { { parameterName, parameterType1 }, { parameterName2, parameterType2 }, { parameterName3, parameterType3 }, { parameterName4, parameterType4 }, { parameterName5, parameterType5 }, { parameterName6, parameterType6 }, { parameterName7, parameterType7 } }; + } + + public static Dictionary Create(string parameterName, Type parameterType1, string parameterName2, Type parameterType2, string parameterName3, Type parameterType3, string parameterName4, Type parameterType4, string parameterName5, Type parameterType5, string parameterName6, Type parameterType6, string parameterName7, Type parameterType7, string parameterName8, Type parameterType8) + { + return new Dictionary() { { parameterName, parameterType1 }, { parameterName2, parameterType2 }, { parameterName3, parameterType3 }, { parameterName4, parameterType4 }, { parameterName5, parameterType5 }, { parameterName6, parameterType6 }, { parameterName7, parameterType7 }, { parameterName8, parameterType8 } }; + } + + public static Dictionary Create(string parameterName, Type parameterType1, string parameterName2, Type parameterType2, string parameterName3, Type parameterType3, string parameterName4, Type parameterType4, string parameterName5, Type parameterType5, string parameterName6, Type parameterType6, string parameterName7, Type parameterType7, string parameterName8, Type parameterType8, string parameterName9, Type parameterType9) + { + return new Dictionary() { { parameterName, parameterType1 }, { parameterName2, parameterType2 }, { parameterName3, parameterType3 }, { parameterName4, parameterType4 }, { parameterName5, parameterType5 }, { parameterName6, parameterType6 }, { parameterName7, parameterType7 }, { parameterName8, parameterType8 }, { parameterName9, parameterType9 } }; + } + + public static Dictionary Create(string parameterName, Type parameterType1, string parameterName2, Type parameterType2, string parameterName3, Type parameterType3, string parameterName4, Type parameterType4, string parameterName5, Type parameterType5, string parameterName6, Type parameterType6, string parameterName7, Type parameterType7, string parameterName8, Type parameterType8, string parameterName9, Type parameterType9, string parameterName10, Type parameterType10) + { + return new Dictionary() { { parameterName, parameterType1 }, { parameterName2, parameterType2 }, { parameterName3, parameterType3 }, { parameterName4, parameterType4 }, { parameterName5, parameterType5 }, { parameterName6, parameterType6 }, { parameterName7, parameterType7 }, { parameterName8, parameterType8 }, { parameterName9, parameterType9 }, { parameterName10, parameterType10 } }; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/DynamicLinq/SqlSugarDynamicExpressionParser.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/DynamicLinq/SqlSugarDynamicExpressionParser.cs new file mode 100644 index 000000000..2821b2cdf --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/DynamicLinq/SqlSugarDynamicExpressionParser.cs @@ -0,0 +1,53 @@ +using System.Linq.Expressions; +using System.Reflection; + +namespace SqlSugar +{ + public static class SqlSugarDynamicExpressionParser + { + + public static LambdaExpression ParseLambda(ParameterExpression[] parameterExpressions, Type type, string sql, object[] objects) + { + if (StaticConfig.DynamicExpressionParserType == null) + { + Check.ExceptionEasy("Please at program startup assignment: StaticConfig DynamicExpressionParserType = typeof (DynamicExpressionParser); NUGET is required to install Dynamic.Core", "请在程序启动时赋值: StaticConfig.DynamicExpressionParserType = typeof(DynamicExpressionParser); 需要NUGET安装 Dynamic.Core"); + } + + if (StaticConfig.DynamicExpressionParsingConfig != null) + { + // 查找 ParseLambda 方法 + MethodInfo parseLambdaMethod = StaticConfig.DynamicExpressionParserType + .GetMyMethod("ParseLambda", 5, StaticConfig.DynamicExpressionParsingConfig.GetType(), typeof(ParameterExpression[]), typeof(Type), typeof(string), typeof(object[])); + + if (parseLambdaMethod == null) + { + throw new InvalidOperationException("ParseLambda method not found in DynamicExpressionParserType."); + } + + // 调用 ParseLambda 方法来解析 Lambda 表达式 + var lambda = (LambdaExpression)parseLambdaMethod.Invoke(null, new object[] { StaticConfig.DynamicExpressionParsingConfig, parameterExpressions, type, sql, objects }); + + return lambda; + } + else + { + + // 查找 ParseLambda 方法 + MethodInfo parseLambdaMethod = StaticConfig.DynamicExpressionParserType + .GetMyMethod("ParseLambda", 4, typeof(ParameterExpression[]), typeof(Type), typeof(string), typeof(object[])); + + if (parseLambdaMethod == null) + { + throw new InvalidOperationException("ParseLambda method not found in DynamicExpressionParserType."); + } + + // 调用 ParseLambda 方法来解析 Lambda 表达式 + var lambda = (LambdaExpression)parseLambdaMethod.Invoke(null, new object[] { parameterExpressions, type, sql, objects }); + + return lambda; + } + } + + } +} + diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Entities/JsonDeleteResult.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Entities/JsonDeleteResult.cs new file mode 100644 index 000000000..6861f3475 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Entities/JsonDeleteResult.cs @@ -0,0 +1,7 @@ +namespace SqlSugar +{ + public class JsonDeleteResult + { + public int UpdateRows { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Entities/JsonInsertResult.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Entities/JsonInsertResult.cs new file mode 100644 index 000000000..8b4765e74 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Entities/JsonInsertResult.cs @@ -0,0 +1,8 @@ +namespace SqlSugar +{ + public class JsonInsertResult + { + public int IdentityValue { get; set; } + public int InsertCount { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Entities/JsonQueryResult.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Entities/JsonQueryResult.cs new file mode 100644 index 000000000..538bae170 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Entities/JsonQueryResult.cs @@ -0,0 +1,9 @@ +namespace SqlSugar +{ + public class JsonQueryResult + { + public object Data { get; set; } + public Dictionary TableInfo { get; set; } + public int ToTalRows { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Entities/JsonTableConfig.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Entities/JsonTableConfig.cs new file mode 100644 index 000000000..acfc38501 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Entities/JsonTableConfig.cs @@ -0,0 +1,23 @@ +namespace SqlSugar +{ + + public class JsonTableConfig + { + public string TableName { get; set; } + public string TableDescription { get; set; } + public List Conditionals { get; set; } + public bool? AllowQuery { get; set; } + public bool? AllowUpdate { get; set; } + public bool? AllowDelete { get; set; } + public bool? AllowInsert { get; set; } + public List Columns { get; set; } + } + public class JsonColumnConfig + { + public string Name { get; set; } + public string Description { get; set; } + public string ValidateMessage { get; set; } + public object Validate { get; set; } + bool? AllowEdit { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Entities/JsonUpdateResult.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Entities/JsonUpdateResult.cs new file mode 100644 index 000000000..086352d19 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Entities/JsonUpdateResult.cs @@ -0,0 +1,7 @@ +namespace SqlSugar +{ + public class JsonUpdateResult + { + public int UpdateRows { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Enums/AsNameFormatType.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Enums/AsNameFormatType.cs new file mode 100644 index 000000000..f06d0fd5a --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Enums/AsNameFormatType.cs @@ -0,0 +1,8 @@ +namespace SqlSugar +{ + public enum AsNameFormatType + { + Default = 0, + NoConvert = 1 + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Enums/Json2SqlType.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Enums/Json2SqlType.cs new file mode 100644 index 000000000..6e5603ff8 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Enums/Json2SqlType.cs @@ -0,0 +1,12 @@ +namespace SqlSugar +{ + public enum JsonProviderType + { + Queryable, + QueryableCount, + Insertable, + InsertableIdentity, + Updateable, + Deleteable + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Interface/IFuncModel.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Interface/IFuncModel.cs new file mode 100644 index 000000000..6c920a7a4 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Interface/IFuncModel.cs @@ -0,0 +1,6 @@ +namespace SqlSugar +{ + public interface IFuncModel + { + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Interface/IJsonClient.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Interface/IJsonClient.cs new file mode 100644 index 000000000..45d04a595 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Interface/IJsonClient.cs @@ -0,0 +1,13 @@ +namespace SqlSugar +{ + public interface IJsonClient + { + ISqlSugarClient Context { get; set; } + + IJsonProvider Deleteable(string json); + List GetTableNameList(string json); + IJsonProvider Insertable(string json); + IJsonQueryableProvider Queryable(string json); + IJsonProvider Updateable(string json); + } +} \ No newline at end of file diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Interface/IJsonDeleteableProvider.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Interface/IJsonDeleteableProvider.cs new file mode 100644 index 000000000..92bfe2e3e --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Interface/IJsonDeleteableProvider.cs @@ -0,0 +1,7 @@ +namespace SqlSugar +{ + public interface IJsonDeleteableProvider : IJsonProvider + { + // IJsonQueryableProvider UpdateColumns(string tableName, string[] columns); + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Interface/IJsonInsertableProvider.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Interface/IJsonInsertableProvider.cs new file mode 100644 index 000000000..98b49595b --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Interface/IJsonInsertableProvider.cs @@ -0,0 +1,7 @@ +namespace SqlSugar +{ + public interface IJsonInsertableProvider : IJsonProvider + { + // IJsonQueryableProvider UpdateColumns(string tableName, string[] columns); + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Interface/IJsonProvider.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Interface/IJsonProvider.cs new file mode 100644 index 000000000..6cbb9dd4a --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Interface/IJsonProvider.cs @@ -0,0 +1,11 @@ +namespace SqlSugar +{ + + public interface IJsonProvider + { + List ToSqlList(); + SqlObjectResult ToSql(); + List ToSqlString(); + T ToResult(); + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Interface/IJsonQueryableProvider.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Interface/IJsonQueryableProvider.cs new file mode 100644 index 000000000..26b1259b5 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Interface/IJsonQueryableProvider.cs @@ -0,0 +1,9 @@ +namespace SqlSugar +{ + public interface IJsonQueryableProvider : IJsonProvider + { + IJsonQueryableProvider ShowDesciption(); + IJsonQueryableProvider UseAuthentication(JsonTableConfig config); + IJsonQueryableProvider UseAuthentication(List config); + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Interface/IJsonToModel.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Interface/IJsonToModel.cs new file mode 100644 index 000000000..94bd2de61 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Interface/IJsonToModel.cs @@ -0,0 +1,15 @@ +namespace SqlSugar +{ + /// + /// Json to model + /// + public partial interface IContextMethods + { + List JsonToOrderByModels(string json); + List JsonToGroupByModels(string json); + List> JsonToColumnsModels(string json); + List JsonToSelectModels(string json); + IFuncModel JsonToSqlFuncModels(string json); + JoinModel JsonToJoinModels(string json); + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Interface/IJsonUpdateableProvider.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Interface/IJsonUpdateableProvider.cs new file mode 100644 index 000000000..6b2a720bd --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Interface/IJsonUpdateableProvider.cs @@ -0,0 +1,7 @@ +namespace SqlSugar +{ + public interface IJsonUpdateableProvider : IJsonProvider + { + // IJsonQueryableProvider UpdateColumns(string tableName, string[] columns); + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Interface/IModelToSql.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Interface/IModelToSql.cs new file mode 100644 index 000000000..25ddc7233 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Interface/IModelToSql.cs @@ -0,0 +1,14 @@ +namespace SqlSugar +{ + /// + /// Json Model to sql + /// + public partial interface ISqlBuilder + { + KeyValuePair OrderByModelToSql(List models); + KeyValuePair GroupByModelToSql(List models); + KeyValuePair SelectModelToSql(List models); + KeyValuePair FuncModelToSql(IFuncModel model); + } + +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Interface/ISugarQueryable.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Interface/ISugarQueryable.cs new file mode 100644 index 000000000..39ece9c02 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Interface/ISugarQueryable.cs @@ -0,0 +1,16 @@ +namespace SqlSugar +{ + public partial interface ISugarQueryable + { + ISugarQueryable Having(IFuncModel model); + ISugarQueryable OrderBy(List models); + ISugarQueryable GroupBy(List models); + DynamicCoreSelectModel Select(string expShortName, List columns, params object[] args); + ISugarQueryable Select(List models); + ISugarQueryable Select(List models); + ISugarQueryable Select(List models, AsNameFormatType type); + ISugarQueryable AS(string tableName, string shortName); + ISugarQueryable AddJoinInfo(string tableName, string shortName, IFuncModel models, JoinType type = JoinType.Left); + ISugarQueryable AddJoinInfo(List joinInfoParameters); + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonModels/FuncModel.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonModels/FuncModel.cs new file mode 100644 index 000000000..fa7e81b5b --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonModels/FuncModel.cs @@ -0,0 +1,18 @@ +namespace SqlSugar +{ + public class ObjectFuncModel : IFuncModel + { + public string FuncName { get; set; } + public List Parameters { get; set; } + + public static ObjectFuncModel Create(string FuncName, params object[] Parameters) + { + return new ObjectFuncModel() { FuncName = FuncName, Parameters = Parameters?.ToList() }; + } + } + public class ArrayFuncModel : IFuncModel + { + public List Objects { get; set; } + } + +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonModels/GroupByModel.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonModels/GroupByModel.cs new file mode 100644 index 000000000..9b986dba7 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonModels/GroupByModel.cs @@ -0,0 +1,11 @@ +namespace SqlSugar +{ + public class GroupByModel + { + public object FieldName { get; set; } + public static List Create(params GroupByModel[] groupModels) + { + return groupModels.ToList(); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonModels/JoinModel.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonModels/JoinModel.cs new file mode 100644 index 000000000..7e22ff0c9 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonModels/JoinModel.cs @@ -0,0 +1,11 @@ +namespace SqlSugar +{ + + public class JoinModel + { + public string TableName { get; set; } + public string ShortName { get; set; } + public ObjectFuncModel OnWhereList { get; set; } + public JoinType JoinType { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonModels/JsonQueryParameter.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonModels/JsonQueryParameter.cs new file mode 100644 index 000000000..91b9c7cb9 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonModels/JsonQueryParameter.cs @@ -0,0 +1,13 @@ +namespace SqlSugar +{ + internal class JsonQueryParameter + { + public bool IsSelect { get; set; } + public bool IsJoin { get; set; } + public int? PageIndex { get; set; } + public int? PageSize { get; set; } = 20; + public bool JoinNoSelect { get { return IsJoin && !IsSelect; } } + + public bool IsPage { get { return PageIndex != null; } } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonModels/JsonSqlModels.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonModels/JsonSqlModels.cs new file mode 100644 index 000000000..28d7e82f6 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonModels/JsonSqlModels.cs @@ -0,0 +1,18 @@ +namespace SqlSugar +{ + public class SqlObjectResult + { + + + public SqlObjectResult(KeyValuePair> keyValuePair, JsonProviderType jsonSqlType) + { + this.Sql = keyValuePair.Key; + this.Parameters = keyValuePair.Value; + this.JsonSqlType = jsonSqlType; + } + + public JsonProviderType JsonSqlType { get; set; } + public string Sql { get; set; } + public List Parameters { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonModels/JsonTableNameInfo.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonModels/JsonTableNameInfo.cs new file mode 100644 index 000000000..bc368fee9 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonModels/JsonTableNameInfo.cs @@ -0,0 +1,9 @@ +namespace SqlSugar +{ + public class JsonTableNameInfo + { + public string TableName { get; set; } + public string ShortName { get; set; } + public string Scheme { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonModels/OrderByModel.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonModels/OrderByModel.cs new file mode 100644 index 000000000..89a715782 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonModels/OrderByModel.cs @@ -0,0 +1,12 @@ +namespace SqlSugar +{ + public class OrderByModel + { + public object FieldName { get; set; } + public OrderByType OrderByType { get; set; } + public static List Create(params OrderByModel[] orderByModel) + { + return orderByModel.ToList(); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonModels/SelectFieldModel.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonModels/SelectFieldModel.cs new file mode 100644 index 000000000..62807776c --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonModels/SelectFieldModel.cs @@ -0,0 +1,17 @@ +namespace SqlSugar +{ + public class SelectModel + { + public object FieldName { get; set; } + + [Obsolete("名字拼错使用FieldName")] + public object FiledName { get { return FieldName; } set { FieldName = value; } } + + public string AsName { get; set; } + + public static List Create(params SelectModel[] SelectModels) + { + return SelectModels.ToList(); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonToModel/Helper.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonToModel/Helper.cs new file mode 100644 index 000000000..1447dbc8a --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonToModel/Helper.cs @@ -0,0 +1,43 @@ +using Newtonsoft.Json.Linq; + +namespace SqlSugar +{ + /// + /// SqlFunc to model + /// + public partial class ContextMethods : IContextMethods + { + private bool IsObjct(JToken sqlfunc) + { + return sqlfunc.Type == JTokenType.Object; + } + private bool IsArray(string sqlfunc) + { + return sqlfunc.StartsWith('['); + } + public static bool IsSqlFunc(JToken item, string fileName) + { + return item.Type == JTokenType.Object || fileName.Contains("SqlFunc_", StringComparison.CurrentCultureIgnoreCase); + } + private bool IsObject(JToken parameters) + { + return parameters.Type == JTokenType.Object; + } + private bool IsArray(JToken parameters) + { + return parameters.Type == JTokenType.Array; + } + private bool IsString(JToken parameters) + { + return parameters.Type == JTokenType.String; + } + private bool IsFieldName(JToken item) + { + return item.ObjToString().Contains("fieldname", StringComparison.CurrentCultureIgnoreCase); + } + private bool IsArraySingleItem(JToken item) + { + return IsArray(item) && item.Count() == 1; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonToModel/JsonSqlFuncToModel.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonToModel/JsonSqlFuncToModel.cs new file mode 100644 index 000000000..745c7aaa8 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonToModel/JsonSqlFuncToModel.cs @@ -0,0 +1,84 @@ +using Newtonsoft.Json.Linq; + +using System.Text; + +namespace SqlSugar +{ + /// + /// SqlFunc to model + /// + public partial class ContextMethods : IContextMethods + { + #region Root + public ObjectFuncModel JsonToSqlFuncModels(JToken sqlfunc) + { + var key = sqlfunc.First(); + if (IsObjct(sqlfunc)) + { + return GetFuncModelByObject(key); + } + else + { + return GetFuncModelByArray(sqlfunc); + } + } + public IFuncModel JsonToSqlFuncModels(string sqlfunc) + { + if (IsArray(sqlfunc)) + { + return GetFuncModelByArray(sqlfunc); + } + else + { + return GetFuncModelByObject(sqlfunc); + } + } + #endregion + + #region Level 1 + private ObjectFuncModel GetFuncModelByArray(JToken sqlfunc) + { + ObjectFuncModel result = new ObjectFuncModel(); + result.Parameters = new List(); + result.FuncName = "Sqlfunc_Format"; + StringBuilder sb = new StringBuilder(); + foreach (var item in sqlfunc) + { + result.Parameters.Add(GetParameter(item)); + } + return result; + } + private ObjectFuncModel GetFuncModelByObject(JToken key) + { + ObjectFuncModel result = new ObjectFuncModel(); + JProperty jProperty = key.ToObject(); + result.FuncName = jProperty.Name; + var parameters = jProperty.Value; + result.Parameters = GetParameter(parameters); + return result; + } + private ObjectFuncModel GetFuncModelByObject(string sqlfunc) + { + var jObject = this.Context.Utilities.DeserializeObject(sqlfunc); + return JsonToSqlFuncModels(jObject); + } + private ObjectFuncModel GetFuncModelByArray(string sqlfunc) + { + ObjectFuncModel result = new ObjectFuncModel(); + result.Parameters = new List(); + result.FuncName = "Sqlfunc_Format"; + var jArrary = this.Context.Utilities.DeserializeObject(sqlfunc); + StringBuilder sb = new StringBuilder(); + foreach (var item in jArrary) + { + result.Parameters.Add(GetParameter(item)); + } + return result; + } + + #endregion + + + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonToModel/JsonSqlFuncToParameters.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonToModel/JsonSqlFuncToParameters.cs new file mode 100644 index 000000000..abb368075 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonToModel/JsonSqlFuncToParameters.cs @@ -0,0 +1,62 @@ +using Newtonsoft.Json.Linq; + +namespace SqlSugar +{ + public partial class ContextMethods : IContextMethods + { + #region Root + public List GetParameter(JToken parameters) + { + List result = new List(); + if (IsString(parameters)) + { + result.Add(GetStringParameters(parameters)); + } + else if (IsArray(parameters)) + { + result.AddRange(GetArrayParameters(parameters)); + } + else if (IsObject(parameters)) + { + result.Add(GetObjectParameters(parameters)); + } + else + { + result.Add(GetObjectErrorParameters(parameters)); + } + return result; + } + #endregion + + #region Level1 + private static List GetObjectErrorParameters(JToken parameters) + { + Check.Exception(true, ErrorMessage.GetThrowMessage($" {parameters.ToString()} format is error ", $" {parameters.ToString()} 格式错误")); + return null; + } + + public List GetArrayParameters(JToken parameters) + { + List result = new List(); + foreach (var item in parameters) + { + result.Add(GetParameter(item)); + } + return result; + } + + public object GetObjectParameters(JToken parameters) + { + return JsonToSqlFuncModels(parameters); + } + + public object GetStringParameters(JToken parameters) + { + return parameters.ObjToString().ToCheckField(); + } + #endregion + + + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonToModel/JsonToColumnsModels.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonToModel/JsonToColumnsModels.cs new file mode 100644 index 000000000..1ade4ffc1 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonToModel/JsonToColumnsModels.cs @@ -0,0 +1,57 @@ +namespace SqlSugar +{ + public partial class ContextMethods : IContextMethods + { + public List> JsonToColumnsModels(string json) + { + List> result = new List>(); + List conditionalModels = new List(); + if (IsArray(json)) + { + return GetColumnsByArray(json); + } + else + { + return GetColumnsByObject(json); + } + } + private List> GetColumnsByObject(string json) + { + List> result = new List>(); + var dic = this.Context.Utilities.DeserializeObject>(json); + result.Add(GetColumns(dic)); + return result; + } + private List> GetColumnsByArray(string json) + { + List> result = new List>(); + var jarray = this.Context.Utilities.DeserializeObject>>(json); + foreach (var item in jarray) + { + result.Add(GetColumns(item)); + } + return result; + } + + private Dictionary GetColumns(Dictionary dictionary) + { + Dictionary result = new Dictionary(); + foreach (var item in dictionary) + { + var value = GetValue(item); + result.Add(item.Key, value); + } + return result; + } + + private static object GetValue(KeyValuePair item) + { + if (item.Value == null) + return null; + var valueString = item.Value.ToString(); + var vallue = Json2SqlHelper.GetValue(valueString); + var type = Json2SqlHelper.GetType(valueString); + return UtilMethods.ConvertDataByTypeName(type, vallue); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonToModel/JsonToGroupByModels.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonToModel/JsonToGroupByModels.cs new file mode 100644 index 000000000..4ef7fef19 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonToModel/JsonToGroupByModels.cs @@ -0,0 +1,27 @@ +using Newtonsoft.Json.Linq; + +namespace SqlSugar +{ + public partial class ContextMethods : IContextMethods + { + public List JsonToGroupByModels(string json) + { + List conditionalModels = new List(); + var jarray = this.Context.Utilities.DeserializeObject(json); + foreach (var item in jarray) + { + if (item.ObjToString().Contains("fieldname", StringComparison.CurrentCultureIgnoreCase)) + { + var model = item.ToObject(); + conditionalModels.Add(model); + } + else + { + conditionalModels.Add(new GroupByModel() { FieldName = item.ObjToString().ToCheckField() }); + } + } + return conditionalModels; + } + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonToModel/JsonToJoinModels.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonToModel/JsonToJoinModels.cs new file mode 100644 index 000000000..7990e6b50 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonToModel/JsonToJoinModels.cs @@ -0,0 +1,26 @@ +using Newtonsoft.Json.Linq; + +namespace SqlSugar +{ + /// + /// Json to model + /// + public partial class ContextMethods : IContextMethods + { + + public JoinModel JsonToJoinModels(string json) + { + JoinModel conditionalModels = new JoinModel(); + var array = JArray.Parse(json); + Check.Exception(array.Count != 3, json + " format error"); + var tableName = array[0]; + var shortName = array[1]; + var onWhere = array[2]; + JoinModel result = new JoinModel(); + result.TableName = tableName.ObjToString().ToCheckField(); + result.ShortName = shortName.ObjToString().ToCheckField(); + result.OnWhereList = JsonToSqlFuncModels(onWhere); + return result; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonToModel/JsonToOrderByModels.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonToModel/JsonToOrderByModels.cs new file mode 100644 index 000000000..3c6c7ae3b --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonToModel/JsonToOrderByModels.cs @@ -0,0 +1,34 @@ +using Newtonsoft.Json.Linq; + +namespace SqlSugar +{ + public partial class ContextMethods : IContextMethods + { + public List JsonToOrderByModels(string json) + { + List conditionalModels = new List(); + var jarray = this.Context.Utilities.DeserializeObject(json); + foreach (var item in jarray) + { + if (IsFieldName(item)) + { + var model = item.ToObject(); + conditionalModels.Add(model); + } + else if (item.Type == JTokenType.String) + { + conditionalModels.Add(new OrderByModel() { FieldName = item.ObjToString().ToCheckField() }); + } + else if (item.Type == JTokenType.Array) + { + conditionalModels.Add(new OrderByModel() + { + FieldName = item[0].ObjToString(), + OrderByType = item[1].ObjToString().Equals("desc", StringComparison.CurrentCultureIgnoreCase) ? OrderByType.Desc : OrderByType.Asc + }); + } + } + return conditionalModels; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonToModel/JsonToSelectModels.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonToModel/JsonToSelectModels.cs new file mode 100644 index 000000000..f33491f98 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/JsonToModel/JsonToSelectModels.cs @@ -0,0 +1,65 @@ +using Newtonsoft.Json.Linq; + +namespace SqlSugar +{ + public partial class ContextMethods : IContextMethods + { + public List JsonToSelectModels(string json) + { + List conditionalModels = new List(); + var jarray = this.Context.Utilities.DeserializeObject(json); + foreach (var item in jarray) + { + if (IsFieldName(item)) + { + var model = item.ToObject(); + conditionalModels.Add(model); + } + else if (IsString(item)) + { + conditionalModels.Add(new SelectModel() { FieldName = item.ObjToString().ToCheckField(), AsName = item.ObjToString().Replace(".", "_") }); + } + else if (IsArraySingleItem(item)) + { + object fileName = item[0].ObjToString(); + var asName = item[0].ObjToString().Replace(".", "_"); + if (IsSqlFunc(item[0], fileName.ObjToString())) + { + fileName = JsonToSqlFuncModels(item[0]); + } + conditionalModels.Add(new SelectModel() + { + FieldName = fileName, + AsName = asName + }); + + } + else if (IsArray(item)) + { + object fileName = item[0].ObjToString(); + var asName = item[1].ObjToString().Replace(".", "_"); + if (IsSqlFunc(item[0], fileName.ObjToString())) + { + fileName = JsonToSqlFuncModels(item[0]); + } + conditionalModels.Add(new SelectModel() + { + FieldName = fileName, + AsName = asName + }); + + } + else + { + conditionalModels.Add(new SelectModel() + { + FieldName = item.ObjToString().Trim(), + AsName = item.ObjToString().Trim() + }); + } + + } + return conditionalModels; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/ModelToSql/FuncModelToSql.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/ModelToSql/FuncModelToSql.cs new file mode 100644 index 000000000..f5df37e33 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/ModelToSql/FuncModelToSql.cs @@ -0,0 +1,182 @@ +using System.Text.RegularExpressions; + +namespace SqlSugar +{ + /// + ///Json model to sql + /// + public abstract partial class SqlBuilderProvider : SqlBuilderAccessory, ISqlBuilder + { + #region Root + public KeyValuePair FuncModelToSql(IFuncModel model) + { + ObjectFuncModel data = model as ObjectFuncModel; + var name = data.FuncName; + var parameters = data.Parameters; + var dbMethods = this.Context.Queryable().QueryBuilder.LambdaExpressions.DbMehtods; + var methods = GetAllMethods(dbMethods); + var methodName = GetMethodName(name, methods); + var methodInfo = GetMethod(dbMethods, methodName); + var pars = methodInfo.GetParameters(); + + var resSql = ""; + var resPars = new List(); + resSql = GetSql(parameters, dbMethods, methodName, methodInfo, pars, resPars); + if (name.EqualCase("MappingColumn")) + { + if (!(this.Context?.CurrentConnectionConfig?.MoreSettings?.EnableModelFuncMappingColumn == true)) + { + Check.ExceptionEasy("Enable MappingColumn need in ConnectionConfig - > MoreSettings - > EnableModelFuncMappingColumn set to true", "MappingColumn考虑到风险情况需要开启才能使用,请在 ConnectionConfig->MoreSettings->EnableModelFuncMappingColumn设置为true"); + } + resSql = parameters.First() + ""; + } + return new KeyValuePair(resSql, resPars.ToArray()); + } + #endregion + + #region Level2 + private string GetSql(List parameters, IDbMethods dbMethods, string methodName, System.Reflection.MethodInfo methodInfo, System.Reflection.ParameterInfo[] pars, List resPars) + { + string resSql; + if (IsNoParameter(pars)) + { + resSql = GetNoParameterMehtodSql(dbMethods, methodInfo); + } + else if (IsFormatMethod(methodName)) + { + resSql = GetFormatMethodSql(parameters, resPars); + } + else if (IsSqlFuncMethod(pars)) + { + resSql = GetSqlFuncSql(parameters, dbMethods, methodName, methodInfo, resPars); + } + else if (IsMergeStringMethod(methodName)) + { + resSql = GetSqlFuncSql(parameters, dbMethods, methodName, methodInfo, resPars); + } + else + { + resSql = GetNoSupportMethodSql(methodInfo); + } + + return resSql; + } + + + private static System.Reflection.MethodInfo GetMethod(IDbMethods dbMethods, string methodName) + { + return dbMethods.GetType().GetMethods() + .Where(it => it.Name == methodName) + .Where(it => it.Name != "Equals" || it.GetParameters().Length == 1 && it.GetParameters().First().ParameterType == typeof(MethodCallExpressionModel)) + .FirstOrDefault(); + } + private static string GetMethodName(string name, List methods) + { + var result = methods.FirstOrDefault(it => name.EqualCase("SqlFunc_" + it) || name.EqualCase(it)); + Check.Exception(result == null, $" {name} is error "); + return result; + } + private static List GetAllMethods(IDbMethods dbMethods) + { + return new ReflectionInoCacheService().GetOrCreate("Json2SqlGetFuncSql", () => + dbMethods.GetType() + .GetMethods().Where(it => it.Name != "GetHashCode").Select(it => it.Name).ToList()); + } + #endregion + + #region Level3 + private static string GetNoSupportMethodSql(System.Reflection.MethodInfo methodInfo) + { + throw new Exception(methodInfo.Name); + } + private string GetSqlFuncSql(List parameters, IDbMethods dbMethods, string methodName, System.Reflection.MethodInfo methodInfo, List resPars) + { + string resSql; + var args = new List(); + int i = 0; + foreach (var item in parameters) + { + i++; + string value = null; + if (methodName.IsIn("ContainsArray", "ContainsArrayUseSqlParameters") && i == 1) + { + var first = Regex.Split(item + "", ":").First(); + var last = Regex.Split(item + "", ":").Last(); + object[] array = this.Context.Utilities.DeserializeObject(last); + value = GetParameterName(resPars, array); + } + else + { + value = GetSqlPart(item, resPars); + } + args.Add(new MethodCallExpressionArgs + { + MemberName = value, + MemberValue = resPars.FirstOrDefault(it => it.ParameterName == value)?.Value ?? value, + IsMember = true + }); + } + if (IsMergeStringMethod(methodName)) + { + return methodInfo.Invoke(dbMethods, new object[] { args.Select(it => it.MemberName.ObjToString()).ToArray() }).ObjToString(); + } + if (IsToStringFormat(methodName, args)) + { + var fieldName = args.First().MemberName.ObjToString(); + var format = args.Last().MemberValue.ObjToString(); + var queryable = this.Context.Queryable() + .Select(it => SqlFunc.MappingColumn(fieldName).ToString(format)); + var select = queryable.QueryBuilder.GetSelectValue; + return select; + } + resSql = methodInfo.Invoke(dbMethods, new object[] { new MethodCallExpressionModel() { + Name=methodName, + Args=args + } }).ObjToString(); + return resSql; + } + + private static bool IsToStringFormat(string methodName, List args) + { + return methodName == nameof(ToString) && args?.Count == 2; + } + + private string GetFormatMethodSql(List parameters, List resPars) + { + string resSql; + var objects = new List(); + foreach (var item in parameters) + { + var value = GetSqlPart(item, resPars); + objects.Add(value.ObjToString()); + } + resSql = string.Join(" ", string.Join(" ", objects)); + return resSql; + } + private static string GetNoParameterMehtodSql(IDbMethods dbMethods, System.Reflection.MethodInfo methodInfo) + { + return methodInfo.Invoke(dbMethods, Array.Empty()).ObjToString(); + } + #endregion + + #region Helper + private static bool IsMergeStringMethod(string methodName) + { + return methodName == "MergeString"; + } + + private static bool IsSqlFuncMethod(System.Reflection.ParameterInfo[] pars) + { + return pars.First().ParameterType == typeof(MethodCallExpressionModel); + } + private static bool IsFormatMethod(string methodName) + { + return methodName.EqualCase("format"); + } + private static bool IsNoParameter(System.Reflection.ParameterInfo[] pars) + { + return pars.Length == 0; + } + #endregion + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/ModelToSql/GroupByModelToSql.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/ModelToSql/GroupByModelToSql.cs new file mode 100644 index 000000000..3d9d99acb --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/ModelToSql/GroupByModelToSql.cs @@ -0,0 +1,31 @@ +using System.Text; +namespace SqlSugar +{ + public abstract partial class SqlBuilderProvider : SqlBuilderAccessory, ISqlBuilder + { + public KeyValuePair GroupByModelToSql(List models) + { + StringBuilder sql = new StringBuilder(""); + var pars = new List { }; + foreach (var item in models) + { + if (item is GroupByModel && item.FieldName is IFuncModel) + { + var orderByModel = item as GroupByModel; + sql.Append($" {GetSqlPart(item.FieldName, pars)} ,"); + } + else if (item is GroupByModel) + { + var orderByModel = item as GroupByModel; + sql.Append($" {this.GetTranslationColumnName(orderByModel.FieldName.ObjToString().ToSqlFilter())} ,"); + } + else + { + + } + + } + return new KeyValuePair(sql.ToString().TrimEnd(','), pars?.ToArray()); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/ModelToSql/OrderByModelToSql.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/ModelToSql/OrderByModelToSql.cs new file mode 100644 index 000000000..b2c748b20 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/ModelToSql/OrderByModelToSql.cs @@ -0,0 +1,31 @@ +using System.Text; +namespace SqlSugar +{ + public abstract partial class SqlBuilderProvider : SqlBuilderAccessory, ISqlBuilder + { + public KeyValuePair OrderByModelToSql(List models) + { + StringBuilder sql = new StringBuilder(""); + List pars = new List() { }; + foreach (var item in models) + { + if (item is OrderByModel && item.FieldName is IFuncModel) + { + var orderByModel = item as OrderByModel; + sql.Append($" {GetSqlPart(item.FieldName, pars)} {orderByModel.OrderByType.ToString().ToUpper()} ,"); + } + else if (item is OrderByModel) + { + var orderByModel = item as OrderByModel; + sql.Append($" {this.GetTranslationColumnName(orderByModel.FieldName.ObjToString().ToSqlFilter())} {orderByModel.OrderByType.ToString().ToUpper()} ,"); + } + else + { + + } + + } + return new KeyValuePair(sql.ToString().TrimEnd(','), pars?.ToArray()); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/ModelToSql/SelectModelToSql.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/ModelToSql/SelectModelToSql.cs new file mode 100644 index 000000000..5f00ee787 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/ModelToSql/SelectModelToSql.cs @@ -0,0 +1,54 @@ +using System.Text; +namespace SqlSugar +{ + public abstract partial class SqlBuilderProvider : SqlBuilderAccessory, ISqlBuilder + { + public KeyValuePair SelectModelToSql(List models) + { + StringBuilder sql = new StringBuilder(""); + var pars = new List { }; + foreach (var item in models) + { + if (item is SelectModel) + { + var orderByModel = item as SelectModel; + orderByModel.AsName = GetAsName(orderByModel); + orderByModel.FieldName = GetSqlPart(orderByModel.FieldName, pars).ObjToString(); + AppendFiledName(sql, orderByModel); + } + else + { + + } + } + return new KeyValuePair(sql.ToString().TrimEnd(','), pars.ToArray()); + } + + private string GetAsName(SelectModel orderByModel) + { + if (orderByModel.AsName.IsNullOrEmpty()) + { + orderByModel.AsName = orderByModel.FieldName.ObjToString(); + } + if (orderByModel.AsName.StartsWith(UtilConstants.ReplaceKey)) + { + return orderByModel.AsName.Replace(UtilConstants.ReplaceKey, string.Empty); + } + if (orderByModel.AsName?.Contains('[') == true) + { + orderByModel.AsName = orderByModel.AsName.Trim('[').Trim(']'); + return this.SqlTranslationLeft + orderByModel.AsName + this.SqlTranslationRight; + } + if (this.SqlTranslationLeft != null && orderByModel.AsName?.Contains(this.SqlTranslationLeft) == true) + { + return orderByModel.AsName; + } + return this.SqlTranslationLeft + orderByModel.AsName + this.SqlTranslationRight; + } + + private void AppendFiledName(StringBuilder sql, SelectModel orderByModel) + { + sql.Append($" {orderByModel.FieldName} AS {orderByModel.AsName} ,"); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/ModelToSql/SqlPart.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/ModelToSql/SqlPart.cs new file mode 100644 index 000000000..661529f36 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/ModelToSql/SqlPart.cs @@ -0,0 +1,152 @@ +using System.Text.RegularExpressions; +namespace SqlSugar +{ + public abstract partial class SqlBuilderProvider : SqlBuilderAccessory, ISqlBuilder + { + #region Variable + private string[] SqlSplicingOperator = new string[] { ">", ">=", "<", "<=", "(", ")", "!=", "<>", "not", "=", "||", "&&", "&", "|", "null", "is", "isnot", "like", "nolike", "+", "-", "*", "/", "%", "$casewhen", "$then", "$when", "$else", "$end" }; + #endregion + + #region Root + private string GetSqlPart(object value, List pars) + { + Check.Exception(value == null, $" FiledName is error "); + if (IsSqlSplicingOperator(value)) + { + return GetSqlSplicingOperator(value); + } + else if (IsString(value)) + { + return GetSqlPartByString(value, pars); + } + else if (IsListObject(value)) + { + return GetSqlPartByListObject(value, pars); + } + else if (IsObjectFunc(value)) + { + return GetSqlPartByObjectFuncModel(value, pars); + } + else + { + return GetSqlPartError(value); + } + } + + #endregion + + #region Level2 + + private static string GetSqlSplicingOperator(object value) + { + var result = value.ObjToString(); + if (result == "||") return "OR"; + else if (result == "&&") return "AND"; + else if (result.EqualCase("isnot")) return " IS NOT "; + else if (result.EqualCase("$casewhen")) return " CASE WHEN "; + else if (result.EqualCase("$then")) return " THEN "; + else if (result.EqualCase("$when")) return " WHEN "; + else if (result.EqualCase("$else")) return " ELSE "; + else if (result.EqualCase("$end")) return " END "; + return result; + } + private static string GetSqlPartError(object value) + { + Check.Exception(value == null, $" {value} is error "); + return null; + } + private string GetSqlPartByObjectFuncModel(object value, List pars) + { + var data = value as ObjectFuncModel; + var obj = FuncModelToSql(data); + pars.AddRange(obj.Value); + return obj.Key; + } + private string GetSqlPartByListObject(object value, List pars) + { + var list = (value as List); + if (list.Count == 1) + { + return GetSqlPart(list.First(), pars).ObjToString(); + } + else + { + Check.Exception(value == null, $" {value} is error "); + return null; + } + } + private string GetSqlPartByString(object value, List pars) + { + var valueString = value.ObjToString().Trim(); + if (Json2SqlHelper.IsSqlValue(valueString)) + { + return GetParameterName(pars, valueString); + } + else + { + return this.GetTranslationColumnName(value.ObjToString().ToCheckField()); + } + } + #endregion + + #region Level3 + private string GetSplicingOperator(string valueString) + { + var parvalue = Regex.Match(valueString, @"\@s\:(.+)").Groups[1].Value; + if (parvalue == null) parvalue = ""; + parvalue = parvalue.Trim(); + if (parvalue.ToLower().IsIn(SqlSplicingOperator)) + { + return parvalue; + } + else + { + Check.ExceptionEasy($"{valueString} is error ", $"{valueString} 不是有效的拼接符号,拼接符号有:and、or、>=、<=、>、<、=、(、)"); + } + return parvalue; + } + private string GetParameterName(List pars, string valueString) + { + object parvalue = Json2SqlHelper.GetValue(valueString); + SugarParameter parameter = new SugarParameter("@p" + pars.Count, parvalue); + var type = Json2SqlHelper.GetType(valueString); + parvalue = UtilMethods.ConvertDataByTypeName(type, parvalue.ObjToString()); + var parname = GetParameterName(pars, parvalue); + return parname; + } + internal int GetParameterNameIndex = 100; + + private string GetParameterName(List pars, object parvalue) + { + var parname = "@p" + pars.Count + "_" + (GetParameterNameIndex) + $"{this.QueryBuilder?.LambdaExpressions?.ParameterIndex}"; + SugarParameter parameter = new SugarParameter(parname, parvalue); + pars.Add(parameter); + GetParameterNameIndex++; + if (this.QueryBuilder != null) + this.QueryBuilder.LambdaExpressions.ParameterIndex++; + return parname; + } + #endregion + + #region Helper + + private static bool IsListObject(object value) + { + return value.GetType() == typeof(List); + } + private static bool IsString(object value) + { + return value.GetType() == typeof(string); + } + private static bool IsObjectFunc(object value) + { + return value is ObjectFuncModel; + } + private bool IsSqlSplicingOperator(object value) + { + return SqlSplicingOperator.Contains(value.ObjToString()); + } + #endregion + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Common/JsonCommonProvider.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Common/JsonCommonProvider.cs new file mode 100644 index 000000000..710ff9ee0 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Common/JsonCommonProvider.cs @@ -0,0 +1,87 @@ +using Newtonsoft.Json.Linq; +namespace SqlSugar +{ + internal class JsonCommonProvider + { + public JsonCommonProvider(ISqlSugarClient context) + { + //this.context = context; + this.sqlBuilder = InstanceFactory.GetSqlbuilder(context.CurrentConnectionConfig); + if (context is SqlSugarProvider) + { + this.sqlBuilder.Context = context as SqlSugarProvider; + } + else if (context is SqlSugarScopeProvider) + { + this.sqlBuilder.Context = (context as SqlSugarScopeProvider).conn; + } + else if (context is SqlSugarScope) + { + this.sqlBuilder.Context = (context as SqlSugarScope).GetConnection(context.CurrentConnectionConfig.ConfigId); + } + else + { + this.sqlBuilder.Context = (context as SqlSugarClient).Context; + } + } + //public ISqlSugarClient context { get; set; } + public ISqlBuilder sqlBuilder { get; set; } + public int ParameterIndex { get { return ((SqlBuilderProvider)sqlBuilder)?.GetParameterNameIndex ?? 0; } } + public JsonTableNameInfo GetTableName(JToken item) + { + JsonTableNameInfo jsonTableNameInfo = new JsonTableNameInfo(); + if (item.First().Type == JTokenType.Array && item.First.Count() == 2) + { + var tableName = item.First()[0].ObjToString(); + var shortName = item.First()[1].ObjToString(); + jsonTableNameInfo.ShortName = shortName; + jsonTableNameInfo.TableName = tableName; + + } + else + { + var value = item.First().ToString(); + jsonTableNameInfo.TableName = value; + } + return jsonTableNameInfo; + } + public KeyValuePair GetWhere(string item, SqlSugarProvider context) + { + + if (!IsConditionalModel(item)) + { + var obj = context.Utilities.JsonToSqlFuncModels(item); + var sqlobj = sqlBuilder.FuncModelToSql(obj); + return sqlobj; + } + else + { + var obj = context.Utilities.JsonToConditionalModels(item); + var sqlObj = sqlBuilder.ConditionalModelToSql(obj, 0); + return sqlObj; + } + } + public KeyValuePair GetWhere(JToken item, SqlSugarProvider context) + { + var value = item.First().ToString(); + Check.ExceptionEasy(item.First().Type != JTokenType.Array, "Where format error " + item, "Where格式错误" + item); + if (!IsConditionalModel(value)) + { + var obj = context.Utilities.JsonToSqlFuncModels(value); + var sqlobj = sqlBuilder.FuncModelToSql(obj); + return sqlobj; + } + else + { + var obj = context.Utilities.JsonToConditionalModels(value); + var sqlObj = sqlBuilder.ConditionalModelToSql(obj, 0); + return sqlObj; + } + } + + private static bool IsConditionalModel(string value) + { + return value.Contains("fieldname", StringComparison.CurrentCultureIgnoreCase); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Deleteable/JsonDeleteableProvider.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Deleteable/JsonDeleteableProvider.cs new file mode 100644 index 000000000..ef2d5df88 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Deleteable/JsonDeleteableProvider.cs @@ -0,0 +1,88 @@ +using Newtonsoft.Json.Linq; + +namespace SqlSugar +{ + public class JsonDeleteableProvider : IJsonDeleteableProvider + { + private ISqlSugarClient context; + private JObject jObject; + private JsonCommonProvider jsonCommonProvider; + private IDeleteable sugarDeleteable = null; + public JsonDeleteableProvider(ISqlSugarClient context, JObject jObject) + { + this.jObject = jObject; + this.context = context; + this.jsonCommonProvider = new JsonCommonProvider(context); + } + public SqlObjectResult ToSql() + { + return this.ToSqlList().First(); + } + public List ToSqlList() + { + List result = new List(); + JsonQueryParameter jsonQueryParameter = new JsonQueryParameter(); + var appendTypeNames = this.jObject.AsJEnumerable().ToList(); + this.sugarDeleteable = this.context.Deleteable(); + foreach (JToken item in appendTypeNames) + { + AppendAll(jsonQueryParameter, item); + } + result.Add(new SqlObjectResult(this.sugarDeleteable.ToSql(), JsonProviderType.Deleteable)); + return result; + } + private void AppendAll(JsonQueryParameter jsonQueryParameter, JToken item) + { + var name = item.Path.ToLower(); + if (IsWhere(name)) + { + AppendWhere(item); + } + else if (IsTable(name)) + { + AppendTable(item); + } + } + private void AppendTable(JToken item) + { + var tableInfo = jsonCommonProvider.GetTableName(item); + var tableName = tableInfo.TableName.ToCheckField(); + if (tableInfo.ShortName.HasValue()) + { + tableName = tableInfo.ShortName + "." + tableInfo.TableName; + } + this.sugarDeleteable.AS(tableName); + } + private void AppendWhere(JToken item) + { + var sqlObj = jsonCommonProvider.GetWhere(item, sugarDeleteable.DeleteBuilder.Context); + sugarDeleteable.Where(sqlObj.Key, sqlObj.Value); + } + private static bool IsTable(string name) + { + return name.Equals(JsonProviderConfig.KeyDeleteable.Get(), StringComparison.CurrentCultureIgnoreCase); + } + private static bool IsWhere(string name) + { + return name.Equals("Where", StringComparison.CurrentCultureIgnoreCase); + } + public string ToSqlString() + { + throw new NotImplementedException(); + } + + public JsonDeleteResult ToResult() + { + var result = new JsonDeleteResult(); + var sqlInfo = this.ToSqlList(); + var sqlInfoResult = sqlInfo.First(); + result.UpdateRows = this.context.Ado.ExecuteCommand(sqlInfoResult.Sql, sqlInfoResult.Parameters); + return result; + } + + List IJsonProvider.ToSqlString() + { + throw new NotImplementedException(); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Insertable/AppendIdentity.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Insertable/AppendIdentity.cs new file mode 100644 index 000000000..3de2ba8c9 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Insertable/AppendIdentity.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json.Linq; +namespace SqlSugar +{ + public partial class JsonInsertableProvider : IJsonInsertableProvider + { + private void AppendIdentity(JToken item) + { + var tableInfo = jsonCommonProvider.GetTableName(item); + this.IdentityId = tableInfo.TableName; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Insertable/AppendName.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Insertable/AppendName.cs new file mode 100644 index 000000000..dcac36413 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Insertable/AppendName.cs @@ -0,0 +1,16 @@ +using Newtonsoft.Json.Linq; +namespace SqlSugar +{ + public partial class JsonInsertableProvider : IJsonInsertableProvider + { + private void AppendName(JToken item) + { + var tableInfo = jsonCommonProvider.GetTableName(item); + this.TableName = tableInfo.TableName.ToCheckField(); + if (tableInfo.ShortName.HasValue()) + { + this.TableName = tableInfo.ShortName + "." + tableInfo.TableName; + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Insertable/AppendRow.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Insertable/AppendRow.cs new file mode 100644 index 000000000..30386086e --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Insertable/AppendRow.cs @@ -0,0 +1,13 @@ +using Newtonsoft.Json.Linq; +namespace SqlSugar +{ + public partial class JsonInsertableProvider : IJsonInsertableProvider + { + private void AppendRow(JToken item) + { + var value = item.First().ToString(); + var dics = context.Utilities.JsonToColumnsModels(value); + sugarInsertable = this.context.Insertable(dics).AS(this.TableName); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Insertable/Helper.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Insertable/Helper.cs new file mode 100644 index 000000000..a883ef277 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Insertable/Helper.cs @@ -0,0 +1,42 @@ +using Newtonsoft.Json.Linq; + +namespace SqlSugar +{ + public partial class JsonInsertableProvider : IJsonInsertableProvider + { + private static bool IsColumns(string name) + { + return name.Equals("columns", StringComparison.CurrentCultureIgnoreCase); + } + + private static bool IsName(string name) + { + return name.Equals(JsonProviderConfig.KeyInsertable.Get(), StringComparison.CurrentCultureIgnoreCase); + } + + private static bool IsIdentity(string name) + { + return name.Equals("identity", StringComparison.CurrentCultureIgnoreCase); + } + private List ToSqlHelper() + { + List result = new List(); + JsonQueryParameter jsonQueryParameter = new JsonQueryParameter(); + var appendTypeNames = this.jObject.AsJEnumerable().ToList(); + foreach (JToken item in appendTypeNames.OrderBy(it => it.Path.EqualCase(JsonProviderConfig.KeyInsertable.Get()) ? 0 : 1)) + { + AppendAll(jsonQueryParameter, item); + } + var addItem = this.sugarInsertable.ToSql(); + if (this.IdentityId.HasValue()) + { + result.Add(new SqlObjectResult(addItem, JsonProviderType.InsertableIdentity)); + } + else + { + result.Add(new SqlObjectResult(addItem, JsonProviderType.Insertable)); + } + return result; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Insertable/JsonInsertableProvider.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Insertable/JsonInsertableProvider.cs new file mode 100644 index 000000000..3506f168c --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Insertable/JsonInsertableProvider.cs @@ -0,0 +1,60 @@ +using Newtonsoft.Json.Linq; + +namespace SqlSugar +{ + public partial class JsonInsertableProvider : IJsonInsertableProvider + { + public SqlObjectResult ToSql() + { + return this.ToSqlList().First(); + } + public JsonInsertableProvider(ISqlSugarClient context, JObject jObject) + { + this.jObject = jObject; + this.context = context; + this.jsonCommonProvider = new JsonCommonProvider(context); + } + public JsonInsertResult ToResult() + { + var result = new JsonInsertResult(); + var sqlInfo = this.ToSqlList(); + var sqlInfoResult = sqlInfo.First(); + if (sqlInfoResult.JsonSqlType != JsonProviderType.InsertableIdentity) + { + result.InsertCount = this.context.Ado.ExecuteCommand(sqlInfoResult.Sql, sqlInfoResult.Parameters); + } + else + { + result.InsertCount = this.Count; + result.IdentityValue = this.context.Ado.GetInt(sqlInfoResult.Sql, sqlInfoResult.Parameters); + } + return result; + } + public List ToSqlList() + { + return ToSqlHelper(); + } + + private void AppendAll(JsonQueryParameter jsonQueryParameter, JToken item) + { + var name = item.Path.ToLower(); + if (IsName(name)) + { + AppendName(item); + } + else if (IsIdentity(name)) + { + AppendIdentity(item); + } + else if (IsColumns(name)) + { + AppendRow(item); + } + } + + public List ToSqlString() + { + throw new NotImplementedException(); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Insertable/PrivateProperty.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Insertable/PrivateProperty.cs new file mode 100644 index 000000000..1aa32faef --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Insertable/PrivateProperty.cs @@ -0,0 +1,16 @@ +using Newtonsoft.Json.Linq; + +namespace SqlSugar +{ + public partial class JsonInsertableProvider : IJsonInsertableProvider + { + private ISqlSugarClient context; + private JObject jObject; + private JsonCommonProvider jsonCommonProvider; + private string TableName { get; set; } + private string IdentityId { get; set; } + private int Count { get; set; } + private IInsertable> sugarInsertable; + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/ApendJoinLastAfter.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/ApendJoinLastAfter.cs new file mode 100644 index 000000000..c4bfabc60 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/ApendJoinLastAfter.cs @@ -0,0 +1,43 @@ +using Newtonsoft.Json.Linq; +namespace SqlSugar +{ + /// + /// ApendJoinLastAfter + /// + public partial class JsonQueryableProvider : IJsonQueryableProvider + { + private void ApendJoinLastAfter(JToken item) + { + if (IsAppendSelect()) + { + JArray jArray = new JArray(); + var tableConfigs = this.jsonTableConfigs.GroupBy(it => it.TableName).Select(it => it.First()).ToList(); + var isJoinTable = IsAnyJoin(appendTypeNames); + foreach (var config in tableConfigs) + { + + if (isJoinTable) + { + + } + else + { + if (config.Columns.Count != 0) + { + foreach (var column in config.Columns.Select(it => it.Name).Distinct()) + { + jArray.Add(column); + } + } + } + } + this.AppendSelect(jArray); + } + } + + private bool IsAppendSelect() + { + return !IsAnySelect(appendTypeNames) && this.jsonTableConfigs.Count != 0; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/AppendFrom.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/AppendFrom.cs new file mode 100644 index 000000000..f84e40c96 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/AppendFrom.cs @@ -0,0 +1,26 @@ +using Newtonsoft.Json.Linq; + +namespace SqlSugar +{ + /// + /// AppendFrom + /// + public partial class JsonQueryableProvider : IJsonQueryableProvider + { + + private void AppendFrom(JToken item) + { + var tableNameInfo = jsonCommonProvider.GetTableName(item); + tableNameInfo.TableName.ToCheckField(); + AddMasterTableInfos(tableNameInfo); + if (tableNameInfo.ShortName.HasValue()) + { + this.sugarQueryable.AS(tableNameInfo.TableName, tableNameInfo.ShortName); + } + else + { + this.sugarQueryable.AS(tableNameInfo.TableName, tableNameInfo.ShortName); + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/AppendGroupBy.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/AppendGroupBy.cs new file mode 100644 index 000000000..c82a03d55 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/AppendGroupBy.cs @@ -0,0 +1,17 @@ +using Newtonsoft.Json.Linq; +namespace SqlSugar +{ + /// + /// AppendGroupBy + /// + public partial class JsonQueryableProvider : IJsonQueryableProvider + { + + private void AppendGroupBy(JToken item) + { + var value = item.First().ToString(); + var obj = context.Utilities.JsonToGroupByModels(value); + sugarQueryable.GroupBy(obj); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/AppendHaving.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/AppendHaving.cs new file mode 100644 index 000000000..a67b36322 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/AppendHaving.cs @@ -0,0 +1,17 @@ +using Newtonsoft.Json.Linq; +namespace SqlSugar +{ + /// + /// AppendHaving + /// + public partial class JsonQueryableProvider : IJsonQueryableProvider + { + private void AppendHaving(JToken item) + { + var value = item.First().ToString(); + var obj = context.Utilities.JsonToSqlFuncModels(value); + sugarQueryable.Having(obj); + } + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/AppendJoin.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/AppendJoin.cs new file mode 100644 index 000000000..a31196f80 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/AppendJoin.cs @@ -0,0 +1,52 @@ +using Newtonsoft.Json.Linq; +namespace SqlSugar +{ + /// + /// AppendJoin + /// + public partial class JsonQueryableProvider : IJsonQueryableProvider + { + private bool AppendJoin(JToken item) + { + BeforeJoin(); + bool isJoin = true; + var value = item.First().ToString(); + var obj = context.Utilities.JsonToJoinModels(value); + sugarQueryable.AddJoinInfo(obj.TableName, obj.ShortName, obj.OnWhereList, GetJoinType(item)); + AddTableInfos(obj.TableName, obj.ShortName); + AfterJoin(); + return isJoin; + } + + private static JoinType GetJoinType(JToken obj) + { + var key = obj.Path.ToLower(); + if (key.Contains("right")) + { + return JoinType.Right; + } + else if (key.Contains("left")) + { + return JoinType.Left; + } + else if (key.Contains("full")) + { + return JoinType.Full; + } + else + { + return JoinType.Inner; + } + } + + private void AfterJoin() + { + + } + + private void BeforeJoin() + { + + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/AppendOrderBy.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/AppendOrderBy.cs new file mode 100644 index 000000000..59dd09971 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/AppendOrderBy.cs @@ -0,0 +1,18 @@ +using Newtonsoft.Json.Linq; + +namespace SqlSugar +{ + /// + /// AppendOrderBy + /// + public partial class JsonQueryableProvider : IJsonQueryableProvider + { + private void AppendOrderBy(JToken item) + { + var value = item.First().ToString(); + var obj = context.Utilities.JsonToOrderByModels(value); + sugarQueryable.OrderBy(obj); + } + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/AppendPage.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/AppendPage.cs new file mode 100644 index 000000000..fd5fd9171 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/AppendPage.cs @@ -0,0 +1,24 @@ +using Newtonsoft.Json.Linq; +namespace SqlSugar +{ + /// + /// AppendPage + /// + public partial class JsonQueryableProvider : IJsonQueryableProvider + { + private int AppendPageSize(JToken item) + { + return Convert.ToInt32(item.First().ToString().ObjToInt()); + } + + private int AppendPageNumber(JToken item) + { + var result = Convert.ToInt32(item.First().ToString().ObjToInt()); + if (result == 0) + { + result = 1; + } + return result; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/AppendSelect.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/AppendSelect.cs new file mode 100644 index 000000000..7f211b66e --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/AppendSelect.cs @@ -0,0 +1,79 @@ +using Newtonsoft.Json.Linq; +namespace SqlSugar +{ + + /// + /// AppendSelect + /// + public partial class JsonQueryableProvider : IJsonQueryableProvider + { + private bool AppendSelect(JToken item) + { + bool isSelect = true; + if (item.Type == JTokenType.Property) + { + var value = item.First().ToString(); + var obj = context.Utilities.JsonToSelectModels(value); + obj = FilterSelect(obj); + sugarQueryable.Select(obj); + } + else + { + var obj = context.Utilities.JsonToSelectModels(item.ToString()); + obj = FilterSelect(obj); + sugarQueryable.Select(obj); + } + return isSelect; + } + + private List FilterSelect(List obj) + { + if (this.jsonTableConfigs.Count == 0) + { + return obj; + } + List result = new List(); + foreach (var item in obj) + { + if (item.FieldName is string) + { + var tableName = GetTableName(item.FieldName + ""); + var columnName = GetColumnName(item.FieldName + ""); + if (IsMyColums(tableName, columnName)) + { + result.Add(item); + } + } + else + { + result.Add(item); + } + } + return result; + } + + private bool IsMyColums(string tableName, string columnName) + { + return this.jsonTableConfigs.Any(it => it.TableName.EqualCase(tableName) + && it.Columns.Any(z => z.Name.EqualCase(columnName))); + } + + private string GetColumnName(string filedName) + { + return filedName.Split('.').Last(); + } + + private string GetTableName(string filedName) + { + if (!filedName.Contains('.')) + { + return TableInfos.First(it => it.IsMaster).Table; + } + else + { + var shortName = filedName.Split('.').First(); + return TableInfos.First(it => it.ShortName == shortName).Table; + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/AppendWhere.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/AppendWhere.cs new file mode 100644 index 000000000..6f008c040 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/AppendWhere.cs @@ -0,0 +1,33 @@ +using Newtonsoft.Json.Linq; + +namespace SqlSugar +{ + + /// + /// AppendWhere + /// + public partial class JsonQueryableProvider : IJsonQueryableProvider + { + private void AppendWhere(JToken item) + { + BeforeWhere(); + var sqlObj = jsonCommonProvider.GetWhere(item, sugarQueryable.Context); + sugarQueryable.Where(sqlObj.Key, sqlObj.Value); + AfterWhere(); + } + + private void AfterWhere() + { + + } + + private void BeforeWhere() + { + if (!IsExecutedBeforeWhereFunc) + { + BeforeWhereFunc(); + IsExecutedBeforeWhereFunc = true; + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/Entities.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/Entities.cs new file mode 100644 index 000000000..ffebe109a --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/Entities.cs @@ -0,0 +1,11 @@ +namespace SqlSugar +{ + internal class JsonQueryableProvider_TableInfo + { + public string Table { get; set; } + public string ShortName { get; set; } + public bool IsMaster { get; set; } + public bool IsJoin { get; set; } + public int Index { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/Helper.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/Helper.cs new file mode 100644 index 000000000..8a251369b --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/Helper.cs @@ -0,0 +1,100 @@ +using Newtonsoft.Json.Linq; + +namespace SqlSugar +{ + /// + /// Helper + /// + public partial class JsonQueryableProvider : IJsonQueryableProvider + { + private static bool IsJoin(string name) + { + return name.StartsWith("LeftJoin", StringComparison.CurrentCultureIgnoreCase) || name.StartsWith("RightJoin", StringComparison.CurrentCultureIgnoreCase) || name.StartsWith("InnerJoin", StringComparison.CurrentCultureIgnoreCase); + } + private static bool IsJoinLastAfter(string name) + { + return name == "joinlastafter"; + } + + private static bool IsPageSize(string name) + { + return name.Equals("PageSize", StringComparison.CurrentCultureIgnoreCase); + } + + private static bool IsPageNumber(string name) + { + return name.Equals("PageNumber", StringComparison.CurrentCultureIgnoreCase); + } + + private static bool IsSelect(string name) + { + return name.Equals("Select", StringComparison.CurrentCultureIgnoreCase); + } + + private static bool IsHaving(string name) + { + return name.Equals("Having", StringComparison.CurrentCultureIgnoreCase); + } + + private static bool IsGroupBy(string name) + { + return name.Equals("GroupBy", StringComparison.CurrentCultureIgnoreCase); + } + + private static bool IsOrderBy(string name) + { + return name.Equals("OrderBy", StringComparison.CurrentCultureIgnoreCase); + } + + private static bool IsWhere(string name) + { + return name.Equals("Where", StringComparison.CurrentCultureIgnoreCase); + } + + private static bool IsForm(string name) + { + return name.Equals(JsonProviderConfig.KeyQueryable.Get(), StringComparison.CurrentCultureIgnoreCase); + } + + private static bool IsAnySelect(List appendTypeNames) + { + return appendTypeNames.Any(it => IsSelect(it.Path.ToLower())); + } + private static bool IsAnyJoin(List appendTypeNames) + { + return appendTypeNames.Any(it => IsJoin(it.Path.ToLower())); + } + private int GetSort(string name) + { + if (IsForm(name)) + { + return 0; + } + else if (IsJoin(name)) + { + return 1; + } + else if (IsJoinLastAfter(name)) + { + return 2; + } + else + { + return 100; + } + } + private void AddMasterTableInfos(JsonTableNameInfo tableNameInfo) + { + AddTableInfos(tableNameInfo.TableName, tableNameInfo.ShortName, true); + } + private void AddTableInfos(string tableName, string shortName, bool isMaster = false) + { + UtilMethods.IsNullReturnNew(TableInfos); + TableInfos.Add(new JsonQueryableProvider_TableInfo() { Table = tableName, ShortName = shortName, IsMaster = true }); + } + private JsonQueryableProvider_TableInfo GetMasterTable() + { + return this.TableInfos.First(it => it.IsMaster); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/JsonQueryableProvider.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/JsonQueryableProvider.cs new file mode 100644 index 000000000..86f9c9654 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/JsonQueryableProvider.cs @@ -0,0 +1,114 @@ +using Newtonsoft.Json.Linq; + +namespace SqlSugar +{ + /// + /// JsonQueryableProvider + /// + public partial class JsonQueryableProvider : IJsonQueryableProvider + { + + public JsonQueryableProvider(ISqlSugarClient context, JObject jobject) + { + this.jobject = jobject; + this.context = context; + this.jsonCommonProvider = new JsonCommonProvider(context); + } + public IJsonQueryableProvider ShowDesciption() + { + this.IsDescription = true; + return this; + } + public IJsonQueryableProvider UseAuthentication(JsonTableConfig config) + { + if (config == null) + { + jsonTableConfigs = new List() { config }; + } + else + { + jsonTableConfigs.Add(config); + } + return this; + } + public IJsonQueryableProvider UseAuthentication(List configs) + { + foreach (JsonTableConfig config in configs) + { + UseAuthentication(config); + } + return this; + } + + public SqlObjectResult ToSql() + { + return this.ToSqlList().First(); + } + public JsonQueryResult ToResult() + { + return ToResultDefault(); + } + + public List ToSqlList() + { + var result = ToSqlDefault(); + return result; + } + + public List ToSqlString() + { + throw new NotImplementedException(); + } + + private void AppendQueryableAll(JsonQueryParameter jsonQueryParameter, JToken item) + { + SetQueryableParameterIndex(); + var name = item.Path.ToLower(); + if (IsForm(name)) + { + AppendFrom(item); + } + else if (IsWhere(name)) + { + AppendWhere(item); + } + else if (IsOrderBy(name)) + { + AppendOrderBy(item); + } + else if (IsJoinLastAfter(name)) + { + ApendJoinLastAfter(item); + } + else if (IsGroupBy(name)) + { + AppendGroupBy(item); + } + else if (IsHaving(name)) + { + AppendHaving(item); + } + else if (IsSelect(name)) + { + jsonQueryParameter.IsSelect = AppendSelect(item); + } + else if (IsPageNumber(name)) + { + jsonQueryParameter.PageIndex = AppendPageNumber(item); + } + else if (IsPageSize(name)) + { + jsonQueryParameter.PageSize = AppendPageSize(item); + } + else if (IsJoin(name)) + { + jsonQueryParameter.IsSelect = AppendJoin(item); + } + } + + private void SetQueryableParameterIndex() + { + ((SqlBuilderProvider)sugarQueryable.SqlBuilder).GetParameterNameIndex = jsonCommonProvider.ParameterIndex; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/Property.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/Property.cs new file mode 100644 index 000000000..72da01ad3 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/Property.cs @@ -0,0 +1,22 @@ +using Newtonsoft.Json.Linq; + +namespace SqlSugar +{ + /// + /// Property + /// + public partial class JsonQueryableProvider : IJsonQueryableProvider + { + int appendIndex = 1000; + List appendTypeNames; + JObject jobject; + ISqlSugarClient context; + ISugarQueryable sugarQueryable; + JsonCommonProvider jsonCommonProvider; + List jsonTableConfigs = new List(); + bool IsDescription = false; + List TableInfos = new List(); + bool IsExecutedBeforeWhereFunc = false; + Action BeforeWhereFunc { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/RegisterAop.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/RegisterAop.cs new file mode 100644 index 000000000..9e585b5d4 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/RegisterAop.cs @@ -0,0 +1,37 @@ +namespace SqlSugar +{ + /// + /// RegisterAop + /// + public partial class JsonQueryableProvider : IJsonQueryableProvider + { + private void RegisterAop() + { + this.BeforeWhereFunc = () => + { + var masterTable = GetMasterTable(); + var masterFilters = this.jsonTableConfigs.Where(it => it.TableName.EqualCase(masterTable.Table)).ToList(); + if (masterFilters.Count != 0) + { + foreach (var filter in masterFilters) + { + var conditions = filter.Conditionals; + conditions = GetConvertConditions(conditions); + var p = this.sugarQueryable.SqlBuilder.ConditionalModelToSql(conditions); + var sql = p.Key; + sugarQueryable.SqlBuilder.RepairReplicationParameters(ref sql, p.Value, appendIndex); + appendIndex++; + sugarQueryable.Where(sql, p.Value); + } + ; + + } + }; + } + + private List GetConvertConditions(List conditions) + { + return conditions; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/ResultDefault.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/ResultDefault.cs new file mode 100644 index 000000000..52249bacf --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/ResultDefault.cs @@ -0,0 +1,45 @@ +using Newtonsoft.Json.Linq; + +namespace SqlSugar +{ + + /// + /// ResultDefault + /// + public partial class JsonQueryableProvider : IJsonQueryableProvider + { + private List ToSqlDefault() + { + List result = new List(); + sugarQueryable = context.Queryable(); + appendTypeNames = GetTypeNames(); + JsonQueryParameter jsonQueryParameter = new JsonQueryParameter(); + RegisterAop(); + foreach (JToken item in appendTypeNames) + { + AppendQueryableAll(jsonQueryParameter, item); + } + return ToPageDefault(result, jsonQueryParameter); + } + + private List GetTypeNames() + { + var result = this.jobject.AsJEnumerable().ToList(); + result.Add(JToken.Parse("{JoinLastAfter:null}").First()); + result = result.OrderBy(it => GetSort(it.Path.ToLower())).ToList(); + return result; + } + + private JsonQueryResult ToResultDefault() + { + JsonQueryResult result = new JsonQueryResult(); + var toSqls = this.ToSqlList(); + var SqlCount = toSqls.FirstOrDefault(it => it.JsonSqlType == JsonProviderType.QueryableCount); + var SqlList = toSqls.FirstOrDefault(it => it.JsonSqlType == JsonProviderType.Queryable); + AddCount(result, SqlCount); + AddList(result, SqlList); + AddDescription(); + return result; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/ResultHelper.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/ResultHelper.cs new file mode 100644 index 000000000..fe3b35955 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Queryable/ResultHelper.cs @@ -0,0 +1,64 @@ +namespace SqlSugar +{ + /// + /// ResultHelper + /// + public partial class JsonQueryableProvider : IJsonQueryableProvider + { + #region SqlHelper + private List ToPageDefault(List result, JsonQueryParameter jsonQueryParameter) + { + if (jsonQueryParameter.IsPage) + { + AddPageSql(result, jsonQueryParameter); + } + else + { + AddDefaultSql(result); + } + Check.ExceptionEasy(jsonQueryParameter.JoinNoSelect, "join query need Select", "联表查询需要设置Select"); + return result; + } + + private void AddDefaultSql(List result) + { + result.Add(new SqlObjectResult(sugarQueryable.Clone().ToSql(), JsonProviderType.Queryable)); + } + + private void AddPageSql(List result, JsonQueryParameter jsonQueryParameter) + { + var skipValue = (jsonQueryParameter.PageIndex.Value - 1) * jsonQueryParameter.PageSize.Value; + var takeValue = jsonQueryParameter.PageSize.Value; + result.Add(new SqlObjectResult(sugarQueryable.Clone().Skip(skipValue).Take(takeValue).ToSql(), JsonProviderType.Queryable)); + var countQueryable = sugarQueryable.Select("COUNT(1)"); + countQueryable.QueryBuilder.OrderByValue = null; + result.Add(new SqlObjectResult(countQueryable.ToSql(), JsonProviderType.QueryableCount)); + } + #endregion + + #region ObjectHeper + private void AddDescription() + { + if (this.IsDescription) + { + } + } + + private void AddList(JsonQueryResult result, SqlObjectResult SqlList) + { + if (SqlList != null) + { + result.Data = this.context.Ado.SqlQuery(SqlList.Sql, SqlList.Parameters); + } + } + + private void AddCount(JsonQueryResult result, SqlObjectResult SqlCount) + { + if (SqlCount != null) + { + result.ToTalRows = this.context.Ado.GetInt(SqlCount.Sql, SqlCount.Parameters); + } + } + #endregion + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Updateable/AppendRow.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Updateable/AppendRow.cs new file mode 100644 index 000000000..8675f175a --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Updateable/AppendRow.cs @@ -0,0 +1,21 @@ +using Newtonsoft.Json.Linq; +namespace SqlSugar +{ + public partial class JsonUpdateableProvider : IJsonUpdateableProvider + { + private void AppendRow(JToken item) + { + var itemFirst = item.First(); + var isObject = itemFirst.Type == JTokenType.Object; + var value = itemFirst.ToString(); + var dics = context.Utilities.JsonToColumnsModels(value); + if (isObject) + sugarUpdateable = this.context.Updateable(dics.First()).AS(this.TableName); + else + { + sugarUpdateable = this.context.Updateable(dics).AS(this.TableName); + isList = dics.Take(2).Any(); + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Updateable/AppendTable.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Updateable/AppendTable.cs new file mode 100644 index 000000000..b84bd89b7 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Updateable/AppendTable.cs @@ -0,0 +1,17 @@ +using Newtonsoft.Json.Linq; + +namespace SqlSugar +{ + public partial class JsonUpdateableProvider : IJsonUpdateableProvider + { + private void AppendTable(JToken item) + { + var tableInfo = jsonCommonProvider.GetTableName(item); + this.TableName = tableInfo.TableName.ToCheckField(); + if (tableInfo.ShortName.HasValue()) + { + this.TableName = tableInfo.ShortName + "." + tableInfo.TableName; + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Updateable/AppendWhere.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Updateable/AppendWhere.cs new file mode 100644 index 000000000..e32627061 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Updateable/AppendWhere.cs @@ -0,0 +1,15 @@ +using Newtonsoft.Json.Linq; + +namespace SqlSugar +{ + public partial class JsonUpdateableProvider : IJsonUpdateableProvider + { + private void AppendWhere(JToken item) + { + Check.Exception(isList, "Batch updates cannot use Where, only WhereColumns can set columns", "批量更新不能使用Where,只能通过WhereColumns设置列"); + var sqlObj = jsonCommonProvider.GetWhere(item, sugarUpdateable.UpdateBuilder.Context); + sugarUpdateable.Where(sqlObj.Key, sqlObj.Value); + } + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Updateable/AppendWhereColumns.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Updateable/AppendWhereColumns.cs new file mode 100644 index 000000000..0ee72bd03 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Updateable/AppendWhereColumns.cs @@ -0,0 +1,15 @@ +using Newtonsoft.Json.Linq; + +namespace SqlSugar +{ + public partial class JsonUpdateableProvider : IJsonUpdateableProvider + { + private void AppendWhereColumns(JToken item) + { + var columns = item.First().ToObject(); + Check.ExceptionEasy(columns.IsNullOrEmpty(), "need WhereColumns", "WhereColumns 需要设置列名"); + this.sugarUpdateable.WhereColumns(columns); + } + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Updateable/Helpercs.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Updateable/Helpercs.cs new file mode 100644 index 000000000..dc25b13b9 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Updateable/Helpercs.cs @@ -0,0 +1,26 @@ +namespace SqlSugar +{ + public partial class JsonUpdateableProvider : IJsonUpdateableProvider + { + + private static bool IsColumns(string name) + { + return name.Equals("Columns", StringComparison.CurrentCultureIgnoreCase); + } + + private static bool IsWhere(string name) + { + return name.Equals("Where", StringComparison.CurrentCultureIgnoreCase); + } + + private static bool IsWhereColumns(string name) + { + return name.Equals("WhereColumns", StringComparison.CurrentCultureIgnoreCase); + } + + private static bool IsTable(string name) + { + return name.Equals(JsonProviderConfig.KeyUpdateable.Get(), StringComparison.CurrentCultureIgnoreCase); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Updateable/JsonUpdateableProvider.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Updateable/JsonUpdateableProvider.cs new file mode 100644 index 000000000..8e24e0867 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Provider/Updateable/JsonUpdateableProvider.cs @@ -0,0 +1,82 @@ +using Newtonsoft.Json.Linq; +namespace SqlSugar +{ + public partial class JsonUpdateableProvider : IJsonUpdateableProvider + { + private ISqlSugarClient context; + private JObject jObject; + private JsonCommonProvider jsonCommonProvider; + private string TableName { get; set; } + private bool isList { get; set; } + private IUpdateable> sugarUpdateable; + public JsonUpdateableProvider(ISqlSugarClient context, JObject jObject) + { + this.jObject = jObject; + this.context = context; + this.jsonCommonProvider = new JsonCommonProvider(context); + } + public JsonUpdateResult ToResult() + { + var result = new JsonUpdateResult(); + var sqlInfo = this.ToSqlList(); + var sqlInfoResult = sqlInfo.First(); + result.UpdateRows = this.context.Ado.ExecuteCommand(sqlInfoResult.Sql, sqlInfoResult.Parameters); + return result; + } + public SqlObjectResult ToSql() + { + return this.ToSqlList().First(); + } + public List ToSqlList() + { + List result = new List(); + JsonQueryParameter jsonQueryParameter = new JsonQueryParameter(); + List appendTypeNames = GetAppendTypes(); + foreach (JToken item in appendTypeNames) + { + AppendAll(jsonQueryParameter, item); + } + var addItem = this.sugarUpdateable.ToSql(); + result.Add(new SqlObjectResult(addItem, JsonProviderType.Updateable)); + return result; + } + + private List GetAppendTypes() + { + var appendTypeNames = this.jObject.AsJEnumerable().ToList(); + appendTypeNames = appendTypeNames.OrderBy(it => + { + if (it.Path.EqualCase(JsonProviderConfig.KeyUpdateable.Get())) return 0; + if (it.Path.EqualCase("Columns")) return 1; + else return 3; + + }).ToList(); + return appendTypeNames; + } + + private void AppendAll(JsonQueryParameter jsonQueryParameter, JToken item) + { + var name = item.Path.ToLower(); + if (IsTable(name)) + { + AppendTable(item); + } + else if (IsWhereColumns(name)) + { + AppendWhereColumns(item); + } + else if (IsWhere(name)) + { + AppendWhere(item); + } + else if (IsColumns(name)) + { + AppendRow(item); + } + } + public List ToSqlString() + { + throw new NotImplementedException(); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Queryable/QueryableProvider.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Queryable/QueryableProvider.cs new file mode 100644 index 000000000..b375d060d --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Queryable/QueryableProvider.cs @@ -0,0 +1,112 @@ +namespace SqlSugar +{ + + public partial class QueryableProvider : QueryableAccessory, ISugarQueryable + { + public ISugarQueryable AddJoinInfo(string tableName, string shortName, IFuncModel models, JoinType type = JoinType.Left) + { + var sqlobj = this.SqlBuilder.FuncModelToSql(models); + this.QueryBuilder.Parameters.AddRange(sqlobj.Value); + return this.AddJoinInfo(tableName, shortName, sqlobj.Key, type); + } + public ISugarQueryable AddJoinInfo(List joinInfoParameters) + { + if (joinInfoParameters != null) + { + foreach (var item in joinInfoParameters) + { + this.AddJoinInfo(item.TableName, item.ShortName, item.Models, item.Type); + } + } + return this; + } + public ISugarQueryable AS(string tableName, string shortName) + { + this.QueryBuilder.TableShortName = shortName; + return this.AS(tableName); + } + public ISugarQueryable OrderBy(List models) + { + if (models == null || models.Count == 0) + { + return this; + } + var orderObj = this.SqlBuilder.OrderByModelToSql(models); + this.OrderBy(orderObj.Key); + this.QueryBuilder.Parameters.AddRange(orderObj.Value); + return this; + } + public ISugarQueryable GroupBy(List models) + { + if (models == null || models.Count == 0) + { + return this; + } + var orderObj = this.SqlBuilder.GroupByModelToSql(models); + if (orderObj.Value?.Length > 0 && this.Context.CurrentConnectionConfig?.DbType == DbType.SqlServer) + { + var groupBySql = UtilMethods.GetSqlString(DbType.SqlServer, orderObj.Key, orderObj.Value); + this.QueryBuilder.GroupBySql = groupBySql; + this.QueryBuilder.GroupBySqlOld = orderObj.Key; + this.QueryBuilder.GroupParameters = orderObj.Value.ToList(); + this.GroupBy(orderObj.Key); + } + else + { + this.GroupBy(orderObj.Key); + this.QueryBuilder.Parameters.AddRange(orderObj.Value); + } + return this; + } + public ISugarQueryable Select(List models) + { + var orderObj = this.SqlBuilder.SelectModelToSql(models); + if (this.QueryBuilder.GroupParameters?.Count > 0 && this.QueryBuilder.GroupBySql.HasValue()) + { + var selectSql = UtilMethods.GetSqlString(DbType.SqlServer, orderObj.Key, UtilMethods.CopySugarParameters(orderObj.Value.ToList()).ToArray()); + if (selectSql.Contains(this.QueryBuilder.GroupBySql)) + { + this.Select(UtilConstants.GroupReplaceKey + selectSql); + return this; + } + } + this.Select(orderObj.Key); + this.QueryBuilder.Parameters.AddRange(orderObj.Value); + return this; + } + + public ISugarQueryable Select(List models) + { + var orderObj = this.SqlBuilder.SelectModelToSql(models); + var result = this.Select(orderObj.Key); + result.QueryBuilder.Parameters.AddRange(orderObj.Value); + return result; + } + + + + public ISugarQueryable Select(List models, AsNameFormatType type) + { + if (type == AsNameFormatType.NoConvert) + { + foreach (var model in models) + { + if (!string.IsNullOrEmpty(model.AsName)) + { + model.AsName = (UtilConstants.ReplaceKey + SqlBuilder.SqlTranslationLeft + model.AsName + SqlBuilder.SqlTranslationRight); + model.AsName.ToCheckField(); + } + } + } + return Select(models); + } + public ISugarQueryable Having(IFuncModel model) + { + this.QueryBuilder.WhereIndex++; + var orderObj = this.SqlBuilder.FuncModelToSql(model); + this.Having(orderObj.Key); + this.QueryBuilder.Parameters.AddRange(orderObj.Value); + return this; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Utils/Json2SqlConfig.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Utils/Json2SqlConfig.cs new file mode 100644 index 000000000..684cbb719 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Utils/Json2SqlConfig.cs @@ -0,0 +1,31 @@ +namespace SqlSugar +{ + public static class JsonProviderConfig + { + public const string KeyInsertable = "Insertable"; + public const string KeyUpdateable = "Updateable"; + public const string KeyQueryable = "Queryable"; + public const string KeyDeleteable = "Deleteable"; + + private static Dictionary words = new Dictionary() + { + { KeyInsertable,"Table"}, + { KeyUpdateable,"Table"}, + { KeyQueryable,"Table"}, + { KeyDeleteable,"Table"} + }; + public static string Rename(string key, string name) + { + return words[key] = name; + } + internal static string Get(this string value) + { + return words[value]; + } + internal static string GetWord(string key) + { + Check.ExceptionEasy(words.ContainsKey(key) == false, $"{key} is error", $"{key} 不存在 "); + return words[key]; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Utils/Json2SqlHelper.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Utils/Json2SqlHelper.cs new file mode 100644 index 000000000..76cc2270b --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Json2Sql/Utils/Json2SqlHelper.cs @@ -0,0 +1,36 @@ +using Newtonsoft.Json.Linq; + +using System.Text.RegularExpressions; +namespace SqlSugar +{ + internal static class Json2SqlHelper + { + public static bool IsSqlValue(string valueString) + { + return Regex.IsMatch(valueString, @"^\{\w{1,10}\}\:"); + } + public static string GetType(string valueString) + { + return Regex.Match(valueString, @"^\{(\w+)\}\:").Groups[1].Value; + } + public static string GetValue(string valueString) + { + return Regex.Replace(valueString, @"^\{\w{1,10}\}\:", ""); + } + + public static List GetTableNames(string json) + { + List result = new List(); + var mainTable = JObject.Parse(json).AsJEnumerable().Where(it => + it.Path.ToLower().IsIn( + JsonProviderConfig.KeyInsertable.Get().ToLower(), + JsonProviderConfig.KeyUpdateable.Get().ToLower(), + JsonProviderConfig.KeyDeleteable.Get().ToLower(), + JsonProviderConfig.KeyQueryable.Get().ToLower() + )).FirstOrDefault(); + if (mainTable != null) + result.Add(mainTable.First().ToString()); + return result; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/JsonClient.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/JsonClient.cs new file mode 100644 index 000000000..524195e4d --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/JsonClient.cs @@ -0,0 +1,34 @@ +using Newtonsoft.Json.Linq; +namespace SqlSugar +{ + public class JsonClient : IJsonClient + { + public ISqlSugarClient Context { get; set; } + + public IJsonQueryableProvider Queryable(string json) + { + var iJsonToSql = new JsonQueryableProvider(Context, JObject.Parse(json)); + return iJsonToSql; + } + public IJsonProvider Insertable(string json) + { + var iJsonToSql = new JsonInsertableProvider(Context, JObject.Parse(json)); + return iJsonToSql; + } + public IJsonProvider Updateable(string json) + { + var iJsonToSql = new JsonUpdateableProvider(Context, JObject.Parse(json)); + return iJsonToSql; + } + public IJsonProvider Deleteable(string json) + { + var iJsonToSql = new JsonDeleteableProvider(Context, JObject.Parse(json)); + return iJsonToSql; + } + public List GetTableNameList(string json) + { + List result = Json2SqlHelper.GetTableNames(json); + return result; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/OnlyCore/Compatible.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/OnlyCore/Compatible.cs new file mode 100644 index 000000000..0601f8213 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/OnlyCore/Compatible.cs @@ -0,0 +1,7 @@ +namespace SqlSugar +{ + public static class SugarCompatible + { + public const bool IsFramework = false; + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/OnlyCore/DataExtensions.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/OnlyCore/DataExtensions.cs new file mode 100644 index 000000000..738680c48 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/OnlyCore/DataExtensions.cs @@ -0,0 +1,1127 @@ +using Dm; + +using Kdbndp; + +using Microsoft.Data.SqlClient; +using Microsoft.Data.Sqlite; + +using MySqlConnector; + +using Npgsql; + +using Oracle.ManagedDataAccess.Client; + +using System.Data; +using System.Data.OscarClient; + +namespace SqlSugar +{ + + /// + /// 数据填充器 + /// + public class SqlDataAdapter : IDataAdapter + { + private SqlCommand command; + private string sql; + private SqlConnection _sqlConnection; + + /// + /// SqlDataAdapter + /// + /// + public SqlDataAdapter(SqlCommand command) + { + this.command = command; + } + + public SqlDataAdapter() + { + + } + + /// + /// SqlDataAdapter + /// + /// + /// + public SqlDataAdapter(string sql, SqlConnection _sqlConnection) + { + this.sql = sql; + this._sqlConnection = _sqlConnection; + } + + /// + /// SelectCommand + /// + public SqlCommand SelectCommand + { + get + { + if (this.command == null) + { + this.command = new SqlCommand(this.sql, this._sqlConnection); + } + return this.command; + } + set + { + this.command = value; + } + } + + /// + /// Fill + /// + /// + public void Fill(DataTable dt) + { + if (dt == null) + { + dt = new DataTable(); + } + var columns = dt.Columns; + var rows = dt.Rows; + using (SqlDataReader dr = command.ExecuteReader()) + { + for (int i = 0; i < dr.FieldCount; i++) + { + string name = dr.GetName(i).Trim(); + if (!columns.Contains(name)) + columns.Add(new DataColumn(name, dr.GetFieldType(i))); + else + { + columns.Add(new DataColumn(name + i, dr.GetFieldType(i))); + } + } + + while (dr.Read()) + { + DataRow daRow = dt.NewRow(); + for (int i = 0; i < columns.Count; i++) + { + daRow[columns[i].ColumnName] = dr.GetValue(i); + } + dt.Rows.Add(daRow); + } + } + + dt.AcceptChanges(); + } + + /// + /// Fill + /// + /// + public void Fill(DataSet ds) + { + if (ds == null) + { + ds = new DataSet(); + } + using (SqlDataReader dr = command.ExecuteReader()) + { + do + { + var dt = new DataTable(); + var columns = dt.Columns; + var rows = dt.Rows; + for (int i = 0; i < dr.FieldCount; i++) + { + string name = dr.GetName(i).Trim(); + if (!columns.Contains(name)) + columns.Add(new DataColumn(name, dr.GetFieldType(i))); + else + { + columns.Add(new DataColumn(name + i, dr.GetFieldType(i))); + } + + } + + while (dr.Read()) + { + DataRow daRow = dt.NewRow(); + for (int i = 0; i < columns.Count; i++) + { + daRow[columns[i].ColumnName] = dr.GetValue(i); + } + dt.Rows.Add(daRow); + } + dt.AcceptChanges(); + ds.Tables.Add(dt); + } while (dr.NextResult()); + } + } + } + /// + /// 数据填充器 + /// + public class MySqlDataAdapter : IDataAdapter + { + private MySqlCommand command; + private string sql; + private MySqlConnection _sqlConnection; + + /// + /// SqlDataAdapter + /// + /// + public MySqlDataAdapter(MySqlCommand command) + { + this.command = command; + } + + public MySqlDataAdapter() + { + + } + + /// + /// SqlDataAdapter + /// + /// + /// + public MySqlDataAdapter(string sql, MySqlConnection _sqlConnection) + { + this.sql = sql; + this._sqlConnection = _sqlConnection; + } + + /// + /// SelectCommand + /// + public MySqlCommand SelectCommand + { + get + { + if (this.command == null) + { + this.command = new MySqlCommand(this.sql, this._sqlConnection); + } + return this.command; + } + set + { + this.command = value; + } + } + + /// + /// Fill + /// + /// + public void Fill(DataTable dt) + { + if (dt == null) + { + dt = new DataTable(); + } + var columns = dt.Columns; + var rows = dt.Rows; + using (MySqlDataReader dr = command.ExecuteReader()) + { + for (int i = 0; i < dr.FieldCount; i++) + { + string name = dr.GetName(i).Trim(); + if (!columns.Contains(name)) + columns.Add(new DataColumn(name, dr.GetFieldType(i))); + else + { + columns.Add(new DataColumn(name + i, dr.GetFieldType(i))); + } + } + + while (dr.Read()) + { + DataRow daRow = dt.NewRow(); + for (int i = 0; i < columns.Count; i++) + { + daRow[columns[i].ColumnName] = dr.GetValue(i); + } + dt.Rows.Add(daRow); + } + } + dt.AcceptChanges(); + } + + /// + /// Fill + /// + /// + public void Fill(DataSet ds) + { + if (ds == null) + { + ds = new DataSet(); + } + using (MySqlDataReader dr = command.ExecuteReader()) + { + do + { + var dt = new DataTable(); + var columns = dt.Columns; + var rows = dt.Rows; + for (int i = 0; i < dr.FieldCount; i++) + { + string name = dr.GetName(i).Trim(); + if (dr.GetFieldType(i).Name == "MySqlDateTime") + { + if (!columns.Contains(name)) + columns.Add(new DataColumn(name, UtilConstants.DateType)); + else + { + columns.Add(new DataColumn(name + i, UtilConstants.DateType)); + } + } + else + { + if (!columns.Contains(name)) + columns.Add(new DataColumn(name, dr.GetFieldType(i))); + else + { + columns.Add(new DataColumn(name + i, dr.GetFieldType(i))); + } + } + } + + while (dr.Read()) + { + DataRow daRow = dt.NewRow(); + for (int i = 0; i < columns.Count; i++) + { + daRow[columns[i].ColumnName] = dr.GetValue(i); + } + dt.Rows.Add(daRow); + } + dt.AcceptChanges(); + ds.Tables.Add(dt); + } while (dr.NextResult()); + } + } + } + + /// + /// 数据填充器 + /// + public class SqliteDataAdapter : IDataAdapter + { + private SqliteCommand command; + private string sql; + private SqliteConnection _sqlConnection; + + /// + /// SqlDataAdapter + /// + /// + public SqliteDataAdapter(SqliteCommand command) + { + this.command = command; + } + + public SqliteDataAdapter() + { + + } + + /// + /// SqlDataAdapter + /// + /// + /// + public SqliteDataAdapter(string sql, SqliteConnection _sqlConnection) + { + this.sql = sql; + this._sqlConnection = _sqlConnection; + } + + /// + /// SelectCommand + /// + public SqliteCommand SelectCommand + { + get + { + if (this.command == null) + { + this.command = new SqliteCommand(this.sql, this._sqlConnection); + } + return this.command; + } + set + { + this.command = value; + } + } + + /// + /// Fill + /// + /// + public void Fill(DataTable dt) + { + if (dt == null) + { + dt = new DataTable(); + } + var columns = dt.Columns; + var rows = dt.Rows; + using (SqliteDataReader dr = command.ExecuteReader()) + { + for (int i = 0; i < dr.FieldCount; i++) + { + string name = dr.GetName(i).Trim(); + var type = dr.GetFieldType(i); + if (dr.GetDataTypeName(i).EqualCase("datetime")) + { + type = UtilConstants.DateType; + } + if (!columns.Contains(name)) + columns.Add(new DataColumn(name, type)); + else + { + columns.Add(new DataColumn(name + i, type)); + } + } + + while (dr.Read()) + { + DataRow daRow = dt.NewRow(); + for (int i = 0; i < columns.Count; i++) + { + daRow[columns[i].ColumnName] = dr.GetValue(i); + } + dt.Rows.Add(daRow); + } + } + dt.AcceptChanges(); + } + + /// + /// Fill + /// + /// + public void Fill(DataSet ds) + { + if (ds == null) + { + ds = new DataSet(); + } + using (SqliteDataReader dr = command.ExecuteReader()) + { + do + { + var dt = new DataTable(); + var columns = dt.Columns; + var rows = dt.Rows; + for (int i = 0; i < dr.FieldCount; i++) + { + string name = dr.GetName(i).Trim(); + var type = dr.GetFieldType(i); + if (dr.GetDataTypeName(i).EqualCase("datetime")) + { + type = UtilConstants.DateType; + } + if (!columns.Contains(name)) + columns.Add(new DataColumn(name, type)); + else + { + columns.Add(new DataColumn(name + i, type)); + } + } + + while (dr.Read()) + { + DataRow daRow = dt.NewRow(); + for (int i = 0; i < columns.Count; i++) + { + daRow[columns[i].ColumnName] = dr.GetValue(i); + } + dt.Rows.Add(daRow); + } + dt.AcceptChanges(); + ds.Tables.Add(dt); + } while (dr.NextResult()); + } + } + } + + /// + /// 数据填充器 + /// + public class MyOracleDataAdapter : IDataAdapter + { + private OracleCommand command; + private string sql; + private OracleConnection _sqlConnection; + + /// + /// SqlDataAdapter + /// + /// + public MyOracleDataAdapter(OracleCommand command) + { + this.command = command; + } + + public MyOracleDataAdapter() + { + + } + + /// + /// SqlDataAdapter + /// + /// + /// + public MyOracleDataAdapter(string sql, OracleConnection _sqlConnection) + { + this.sql = sql; + this._sqlConnection = _sqlConnection; + } + + /// + /// SelectCommand + /// + public OracleCommand SelectCommand + { + get + { + if (this.command == null) + { + this.command = new OracleCommand(this.sql, this._sqlConnection); + } + return this.command; + } + set + { + this.command = value; + } + } + + /// + /// Fill + /// + /// + public void Fill(DataTable dt) + { + if (dt == null) + { + dt = new DataTable(); + } + var columns = dt.Columns; + var rows = dt.Rows; + using (OracleDataReader dr = command.ExecuteReader()) + { + for (int i = 0; i < dr.FieldCount; i++) + { + string name = dr.GetName(i).Trim(); + if (!columns.Contains(name)) + columns.Add(new DataColumn(name, dr.GetFieldType(i))); + else + { + columns.Add(new DataColumn(name + i, dr.GetFieldType(i))); + } + } + + while (dr.Read()) + { + DataRow daRow = dt.NewRow(); + for (int i = 0; i < columns.Count; i++) + { + daRow[columns[i].ColumnName] = dr.GetValue(i); + } + dt.Rows.Add(daRow); + } + } + dt.AcceptChanges(); + } + + /// + /// Fill + /// + /// + public void Fill(DataSet ds) + { + if (ds == null) + { + ds = new DataSet(); + } + using (OracleDataReader dr = command.ExecuteReader()) + { + do + { + var dt = new DataTable(); + var columns = dt.Columns; + var rows = dt.Rows; + for (int i = 0; i < dr.FieldCount; i++) + { + string name = dr.GetName(i).Trim(); + if (!columns.Contains(name)) + columns.Add(new DataColumn(name, dr.GetFieldType(i))); + else + { + columns.Add(new DataColumn(name + i, dr.GetFieldType(i))); + } + } + + while (dr.Read()) + { + DataRow daRow = dt.NewRow(); + for (int i = 0; i < columns.Count; i++) + { + daRow[columns[i].ColumnName] = dr.GetValue(i); + } + dt.Rows.Add(daRow); + } + dt.AcceptChanges(); + ds.Tables.Add(dt); + } while (dr.NextResult()); + } + } + } + + /// + /// 数据填充器 + /// + public class NpgsqlDataAdapter : IDataAdapter + { + private NpgsqlCommand command; + private string sql; + private NpgsqlConnection _sqlConnection; + + /// + /// SqlDataAdapter + /// + /// + public NpgsqlDataAdapter(NpgsqlCommand command) + { + this.command = command; + } + + public NpgsqlDataAdapter() + { + + } + + /// + /// SqlDataAdapter + /// + /// + /// + public NpgsqlDataAdapter(string sql, NpgsqlConnection _sqlConnection) + { + this.sql = sql; + this._sqlConnection = _sqlConnection; + } + + /// + /// SelectCommand + /// + public NpgsqlCommand SelectCommand + { + get + { + if (this.command == null) + { + this.command = new NpgsqlCommand(this.sql, this._sqlConnection); + } + return this.command; + } + set + { + this.command = value; + } + } + + /// + /// Fill + /// + /// + public void Fill(DataTable dt) + { + if (dt == null) + { + dt = new DataTable(); + } + var columns = dt.Columns; + var rows = dt.Rows; + using (NpgsqlDataReader dr = command.ExecuteReader()) + { + for (int i = 0; i < dr.FieldCount; i++) + { + string name = dr.GetName(i).Trim(); + if (!columns.Contains(name)) + columns.Add(new DataColumn(name, dr.GetFieldType(i))); + else + { + columns.Add(new DataColumn(name + i, dr.GetFieldType(i))); + } + } + + while (dr.Read()) + { + DataRow daRow = dt.NewRow(); + for (int i = 0; i < columns.Count; i++) + { + daRow[columns[i].ColumnName] = dr.GetValue(i); + } + dt.Rows.Add(daRow); + } + } + dt.AcceptChanges(); + } + + /// + /// Fill + /// + /// + public void Fill(DataSet ds) + { + if (ds == null) + { + ds = new DataSet(); + } + using (NpgsqlDataReader dr = command.ExecuteReader()) + { + do + { + var dt = new DataTable(); + var columns = dt.Columns; + var rows = dt.Rows; + for (int i = 0; i < dr.FieldCount; i++) + { + string name = dr.GetName(i).Trim(); + if (!columns.Contains(name)) + columns.Add(new DataColumn(name, dr.GetFieldType(i))); + else + { + columns.Add(new DataColumn(name + i, dr.GetFieldType(i))); + } + } + + while (dr.Read()) + { + DataRow daRow = dt.NewRow(); + for (int i = 0; i < columns.Count; i++) + { + daRow[columns[i].ColumnName] = dr.GetValue(i); + } + dt.Rows.Add(daRow); + } + dt.AcceptChanges(); + ds.Tables.Add(dt); + } while (dr.NextResult()); + } + } + } + + + /// + /// 数据填充器 + /// + public class MyDmDataAdapter : IDataAdapter + { + private DmCommand command; + private string sql; + private DmConnection _sqlConnection; + + /// + /// SqlDataAdapter + /// + /// + public MyDmDataAdapter(DmCommand command) + { + this.command = command; + } + + public MyDmDataAdapter() + { + + } + + /// + /// SqlDataAdapter + /// + /// + /// + public MyDmDataAdapter(string sql, DmConnection _sqlConnection) + { + this.sql = sql; + this._sqlConnection = _sqlConnection; + } + + /// + /// SelectCommand + /// + public DmCommand SelectCommand + { + get + { + if (this.command == null) + { + this.command = new DmCommand(this.sql, this._sqlConnection); + } + return this.command; + } + set + { + this.command = value; + } + } + + /// + /// Fill + /// + /// + public void Fill(DataTable dt) + { + if (dt == null) + { + dt = new DataTable(); + } + var columns = dt.Columns; + var rows = dt.Rows; + using (var dr = command.ExecuteReader()) + { + for (int i = 0; i < dr.FieldCount; i++) + { + string name = dr.GetName(i).Trim(); + if (!columns.Contains(name)) + columns.Add(new DataColumn(name, dr.GetFieldType(i))); + else + { + columns.Add(new DataColumn(name + i, dr.GetFieldType(i))); + } + } + + while (dr.Read()) + { + DataRow daRow = dt.NewRow(); + for (int i = 0; i < columns.Count; i++) + { + daRow[columns[i].ColumnName] = dr.GetValue(i); + } + dt.Rows.Add(daRow); + } + } + dt.AcceptChanges(); + } + + /// + /// Fill + /// + /// + public void Fill(DataSet ds) + { + if (ds == null) + { + ds = new DataSet(); + } + using (var dr = command.ExecuteReader()) + { + do + { + var dt = new DataTable(); + var columns = dt.Columns; + var rows = dt.Rows; + for (int i = 0; i < dr.FieldCount; i++) + { + string name = dr.GetName(i).Trim(); + if (!columns.Contains(name)) + columns.Add(new DataColumn(name, dr.GetFieldType(i))); + else + { + columns.Add(new DataColumn(name + i, dr.GetFieldType(i))); + } + } + + while (dr.Read()) + { + DataRow daRow = dt.NewRow(); + for (int i = 0; i < columns.Count; i++) + { + daRow[columns[i].ColumnName] = dr.GetValue(i); + } + dt.Rows.Add(daRow); + } + dt.AcceptChanges(); + ds.Tables.Add(dt); + } while (dr.NextResult()); + } + } + } + + + /// + /// 数据填充器 + /// + public class KdbndpDataAdapter : IDataAdapter + { + private KdbndpCommand command; + private string sql; + private KdbndpConnection _sqlConnection; + + /// + /// SqlDataAdapter + /// + /// + public KdbndpDataAdapter(KdbndpCommand command) + { + this.command = command; + } + + public KdbndpDataAdapter() + { + + } + + /// + /// SqlDataAdapter + /// + /// + /// + public KdbndpDataAdapter(string sql, KdbndpConnection _sqlConnection) + { + this.sql = sql; + this._sqlConnection = _sqlConnection; + } + + /// + /// SelectCommand + /// + public KdbndpCommand SelectCommand + { + get + { + if (this.command == null) + { + this.command = new KdbndpCommand(this.sql, this._sqlConnection); + } + return this.command; + } + set + { + this.command = value; + } + } + + /// + /// Fill + /// + /// + public void Fill(DataTable dt) + { + if (dt == null) + { + dt = new DataTable(); + } + var columns = dt.Columns; + var rows = dt.Rows; + using (KdbndpDataReader dr = command.ExecuteReader()) + { + for (int i = 0; i < dr.FieldCount; i++) + { + string name = dr.GetName(i).Trim(); + if (!columns.Contains(name)) + columns.Add(new DataColumn(name, dr.GetFieldType(i))); + else + { + columns.Add(new DataColumn(name + i, dr.GetFieldType(i))); + } + } + + while (dr.Read()) + { + DataRow daRow = dt.NewRow(); + for (int i = 0; i < columns.Count; i++) + { + daRow[columns[i].ColumnName] = dr.GetValue(i); + } + dt.Rows.Add(daRow); + } + } + dt.AcceptChanges(); + } + + /// + /// Fill + /// + /// + public void Fill(DataSet ds) + { + if (ds == null) + { + ds = new DataSet(); + } + using (KdbndpDataReader dr = command.ExecuteReader()) + { + do + { + var dt = new DataTable(); + var columns = dt.Columns; + var rows = dt.Rows; + for (int i = 0; i < dr.FieldCount; i++) + { + string name = dr.GetName(i).Trim(); + if (!columns.Contains(name)) + columns.Add(new DataColumn(name, dr.GetFieldType(i))); + else + { + columns.Add(new DataColumn(name + i, dr.GetFieldType(i))); + } + } + + while (dr.Read()) + { + DataRow daRow = dt.NewRow(); + for (int i = 0; i < columns.Count; i++) + { + daRow[columns[i].ColumnName] = dr.GetValue(i); + } + dt.Rows.Add(daRow); + } + dt.AcceptChanges(); + ds.Tables.Add(dt); + } while (dr.NextResult()); + } + } + } + + + /// + /// 数据填充器 + /// + public class OscarDataAdapter : IDataAdapter + { + private OscarCommand command; + private string sql; + private OscarConnection _sqlConnection; + + /// + /// SqlDataAdapter + /// + /// + public OscarDataAdapter(OscarCommand command) + { + this.command = command; + } + + public OscarDataAdapter() + { + + } + + /// + /// SqlDataAdapter + /// + /// + /// + public OscarDataAdapter(string sql, OscarConnection _sqlConnection) + { + this.sql = sql; + this._sqlConnection = _sqlConnection; + } + + /// + /// SelectCommand + /// + public OscarCommand SelectCommand + { + get + { + if (this.command == null) + { + this.command = new OscarCommand(this.sql, this._sqlConnection); + } + return this.command; + } + set + { + this.command = value; + } + } + + /// + /// Fill + /// + /// + public void Fill(DataTable dt) + { + if (dt == null) + { + dt = new DataTable(); + } + var columns = dt.Columns; + var rows = dt.Rows; + using (OscarDataReader dr = command.ExecuteReader()) + { + for (int i = 0; i < dr.FieldCount; i++) + { + string name = dr.GetName(i).Trim(); + if (!columns.Contains(name)) + columns.Add(new DataColumn(name, dr.GetFieldType(i))); + else + { + columns.Add(new DataColumn(name + i, dr.GetFieldType(i))); + } + } + + while (dr.Read()) + { + DataRow daRow = dt.NewRow(); + for (int i = 0; i < columns.Count; i++) + { + daRow[columns[i].ColumnName] = dr.GetValue(i); + } + dt.Rows.Add(daRow); + } + } + dt.AcceptChanges(); + } + + /// + /// Fill + /// + /// + public void Fill(DataSet ds) + { + if (ds == null) + { + ds = new DataSet(); + } + using (OscarDataReader dr = command.ExecuteReader()) + { + do + { + var dt = new DataTable(); + var columns = dt.Columns; + var rows = dt.Rows; + for (int i = 0; i < dr.FieldCount; i++) + { + string name = dr.GetName(i).Trim(); + if (!columns.Contains(name)) + columns.Add(new DataColumn(name, dr.GetFieldType(i))); + else + { + columns.Add(new DataColumn(name + i, dr.GetFieldType(i))); + } + } + + while (dr.Read()) + { + DataRow daRow = dt.NewRow(); + for (int i = 0; i < columns.Count; i++) + { + daRow[columns[i].ColumnName] = dr.GetValue(i); + } + dt.Rows.Add(daRow); + } + dt.AcceptChanges(); + ds.Tables.Add(dt); + } while (dr.NextResult()); + } + } + } + + +} \ No newline at end of file diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/OnlyCore/IDataExtensions.cs.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/OnlyCore/IDataExtensions.cs.cs new file mode 100644 index 000000000..676a0ae34 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/OnlyCore/IDataExtensions.cs.cs @@ -0,0 +1,48 @@ +using System.Data; +using System.Text.RegularExpressions; + +namespace SqlSugar +{ + public interface IDataAdapter + { + void Fill(DataSet ds); + } + public partial class SqliteProvider : AdoProvider + { + public override void ExecuteBefore(string sql, SugarParameter[] parameters) + { + this.BeforeTime = DateTime.Now; + if (sql.HasValue() && parameters.HasValue()) + { + foreach (var parameter in parameters) + { + //Compatible with.NET CORE parameters case + var name = parameter.ParameterName; + if (!sql.Contains(name) && Regex.IsMatch(sql, "(" + name + "$)" + "|(" + name + @"[ ,\,])", RegexOptions.IgnoreCase)) + { + parameter.ParameterName = Regex.Match(sql, "(" + name + "$)" + "|(" + name + @"[ ,\,])", RegexOptions.IgnoreCase).Value.Trim(); + } + } + } + if (this.IsEnableLogEvent) + { + Action action = LogEventStarting; + if (action != null) + { + if (parameters == null || parameters.Length == 0) + { + action(sql, Array.Empty()); + } + else + { + action(sql, parameters); + } + } + } + } + } +} +namespace System.Data.Sqlite +{ + +} \ No newline at end of file diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/OnlyCore/PartialExpressionContexts.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/OnlyCore/PartialExpressionContexts.cs new file mode 100644 index 000000000..ef084a3fc --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/OnlyCore/PartialExpressionContexts.cs @@ -0,0 +1,32 @@ +namespace SqlSugar +{ + public partial class PostgreSQLExpressionContext + { + } + public partial class DmExpressionContext + { + } + public partial class OracleExpressionContext + { + } + public partial class SqlServerBlukCopy + { + } + public partial class MySqlBlukCopy + { + internal SqlSugarProvider Context { get; set; } + internal ISqlBuilder Builder { get; set; } + internal T[] Entitys { get; set; } + internal string Chara { get; set; } + + public MySqlBlukCopy(SqlSugarProvider context, ISqlBuilder builder, T[] entitys) + { + this.Context = context; + this.Builder = builder; + this.Entitys = entitys; + } + } + public partial class OracleBlukCopy + { + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/CodeFirst/DmCodeFirst.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/CodeFirst/DmCodeFirst.cs new file mode 100644 index 000000000..11f3f9996 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/CodeFirst/DmCodeFirst.cs @@ -0,0 +1,105 @@ +namespace SqlSugar +{ + public class DmCodeFirst : CodeFirstProvider + { + public override void NoExistLogic(EntityInfo entityInfo) + { + var tableName = GetTableName(entityInfo); + //Check.Exception(entityInfo.Columns.Where(it => it.IsPrimarykey).Count() > 1, "Use Code First ,The primary key must not exceed 1"); + List columns = new List(); + if (entityInfo.Columns.HasValue()) + { + foreach (var item in entityInfo.Columns.Where(it => it.IsIgnore == false)) + { + DbColumnInfo dbColumnInfo = this.EntityColumnToDbColumn(entityInfo, tableName, item); + columns.Add(dbColumnInfo); + } + if (entityInfo.IsCreateTableFiledSort) + { + columns = columns.OrderBy(c => c.CreateTableFieldSort).ToList(); + columns = columns.OrderBy(it => it.IsPrimarykey ? 0 : 1).ToList(); + } + } + columns = columns.OrderBy(it => it.IsPrimarykey ? 0 : 1).ToList(); + this.Context.DbMaintenance.CreateTable(tableName, columns, true); + } + protected override DbColumnInfo EntityColumnToDbColumn(EntityInfo entityInfo, string tableName, EntityColumnInfo item) + { + var propertyType = UtilMethods.GetUnderType(item.PropertyInfo); + var result = new DbColumnInfo() + { + TableId = entityInfo.Columns.IndexOf(item), + DbColumnName = item.DbColumnName.HasValue() ? item.DbColumnName : item.PropertyName, + IsPrimarykey = item.IsPrimarykey, + IsIdentity = item.IsIdentity, + TableName = tableName, + IsNullable = item.IsNullable, + DefaultValue = item.DefaultValue, + ColumnDescription = item.ColumnDescription, + Length = item.Length, + DecimalDigits = item.DecimalDigits, + Scale = item.DecimalDigits, + CreateTableFieldSort = item.CreateTableFieldSort + }; + GetDbType(item, propertyType, result); + if (result.DataType.Equals("varchar", StringComparison.CurrentCultureIgnoreCase) && result.Length == 0) + { + result.Length = 1; + } + return result; + } + protected override void GetDbType(EntityColumnInfo item, Type propertyType, DbColumnInfo result) + { + if (!string.IsNullOrEmpty(item.DataType)) + { + result.DataType = item.DataType; + } + else if (item.DataType == null && item.UnderType == UtilConstants.LongType) + { + result.Length = 0; + result.DecimalDigits = 0; + result.DataType = "NUMBER(19,0)"; + } + else if (propertyType.IsEnum()) + { + result.DataType = this.Context.Ado.DbBind.GetDbTypeName(item.Length > 9 ? UtilConstants.LongType.Name : UtilConstants.IntType.Name); + } + else + { + if (propertyType.Name.Equals("Guid", StringComparison.CurrentCultureIgnoreCase)) + { + result.DataType = this.Context.Ado.DbBind.GetDbTypeName(UtilConstants.StringType.Name); + if (result.Length <= 1) + { + result.Length = 36; + } + } + else + { + result.DataType = this.Context.Ado.DbBind.GetDbTypeName(propertyType.Name); + } + } + } + + protected override void ConvertColumns(List dbColumns) + { + foreach (var item in dbColumns) + { + if (item.DataType == "DateTime") + { + item.Length = 0; + } + } + } + + protected override void ChangeKey(EntityInfo entityInfo, string tableName, EntityColumnInfo item) + { + this.Context.DbMaintenance.UpdateColumn(tableName, EntityColumnToDbColumn(entityInfo, tableName, item)); + if (!item.IsPrimarykey) + this.Context.DbMaintenance.DropConstraint(tableName, null); + if (item.IsPrimarykey) + this.Context.DbMaintenance.AddPrimaryKey(tableName, item.DbColumnName); + } + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/DbBind/DmDbBind.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/DbBind/DmDbBind.cs new file mode 100644 index 000000000..e1af5037e --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/DbBind/DmDbBind.cs @@ -0,0 +1,170 @@ +namespace SqlSugar +{ + public class DmDbBind : DbBindProvider + { + public override string GetDbTypeName(string csharpTypeName) + { + if (csharpTypeName == UtilConstants.ByteArrayType.Name) + return "blob"; + if (csharpTypeName.Equals("int32", StringComparison.CurrentCultureIgnoreCase)) + csharpTypeName = "int"; + if (csharpTypeName.Equals("int16", StringComparison.CurrentCultureIgnoreCase)) + csharpTypeName = "short"; + if (csharpTypeName.Equals("int64", StringComparison.CurrentCultureIgnoreCase)) + csharpTypeName = "long"; + if (csharpTypeName.Equals("uint32", StringComparison.CurrentCultureIgnoreCase)) + csharpTypeName = "int"; + if (csharpTypeName.Equals("uint16", StringComparison.CurrentCultureIgnoreCase)) + csharpTypeName = "short"; + if (csharpTypeName.Equals("uint64", StringComparison.CurrentCultureIgnoreCase)) + csharpTypeName = "long"; + if (csharpTypeName.ToLower().IsIn("boolean", "bool")) + csharpTypeName = "bool"; + if (csharpTypeName == "Guid") + csharpTypeName = "string"; + if (csharpTypeName == "DateTimeOffset") + csharpTypeName = "DateTime"; + var mappings = this.MappingTypes.Where(it => it.Value.ToString().Equals(csharpTypeName, StringComparison.CurrentCultureIgnoreCase)); + return mappings.HasValue() ? mappings.First().Key : "varchar"; + } + public override string GetPropertyTypeName(string dbTypeName) + { + dbTypeName = dbTypeName.ToLower(); + var propertyTypes = MappingTypes.Where(it => it.Value.ToString().Equals(dbTypeName, StringComparison.CurrentCultureIgnoreCase) || it.Key.Equals(dbTypeName, StringComparison.CurrentCultureIgnoreCase)); + if (dbTypeName == "int32") + { + return "int"; + } + else if (dbTypeName == "int64") + { + return "long"; + } + else if (dbTypeName == "int16") + { + return "short"; + } + else if (propertyTypes == null) + { + return "other"; + } + else if (dbTypeName == "sbyte") + { + return "byte"; + } + else if (dbTypeName == "xml" || dbTypeName == "string") + { + return "string"; + } + if (dbTypeName == "byte[]") + { + return "byte[]"; + } + else if (propertyTypes?.Any() != true) + { + Check.ThrowNotSupportedException(string.Format(" \"{0}\" Type NotSupported, DbBindProvider.GetPropertyTypeName error.", dbTypeName)); + return null; + } + else if (propertyTypes.First().Value == CSharpDataType.byteArray) + { + return "byte[]"; + } + else + { + return propertyTypes.First().Value.ToString(); + } + } + public override List> MappingTypes + { + get + { + var extService = this.Context.CurrentConnectionConfig.ConfigureExternalServices; + if (extService?.AppendDataReaderTypeMappings.HasValue() == true) + { + return extService.AppendDataReaderTypeMappings.Union(MappingTypesConst).ToList(); + } + else + { + return MappingTypesConst; + } + } + } + public static List> MappingTypesConst = new List>() + { + new KeyValuePair("int",CSharpDataType.@int), + new KeyValuePair("bigint",CSharpDataType.@long), + new KeyValuePair("tinyint",CSharpDataType.@short), + new KeyValuePair("smallint",CSharpDataType.@short), + new KeyValuePair("integer",CSharpDataType.@int), + new KeyValuePair("interval year to month",CSharpDataType.@int), + new KeyValuePair("interval day to second",CSharpDataType.TimeSpan), + new KeyValuePair("intervalds",CSharpDataType.TimeSpan), + + new KeyValuePair("number",CSharpDataType.@int), + new KeyValuePair("number",CSharpDataType.@float), + new KeyValuePair("number",CSharpDataType.@short), + new KeyValuePair("number",CSharpDataType.@byte), + new KeyValuePair("number",CSharpDataType.@double), + new KeyValuePair("binaryfloat",CSharpDataType.@float), + new KeyValuePair("binarydouble",CSharpDataType.@double), + new KeyValuePair("number",CSharpDataType.@long), + new KeyValuePair("number",CSharpDataType.@bool), + new KeyValuePair("boolean",CSharpDataType.@bool), + new KeyValuePair("bit",CSharpDataType.@bool), + new KeyValuePair("decimal",CSharpDataType.@decimal), + new KeyValuePair("number",CSharpDataType.@decimal), + new KeyValuePair("numeric",CSharpDataType.@decimal), + new KeyValuePair("number",CSharpDataType.Single), + new KeyValuePair("decimal",CSharpDataType.Single), + new KeyValuePair("dec",CSharpDataType.@decimal), + new KeyValuePair("double precision",CSharpDataType.@double), + new KeyValuePair("binary", CSharpDataType.@byteArray), + + new KeyValuePair("varchar",CSharpDataType.@string), + new KeyValuePair("nvarchar",CSharpDataType.@string), + new KeyValuePair("varchar",CSharpDataType.@Guid), + new KeyValuePair("varchar2",CSharpDataType.@string), + new KeyValuePair("nvarchar2",CSharpDataType.@string), + new KeyValuePair("longvarchar",CSharpDataType.@string), + + + new KeyValuePair("char",CSharpDataType.@string), + new KeyValuePair("nchar",CSharpDataType.@string), + new KeyValuePair("clob",CSharpDataType.@string), + new KeyValuePair("text",CSharpDataType.@string), + new KeyValuePair("long",CSharpDataType.@string), + new KeyValuePair("nclob",CSharpDataType.@string), + new KeyValuePair("rowid",CSharpDataType.@string), + + new KeyValuePair("timestamp",CSharpDataType.DateTime), + new KeyValuePair("date",CSharpDataType.DateTime), + + new KeyValuePair("timestamp with local time zone",CSharpDataType.DateTime), + new KeyValuePair("timestamp with time zone",CSharpDataType.DateTime), + new KeyValuePair("timestamp with time zone",CSharpDataType.DateTime), + + + + new KeyValuePair("timestamp with local time zone",CSharpDataType.DateTimeOffset), + new KeyValuePair("timestamp with time zone",CSharpDataType.DateTimeOffset), + new KeyValuePair("timestamp with time zone",CSharpDataType.DateTimeOffset), + + new KeyValuePair("time",CSharpDataType.TimeSpan), + + new KeyValuePair("float",CSharpDataType.@decimal), + new KeyValuePair("real",CSharpDataType.@float), + + new KeyValuePair("blob",CSharpDataType.byteArray), + new KeyValuePair("image",CSharpDataType.byteArray), + new KeyValuePair("long raw",CSharpDataType.byteArray), + new KeyValuePair("raw",CSharpDataType.byteArray), + new KeyValuePair("bfile",CSharpDataType.byteArray), + new KeyValuePair("varbinary",CSharpDataType.byteArray) }; + public override List StringThrow + { + get + { + return new List() { "int32", "datetime", "decimal", "double", "byte" }; + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/DbFirst/DmDbFirst.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/DbFirst/DmDbFirst.cs new file mode 100644 index 000000000..c77989205 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/DbFirst/DmDbFirst.cs @@ -0,0 +1,6 @@ +namespace SqlSugar +{ + public class DmDbFirst : DbFirstProvider + { + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/DbMaintenance/DmDbMaintenance.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/DbMaintenance/DmDbMaintenance.cs new file mode 100644 index 000000000..8071b5e91 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/DbMaintenance/DmDbMaintenance.cs @@ -0,0 +1,669 @@ +using System.Data; +using System.Data.Common; +using System.Text.RegularExpressions; + +namespace SqlSugar +{ + public class DmDbMaintenance : DbMaintenanceProvider + { + #region DML + protected override string GetDataBaseSql + { + get + { + throw new NotSupportedException(); + } + } + protected override string GetColumnInfosByTableNameSql + { + get + { + throw new NotSupportedException(); + } + } + protected override string GetTableInfoListSql + { + get + { + return @"SELECT a.TABLE_NAME AS Name,b.COMMENTS AS Description +FROM USER_TABLES a +LEFT JOIN (SELECT DISTINCT TABLE_NAME,COMMENTS FROM USER_TAB_COMMENTS WHERE COMMENTS IS NOT NULL) b ON a.TABLE_NAME=b.TABLE_NAME +WHERE +a.table_name!='HELP' +AND a.table_name NOT LIKE '%$%' +AND a.table_name NOT LIKE 'LOGMNRC_%' +AND a.table_name!='LOGMNRP_CTAS_PART_MAP' +AND a.table_name!='LOGMNR_LOGMNR_BUILDLOG' +AND a.table_name!='SQLPLUS_PRODUCT_PROFILE'"; + } + } + protected override string GetViewInfoListSql + { + get + { + return @"select VIEW_NAME name from all_views WHERE OWNER=SF_GET_SCHEMA_NAME_BY_ID(CURRENT_SCHID) order by VIEW_NAME"; + } + } + #endregion + + #region DDL + protected override string IsAnyIndexSql + { + get + { + return "select count(1) from USER_INDEXES where upper(index_name)=upper('{0}') and table_owner=SF_GET_SCHEMA_NAME_BY_ID(CURRENT_SCHID)"; + } + } + protected override string CreateIndexSql + { + get + { + return "CREATE {3} INDEX Index_{0}_{2} ON {0}({1})"; + } + } + protected override string AddDefaultValueSql + { + get + { + return "ALTER TABLE {0} MODIFY({1} DEFAULT '{2}')"; + } + } + protected override string CreateDataBaseSql + { + get + { + return "CREATE DATABASE {0}"; + } + } + protected override string AddPrimaryKeySql + { + get + { + return "ALTER TABLE {0} ADD PRIMARY KEY({2}) /*{1}*/"; + } + } + protected override string AddColumnToTableSql + { + get + { + return "ALTER TABLE {0} ADD ({1} {2}{3} {4} {5} {6})"; + } + } + protected override string AlterColumnToTableSql + { + get + { + return "ALTER TABLE {0} modify ({1} {2}{3} {4} {5} {6}) "; + } + } + protected override string BackupDataBaseSql + { + get + { + return @"USE master;BACKUP DATABASE {0} TO disk = '{1}'"; + } + } + protected override string CreateTableSql + { + get + { + return "CREATE TABLE {0}(\r\n{1} )"; + } + } + protected override string CreateTableColumn + { + get + { + return "{0} {1}{2} {3} {4} {5}"; + } + } + protected override string TruncateTableSql + { + get + { + return "TRUNCATE TABLE {0}"; + } + } + protected override string BackupTableSql + { + get + { + return "create table {1} as select * from {2} where ROWNUM<={0}"; + } + } + protected override string DropTableSql + { + get + { + return "DROP TABLE {0}"; + } + } + protected override string DropColumnToTableSql + { + get + { + return "ALTER TABLE {0} DROP COLUMN {1}"; + } + } + protected override string DropConstraintSql + { + get + { + return "ALTER TABLE {0} DROP CONSTRAINT {1}"; + } + } + protected override string RenameColumnSql + { + get + { + return "ALTER TABLE {0} rename column {1} to {2}"; + } + } + protected override string AddColumnRemarkSql + { + get + { + return "comment on column {1}.{0} is '{2}'"; + } + } + + protected override string DeleteColumnRemarkSql + { + get + { + return "comment on column {1}.{0} is ''"; + } + } + + protected override string IsAnyColumnRemarkSql + { + get + { + return "select * from user_col_comments where Table_Name='{1}' AND COLUMN_NAME='{0}' order by column_name"; + } + } + + protected override string AddTableRemarkSql + { + get + { + return "comment on table {0} is '{1}'"; + } + } + + protected override string DeleteTableRemarkSql + { + get + { + return "comment on table {0} is ''"; + } + } + + protected override string IsAnyTableRemarkSql + { + get + { + return "select * from user_tab_comments where Table_Name='{0}'order by Table_Name"; + } + } + + protected override string RenameTableSql + { + get + { + return "alter table {0} rename to {1}"; + } + } + protected override string IsAnyProcedureSql => throw new NotImplementedException(); + #endregion + + #region Check + protected override string CheckSystemTablePermissionsSql + { + get + { + return "select t.table_name from user_tables t where rownum=1"; + } + } + #endregion + + #region Scattered + protected override string CreateTableNull + { + get + { + return " null "; + } + } + protected override string CreateTableNotNull + { + get + { + return " not null "; + } + } + protected override string CreateTablePirmaryKey + { + get + { + return "PRIMARY KEY"; + } + } + protected override string CreateTableIdentity + { + get + { + return "IDENTITY(1,1)"; + } + } + #endregion + + #region Methods + public override bool UpdateColumn(string tableName, DbColumnInfo column) + { + ConvertCreateColumnInfo(column); + var oldColumn = this.Context.DbMaintenance.GetColumnInfosByTableName(tableName, false) + .FirstOrDefault(it => it.DbColumnName.EqualCase(column.DbColumnName)); + if (oldColumn != null) + { + if (oldColumn.IsNullable == column.IsNullable) + { + var sql = GetUpdateColumnSqlOnlyType(tableName, column); + this.Context.Ado.ExecuteCommand(sql); + return true; + } + } + return base.UpdateColumn(tableName, column); + } + protected virtual string GetUpdateColumnSqlOnlyType(string tableName, DbColumnInfo columnInfo) + { + string columnName = this.SqlBuilder.GetTranslationColumnName(columnInfo.DbColumnName); + tableName = this.SqlBuilder.GetTranslationTableName(tableName); + string dataSize = GetSize(columnInfo); + string dataType = columnInfo.DataType; + string nullType = ""; + string primaryKey = null; + string identity = null; + string result = string.Format(this.AlterColumnToTableSql, tableName, columnName, dataType, dataSize, nullType, primaryKey, identity); + return result; + } + public override List GetDbTypes() + { + var result = this.Context.Ado.SqlQuery(@"SELECT DISTINCT DATA_TYPE +FROM DBA_TAB_COLUMNS +WHERE OWNER = user "); + result.Add("TIMESTAMP"); + result.Add("NCLOB"); + return result.Distinct().ToList(); + } + public override List GetTriggerNames(string tableName) + { + return this.Context.Ado.SqlQuery(@"SELECT trigger_name +FROM all_triggers +WHERE table_name = '" + tableName + "'"); + } + public override List GetFuncList() + { + return this.Context.Ado.SqlQuery(" SELECT object_name\r\nFROM all_objects\r\nWHERE object_type = 'FUNCTION' AND owner = USER "); + } + public override bool RenameTable(string oldTableName, string newTableName) + { + oldTableName = SqlBuilder.GetTranslationColumnName(oldTableName); + newTableName = SqlBuilder.GetTranslationColumnName(newTableName); + return base.RenameTable(oldTableName, newTableName); + } + public override List GetIndexList(string tableName) + { + var sql = $"SELECT index_name FROM user_ind_columns\r\nWHERE upper(table_name) = upper('{tableName}')"; + return this.Context.Ado.SqlQuery(sql); + } + public override bool AddColumn(string tableName, DbColumnInfo columnInfo) + { + if (columnInfo.DataType == "varchar" && columnInfo.Length == 0) + { + columnInfo.DataType = "varchar2"; + columnInfo.Length = 50; + } + ConvertCreateColumnInfo(columnInfo); + return base.AddColumn(tableName, columnInfo); + } + public override bool CreateIndex(string tableName, string[] columnNames, bool isUnique = false) + { + string sql = string.Format(CreateIndexSql, tableName, string.Join(",", columnNames), string.Join("_", columnNames.Select(it => (it + "abc").Substring(0, 3))), isUnique ? "UNIQUE" : ""); + this.Context.Ado.ExecuteCommand(sql); + return true; + } + public override bool AddDefaultValue(string tableName, string columnName, string defaultValue) + { + columnName = SqlBuilder.GetTranslationColumnName(columnName); + tableName = SqlBuilder.GetTranslationColumnName(tableName); + if (defaultValue == "''") + { + defaultValue = ""; + } + if (defaultValue.ToLower().IsIn("sysdate")) + { + var template = AddDefaultValueSql.Replace("'", ""); + string sql = string.Format(template, tableName, columnName, defaultValue); + this.Context.Ado.ExecuteCommand(sql); + return true; + } + else + { + return base.AddDefaultValue(tableName, columnName, defaultValue); + } + } + public override bool CreateDatabase(string databaseDirectory = null) + { + if (this.Context.Ado.IsValidConnection()) + { + return true; + } + Check.ExceptionEasy("dm no support create database ", "达梦不支持建库方法,请写有效连接字符串可以正常运行该方法。"); + return true; + } + public override bool CreateDatabase(string databaseName, string databaseDirectory = null) + { + if (this.Context.Ado.IsValidConnection()) + { + return true; + } + Check.ExceptionEasy("dm no support create database ", "达梦不支持建库方法,请写有效连接字符串可以正常运行该方法。"); + return true; + } + public override bool AddRemark(EntityInfo entity) + { + var db = this.Context; + var columns = entity.Columns.Where(it => it.IsIgnore == false).ToList(); + + foreach (var item in columns) + { + if (item.ColumnDescription != null) + { + //column remak + if (db.DbMaintenance.IsAnyColumnRemark(item.DbColumnName.ToUpper(IsUppper), item.DbTableName.ToUpper(IsUppper))) + { + db.DbMaintenance.DeleteColumnRemark(this.SqlBuilder.GetTranslationColumnName(item.DbColumnName), this.SqlBuilder.GetTranslationColumnName(item.DbTableName)); + db.DbMaintenance.AddColumnRemark(this.SqlBuilder.GetTranslationColumnName(item.DbColumnName), this.SqlBuilder.GetTranslationColumnName(item.DbTableName), item.ColumnDescription); + } + else + { + db.DbMaintenance.AddColumnRemark(this.SqlBuilder.GetTranslationColumnName(item.DbColumnName), this.SqlBuilder.GetTranslationColumnName(item.DbTableName), item.ColumnDescription); + } + } + } + + //table remak + if (entity.TableDescription != null) + { + if (db.DbMaintenance.IsAnyTableRemark(entity.DbTableName)) + { + db.DbMaintenance.DeleteTableRemark(SqlBuilder.GetTranslationColumnName(entity.DbTableName)); + db.DbMaintenance.AddTableRemark(SqlBuilder.GetTranslationColumnName(entity.DbTableName), entity.TableDescription); + } + else + { + db.DbMaintenance.AddTableRemark(SqlBuilder.GetTranslationColumnName(entity.DbTableName), entity.TableDescription); + } + } + return true; + } + + public override bool AddTableRemark(string tableName, string description) + { + return base.AddTableRemark(SqlBuilder.GetTranslationColumnName(tableName), description); + } + public override bool AddColumnRemark(string columnName, string tableName, string description) + { + return base.AddColumnRemark(SqlBuilder.GetTranslationColumnName(columnName), SqlBuilder.GetTranslationColumnName(tableName), description); + } + public override List GetColumnInfosByTableName(string tableName, bool isCache = true) + { + string cacheKey = "DbMaintenanceProvider.GetColumnInfosByTableName." + this.SqlBuilder.GetNoTranslationColumnName(tableName).ToLower(); + cacheKey = GetCacheKey(cacheKey); + if (!isCache) + return GetColumnInfosByTableName(tableName); + else + return this.Context.Utilities.GetReflectionInoCacheInstance().GetOrCreate(cacheKey, + () => + { + return GetColumnInfosByTableName(tableName); + + }); + } + + private List GetColumnInfosByTableName(string tableName) + { + List columns = GetOracleDbType(tableName); + string sql = "select * from " + SqlBuilder.GetTranslationTableName(tableName) + " WHERE 1=2 "; + if (!this.GetTableInfoList(false).Any(it => it.Name == SqlBuilder.GetTranslationTableName(tableName).TrimStart('\"').TrimEnd('\"'))) + { + sql = "select * from \"" + tableName + "\" WHERE 1=2 "; + } + var oldIsEnableLog = this.Context.Ado.IsEnableLogEvent; + this.Context.Ado.IsEnableLogEvent = false; + using (DbDataReader reader = (DbDataReader)this.Context.Ado.GetDataReader(sql)) + { + this.Context.Ado.IsEnableLogEvent = oldIsEnableLog; + List result = new List(); + var schemaTable = reader.GetSchemaTable(); + foreach (System.Data.DataRow row in schemaTable.Rows) + { + DbColumnInfo column = new DbColumnInfo() + { + TableName = tableName, + DataType = row["DataType"].ToString().Replace("System.", "").Trim(), + IsNullable = (bool)row["AllowDBNull"], + IsIdentity = (bool)row["IsIdentity"], + ColumnDescription = GetFieldComment(tableName, row["ColumnName"].ToString()), + DbColumnName = row["ColumnName"].ToString(), + //DefaultValue = row["defaultValue"].ToString(), + IsPrimarykey = GetPrimaryKeyByTableNames(tableName).Any(it => it.Equals(row["ColumnName"].ToString(), StringComparison.CurrentCultureIgnoreCase)), + Length = row["ColumnSize"].ObjToInt(), + Scale = row["numericscale"].ObjToInt() + }; + if (column.DataType.EqualCase("number") || column.DataType.EqualCase("decimal")) + { + column.Length = row["numericprecision"].ObjToInt(); + column.Scale = row["numericscale"].ObjToInt(); + column.DecimalDigits = row["numericscale"].ObjToInt(); + if (column.Length == 38 && column.Scale == 0) + { + column.Length = 22; + } + } + var current = columns.FirstOrDefault(it => it.DbColumnName.EqualCase(column.DbColumnName)); + if (current != null) + { + column.OracleDataType = current.DataType; + column.DefaultValue = current.DefaultValue?.TrimStart('\'')?.TrimEnd('\''); + } + result.Add(column); + } + return result; + } + } + + private List GetOracleDbType(string tableName) + { + var sql0 = $@"select + t1.table_name as TableName, + t6.comments, + t1.column_id, + t1.column_name as DbColumnName, + t5.comments, + t1.data_type as DataType, + t1.data_length as Length, + t1.char_length, + t1.data_precision, + t1.data_scale, + t1.nullable, + t1.data_default as DefaultValue, + t4.index_name, + t4.column_position, + t4.descend + from user_tab_columns t1 + left join (select t2.table_name, + t2.column_name, + t2.column_position, + t2.descend, + t3.index_name + from user_ind_columns t2 + left join user_indexes t3 + on t2.table_name = t3.table_name and t2.index_name = t3.index_name + and t3.status = 'valid' and t3.uniqueness = 'unique') t4 --unique:唯一索引 + on t1.table_name = t4.table_name and t1.column_name = t4.column_name + left join user_col_comments t5 on t1.table_name = t5.table_name and t1.column_name = t5.column_name + left join user_tab_comments t6 on t1.table_name = t6.table_name + where upper(t1.table_name)=upper('{tableName}') + order by t1.table_name, t1.column_id"; + + var columns = this.Context.Ado.SqlQuery(sql0); + return columns; + } + + private List GetPrimaryKeyByTableNames(string tableName) + { + string cacheKey = "DbMaintenanceProvider.GetPrimaryKeyByTableNames." + this.SqlBuilder.GetNoTranslationColumnName(tableName).ToLower(); + cacheKey = GetCacheKey(cacheKey); + return this.Context.Utilities.GetReflectionInoCacheInstance().GetOrCreate(cacheKey, + () => + { + var oldIsEnableLog = this.Context.Ado.IsEnableLogEvent; + this.Context.Ado.IsEnableLogEvent = false; + string sql = @" select distinct cu.COLUMN_name KEYNAME from user_cons_columns cu, user_constraints au + where cu.constraint_name = au.constraint_name + and au.constraint_type = 'P' and au.table_name = '" + tableName.ToUpper(IsUppper) + @"'"; + var pks = this.Context.Ado.SqlQuery(sql); + this.Context.Ado.IsEnableLogEvent = oldIsEnableLog; + return pks; + }); + } + + public string GetTableComment(string tableName) + { + string cacheKey = "DbMaintenanceProvider.GetTableComment." + tableName; + var comments = this.Context.Utilities.GetReflectionInoCacheInstance().GetOrCreate(cacheKey, + () => + { + string sql = "SELECT COMMENTS FROM USER_TAB_COMMENTS WHERE TABLE_NAME =@tableName ORDER BY TABLE_NAME"; + var oldIsEnableLog = this.Context.Ado.IsEnableLogEvent; + this.Context.Ado.IsEnableLogEvent = false; + var pks = this.Context.Ado.SqlQuery(sql, new { tableName = tableName.ToUpper(IsUppper) }); + this.Context.Ado.IsEnableLogEvent = oldIsEnableLog; + return pks; + }); + return comments.HasValue() ? comments.First() : ""; + } + + public string GetFieldComment(string tableName, string filedName) + { + string cacheKey = "DbMaintenanceProvider.GetFieldComment." + tableName; + var comments = this.Context.Utilities.GetReflectionInoCacheInstance().GetOrCreate(cacheKey, + () => + { + string sql = "SELECT TVNAME AS TableName, COLNAME as DbColumnName ,COMMENT$ AS ColumnDescription from SYSCOLUMNCOMMENTS WHERE TVNAME='" + tableName.ToUpper(IsUppper) + "' ORDER BY TVNAME"; + var oldIsEnableLog = this.Context.Ado.IsEnableLogEvent; + this.Context.Ado.IsEnableLogEvent = false; + var pks = this.Context.Ado.SqlQuery(sql); + this.Context.Ado.IsEnableLogEvent = oldIsEnableLog; + return pks; + }); + if (comments.HasValue()) + { + var comment = comments.FirstOrDefault(it => it.DbColumnName.Equals(filedName, StringComparison.CurrentCultureIgnoreCase)); + return comment?.ColumnDescription; + } + else + { + return ""; + } + + } + + public override bool CreateTable(string tableName, List columns, bool isCreatePrimaryKey = true) + { + if (columns.HasValue()) + { + foreach (var item in columns) + { + ConvertCreateColumnInfo(item); + if (item.DbColumnName.Equals("GUID", StringComparison.CurrentCultureIgnoreCase) && item.Length == 0) + { + item.Length = 10; + } + } + } + string sql = GetCreateTableSql(tableName, columns); + //string primaryKeyInfo = null; + if (columns.Any(it => it.IsPrimarykey) && isCreatePrimaryKey) + { + sql = sql.TrimEnd(')') + string.Format(", Primary key({0})", string.Join(",", columns.Where(it => it.IsPrimarykey).Select(it => this.SqlBuilder.GetTranslationColumnName(it.DbColumnName.ToUpper(IsUppper))))); + sql = sql + ")"; + } + //sql = sql.Replace("$PrimaryKey", primaryKeyInfo); + this.Context.Ado.ExecuteCommand(sql); + return true; + } + public static string ExtractSchema(string connectionString) + { + string pattern = @"(?i)(?:^|;)schema=(\w+)"; + Match match = Regex.Match(connectionString?.Replace(" ", ""), pattern, RegexOptions.IgnoreCase); + return match.Success ? match.Groups[1].Value : null; + } + public override bool IsAnyTable(string tableName, bool isCache = true) + { + var isSchema = this.Context.CurrentConnectionConfig?.ConnectionString?.Replace(" ", "")?.ToLower()?.Contains("schema=") == true; + if (isSchema) + { + var schema = ExtractSchema(this.Context.CurrentConnectionConfig?.ConnectionString); + Check.ExceptionEasy(schema == null, "ConnectionString schema format error, please use schema=(\\w+)", "连接字符串schema格式错误,请用schema=(\\w+)"); + return this.Context.Ado.GetInt($@"SELECT COUNT(*) +FROM ALL_TABLES t +WHERE upper(t.TABLE_NAME) = upper('{tableName}') + AND upper(t.OWNER) = upper('{schema}') +") > 0; + + } + else + { + return base.IsAnyTable(tableName, isCache); + } + } + #endregion + + #region Helper + public bool IsUppper + { + get + { + if (this.Context.CurrentConnectionConfig.MoreSettings == null) + { + return true; + } + else + { + return this.Context.CurrentConnectionConfig.MoreSettings.IsAutoToUpper == true; + } + } + } + private static void ConvertCreateColumnInfo(DbColumnInfo x) + { + string[] array = new string[] { "int", "date", "clob", "nclob" }; + if (x.OracleDataType.HasValue()) + { + x.DataType = x.OracleDataType; + } + if (array.Contains(x.DataType?.ToLower())) + { + x.Length = 0; + x.DecimalDigits = 0; + } + if (x.DecimalDigits > 0 && x.DataType?.ToLower()?.IsIn("varchar", "clob", "varchar2", "nvarchar2", "nvarchar") == true) + { + x.DecimalDigits = 0; + } + } + #endregion + } +} \ No newline at end of file diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/DmProvider.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/DmProvider.cs new file mode 100644 index 000000000..4efea575e --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/DmProvider.cs @@ -0,0 +1,246 @@ +using Dm; + +using System.Data; +using System.Data.Common; +using System.Text.RegularExpressions; + + +namespace SqlSugar +{ + public partial class DmProvider : AdoProvider + { + public DmProvider() + { + //this.FormatSql = sql => + //{ + // sql = sql.Replace("+@", "+:"); + // if (sql.HasValue() && sql.Contains('@')) + // { + // var exceptionalCaseInfo = Regex.Matches(sql, @"\'[^\=]*?\@.*?\'|[\.,\w]+\@[\.,\w]+ | [\.,\w]+\@[\.,\w]+|[\.,\w]+\@[\.,\w]+ |\d+\@\d|\@\@"); + // if (exceptionalCaseInfo != null) + // { + // foreach (var item in exceptionalCaseInfo.Cast()) + // { + // if (item.Value != null && item.Value.IndexOf(",") == 1 && Regex.IsMatch(item.Value, @"^ \,\@\w+$")) + // { + // continue; + // } + // else if (item.Value != null && Regex.IsMatch(item.Value.Trim(), @"^\w+\,\@\w+\,$")) + // { + // continue; + // } + // else if (item.Value != null && item.Value.ObjToString().Contains("||") && Regex.IsMatch(item.Value.Replace(" ", "").Trim(), @"\|\|@\w+\|\|")) + // { + // continue; + // } + // else if (item.Value != null && Regex.IsMatch(item.Value.Replace(" ", "").Trim(), @"\(\@\w+\,")) + // { + // continue; + // } + // else if (item.Value != null && item.Value.Contains("=") && Regex.IsMatch(item.Value, @"\w+ \@\w+[ ]{0,1}\=[ ]{0,1}\'")) + // { + // continue; + // } + // sql = sql.Replace(item.Value, item.Value.Replace("@", UtilConstants.ReplaceKey)); + // } + // } + // sql = sql.Replace("@", ":"); + // sql = sql.Replace(UtilConstants.ReplaceKey, "@"); + // } + // return sql; + //}; + } + public override string SqlParameterKeyWord + { + get + { + return ":"; + } + } + public override IDbConnection Connection + { + get + { + if (base._DbConnection == null) + { + try + { + var npgsqlConnectionString = base.Context.CurrentConnectionConfig.ConnectionString; + base._DbConnection = new DmConnection(npgsqlConnectionString); + } + catch (Exception ex) + { + Check.Exception(true, ErrorMessage.ConnnectionOpen, ex.Message); + } + } + return base._DbConnection; + } + set + { + base._DbConnection = value; + } + } + + public override void BeginTran(string transactionName) + { + base.BeginTran(); + } + /// + /// Only SqlServer + /// + /// + /// + public override void BeginTran(IsolationLevel iso, string transactionName) + { + base.BeginTran(iso); + } + public override IDataAdapter GetAdapter() + { + return new MyDmDataAdapter(); + } + public override DbCommand GetCommand(string sql, SugarParameter[] parameters) + { + sql = ReplaceKeyWordParameterName(sql, parameters); + DmCommand sqlCommand = new DmCommand(sql, (DmConnection)this.Connection); + sqlCommand.CommandType = this.CommandType; + sqlCommand.CommandTimeout = this.CommandTimeOut; + if (this.Transaction != null) + { + sqlCommand.Transaction = (DmTransaction)this.Transaction; + } + if (parameters.HasValue()) + { + IDataParameter[] ipars = ToIDbDataParameter(parameters); + sqlCommand.Parameters.AddRange((DmParameter[])ipars); + } + CheckConnection(); + return sqlCommand; + } + public override void SetCommandToAdapter(IDataAdapter dataAdapter, DbCommand command) + { + ((MyDmDataAdapter)dataAdapter).SelectCommand = (DmCommand)command; + } + /// + /// if mysql return MySqlParameter[] pars + /// if sqlerver return SqlParameter[] pars ... + /// + /// + /// + public override IDataParameter[] ToIDbDataParameter(params SugarParameter[] parameters) + { + if (parameters == null || parameters.Length == 0) return null; + DmParameter[] result = new DmParameter[parameters.Length]; + int index = 0; + var isVarchar = this.Context.IsVarchar(); + foreach (var parameter in parameters) + { + if (parameter.Value == null) parameter.Value = DBNull.Value; + var sqlParameter = new DmParameter(); + sqlParameter.ParameterName = parameter.ParameterName; + sqlParameter.Size = parameter.Size; + sqlParameter.Value = parameter.Value; + sqlParameter.DbType = parameter.DbType; + if (sqlParameter.ParameterName[0] == '@') + { + sqlParameter.ParameterName = string.Concat(":", sqlParameter.ParameterName.AsSpan(1, sqlParameter.ParameterName.Length - 1)); + } + if (sqlParameter.DbType == System.Data.DbType.Guid) + { + sqlParameter.DbType = System.Data.DbType.String; + if (sqlParameter.Value != DBNull.Value) + sqlParameter.Value = sqlParameter.Value.ToString(); + } + if (parameter.IsClob) + { + sqlParameter.DmSqlType = DmDbType.Clob; + sqlParameter.Value = parameter.Value; + } + if (parameter.IsNClob) + { + sqlParameter.DmSqlType = DmDbType.Clob; + sqlParameter.Value = parameter.Value; + } + if (parameter.Direction == 0) + { + parameter.Direction = ParameterDirection.Input; + } + sqlParameter.Direction = parameter.Direction; + result[index] = sqlParameter; + if (sqlParameter.Direction.IsIn(ParameterDirection.Output, ParameterDirection.InputOutput, ParameterDirection.ReturnValue)) + { + if (this.OutputParameters == null) this.OutputParameters = new List(); + this.OutputParameters.RemoveAll(it => it.ParameterName == sqlParameter.ParameterName); + this.OutputParameters.Add(sqlParameter); + } + if (isVarchar && sqlParameter.DbType == System.Data.DbType.String) + { + sqlParameter.DbType = System.Data.DbType.AnsiString; + } + if (parameter.IsRefCursor) + { + sqlParameter.DmSqlType = DmDbType.Cursor; + } + if (IsSpOutPutParameter(sqlParameter)) + { + sqlParameter.ParameterName = sqlParameter.ParameterName.Replace("@", ":"); + } + ++index; + } + return result; + } + + private bool IsSpOutPutParameter(DmParameter sqlParameter) + { + return sqlParameter.Direction == ParameterDirection.Output && this.CommandType == CommandType.StoredProcedure; + } + + private static string[] KeyWord = new string[] { "@month", ":month", ":day", "@day", "@group", ":group", ":index", "@index", "@order", ":order", "@user", "@level", ":user", ":level", ":type", "@type", ":year", "@year" }; + private static string ReplaceKeyWordParameterName(string sql, SugarParameter[] parameters) + { + sql = ReplaceKeyWordWithAd(sql, parameters); + if (parameters.HasValue() && parameters.Any(it => it.ParameterName.ToLower().IsIn(KeyWord))) + { + int i = 0; + foreach (var Parameter in parameters.OrderByDescending(it => it.ParameterName.Length)) + { + if (Parameter.ParameterName?.ToLower().IsContainsIn(KeyWord) == true) + { + var newName = ":p" + i + 100; + sql = Regex.Replace(sql, Parameter.ParameterName, newName, RegexOptions.IgnoreCase); + Parameter.ParameterName = newName; + i++; + } + } + } + return sql; + } + + private static string ReplaceKeyWordWithAd(string sql, SugarParameter[] parameters) + { + if (parameters != null && sql?.Contains('@') == true) + { + foreach (var item in parameters.OrderByDescending(it => it.ParameterName.Length)) + { + if (item.ParameterName.StartsWith('@')) + { + item.ParameterName = ":" + item.ParameterName.TrimStart('@'); + } + sql = Regex.Replace(sql, "@" + item.ParameterName.TrimStart(':'), item.ParameterName, RegexOptions.IgnoreCase); + } + } + + return sql; + } + public override Action ErrorEvent => it => + { + if (base.ErrorEvent != null) + { + base.ErrorEvent(it); + } + if (it.Message?.Contains("Detail redacted as it may contain sensitive data.") == true) + { + Check.ExceptionEasy(it.Message, $"错误:可能是字段太小超出,详细错误:{it.Message} "); + } + }; + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/Insertable/DmInserttable.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/Insertable/DmInserttable.cs new file mode 100644 index 000000000..8ece02f42 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/Insertable/DmInserttable.cs @@ -0,0 +1,7 @@ +namespace SqlSugar +{ + public class DmInserttable : InsertableProvider where T : class, new() + { + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/Queryable/DmQueryable.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/Queryable/DmQueryable.cs new file mode 100644 index 000000000..bde4467a2 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/Queryable/DmQueryable.cs @@ -0,0 +1,63 @@ +namespace SqlSugar +{ + public class DmQueryable : QueryableProvider + { + public override ISugarQueryable With(string withString) + { + return this; + } + + //public override ISugarQueryable PartitionBy(string groupFileds) + //{ + // this.GroupBy(groupFileds); + // return this; + //} + } + public class DmQueryable : QueryableProvider + { + public new ISugarQueryable With(string withString) + { + return this; + } + } + public class DmQueryable : QueryableProvider + { + + } + public class DmQueryable : QueryableProvider + { + + } + public class DmQueryable : QueryableProvider + { + + } + public class DmQueryable : QueryableProvider + { + + } + public class DmQueryable : QueryableProvider + { + + } + public class DmQueryable : QueryableProvider + { + + } + public class DmQueryable : QueryableProvider + { + + } + public class DmQueryable : QueryableProvider + { + + } + public class DmQueryable : QueryableProvider + { + + } + public class DmQueryable : QueryableProvider + { + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/SqlBuilder/DmBlukCopy.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/SqlBuilder/DmBlukCopy.cs new file mode 100644 index 000000000..e1d0120d2 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/SqlBuilder/DmBlukCopy.cs @@ -0,0 +1,150 @@ +using Dm; + +using System.Data; +namespace SqlSugar +{ + public class DmBlukCopy + { + internal List> DbColumnInfoList { get; set; } + internal SqlSugarProvider Context { get; set; } + internal ISqlBuilder Builder { get; set; } + internal InsertBuilder InsertBuilder { get; set; } + internal object[] Inserts { get; set; } + + public int ExecuteBulkCopy() + { + if (DbColumnInfoList == null || DbColumnInfoList.Count == 0) return 0; + + if (Inserts.First().GetType() == typeof(DataTable)) + { + return WriteToServer(); + } + DataTable dt = GetCopyData(); + DmBulkCopy bulkCopy = GetBulkCopyInstance(); + bulkCopy.DestinationTableName = InsertBuilder.GetTableNameString; + try + { + bulkCopy.WriteToServer(dt); + } + catch (Exception) + { + CloseDb(); + throw; + } + CloseDb(); + return DbColumnInfoList.Count; + } + + public async Task ExecuteBulkCopyAsync() + { + if (DbColumnInfoList == null || DbColumnInfoList.Count == 0) return 0; + + if (Inserts.First().GetType() == typeof(DataTable)) + { + return WriteToServer(); + } + DataTable dt = GetCopyData(); + DmBulkCopy bulkCopy = GetBulkCopyInstance(); + bulkCopy.DestinationTableName = InsertBuilder.GetTableNameString; + try + { + bulkCopy.WriteToServer(dt); + await Task.Delay(0).ConfigureAwait(false); + } + catch (Exception) + { + CloseDb(); + throw; + } + CloseDb(); + return DbColumnInfoList.Count; + } + + private int WriteToServer() + { + var dt = this.Inserts.First() as DataTable; + if (dt == null) + return 0; + Check.Exception(dt.TableName == "Table", "dt.TableName can't be null "); + dt = GetCopyWriteDataTable(dt); + DmBulkCopy copy = GetBulkCopyInstance(); + copy.DestinationTableName = this.Builder.GetTranslationColumnName(dt.TableName); + copy.WriteToServer(dt); + CloseDb(); + return dt.Rows.Count; + } + private DataTable GetCopyWriteDataTable(DataTable dt) + { + var result = this.Context.Ado.GetDataTable("select top 0 * from " + this.Builder.GetTranslationColumnName(dt.TableName)); + foreach (DataRow item in dt.Rows) + { + DataRow dr = result.NewRow(); + foreach (DataColumn column in result.Columns) + { + + if (dt.Columns.Cast().Select(it => it.ColumnName.ToLower()).Contains(column.ColumnName.ToLower())) + { + dr[column.ColumnName] = item[column.ColumnName]; + if (dr[column.ColumnName] == null) + { + dr[column.ColumnName] = DBNull.Value; + } + } + } + result.Rows.Add(dr); + } + result.TableName = dt.TableName; + return result; + } + private DmBulkCopy GetBulkCopyInstance() + { + DmBulkCopy copy; + if (this.Context.Ado.Transaction == null) + { + copy = new DmBulkCopy((DmConnection)this.Context.Ado.Connection); + } + else + { + copy = new DmBulkCopy((DmConnection)this.Context.Ado.Connection, DmBulkCopyOptions.Default, (DmTransaction)this.Context.Ado.Transaction); + } + if (this.Context.Ado.Connection.State == ConnectionState.Closed) + { + this.Context.Ado.Connection.Open(); + } + copy.BulkCopyTimeout = this.Context.Ado.CommandTimeOut; + return copy; + } + private DataTable GetCopyData() + { + var dt = this.Context.Ado.GetDataTable("select top 0 * from " + InsertBuilder.GetTableNameString); + foreach (var rowInfos in DbColumnInfoList) + { + var dr = dt.NewRow(); + foreach (var value in rowInfos) + { + if (value.Value != null && UtilMethods.GetUnderType(value.Value.GetType()) == UtilConstants.DateType) + { + if (value.Value != null && value.Value.ToString() == DateTime.MinValue.ToString()) + { + value.Value = Convert.ToDateTime("1753/01/01"); + } + } + if (value.Value == null) + { + value.Value = DBNull.Value; + } + dr[value.DbColumnName] = value.Value; + } + dt.Rows.Add(dr); + } + return dt; + } + private void CloseDb() + { + if (this.Context.CurrentConnectionConfig.IsAutoCloseConnection && this.Context.Ado.Transaction == null) + { + this.Context.Ado.Connection.Close(); + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/SqlBuilder/DmBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/SqlBuilder/DmBuilder.cs new file mode 100644 index 000000000..aefc9bb28 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/SqlBuilder/DmBuilder.cs @@ -0,0 +1,61 @@ +namespace SqlSugar +{ + public class DmBuilder : SqlBuilderProvider + { + public override string SqlParameterKeyWord + { + get + { + return ":"; + } + } + public override string SqlDateNow + { + get + { + return "sysdate"; + } + } + public override string FullSqlDateNow + { + get + { + return "select sysdate from dual"; + } + } + public override string SqlTranslationLeft { get { return "\""; } } + public override string SqlTranslationRight { get { return "\""; } } + public override string GetTranslationTableName(string name) + { + var result = base.GetTranslationTableName(name); + if (result.Contains('(') && result.Contains(')')) + return result; + else + return result.ToUpper(IsUppper); + } + public override string GetTranslationColumnName(string entityName, string propertyName) + { + var result = base.GetTranslationColumnName(entityName, propertyName); + return result.ToUpper(IsUppper); + } + public override string GetTranslationColumnName(string propertyName) + { + var result = base.GetTranslationColumnName(propertyName); + return result.ToUpper(IsUppper); + } + public bool IsUppper + { + get + { + if (this.Context.CurrentConnectionConfig.MoreSettings == null) + { + return true; + } + else + { + return this.Context.CurrentConnectionConfig.MoreSettings.IsAutoToUpper == true; + } + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/SqlBuilder/DmDeleteBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/SqlBuilder/DmDeleteBuilder.cs new file mode 100644 index 000000000..0ade3766a --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/SqlBuilder/DmDeleteBuilder.cs @@ -0,0 +1,7 @@ +namespace SqlSugar +{ + public class DmDeleteBuilder : DeleteBuilder + { + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/SqlBuilder/DmExpressionContext.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/SqlBuilder/DmExpressionContext.cs new file mode 100644 index 000000000..e6e5777d4 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/SqlBuilder/DmExpressionContext.cs @@ -0,0 +1,338 @@ +namespace SqlSugar +{ + public partial class DmExpressionContext : ExpressionContext, ILambdaExpressions + { + public SqlSugarProvider Context { get; set; } + public DmExpressionContext() + { + base.DbMehtods = new DmMethod(); + } + public override string SqlParameterKeyWord + { + get + { + return ":"; + } + } + public override string SqlTranslationLeft { get { return "\""; } } + public override string SqlTranslationRight { get { return "\""; } } + public override string GetTranslationTableName(string entityName, bool isMapping = true) + { + return base.GetTranslationTableName(entityName, isMapping).ToUpper(IsUppper); + } + public override string GetTranslationColumnName(string columnName) + { + return base.GetTranslationColumnName(columnName).ToUpper(IsUppper); + } + public override string GetDbColumnName(string entityName, string propertyName) + { + return base.GetDbColumnName(entityName, propertyName).ToUpper(IsUppper); + } + public bool IsUppper + { + get + { + if (this.SugarContext?.Context?.Context?.CurrentConnectionConfig?.MoreSettings == null) + { + return true; + } + else + { + return this.SugarContext?.Context?.Context?.CurrentConnectionConfig?.MoreSettings.IsAutoToUpper == true; + } + } + } + } + public partial class DmMethod : DefaultDbMethod, IDbMethods + { + + public override string WeekOfYear(MethodCallExpressionModel mode) + { + var parameterNameA = mode.Args[0].MemberName; + return $"TO_NUMBER(TO_CHAR({parameterNameA}, 'WW')) "; + } + public override string ParameterKeyWord { get; set; } = ":"; + public string ForXmlPathLast; + public override string GetForXmlPath() + { + if (string.IsNullOrEmpty(ForXmlPathLast)) return null; + return " GROUP BY " + ForXmlPathLast; + } + public override string GetStringJoinSelector(string result, string separator) + { + if (result.ObjToString().Trim().StartsWith("DISTINCT ", StringComparison.OrdinalIgnoreCase)) + { + int index = result.IndexOf(result, StringComparison.Ordinal); // 找到去掉前缀空格后的位置 + result = result.Substring(index + 9); // 9 是 "DISTINCT " 的长度 + ForXmlPathLast = result; + return $"listagg(to_char(max({result})),'{separator}') within group(order by max({result})) "; + } + else + { + return $"listagg(to_char({result}),'{separator}') within group(order by {result}) "; + } + } + public override string ToInt64(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS Number)", parameter.MemberName); + } + + public override string ToTime(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" to_timestamp({0},'0000-01-01 hh24:mi:ss') ", parameter.MemberName); + } + public override string Substring(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + var parameter3 = model.Args[2]; + return string.Format("SUBSTR({0},1 + {1},{2})", parameter.MemberName, parameter2.MemberName, parameter3.MemberName); + } + public override string DateValue(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + var type = (DateType)Enum.Parse(typeof(DateType), parameter2.MemberValue.ObjToString(), false); + switch (type) + { + case DateType.Year: + return string.Format("(CAST(TO_CHAR({0},'yyyy') AS NUMBER))", parameter.MemberName); + case DateType.Month: + return string.Format("(CAST(TO_CHAR({0},'mm') AS NUMBER))", parameter.MemberName); + case DateType.Hour: + return string.Format("(CAST(TO_CHAR({0},'hh24') AS NUMBER))", parameter.MemberName); + case DateType.Second: + return string.Format("(CAST(TO_CHAR({0},'ss') AS NUMBER))", parameter.MemberName); + case DateType.Minute: + return string.Format("(CAST(TO_CHAR({0},'mi') AS NUMBER))", parameter.MemberName); + case DateType.Millisecond: + return string.Format("(CAST(TO_CHAR({0},'ff3') AS NUMBER))", parameter.MemberName); + case DateType.Quarter: + return string.Format("(CAST(TO_CHAR({0},'q') AS NUMBER))", parameter.MemberName); + case DateType.Weekday: + return $" (TO_NUMBER(TO_CHAR({parameter.MemberName}, 'D'))-1) "; + case DateType.Day: + default: + return string.Format("(CAST(TO_CHAR({0},'dd') AS NUMBER))", parameter.MemberName); + } + } + //public override string DateAddByType(MethodCallExpressionModel model) + //{ + // var parameter = model.Args[0]; + // var parameter2 = model.Args[1]; + // var parameter3 = model.Args[2]; + // var type = (DateType)Enum.Parse(typeof(DateType), parameter3.MemberValue.ObjToString(), false); + // double time = 1; + // switch (type) + // { + // case DateType.Year: + // time = 1 * 365; + // break; + // case DateType.Month: + // time = 1 * 30; + // break; + // case DateType.Day: + // break; + // case DateType.Hour: + // time = 1 / 24.0; + // break; + // case DateType.Second: + // time = 1 / 24.0 / 60.0 / 60.0; + // break; + // case DateType.Minute: + // time = 1 / 24.0 / 60.0; + // break; + // case DateType.Millisecond: + // time = 1 / 24.0 / 60.0 / 60.0 / 1000; + // break; + // } + // return string.Format("({0}+({1}*{2})) ", parameter.MemberName, time, parameter2.MemberName); + //} + + public override string DateAddDay(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + return string.Format("({0}+(1*{1})) ", parameter.MemberName, parameter2.MemberName); + } + + public override string ToString(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS VARCHAR2(4000))", parameter.MemberName); + } + + public override string ToDate(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" to_date({0},'yyyy-mm-dd hh24:mi:ss')", parameter.MemberName); + } + public override string Contains(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + return string.Format(" ({0} like '%'||{1}||'%') ", parameter.MemberName, parameter2.MemberName); + } + public override string StartsWith(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + return string.Format(" ({0} like {1}||'%') ", parameter.MemberName, parameter2.MemberName); + } + public override string EndsWith(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + return string.Format(" ({0} like '%'||{1}) ", parameter.MemberName, parameter2.MemberName); + } + public override string Trim(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" trim({0}) ", parameter.MemberName); + } + public override string DateIsSameDay(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + return string.Format(" ( cast({0} as date)= cast( {1} as date) ) ", parameter.MemberName, parameter2.MemberName); ; + } + public override string DateIsSameByType(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + var parameter3 = model.Args[2]; + + var dateType = parameter3.MemberValue.ObjToString().ToLower(); + var date1 = parameter.MemberName; + var date2 = parameter2.MemberName; + + if (dateType == "year") + { + return string.Format("(EXTRACT(YEAR FROM {0}) = EXTRACT(YEAR FROM {1}))", date1, date2); + } + else if (dateType == "month") + { + return string.Format("(EXTRACT(YEAR FROM {0}) = EXTRACT(YEAR FROM {1}) AND EXTRACT(MONTH FROM {0}) = EXTRACT(MONTH FROM {1}))", date1, date2); + } + else if (dateType == "day") + { + return string.Format("(TRUNC({0}) = TRUNC({1}))", date1, date2); + } + else if (dateType == "hour") + { + return string.Format("(TRUNC({0}, 'HH24') = TRUNC({1}, 'HH24'))", date1, date2); + } + else if (dateType == "minute") + { + return string.Format("(TRUNC({0}, 'MI') = TRUNC({1}, 'MI'))", date1, date2); + } + else if (dateType == "second") + { + return string.Format("(TRUNC({0}, 'SS') = TRUNC({1}, 'SS'))", date1, date2); + } + else if (dateType == "week" || dateType == "weekday") + { + return string.Format("(TRUNC({0}, 'IW') = TRUNC({1}, 'IW'))", date1, date2); + } + else + { + // 默认按天比较 + return string.Format("(TRUNC({0}) = TRUNC({1}))", date1, date2); + } + } + public override string Length(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" LENGTH({0}) ", parameter.MemberName); + } + + public override string IsNull(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter1 = model.Args[1]; + return string.Format("NVL({0},{1})", parameter.MemberName, parameter1.MemberName); + } + + public override string MergeString(params string[] strings) + { + return string.Join("||", strings); + } + + public override string GetDate() + { + return "sysdate"; + } + + public override string GetRandom() + { + return "dbms_random.value"; + } + + public override string CharIndex(MethodCallExpressionModel model) + { + return string.Format("instr ({0},{1},1,1) ", model.Args[0].MemberName, model.Args[1].MemberName); + } + + public override string ToDecimal(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS decimal(18,4))", parameter.MemberName); + } + + public override string TrimEnd(MethodCallExpressionModel mode) + { + var parameterNameA = mode.Args[0].MemberName; + var parameterNameB = mode.Args[1].MemberName; + return $" RTRIM({parameterNameA}, {parameterNameB}) "; + } + public override string TrimStart(MethodCallExpressionModel mode) + { + + var parameterNameA = mode.Args[0].MemberName; + var parameterNameB = mode.Args[1].MemberName; + return $" LTRIM({parameterNameA}, {parameterNameB}) "; + } + + public override string Left(MethodCallExpressionModel mode) + { + var parameterNameA = mode.Args[0].MemberName; + var parameterNameB = mode.Args[1].MemberName; + return $" SUBSTR({parameterNameA}, 1, {parameterNameB}) "; + } + public override string Right(MethodCallExpressionModel mode) + { + var parameterNameA = mode.Args[0].MemberName; + var parameterNameB = mode.Args[1].MemberName; + return $" SUBSTR({parameterNameA}, -2, {parameterNameB}) "; + } + + public override string Ceil(MethodCallExpressionModel mode) + { + var parameterNameA = mode.Args[0].MemberName; + return $" CEIL({parameterNameA}) "; + } + + public override string NewUid(MethodCallExpressionModel mode) + { + return " SUBSTR(LOWER(RAWTOHEX(SYS_GUID())), 1, 8) ||\r\n '-' ||\r\n SUBSTR(LOWER(RAWTOHEX(SYS_GUID())), 9, 4) ||\r\n '-' ||\r\n SUBSTR(LOWER(RAWTOHEX(SYS_GUID())), 13, 4) ||\r\n '-' ||\r\n SUBSTR(LOWER(RAWTOHEX(SYS_GUID())), 17, 4) ||\r\n '-' ||\r\n SUBSTR(LOWER(RAWTOHEX(SYS_GUID())), 21) "; + } + public override string JsonField(MethodCallExpressionModel model) + { + return $"JSON_VALUE({model.Args[0].MemberName}, '$.{model.Args[1].MemberValue.ToString().ToSqlFilter()}')"; + //"JSON_VALUE(j.kingorder, '$.Id') = '1'"; + } + + public override string FullTextContains(MethodCallExpressionModel mode) + { + var columns = mode.Args[0].MemberName; + if (mode.Args[0].MemberValue is List) + { + columns = "(" + string.Join(",", mode.Args[0].MemberValue as List) + ")"; + } + var searchWord = mode.Args[1].MemberName; + return $" CONTAINS({columns}, {searchWord}, 1) "; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/SqlBuilder/DmFastBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/SqlBuilder/DmFastBuilder.cs new file mode 100644 index 000000000..d7f4c54eb --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/SqlBuilder/DmFastBuilder.cs @@ -0,0 +1,98 @@ +using Dm; + +using System.Data; + +namespace SqlSugar +{ + + public class DmFastBuilder : FastBuilder, IFastBuilder + { + public override bool IsActionUpdateColumns { get; set; } = true; + public override DbFastestProperties DbFastestProperties { get; set; } = new DbFastestProperties() + { + HasOffsetTime = true, + IsConvertDateTimeOffsetToDateTime = true + }; + public async Task ExecuteBulkCopyAsync(DataTable dt) + { + dt = UtilMethods.ConvertDateTimeOffsetToDateTime(dt); + if (DbFastestProperties?.IsOffIdentity == true) + { + var isNoTran = this.Context.Ado.IsNoTran() && this.Context.CurrentConnectionConfig.IsAutoCloseConnection; + try + { + if (isNoTran) + this.Context.Ado.BeginTran(); + + this.Context.Ado.ExecuteCommand($"SET IDENTITY_INSERT {dt.TableName} ON"); + var result = await _Execute(dt).ConfigureAwait(false); + this.Context.Ado.ExecuteCommand($"SET IDENTITY_INSERT {dt.TableName} OFF"); + + if (isNoTran) + this.Context.Ado.CommitTran(); + + return result; + } + catch (Exception) + { + if (isNoTran) + this.Context.Ado.CommitTran(); + throw; + } + } + else + { + return await _Execute(dt).ConfigureAwait(false); + } + } + public override async Task CreateTempAsync(DataTable dt) + { + var queryable = this.Context.Queryable(); + var tableName = queryable.SqlBuilder.GetTranslationTableName(dt.TableName); + var sqlBuilder = this.Context.Queryable().SqlBuilder; + var dts = dt.Columns.Cast().Select(it => sqlBuilder.GetTranslationColumnName(it.ColumnName)).ToList(); + dt.TableName = "temp" + SnowFlakeSingle.instance.getID(); + var sql = queryable.AS(tableName).Where(it => false).Select(string.Join(",", dts)).ToSql().Key; + await Context.Ado.ExecuteCommandAsync($"CREATE TABLE {dt.TableName} as ( {sql} ) ").ConfigureAwait(false); + } + public override string UpdateSql { get; set; } = @"UPDATE {1} TM INNER JOIN {2} TE ON {3} SET {0} "; + + private async Task _Execute(DataTable dt) + { + DmBulkCopy bulkCopy = GetBulkCopyInstance(); + bulkCopy.DestinationTableName = dt.TableName; + try + { + bulkCopy.WriteToServer(dt); + await Task.Delay(0).ConfigureAwait(false);//No Support Async + } + catch (Exception) + { + CloseDb(); + throw; + } + CloseDb(); + return dt.Rows.Count; + } + + public DmBulkCopy GetBulkCopyInstance() + { + DmBulkCopy copy; + if (this.Context.Ado.Transaction == null) + { + copy = new DmBulkCopy((DmConnection)this.Context.Ado.Connection); + } + else + { + copy = new DmBulkCopy((DmConnection)this.Context.Ado.Connection, DmBulkCopyOptions.Default, (DmTransaction)this.Context.Ado.Transaction); + } + if (this.Context.Ado.Connection.State == ConnectionState.Closed) + { + this.Context.Ado.Connection.Open(); + } + copy.BulkCopyTimeout = this.Context.Ado.CommandTimeOut; + return copy; + } + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/SqlBuilder/DmInsertBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/SqlBuilder/DmInsertBuilder.cs new file mode 100644 index 000000000..a137216ac --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/SqlBuilder/DmInsertBuilder.cs @@ -0,0 +1,139 @@ +namespace SqlSugar +{ + public class DmInsertBuilder : InsertBuilder + { + public override string SqlTemplate + { + get + { + if (IsReturnIdentity) + { + return @"INSERT INTO {0} + ({1}) + VALUES + ({2}) ;select @@identity"; + } + else + { + return @"INSERT INTO {0} + ({1}) + VALUES + ({2}) ;"; + + } + } + } + public override string SqlTemplateBatchUnion + { + get + { + return "\t\r\nUNION ALL "; + } + } + public override string SqlTemplateBatch => "INSERT INTO {0} ({1})"; + + public override string SqlTemplateBatchSelect => " {0} "; + public override string FormatDateTimeOffset(object value) + { + var date = UtilMethods.ConvertFromDateTimeOffset((DateTimeOffset)value); + return "'" + date.ToString("yyyy-MM-dd HH:mm:ss.fff zzz") + "'"; + } + public override string ToSqlString() + { + var result = base.ToSqlString(); + if (!this.EntityInfo.Columns.Any(it => it.IsIdentity) && this.IsReturnIdentity == false) + { + result = result.Replace(";select @@identity", ""); + } + if (this.IsOffIdentity) + { + var tableName = this.GetTableNameString; + result = $"SET IDENTITY_INSERT {tableName} ON;" + result.TrimEnd(';') + $";SET IDENTITY_INSERT {tableName} OFF"; + result = result.Replace(";\r\n;", ";"); + } + return result; + } + + public override object FormatValue(object value) + { + if (value != null && value is DateTime) + { + var date = value.ObjToDate(); + if (date < UtilMethods.GetMinDate(this.Context.CurrentConnectionConfig)) + { + date = UtilMethods.GetMinDate(this.Context.CurrentConnectionConfig); + } + if (this.Context.CurrentConnectionConfig?.MoreSettings?.DisableMillisecond == true) + { + return "to_date('" + date.ToString("yyyy-MM-dd HH:mm:ss") + "', 'YYYY-MM-DD HH24:MI:SS') "; + } + else + { + return "to_timestamp('" + date.ToString("yyyy-MM-dd HH:mm:ss.ffffff") + "', 'YYYY-MM-DD HH24:MI:SS.FF') "; + } + } + else + { + return base.FormatValue(value); + } + } + + //public override string ToSqlString() + //{ + // if (IsNoInsertNull) + // { + // DbColumnInfoList = DbColumnInfoList.Where(it => it.Value != null).ToList(); + // } + // var groupList = DbColumnInfoList.GroupBy(it => it.TableId).ToList(); + // var isSingle = groupList.Count() == 1; + // string columnsString = string.Join(",", groupList.First().Select(it => Builder.GetTranslationColumnName(it.DbColumnName))); + // if (isSingle) + // { + // string columnParametersString = string.Join(",", this.DbColumnInfoList.Select(it => Builder.SqlParameterKeyWord + it.DbColumnName)); + // return string.Format(SqlTemplate, GetTableNameString, columnsString, columnParametersString); + // } + // else + // { + // StringBuilder batchInsetrSql = new StringBuilder(); + // int pageSize = 200; + // int pageIndex = 1; + // int totalRecord = groupList.Count; + // int pageCount = (totalRecord + pageSize - 1) / pageSize; + // while (pageCount >= pageIndex) + // { + // batchInsetrSql.AppendFormat(SqlTemplateBatch, GetTableNameString, columnsString); + // int i = 0; + // foreach (var columns in groupList.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList()) + // { + // var isFirst = i == 0; + // if (isFirst) + // { + // batchInsetrSql.Append(SqlTemplateBatchUnion); + // } + // batchInsetrSql.Append("\r\n ( " + string.Join(",", columns.Select(it => + // { + // object value = null; + // if (it.Value is DateTime) + // { + // value = ((DateTime)it.Value).ToString("O"); + // } + // else + // { + // value = it.Value; + // } + // if (value == null||value==DBNull.Value) + // { + // return string.Format(SqlTemplateBatchSelect, "NULL"); + // } + // return string.Format(SqlTemplateBatchSelect, "'" + value.ObjToString().ToSqlFilter() + "'"); + // })) + "),"); + // ++i; + // } + // pageIndex++; + // batchInsetrSql.Remove(batchInsetrSql.Length - 1,1).Append("\r\n;\r\n"); + // } + // return batchInsetrSql.ToString(); + // } + //} + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/SqlBuilder/DmQueryBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/SqlBuilder/DmQueryBuilder.cs new file mode 100644 index 000000000..f3af297a9 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/SqlBuilder/DmQueryBuilder.cs @@ -0,0 +1,193 @@ +using System.Text; +using System.Text.RegularExpressions; + +namespace SqlSugar +{ + public partial class DmQueryBuilder : QueryBuilder + { + public override bool IsComplexModel(string sql) + { + return Regex.IsMatch(sql, @"AS ""\w+\.\w+""") || Regex.IsMatch(sql, @"AS ""\w+\.\w+\.\w+"""); + } + public override string SqlTemplate + { + get + { + return "SELECT {0}{" + UtilConstants.ReplaceKey + "} FROM {1}{2}{3}{4}"; + } + } + public override string ToSqlString() + { + + if (PartitionByValue.HasValue()) + { + return base.ToSqlString(); + } + //Support MySql Model + if (this.Context.CurrentConnectionConfig.MoreSettings?.DatabaseModel == DbType.MySql) + { + return MySqlToSqlString(); + } + var isDistinctPage = IsDistinct && (Take > 1 || Skip > 1); + if (isDistinctPage) + { + return OffsetPage(); + } + + string oldOrderBy = this.OrderByValue; + string externalOrderBy = oldOrderBy; + var isIgnoreOrderBy = this.IsCount && this.PartitionByValue.IsNullOrEmpty(); + AppendFilter(); + sql = new StringBuilder(); + if (this.OrderByValue == null && (Skip != null || Take != null)) this.OrderByValue = " ORDER BY " + this.Builder.SqlDateNow + " "; + if (this.PartitionByValue.HasValue()) + { + this.OrderByValue = this.PartitionByValue + this.OrderByValue; + } + var isRowNumber = Skip != null || Take != null; + var rowNumberString = string.Format(",ROW_NUMBER() OVER({0}) AS RowIndex ", GetOrderByString); + string groupByValue = GetGroupByString + HavingInfos; + string orderByValue = (!isRowNumber && this.OrderByValue.HasValue()) ? GetOrderByString : null; + if (isIgnoreOrderBy) { orderByValue = null; } + sql.AppendFormat(SqlTemplate, GetSelectValue, GetTableNameString, GetWhereValueString, groupByValue, orderByValue); + sql.Replace(UtilConstants.ReplaceKey, isRowNumber ? (isIgnoreOrderBy ? null : rowNumberString) : null); + if (isIgnoreOrderBy) { this.OrderByValue = oldOrderBy; return sql.ToString(); } + var result = ToPageSql(sql.ToString(), this.Take, this.Skip); + if (ExternalPageIndex > 0) + { + if (externalOrderBy.IsNullOrEmpty()) + { + externalOrderBy = " ORDER BY " + this.Builder.SqlDateNow + " "; + } + result = string.Format("SELECT *,ROW_NUMBER() OVER({0}) AS RowIndex2 FROM ({1}) ExternalTable ", GetExternalOrderBy(externalOrderBy), result); + result = ToPageSql2(result, ExternalPageIndex, ExternalPageSize, true); + } + this.OrderByValue = oldOrderBy; + result = GetSqlQuerySql(result); + if (result.Contains("-- No table")) + { + return "select * from (select 1 as id) where id=0 -- No table"; + } + return result; + } + + public string MySqlToSqlString() + { + var PageTempalte = "SELECT {0} FROM {1} {2} {3} {4} LIMIT {5},{6}"; + var SqlTemplate = MySqlTemplate; + base.AppendFilter(); + string result = null; + string oldOrderBy = this.OrderByValue; + sql = new StringBuilder(); + sql.AppendFormat(SqlTemplate, GetMySelectValue, GetTableNameString, GetWhereValueString, GetGroupByString + HavingInfos, (Skip != null || Take != null) ? null : GetOrderByString); + if (IsCount) { return sql.ToString(); } + if (Skip != null && Take == null) + { + if (this.OrderByValue == "ORDER BY ") this.OrderByValue += GetMySelectValue.Split(',')[0]; + result = string.Format(PageTempalte, GetMySelectValue, GetTableNameString, GetWhereValueString, GetGroupByString + HavingInfos, (Skip != null || Take != null) ? null : GetOrderByString, Skip.ObjToInt(), long.MaxValue); + } + else if (Skip == null && Take != null) + { + if (this.OrderByValue == "ORDER BY ") this.OrderByValue += GetMySelectValue.Split(',')[0]; + result = string.Format(PageTempalte, GetMySelectValue, GetTableNameString, GetWhereValueString, GetGroupByString + HavingInfos, GetOrderByString, 0, Take.ObjToInt()); + } + else if (Skip != null && Take != null) + { + if (this.OrderByValue == "ORDER BY ") this.OrderByValue += GetMySelectValue.Split(',')[0]; + result = string.Format(PageTempalte, GetMySelectValue, GetTableNameString, GetWhereValueString, GetGroupByString + HavingInfos, GetOrderByString, Skip.ObjToInt() > 0 ? Skip.ObjToInt() : 0, Take); + } + else + { + result = sql.ToString(); + } + this.OrderByValue = oldOrderBy; + result = GetSqlQuerySql(result); + result = result.Replace(UtilConstants.ReplaceCommaKey, ""); + if (result.IndexOf("-- No table") > 0) + { + return "-- No table"; + } + return result; + } + public string GetMySelectValue + { + get + { + string reval = string.Empty; + if (this.SelectValue == null || this.SelectValue is string) + { + reval = GetSelectValueByString(); + } + else + { + reval = GetSelectValueByExpression(); + } + if (this.SelectType == ResolveExpressType.SelectMultiple) + { + this.SelectCacheKey = this.SelectCacheKey + string.Join("-", this.JoinQueryInfos.Select(it => it.TableName)); + } + if (IsDistinct) + { + reval = " DISTINCT " + reval; + } + if (this.SubToListParameters != null && this.SubToListParameters.Count != 0) + { + reval = SubToListMethod(reval); + } + return reval; + } + } + public string MySqlTemplate + { + get + { + if (this.SampleBy.HasValue()) + { + return "SELECT {0} FROM {1}{2} " + this.SampleBy + " {3}{4}"; + } + return "SELECT {0} FROM {1}{2}{3}{4} "; + } + } + public override string GetExternalOrderBy(string externalOrderBy) + { + return Regex.Replace(externalOrderBy, @"""\w+""\.", ""); + } + + private string OffsetPage() + { + var skip = this.Skip ?? 1; + var take = this.Take; + this.Skip = null; + this.Take = null; + this.Offset = null; + var pageSql = $"SELECT * FROM ( SELECT PAGETABLE1.*,ROWNUM PAGEINDEX FROM( {this.ToSqlString()}) PAGETABLE1 WHERE ROWNUM<={skip + take}) WHERE PAGEINDEX>={(skip == 0 ? skip : (skip + 1))}"; + return pageSql; + } + public override string ToPageSql(string sql, int? take, int? skip, bool isExternal = false) + { + string temp = isExternal ? ExternalPageTempalte : PageTempalte; + if (skip != null && take == null) + { + return string.Format(temp, sql.ToString(), skip.ObjToInt() + 1, long.MaxValue); + } + else if (skip == null && take != null) + { + return string.Format(temp, sql.ToString(), 1, take.ObjToInt()); + } + else if (skip != null && take != null) + { + return string.Format(temp, sql.ToString(), skip.ObjToInt() + 1, skip.ObjToInt() + take.ObjToInt()); + } + else + { + return sql.ToString(); + } + } + + public override string ToPageSql2(string sql, int? pageIndex, int? pageSize, bool isExternal = false) + { + string temp = isExternal ? ExternalPageTempalte : PageTempalte; + return string.Format(temp, sql.ToString(), (pageIndex - 1) * pageSize + 1, pageIndex * pageSize); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/SqlBuilder/DmUpdateBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/SqlBuilder/DmUpdateBuilder.cs new file mode 100644 index 000000000..b93447307 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Dm/SqlBuilder/DmUpdateBuilder.cs @@ -0,0 +1,135 @@ +using System.Text; + +namespace SqlSugar +{ + public class DmUpdateBuilder : UpdateBuilder + { + public override string ReSetValueBySqlExpListType { get; set; } = "dm"; + protected override string GetJoinUpdate(string columnsString, ref string whereString) + { + var joinString = $" {Builder.GetTranslationColumnName(this.TableName)} {Builder.GetTranslationColumnName(this.ShortName)} "; + foreach (var item in this.JoinInfos) + { + joinString += $"\r\n USING {Builder.GetTranslationColumnName(item.TableName)} {Builder.GetTranslationColumnName(item.ShortName)} ON {item.JoinWhere} "; + } + var tableName = joinString + "\r\n "; + var newTemp = SqlTemplate.Replace("UPDATE", "MERGE INTO").Replace("SET", "WHEN MATCHED THEN \r\nUPDATE SET"); + return string.Format(newTemp, tableName, columnsString, whereString); + } + protected override string TomultipleSqlString(List> groupList) + { + StringBuilder sb = new StringBuilder(); + int i = 0; + sb.AppendLine(string.Join("\r\n", groupList.Select(t => + { + var updateTable = string.Format("UPDATE {0} SET", base.GetTableNameStringNoWith); + var setValues = string.Join(",", t.Where(s => !s.IsPrimarykey).Where(s => OldPrimaryKeys?.Contains(s.DbColumnName) != true).Select(m => GetOracleUpdateColums(i, m)).ToArray()); + var pkList = t.Where(s => s.IsPrimarykey).ToList(); + if (this.IsWhereColumns && this.PrimaryKeys?.Count > 0) + { + var whereColumns = pkList.Where(it => this.PrimaryKeys?.Any(p => p.EqualCase(it.PropertyName) || p.EqualCase(it.DbColumnName)) == true).ToList(); + if (whereColumns.Count != 0) + { + pkList = whereColumns; + } + } + List whereList = new List(); + foreach (var item in pkList) + { + var isFirst = pkList.First() == item; + var whereString = ""; + whereString += GetOracleUpdateColums(i, item); + whereList.Add(whereString); + } + i++; + return string.Format("{0} {1} WHERE {2};", updateTable, setValues, string.Join("AND", whereList)); + }).ToArray())); + var result = sb.ToString(); + if (groupList.Count == 0) + { + return null; + } + return result; + } + + private string GetOracleUpdateColums(int i, DbColumnInfo m) + { + return string.Format("\"{0}\"={1} ", m.DbColumnName.ToUpper(IsUppper), base.GetDbColumn(m, FormatValue(i, m.DbColumnName, m.Value))); + } + public bool IsUppper + { + get + { + if (this.Context.CurrentConnectionConfig.MoreSettings == null) + { + return true; + } + else + { + return this.Context.CurrentConnectionConfig.MoreSettings.IsAutoToUpper == true; + } + } + } + public override string FormatDateTimeOffset(object value) + { + var date = UtilMethods.ConvertFromDateTimeOffset((DateTimeOffset)value); + return "'" + date.ToString("yyyy-MM-dd HH:mm:ss.fff zzz") + "'"; + } + public object FormatValue(int i, string name, object value) + { + if (value == null) + { + return "NULL"; + } + else + { + var type = UtilMethods.GetUnderType(value.GetType()); + if (type == UtilConstants.DateType) + { + var date = value.ObjToDate(); + if (date < UtilMethods.GetMinDate(this.Context.CurrentConnectionConfig)) + { + date = UtilMethods.GetMinDate(this.Context.CurrentConnectionConfig); + } + if (this.Context.CurrentConnectionConfig?.MoreSettings?.DisableMillisecond == true) + { + return "to_date('" + date.ToString("yyyy-MM-dd HH:mm:ss") + "', 'YYYY-MM-DD HH24:MI:SS') "; + } + else + { + return "to_timestamp('" + date.ToString("yyyy-MM-dd HH:mm:ss.ffffff") + "', 'YYYY-MM-DD HH24:MI:SS.FF') "; + } + } + else if (type.IsEnum()) + { + if (this.Context.CurrentConnectionConfig.MoreSettings?.TableEnumIsString == true) + { + return value.ToSqlValue(); + } + else + { + return Convert.ToInt64(value); + } + } + else if (type == UtilConstants.ByteArrayType) + { + var parameterName = this.Builder.SqlParameterKeyWord + name + i; + this.Parameters.Add(new SugarParameter(parameterName, value)); + return parameterName; + } + else if (type == UtilConstants.BoolType) + { + return value.ObjToBool() ? "1" : "0"; + } + else if (type == UtilConstants.StringType || type == UtilConstants.ObjType) + { + return "'" + value.ToString().ToSqlFilter() + "'"; + } + else + { + return "'" + value.ToString() + "'"; + } + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Kdbndp/CodeFirst/KdbndpCodeFirst.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Kdbndp/CodeFirst/KdbndpCodeFirst.cs new file mode 100644 index 000000000..1c368907b --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Kdbndp/CodeFirst/KdbndpCodeFirst.cs @@ -0,0 +1,91 @@ +namespace SqlSugar +{ + public class KdbndpCodeFirst : CodeFirstProvider + { + public override void NoExistLogic(EntityInfo entityInfo) + { + var tableName = GetTableName(entityInfo); + //Check.Exception(entityInfo.Columns.Where(it => it.IsPrimarykey).Count() > 1, "Use Code First ,The primary key must not exceed 1"); + List columns = new List(); + if (entityInfo.Columns.HasValue()) + { + foreach (var item in entityInfo.Columns.Where(it => it.IsIgnore == false)) + { + DbColumnInfo dbColumnInfo = this.EntityColumnToDbColumn(entityInfo, tableName, item); + columns.Add(dbColumnInfo); + } + if (entityInfo.IsCreateTableFiledSort) + { + columns = columns.OrderBy(c => c.CreateTableFieldSort).ToList(); + } + } + columns = columns.OrderBy(it => it.IsPrimarykey ? 0 : 1).ToList(); + this.Context.DbMaintenance.CreateTable(tableName, columns, true); + } + protected override DbColumnInfo EntityColumnToDbColumn(EntityInfo entityInfo, string tableName, EntityColumnInfo item) + { + var propertyType = UtilMethods.GetUnderType(item.PropertyInfo); + var result = new DbColumnInfo() + { + TableId = entityInfo.Columns.IndexOf(item), + DbColumnName = item.DbColumnName.HasValue() ? item.DbColumnName : item.PropertyName, + IsPrimarykey = item.IsPrimarykey, + IsIdentity = item.IsIdentity, + TableName = tableName, + IsNullable = item.IsNullable, + DefaultValue = item.DefaultValue, + ColumnDescription = item.ColumnDescription, + Length = item.Length, + CreateTableFieldSort = item.CreateTableFieldSort, + DecimalDigits = item.DecimalDigits, + Scale = item.DecimalDigits + }; + GetDbType(item, propertyType, result); + if (result.DataType.Equals("varchar", StringComparison.CurrentCultureIgnoreCase) && result.Length == 0) + { + result.Length = 1; + } + if (result.DataType.EqualCase("timestamp") && this.Context.CurrentConnectionConfig?.MoreSettings?.DatabaseModel == DbType.SqlServer) + { + result.DataType = "datetime"; + } + if (IsSqlServerModel()) + { + if (result.DataType.EqualCase("int4")) + { + result.DataType = "int"; + } + if (result.DataType.EqualCase("int8")) + { + result.DataType = "bigint"; + } + } + return result; + } + + protected override void ConvertColumns(List dbColumns) + { + foreach (var item in dbColumns) + { + if (item.DataType == "DateTime") + { + item.Length = 0; + } + } + } + + protected override void ChangeKey(EntityInfo entityInfo, string tableName, EntityColumnInfo item) + { + this.Context.DbMaintenance.UpdateColumn(tableName, EntityColumnToDbColumn(entityInfo, tableName, item)); + if (!item.IsPrimarykey) + this.Context.DbMaintenance.DropConstraint(tableName, null); + if (item.IsPrimarykey) + this.Context.DbMaintenance.AddPrimaryKey(tableName, item.DbColumnName); + } + + private bool IsSqlServerModel() + { + return this.Context.CurrentConnectionConfig?.MoreSettings?.DatabaseModel == DbType.SqlServer; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Kdbndp/DbBind/KdbndpBind.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Kdbndp/DbBind/KdbndpBind.cs new file mode 100644 index 000000000..fb7d46f9e --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Kdbndp/DbBind/KdbndpBind.cs @@ -0,0 +1,162 @@ +namespace SqlSugar +{ + public class KdbndpDbBind : DbBindProvider + { + public override string GetDbTypeName(string csharpTypeName) + { + if (csharpTypeName == UtilConstants.ByteArrayType.Name) + return "bytea"; + + var result = base.GetDbTypeName(csharpTypeName); + return result; + } + public override string GetPropertyTypeName(string dbTypeName) + { + dbTypeName = dbTypeName.Replace("pg_catalog.", ""); + dbTypeName = dbTypeName.Replace("sys.", ""); + dbTypeName = dbTypeName.ToLower(); + var propertyTypes = MappingTypes.Where(it => it.Value.ToString().Equals(dbTypeName, StringComparison.CurrentCultureIgnoreCase) || it.Key.Equals(dbTypeName, StringComparison.CurrentCultureIgnoreCase)); + if (propertyTypes == null) + { + return "other"; + } + else if (dbTypeName == "xml" || dbTypeName == "string" || dbTypeName == "jsonb" || dbTypeName == "json") + { + return "string"; + } + else if (dbTypeName == "bpchar")//数据库char datatype 查询出来的时候是 bpchar + { + return "char"; + } + if (dbTypeName == "byte[]") + { + return "byte[]"; + } + else if (propertyTypes?.Any() != true) + { + Check.ThrowNotSupportedException(string.Format(" \"{0}\" Type NotSupported, DbBindProvider.GetPropertyTypeName error.", dbTypeName)); + return null; + } + else if (propertyTypes.First().Value == CSharpDataType.byteArray) + { + return "byte[]"; + } + else + { + return propertyTypes.First().Value.ToString(); + } + } + public override List> MappingTypes + { + get + { + var extService = this.Context.CurrentConnectionConfig.ConfigureExternalServices; + if (extService?.AppendDataReaderTypeMappings.HasValue() == true) + { + return extService.AppendDataReaderTypeMappings.Union(MappingTypesConst).ToList(); + } + else + { + return MappingTypesConst; + } + } + } + public static List> MappingTypesConst = new List>(){ + + new KeyValuePair("int2",CSharpDataType.@short), + new KeyValuePair("uint2",CSharpDataType.@short), + //new KeyValuePair("int1",CSharpDataType.@byte), + new KeyValuePair("smallint",CSharpDataType.@short), + new KeyValuePair("smallint",CSharpDataType.@byte), + new KeyValuePair("tinyint",CSharpDataType.@byte), + new KeyValuePair("int4",CSharpDataType.@int), + new KeyValuePair("uint4",CSharpDataType.@int), + new KeyValuePair("integer",CSharpDataType.@int), + new KeyValuePair("tinyint",CSharpDataType.@int), + new KeyValuePair("int8",CSharpDataType.@long), + new KeyValuePair("uint8",CSharpDataType.@long), + new KeyValuePair("bigint",CSharpDataType.@long), + new KeyValuePair("float4",CSharpDataType.@float), + new KeyValuePair("float4",CSharpDataType.Single), + new KeyValuePair("real",CSharpDataType.@float), + new KeyValuePair("float8",CSharpDataType.@double), + new KeyValuePair("double precision",CSharpDataType.@int), + new KeyValuePair("numeric",CSharpDataType.@decimal), + new KeyValuePair("decimal",CSharpDataType.@decimal), + new KeyValuePair("path",CSharpDataType.@decimal), + new KeyValuePair("point",CSharpDataType.@decimal), + new KeyValuePair("polygon",CSharpDataType.@decimal), + + new KeyValuePair("boolean",CSharpDataType.@bool), + new KeyValuePair("bool",CSharpDataType.@bool), + new KeyValuePair("box",CSharpDataType.@bool), + new KeyValuePair("bytea",CSharpDataType.@bool), + + new KeyValuePair("varchar",CSharpDataType.@string), + new KeyValuePair("character varying",CSharpDataType.@string), + new KeyValuePair("name",CSharpDataType.@string), + new KeyValuePair("text",CSharpDataType.@string), + new KeyValuePair("char",CSharpDataType.@string), + new KeyValuePair("nchar",CSharpDataType.@string), + new KeyValuePair("character",CSharpDataType.@string), + new KeyValuePair("cidr",CSharpDataType.@string), + new KeyValuePair("circle",CSharpDataType.@string), + new KeyValuePair("tsquery",CSharpDataType.@string), + new KeyValuePair("tsvector",CSharpDataType.@string), + new KeyValuePair("txid_snapshot",CSharpDataType.@string), + new KeyValuePair("varcharbyte",CSharpDataType.@string), + new KeyValuePair("varcharbyte varying",CSharpDataType.@string), + new KeyValuePair("bpcharbyte",CSharpDataType.@string), + new KeyValuePair("nvarchar",CSharpDataType.@string), + new KeyValuePair("characterbyte",CSharpDataType.@string), + new KeyValuePair("information_schema.character_data",CSharpDataType.@string), + new KeyValuePair("uuid",CSharpDataType.Guid), + new KeyValuePair("uniqueidentifier",CSharpDataType.Guid), + new KeyValuePair("xml",CSharpDataType.@string), + new KeyValuePair("json",CSharpDataType.@string), + new KeyValuePair("rowid",CSharpDataType.@string), + new KeyValuePair("information_schema.sql_identifier",CSharpDataType.@string), + new KeyValuePair("information_schema.cardinal_number",CSharpDataType.@string), + new KeyValuePair("interval",CSharpDataType.@decimal), + new KeyValuePair("lseg",CSharpDataType.@decimal), + new KeyValuePair("macaddr",CSharpDataType.@decimal), + new KeyValuePair("money",CSharpDataType.@decimal), + new KeyValuePair("timestamp",CSharpDataType.DateTime), + new KeyValuePair("datetime2",CSharpDataType.DateTime), + new KeyValuePair("datetime",CSharpDataType.DateTime), + new KeyValuePair("timestamp with time zone",CSharpDataType.DateTime), + new KeyValuePair("timestamptz",CSharpDataType.DateTime), + new KeyValuePair("timestamp without time zone",CSharpDataType.DateTime), + new KeyValuePair("date",CSharpDataType.DateTime), + new KeyValuePair("time",CSharpDataType.DateTime), + new KeyValuePair("time with time zone",CSharpDataType.DateTime), + new KeyValuePair("timetz",CSharpDataType.DateTime), + new KeyValuePair("time without time zone",CSharpDataType.DateTime), + + new KeyValuePair("bit",CSharpDataType.byteArray), + new KeyValuePair("blob",CSharpDataType.byteArray), + new KeyValuePair("bit varying",CSharpDataType.byteArray), + new KeyValuePair("binary",CSharpDataType.byteArray), + new KeyValuePair("varbinary",CSharpDataType.byteArray), + new KeyValuePair("image",CSharpDataType.byteArray), + new KeyValuePair("varbit",CSharpDataType.@byte), + new KeyValuePair("rowversion",CSharpDataType.byteArray), + new KeyValuePair("regclass",CSharpDataType.@object), + + new KeyValuePair("geometry",CSharpDataType.@object), + new KeyValuePair("public.geometry",CSharpDataType.@object), + new KeyValuePair("geography",CSharpDataType.@object), + new KeyValuePair("public.geography",CSharpDataType.@object), + + new KeyValuePair("dsinterval",CSharpDataType.TimeSpan), + new KeyValuePair("yminterval",CSharpDataType.@int), + }; + public override List StringThrow + { + get + { + return new List() { "int32", "datetime", "decimal", "double", "byte" }; + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Kdbndp/DbFirst/KdbndpDbFirst.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Kdbndp/DbFirst/KdbndpDbFirst.cs new file mode 100644 index 000000000..36cd4351b --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Kdbndp/DbFirst/KdbndpDbFirst.cs @@ -0,0 +1,6 @@ +namespace SqlSugar +{ + public class KdbndpDbFirst : DbFirstProvider + { + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Kdbndp/DbMaintenance/KdbndpDbMaintenance.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Kdbndp/DbMaintenance/KdbndpDbMaintenance.cs new file mode 100644 index 000000000..bc3d8f8c0 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Kdbndp/DbMaintenance/KdbndpDbMaintenance.cs @@ -0,0 +1,712 @@ +namespace SqlSugar +{ + public class KdbndpDbMaintenance : DbMaintenanceProvider + { + #region DML + protected override string GetDataBaseSql + { + get + { + if (IsPgModel()) + { + return "SELECT datname FROM pg_database"; + } + return "SELECT datname FROM sys_database"; + } + } + protected override string GetColumnInfosByTableNameSql + { + get + { + string sql = $@"select cast (pclass.oid as int4) as TableId,cast(ptables.tablename as varchar) as TableName, + pcolumn.column_name as DbColumnName,pcolumn.udt_name as DataType, + pcolumn.character_maximum_length as Length, + pcolumn.column_default as DefaultValue, + col_description(pclass.oid, pcolumn.ordinal_position) as ColumnDescription, + case when pkey.colname = pcolumn.column_name + then true else false end as IsPrimaryKey, + case when UPPER(pcolumn.column_default) like 'NEXTVAL%' + then true else false end as IsIdentity, + case when UPPER(pcolumn.is_nullable) = 'YES' + then true else false end as IsNullable + from (select * from sys_tables where UPPER(tablename) = UPPER('{{0}}') and lower(schemaname)='{GetSchema()}') ptables inner join sys_class pclass + on ptables.tablename = pclass.relname inner join (SELECT * + FROM information_schema.columns where UPPER(table_schema)=UPPER('{GetSchema()}') + ) pcolumn on pcolumn.table_name = ptables.tablename + left join ( + select sys_class.relname,sys_attribute.attname as colname from + sys_constraint inner join sys_class + on sys_constraint.conrelid = sys_class.oid + inner join sys_attribute on sys_attribute.attrelid = sys_class.oid + and sys_attribute.attnum = sys_constraint.conkey[1] + inner join sys_type on sys_type.oid = sys_attribute.atttypid + where sys_constraint.contype='p' + ) pkey on pcolumn.table_name = pkey.relname + order by ptables.tablename"; + + + if (IsPgModel()) + { + sql = sql.Replace("sys_", "pg_"); + } + else if (IsSqlServerModel()) + { + + sql = sql.Replace("sys_", "pg_"); + sql = sql.Replace("pg_constraint.conkey[1]", "pg_constraint.conkey{{1}}"); + sql = sql.Replace("UPPER(", "pg_catalog.upper("); + sql = sql.Replace("lower(", "pg_catalog.lower("); + sql = sql.Replace("NEXTVAL%", "%nextval%"); + sql = sql.Replace("pcolumn.udt_name", "pcolumn.data_type"); + sql = sql.Replace("case when pkey.colname = pcolumn.column_name", "case when pkey.colname::text = pcolumn.column_name::text"); + sql = sql.Replace("pcolumn on pcolumn.table_name = ptables.tablename", "pcolumn on pcolumn.table_name::text = ptables.tablename::text "); + sql = sql.Replace("pkey on pcolumn.table_name = pkey.relname", "pkey on pcolumn.table_name::text = pkey.relname::text "); + } + else if (IsMySql()) + { + sql = sql.Replace("pcolumn.udt_name", "pcolumn.data_type"); + } + return sql; + } + } + protected override string GetTableInfoListSql + { + get + { + if (IsPgModel()) + { + return @"select cast(relname as varchar) as Name, + cast(obj_description(relfilenode,'pg_class') as varchar) as Description from pg_class c + where relkind = 'r' and c.oid >= 16384 and c.relnamespace != 99 and c.relname not like '%pl_profiler_saved%' order by relname"; + } + var result = @"select cast(relname as varchar) as Name, + cast(obj_description(relfilenode,'pg_class') as varchar) as Description from sys_class c + where relkind = 'r' and c.oid >= 16384 and c.relnamespace != 99 and c.relname not like '%pl_profiler_saved%' order by relname"; + + if (IsSqlServerModel()) + { + result = result.Replace(" as varchar)", " as varchar(max))"); + } + return result; + } + } + protected override string GetViewInfoListSql + { + get + { + return @"select table_name as name from information_schema.views where lower(table_schema) ='" + GetSchema() + "' "; + } + } + #endregion + + #region DDL + protected override string CreateDataBaseSql + { + get + { + return "CREATE DATABASE {0}"; + } + } + protected override string AddPrimaryKeySql + { + get + { + return "ALTER TABLE {0} ADD PRIMARY KEY({2}) /*{1}*/"; + } + } + protected override string AddColumnToTableSql + { + get + { + if (IsSqlServerModel()) + { + return "ALTER TABLE {0} ADD {1} {2}{3} {4} {5} {6}"; + } + return "ALTER TABLE {0} ADD COLUMN {1} {2}{3} {4} {5} {6}"; + } + } + protected override string AlterColumnToTableSql + { + get + { + return "alter table {0} ALTER COLUMN {1} {2}{3} {4} {5} {6}"; + } + } + protected override string BackupDataBaseSql + { + get + { + return "mysqldump.exe {0} -uroot -p > {1} "; + } + } + protected override string CreateTableSql + { + get + { + return "CREATE TABLE {0}(\r\n{1} $PrimaryKey)"; + } + } + protected override string CreateTableColumn + { + get + { + return "{0} {1}{2} {3} {4} {5}"; + } + } + protected override string TruncateTableSql + { + get + { + return "TRUNCATE TABLE {0}"; + } + } + protected override string BackupTableSql + { + get + { + return "create table {0} as (select * from {1} limit {2} offset 0)"; + } + } + protected override string DropTableSql + { + get + { + return "DROP TABLE {0}"; + } + } + protected override string DropColumnToTableSql + { + get + { + return "ALTER TABLE {0} DROP COLUMN {1}"; + } + } + protected override string DropConstraintSql + { + get + { + return "ALTER TABLE {0} DROP CONSTRAINT {1}"; + } + } + protected override string RenameColumnSql + { + get + { + return "ALTER TABLE {0} RENAME {1} TO {2}"; + } + } + protected override string AddColumnRemarkSql => "comment on column {1}.{0} is '{2}'"; + + protected override string DeleteColumnRemarkSql => "comment on column {1}.{0} is ''"; + + protected override string IsAnyColumnRemarkSql { get { throw new NotSupportedException(); } } + + protected override string AddTableRemarkSql => "comment on table {0} is '{1}'"; + + protected override string DeleteTableRemarkSql => "comment on table {0} is ''"; + + protected override string IsAnyTableRemarkSql { get { throw new NotSupportedException(); } } + + protected override string RenameTableSql => "alter table {0} rename to {1}"; + + protected override string CreateIndexSql + { + get + { + return "CREATE {3} INDEX Index_{0}_{2} ON {0} ({1})"; + } + } + protected override string AddDefaultValueSql + { + get + { + if (IsSqlServerModel()) + { + return "ALTER TABLE {0} ALTER {1} SET DEFAULT {2}"; + } + return "ALTER TABLE {0} ALTER COLUMN {1} SET DEFAULT {2}"; + } + } + protected override string IsAnyIndexSql + { + get + { + var sql = "SELECT count(1) WHERE upper('{0}') IN ( SELECT upper(indexname) FROM sys_indexes ) "; + if (IsPgModel()) + { + sql = sql.Replace("sys_", "pg_"); + } + return sql; + } + } + protected override string IsAnyProcedureSql => throw new NotImplementedException(); + #endregion + + #region Check + protected override string CheckSystemTablePermissionsSql + { + get + { + return "select 1 from information_schema.columns limit 1 offset 0"; + } + } + #endregion + + #region Scattered + protected override string CreateTableNull + { + get + { + return "DEFAULT NULL"; + } + } + protected override string CreateTableNotNull + { + get + { + return "NOT NULL"; + } + } + protected override string CreateTablePirmaryKey + { + get + { + return "PRIMARY KEY"; + } + } + protected override string CreateTableIdentity + { + get + { + return "serial"; + } + } + #endregion + + #region Methods + public override List GetDbTypes() + { + var result = this.Context.Ado.SqlQuery(@"SELECT DISTINCT data_type +FROM information_schema.columns"); + result.Add("varchar"); + result.Add("timestamp"); + result.Add("uuid"); + result.Add("int2"); + result.Add("int4"); + result.Add("int8"); + result.Add("time"); + result.Add("date"); + result.Add("float8"); + result.Add("float4"); + result.Add("json"); + result.Add("jsonp"); + return result.Distinct().ToList(); + } + public override List GetTriggerNames(string tableName) + { + return this.Context.Ado.SqlQuery(@"SELECT tgname +FROM pg_trigger +WHERE tgrelid = '" + tableName + "'::regclass"); + } + public override List GetFuncList() + { + return this.Context.Ado.SqlQuery(" SELECT routine_name\r\nFROM information_schema.routines\r\nWHERE lower( routine_schema ) = '" + GetSchema().ToLower() + "' AND routine_type = 'FUNCTION' "); + } + public override bool AddDefaultValue(string tableName, string columnName, string defaultValue) + { + if (defaultValue?.StartsWith('\'') == true && defaultValue?.EndsWith('\'') == true && defaultValue?.Contains('(') == false + && !defaultValue.EqualCase("'current_timestamp'") && !defaultValue.EqualCase("'current_date'")) + { + string sql = string.Format(AddDefaultValueSql, this.SqlBuilder.GetTranslationColumnName(tableName), this.SqlBuilder.GetTranslationColumnName(columnName), defaultValue); + return this.Context.Ado.ExecuteCommand(sql) > 0; + } + else if (defaultValue.EqualCase("current_timestamp") || defaultValue.EqualCase("current_date")) + { + string sql = string.Format(AddDefaultValueSql, this.SqlBuilder.GetTranslationColumnName(tableName), this.SqlBuilder.GetTranslationColumnName(columnName), defaultValue); + return this.Context.Ado.ExecuteCommand(sql) > 0; + } + else if (defaultValue?.Contains('(') == false + && !defaultValue.EqualCase("'current_timestamp'") && !defaultValue.EqualCase("'current_date'")) + { + string sql = string.Format(AddDefaultValueSql, this.SqlBuilder.GetTranslationColumnName(tableName), this.SqlBuilder.GetTranslationColumnName(columnName), "'" + defaultValue + "'"); + return this.Context.Ado.ExecuteCommand(sql) > 0; + } + else + { + return base.AddDefaultValue(this.SqlBuilder.GetTranslationTableName(tableName), this.SqlBuilder.GetTranslationTableName(columnName), defaultValue); + } + } + public override List GetIndexList(string tableName) + { + var sql = $"SELECT indexname FROM sys_indexes WHERE UPPER(tablename) = UPPER('{tableName}') AND UPPER(schemaname) = UPPER('" + GetSchema() + "') "; + if (IsPgModel()) + { + sql = sql.Replace("sys_", "pg_"); + } + return this.Context.Ado.SqlQuery(sql); + } + public override List GetProcList(string dbName) + { + var sql = $"SELECT proname FROM sys_proc p JOIN pg_namespace n ON p.pronamespace = n.oid WHERE UPPER(n.nspname) = UPPER('{dbName}')"; + if (IsPgModel()) + { + sql = sql.Replace("sys_", "pg_"); + } + return this.Context.Ado.SqlQuery(sql); + } + private string GetSchema() + { + var schema = "public"; + if (IsSqlServerModel()) + { + schema = "dbo"; + } + if (System.Text.RegularExpressions.Regex.IsMatch(this.Context.CurrentConnectionConfig.ConnectionString.ToLower(), "searchpath=")) + { + var regValue = System.Text.RegularExpressions.Regex.Match(this.Context.CurrentConnectionConfig.ConnectionString.ToLower(), @"searchpath\=(\w+)").Groups[1].Value; + if (regValue.HasValue()) + { + schema = regValue; + } + } + else if (System.Text.RegularExpressions.Regex.IsMatch(this.Context.CurrentConnectionConfig.ConnectionString.ToLower(), "search path=")) + { + var regValue = System.Text.RegularExpressions.Regex.Match(this.Context.CurrentConnectionConfig.ConnectionString.ToLower(), @"search path\=(\w+)").Groups[1].Value; + if (regValue.HasValue()) + { + schema = regValue; + } + } + + return schema.ToLower(); + } + public override bool UpdateColumn(string tableName, DbColumnInfo columnInfo) + { + if (IsSqlServerModel()) + { + if (columnInfo.DataType.EqualCase("uuid")) + { + columnInfo.DataType = "uniqueidentifier"; + columnInfo.Length = 0; + columnInfo.Scale = 0; + } + } + + ConvertCreateColumnInfo(columnInfo); + tableName = this.SqlBuilder.GetTranslationTableName(tableName); + var columnName = this.SqlBuilder.GetTranslationColumnName(columnInfo.DbColumnName); + string type = GetType(tableName, columnInfo); + //this.Context.Ado.ExecuteCommand(sql); + + string sql = @"ALTER TABLE {table} ALTER {column} TYPE {type};ALTER TABLE {table} ALTER COLUMN {column} {null}"; + + var isnull = columnInfo.IsNullable ? " DROP NOT NULL " : " SET NOT NULL "; + + sql = sql.Replace("{table}", tableName) + .Replace("{type}", type) + .Replace("{column}", columnName) + .Replace("{null}", isnull); + this.Context.Ado.ExecuteCommand(sql); + return true; + } + protected string GetType(string tableName, DbColumnInfo columnInfo) + { + string columnName = this.SqlBuilder.GetTranslationColumnName(columnInfo.DbColumnName); + tableName = this.SqlBuilder.GetTranslationTableName(tableName); + string dataSize = GetSize(columnInfo); + string dataType = columnInfo.DataType; + //if (!string.IsNullOrEmpty(dataType)) + //{ + // dataType = dataType; + //} + return dataType + "" + dataSize; + } + //public override bool IsAnyColumn(string tableName, string columnName, bool isCache = true) + //{ + // var sql = + // $"select count(*) from information_schema.columns WHERE table_schema = '{GetSchema()}' and UPPER(table_name) = '{tableName.ToUpper(IsUpper)}' and UPPER(column_name) = '{columnName.ToUpper(IsUpper)}'"; + // return this.Context.Ado.GetInt(sql) > 0; + //} + + public override bool IsAnyTable(string tableName, bool isCache = true) + { + var sql = $"select count(*) from information_schema.tables where UPPER(table_schema)=UPPER('{GetSchema()}') and UPPER(table_type)=UPPER('BASE TABLE') and UPPER(table_name)=UPPER('{tableName.ToUpper(IsUpper)}')"; + if (IsSqlServerModel()) + { + sql = $"select count(*) from information_schema.tables where UPPER(table_schema)=UPPER('{GetSchema()}') and pg_catalog.UPPER(table_name)=pg_catalog.UPPER('{tableName.ToUpper(IsUpper)}')"; + } + return this.Context.Ado.GetInt(sql) > 0; + } + + /// + ///by current connection string + /// + /// + /// + /// + public override bool CreateDatabase(string databaseName, string databaseDirectory = null) + { + if (this.Context.Ado.IsValidConnection()) + { + return true; + } + if (databaseDirectory != null) + { + if (!FileHelper.IsExistDirectory(databaseDirectory)) + { + FileHelper.CreateDirectory(databaseDirectory); + } + } + var oldDatabaseName = this.Context.Ado.Connection.Database; + var connection = this.Context.CurrentConnectionConfig.ConnectionString; + if (IsSqlServerModel()) + { + connection = connection.Replace(oldDatabaseName, "master"); + } + else + { + connection = connection.Replace(oldDatabaseName, "test"); + } + var newDb = new SqlSugarClient(new ConnectionConfig() + { + DbType = this.Context.CurrentConnectionConfig.DbType, + IsAutoCloseConnection = true, + ConnectionString = connection + }); + if (newDb.Ado.IsValidConnection() == false) + { + newDb = new SqlSugarClient(new ConnectionConfig() + { + DbType = this.Context.CurrentConnectionConfig.DbType, + IsAutoCloseConnection = true, + ConnectionString = this.Context.CurrentConnectionConfig.ConnectionString.Replace(oldDatabaseName, "TEST") + }); + } + if (!GetDataBaseList(newDb).Any(it => it.Equals(databaseName, StringComparison.CurrentCultureIgnoreCase))) + { + newDb.Ado.ExecuteCommand(string.Format(CreateDataBaseSql, this.SqlBuilder.SqlTranslationLeft + databaseName + this.SqlBuilder.SqlTranslationRight, databaseDirectory)); + } + return true; + } + public override bool AddRemark(EntityInfo entity) + { + var db = this.Context; + var columns = entity.Columns.Where(it => it.IsIgnore == false).ToList(); + + foreach (var item in columns) + { + if (item.ColumnDescription != null) + { + db.DbMaintenance.AddColumnRemark(SqlBuilder.GetTranslationColumnName(item.DbColumnName).ToUpper(IsUpper), SqlBuilder.GetTranslationColumnName(item.DbTableName).ToUpper(IsUpper), item.ColumnDescription); + + } + } + //table remak + if (entity.TableDescription != null) + { + db.DbMaintenance.AddTableRemark(SqlBuilder.GetTranslationColumnName(entity.DbTableName), entity.TableDescription); + } + return true; + } + public override bool AddColumn(string tableName, DbColumnInfo columnInfo) + { + if (IsSqlServerModel()) + { + if (columnInfo.DataType.EqualCase("uuid")) + { + columnInfo.DataType = "uniqueidentifier"; + columnInfo.Length = 0; + columnInfo.Scale = 0; + } + } + return base.AddColumn(tableName, columnInfo); + } + public override bool RenameTable(string oldTableName, string newTableName) + { + return base.RenameTable(this.SqlBuilder.GetTranslationTableName(oldTableName), this.SqlBuilder.GetTranslationTableName(newTableName)); + } + public override bool CreateTable(string tableName, List columns, bool isCreatePrimaryKey = true) + { + if (columns.HasValue()) + { + foreach (var item in columns) + { + + ConvertCreateColumnInfo(item); + //if (item.DbColumnName.Equals("GUID", StringComparison.CurrentCultureIgnoreCase) && item.Length == 0) + //{ + // if (item.DataType?.ToLower() != "uuid") + // { + // item.Length = 10; + // } + //} + } + } + string sql = GetCreateTableSql(tableName, columns); + string primaryKeyInfo = null; + if (columns.Any(it => it.IsPrimarykey) && isCreatePrimaryKey) + { + primaryKeyInfo = string.Format(", Primary key({0})", string.Join(",", columns.Where(it => it.IsPrimarykey).Select(it => this.SqlBuilder.GetTranslationColumnName(it.DbColumnName.ToUpper(IsUpper))))); + + } + sql = sql.Replace("$PrimaryKey", primaryKeyInfo); + this.Context.Ado.ExecuteCommand(sql); + return true; + } + protected override string GetCreateTableSql(string tableName, List columns) + { + List columnArray = new List(); + Check.Exception(columns.IsNullOrEmpty(), "No columns found "); + foreach (var item in columns) + { + string columnName = item.DbColumnName; + string dataType = item.DataType; + if (dataType == "varchar" && item.Length == 0) + { + item.Length = 1; + } + //if (dataType == "uuid") + //{ + // item.Length = 50; + // dataType = "varchar"; + //} + string dataSize = item.Length > 0 ? string.Format("({0})", item.Length) : null; + if (item.DecimalDigits > 0 && item.Length > 0 && dataType == "numeric") + { + dataSize = $"({item.Length},{item.DecimalDigits})"; + } + string nullType = item.IsNullable ? this.CreateTableNull : CreateTableNotNull; + string primaryKey = null; + string addItem = string.Format(this.CreateTableColumn, this.SqlBuilder.GetTranslationColumnName(columnName.ToUpper(IsUpper)), dataType, dataSize, nullType, primaryKey, ""); + if (item.IsIdentity) + { + if (dataType?.ToLower() == "int") + { + dataSize = "int4"; + } + else if (dataType?.ToLower() == "long") + { + dataSize = "int8"; + } + string length = dataType.Substring(dataType.Length - 1); + string identityDataType = "serial" + length; + addItem = addItem.Replace(dataType, identityDataType); + } + columnArray.Add(addItem); + } + string tableString = string.Format(this.CreateTableSql, this.SqlBuilder.GetTranslationTableName(tableName.ToUpper(IsUpper)), string.Join(",\r\n", columnArray)); + return tableString; + } + protected override bool IsAnyDefaultValue(string tableName, string columnName, List columns) + { + var defaultValue = columns.Where(it => it.DbColumnName.Equals(columnName, StringComparison.CurrentCultureIgnoreCase)).First().DefaultValue; + if (defaultValue?.StartsWith("NULL::") == true) + { + return false; + } + return defaultValue.HasValue(); + } + public override bool IsAnyConstraint(string constraintName) + { + throw new NotSupportedException("PgSql IsAnyConstraint NotSupportedException"); + } + public override bool BackupDataBase(string databaseName, string fullFileName) + { + Check.ThrowNotSupportedException("PgSql BackupDataBase NotSupported"); + return false; + } + + public override List GetColumnInfosByTableName(string tableName, bool isCache = true) + { + var result = base.GetColumnInfosByTableName(tableName.TrimEnd('"').TrimStart('"').ToLower(), isCache); + if (result == null || result.Count == 0) + { + result = base.GetColumnInfosByTableName(tableName, isCache); + } + try + { + string sql = $@"select + kcu.column_name as key_column + from information_schema.table_constraints tco + join information_schema.key_column_usage kcu + on kcu.constraint_name = tco.constraint_name + and kcu.constraint_schema = tco.constraint_schema + and kcu.constraint_name = tco.constraint_name + where tco.constraint_type = 'PRIMARY KEY' + and kcu.table_schema='{GetSchema()}' and + upper(kcu.table_name)=upper('{tableName.TrimEnd('"').TrimStart('"')}')"; + List pkList = new List(); + if (isCache) + { + pkList = GetListOrCache("GetColumnInfosByTableName_N_Pk" + tableName, sql); + } + else + { + pkList = this.Context.Ado.SqlQuery(sql); + } + if (pkList.Count > 1) + { + foreach (var item in result) + { + if (pkList.Select(it => it.ToUpper()).Contains(item.DbColumnName.ToUpper())) + { + item.IsPrimarykey = true; + } + } + } + } + catch + { + + } + return result; + } + public bool IsUpper + { + get + { + if (this.Context.CurrentConnectionConfig.MoreSettings == null) + { + return true; + } + else + { + return this.Context.CurrentConnectionConfig.MoreSettings.IsAutoToUpper == true; + } + } + } + private void ConvertCreateColumnInfo(DbColumnInfo x) + { + string[] array = new string[] { "int4", "text", "int2", "int8", "date", "bit", "text", "timestamp" }; + + if (array.Contains(x.DataType?.ToLower())) + { + x.Length = 0; + x.DecimalDigits = 0; + } + + if (IsSqlServerModel()) + { + if (x.DataType.EqualCase("int8")) + { + x.DataType = "bigint"; + x.Length = 0; + x.Scale = 0; + } + } + } + private bool IsPgModel() + { + return this.Context.CurrentConnectionConfig?.MoreSettings?.DatabaseModel == DbType.PostgreSQL; + } + private bool IsSqlServerModel() + { + return this.Context.CurrentConnectionConfig?.MoreSettings?.DatabaseModel == DbType.SqlServer; + } + private bool IsMySql() + { + return this.Context.CurrentConnectionConfig?.MoreSettings?.DatabaseModel == DbType.MySql; + } + #endregion + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Kdbndp/Insertable/KdbndpInserttable.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Kdbndp/Insertable/KdbndpInserttable.cs new file mode 100644 index 000000000..b8cf9fae9 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Kdbndp/Insertable/KdbndpInserttable.cs @@ -0,0 +1,99 @@ +namespace SqlSugar +{ + public class KdbndpInserttable : InsertableProvider where T : class, new() + { + public override int ExecuteReturnIdentity() + { + InsertBuilder.IsReturnIdentity = true; + PreToSql(); + string sql = InsertBuilder.ToSqlString().Replace("$PrimaryKey", this.SqlBuilder.GetTranslationColumnName(GetIdentityKeys().FirstOrDefault() ?? "")); + RestoreMapping(); + sql = GetSql(sql); + AutoRemoveDataCache(); + var result = Ado.GetScalar(sql, InsertBuilder.Parameters == null ? null : InsertBuilder.Parameters.ToArray()).ObjToInt(); + return result; + } + + + public override async Task ExecuteReturnIdentityAsync() + { + InsertBuilder.IsReturnIdentity = true; + PreToSql(); + string sql = InsertBuilder.ToSqlString().Replace("$PrimaryKey", this.SqlBuilder.GetTranslationColumnName(GetIdentityKeys().FirstOrDefault() ?? "")); + RestoreMapping(); + sql = GetSql(sql); + AutoRemoveDataCache(); + var obj = await Ado.GetScalarAsync(sql, InsertBuilder.Parameters == null ? null : InsertBuilder.Parameters.ToArray()).ConfigureAwait(false); + var result = obj.ObjToInt(); + return result; + } + public override KeyValuePair> ToSql() + { + var result = base.ToSql(); + if (GetPrimaryKeys()?.Count > 0) + { + return new KeyValuePair>(result.Key.Replace("$PrimaryKey", GetPrimaryKeys().FirstOrDefault()), result.Value); + } + else + { + return new KeyValuePair>(result.Key.Replace(" returning $PrimaryKey", ""), result.Value); + } + } + + public override long ExecuteReturnBigIdentity() + { + InsertBuilder.IsReturnIdentity = true; + PreToSql(); + string sql = InsertBuilder.ToSqlString().Replace("$PrimaryKey", this.SqlBuilder.GetTranslationColumnName(GetIdentityKeys().FirstOrDefault() ?? "")); + RestoreMapping(); + sql = GetSql(sql); + AutoRemoveDataCache(); + var result = Convert.ToInt64(Ado.GetScalar(sql, InsertBuilder.Parameters == null ? null : InsertBuilder.Parameters.ToArray()) ?? "0"); + return result; + } + public override async Task ExecuteReturnBigIdentityAsync() + { + InsertBuilder.IsReturnIdentity = true; + PreToSql(); + string sql = InsertBuilder.ToSqlString().Replace("$PrimaryKey", this.SqlBuilder.GetTranslationColumnName(GetIdentityKeys().FirstOrDefault() ?? "")); + RestoreMapping(); + sql = GetSql(sql); + AutoRemoveDataCache(); + var result = Convert.ToInt64(await Ado.GetScalarAsync(sql, InsertBuilder.Parameters == null ? null : InsertBuilder.Parameters.ToArray()).ConfigureAwait(false) ?? "0"); + return result; + } + + public override bool ExecuteCommandIdentityIntoEntity() + { + var result = InsertObjs.First(); + var identityKeys = GetIdentityKeys(); + if (identityKeys.Count == 0) { return this.ExecuteCommand() > 0; } + var idValue = ExecuteReturnBigIdentity(); + Check.Exception(identityKeys.Count > 1, "ExecuteCommandIdentityIntoEntity does not support multiple identity keys"); + var identityKey = identityKeys.First(); + object setValue = 0; + if (idValue > int.MaxValue) + setValue = idValue; + else + setValue = Convert.ToInt32(idValue); + var propertyName = this.Context.EntityMaintenance.GetPropertyName(identityKey); + typeof(T).GetProperties().First(t => t.Name.Equals(propertyName, StringComparison.CurrentCultureIgnoreCase)).SetValue(result, setValue, null); + return idValue > 0; + } + + private string GetSql(string sql) + { + if (GetIdentityKeys().FirstOrDefault() == null) + { + sql = sql.Replace("returning \"\"", ""); + var id = this.Context.DbMaintenance.GetIsIdentities(this.Context.EntityMaintenance.GetTableName(this.InsertBuilder.GetTableNameString)).FirstOrDefault(); + if (id != null) + { + sql = sql.TrimEnd().TrimEnd(';') + " returning " + this.SqlBuilder.GetTranslationColumnName(id); + } + } + + return sql; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Kdbndp/KdbndpSQLProvider.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Kdbndp/KdbndpSQLProvider.cs new file mode 100644 index 000000000..ae4f04dc1 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Kdbndp/KdbndpSQLProvider.cs @@ -0,0 +1,196 @@ +using Kdbndp; + +using KdbndpTypes; + +using System.Data; +using System.Data.Common; + +namespace SqlSugar +{ + public partial class KdbndpProvider : AdoProvider + { + public KdbndpProvider() { } + public override IDbConnection Connection + { + get + { + if (base._DbConnection == null) + { + try + { + var npgsqlConnectionString = base.Context.CurrentConnectionConfig.ConnectionString; + base._DbConnection = new KdbndpConnection(npgsqlConnectionString); + } + catch (Exception ex) + { + Check.Exception(true, ErrorMessage.ConnnectionOpen, ex.Message); + } + } + return base._DbConnection; + } + set + { + base._DbConnection = value; + } + } + public override void CheckConnection() + { + try + { + base.CheckConnection(); + } + catch (Exception ex) + { + if (ex.Message.Contains("Version string portion was too short or too long")) + { + Check.Exception(true, "人大金仓R6请安装 Nuget:SqlSugarCore.Kdbndp到最新版本"); + } + throw; + } + } + public override void BeginTran(string transactionName) + { + base.BeginTran(); + } + /// + /// Only SqlServer + /// + /// + /// + public override void BeginTran(IsolationLevel iso, string transactionName) + { + base.BeginTran(iso); + } + public override IDataAdapter GetAdapter() + { + return new KdbndpDataAdapter(); + } + public override DbCommand GetCommand(string sql, SugarParameter[] parameters) + { + KdbndpCommand sqlCommand = new KdbndpCommand(sql, (KdbndpConnection)this.Connection); + sqlCommand.CommandType = this.CommandType; + sqlCommand.CommandTimeout = this.CommandTimeOut; + if (sqlCommand.CommandType == CommandType.StoredProcedure) + { + sqlCommand.DbModeType = DbMode.Oracle; + } + if (this.Transaction != null) + { + sqlCommand.Transaction = (KdbndpTransaction)this.Transaction; + } + if (parameters.HasValue()) + { + IDataParameter[] ipars = ToIDbDataParameter(parameters); + sqlCommand.Parameters.AddRange((KdbndpParameter[])ipars); + } + CheckConnection(); + return sqlCommand; + } + public override void SetCommandToAdapter(IDataAdapter dataAdapter, DbCommand command) + { + ((KdbndpDataAdapter)dataAdapter).SelectCommand = (KdbndpCommand)command; + } + /// + /// if mysql return MySqlParameter[] pars + /// if sqlerver return SqlParameter[] pars ... + /// + /// + /// + public override IDataParameter[] ToIDbDataParameter(params SugarParameter[] parameters) + { + if (parameters == null || parameters.Length == 0) return null; + KdbndpParameter[] result = new KdbndpParameter[parameters.Length]; + int index = 0; + foreach (var parameter in parameters) + { + if (parameter.Value == null) parameter.Value = DBNull.Value; + var sqlParameter = new KdbndpParameter(); + sqlParameter.ParameterName = parameter.ParameterName; + sqlParameter.Size = parameter.Size; + sqlParameter.Value = parameter.Value; + sqlParameter.DbType = parameter.DbType; + sqlParameter.Direction = parameter.Direction; + if (parameter.IsJson) + { + sqlParameter.KdbndpDbType = KdbndpDbType.Json; + } + if (parameter.IsArray) + { + // sqlParameter.Value = this.Context.Utilities.SerializeObject(sqlParameter.Value); + var type = sqlParameter.Value.GetType(); + if (ArrayMapping.TryGetValue(type, out KdbndpDbType value)) + { + sqlParameter.KdbndpDbType = value | KdbndpDbType.Array; + } + else + { + Check.Exception(true, sqlParameter.Value.GetType().Name + " No Support"); + } + } + if (sqlParameter.Direction == 0) + { + sqlParameter.Direction = ParameterDirection.Input; + } + if (parameter.IsRefCursor) + { + sqlParameter.KdbndpDbType = KdbndpDbType.Refcursor; + sqlParameter.Direction = ParameterDirection.Output; + } + result[index] = sqlParameter; + if (sqlParameter.Direction.IsIn(ParameterDirection.Output, ParameterDirection.InputOutput, ParameterDirection.ReturnValue)) + { + if (this.OutputParameters == null) this.OutputParameters = new List(); + this.OutputParameters.RemoveAll(it => it.ParameterName == sqlParameter.ParameterName); + this.OutputParameters.Add(sqlParameter); + } + //人大金仓MYSQL模式下json字段类型处理 + if (sqlParameter.KdbndpDbType == KdbndpDbType.Json && this.Context?.CurrentConnectionConfig?.MoreSettings?.DatabaseModel == DbType.MySql) + { + sqlParameter.KdbndpDbType = KdbndpDbType.Varchar; + } + if (parameter.CustomDbType != null && parameter.CustomDbType is KdbndpDbType) + { + sqlParameter.KdbndpDbType = ((KdbndpDbType)parameter.CustomDbType); + } + ++index; + } + return result; + } + public override Action ErrorEvent => it => + { + if (base.ErrorEvent != null) + { + base.ErrorEvent(it); + } + if (it.Message?.StartsWith("42883: function uuid_generate_v4() does not exist") == true) + { + Check.ExceptionEasy(it.Message, $"使用uuid_generate_v4()函数需要创建 CREATE EXTENSION IF NOT EXISTS kbcrypto;CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\" "); + } + }; + + static readonly Dictionary ArrayMapping = new Dictionary() + { + { typeof(int[]),KdbndpDbType.Integer}, + { typeof(short[]),KdbndpDbType.Smallint}, + { typeof(long[]),KdbndpDbType.Bigint}, + { typeof(decimal[]),KdbndpDbType.Numeric}, + { typeof(char[]),KdbndpDbType.Text}, + { typeof(byte[]),KdbndpDbType.Bytea}, + { typeof(bool[]),KdbndpDbType.Boolean}, + {typeof(DateTime[]),KdbndpDbType.Date}, + + + { typeof(int?[]),KdbndpDbType.Integer}, + { typeof(short?[]),KdbndpDbType.Smallint}, + { typeof(long?[]),KdbndpDbType.Bigint}, + { typeof(decimal?[]),KdbndpDbType.Numeric}, + { typeof(char?[]),KdbndpDbType.Text}, + { typeof(byte?[]),KdbndpDbType.Bytea}, + { typeof(bool?[]),KdbndpDbType.Boolean}, + {typeof(DateTime?[]),KdbndpDbType.Date}, + + + { typeof(string[]), KdbndpDbType.Text}, + }; + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Kdbndp/Queryable/KdbndpQueryable.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Kdbndp/Queryable/KdbndpQueryable.cs new file mode 100644 index 000000000..d1acc11ac --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Kdbndp/Queryable/KdbndpQueryable.cs @@ -0,0 +1,63 @@ +namespace SqlSugar +{ + public class KdbndpQueryable : QueryableProvider + { + public override ISugarQueryable With(string withString) + { + return this; + } + + //public override ISugarQueryable PartitionBy(string groupFileds) + //{ + // this.QueryBuilder. + // return this; + //} + } + public class KdbndpQueryable : QueryableProvider + { + public new ISugarQueryable With(string withString) + { + return this; + } + } + public class KdbndpQueryable : QueryableProvider + { + + } + public class KdbndpQueryable : QueryableProvider + { + + } + public class KdbndpQueryable : QueryableProvider + { + + } + public class KdbndpQueryable : QueryableProvider + { + + } + public class KdbndpQueryable : QueryableProvider + { + + } + public class KdbndpQueryable : QueryableProvider + { + + } + public class KdbndpQueryable : QueryableProvider + { + + } + public class KdbndpQueryable : QueryableProvider + { + + } + public class KdbndpQueryable : QueryableProvider + { + + } + public class KdbndpQueryable : QueryableProvider + { + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Kdbndp/SqlBuilder/KdbndpBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Kdbndp/SqlBuilder/KdbndpBuilder.cs new file mode 100644 index 000000000..8752a9eca --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Kdbndp/SqlBuilder/KdbndpBuilder.cs @@ -0,0 +1,117 @@ +namespace SqlSugar +{ + public class KdbndpBuilder : SqlBuilderProvider + { + public override string SqlTranslationLeft + { + get + { + return "\""; + } + } + public override string SqlTranslationRight + { + get + { + return "\""; + } + } + public override string SqlDateNow + { + get + { + return "current_date"; + } + } + public override string FullSqlDateNow + { + get + { + return "select current_date"; + } + } + + + public override string GetTranslationColumnName(string propertyName) + { + if (propertyName.Contains('.') && !propertyName.Contains(SqlTranslationLeft)) + { + return string.Join(".", propertyName.Split('.').Select(it => $"{SqlTranslationLeft}{it.ToUpper(IsUpper)}{SqlTranslationRight}")); + } + if (propertyName.Contains(SqlTranslationLeft)) return propertyName; + else + return SqlTranslationLeft + propertyName.ToUpper(IsUpper) + SqlTranslationRight; + } + + //public override string GetNoTranslationColumnName(string name) + //{ + // return name.TrimEnd(Convert.ToChar(SqlTranslationRight)).TrimStart(Convert.ToChar(SqlTranslationLeft)).ToLower(); + //} + public override string GetTranslationColumnName(string entityName, string propertyName) + { + Check.ArgumentNullException(entityName, string.Format(null, ErrorMessage.ObjNotExistCompositeFormat, "Table Name")); + Check.ArgumentNullException(propertyName, string.Format(null, ErrorMessage.ObjNotExistCompositeFormat, "Column Name")); + var context = this.Context; + var mappingInfo = context + .MappingColumns + .FirstOrDefault(it => + it.EntityName.Equals(entityName, StringComparison.CurrentCultureIgnoreCase) && + it.PropertyName.Equals(propertyName, StringComparison.CurrentCultureIgnoreCase)); + return (mappingInfo == null ? SqlTranslationLeft + propertyName.ToUpper(IsUpper) + SqlTranslationRight : SqlTranslationLeft + mappingInfo.DbColumnName.ToUpper(IsUpper) + SqlTranslationRight); + } + + public override string GetTranslationTableName(string name) + { + Check.ArgumentNullException(name, string.Format(null, ErrorMessage.ObjNotExistCompositeFormat, "Table Name")); + var context = this.Context; + + var mappingInfo = context + .MappingTables + .FirstOrDefault(it => it.EntityName.Equals(name, StringComparison.CurrentCultureIgnoreCase)); + name = (mappingInfo == null ? name : mappingInfo.DbTableName); + if (name.Contains('.') && !name.Contains('(')) + { + if (SqlTranslationLeft.HasValue() && SqlTranslationLeft == SqlTranslationRight) + { + return string.Join(".", name.ToUpper(IsUpper).Split('.').Select(it => SqlTranslationLeft + it.Replace(SqlTranslationLeft, "") + SqlTranslationRight)); + } + return string.Join(".", name.ToUpper(IsUpper).Split('.').Select(it => SqlTranslationLeft + it + SqlTranslationRight)); + } + else if (name.Contains('(')) + { + return name; + } + else if (name.Contains(SqlTranslationLeft) && name.Contains(SqlTranslationRight)) + { + return name; + } + else + { + return SqlTranslationLeft + name.ToUpper(IsUpper).TrimEnd('"').TrimStart('"') + SqlTranslationRight; + } + } + public override string GetUnionFomatSql(string sql) + { + return " ( " + sql + " ) "; + } + + + /// + /// + /// + public bool IsUpper + { + get + { + if (this.Context.CurrentConnectionConfig.MoreSettings == null) + { + return true; + } + else + { + return this.Context.CurrentConnectionConfig.MoreSettings.IsAutoToUpper == true; + } + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Kdbndp/SqlBuilder/KdbndpDeleteBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Kdbndp/SqlBuilder/KdbndpDeleteBuilder.cs new file mode 100644 index 000000000..f81c546bd --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Kdbndp/SqlBuilder/KdbndpDeleteBuilder.cs @@ -0,0 +1,7 @@ +namespace SqlSugar +{ + public class KdbndpDeleteBuilder : DeleteBuilder + { + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Kdbndp/SqlBuilder/KdbndpExpressionContext.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Kdbndp/SqlBuilder/KdbndpExpressionContext.cs new file mode 100644 index 000000000..bbddf6f8d --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Kdbndp/SqlBuilder/KdbndpExpressionContext.cs @@ -0,0 +1,563 @@ +namespace SqlSugar +{ + public class KdbndpExpressionContext : ExpressionContext, ILambdaExpressions + { + public SqlSugarProvider Context { get; set; } + public KdbndpExpressionContext() + { + base.DbMehtods = new KdbndpMethod(); + } + public override string SqlTranslationLeft + { + get + { + return "\""; + } + } + public override string SqlTranslationRight + { + get + { + return "\""; + } + } + public override string GetTranslationText(string name) + { + return SqlTranslationLeft + name.ToUpper(IsUpper) + SqlTranslationRight; + } + + public override string GetTranslationTableName(string entityName, bool isMapping = true) + { + Check.ArgumentNullException(entityName, string.Format(null, ErrorMessage.ObjNotExistCompositeFormat, "Table Name")); + if (IsTranslationText(entityName)) return entityName; + isMapping = isMapping && this.MappingTables.HasValue(); + var isComplex = entityName.Contains(UtilConstants.Dot); + if (isMapping && isComplex) + { + var columnInfo = entityName.Split(UtilConstants.DotChar); + var mappingInfo = this.MappingTables.FirstOrDefault(it => it.EntityName.Equals(columnInfo.Last(), StringComparison.CurrentCultureIgnoreCase)); + if (mappingInfo != null) + { + columnInfo[columnInfo.Length - 1] = mappingInfo.EntityName; + } + return string.Join(UtilConstants.Dot, columnInfo.Select(it => GetTranslationText(it))); + } + else if (isMapping) + { + var mappingInfo = this.MappingTables.FirstOrDefault(it => it.EntityName.Equals(entityName, StringComparison.CurrentCultureIgnoreCase)); + + var tableName = mappingInfo?.DbTableName + ""; + if (tableName.Contains('.')) + { + tableName = string.Join(UtilConstants.Dot, tableName.Split(UtilConstants.DotChar).Select(it => GetTranslationText(it))); + return tableName; + } + + return SqlTranslationLeft + (mappingInfo == null ? entityName : mappingInfo.DbTableName).ToUpper(IsUpper) + SqlTranslationRight; + } + else if (isComplex) + { + return string.Join(UtilConstants.Dot, entityName.Split(UtilConstants.DotChar).Select(it => GetTranslationText(it))); + } + else + { + return GetTranslationText(entityName); + } + } + public override string GetTranslationColumnName(string columnName) + { + Check.ArgumentNullException(columnName, string.Format(null, ErrorMessage.ObjNotExistCompositeFormat, "Column Name")); + if (columnName.Substring(0, 1) == this.SqlParameterKeyWord) + { + return columnName; + } + if (IsTranslationText(columnName)) return columnName; + if (columnName.Contains(UtilConstants.Dot)) + { + return string.Join(UtilConstants.Dot, columnName.Split(UtilConstants.DotChar).Select(it => GetTranslationText(it))); + } + else + { + return GetTranslationText(columnName); + } + } + public override string GetDbColumnName(string entityName, string propertyName) + { + if (this.MappingColumns.HasValue()) + { + var mappingInfo = this.MappingColumns.SingleOrDefault(it => it.EntityName == entityName && it.PropertyName == propertyName); + return (mappingInfo == null ? propertyName : mappingInfo.DbColumnName).ToUpper(IsUpper); + } + else + { + return propertyName.ToUpper(IsUpper); + } + } + public bool IsUpper + { + get + { + if (this.SugarContext?.Context?.Context?.CurrentConnectionConfig?.MoreSettings == null) + { + return true; + } + else + { + return this.SugarContext?.Context?.Context?.CurrentConnectionConfig?.MoreSettings.IsAutoToUpper == true; + } + } + } + } + public class KdbndpMethod : DefaultDbMethod, IDbMethods + { + public override string CharIndexNew(MethodCallExpressionModel model) + { + return string.Format(" (strpos ({0},{1}))", model.Args[0].MemberName, model.Args[1].MemberName); + } + public override string CharIndex(MethodCallExpressionModel model) + { + return string.Format(" (strpos ({1},{0})-1)", model.Args[0].MemberName, model.Args[1].MemberName); + } + public override string TrueValue() + { + return "true"; + } + public override string FalseValue() + { + return "false"; + } + public override string DateDiff(MethodCallExpressionModel model) + { + if (IsSqlServerModel(model)) + { + return base.DateDiff(model); + } + + var parameter = (DateType)(Enum.Parse(typeof(DateType), model.Args[0].MemberValue.ObjToString())); + var begin = model.Args[1].MemberName; + var end = model.Args[2].MemberName; + switch (parameter) + { + case DateType.Year: + return $" ( DATE_PART('Year', {end} ) - DATE_PART('Year', {begin}) )"; + case DateType.Month: + return $" ( ( DATE_PART('Year', {end} ) - DATE_PART('Year', {begin}) ) * 12 + (DATE_PART('month', {end}) - DATE_PART('month', {begin})) )"; + case DateType.Day: + return $" ( DATE_PART('day', {end} - {begin}) )"; + case DateType.Hour: + return $" ( ( DATE_PART('day', {end} - {begin}) ) * 24 + DATE_PART('hour', {end} - {begin} ) )"; + case DateType.Minute: + return $" ( ( ( DATE_PART('day', {end} - {begin}) ) * 24 + DATE_PART('hour', {end} - {begin} ) ) * 60 + DATE_PART('minute', {end} - {begin} ) )"; + case DateType.Second: + return $" ( ( ( DATE_PART('day', {end} - {begin}) ) * 24 + DATE_PART('hour', {end} - {begin} ) ) * 60 + DATE_PART('minute', {end} - {begin} ) ) * 60 + DATE_PART('second', {end} - {begin} )"; + case DateType.Millisecond: + break; + default: + break; + } + throw new Exception(parameter + " datediff no support"); + } + public override string IIF(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + var parameter3 = model.Args[2]; + if (parameter.Type == UtilConstants.BoolType) + { + parameter.MemberName = parameter.MemberName.ToString().Replace("=1", "=true"); + parameter2.MemberName = false; + parameter3.MemberName = true; + } + return string.Format("( CASE WHEN {0} THEN {1} ELSE {2} END )", parameter.MemberName, parameter2.MemberName, parameter3.MemberName); + } + public override string Substring(MethodCallExpressionModel model) + { + if (model?.Conext?.SugarContext?.Context?.CurrentConnectionConfig?.MoreSettings?.DatabaseModel == DbType.MySql) + { + return base.Substring(model).Replace(" + ", " operator(pg_catalog.+) "); + } + return base.Substring(model); + } + public override string DateValue(MethodCallExpressionModel model) + { + if (IsSqlServerModel(model)) + { + return new SqlServerMethod().DateValue(model); + } + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + var format = "dd"; + if (parameter2.MemberValue.ObjToString() == DateType.Year.ToString()) + { + format = "yyyy"; + } + if (parameter2.MemberValue.ObjToString() == DateType.Month.ToString()) + { + format = "MM"; + } + if (parameter2.MemberValue.ObjToString() == DateType.Day.ToString()) + { + format = "dd"; + } + if (parameter2.MemberValue.ObjToString() == DateType.Hour.ToString()) + { + format = "hh24"; + } + if (parameter2.MemberValue.ObjToString() == DateType.Minute.ToString()) + { + format = "mi"; + } + if (parameter2.MemberValue.ObjToString() == DateType.Second.ToString()) + { + format = "ss"; + } + if (parameter2.MemberValue.ObjToString() == DateType.Millisecond.ToString()) + { + format = "ms"; + } + if (parameter2.MemberValue.ObjToString() == DateType.Quarter.ToString()) + { + format = "q"; + } + if (parameter2.MemberValue.ObjToString() == DateType.Weekday.ToString()) + { + return $" extract(DOW FROM cast({parameter.MemberName} as TIMESTAMP)) "; + } + + return string.Format(" cast( to_char({1},'{0}')as integer ) ", format, parameter.MemberName); + } + + public override string Contains(MethodCallExpressionModel model) + { + if (IsSqlServerModel(model)) + { + return base.Contains(model); + } + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + return string.Format(" ({0} like pg_catalog.concat('%',{1},'%')) ", parameter.MemberName, parameter2.MemberName); + } + + public override string StartsWith(MethodCallExpressionModel model) + { + if (IsSqlServerModel(model)) + { + return base.StartsWith(model); + } + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + return string.Format(" ({0} like pg_catalog.concat({1},'%')) ", parameter.MemberName, parameter2.MemberName); + } + + public override string EndsWith(MethodCallExpressionModel model) + { + if (IsSqlServerModel(model)) + { + return base.EndsWith(model); + } + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + return string.Format(" ({0} like pg_catalog.concat('%',{1}))", parameter.MemberName, parameter2.MemberName); + } + + public override string DateIsSameDay(MethodCallExpressionModel model) + { + if (IsSqlServerModel(model)) + { + return new SqlServerMethod().DateIsSameDay(model); + } + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + return string.Format(" ( to_char({0},'yyyy-MM-dd')=to_char({1},'yyyy-MM-dd') ) ", parameter.MemberName, parameter2.MemberName); ; + } + + public override string HasValue(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format("( {0} IS NOT NULL )", parameter.MemberName); + } + + public override string DateIsSameByType(MethodCallExpressionModel model) + { + if (IsSqlServerModel(model)) + { + return new SqlServerMethod().DateIsSameByType(model); + } + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + var parameter3 = model.Args[2]; + DateType dateType = (DateType)parameter3.MemberValue; + var format = "yyyy-MM-dd"; + if (dateType == DateType.Quarter) + { + return string.Format(" (date_trunc('quarter',{0})=date_trunc('quarter',{1}) ) ", parameter.MemberName, parameter2.MemberName, format); + } + switch (dateType) + { + case DateType.Year: + format = "yyyy"; + break; + case DateType.Month: + format = "yyyy-MM"; + break; + case DateType.Day: + break; + case DateType.Hour: + format = "yyyy-MM-dd HH"; + break; + case DateType.Second: + format = "yyyy-MM-dd HH:mm:ss"; + break; + case DateType.Minute: + format = "yyyy-MM-dd HH:mm"; + break; + case DateType.Millisecond: + format = "yyyy-MM-dd HH:mm.ms"; + break; + default: + break; + } + return string.Format(" ( to_char({0},'{2}')=to_char({1},'{2}') ) ", parameter.MemberName, parameter2.MemberName, format); + } + + public override string ToDate(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + if (IsSqlServerModel(model)) + { + return string.Format(" CAST({0} AS dateTime)", parameter.MemberName); + } + return string.Format(" CAST({0} AS timestamp)", parameter.MemberName); + } + public override string DateAddByType(MethodCallExpressionModel model) + { + if (IsSqlServerModel(model)) + { + return base.DateAddByType(model); + } + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + var parameter3 = model.Args[2]; + if (model?.Conext?.SugarContext?.Context?.CurrentConnectionConfig?.MoreSettings?.DatabaseModel == DbType.MySql) + { + return string.Format(" ({1} + ({2} operator(pg_catalog.||) '{0}')::INTERVAL) ", parameter3.MemberValue, parameter.MemberName, parameter2.MemberName); + } + return string.Format(" ({1} + ({2}||'{0}')::INTERVAL) ", parameter3.MemberValue, parameter.MemberName, parameter2.MemberName); + } + + public override string DateAddDay(MethodCallExpressionModel model) + { + if (IsSqlServerModel(model)) + { + return base.DateAddDay(model); + } + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + return string.Format(" ({0} + ({1}||'day')::INTERVAL) ", parameter.MemberName, parameter2.MemberName); + } + + public override string ToInt32(MethodCallExpressionModel model) + { + if (IsSqlServerModel(model)) + { + return new SqlServerMethod().ToInt32(model); + } + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS INT4)", parameter.MemberName); + } + + public override string ToInt64(MethodCallExpressionModel model) + { + if (IsSqlServerModel(model)) + { + return new SqlServerMethod().ToInt64(model); + } + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS INT8)", parameter.MemberName); + } + + public override string ToString(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + if (IsSqlServerModel(model)) + { + return base.ToString(model); + } + return string.Format(" CAST({0} AS VARCHAR)", parameter.MemberName); + } + + public override string ToGuid(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS UUID)", parameter.MemberName); + } + + public override string ToDouble(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS DECIMAL(18,4))", parameter.MemberName); + } + + public override string ToBool(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS boolean)", parameter.MemberName); + } + + public override string ToDecimal(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS DECIMAL(18,4))", parameter.MemberName); + } + + public override string Length(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" LENGTH({0})", parameter.MemberName); + } + public override string MergeString(params string[] strings) + { + var key = Guid.NewGuid() + ""; + if (strings.Length == 1) + { + return " pg_catalog.concat(" + string.Join(",", strings.Select(it => it?.Replace("+", key))).Replace("+", "").Replace(key, "+") + ",null) "; + } + return " pg_catalog.concat(" + string.Join(",", strings.Select(it => it?.Replace("+", key))).Replace("+", "").Replace(key, "+") + ") "; + } + public override string IsNull(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter1 = model.Args[1]; + return string.Format("(CASE WHEN {0} IS NULL THEN {1} ELSE {0} END)", parameter.MemberName, parameter1.MemberName); + } + public override string GetDate() + { + return "NOW()"; + } + public override string GetRandom() + { + return "RANDOM()"; + } + + public override string EqualTrue(string fieldName) + { + return "( " + fieldName + "=true )"; + } + + public override string JsonField(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter1 = model.Args[1]; + //var parameter2 = model.Args[2]; + //var parameter3= model.Args[3]; + var result = GetJson(parameter.MemberName, parameter1.MemberName, model.Args.Count == 2); + if (model.Args.Count > 2) + { + result = GetJson(result, model.Args[2].MemberName, model.Args.Count == 3); + } + if (model.Args.Count > 3) + { + result = GetJson(result, model.Args[3].MemberName, model.Args.Count == 4); + } + if (model.Args.Count > 4) + { + result = GetJson(result, model.Args[4].MemberName, model.Args.Count == 5); + } + if (model.Args.Count > 5) + { + result = GetJson(result, model.Args[5].MemberName, model.Args.Count == 6); + } + return result; + } + + public override string JsonContainsFieldName(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter1 = model.Args[1]; + return $"({parameter.MemberName}::jsonb ?{parameter1.MemberName})"; + } + + private string GetJson(object memberName1, object memberName2, bool isLast) + { + if (isLast) + { + return $"({memberName1}::json->>{memberName2})"; + } + else + { + return $"({memberName1}->{memberName2})"; + } + } + + public override string JsonArrayLength(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + //var parameter1 = model.Args[1]; + return $" json_array_length({parameter.MemberName}::json) "; + } + + public override string JsonParse(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + //var parameter1 = model.Args[1]; + return $" ({parameter.MemberName}::json) "; + } + + public override string JsonArrayAny(MethodCallExpressionModel model) + { + if (UtilMethods.IsNumber(model.Args[1].MemberValue.GetType().Name)) + { + return $"{model.Args[0].MemberName}::jsonb @> '[{model.Args[1].MemberValue.ObjToStringNoTrim().ToSqlFilter()}]'::jsonb "; + } + else + { + return $"{model.Args[0].MemberName}::jsonb @> '[\"{model.Args[1].MemberValue}\"]'::jsonb "; + } + } + public override string Format(MethodCallExpressionModel model) + { + return base.Format(model).Replace("concat(", "pg_catalog.concat("); + } + public override string JsonListObjectAny(MethodCallExpressionModel model) + { + if (UtilMethods.IsNumber(model.Args[2].MemberValue.GetType().Name)) + { + return $"{model.Args[0].MemberName}::jsonb @> '[{{\"{model.Args[1].MemberValue}\":{model.Args[2].MemberValue}}}]'::jsonb "; + } + else + { + return $"{model.Args[0].MemberName}::jsonb @> '[{{\"{model.Args[1].MemberValue}\":\"{model.Args[2].MemberValue.ObjToStringNoTrim().ToSqlFilter()}\"}}]'::jsonb "; + } + } + public override string GetDateString(string dateValue, string formatString) + { + if (formatString.HasValue() && formatString.Contains("hh:mm")) + { + formatString = formatString.Replace("hh:mm", "hh:mi"); + } + else if (formatString.HasValue() && formatString.Contains("hhmm")) + { + formatString = formatString.Replace("hhmm", "hhmi"); + } + else if (formatString.HasValue() && formatString.Contains("HH:mm")) + { + formatString = formatString.Replace("HH:mm", "HH:mi"); + } + else if (formatString.HasValue() && formatString.Contains("HHmm")) + { + formatString = formatString.Replace("HHmm", "HHmi"); + } + if (formatString.Contains("HH")) + { + formatString = formatString.Replace("HH", "hh24"); + } + return $" to_char({dateValue},'{formatString}') "; + } + + private static bool IsSqlServerModel(MethodCallExpressionModel model) + { + return model?.Conext?.SugarContext?.Context?.CurrentConnectionConfig?.MoreSettings?.DatabaseModel == DbType.SqlServer; + } + + } +} \ No newline at end of file diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Kdbndp/SqlBuilder/KdbndpFastBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Kdbndp/SqlBuilder/KdbndpFastBuilder.cs new file mode 100644 index 000000000..ff6ee44f4 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Kdbndp/SqlBuilder/KdbndpFastBuilder.cs @@ -0,0 +1,139 @@ +using NpgsqlTypes; + +using System.Data; + +namespace SqlSugar +{ + public class KdbndpFastBuilder : FastBuilder, IFastBuilder + { + public static Dictionary PgSqlType = UtilMethods.EnumToDictionary(); + + public KdbndpFastBuilder() + { + } + + public override string UpdateSql { get; set; } = @"UPDATE {1} SET {0} FROM {2} AS TE WHERE {3} +"; + + //public virtual async Task UpdateByTempAsync(string tableName, string tempName, string[] updateColumns, string[] whereColumns) + //{ + // Check.ArgumentNullException(!updateColumns.Any(), "update columns count is 0"); + // Check.ArgumentNullException(!whereColumns.Any(), "where columns count is 0"); + // var sets = string.Join(",", updateColumns.Select(it => $"TM.{it}=TE.{it}")); + // var wheres = string.Join(",", whereColumns.Select(it => $"TM.{it}=TE.{it}")); + // string sql = string.Format(UpdateSql, sets, tableName, tempName, wheres); + // return await this.Context.Ado.ExecuteCommandAsync(sql); + //} + public async Task ExecuteBulkCopyAsync(DataTable dt) + { + List lsColNames = new List(); + for (int i = 0; i < dt.Columns.Count; i++) + { + lsColNames.Add($"\"{dt.Columns[i].ColumnName}\""); + } + string copyString = $"COPY {dt.TableName} ( {string.Join(",", lsColNames)} ) FROM STDIN (FORMAT BINARY)"; + Kdbndp.KdbndpConnection conn = (Kdbndp.KdbndpConnection)this.Context.Ado.Connection; + var columns = this.Context.DbMaintenance.GetColumnInfosByTableName(this.FastEntityInfo.DbTableName); + try + { + var identityColumnInfo = this.FastEntityInfo.Columns.FirstOrDefault(it => it.IsIdentity); + if (identityColumnInfo != null) + { + throw new Exception("PgSql bulkcopy no support identity"); + } + BulkCopy(dt, copyString, conn, columns); + } + catch (Exception) + { + throw; + } + finally + { + base.CloseDb(); + } + return await Task.FromResult(dt.Rows.Count).ConfigureAwait(false); + } + + private void BulkCopy(DataTable dt, string copyString, Kdbndp.KdbndpConnection conn, List columns) + { + if (conn.State == ConnectionState.Closed) + conn.Open(); + List columnViews = new List(); + foreach (DataColumn item in dt.Columns) + { + ColumnView result = new ColumnView(); + result.DbColumnInfo = columns.FirstOrDefault(it => it.DbColumnName.EqualCase(item.ColumnName)); + result.DataColumn = item; + result.EntityColumnInfo = this.FastEntityInfo.Columns.FirstOrDefault(it => it.DbColumnName.EqualCase(item.ColumnName)); + var key = result.DbColumnInfo?.DataType?.ToLower(); + if (result.DbColumnInfo == null) + { + result.Type = null; + } + else if (PgSqlType.TryGetValue(key, out NpgsqlDbType value)) + { + result.Type = value; + } + else if (key?.First() == '_') + { + var type = PgSqlType[key.Substring(1)]; + result.Type = NpgsqlDbType.Array | type; + } + else + { + result.Type = null; + } + columnViews.Add(result); + } + using (var writer = conn.BeginBinaryImport(copyString)) + { + foreach (DataRow row in dt.Rows) + { + writer.StartRow(); + foreach (var column in columnViews) + { + var value = row[column.DataColumn.ColumnName]; + if (value == null) + { + value = DBNull.Value; + } + if (column.Type == null) + { + writer.Write(value); + } + else + { + writer.Write(value); + } + } + } + writer.Complete(); + } + } + + + public override async Task UpdateByTempAsync(string tableName, string tempName, string[] updateColumns, string[] whereColumns) + { + var sqlquerybulder = this.Context.Queryable().SqlBuilder; + Check.ArgumentNullException(updateColumns.Length == 0, "update columns count is 0"); + Check.ArgumentNullException(whereColumns.Length == 0, "where columns count is 0"); + var sets = string.Join(",", updateColumns.Select(it => $"{sqlquerybulder.GetTranslationColumnName(it)}=TE.{sqlquerybulder.GetTranslationColumnName(it)}")); + var wheres = string.Join(" AND ", whereColumns.Select(it => $"{tableName}.{sqlquerybulder.GetTranslationColumnName(it)}=TE.{sqlquerybulder.GetTranslationColumnName(it)}")); + string sql = string.Format(UpdateSql, sets, tableName, tempName, wheres); + return await Context.Ado.ExecuteCommandAsync(sql).ConfigureAwait(false); + } + public override async Task CreateTempAsync(DataTable dt) + { + await Context.Queryable().Where(it => false).AS(dt.TableName).Select(" * into temp mytemptable").ToListAsync().ConfigureAwait(false); + dt.TableName = "mytemptable"; + } + + public class ColumnView + { + public DataColumn DataColumn { get; set; } + public EntityColumnInfo EntityColumnInfo { get; set; } + public DbColumnInfo DbColumnInfo { get; set; } + public NpgsqlDbType? Type { get; set; } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Kdbndp/SqlBuilder/KdbndpInsertBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Kdbndp/SqlBuilder/KdbndpInsertBuilder.cs new file mode 100644 index 000000000..a87493064 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Kdbndp/SqlBuilder/KdbndpInsertBuilder.cs @@ -0,0 +1,131 @@ +using System.Text; + +namespace SqlSugar +{ + public class KdbndpInsertBuilder : InsertBuilder + { + public override string SqlTemplate + { + get + { + if (IsReturnIdentity) + { + return @"INSERT INTO {0} + ({1}) + VALUES + ({2}) returning $PrimaryKey"; + } + else + { + return @"INSERT INTO {0} + ({1}) + VALUES + ({2}) ;"; + + } + } + } + public override string SqlTemplateBatch => "INSERT INTO {0} ({1})"; + public override string SqlTemplateBatchUnion => " VALUES "; + + public override string SqlTemplateBatchSelect => " {0} "; + + public override string ToSqlString() + { + if (IsNoInsertNull) + { + DbColumnInfoList = DbColumnInfoList.Where(it => it.Value != null).ToList(); + } + var groupList = DbColumnInfoList.GroupBy(it => it.TableId).ToList(); + var isSingle = groupList.Count == 1; + string columnsString = string.Join(",", groupList.First().Select(it => Builder.GetTranslationColumnName(it.DbColumnName))); + if (isSingle) + { + string columnParametersString = string.Join(",", this.DbColumnInfoList.Select(it => base.GetDbColumn(it, Builder.SqlParameterKeyWord + it.DbColumnName))); + return string.Format(SqlTemplate, GetTableNameString, columnsString, columnParametersString); + } + else + { + StringBuilder batchInsetrSql = new StringBuilder(); + int pageSize = 200; + int pageIndex = 1; + int totalRecord = groupList.Count; + int pageCount = (totalRecord + pageSize - 1) / pageSize; + while (pageCount >= pageIndex) + { + batchInsetrSql.AppendFormat(SqlTemplateBatch, GetTableNameString, columnsString); + int i = 0; + foreach (var columns in groupList.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList()) + { + var isFirst = i == 0; + if (isFirst) + { + batchInsetrSql.Append(SqlTemplateBatchUnion); + } + batchInsetrSql.Append("\r\n ( " + string.Join(",", columns.Select(it => + { + if (it.InsertServerTime || it.InsertSql.HasValue() || it.SqlParameterDbType is Type || it?.PropertyType?.Name == "DateOnly" || it?.PropertyType?.Name == "TimeOnly") + { + return GetDbColumn(it, null); + } + object value = null; + if (it.Value is DateTime) + { + if (IsSqlServerModel()) + { + value = ((DateTime)it.Value).ToString("yyyy-MM-dd HH:mm:ss.fff"); + } + else + { + value = ((DateTime)it.Value).ToString("O"); + } + } + else if (it.Value is DateTimeOffset) + { + return FormatDateTimeOffset(it.Value); + } + else if (IsSqlOracleModel() && it.Value is TimeSpan timeSpan) + { + return $"'{timeSpan.Days} {timeSpan.Hours:00}:{timeSpan.Minutes:00}:{timeSpan.Seconds:00}.{timeSpan.Milliseconds:000}{timeSpan.Ticks % 10000:0000}'"; + } + else if (it.Value is bool && (IsMySqlModel() || IsSqlServerModel())) + { + return Convert.ToBoolean(it.Value) ? "1" : "0"; + } + else + { + value = it.Value; + } + if (value == null || value == DBNull.Value) + { + return string.Format(SqlTemplateBatchSelect, "NULL"); + } + return string.Format(SqlTemplateBatchSelect, "'" + value.ObjToString().ToSqlFilter() + "'"); + })) + "),"); + ++i; + } + pageIndex++; + batchInsetrSql.Remove(batchInsetrSql.Length - 1, 1).Append("\r\n;\r\n"); + } + return batchInsetrSql.ToString(); + } + } + private bool IsSqlOracleModel() + { + return this.Context?.CurrentConnectionConfig?.MoreSettings?.DatabaseModel == DbType.Oracle; + } + private bool IsSqlServerModel() + { + return this.Context?.CurrentConnectionConfig?.MoreSettings?.DatabaseModel == DbType.SqlServer; + } + private bool IsMySqlModel() + { + return this.Context?.CurrentConnectionConfig?.MoreSettings?.DatabaseModel == DbType.MySql; + } + public override string FormatDateTimeOffset(object value) + { + var date = UtilMethods.ConvertFromDateTimeOffset((DateTimeOffset)value); + return "'" + date.ToString("yyyy-MM-dd HH:mm:ss.fff zzz") + "'"; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Kdbndp/SqlBuilder/KdbndpQueryBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Kdbndp/SqlBuilder/KdbndpQueryBuilder.cs new file mode 100644 index 000000000..910004876 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Kdbndp/SqlBuilder/KdbndpQueryBuilder.cs @@ -0,0 +1,149 @@ +using System.Text; +using System.Text.RegularExpressions; + +namespace SqlSugar +{ + public partial class KdbndpQueryBuilder : QueryBuilder + { + #region Sql Template + public override string SqlTemplate + { + get + { + if (this.PartitionByValue.HasValue()) + { + return "SELECT {0}{" + UtilConstants.ReplaceKey + "} FROM {1}{2}{3}{4}"; + } + return base.SqlTemplate; + } + } + public override string PageTempalte + { + get + { + /* + SELECT * FROM TABLE WHERE CONDITION ORDER BY ID DESC LIMIT 10 offset 0 + */ + if (this.PartitionByValue.HasValue()) + { + return base.PageTempalte; + } + var template = "SELECT {0} FROM {1} {2} {3} {4} LIMIT {6} offset {5}"; + return template; + } + } + public override string DefaultOrderByTemplate + { + get + { + return "ORDER BY NOW() "; + } + } + + #endregion + + #region Common Methods + public override bool IsComplexModel(string sql) + { + return Regex.IsMatch(sql, @"AS ""\w+\.\w+""") || Regex.IsMatch(sql, @"AS ""\w+\.\w+\.\w+"""); + } + public override string ToSqlString() + { + if (PartitionByValue.HasValue()) + { + if (this.Context?.CurrentConnectionConfig?.MoreSettings?.DatabaseModel == DbType.SqlServer) + { + return base.ToSqlString(); + } + else + { + return base.ToSqlString().Replace(" GetDate() ", " NOW() "); + } + } + base.AppendFilter(); + string oldOrderValue = this.OrderByValue; + var isNullOrderValue = Skip == 0 && Take == 1 && oldOrderValue == "ORDER BY NOW() "; + if (isNullOrderValue) + { + this.OrderByValue = null; + } + string result = null; + sql = new StringBuilder(); + sql.AppendFormat(SqlTemplate, GetSelectValue, GetTableNameString, GetWhereValueString, GetGroupByString + HavingInfos, (Skip != null || Take != null) ? null : GetOrderByString); + if (IsCount) { return sql.ToString(); } + if (Skip != null && Take == null) + { + if (this.OrderByValue == "ORDER BY ") this.OrderByValue += GetSelectValue.Split(',')[0]; + result = string.Format(PageTempalte, GetSelectValue, GetTableNameString, GetWhereValueString, GetGroupByString + HavingInfos, (Skip != null || Take != null) ? null : GetOrderByString, Skip.ObjToInt(), long.MaxValue); + } + else if (Skip == null && Take != null) + { + if (this.OrderByValue == "ORDER BY ") this.OrderByValue += GetSelectValue.Split(',')[0]; + result = string.Format(PageTempalte, GetSelectValue, GetTableNameString, GetWhereValueString, GetGroupByString + HavingInfos, GetOrderByString, 0, Take.ObjToInt()); + } + else if (Skip != null && Take != null) + { + if (this.OrderByValue == "ORDER BY ") this.OrderByValue += GetSelectValue.Split(',')[0]; + result = string.Format(PageTempalte, GetSelectValue, GetTableNameString, GetWhereValueString, GetGroupByString + HavingInfos, GetOrderByString, Skip.ObjToInt() > 0 ? Skip.ObjToInt() : 0, Take); + } + else + { + result = sql.ToString(); + } + this.OrderByValue = oldOrderValue; + result = GetSqlQuerySql(result); + if (result.IndexOf("-- No table") > 0) + { + return "-- No table"; + } + if (TranLock != null) + { + result = result + TranLock; + } + //if (result.Contains("uuid_generate_v4()")) + //{ + // result = " CREATE EXTENSION IF NOT EXISTS kbcrypto;CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\"; " + result; + //} + return result; + } + + #endregion + + #region Get SQL Partial + public override string GetExternalOrderBy(string externalOrderBy) + { + return Regex.Replace(externalOrderBy, @"""\w+""\.", ""); + } + + public override string GetSelectValue + { + get + { + string result = string.Empty; + if (this.SelectValue == null || this.SelectValue is string) + { + result = GetSelectValueByString(); + } + else + { + result = GetSelectValueByExpression(); + } + if (this.SelectType == ResolveExpressType.SelectMultiple) + { + this.SelectCacheKey = this.SelectCacheKey + string.Join("-", this.JoinQueryInfos.Select(it => it.TableName)); + } + if (IsDistinct) + { + result = " DISTINCT " + result; + } + if (this.SubToListParameters != null && this.SubToListParameters.Count != 0) + { + result = SubToListMethod(result); + } + return result; + } + } + + #endregion + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Kdbndp/SqlBuilder/KdbndpUpdateBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Kdbndp/SqlBuilder/KdbndpUpdateBuilder.cs new file mode 100644 index 000000000..d83d68283 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Kdbndp/SqlBuilder/KdbndpUpdateBuilder.cs @@ -0,0 +1,281 @@ +using System.Text; + +namespace SqlSugar +{ + public class KdbndpUpdateBuilder : UpdateBuilder + { + public override string SqlTemplateBatch + { + get + { + return @"UPDATE {1} {2} SET {0} FROM ${{0}} "; + } + } + public override string SqlTemplateJoin + { + get + { + return @" (VALUES + {0} + + ) AS T ({2}) WHERE {1} + "; + } + } + + public override string SqlTemplateBatchUnion + { + get + { + return ","; + } + } + + public override object FormatValue(object value) + { + if (value == null) + { + return "NULL"; + } + else + { + var type = UtilMethods.GetUnderType(value.GetType()); + if (type == UtilConstants.DateType) + { + var date = value.ObjToDate(); + if (date < Convert.ToDateTime("1900-1-1")) + { + date = Convert.ToDateTime("1900-1-1"); + } + return "'" + date.ToString("yyyy-MM-dd HH:mm:ss.fff") + "'"; + } + else if (type == UtilConstants.DateTimeOffsetType) + { + return FormatDateTimeOffset(value); + } + else if (type == UtilConstants.ByteArrayType) + { + string bytesString = "0x" + BitConverter.ToString((byte[])value); + return bytesString; + } + else if (type.IsEnum()) + { + return Convert.ToInt64(value); + } + else if (type == UtilConstants.BoolType) + { + return value.ObjToBool() ? "1" : "0"; + } + else if (value is DateTimeOffset) + { + return FormatDateTimeOffset(value); + } + else if (IsSqlOracleModel() && value is TimeSpan timeSpan) + { + return $"'{timeSpan.Days} {timeSpan.Hours:00}:{timeSpan.Minutes:00}:{timeSpan.Seconds:00}.{timeSpan.Milliseconds:000}{timeSpan.Ticks % 10000:0000}'"; + } + else if (type == UtilConstants.StringType || type == UtilConstants.ObjType) + { + return "'" + value.ToString().ToSqlFilter() + "'"; + } + else + { + return "'" + value.ToString() + "'"; + } + } + } + + protected override string TomultipleSqlString(List> groupList) + { + Check.Exception(PrimaryKeys == null || PrimaryKeys.Count == 0, " Update List need Primary key"); + int pageSize = 200; + int pageIndex = 1; + int totalRecord = groupList.Count; + int pageCount = (totalRecord + pageSize - 1) / pageSize; + StringBuilder batchUpdateSql = new StringBuilder(); + while (pageCount >= pageIndex) + { + StringBuilder updateTable = new StringBuilder(); + string setValues = string.Join(",", groupList.First().Where(it => it.IsPrimarykey == false && (it.IsIdentity == false || (IsOffIdentity && it.IsIdentity))).Select(it => + { + if (SetValues.IsValuable()) + { + var setValue = SetValues.Where(sv => sv.Key == Builder.GetTranslationColumnName(it.DbColumnName)); + if (setValue?.Any() == true) + { + return setValue.First().Value; + } + } + var result = string.Format("{0}=T.{0}", Builder.GetTranslationColumnName(it.DbColumnName)); + return result; + })); + string tempColumnValue = string.Join(",", groupList.First().Select(it => + { + if (SetValues.IsValuable()) + { + var setValue = SetValues.Where(sv => sv.Key == Builder.GetTranslationColumnName(it.DbColumnName)); + if (setValue?.Any() == true) + { + return setValue.First().Value; + } + } + var result = Builder.GetTranslationColumnName(it.DbColumnName); + return result; + })); + batchUpdateSql.AppendFormat(SqlTemplateBatch.ToString(), setValues, GetTableNameStringNoWith, TableWithString); + int i = 0; + var tableColumnList = this.Context.DbMaintenance.GetColumnInfosByTableName(GetTableNameStringNoWith); + + foreach (var columns in groupList.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList()) + { + var isFirst = i == 0; + if (!isFirst) + { + updateTable.Append(SqlTemplateBatchUnion); + } + updateTable.Append("\r\n (" + string.Join(",", columns.Select(it => + { + var columnInfo = tableColumnList.FirstOrDefault(x => x.DbColumnName.Equals(it.DbColumnName, StringComparison.OrdinalIgnoreCase)); + var dbType = columnInfo?.DataType; + if (dbType == null) + { + var typeName = it.PropertyType.Name.ToLower(); + if (typeName == "int32") + typeName = "int"; + if (typeName == "int64") + typeName = "long"; + if (typeName == "int16") + typeName = "short"; + if (typeName == "boolean") + typeName = "bool"; + + var isAnyType = PostgreSQLDbBind.MappingTypesConst.Where(x => x.Value.ToString().Equals(typeName, StringComparison.CurrentCultureIgnoreCase)).Any(); + if (isAnyType) + { + dbType = PostgreSQLDbBind.MappingTypesConst.Where(x => x.Value.ToString().Equals(typeName, StringComparison.CurrentCultureIgnoreCase)).FirstOrDefault().Key; + } + else + { + dbType = "varchar"; + } + } + if (IsMySqlModel()) + { + if (dbType == "numeric") + { + dbType = "numeric(18,6)"; + } + } + if (IsSqlServerModel()) + { + if (dbType == "numeric") + { + dbType = "numeric(18,6)"; + } + else if (dbType == "varchar") + { + dbType = "varchar(max)"; + } + else if (dbType == "nvarchar") + { + dbType = "nvarchar(max)"; + } + else if (dbType == "char") + { + dbType = "char(8000)"; + } + else if (dbType == "nchar") + { + dbType = "nchar(4000)"; + } + } + return string.Format("CAST({0} AS {1})", base.GetDbColumn(it, FormatValue(it.Value)), dbType); + + })) + ")"); + ++i; + } + pageIndex++; + updateTable.Append("\r\n"); + string whereString = null; + if (this.WhereValues.HasValue()) + { + foreach (var item in WhereValues) + { + var isFirst = whereString == null; + whereString += (isFirst ? null : " AND "); + whereString += item.Replace(" \"", $" {Builder.GetTranslationColumnName(this.EntityInfo.DbTableName)}.\""); + } + } + if (PrimaryKeys.HasValue()) + { + foreach (var item in PrimaryKeys) + { + var isFirst = whereString == null; + whereString += (isFirst ? null : " AND "); + whereString += string.Format("{0}.{1}=T.{1}", GetTableNameStringNoWith, Builder.GetTranslationColumnName(item)); + } + } + var format = string.Format(SqlTemplateJoin, updateTable, whereString, tempColumnValue); + batchUpdateSql.Replace("${0}", format); + batchUpdateSql.Append(';'); + } + batchUpdateSql = GetBatchUpdateSql(batchUpdateSql); + return batchUpdateSql.ToString(); + } + + private bool IsSqlServerModel() + { + return this.Context?.CurrentConnectionConfig?.MoreSettings?.DatabaseModel == DbType.SqlServer; + } + + private bool IsMySqlModel() + { + return this.Context?.CurrentConnectionConfig?.MoreSettings?.DatabaseModel == DbType.MySql; + } + + private StringBuilder GetBatchUpdateSql(StringBuilder batchUpdateSql) + { + if (ReSetValueBySqlExpListType == null && ReSetValueBySqlExpList != null) + { + var result = batchUpdateSql.ToString(); + foreach (var item in ReSetValueBySqlExpList) + { + var dbColumnName = item.Value.DbColumnName; + if (item.Value.Type == ReSetValueBySqlExpListModelType.List) + { + result = result.Replace($"{dbColumnName}=T.{dbColumnName}", $"{dbColumnName}={GetTableNameString}.{dbColumnName}{item.Value.Sql}T.{dbColumnName}"); + } + else + { + result = result.Replace($"{dbColumnName}=T.{dbColumnName}", $"{dbColumnName}={item.Value.Sql.Replace(dbColumnName, $"{Builder.GetTranslationColumnName(this.TableName)}.{dbColumnName}")}"); + } + batchUpdateSql = new StringBuilder(result); + } + } + + return batchUpdateSql; + } + protected override string GetJoinUpdate(string columnsString, ref string whereString) + { + var formString = $" {Builder.GetTranslationColumnName(this.TableName)} AS {Builder.GetTranslationColumnName(this.ShortName)} "; + var joinString = ""; + foreach (var item in this.JoinInfos) + { + whereString += " AND " + item.JoinWhere; + joinString += $"\r\n FROM {Builder.GetTranslationColumnName(item.TableName)} {Builder.GetTranslationColumnName(item.ShortName)} "; + } + var tableName = formString + "\r\n "; + columnsString = columnsString.Replace(Builder.GetTranslationColumnName(this.ShortName) + ".", "") + joinString; + return string.Format(SqlTemplate, tableName, columnsString, whereString); + } + public override string FormatDateTimeOffset(object value) + { + var date = UtilMethods.ConvertFromDateTimeOffset((DateTimeOffset)value); + return "'" + date.ToString("yyyy-MM-dd HH:mm:ss.fff zzz") + "'"; + } + private bool IsSqlOracleModel() + { + return this.Context?.CurrentConnectionConfig?.MoreSettings?.DatabaseModel == DbType.Oracle; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/CodeFirst/MySqlCodeFirst.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/CodeFirst/MySqlCodeFirst.cs new file mode 100644 index 000000000..c63220f08 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/CodeFirst/MySqlCodeFirst.cs @@ -0,0 +1,140 @@ +namespace SqlSugar +{ + public class MySqlCodeFirst : CodeFirstProvider + { + public override void NoExistLogic(EntityInfo entityInfo) + { + var tableName = GetTableName(entityInfo); + //Check.Exception(entityInfo.Columns.Where(it => it.IsPrimarykey).Count() > 1, "Use Code First ,The primary key must not exceed 1"); + List columns = new List(); + if (entityInfo.Columns.HasValue()) + { + foreach (var item in entityInfo.Columns.OrderBy(it => it.IsPrimarykey ? 0 : 1)) + { + if (item.IsIgnore) + continue; + if (item.PropertyInfo != null && UtilMethods.GetUnderType(item.PropertyInfo.PropertyType) == UtilConstants.GuidType && item.Length == 0 && item.DataType == null) + { + item.Length = Guid.NewGuid().ToString().Length; + } + DbColumnInfo dbColumnInfo = this.EntityColumnToDbColumn(entityInfo, tableName, item); + columns.Add(dbColumnInfo); + } + if (entityInfo.IsCreateTableFiledSort) + { + columns = columns.OrderBy(c => c.CreateTableFieldSort).ToList(); + columns = columns.OrderBy(it => it.IsPrimarykey ? 0 : 1).ToList(); + } + } + this.Context.DbMaintenance.CreateTable(tableName, columns, true); + } + protected override DbColumnInfo EntityColumnToDbColumn(EntityInfo entityInfo, string tableName, EntityColumnInfo item) + { + var propertyType = UtilMethods.GetUnderType(item.PropertyInfo); + var result = new DbColumnInfo() + { + TableId = entityInfo.Columns.IndexOf(item), + DbColumnName = item.DbColumnName.HasValue() ? item.DbColumnName : item.PropertyName, + IsPrimarykey = item.IsPrimarykey, + IsIdentity = item.IsIdentity, + TableName = tableName, + IsNullable = item.IsNullable, + DefaultValue = item.DefaultValue, + ColumnDescription = item.ColumnDescription, + Length = item.Length, + DecimalDigits = item.DecimalDigits, + CreateTableFieldSort = item.CreateTableFieldSort + }; + GetDbType(item, propertyType, result); + if (result.DataType.Equals("varchar", StringComparison.CurrentCultureIgnoreCase) && result.Length == 0) + { + result.Length = 1; + } + return result; + } + + protected override void ConvertColumns(List dbColumns) + { + foreach (var item in dbColumns) + { + if (item.DataType == "DateTime") + { + item.Length = 0; + } + } + } + + protected override void ChangeKey(EntityInfo entityInfo, string tableName, EntityColumnInfo item) + { + this.Context.DbMaintenance.UpdateColumn(tableName, EntityColumnToDbColumn(entityInfo, tableName, item)); + if (!item.IsPrimarykey) + this.Context.DbMaintenance.DropConstraint(tableName, null); + if (item.IsPrimarykey) + this.Context.DbMaintenance.AddPrimaryKey(tableName, item.DbColumnName); + } + + internal DbColumnInfo GetEntityColumnToDbColumn(EntityInfo entity, string dbTableName, EntityColumnInfo item) + { + return EntityColumnToDbColumn(entity, dbTableName, item); + } + + protected override void GetDbType(EntityColumnInfo item, Type propertyType, DbColumnInfo result) + { + if (!string.IsNullOrEmpty(item.DataType)) + { + result.DataType = item.DataType; + } + else if (propertyType.IsEnum()) + { + result.DataType = this.Context.Ado.DbBind.GetDbTypeName(item.Length > 9 ? UtilConstants.LongType.Name : UtilConstants.IntType.Name); + } + else if (item.IsJson && item.DataType == null) + { + result.DataType = "json"; + } + else if (propertyType == UtilConstants.DecType && item.Length == 0 && item.DecimalDigits == 0) + { + result.Length = 18; + result.DecimalDigits = 4; + result.DataType = "decimal"; + } + else + { + var name = GetType(propertyType.Name); + if (name == "Boolean") + { + result.DataType = "tinyint"; + result.Length = 1; + result.Scale = 0; + result.DecimalDigits = 0; + } + else + { + result.DataType = this.Context.Ado.DbBind.GetDbTypeName(name); + if (name == "Guid" && result.DataType == "varchar" && result.Length <= 1) + { + result.Length = 36; + } + } + } + } + + + protected override bool IsNoSamgeType(EntityColumnInfo ec, DbColumnInfo dc) + { + if (ec.UnderType == UtilConstants.BoolType && dc.DataType == "tinyint" && dc.Length == 1) + { + return false; + } + else if (ec.UnderType == UtilConstants.DobType && dc.DataType == "double") + { + return false; + } + else if (ec.UnderType == UtilConstants.DateType && dc.DataType?.StartsWith("datetime") == true) + { + return false; + } + return base.IsNoSamgeType(ec, dc); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/DbBind/MySqlDbBind.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/DbBind/MySqlDbBind.cs new file mode 100644 index 000000000..2aab9f37d --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/DbBind/MySqlDbBind.cs @@ -0,0 +1,94 @@ +namespace SqlSugar +{ + public class MySqlDbBind : DbBindProvider + { + public override string GetDbTypeName(string csharpTypeName) + { + if (csharpTypeName == UtilConstants.ByteArrayType.Name) + { + return "longblob"; + } + if (csharpTypeName == "Int32") + csharpTypeName = "int"; + if (csharpTypeName == "Int16") + csharpTypeName = "short"; + if (csharpTypeName == "Int64") + csharpTypeName = "long"; + if (csharpTypeName == "Boolean") + csharpTypeName = "bool"; + if (csharpTypeName == "SByte") + csharpTypeName = "Byte"; + if (csharpTypeName == "DateTimeOffset") + csharpTypeName = "DateTime"; + var mappings = this.MappingTypes.Where(it => it.Value.ToString().Equals(csharpTypeName, StringComparison.CurrentCultureIgnoreCase)); + return (mappings?.Any() == true) ? mappings.First().Key : "varchar"; + } + public override List> MappingTypes + { + get + { + var extService = this.Context.CurrentConnectionConfig.ConfigureExternalServices; + if (extService?.AppendDataReaderTypeMappings.HasValue() == true) + { + return extService.AppendDataReaderTypeMappings.Union(MappingTypesConst).ToList(); + } + else + { + return MappingTypesConst; + } + } + } + public static List> MappingTypesConst = new List>(){ + + new KeyValuePair("int",CSharpDataType.@int), + new KeyValuePair("mediumint",CSharpDataType.@int), + new KeyValuePair("integer",CSharpDataType.@int), + new KeyValuePair("int unsigned",CSharpDataType.@int), + + new KeyValuePair("varchar",CSharpDataType.@string), + new KeyValuePair("text",CSharpDataType.@string), + new KeyValuePair("char",CSharpDataType.@string), + new KeyValuePair("enum",CSharpDataType.@string), + new KeyValuePair("mediumtext",CSharpDataType.@string), + new KeyValuePair("tinytext",CSharpDataType.@string), + new KeyValuePair("longtext",CSharpDataType.@string), + + new KeyValuePair("tinyint",CSharpDataType.@byte), + new KeyValuePair("smallint",CSharpDataType.@short), + new KeyValuePair("bigint",CSharpDataType.@long), + new KeyValuePair("bit",CSharpDataType.@bool), + new KeyValuePair("real",CSharpDataType.@double), + new KeyValuePair("double",CSharpDataType.@double), + new KeyValuePair("float",CSharpDataType.@float), + new KeyValuePair("float",CSharpDataType.Single), + new KeyValuePair("decimal",CSharpDataType.@decimal), + new KeyValuePair("numeric",CSharpDataType.@decimal), + new KeyValuePair("year",CSharpDataType.@int), + + new KeyValuePair("datetime",CSharpDataType.DateTime), + new KeyValuePair("timestamp",CSharpDataType.DateTime), + new KeyValuePair("date",CSharpDataType.DateTime), + new KeyValuePair("time",CSharpDataType.DateTime), + + new KeyValuePair("blob",CSharpDataType.byteArray), + new KeyValuePair("longblob",CSharpDataType.byteArray), + new KeyValuePair("tinyblob",CSharpDataType.byteArray), + new KeyValuePair("varbinary",CSharpDataType.byteArray), + new KeyValuePair("binary",CSharpDataType.byteArray), + new KeyValuePair("multipoint",CSharpDataType.byteArray), + new KeyValuePair("geometry",CSharpDataType.byteArray), + new KeyValuePair("multilinestring",CSharpDataType.byteArray), + new KeyValuePair("polygon",CSharpDataType.byteArray), + new KeyValuePair("mediumblob",CSharpDataType.byteArray), + + new KeyValuePair("varchar",CSharpDataType.Guid), + }; + public override List StringThrow + { + get + { + return new List() { "int32", "datetime", "decimal", "double", "byte" }; + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/DbFirst/MySqlDbFirst.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/DbFirst/MySqlDbFirst.cs new file mode 100644 index 000000000..17c3ebc3f --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/DbFirst/MySqlDbFirst.cs @@ -0,0 +1,30 @@ +namespace SqlSugar +{ + public class MySqlDbFirst : DbFirstProvider + { + protected override string GetPropertyTypeName(DbColumnInfo item) + { + if (item.DataType == "tinyint" && item.Length == 1 && Context.CurrentConnectionConfig.ConnectionString.Contains("treattinyasboolea", StringComparison.CurrentCultureIgnoreCase) == false) + { + item.DataType = "bit"; + item.DefaultValue = "true"; + return "bool"; + } + if (item.DataType == "mediumint") + { + item.DataType = "int"; + return "int"; + } + if (item.DataType == "mediumint unsigned") + { + item.DataType = "mediumint unsigned"; + return "uint"; + } + if (item.DataType == "double unsigned") + { + return "double"; + } + return base.GetPropertyTypeName(item); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/DbMaintenance/DorisHelper.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/DbMaintenance/DorisHelper.cs new file mode 100644 index 000000000..c6fe07330 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/DbMaintenance/DorisHelper.cs @@ -0,0 +1,54 @@ +namespace SqlSugar +{ + internal static class DorisHelper + { + + public static void UpdateDateParameter(MySqlConnector.MySqlParameter sqlParameter) + { + if (sqlParameter.DbType == System.Data.DbType.DateTime) + { + sqlParameter.DbType = System.Data.DbType.String; + sqlParameter.Value = sqlParameter.Value.ObjToDate().ToString("yyyy-MM-dd HH:mm:ss.fff"); + } + if (sqlParameter.DbType == System.Data.DbType.DateTimeOffset) + { + sqlParameter.DbType = System.Data.DbType.String; + sqlParameter.Value = sqlParameter.Value.ObjToDate().ToString("yyyy-MM-dd HH:mm:ss.fff"); + } + } + public static List GetColumns(List colums) + { + foreach (var item in colums) + { + if (item.DataType?.Contains('(') == true) + { + item.DataType = item.DataType.Substring(0, item.DataType.IndexOf('(')); + } + if (item.DataType == "tinyint" && item.Length == 4) + { + item.Length = 1; + } + } + return colums; + } + + public static bool IsDoris(ISqlSugarClient context) + { + return context.CurrentConnectionConfig?.MoreSettings?.DatabaseModel == DbType.Doris; + } + + public static string UpdateDorisSql(ISqlBuilder sqlBuilder, List columns, string sql) + { + var pk = columns.FirstOrDefault(it => it.IsPrimarykey); + if (pk != null) + { + Check.ExceptionEasy(columns.Where(it => it.IsIdentity).Count() > 1, "Doris identity key no supported", "Doris不支持自增"); + Check.ExceptionEasy(columns.Where(it => it.IsPrimarykey).Count() > 1, "Doris Only one primary key is supported", "Doris只支持单主键"); + sql = sql.Replace("$PrimaryKey)", ")"); + var pkName = sqlBuilder.GetTranslationColumnName(pk.DbColumnName); + sql += " \r\nENGINE=OLAP\r\nUNIQUE KEY(" + pkName + ")\r\nDISTRIBUTED BY HASH(" + pkName + ") BUCKETS 1\r\nPROPERTIES (\r\n 'replication_num' = '1',\r\n 'storage_format' = 'DEFAULT'\r\n);\r\n\r\n"; + } + return sql; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/DbMaintenance/MySqlDbMaintenance.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/DbMaintenance/MySqlDbMaintenance.cs new file mode 100644 index 000000000..8ea57d52f --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/DbMaintenance/MySqlDbMaintenance.cs @@ -0,0 +1,810 @@ +using System.Data; +using System.Reflection; +using System.Text.RegularExpressions; + +namespace SqlSugar +{ + public class MySqlDbMaintenance : DbMaintenanceProvider + { + #region DML + protected override string GetDataBaseSql + { + get + { + return "SHOW DATABASES"; + } + } + protected override string GetColumnInfosByTableNameSql + { + get + { + string sql = @"SELECT + 0 as TableId, + TABLE_NAME as TableName, + column_name AS DbColumnName, + CASE WHEN left(COLUMN_TYPE,LOCATE('(',COLUMN_TYPE)-1)='' THEN COLUMN_TYPE ELSE left(COLUMN_TYPE,LOCATE('(',COLUMN_TYPE)-1) END AS DataType, + CAST(SUBSTRING(COLUMN_TYPE,LOCATE('(',COLUMN_TYPE)+1,LOCATE(')',COLUMN_TYPE)-LOCATE('(',COLUMN_TYPE)-1) AS decimal(18,0) ) AS Length, + column_default AS `DefaultValue`, + column_comment AS `ColumnDescription`, + CASE WHEN COLUMN_KEY = 'PRI' + THEN true ELSE false END AS `IsPrimaryKey`, + CASE WHEN EXTRA='auto_increment' THEN true ELSE false END as IsIdentity, + CASE WHEN is_nullable = 'YES' + THEN true ELSE false END AS `IsNullable`, + numeric_scale as Scale, + numeric_scale as DecimalDigits, + LOCATE( 'unsigned',COLUMN_type ) >0 as IsUnsigned + FROM + Information_schema.columns where TABLE_NAME='{0}' and TABLE_SCHEMA=(select database()) ORDER BY ordinal_position"; + if (DorisHelper.IsDoris(this.Context)) + { + sql = sql.Replace("CASE WHEN left(COLUMN_TYPE,LOCATE('(',COLUMN_TYPE)-1)='' THEN COLUMN_TYPE ELSE left(COLUMN_TYPE,LOCATE('(',COLUMN_TYPE)-1) END AS DataType", "COLUMN_TYPE AS DataType "); + } + return sql; + } + } + protected override string GetTableInfoListSql + { + get + { + return @"select TABLE_NAME as Name,TABLE_COMMENT as Description from information_schema.tables + where TABLE_SCHEMA=(select database()) AND TABLE_TYPE='BASE TABLE'"; + } + } + protected override string GetViewInfoListSql + { + get + { + return @"select TABLE_NAME as Name,TABLE_COMMENT as Description from information_schema.tables + where TABLE_SCHEMA=(select database()) AND TABLE_TYPE='VIEW' + "; + } + } + #endregion + + #region DDL + protected override string CreateDataBaseSql + { + get + { + return "CREATE DATABASE `{0}` CHARACTER SET utf8 COLLATE utf8_general_ci "; + } + } + protected override string AddPrimaryKeySql + { + get + { + return "ALTER TABLE {0} ADD PRIMARY KEY({2}) /*{1}*/"; + } + } + protected override string AddColumnToTableSql + { + get + { + return "ALTER TABLE {0} ADD {1} {2}{3} {4} {5} {6}"; + } + } + protected override string AlterColumnToTableSql + { + get + { + // return "ALTER TABLE {0} ALTER COLUMN {1} {2}{3} {4} {5} {6}"; + return "alter table {0} change column {1} {1} {2}{3} {4} {5} {6}"; + } + } + protected override string BackupDataBaseSql + { + get + { + return "mysqldump.exe {0} -uroot -p > {1} "; + } + } + protected override string CreateTableSql + { + get + { + return "CREATE TABLE {0}(\r\n{1} $PrimaryKey)"; + } + } + protected override string CreateTableColumn + { + get + { + return "{0} {1}{2} {3} {4} {5}"; + } + } + protected override string TruncateTableSql + { + get + { + return "TRUNCATE TABLE {0}"; + } + } + protected override string BackupTableSql + { + get + { + return "Create table {1} (Select * from {2} LIMIT 0,{0})"; + } + } + protected override string DropTableSql + { + get + { + return "DROP TABLE {0}"; + } + } + protected override string DropColumnToTableSql + { + get + { + return "ALTER TABLE {0} DROP COLUMN {1}"; + } + } + protected override string DropConstraintSql + { + get + { + return "ALTER TABLE {0} drop primary key;"; + } + } + protected override string RenameColumnSql + { + get + { + return "alter table {0} change column {1} {2}"; + } + } + protected override string IsAnyProcedureSql + { + get + { + return "select count(*) from information_schema.Routines where ROUTINE_NAME='{0}' and ROUTINE_TYPE='PROCEDURE'"; + } + } + #endregion + + #region Check + protected override string CheckSystemTablePermissionsSql + { + get + { + return "select 1 from Information_schema.columns where TABLE_SCHEMA=(select database()) limit 0,1"; + } + } + #endregion + + #region Scattered + protected override string CreateTableNull + { + get + { + return "DEFAULT NULL"; + } + } + protected override string CreateTableNotNull + { + get + { + return "NOT NULL"; + } + } + protected override string CreateTablePirmaryKey + { + get + { + return "PRIMARY KEY"; + } + } + protected override string CreateTableIdentity + { + get + { + return "AUTO_INCREMENT"; + } + } + + protected override string AddColumnRemarkSql + { + get + { + throw new NotSupportedException(); + } + } + + protected override string DeleteColumnRemarkSql + { + get + { + throw new NotSupportedException(); + } + } + + protected override string IsAnyColumnRemarkSql + { + get + { + throw new NotSupportedException(); + } + } + + protected override string AddTableRemarkSql + { + get + { + return "ALTER TABLE {0} COMMENT='{1}';"; + } + } + + protected override string DeleteTableRemarkSql + { + get + { + return "ALTER TABLE {0} COMMENT='';"; + } + } + + protected override string IsAnyTableRemarkSql + { + get + { + throw new NotSupportedException(); + } + } + + protected override string RenameTableSql + { + get + { + return "alter table {0} rename {1}"; + } + } + + protected override string CreateIndexSql + { + get + { + return "CREATE {3} INDEX `Index_{0}_{2}` ON `{0}` ({1})"; + } + } + + protected override string AddDefaultValueSql + { + get + { + return "ALTER TABLE `{0}` ALTER COLUMN `{1}` SET DEFAULT '{2}'"; + } + } + protected override string IsAnyIndexSql + { + get + { + return "SELECT count(*) FROM information_schema.statistics WHERE index_name = '{0}' and index_schema = '{1}'"; + } + } + #endregion + + #region Methods + public override bool DropIndex(string indexName, string tableName) + { + indexName = this.SqlBuilder.GetNoTranslationColumnName(indexName); + tableName = this.SqlBuilder.GetNoTranslationColumnName(tableName); + this.Context.Ado.ExecuteCommand($" DROP INDEX {indexName} ON {tableName}"); + return true; + } + public override List GetColumnInfosByTableName(string tableName, bool isCache = true) + { + if (DorisHelper.IsDoris(this.Context)) + { + var colums = base.GetColumnInfosByTableName(tableName, isCache); + colums = DorisHelper.GetColumns(colums); + return colums; + } + return base.GetColumnInfosByTableName(tableName, isCache); + } + + public override bool SetAutoIncrementInitialValue(string tableName, int initialValue) + { + initialValue++; + this.Context.Ado.ExecuteCommand($"ALTER TABLE " + this.SqlBuilder.GetTranslationColumnName(tableName) + " AUTO_INCREMENT = " + initialValue); + return true; + } + public override bool SetAutoIncrementInitialValue(Type entityType, int initialValue) + { + return this.SetAutoIncrementInitialValue(this.Context.EntityMaintenance.GetEntityInfo(entityType).DbTableName, initialValue); + } + public override List GetDbTypes() + { + return this.Context.Ado.SqlQuery(@"SELECT DISTINCT DATA_TYPE +FROM information_schema.COLUMNS"); + } + public override List GetTriggerNames(string tableName) + { + return this.Context.Ado.SqlQuery(@"SELECT TRIGGER_NAME +FROM INFORMATION_SCHEMA.TRIGGERS +WHERE EVENT_OBJECT_TABLE = '" + tableName + "'"); + } + public override List GetFuncList() + { + return this.Context.Ado.SqlQuery(" SELECT routine_name\r\nFROM information_schema.ROUTINES\r\nWHERE routine_schema = (SELECT DATABASE()) AND routine_type = 'FUNCTION'; "); + } + public override List GetIndexList(string tableName) + { + var sql = $"SHOW INDEX FROM {this.SqlBuilder.GetTranslationColumnName(tableName)}"; + return this.Context.Ado.GetDataTable(sql).AsEnumerable().Cast().Select(it => it["key_name"]).Cast().ToList(); + } + public override List GetProcList(string dbName) + { + var sql = $"SELECT ROUTINE_NAME FROM information_schema.ROUTINES WHERE ROUTINE_TYPE = 'PROCEDURE' AND ROUTINE_SCHEMA = '{dbName}'"; + return this.Context.Ado.SqlQuery(sql); + } + public override bool IsAnyTable(string tableName, bool isCache = true) + { + try + { + return base.IsAnyTable(tableName, isCache); + } + catch (Exception ex) + { + if (SugarCompatible.IsFramework && ex.Message == "Invalid attempt to Read when reader is closed.") + { + Check.ExceptionEasy($"To upgrade the MySql.Data. Error:{ex.Message}", $" 请先升级MySql.Data 。 详细错误:{ex.Message}"); + return true; + } + else + { + throw; + } + } + } + public override bool IsAnyColumnRemark(string columnName, string tableName) + { + var isAny = this.Context.DbMaintenance.GetColumnInfosByTableName(tableName, false) + .Any(it => it.ColumnDescription.HasValue() && it.DbColumnName.EqualCase(columnName)); + return isAny; + } + public override bool AddColumnRemark(string columnName, string tableName, string description) + { + + tableName = this.SqlBuilder.GetTranslationColumnName(tableName); + columnName = this.SqlBuilder.GetTranslationColumnName(columnName); + var sql = this.Context.Ado.GetDataTable($"SHOW CREATE TABLE {tableName};").Rows[0][1] + ""; + var columns = sql.Split('\n'); + var columnList = columns.Where(it => it.Last() == ',').ToList(); + + foreach (var column in columnList) + { + if (column.Contains(columnName)) + { + Regex regex = new Regex(" COMMENT .+$"); + var newcolumn = regex.Replace(column.TrimEnd(','), ""); + newcolumn += $" COMMENT '{description.ToSqlFilter()}' "; + var updateSql = $"ALTER TABLE {tableName} MODIFY COLUMN " + newcolumn.TrimEnd(','); + this.Context.Ado.ExecuteCommand(updateSql); + break; + } + } + ////base.AddColumnRemark(columnName, tableName, description); + //var message= @"db.DbMaintenance.UpdateColumn(""tablename"", new DbColumnInfo() + //{{ + // DataType = ""VARCHAR(30) NOT NULL COMMENT 'xxxxx'"", + // DbColumnName = ""columnname"" + //}})" ; + //Check.Exception(true,"MySql no support AddColumnRemark , use " + message); + return true; + } + /// + ///by current connection string + /// + /// + /// + /// + public override bool CreateDatabase(string databaseName, string databaseDirectory = null) + { + + if (this.Context.Ado.IsValidConnection() && this.Context.Ado.Connection.Database?.ToLower() == databaseName?.ToLower()) + { + return true; + } + + if (databaseDirectory != null) + { + if (!FileHelper.IsExistDirectory(databaseDirectory)) + { + FileHelper.CreateDirectory(databaseDirectory); + } + } + var oldDatabaseName = this.Context.Ado.Connection.Database; + var connection = this.Context.CurrentConnectionConfig.ConnectionString; + if (Regex.Split(connection, oldDatabaseName).Length > 2) + { + var name = Regex.Match(connection, @"database\=\w+|datasource\=\w+", RegexOptions.IgnoreCase).Value; + if (!string.IsNullOrEmpty(name)) + { + connection = connection.Replace(name, "database=mysql"); + } + else + { + Check.ExceptionEasy("Failed to create the database. The database name has a keyword. Please change the name", "建库失败,库名存在关键字,请换一个名字"); + } + } + else + { + connection = connection.Replace(oldDatabaseName, "mysql"); + } + var newDb = new SqlSugarClient(new ConnectionConfig() + { + DbType = this.Context.CurrentConnectionConfig.DbType, + IsAutoCloseConnection = true, + ConnectionString = connection + }); + if (!GetDataBaseList(newDb).Any(it => it.Equals(databaseName, StringComparison.CurrentCultureIgnoreCase))) + { + var createSql = CreateDataBaseSql; + if (ContainsCharSet("utf8mb4")) + { + createSql = createSql.Replace("utf8 COLLATE utf8_general_ci", "utf8mb4"); + } + if (!string.IsNullOrEmpty(StaticConfig.CodeFirst_MySqlCollate)) + { + if (createSql.Contains(" COLLATE ")) + { + createSql = $" {Regex.Split(createSql, " COLLATE ").First()} COLLATE {StaticConfig.CodeFirst_MySqlCollate} "; + } + else + { + createSql += $" COLLATE {StaticConfig.CodeFirst_MySqlCollate} "; + } + } + newDb.Ado.ExecuteCommand(string.Format(createSql, databaseName, databaseDirectory)); + } + return true; + } + public override bool AddTableRemark(string tableName, string description) + { + if (DorisHelper.IsDoris(this.Context)) + { + return false; + } + string sql = string.Format(this.AddTableRemarkSql, this.SqlBuilder.GetTranslationTableName(tableName), description); + this.Context.Ado.ExecuteCommand(sql); + return true; + } + public override bool CreateTable(string tableName, List columns, bool isCreatePrimaryKey = true) + { + if (columns.HasValue()) + { + foreach (var item in columns) + { + if (item.DbColumnName.Equals("GUID", StringComparison.CurrentCultureIgnoreCase) && item.Length == 0) + { + item.Length = 10; + } + } + } + string sql = GetCreateTableSql(tableName, columns); + string primaryKeyInfo = null; + if (columns.Any(it => it.IsPrimarykey) && isCreatePrimaryKey) + { + primaryKeyInfo = string.Format(", Primary key({0})", string.Join(",", columns.Where(it => it.IsPrimarykey).Select(it => this.SqlBuilder.GetTranslationColumnName(it.DbColumnName)))); + + } + if (DorisHelper.IsDoris(this.Context)) + { + sql = DorisHelper.UpdateDorisSql(this.SqlBuilder, columns, sql); + } + sql = sql.Replace("$PrimaryKey", primaryKeyInfo); + if (!string.IsNullOrEmpty(StaticConfig.CodeFirst_MySqlTableEngine)) + { + sql += " ENGINE = " + StaticConfig.CodeFirst_MySqlTableEngine; + } + this.Context.Ado.ExecuteCommand(sql); + return true; + } + public override bool AddRemark(EntityInfo entity) + { + var oldColumns = this.Context.DbMaintenance.GetColumnInfosByTableName(entity.DbTableName, false); + var db = this.Context; + db.DbMaintenance.AddTableRemark(entity.DbTableName, entity.TableDescription); + List columns = entity.Columns.Where(it => it.IsIgnore == false).ToList(); + foreach (var item in columns) + { + if (item.ColumnDescription != null) + { + var mySqlCodeFirst = this.Context.CodeFirst as MySqlCodeFirst; + if (item.UnderType == UtilConstants.GuidType && item.Length == 0) + { + item.Length = 36; + } + var columnInfo = oldColumns.FirstOrDefault(it => it.DbColumnName.EqualCase(item.DbColumnName)); + if (columnInfo?.ColumnDescription.ObjToString() != item.ColumnDescription.ObjToString()) + { + string sql = GetUpdateColumnSql(entity.DbTableName, mySqlCodeFirst.GetEntityColumnToDbColumn(entity, entity.DbTableName, item)) + " " + (item.IsIdentity ? "AUTO_INCREMENT" : "") + " " + " COMMENT '" + item.ColumnDescription + "'"; + db.Ado.ExecuteCommand(sql); + } + } + } + return true; + } + protected override string GetCreateTableSql(string tableName, List columns) + { + List columnArray = new List(); + Check.Exception(columns.IsNullOrEmpty(), "No columns found "); + foreach (var item in columns) + { + + ConvertCreateColumnInfo(item); + + string columnName = item.DbColumnName; + string dataSize = ""; + dataSize = GetSize(item); + string dataType = item.DataType; + string nullType = item.IsNullable ? this.CreateTableNull : CreateTableNotNull; + string primaryKey = null; + string identity = item.IsIdentity ? this.CreateTableIdentity : null; + string addItem = string.Format(this.CreateTableColumn, this.SqlBuilder.GetTranslationColumnName(columnName), dataType, dataSize, nullType, primaryKey, identity); + if (!string.IsNullOrEmpty(item.ColumnDescription)) + { + addItem += " COMMENT '" + item.ColumnDescription.ToSqlFilter() + "' "; + } + columnArray.Add(addItem); + } + string tableString = string.Format(this.CreateTableSql, this.SqlBuilder.GetTranslationTableName(tableName), string.Join(",\r\n", columnArray)); + return tableString; + } + + public override bool AddPrimaryKey(string tableName, string columnName) + { + if (DorisHelper.IsDoris(this.Context)) + { + return false; + } + return base.AddPrimaryKey(tableName, columnName); + } + public override bool AddColumn(string tableName, DbColumnInfo columnInfo) + { + tableName = this.SqlBuilder.GetTranslationTableName(tableName); + var isAddNotNUll = columnInfo.IsNullable == false && columnInfo.DefaultValue.HasValue(); + if (isAddNotNUll) + { + columnInfo = this.Context.Utilities.TranslateCopy(columnInfo); + columnInfo.IsNullable = true; + } + string sql = GetAddColumnSql(tableName, columnInfo); + if (sql != null && columnInfo.ColumnDescription.HasValue() && !sql.Contains("comment", StringComparison.CurrentCultureIgnoreCase)) + { + sql = $"{sql}{" COMMENT '" + columnInfo.ColumnDescription.ToSqlFilter() + "' "}"; + } + if (DorisHelper.IsDoris(this.Context)) + { + sql = sql.Replace(" ADD ", " ADD COLUMN "); + } + this.Context.Ado.ExecuteCommand(sql); + if (isAddNotNUll) + { + var dtColums = this.Context.Queryable().AS(columnInfo.TableName).Where("1=2") + .Select(this.SqlBuilder.GetTranslationColumnName(columnInfo.DbColumnName)).ToDataTable().Columns.Cast(); + var dtColumInfo = dtColums.First(it => it.ColumnName.EqualCase(columnInfo.DbColumnName)); + var type = UtilMethods.GetUnderType(dtColumInfo.DataType); + var value = type == UtilConstants.StringType ? (object)"" : Activator.CreateInstance(type); + //if (this.Context.CurrentConnectionConfig.DbType == DbType.Oracle) + //{ + // value = columnInfo.DefaultValue; + // if (value.Equals("")) + // { + // value = "empty"; + // } + //} + value = GetDefaultValue(columnInfo, value); + var dt = new Dictionary(); + dt.Add(columnInfo.DbColumnName, value); + this.Context.Updateable(dt) + .AS(tableName) + .Where($"{columnInfo.DbColumnName} is null ").ExecuteCommand(); + columnInfo.IsNullable = false; + UpdateColumn(tableName, columnInfo); + } + return true; + } + public override bool UpdateColumn(string tableName, DbColumnInfo column) + { + ConvertCreateColumnInfo(column); + if (DorisHelper.IsDoris(this.Context)) + { + return DorisUpadteColumns(ref tableName, column); + } + return base.UpdateColumn(tableName, column); + } + + + protected override string GetSize(DbColumnInfo item) + { + string dataSize = null; + var isMax = item.Length > 4000 || item.Length == -1; + if (isMax) + { + dataSize = ""; + item.DataType = "longtext"; + } + else if (item.Length > 0 && item.DecimalDigits == 0) + { + dataSize = item.Length > 0 ? string.Format("({0})", item.Length) : null; + } + else if (item.Length == 0 && item.DecimalDigits > 0) + { + item.Length = 10; + dataSize = string.Format("({0},{1})", item.Length, item.DecimalDigits); + } + else if (item.Length > 0 && item.DecimalDigits > 0) + { + dataSize = item.Length > 0 ? string.Format("({0},{1})", item.Length, item.DecimalDigits) : null; + } + return dataSize; + } + + public override bool RenameColumn(string tableName, string oldColumnName, string newColumnName) + { + var columns = GetColumnInfosByTableName(tableName, false).Where(it => it.DbColumnName.Equals(oldColumnName, StringComparison.CurrentCultureIgnoreCase)); + if (columns?.Any() == true) + { + var column = columns.First(); + var appendSql = " " + column.DataType; + if (column.Length > 0 && column.Scale == 0) + { + appendSql += string.Format("({0}) ", column.Length); + } + else if (column.Scale > 0 && column.Length > 0) + { + appendSql += string.Format("({0},{1}) ", column.Length, column.Scale); + } + else + { + appendSql += column.IsNullable ? " NULL " : " NOT NULL "; + } + tableName = this.SqlBuilder.GetTranslationTableName(tableName); + oldColumnName = this.SqlBuilder.GetTranslationColumnName(oldColumnName); + newColumnName = this.SqlBuilder.GetTranslationColumnName(newColumnName); + string sql = string.Format(this.RenameColumnSql, tableName, oldColumnName, newColumnName + appendSql); + this.Context.Ado.ExecuteCommand(sql); + return true; + } + else + { + return false; + } + } + public override bool AddDefaultValue(string tableName, string columnName, string defaultValue) + { + if (defaultValue == "''") + { + defaultValue = ""; + } + if (defaultValue.ToLower().IsIn("null", "now()", "current_timestamp") || defaultValue.Contains("current_timestamp", StringComparison.CurrentCultureIgnoreCase) || defaultValue.Contains("()")) + { + defaultValue = "(" + defaultValue + ")"; + string sql = string.Format(AddDefaultValueSql.Replace("'", ""), tableName, columnName, defaultValue); + this.Context.Ado.ExecuteCommand(sql); + return true; + } + else if (defaultValue == "0" || defaultValue == "1") + { + string sql = string.Format(AddDefaultValueSql.Replace("'", ""), tableName, columnName, defaultValue); + this.Context.Ado.ExecuteCommand(sql); + return true; + } + else + { + return base.AddDefaultValue(tableName, columnName, defaultValue); + } + } + public override bool IsAnyConstraint(string constraintName) + { + throw new NotSupportedException("MySql IsAnyConstraint NotSupportedException"); + } + public override bool BackupDataBase(string databaseName, string fullFileName) + { + if (fullFileName == null) + { + fullFileName = $"c:\\{databaseName}.sql"; + } + var db = this.Context.CopyNew(); + using (db.Ado.OpenAlways()) + { + if (databaseName != null) + { + db.Ado.Connection.ChangeDatabase(databaseName); + } + // Load the MySqlBackup assembly + Assembly assembly = null; + + try + { + if (StaticConfig.Backup_MySqlBackupType != null) + { + assembly = StaticConfig.Backup_MySqlBackupType.Assembly; + } + else + { + Assembly currentAssembly = Assembly.GetExecutingAssembly(); + string exePath = currentAssembly.Location.Replace("SqlSugar.dll", "MySqlBackupNet.MySqlConnector.dll"); + assembly = Assembly.LoadFrom(exePath); + } + } + catch (Exception) + { + Check.ExceptionEasy("Need MySqlBackup.NET.MySqlConnector", "需要安装:MySqlBackup.NET.MySqlConnector"); + throw; + } + + // Get the MySqlBackup type + Type mbType = assembly.GetType("MySqlConnector.MySqlBackup", false); + + // Create an instance of the MySqlBackup class + object mb = Activator.CreateInstance(mbType, db.Ado.Connection.CreateCommand()); + + // Get the ExportToFile method + MethodInfo exportMethod = mbType.GetMethod("ExportToFile"); + + // Invoke the ExportToFile method + exportMethod.Invoke(mb, new object[] { fullFileName }); + } + return true; + } + + #endregion + + #region Helper + + private bool DorisUpadteColumns(ref string tableName, DbColumnInfo column) + { + tableName = this.SqlBuilder.GetTranslationTableName(tableName); + var columName = this.SqlBuilder.GetTranslationTableName(column.DbColumnName); + string sql = GetUpdateColumnSql(tableName, column) + .Replace("change column", " MODIFY COLUMN") + .Replace("DEFAULT NULL ", " NULL ") + .Replace($"{columName} {columName}", columName); + try + { + this.Context.Ado.ExecuteCommand(sql); + } + catch (Exception ex) + { + if (ex.Message == "errCode = 2, detailMessage = Nothing is changed. please check your alter stmt.") + { + return true; + } + throw; + } + return true; + } + private bool ContainsCharSet(string charset) + { + if (Context.CurrentConnectionConfig.ConnectionString.ObjToString().Contains(charset, StringComparison.CurrentCultureIgnoreCase)) + { + return true; + } + else + { + return false; + } + } + private static void ConvertCreateColumnInfo(DbColumnInfo x) + { + string[] array = new string[] { "longtext", "date" }; + + if (("nvarchar".EqualCase(x.DataType) || "varchar".EqualCase(x.DataType))) + { + if (x.Length < 1) + { + x.DataType = $"{x.DataType}(255)"; // 设置默认长度为 255,你可以根据需要修改 + } + } + else if (array.Contains(x.DataType?.ToLower())) + { + x.Length = 0; + x.DecimalDigits = 0; + } + } + #endregion + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/MySqlProvider.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/MySqlProvider.cs new file mode 100644 index 000000000..c43fbfbb9 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/MySqlProvider.cs @@ -0,0 +1,323 @@ +using MySqlConnector; + +using Newtonsoft.Json.Linq; + +using System.Data; +using System.Data.Common; +namespace SqlSugar +{ + public class MySqlProvider : AdoProvider + { + public MySqlProvider() { } + public override IDbConnection Connection + { + get + { + if (base._DbConnection == null) + { + try + { + var mySqlConnectionString = base.Context.CurrentConnectionConfig.ConnectionString; + Check.ExceptionEasy(String.IsNullOrEmpty(mySqlConnectionString), "ConnectionString is not null", "连接字符串ConnectionString不能为Null"); + if (!mySqlConnectionString.Contains("charset", StringComparison.CurrentCultureIgnoreCase) && !mySqlConnectionString.Contains("character", StringComparison.CurrentCultureIgnoreCase)) + { + mySqlConnectionString = mySqlConnectionString.Trim().TrimEnd(';') + ";charset=utf8;"; + } + //if (!mySqlConnectionString.ToLower().Contains("min")) + //{ + // mySqlConnectionString = mySqlConnectionString.Trim().TrimEnd(';') + ";min pool size=1"; + //} + base._DbConnection = new MySqlConnection(mySqlConnectionString); + } + catch (Exception ex) + { + if (ex is SqlSugarException) + { + throw; + } + else + { + Check.Exception(true, ErrorMessage.ConnnectionOpen, ex.Message); + } + } + } + return base._DbConnection; + } + set + { + base._DbConnection = value; + } + } + + public override void BeginTran(string transactionName) + { + base.BeginTran(); + } + /// + /// Only SqlServer + /// + /// + /// + public override void BeginTran(IsolationLevel iso, string transactionName) + { + base.BeginTran(iso); + } + public override IDataAdapter GetAdapter() + { + return new MySqlDataAdapter(); + } + public override DbCommand GetCommand(string sql, SugarParameter[] parameters) + { + MySqlCommand sqlCommand = new MySqlCommand(sql, (MySqlConnection)this.Connection); + sqlCommand.CommandType = this.CommandType; + sqlCommand.CommandTimeout = this.CommandTimeOut; + if (this.Transaction != null) + { + sqlCommand.Transaction = (MySqlTransaction)this.Transaction; + } + if (parameters.HasValue()) + { + IDataParameter[] ipars = ToIDbDataParameter(parameters); + sqlCommand.Parameters.AddRange((MySqlParameter[])ipars); + } + CheckConnection(); + return sqlCommand; + } + public override void SetCommandToAdapter(IDataAdapter dataAdapter, DbCommand command) + { + ((MySqlDataAdapter)dataAdapter).SelectCommand = (MySqlCommand)command; + } + /// + /// if mysql return MySqlParameter[] pars + /// if sqlerver return SqlParameter[] pars ... + /// + /// + /// + public override IDataParameter[] ToIDbDataParameter(params SugarParameter[] parameters) + { + if (parameters == null || parameters.Length == 0) return null; + MySqlParameter[] result = new MySqlParameter[parameters.Length]; + int index = 0; + var isVarchar = this.Context.IsVarchar(); + foreach (var parameter in parameters) + { + if (parameter.Value == null) parameter.Value = DBNull.Value; + var sqlParameter = new MySqlParameter(); + sqlParameter.ParameterName = parameter.ParameterName; + sqlParameter.Size = parameter.Size; + sqlParameter.Value = parameter.Value; + sqlParameter.DbType = parameter.DbType; + if (parameter.Direction == 0) + { + parameter.Direction = ParameterDirection.Input; + } + sqlParameter.Direction = parameter.Direction; + //if (sqlParameter.Direction == 0) + //{ + // sqlParameter.Direction = ParameterDirection.Input; + //} + result[index] = sqlParameter; + if (sqlParameter.Direction.IsIn(ParameterDirection.Output, ParameterDirection.InputOutput, ParameterDirection.ReturnValue)) + { + if (this.OutputParameters == null) this.OutputParameters = new List(); + this.OutputParameters.RemoveAll(it => it.ParameterName == sqlParameter.ParameterName); + this.OutputParameters.Add(sqlParameter); + } + if (isVarchar && sqlParameter.DbType == System.Data.DbType.String) + { + sqlParameter.DbType = System.Data.DbType.AnsiString; + } + else if (parameter.DbType == System.Data.DbType.DateTimeOffset) + { + if (sqlParameter.Value != DBNull.Value) + sqlParameter.Value = UtilMethods.ConvertFromDateTimeOffset((DateTimeOffset)sqlParameter.Value); + sqlParameter.DbType = System.Data.DbType.DateTime; + } + if (sqlParameter.Value is DateTime && sqlParameter.Value.ObjToDate() == DateTime.MinValue) + { + var date = Convert.ToDateTime(sqlParameter.Value); + if (date == DateTime.MinValue) + { + sqlParameter.Value = UtilMethods.GetMinDate(this.Context.CurrentConnectionConfig); + } + } + if (parameter.IsJson == false && sqlParameter.Value != null && sqlParameter.Value is JArray) + { + sqlParameter.Value = sqlParameter.Value.ToString(); + } + if (DorisHelper.IsDoris(this.Context)) + { + DorisHelper.UpdateDateParameter(sqlParameter); + } + ++index; + } + return result; + } + + + protected override void SugarCatch(Exception ex, string sql, SugarParameter[] parameters) + { + base.SugarCatch(ex, sql, parameters); + + if (ex is NullReferenceException && SugarCompatible.IsFramework) + { + Check.ExceptionEasy($"To upgrade the MySql.Data. Error:{ex.Message}", $" 请先升级MySql.Data 。 详细错误:{ex.Message}"); + } + } + + #region async + public override async Task GetCommandAsync(string sql, SugarParameter[] parameters) + { + MySqlCommand sqlCommand = new MySqlCommand(sql, (MySqlConnection)this.Connection); + sqlCommand.CommandType = this.CommandType; + sqlCommand.CommandTimeout = this.CommandTimeOut; + if (this.Transaction != null) + { + sqlCommand.Transaction = (MySqlTransaction)this.Transaction; + } + if (parameters.HasValue()) + { + IDataParameter[] ipars = ToIDbDataParameter(parameters); + sqlCommand.Parameters.AddRange((MySqlParameter[])ipars); + } + if (this.Connection.State != ConnectionState.Open) + { + try + { + await (Connection as MySqlConnection).OpenAsync().ConfigureAwait(false); + } + catch (Exception ex) + { + Check.Exception(true, ex.Message); + } + } + return sqlCommand; + } + public override async Task ExecuteCommandAsync(string sql, params SugarParameter[] parameters) + { + if (this.Context.CurrentConnectionConfig?.SqlMiddle?.IsSqlMiddle == true) + return await base.ExecuteCommandAsync(sql, parameters).ConfigureAwait(false); + try + { + Async(); + InitParameters(ref sql, parameters); + if (FormatSql != null) + sql = FormatSql(sql); + SetConnectionStart(sql); + if (this.ProcessingEventStartingSQL != null) + ExecuteProcessingSQL(ref sql, ref parameters); + ExecuteBefore(sql, parameters); + var sqlCommand = await GetCommandAsync(sql, parameters).ConfigureAwait(false); + int count; + if (this.CancellationToken == null) + count = await sqlCommand.ExecuteNonQueryAsync().ConfigureAwait(false); + else + count = await sqlCommand.ExecuteNonQueryAsync(CancellationToken.Value).ConfigureAwait(false); + if (this.IsClearParameters) + sqlCommand.Parameters.Clear(); + SqlExecuteCount = count; + ExecuteAfter(sql, parameters); + sqlCommand.Dispose(); + return count; + } + catch (Exception ex) + { + CommandType = CommandType.Text; + if (ErrorEvent != null) + ExecuteErrorEvent(sql, parameters, ex); + throw; + } + finally + { + if (this.IsAutoClose()) + { + await CloseAsync().ConfigureAwait(false); + } + SetConnectionEnd(sql); + } + } + public override async Task GetDataReaderAsync(string sql, params SugarParameter[] parameters) + { + if (this.Context.CurrentConnectionConfig?.SqlMiddle?.IsSqlMiddle == true) + return await base.GetDataReaderAsync(sql, parameters).ConfigureAwait(false); + try + { + Async(); + InitParameters(ref sql, parameters); + if (FormatSql != null) + sql = FormatSql(sql); + SetConnectionStart(sql); + var isSp = this.CommandType == CommandType.StoredProcedure; + if (this.ProcessingEventStartingSQL != null) + ExecuteProcessingSQL(ref sql, ref parameters); + ExecuteBefore(sql, parameters); + var sqlCommand = await GetCommandAsync(sql, parameters).ConfigureAwait(false); + DbDataReader sqlDataReader; + if (this.CancellationToken == null) + sqlDataReader = await sqlCommand.ExecuteReaderAsync(IsAutoClose() ? CommandBehavior.CloseConnection : CommandBehavior.Default).ConfigureAwait(false); + else + sqlDataReader = await sqlCommand.ExecuteReaderAsync(IsAutoClose() ? CommandBehavior.CloseConnection : CommandBehavior.Default, CancellationToken.Value).ConfigureAwait(false); + if (isSp) + DataReaderParameters = sqlCommand.Parameters; + if (this.IsClearParameters) + sqlCommand.Parameters.Clear(); + ExecuteAfter(sql, parameters); + SetConnectionEnd(sql); + if (SugarCompatible.IsFramework || this.Context.CurrentConnectionConfig.DbType != DbType.Sqlite) + sqlCommand.Dispose(); + return sqlDataReader; + } + catch (Exception ex) + { + CommandType = CommandType.Text; + if (ErrorEvent != null) + ExecuteErrorEvent(sql, parameters, ex); + throw; + } + } + public override async Task GetScalarAsync(string sql, params SugarParameter[] parameters) + { + if (this.Context.CurrentConnectionConfig?.SqlMiddle?.IsSqlMiddle == true) + return await base.GetScalarAsync(sql, parameters).ConfigureAwait(false); + try + { + Async(); + InitParameters(ref sql, parameters); + if (FormatSql != null) + sql = FormatSql(sql); + SetConnectionStart(sql); + if (this.ProcessingEventStartingSQL != null) + ExecuteProcessingSQL(ref sql, ref parameters); + ExecuteBefore(sql, parameters); + var sqlCommand = await GetCommandAsync(sql, parameters).ConfigureAwait(false); + object scalar; + if (CancellationToken == null) + scalar = await sqlCommand.ExecuteScalarAsync().ConfigureAwait(false); + else + scalar = await sqlCommand.ExecuteScalarAsync(CancellationToken.Value).ConfigureAwait(false); + //scalar = (scalar == null ? 0 : scalar); + if (this.IsClearParameters) + sqlCommand.Parameters.Clear(); + ExecuteAfter(sql, parameters); + sqlCommand.Dispose(); + return scalar; + } + catch (Exception ex) + { + CommandType = CommandType.Text; + if (ErrorEvent != null) + ExecuteErrorEvent(sql, parameters, ex); + throw; + } + finally + { + if (this.IsAutoClose()) + { + await CloseAsync().ConfigureAwait(false); + } + SetConnectionEnd(sql); + } + } + #endregion + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/PatialClass/MySqlFastBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/PatialClass/MySqlFastBuilder.cs new file mode 100644 index 000000000..5a3a4ab5a --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/PatialClass/MySqlFastBuilder.cs @@ -0,0 +1,31 @@ +using MySqlConnector; + +using System.Data; + +namespace SqlSugar +{ + public partial class MySqlFastBuilder : FastBuilder, IFastBuilder + { + private async Task MySqlConnectorBulkCopy(DataTable dt) + { + try + { + this.Context.Open(); + var tran = (MySqlTransaction)this.Context.Ado.Transaction; + var connection = (MySqlConnection)this.Context.Ado.Connection; + MySqlBulkCopy bulkCopy = new MySqlBulkCopy(connection, tran); + bulkCopy.DestinationTableName = dt.TableName; + await bulkCopy.WriteToServerAsync(dt).ConfigureAwait(false); + return dt.Rows.Count; + } + catch (Exception) + { + throw; + } + finally + { + CloseDb(); + } + } + } +} \ No newline at end of file diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/Queryable/MySqlQueryable.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/Queryable/MySqlQueryable.cs new file mode 100644 index 000000000..54bca969e --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/Queryable/MySqlQueryable.cs @@ -0,0 +1,72 @@ +namespace SqlSugar +{ + public class MySqlQueryable : QueryableProvider + { + public override ISugarQueryable With(string withString) + { + if (withString == null) + { + return this; + } + if (UtilMethods.StringCheckFirstAndLast(withString.TrimStart(' ').TrimEnd(' '), "/*", "*/")) + { + QueryBuilder.TableWithString = withString; + } + else if (UtilMethods.StringCheckFirstAndLast(withString.TrimStart(' ').TrimEnd(' ').ToLower(), "force", ")")) + { + QueryBuilder.TableWithString = withString; + } + return this; + } + + public override ISugarQueryable PartitionBy(string groupFileds) + { + this.GroupBy(groupFileds); + return this; + } + } + public class MySqlQueryable : QueryableProvider + { + + } + public class MySqlQueryable : QueryableProvider + { + + } + public class MySqlQueryable : QueryableProvider + { + + } + public class MySqlQueryable : QueryableProvider + { + + } + public class MySqlQueryable : QueryableProvider + { + + } + public class MySqlQueryable : QueryableProvider + { + + } + public class MySqlQueryable : QueryableProvider + { + + } + public class MySqlQueryable : QueryableProvider + { + + } + public class MySqlQueryable : QueryableProvider + { + + } + public class MySqlQueryable : QueryableProvider + { + + } + public class MySqlQueryable : QueryableProvider + { + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/SqlBuilder/MySqlBlukCopy.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/SqlBuilder/MySqlBlukCopy.cs new file mode 100644 index 000000000..9a7d2cbb8 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/SqlBuilder/MySqlBlukCopy.cs @@ -0,0 +1,202 @@ +using MySqlConnector; + +using System.Data; +using System.Reflection; +using System.Text; +namespace SqlSugar +{ + public partial class MySqlBlukCopy + { + + private MySqlBlukCopy() + { + + } + + public bool ExecuteBulkCopy(string characterSet) + { + this.Chara = characterSet; + return ExecuteBulkCopy(); + } + + public bool ExecuteBulkCopy() + { + var IsBulkLoad = false; + if (Entitys == null || Entitys.Length <= 0) + return IsBulkLoad; + if (Entitys.First() == null && Entitys.Length == 1) + return IsBulkLoad; + DataTable dt = new DataTable(); + Type type = typeof(T); + var entity = this.Context.EntityMaintenance.GetEntityInfo(); + dt.TableName = this.Builder.GetTranslationColumnName(entity.DbTableName); + //if (this.Context.MappingTables != null && this.Context.MappingTables.Any(it => it.EntityName == it.EntityName)) + //{ + // dt.TableName = this.Builder.GetTranslationColumnName(this.Context.MappingTables.First(it => it.EntityName == it.EntityName).DbTableName); + //} + //创建属性的集合 + List pList = new List(); + //把所有的public属性加入到集合 并添加DataTable的列 + Array.ForEach(entity.Columns.ToArray(), p => + { + if (!p.IsIgnore && !p.IsOnlyIgnoreInsert) + { + pList.Add(p.PropertyInfo); dt.Columns.Add(p.DbColumnName); + } + }); + DataRow row = null; + foreach (T item in Entitys) + { + row = dt.NewRow(); + pList.ForEach(p => + { + var name = p.Name; + if (entity.Columns.Any(it => it.PropertyName == name)) + { + name = entity.Columns.First(it => it.PropertyName == name).DbColumnName; + } + row[name] = GetValue(p, item); + }); + dt.Rows.Add(row); + } + var dllPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "failFiles"); + DirectoryInfo dir = new DirectoryInfo(dllPath); + if (!dir.Exists) + { + dir.Create(); + } + var fileName = Path.Combine(dllPath, Guid.NewGuid().ToString() + ".csv"); + var dataTableToCsv = DataTableToCsvString(dt); + File.WriteAllText(fileName, dataTableToCsv, new UTF8Encoding(false)); + MySqlConnection conn = this.Context.Ado.Connection as MySqlConnection; + try + { + this.Context.Ado.Open(); + // IsolationLevel.Parse + MySqlBulkLoader bulk = new MySqlBulkLoader(conn) + { + CharacterSet = GetChara(), + FieldTerminator = ",", + FieldQuotationCharacter = '"', + EscapeCharacter = '"', + LineTerminator = Environment.NewLine, + FileName = fileName, + NumberOfLinesToSkip = 0, + TableName = dt.TableName, + Local = true, + }; + bulk.Columns.AddRange(dt.Columns.Cast().Select(colum => colum.ColumnName).Distinct().ToArray()); + IsBulkLoad = bulk.Load() > 0; + //执行成功才删除文件 + if (IsBulkLoad && File.Exists(fileName)) + { + File.Delete(fileName); + } + } + catch (MySqlException) + { + throw; + } + finally + { + CloseDb(); + } + return IsBulkLoad; ; + } + + public Task ExecuteBulkCopyAsync() + { + return Task.FromResult(ExecuteBulkCopy()); + } + + public Task ExecuteBulkCopyAsync(string characterSet) + { + this.Chara = characterSet; + return Task.FromResult(ExecuteBulkCopy()); + } + + #region Helper + private string GetChara() + { + if (this.Chara == null) + { + return "utf8mb4"; + } + else + { + return this.Chara; + } + } + + private void CloseDb() + { + if (this.Context.CurrentConnectionConfig.IsAutoCloseConnection && this.Context.Ado.Transaction == null) + { + this.Context.Ado.Connection.Close(); + } + } + + /// + ///DataTable to CSV + /// + /// datatable + /// CSV + public string DataTableToCsvString(DataTable table) + { + if (table.Rows.Count == 0) + return ""; + StringBuilder sb = new StringBuilder(); + DataColumn colum; + foreach (DataRow row in table.Rows) + { + for (int i = 0; i < table.Columns.Count; i++) + { + colum = table.Columns[i]; + if (i != 0) sb.Append(','); + if (colum.DataType == typeof(string) && (row[colum].ToString().Contains(',') || row[colum].ToString().Contains('\r') || row[colum].ToString().Contains('"') || row[colum].ToString().Contains('\n'))) + { + sb.Append('\"' + row[colum].ToString().Replace("\"", "\"\"") + '\"'); + } + else if (colum.DataType == typeof(bool)) + { + if (row[colum] == DBNull.Value) + { + sb.Append("NULL"); + } + else + { + sb.Append(row[colum].ObjToBool() ? 1 : 0); + } + } + else if (colum.DataType == UtilConstants.DateType && row[colum] != null && row[colum] != DBNull.Value) + { + sb.Append(row[colum].ObjToDate().ToString("yyyy-MM-dd HH:mm:ss.fff")); + } + else if (row[colum] == null || row[colum] == DBNull.Value) + { + sb.Append("NULL"); + } + else sb.Append(row[colum].ToString()); + } + sb.AppendLine(); + } + return sb.ToString(); + } + + + private static object GetValue(PropertyInfo p, T item) + { + var result = p.GetValue(item, null); + if (result != null && UtilMethods.GetUnderType(p.PropertyType) == UtilConstants.BoolType) + { + if (result.ObjToBool() == false) + { + result = null; + } + } + return result; + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/SqlBuilder/MySqlBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/SqlBuilder/MySqlBuilder.cs new file mode 100644 index 000000000..1f1b933a2 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/SqlBuilder/MySqlBuilder.cs @@ -0,0 +1,35 @@ +namespace SqlSugar +{ + public class MySqlBuilder : SqlBuilderProvider + { + public override string SqlTranslationLeft { get { return "`"; } } + public override string SqlTranslationRight { get { return "`"; } } + public override string SqlDateNow + { + get + { + return "NOW(6)"; + } + } + public override string FullSqlDateNow + { + get + { + return "select NOW(6)"; + } + } + public override string GetUnionFomatSql(string sql) + { + return " ( " + sql + " ) "; + } + + public override string RemoveParentheses(string sql) + { + if (sql.Contains(" ORDER BY") && sql.StartsWith('(') && sql.EndsWith(')') && !sql.Contains("limit", StringComparison.CurrentCultureIgnoreCase)) + { + sql = $" {sql.TrimEnd(')')} limit 0,{int.MaxValue}) "; + } + return sql; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/SqlBuilder/MySqlDeleteBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/SqlBuilder/MySqlDeleteBuilder.cs new file mode 100644 index 000000000..97189bcab --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/SqlBuilder/MySqlDeleteBuilder.cs @@ -0,0 +1,7 @@ +namespace SqlSugar +{ + public class MySqlDeleteBuilder : DeleteBuilder + { + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/SqlBuilder/MySqlExpressionContext.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/SqlBuilder/MySqlExpressionContext.cs new file mode 100644 index 000000000..f4633548e --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/SqlBuilder/MySqlExpressionContext.cs @@ -0,0 +1,332 @@ +namespace SqlSugar +{ + public class MySqlExpressionContext : ExpressionContext, ILambdaExpressions + { + public SqlSugarProvider Context { get; set; } + public MySqlExpressionContext() + { + base.DbMehtods = new MySqlMethod(); + } + public override string SqlTranslationLeft { get { return "`"; } } + public override string SqlTranslationRight { get { return "`"; } } + } + public class MySqlMethod : DefaultDbMethod, IDbMethods + { + public override string JsonArrayLength(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return $" JSON_LENGTH({parameter.MemberName}) "; + } + + public override string JsonIndex(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter1 = model.Args[1]; + return $"JSON_UNQUOTE(JSON_EXTRACT({parameter.MemberName}, '$[{parameter1.MemberValue}]'))"; + } + public override string WeekOfYear(MethodCallExpressionModel mode) + { + var parameterNameA = mode.Args[0].MemberName; + return $" WEEK({parameterNameA}) "; + } + public override string GetStringJoinSelector(string result, string separator) + { + return $"group_concat({result} separator '{separator}') "; + } + public override string DateDiff(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + var parameter3 = model.Args[2]; + return string.Format(" TIMESTAMPDIFF({0},{1},{2}) ", parameter.MemberValue?.ToString().ToSqlFilter(), parameter2.MemberName, parameter3.MemberName); + } + public override string DateValue(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + if (parameter.MemberName != null && parameter.MemberName is DateTime) + { + if (parameter2.MemberValue?.ToString() == DateType.Weekday.ToString()) + { + return string.Format(" case when {0}('{1}')=6 then 0 else ({0}('{1}')+1) end ", parameter2.MemberValue, parameter.MemberName); + } + else + { + return string.Format(" {0}('{1}') ", parameter2.MemberValue, parameter.MemberName); + } + } + else + { + if (parameter2.MemberValue?.ToString() == DateType.Weekday.ToString()) + { + return string.Format(" case when {0}({1})=6 then 0 else ({0}({1})+1) end ", parameter2.MemberValue, parameter.MemberName); + } + else + { + return string.Format(" {0}({1}) ", parameter2.MemberValue, parameter.MemberName); + } + } + } + + public override string Contains(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + return string.Format(" ({0} like concat('%',{1},'%')) ", parameter.MemberName, parameter2.MemberName); + } + + public override string StartsWith(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + return string.Format(" ({0} like concat({1},'%')) ", parameter.MemberName, parameter2.MemberName); + } + + public override string EndsWith(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + return string.Format(" ({0} like concat('%',{1}))", parameter.MemberName, parameter2.MemberName); + } + + public override string DateIsSameDay(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + return string.Format(" (TIMESTAMPDIFF(day,date({0}),date({1}))=0) ", parameter.MemberName, parameter2.MemberName); ; + } + + public override string DateIsSameByType(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + var parameter3 = model.Args[2]; + if (parameter3.MemberValue.ObjToString() == DateType.Weekday.ObjToString()) + { + parameter3.MemberValue = "Week"; + } + if (parameter3.MemberValue.ObjToString() == DateType.Month.ObjToString()) + { + return string.Format(" (DATE_FORMAT({0}, '%Y%m') = DATE_FORMAT({1}, '%Y%m')) ", parameter.MemberName, parameter2.MemberName, parameter3.MemberValue); + } + else if (parameter3.MemberValue.ObjToString() == DateType.Year.ObjToString()) + { + return string.Format(" (DATE_FORMAT({0}, '%Y') = DATE_FORMAT({1}, '%Y')) ", parameter.MemberName, parameter2.MemberName, parameter3.MemberValue); + } + else if (parameter3.MemberValue.ObjToString() == DateType.Day.ObjToString()) + { + return string.Format(" (DATE_FORMAT({0}, '%Y-%m-%d') = DATE_FORMAT({1}, '%Y-%m-%d')) ", parameter.MemberName, parameter2.MemberName, parameter3.MemberValue); + } + else if (parameter3.MemberValue.ObjToString() == DateType.Hour.ObjToString()) + { + return string.Format(" (DATE_FORMAT({0}, '%Y-%m-%d %H') = DATE_FORMAT({1}, '%Y-%m-%d %H')) ", parameter.MemberName, parameter2.MemberName, parameter3.MemberValue); + } + else if (parameter3.MemberValue.ObjToString() == DateType.Minute.ObjToString()) + { + return string.Format(" (DATE_FORMAT({0}, '%Y-%m-%d %H:%i') = DATE_FORMAT({1}, '%Y-%m-%d %H:%i')) ", parameter.MemberName, parameter2.MemberName, parameter3.MemberValue); + } + else if (parameter3.MemberValue.ObjToString() == DateType.Second.ObjToString()) + { + return string.Format(" (DATE_FORMAT({0}, '%Y-%m-%d %H:%i:%S') = DATE_FORMAT({1}, '%Y-%m-%d %H:%i:%S')) ", parameter.MemberName, parameter2.MemberName, parameter3.MemberValue); + } + return string.Format(" (TIMESTAMPDIFF({2},{0},{1})=0) ", parameter.MemberName, parameter2.MemberName, parameter3.MemberValue); + } + + public override string DateAddByType(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + var parameter3 = model.Args[2]; + if (parameter3.MemberValue.ObjToString() == "Millisecond") + { + parameter3.MemberValue = "Second"; + return string.Format(" (DATE_ADD({1} , INTERVAL {2}/1000 {0})) ", parameter3.MemberValue, parameter.MemberName, parameter2.MemberName); + } + return string.Format(" (DATE_ADD({1} , INTERVAL {2} {0})) ", parameter3.MemberValue, parameter.MemberName, parameter2.MemberName); + } + + public override string DateAddDay(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + return string.Format(" (DATE_ADD({0}, INTERVAL {1} day)) ", parameter.MemberName, parameter2.MemberName); + } + + public override string ToInt32(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS SIGNED)", parameter.MemberName); + } + + public override string ToInt64(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS SIGNED)", parameter.MemberName); + } + + public override string ToString(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS CHAR)", parameter.MemberName); + } + + public override string ToGuid(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS CHAR)", parameter.MemberName); + } + + public override string ToDouble(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS DECIMAL(18,4))", parameter.MemberName); + } + + public override string ToBool(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS SIGNED)", parameter.MemberName); + } + + public override string ToDecimal(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS DECIMAL(18,4))", parameter.MemberName); + } + + public override string Length(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" LENGTH({0})", parameter.MemberName); + } + public override string MergeString(params string[] strings) + { + return " concat(" + string.Join(",", strings) + ") "; + } + public override string IsNull(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter1 = model.Args[1]; + if (parameter1.MemberValue is bool) + { + return string.Format("IFNULL(CAST({0} as SIGNED),{1})", parameter.MemberName, parameter1.MemberName); + } + else + { + return string.Format("IFNULL({0},{1})", parameter.MemberName, parameter1.MemberName); + } + } + public override string GetDate() + { + return "NOW(6)"; + } + + public override string GetRandom() + { + return "rand()"; + } + + public override string Collate(MethodCallExpressionModel model) + { + var name = model.Args[0].MemberName; + return $" binary {name} "; + } + + public override string CharIndex(MethodCallExpressionModel model) + { + return string.Format("instr ({0},{1})", model.Args[0].MemberName, model.Args[1].MemberName); + } + + public override string JsonField(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter1 = model.Args[1]; + //var parameter2 = model.Args[2]; + //var parameter3= model.Args[3]; + var result = GetJson(parameter.MemberName, parameter1.MemberValue, model.Args.Count == 2); + if (model.Args.Count > 2) + { + result = GetJson(result, model.Args[2].MemberValue, model.Args.Count == 3); + } + if (model.Args.Count > 3) + { + result = GetJson(result, model.Args[3].MemberValue, model.Args.Count == 4); + } + if (model.Args.Count > 4) + { + result = GetJson(result, model.Args[4].MemberValue, model.Args.Count == 5); + } + if (model.Args.Count > 5) + { + result = GetJson(result, model.Args[5].MemberValue, model.Args.Count == 6); + } + return result; + } + + private string GetJson(object memberName1, object memberName2, bool isLast) + { + if (memberName1?.ToString()?.Contains("->") == true) + { + return $"{memberName1.ToString().TrimEnd('\'')}.{memberName2}'"; + } + else + { + return $"{memberName1}->'$.{memberName2}'"; + } + } + + public override string JsonArrayAny(MethodCallExpressionModel model) + { + if (model.Args[1].MemberValue == null) + { + return $" JSON_CONTAINS({model.Args[0].MemberName}, JSON_QUOTE({model.Args[1].MemberName}) )"; + } + else if (UtilMethods.IsNumber(model.Args[1].MemberValue.GetType().Name)) + { + return $" JSON_CONTAINS({model.Args[0].MemberName}, '{model.Args[1].MemberValue}')"; + } + else + { + return $" JSON_CONTAINS({model.Args[0].MemberName}, '\"{model.Args[1].MemberValue.ObjToStringNoTrim().ToSqlFilter()}\"')"; + } + } + public override string JsonListObjectAny(MethodCallExpressionModel model) + { + if (UtilMethods.IsNumber(model.Args[2].MemberValue.GetType().Name)) + { + return $" JSON_CONTAINS({model.Args[0].MemberName},'{{\"{model.Args[1].MemberValue}\":{model.Args[2].MemberValue}}}')"; + } + else + { + return $" JSON_CONTAINS({model.Args[0].MemberName},'{{\"{model.Args[1].MemberValue}\":\"{model.Args[2].MemberValue.ObjToStringNoTrim().ToSqlFilter()}\"}}')"; + } + } + public override string NewUid(MethodCallExpressionModel mode) + { + return " CONCAT(\r\n LPAD(UPPER(HEX(FLOOR(UUID_SHORT() / 0x100000000))), 8, '0'),\r\n '-',\r\n LPAD(UPPER(HEX(FLOOR(UUID_SHORT() / 0x10000) & 0xFFFF)), 4, '0'),\r\n '-',\r\n LPAD(UPPER(HEX(FLOOR(UUID_SHORT() / 0x100) & 0xFFFF)), 4, '0'),\r\n '-',\r\n LPAD(UPPER(HEX(UUID_SHORT() & 0xFF)), 4, '0'),\r\n '-000000000000'\r\n ) "; + } + //public override string TrimEnd(MethodCallExpressionModel mode) + //{ + // var parameterNameA = mode.Args[0].MemberName; + // var parameterNameB = mode.Args[1].MemberName; + // return $" TRIM(TRAILING {parameterNameA} FROM {parameterNameB}) "; + //} + //public override string TrimStart(MethodCallExpressionModel mode) + //{ + + // var parameterNameA = mode.Args[0].MemberName; + // var parameterNameB = mode.Args[1].MemberName; + // return $" TRIM(LEADING {parameterNameA} FROM {parameterNameB}) "; + //} + public override string FullTextContains(MethodCallExpressionModel mode) + { + var columns = mode.Args[0].MemberName; + if (mode.Args[0].MemberValue is List) + { + columns = string.Join(",", mode.Args[0].MemberValue as List); + } + var searchWord = mode.Args[1].MemberName; + return $" MATCH({columns}) AGAINST({searchWord}) "; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/SqlBuilder/MySqlFastBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/SqlBuilder/MySqlFastBuilder.cs new file mode 100644 index 000000000..c3a588272 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/SqlBuilder/MySqlFastBuilder.cs @@ -0,0 +1,105 @@ +using MySqlConnector; + +using System.Data; +using System.Text; + +namespace SqlSugar +{ + + public partial class MySqlFastBuilder : FastBuilder, IFastBuilder + { + public override string UpdateSql { get; set; } = @"UPDATE {1} TM INNER JOIN {2} TE ON {3} SET {0} "; + public async Task ExecuteBulkCopyAsync(DataTable dt) + { + if (dt.Columns.Cast().Any(it => it.DataType == UtilConstants.ByteArrayType)) + { + return await MySqlConnectorBulkCopy(dt).ConfigureAwait(false); + } + var dllPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bulkcopyfiles"); + if (StaticConfig.BulkCopy_MySqlCsvPath.HasValue()) + { + dllPath = StaticConfig.BulkCopy_MySqlCsvPath; + } + DirectoryInfo dir = new DirectoryInfo(dllPath); + if (!dir.Exists) + { + dir.Create(); + } + var fileName = Path.Combine(dllPath, Guid.NewGuid().ToString() + ".csv"); + var dataTableToCsv = new MySqlBlukCopy(this.Context.Context, null, null).DataTableToCsvString(dt); + File.WriteAllText(fileName, dataTableToCsv, new UTF8Encoding(false)); + MySqlConnection conn = this.Context.Ado.Connection as MySqlConnection; + int result = 0; + try + { + this.Context.Ado.Open(); + // IsolationLevel.Parse + MySqlBulkLoader bulk = new MySqlBulkLoader(conn) + { + CharacterSet = "utf8mb4", + FieldTerminator = ",", + FieldQuotationCharacter = '"', + EscapeCharacter = '"', + LineTerminator = Environment.NewLine, + FileName = fileName, + NumberOfLinesToSkip = 0, + TableName = dt.TableName, + Local = true, + }; + if (this.CharacterSet.HasValue()) + { + bulk.CharacterSet = this.CharacterSet; + } + bulk.Columns.AddRange(dt.Columns.Cast().Select(colum => new MySqlBuilder().GetTranslationColumnName(colum.ColumnName)).Distinct().ToArray()); + result = await bulk.LoadAsync().ConfigureAwait(false); + //执行成功才删除文件 + if (File.Exists(fileName)) + { + File.Delete(fileName); + } + } + catch (Exception ex) + { + if (ex.Message == "The used command is not allowed with this MySQL version") + { + Check.ExceptionEasy("connection string add : AllowLoadLocalInfile=true", "BulkCopy MySql连接字符串需要添加 AllowLoadLocalInfile=true; 添加后如果还不行Mysql数据库执行一下 SET GLOBAL local_infile=1 "); + } + else if (ex.Message.Contains("To use MySqlBulkLoader.Local=true, set Allo")) + { + Check.ExceptionEasy("connection string add : AllowLoadLocalInfile=true", "BulkCopy MySql连接字符串需要添加 AllowLoadLocalInfile=true; 添加后如果还不行Mysql数据库执行一下 SET GLOBAL local_infile=1 "); + } + else if (ex.Message == "Loading local data is disabled; this must be enabled on both the client and server sides") + { + this.Context.Ado.ExecuteCommand("SET GLOBAL local_infile=1"); + Check.ExceptionEasy(ex.Message, " 检测到你没有开启文件,AllowLoadLocalInfile=true加到自符串上,已自动执行 SET GLOBAL local_infile=1 在试一次"); + } + else + { + throw; + } + } + finally + { + CloseDb(); + } + return result; + } + public override async Task CreateTempAsync(DataTable dt) + { + var queryable = this.Context.Queryable(); + var tableName = queryable.SqlBuilder.GetTranslationTableName(dt.TableName); + dt.TableName = "temp" + SnowFlakeSingle.instance.getID(); + var sql = string.Empty; + if (dt.Columns.Cast().Any(it => it.DataType == UtilConstants.ByteArrayType)) + { + sql = queryable.AS(tableName).Where(it => false) + .Select(string.Join(",", dt.Columns.Cast().Select(it => queryable.SqlBuilder.GetTranslationTableName(it.ColumnName)))).ToSql().Key; + } + else + { + sql = queryable.AS(tableName).Where(it => false).ToSql().Key; + } + await Context.Ado.ExecuteCommandAsync($"Create TEMPORARY table {dt.TableName}({sql}) ").ConfigureAwait(false); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/SqlBuilder/MySqlInsertBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/SqlBuilder/MySqlInsertBuilder.cs new file mode 100644 index 000000000..b919c5549 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/SqlBuilder/MySqlInsertBuilder.cs @@ -0,0 +1,191 @@ +using System.Globalization; +using System.Text; + +namespace SqlSugar +{ + public class MySqlInsertBuilder : InsertBuilder + { + public override string SqlTemplate + { + get + { + if (IsReturnIdentity) + { + return @"INSERT INTO {0} + ({1}) + VALUES + ({2}) ;SELECT LAST_INSERT_ID();"; + } + else + { + return @"INSERT INTO {0} + ({1}) + VALUES + ({2}) ;"; + + } + } + } + int i = 0; + public object FormatValue(object value, string name) + { + var n = "N"; + if (this.Context.CurrentConnectionConfig.MoreSettings?.DisableNvarchar == true) + { + n = ""; + } + if (value == null) + { + return "NULL"; + } + else + { + var type = UtilMethods.GetUnderType(value.GetType()); + if (type == UtilConstants.DateType) + { + return GetDateTimeString(value); + } + else if (value is DateTimeOffset) + { + return GetDateTimeOffsetString(value); + } + else if (value is decimal decValue) + { + return decValue.ToString(CultureInfo.InvariantCulture); + } + else if (value is double douValue) + { + return douValue.ToString(CultureInfo.InvariantCulture); + } + else if (type == UtilConstants.ByteArrayType) + { + string bytesString = "0x" + Convert.ToHexString((byte[])value); + if (bytesString == "0x") + { + bytesString = "''"; + } + return bytesString; + } + else if (type.IsEnum()) + { + if (this.Context.CurrentConnectionConfig.MoreSettings?.TableEnumIsString == true) + { + return value.ToSqlValue(); ; + } + else + { + return Convert.ToInt64(value); + } + } + else if (type == UtilConstants.BoolType) + { + return value.ObjToBool() ? "1" : "0"; + } + else if (type == UtilConstants.StringType || type == UtilConstants.ObjType) + { + ++i; + var parameterName = this.Builder.SqlParameterKeyWord + name + "_" + i; + this.Parameters.Add(new SugarParameter(parameterName, value)); + return parameterName; + } + else + { + return n + "'" + GetString(value) + "'"; + } + } + } + + private object GetDateTimeOffsetString(object value) + { + var date = UtilMethods.ConvertFromDateTimeOffset((DateTimeOffset)value); + if (date < UtilMethods.GetMinDate(this.Context.CurrentConnectionConfig)) + { + date = UtilMethods.GetMinDate(this.Context.CurrentConnectionConfig); + } + return "'" + date.ToString("yyyy-MM-dd HH:mm:ss.fff") + "'"; + } + + private object GetDateTimeString(object value) + { + var date = value.ObjToDate(); + if (date < UtilMethods.GetMinDate(this.Context.CurrentConnectionConfig)) + { + date = UtilMethods.GetMinDate(this.Context.CurrentConnectionConfig); + } + return "'" + date.ToString("yyyy-MM-dd HH:mm:ss.fff") + "'"; + } + + private string GetString(object value) + { + var result = value.ToString(); + if (result.HasValue() && result.Contains('\\')) + { + result = result.Replace("\\", "\\\\"); + } + return result; + } + + public override string ToSqlString() + { + if (IsNoInsertNull) + { + DbColumnInfoList = DbColumnInfoList.Where(it => it.Value != null).ToList(); + } + var groupList = DbColumnInfoList.GroupBy(it => it.TableId).ToList(); + var isSingle = groupList.Count == 1; + string columnsString = string.Join(",", groupList.First().Select(it => Builder.GetTranslationColumnName(it.DbColumnName))); + if (isSingle) + { + string columnParametersString = string.Join(",", this.DbColumnInfoList.Select(it => base.GetDbColumn(it, Builder.SqlParameterKeyWord + it.DbColumnName))); + ActionMinDate(); + var result = string.Format(SqlTemplate, GetTableNameString, columnsString, columnParametersString); + result = GetMySqlIgnore(result); + return result; + } + else + { + StringBuilder batchInsetrSql = new StringBuilder(); + batchInsetrSql.Append("INSERT INTO " + GetTableNameString + " "); + batchInsetrSql.Append('('); + batchInsetrSql.Append(columnsString); + batchInsetrSql.Append(") VALUES"); + string insertColumns = ""; + foreach (var item in groupList) + { + batchInsetrSql.Append('('); + insertColumns = string.Join(",", item.Select(it => base.GetDbColumn(it, FormatValue(it.Value, it.PropertyName)))); + batchInsetrSql.Append(insertColumns); + if (groupList.Last() == item) + { + batchInsetrSql.Append(") "); + } + else + { + batchInsetrSql.Append("), "); + } + } + if (DorisHelper.IsDoris(this.Context)) + { + //doris insert ignore + } + else + { + batchInsetrSql.AppendLine(";select @@IDENTITY"); + } + var result = batchInsetrSql.ToString(); + result = GetMySqlIgnore(result); + return result; + } + } + + private string GetMySqlIgnore(string result) + { + if (this.MySqlIgnore) + { + result = result.Replace("INSERT INTO", " INSERT IGNORE INTO"); + } + + return result; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/SqlBuilder/MySqlQueryBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/SqlBuilder/MySqlQueryBuilder.cs new file mode 100644 index 000000000..2f0723dab --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/SqlBuilder/MySqlQueryBuilder.cs @@ -0,0 +1,165 @@ +using System.Text; +using System.Text.RegularExpressions; + +namespace SqlSugar +{ + public partial class MySqlQueryBuilder : QueryBuilder + { + #region Sql Template + public override string PageTempalte + { + get + { + /* + SELECT * FROM TABLE WHERE CONDITION ORDER BY ID DESC LIMIT 0,10 + */ + var template = "SELECT {0} FROM {1} {2} {3} {4} LIMIT {5},{6}"; + return template; + } + } + public override string DefaultOrderByTemplate + { + get + { + return "ORDER BY NOW() "; + } + } + + #endregion + + #region Common Methods + public override bool IsComplexModel(string sql) + { + return Regex.IsMatch(sql, @"AS \`\w+\.\w+\`") || Regex.IsMatch(sql, @"AS \`\w+\.\w+\.\w+\`"); + } + public override string ToSqlString() + { + base.AppendFilter(); + string oldOrderValue = this.OrderByValue; + string result = null; + sql = new StringBuilder(); + sql.AppendFormat(SqlTemplate, GetSelectValue, GetTableNameString, GetWhereValueString, GetGroupByString + HavingInfos, (Skip != null || Take != null) ? null : GetOrderByString); + if (IsCount) { return sql.ToString(); } + if (Skip != null && Take == null) + { + if (this.OrderByValue == "ORDER BY ") this.OrderByValue += GetSelectValue.Split(',')[0]; + var maxLongValue = long.MaxValue; + if (this.Context.CurrentConnectionConfig?.MoreSettings?.DatabaseModel == DbType.OceanBase) + { + maxLongValue = Convert.ToInt64(maxLongValue / 2.0); + } + result = string.Format(PageTempalte, GetSelectValue, GetTableNameString, GetWhereValueString, GetGroupByString + HavingInfos, GetOrderByString, Skip.ObjToInt(), maxLongValue); + } + else if (Skip == null && Take != null) + { + if (this.OrderByValue == "ORDER BY ") this.OrderByValue += GetSelectValue.Split(',')[0]; + result = string.Format(PageTempalte, GetSelectValue, GetTableNameString, GetWhereValueString, GetGroupByString + HavingInfos, GetOrderByString, 0, Take.ObjToInt()); + } + else if (Skip != null && Take != null) + { + if (Skip == 0 && Take == 1 && this.OrderByValue == "ORDER BY NOW() ") + { + this.OrderByValue = null; + } + if (this.OrderByValue == "ORDER BY ") this.OrderByValue += GetSelectValue.Split(',')[0]; + result = string.Format(PageTempalte, GetSelectValue, GetTableNameString, GetWhereValueString, GetGroupByString + HavingInfos, GetOrderByString, Skip.ObjToInt() > 0 ? Skip.ObjToInt() : 0, Take); + } + else + { + result = sql.ToString(); + } + this.OrderByValue = oldOrderValue; + result = GetSqlQuerySql(result); + if (result.IndexOf("-- No table") > 0) + { + return "-- No table"; + } + if (TranLock != null) + { + result = result + TranLock; + } + return result; + } + private string ToCountSqlString() + { + //base.AppendFilter(); + string oldOrderValue = this.OrderByValue; + string result = null; + sql = new StringBuilder(); + sql.AppendFormat(SqlTemplate, "Count(*)", GetTableNameString, GetWhereValueString, GetGroupByString + HavingInfos, (Skip != null || Take != null) ? null : GetOrderByString); + if (IsCount) + { + if (sql.ToString().Contains("-- No table")) + { + return "-- No table"; + } + return sql.ToString(); + } + if (Skip != null && Take == null) + { + if (this.OrderByValue == "ORDER BY ") this.OrderByValue += GetSelectValue.Split(',')[0]; + result = string.Format(PageTempalte, GetSelectValue, GetTableNameString, GetWhereValueString, GetGroupByString + HavingInfos, (Skip != null || Take != null) ? null : GetOrderByString, Skip.ObjToInt(), long.MaxValue); + } + else if (Skip == null && Take != null) + { + if (this.OrderByValue == "ORDER BY ") this.OrderByValue += GetSelectValue.Split(',')[0]; + result = string.Format(PageTempalte, GetSelectValue, GetTableNameString, GetWhereValueString, GetGroupByString + HavingInfos, GetOrderByString, 0, Take.ObjToInt()); + } + else if (Skip != null && Take != null) + { + if (this.OrderByValue == "ORDER BY ") this.OrderByValue += GetSelectValue.Split(',')[0]; + result = string.Format(PageTempalte, GetSelectValue, GetTableNameString, GetWhereValueString, GetGroupByString + HavingInfos, GetOrderByString, Skip.ObjToInt() > 0 ? Skip.ObjToInt() : 0, Take); + } + else + { + result = sql.ToString(); + } + this.OrderByValue = oldOrderValue; + return result; + } + public override string ToCountSql(string sql) + { + if (this.GroupByValue.HasValue() || this.IsDistinct) + { + return base.ToCountSql(sql); + } + else + { + return ToCountSqlString(); + } + } + #endregion + + #region Get SQL Partial + public override string GetSelectValue + { + get + { + string result = string.Empty; + if (this.SelectValue == null || this.SelectValue is string) + { + result = GetSelectValueByString(); + } + else + { + result = GetSelectValueByExpression(); + } + if (this.SelectType == ResolveExpressType.SelectMultiple) + { + this.SelectCacheKey = this.SelectCacheKey + string.Join("-", this.JoinQueryInfos.Select(it => it.TableName)); + } + if (IsDistinct) + { + result = " DISTINCT " + result; + } + if (this.SubToListParameters != null && this.SubToListParameters.Count != 0) + { + result = SubToListMethod(result); + } + return result; + } + } + + #endregion + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/SqlBuilder/MySqlUpdateBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/SqlBuilder/MySqlUpdateBuilder.cs new file mode 100644 index 000000000..d27ab07d6 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/MySql/SqlBuilder/MySqlUpdateBuilder.cs @@ -0,0 +1,231 @@ +using System.Globalization; +using System.Text; +using System.Text.RegularExpressions; + +namespace SqlSugar +{ + public class MySqlUpdateBuilder : UpdateBuilder + { + public override string SqlTemplateBatch + { + get + { + return @"UPDATE {1} S {2} INNER JOIN ${{0}} SET {0} "; + } + } + public override string SqlTemplateJoin + { + get + { + return @" ( + {0} + + ) T ON {1} + "; + } + } + protected override string GetJoinUpdate(string columnsString, ref string whereString) + { + var joinString = $" {Builder.GetTranslationColumnName(this.TableName)} {Builder.GetTranslationColumnName(this.ShortName)} "; + foreach (var item in this.JoinInfos) + { + joinString += $"\r\n JOIN {Builder.GetTranslationColumnName(item.TableName)} {Builder.GetTranslationColumnName(item.ShortName)} ON {item.JoinWhere} "; + } + var tableName = joinString + "\r\n "; + return string.Format(SqlTemplate, tableName, columnsString, whereString); + } + protected override string TomultipleSqlString(List> groupList) + { + Check.Exception(PrimaryKeys == null || PrimaryKeys.Count == 0, " Update List need Primary key"); + int pageSize = 200; + int pageIndex = 1; + int totalRecord = groupList.Count; + int pageCount = (totalRecord + pageSize - 1) / pageSize; + StringBuilder batchUpdateSql = new StringBuilder(); + while (pageCount >= pageIndex) + { + StringBuilder updateTable = new StringBuilder(); + string setValues = string.Join(",", groupList.First().Where(it => it.IsPrimarykey == false && (it.IsIdentity == false || (IsOffIdentity && it.IsIdentity))).Select(it => + { + if (SetValues.IsValuable()) + { + var setValue = SetValues.Where(sv => sv.Key == Builder.GetTranslationColumnName(it.DbColumnName)); + if (setValue?.Any() == true) + { + return setValue.First().Value; + } + } + var result = string.Format("S.{0}=T.{0}", Builder.GetTranslationColumnName(it.DbColumnName)); + return result; + })); + batchUpdateSql.AppendFormat(SqlTemplateBatch.ToString(), setValues, GetTableNameStringNoWith, TableWithString); + int i = 0; + foreach (var columns in groupList.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList()) + { + var isFirst = i == 0; + if (!isFirst) + { + updateTable.Append(SqlTemplateBatchUnion); + } + updateTable.Append("\r\n SELECT " + string.Join(",", columns.Select(it => string.Format(SqlTemplateBatchSelect, base.GetDbColumn(it, FormatValue(it.Value, it.PropertyName)), this.Builder.GetTranslationColumnName(it.DbColumnName))))); + ++i; + } + pageIndex++; + updateTable.Append("\r\n"); + string whereString = null; + if (this.WhereValues.HasValue()) + { + foreach (var item in WhereValues) + { + var isFirst = whereString == null; + whereString += (isFirst ? null : " AND "); + whereString += Regex.Replace(item, " \\" + this.Builder.SqlTranslationLeft, "S." + this.Builder.SqlTranslationLeft); + } + } + if (PrimaryKeys.HasValue()) + { + foreach (var item in PrimaryKeys) + { + var isFirst = whereString == null; + whereString += (isFirst ? null : " AND "); + whereString += string.Format("S.{0}=T.{0}", Builder.GetTranslationColumnName(item)); + } + } + var format = string.Format(SqlTemplateJoin, updateTable, whereString); + batchUpdateSql.Replace("${0}", format); + batchUpdateSql.Append(';'); + } + batchUpdateSql = GetBatchUpdateSql(batchUpdateSql); + return batchUpdateSql.ToString(); + } + + private StringBuilder GetBatchUpdateSql(StringBuilder batchUpdateSql) + { + if (ReSetValueBySqlExpListType == null && ReSetValueBySqlExpList != null) + { + var result = batchUpdateSql.ToString(); + foreach (var item in ReSetValueBySqlExpList) + { + var dbColumnName = item.Value.DbColumnName; + if (item.Value.Type == ReSetValueBySqlExpListModelType.List) + { + result = result.Replace($"T.{dbColumnName}", "S." + dbColumnName + item.Value.Sql + "T." + dbColumnName); + } + else + { + result = result.Replace($"T.{dbColumnName}", item.Value.Sql.Replace(dbColumnName, "S." + dbColumnName)); + } + batchUpdateSql = new StringBuilder(result); + } + } + + return batchUpdateSql; + } + int i = 0; + public object FormatValue(object value, string name) + { + var n = "N"; + if (this.Context.CurrentConnectionConfig.MoreSettings?.DisableNvarchar == true) + { + n = ""; + } + if (value == null) + { + return "NULL"; + } + else + { + var type = UtilMethods.GetUnderType(value.GetType()); + if (type == UtilConstants.DateType) + { + return GetDateTimeString(value); + } + else if (value is DateTimeOffset) + { + return GetDateTimeOffsetString(value); + } + else if (type == UtilConstants.ByteArrayType) + { + string bytesString = "0x" + Convert.ToHexString((byte[])value); + if (bytesString == "0x") + { + bytesString = "''"; + } + return bytesString; + } + else if (type.IsEnum()) + { + if (this.Context.CurrentConnectionConfig.MoreSettings?.TableEnumIsString == true) + { + return value.ToSqlValue(); + } + else + { + return Convert.ToInt64(value); + } + } + else if (type == UtilConstants.LongType) + { + return GetString(value); + } + else if (type == UtilConstants.IntType) + { + return GetString(value); + } + else if (value is decimal decValue) + { + return decValue.ToString(CultureInfo.InvariantCulture); + } + else if (value is double douValue) + { + return douValue.ToString(CultureInfo.InvariantCulture); + } + else if (type == UtilConstants.BoolType) + { + return value.ObjToBool() ? "1" : "0"; + } + else if (type == UtilConstants.StringType || type == UtilConstants.ObjType) + { + ++i; + var parameterName = this.Builder.SqlParameterKeyWord + name + "_" + i; + this.Parameters.Add(new SugarParameter(parameterName, value)); + return parameterName; + } + else + { + return n + "'" + GetString(value) + "'"; + } + } + } + + private object GetDateTimeString(object value) + { + var date = value.ObjToDate(); + if (date < UtilMethods.GetMinDate(this.Context.CurrentConnectionConfig)) + { + date = UtilMethods.GetMinDate(this.Context.CurrentConnectionConfig); + } + return "'" + date.ToString("yyyy-MM-dd HH:mm:ss.fff") + "'"; + } + + private object GetDateTimeOffsetString(object value) + { + var date = UtilMethods.ConvertFromDateTimeOffset((DateTimeOffset)value); + if (date < UtilMethods.GetMinDate(this.Context.CurrentConnectionConfig)) + { + date = UtilMethods.GetMinDate(this.Context.CurrentConnectionConfig); + } + return "'" + date.ToString("yyyy-MM-dd HH:mm:ss.fff") + "'"; + } + + private string GetString(object value) + { + var result = value.ToString(); + if (result.HasValue() && result.Contains('\\')) + { + result = result.Replace("\\", "\\\\"); + } + return result; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/CodeFirst/OracleCodeFirst.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/CodeFirst/OracleCodeFirst.cs new file mode 100644 index 000000000..f157d3ab8 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/CodeFirst/OracleCodeFirst.cs @@ -0,0 +1,63 @@ +namespace SqlSugar +{ + public class OracleCodeFirst : CodeFirstProvider + { + public OracleCodeFirst() + { + if (DefultLength == 0) + DefultLength = 40; + } + protected override int DefultLength { get; set; } + + protected override void GetDbType(EntityColumnInfo item, Type propertyType, DbColumnInfo result) + { + if (!string.IsNullOrEmpty(item.DataType)) + { + result.DataType = item.DataType; + } + else if (item.DataType == null && item.UnderType == UtilConstants.LongType) + { + result.Length = 0; + result.DecimalDigits = 0; + result.DataType = "NUMBER(19,0)"; + } + else if (item.DataType == null && item.UnderType == UtilConstants.BoolType) + { + result.Length = 0; + result.DecimalDigits = 0; + result.DataType = "NUMBER(1,0)"; + } + //else if (item.DataType == null && item.UnderType == UtilConstants.IntType) + //{ + // result.Length = 0; + // result.DecimalDigits = 0; + // result.DataType = "NUMBER(9,0)"; + //} + else if (propertyType.IsEnum()) + { + result.DataType = this.Context.Ado.DbBind.GetDbTypeName(item.Length > 9 ? UtilConstants.LongType.Name : UtilConstants.IntType.Name); + } + else + { + if (propertyType.Name.Equals("Guid", StringComparison.CurrentCultureIgnoreCase)) + { + result.DataType = this.Context.Ado.DbBind.GetDbTypeName(UtilConstants.StringType.Name); + if (result.Length <= 1) + { + result.Length = 36; + } + } + else + { + result.DataType = this.Context.Ado.DbBind.GetDbTypeName(propertyType.Name); + } + } + } + + protected override void KeyAction(EntityColumnInfo item, DbColumnInfo dbColumn, out bool pkDiff, out bool idEntityDiff) + { + pkDiff = item.IsPrimarykey != dbColumn.IsPrimarykey; + idEntityDiff = false; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/DbBind/OracleDbBind.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/DbBind/OracleDbBind.cs new file mode 100644 index 000000000..47deba041 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/DbBind/OracleDbBind.cs @@ -0,0 +1,136 @@ +namespace SqlSugar +{ + public class OracleDbBind : DbBindProvider + { + public override string GetDbTypeName(string csharpTypeName) + { + if (csharpTypeName == UtilConstants.ByteArrayType.Name) + return "blob"; + if (csharpTypeName.Equals("int32", StringComparison.CurrentCultureIgnoreCase)) + csharpTypeName = "int"; + if (csharpTypeName.Equals("int16", StringComparison.CurrentCultureIgnoreCase)) + csharpTypeName = "short"; + if (csharpTypeName.Equals("int64", StringComparison.CurrentCultureIgnoreCase)) + csharpTypeName = "long"; + if (csharpTypeName.ToLower().IsIn("boolean", "bool")) + csharpTypeName = "bool"; + if (csharpTypeName == "Guid") + csharpTypeName = "string"; + if (csharpTypeName == "DateTimeOffset") + csharpTypeName = "DateTime"; + var mappings = this.MappingTypes.Where(it => it.Value.ToString().Equals(csharpTypeName, StringComparison.CurrentCultureIgnoreCase)); + return mappings.HasValue() ? mappings.First().Key : "varchar"; + } + public override string GetPropertyTypeName(string dbTypeName) + { + dbTypeName = dbTypeName.ToLower(); + var propertyTypes = MappingTypes.Where(it => it.Value.ToString().Equals(dbTypeName, StringComparison.CurrentCultureIgnoreCase) || it.Key.Equals(dbTypeName, StringComparison.CurrentCultureIgnoreCase)); + if (dbTypeName == "int32") + { + return "int"; + } + else if (dbTypeName == "int64") + { + return "long"; + } + else if (dbTypeName == "int16") + { + return "short"; + } + else if (propertyTypes == null) + { + return "other"; + } + else if (dbTypeName == "xml" || dbTypeName == "string") + { + return "string"; + } + if (dbTypeName == "byte[]") + { + return "byte[]"; + } + else if (propertyTypes?.Any() != true) + { + Check.ThrowNotSupportedException(string.Format(" \"{0}\" Type NotSupported, DbBindProvider.GetPropertyTypeName error.", dbTypeName)); + return null; + } + else if (propertyTypes.First().Value == CSharpDataType.byteArray) + { + return "byte[]"; + } + else + { + return propertyTypes.First().Value.ToString(); + } + } + public override List> MappingTypes + { + get + { + var extService = this.Context.CurrentConnectionConfig.ConfigureExternalServices; + if (extService?.AppendDataReaderTypeMappings.HasValue() == true) + { + return extService.AppendDataReaderTypeMappings.Union(MappingTypesConst).ToList(); + } + else + { + return MappingTypesConst; + } + } + } + public static List> MappingTypesConst = new List>() + { + new KeyValuePair("int",CSharpDataType.@int), + new KeyValuePair("integer",CSharpDataType.@int), + new KeyValuePair("interval year to month",CSharpDataType.@int), + new KeyValuePair("interval day to second",CSharpDataType.TimeSpan), + new KeyValuePair("intervalds",CSharpDataType.TimeSpan), + + new KeyValuePair("number",CSharpDataType.@int), + new KeyValuePair("number",CSharpDataType.@float), + new KeyValuePair("number",CSharpDataType.@short), + new KeyValuePair("number",CSharpDataType.@byte), + new KeyValuePair("number",CSharpDataType.@double), + new KeyValuePair("binaryfloat",CSharpDataType.@float), + new KeyValuePair("binarydouble",CSharpDataType.@double), + new KeyValuePair("number",CSharpDataType.@long), + new KeyValuePair("number",CSharpDataType.@bool), + new KeyValuePair("number",CSharpDataType.@decimal), + new KeyValuePair("number",CSharpDataType.Single), + new KeyValuePair("decimal",CSharpDataType.@decimal), + new KeyValuePair("decimal",CSharpDataType.Single), + + new KeyValuePair("varchar",CSharpDataType.@string), + new KeyValuePair("varchar2",CSharpDataType.@string), + new KeyValuePair("nvarchar2",CSharpDataType.@string), + new KeyValuePair("xmltype",CSharpDataType.@string), + new KeyValuePair("char",CSharpDataType.@string), + new KeyValuePair("nchar",CSharpDataType.@string), + new KeyValuePair("clob",CSharpDataType.@string), + new KeyValuePair("long",CSharpDataType.@string), + new KeyValuePair("nclob",CSharpDataType.@string), + new KeyValuePair("rowid",CSharpDataType.@string), + + new KeyValuePair("date",CSharpDataType.DateTime), + new KeyValuePair("timestamptz",CSharpDataType.DateTime), + new KeyValuePair("timestamp",CSharpDataType.DateTime), + new KeyValuePair("timestamp with local time zone",CSharpDataType.DateTime), + new KeyValuePair("timestamp with time zone",CSharpDataType.DateTime), + + new KeyValuePair("float",CSharpDataType.@decimal), + + new KeyValuePair("blob",CSharpDataType.byteArray), + new KeyValuePair("long raw",CSharpDataType.byteArray), + new KeyValuePair("longraw",CSharpDataType.byteArray), + new KeyValuePair("raw",CSharpDataType.byteArray), + new KeyValuePair("bfile",CSharpDataType.byteArray), + new KeyValuePair("varbinary",CSharpDataType.byteArray) }; + public override List StringThrow + { + get + { + return new List() { "int32", "datetime", "decimal", "double", "byte" }; + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/DbFirst/OracleDbFirst.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/DbFirst/OracleDbFirst.cs new file mode 100644 index 000000000..8ae9f9760 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/DbFirst/OracleDbFirst.cs @@ -0,0 +1,7 @@ +namespace SqlSugar +{ + public class OracleDbFirst : DbFirstProvider + { + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/DbMaintenance/OracleDbMaintenance.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/DbMaintenance/OracleDbMaintenance.cs new file mode 100644 index 000000000..23a5320f0 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/DbMaintenance/OracleDbMaintenance.cs @@ -0,0 +1,702 @@ +using System.Data; +using System.Data.Common; + +namespace SqlSugar +{ + public class OracleDbMaintenance : DbMaintenanceProvider + { + #region DML + protected override string GetDataBaseSql + { + get + { + throw new NotSupportedException(); + } + } + protected override string GetColumnInfosByTableNameSql + { + get + { + throw new NotSupportedException(); + } + } + protected override string GetTableInfoListSql + { + get + { + return @"SELECT table_name name , + (select COMMENTS from user_tab_comments where t.table_name=table_name ) as Description + + from user_tables t where + table_name!='HELP' + AND table_name NOT LIKE '%$%' + AND table_name NOT LIKE 'LOGMNRC_%' + AND table_name!='LOGMNRP_CTAS_PART_MAP' + AND table_name!='LOGMNR_LOGMNR_BUILDLOG' + AND table_name!='SQLPLUS_PRODUCT_PROFILE' + "; + } + } + protected override string GetViewInfoListSql + { + get + { + return @"select view_name name from user_views + WHERE VIEW_name NOT LIKE '%$%' + AND VIEW_NAME !='PRODUCT_PRIVS' + AND VIEW_NAME NOT LIKE 'MVIEW_%' "; + } + } + #endregion + + #region DDL + protected override string IsAnyIndexSql + { + get + { + return "SELECT NVL2((SELECT INDEX_NAME FROM ALL_INDEXES WHERE INDEX_NAME=UPPER('{0}') AND OWNER = USER ),1,0)+NVL2((SELECT CONSTRAINT_NAME FROM ALL_CONSTRAINTS WHERE CONSTRAINT_NAME=UPPER('{0}') AND OWNER = USER),2,0) AS ROWCOUNT FROM DUAL"; + } + } + protected override string CreateIndexSql + { + get + { + return "CREATE {3} INDEX Index_{0}_{2} ON {0}({1})"; + } + } + protected override string AddDefaultValueSql + { + get + { + return "ALTER TABLE {0} MODIFY({1} DEFAULT '{2}')"; + } + } + protected override string CreateDataBaseSql + { + get + { + return "CREATE DATABASE {0}"; + } + } + protected override string AddPrimaryKeySql + { + get + { + return "ALTER TABLE {0} ADD CONSTRAINT {1} PRIMARY KEY({2})"; + } + } + protected override string AddColumnToTableSql + { + get + { + return "ALTER TABLE {0} ADD ({1} {2}{3} {4} {5} {6})"; + } + } + protected override string AlterColumnToTableSql + { + get + { + return "ALTER TABLE {0} modify ({1} {2}{3} {4} {5} {6}) "; + } + } + protected override string BackupDataBaseSql + { + get + { + return @"USE master;BACKUP DATABASE {0} TO disk = '{1}'"; + } + } + protected override string CreateTableSql + { + get + { + return "CREATE TABLE {0}(\r\n{1})"; + } + } + protected override string CreateTableColumn + { + get + { + return "{0} {1}{2} {3} {4} {5}"; + } + } + protected override string TruncateTableSql + { + get + { + return "TRUNCATE TABLE {0}"; + } + } + protected override string BackupTableSql + { + get + { + return "create table {1} as select * from {2} where ROWNUM<={0}"; + } + } + protected override string DropTableSql + { + get + { + return "DROP TABLE {0}"; + } + } + protected override string DropColumnToTableSql + { + get + { + return "ALTER TABLE {0} DROP COLUMN {1}"; + } + } + protected override string DropConstraintSql + { + get + { + return "ALTER TABLE {0} DROP CONSTRAINT {1}"; + } + } + protected override string RenameColumnSql + { + get + { + return "ALTER TABLE {0} rename column {1} to {2}"; + } + } + protected override string AddColumnRemarkSql + { + get + { + return "comment on column {1}.{0} is '{2}'"; + } + } + + protected override string DeleteColumnRemarkSql + { + get + { + return "comment on column {1}.{0} is ''"; + } + } + + protected override string IsAnyColumnRemarkSql + { + get + { + return "select * from user_col_comments where Table_Name='{1}' AND COLUMN_NAME='{0}' order by column_name"; + } + } + + protected override string AddTableRemarkSql + { + get + { + return "comment on table {0} is '{1}'"; + } + } + + protected override string DeleteTableRemarkSql + { + get + { + return "comment on table {0} is ''"; + } + } + + protected override string IsAnyTableRemarkSql + { + get + { + return "select * from user_tab_comments where Table_Name='{0}'order by Table_Name"; + } + } + + protected override string RenameTableSql + { + get + { + return "alter table {0} rename to {1}"; + } + } + protected override string IsAnyProcedureSql + { + get + { + return "SELECT COUNT(*) FROM user_objects WHERE OBJECT_TYPE = 'PROCEDURE' AND OBJECT_NAME ='{0}'"; + } + } + #endregion + + #region Check + protected override string CheckSystemTablePermissionsSql + { + get + { + return "select t.table_name from user_tables t where rownum=1"; + } + } + #endregion + + #region Scattered + protected override string CreateTableNull + { + get + { + return " NULL "; + } + } + protected override string CreateTableNotNull + { + get + { + return " NOT NULL "; + } + } + protected override string CreateTablePirmaryKey + { + get + { + return "PRIMARY KEY"; + } + } + protected override string CreateTableIdentity + { + get + { + return ""; + } + } + #endregion + + #region Methods + public override bool IsAnyTable(string tableName, bool isCache = true) + { + if (isCache) + { + return base.IsAnyTable(tableName, isCache); + } + else + { + if (tableName.Contains('"')) + { + tableName = SqlBuilder.GetNoTranslationColumnName(tableName); + } + return this.Context.Ado.GetInt(@" + SELECT COUNT(table_name) + FROM user_tables + WHERE UPPER(table_name) = UPPER(@p)", new { p = tableName }) > 0; + } + } + public override bool UpdateColumn(string tableName, DbColumnInfo column) + { + ConvertCreateColumnInfo(column); + var oldColumn = this.Context.DbMaintenance.GetColumnInfosByTableName(tableName, false) + .FirstOrDefault(it => it.DbColumnName.EqualCase(column.DbColumnName)); + if (oldColumn != null) + { + if (oldColumn.IsNullable == column.IsNullable) + { + var sql = GetUpdateColumnSqlOnlyType(tableName, column); + this.Context.Ado.ExecuteCommand(sql); + return true; + } + } + return base.UpdateColumn(tableName, column); + } + protected virtual string GetUpdateColumnSqlOnlyType(string tableName, DbColumnInfo columnInfo) + { + string columnName = this.SqlBuilder.GetTranslationColumnName(columnInfo.DbColumnName); + tableName = this.SqlBuilder.GetTranslationTableName(tableName); + string dataSize = GetSize(columnInfo); + string dataType = columnInfo.DataType; + string nullType = ""; + string primaryKey = null; + string identity = null; + string result = string.Format(this.AlterColumnToTableSql, tableName, columnName, dataType, dataSize, nullType, primaryKey, identity); + return result; + } + public override bool RenameTable(string oldTableName, string newTableName) + { + return base.RenameTable(SqlBuilder.GetTranslationColumnName(oldTableName), SqlBuilder.GetTranslationColumnName(newTableName)); + } + public override List GetDbTypes() + { + var result = this.Context.Ado.SqlQuery(@"SELECT DISTINCT DATA_TYPE +FROM DBA_TAB_COLUMNS +WHERE OWNER = user "); + result.Add("TIMESTAMP"); + result.Add("NCLOB"); + return result.Distinct().ToList(); + } + public override List GetTriggerNames(string tableName) + { + return this.Context.Ado.SqlQuery(@"SELECT trigger_name +FROM all_triggers +WHERE table_name = '" + tableName + "'"); + } + public override List GetFuncList() + { + return this.Context.Ado.SqlQuery(" SELECT object_name\r\nFROM all_objects\r\nWHERE object_type = 'FUNCTION' AND owner = USER "); + } + public override List GetIndexList(string tableName) + { + var sql = $"SELECT index_name FROM user_ind_columns\r\nWHERE upper(table_name) = upper('{tableName}')"; + return this.Context.Ado.SqlQuery(sql).Distinct().ToList(); + } + public override List GetProcList(string dbName) + { + var sql = $"SELECT OBJECT_NAME FROM ALL_OBJECTS WHERE OBJECT_TYPE = 'PROCEDURE' AND OWNER =user "; + return this.Context.Ado.SqlQuery(sql); + } + public override bool AddColumn(string tableName, DbColumnInfo columnInfo) + { + if (columnInfo.DataType == "varchar" && columnInfo.Length == 0) + { + columnInfo.DataType = "varchar2"; + columnInfo.Length = 50; + } + ConvertCreateColumnInfo(columnInfo); + return base.AddColumn(tableName, columnInfo); + } + public override bool CreateIndex(string tableName, string[] columnNames, bool isUnique = false) + { + string sql = string.Format(CreateIndexSql, tableName, string.Join(",", columnNames), string.Join("_", columnNames.Select(it => (it + "abc").Substring(0, 3))), isUnique ? "UNIQUE" : ""); + this.Context.Ado.ExecuteCommand(sql); + return true; + } + public override bool AddDefaultValue(string tableName, string columnName, string defaultValue) + { + columnName = SqlBuilder.GetTranslationColumnName(columnName); + tableName = SqlBuilder.GetTranslationColumnName(tableName); + + if (defaultValue == "''") + { + defaultValue = ""; + } + if (defaultValue.ToLower().IsIn("sysdate")) + { + var template = AddDefaultValueSql.Replace("'", ""); + string sql = string.Format(template, tableName, columnName, defaultValue); + this.Context.Ado.ExecuteCommand(sql); + return true; + } + else + { + return base.AddDefaultValue(tableName, columnName, defaultValue); + } + } + public override bool CreateDatabase(string databaseDirectory = null) + { + if (this.Context.Ado.IsValidConnection()) + { + return true; + } + Check.ExceptionEasy("Oracle no support create database ", "Oracle不支持建库方法,请写有效连接字符串可以正常运行该方法。"); + return true; + } + public override bool CreateDatabase(string databaseName, string databaseDirectory = null) + { + if (this.Context.Ado.IsValidConnection()) + { + return true; + } + Check.ExceptionEasy("Oracle no support create database ", "Oracle不支持建库方法,请写有效连接字符串可以正常运行该方法。"); + return true; + } + public override bool AddRemark(EntityInfo entity) + { + var db = this.Context; + var columns = entity.Columns.Where(it => it.IsIgnore == false).ToList(); + + foreach (var item in columns) + { + if (item.ColumnDescription != null) + { + //column remak + if (db.DbMaintenance.IsAnyColumnRemark(item.DbColumnName.ToUpper(IsUppper), item.DbTableName.ToUpper(IsUppper))) + { + db.DbMaintenance.DeleteColumnRemark(this.SqlBuilder.GetTranslationColumnName(item.DbColumnName), this.SqlBuilder.GetTranslationColumnName(item.DbTableName)); + db.DbMaintenance.AddColumnRemark(this.SqlBuilder.GetTranslationColumnName(item.DbColumnName), this.SqlBuilder.GetTranslationColumnName(item.DbTableName), item.ColumnDescription); + } + else + { + db.DbMaintenance.AddColumnRemark(this.SqlBuilder.GetTranslationColumnName(item.DbColumnName), this.SqlBuilder.GetTranslationColumnName(item.DbTableName), item.ColumnDescription); + } + } + } + + //table remak + if (entity.TableDescription != null) + { + if (db.DbMaintenance.IsAnyTableRemark(entity.DbTableName)) + { + db.DbMaintenance.DeleteTableRemark(SqlBuilder.GetTranslationColumnName(entity.DbTableName)); + db.DbMaintenance.AddTableRemark(SqlBuilder.GetTranslationColumnName(entity.DbTableName), entity.TableDescription); + } + else + { + db.DbMaintenance.AddTableRemark(SqlBuilder.GetTranslationColumnName(entity.DbTableName), entity.TableDescription); + } + } + return true; + } + public override List GetColumnInfosByTableName(string tableName, bool isCache = true) + { + string cacheKey = "DbMaintenanceProvider.GetColumnInfosByTableName." + this.SqlBuilder.GetNoTranslationColumnName(tableName).ToLower(); + cacheKey = GetCacheKey(cacheKey); + if (!isCache) + return GetColumnInfosByTableName(tableName); + else + return this.Context.Utilities.GetReflectionInoCacheInstance().GetOrCreate(cacheKey, + () => + { + return GetColumnInfosByTableName(tableName); + + }); + } + + private List GetColumnInfosByTableName(string tableName) + { + List columns = GetOracleDbType(tableName); + string sql = "select * /* " + Guid.NewGuid() + " */ from " + SqlBuilder.GetTranslationTableName(SqlBuilder.GetNoTranslationColumnName(tableName)) + " WHERE 1=2 "; + if (!IsAnyTable(tableName, false) && !GetViewInfoList(false).Any(it => it.Name.EqualCase(tableName))) + { + return new List(); + } + this.Context.Utilities.RemoveCache>("DbMaintenanceProvider.GetFieldComment." + tableName); + this.Context.Utilities.RemoveCache>("DbMaintenanceProvider.GetPrimaryKeyByTableNames." + this.SqlBuilder.GetNoTranslationColumnName(tableName).ToLower()); + var oldIsEnableLog = this.Context.Ado.IsEnableLogEvent; + this.Context.Ado.IsEnableLogEvent = false; + using (DbDataReader reader = (DbDataReader)this.Context.Ado.GetDataReader(sql)) + { + this.Context.Ado.IsEnableLogEvent = oldIsEnableLog; + List result = new List(); + var schemaTable = reader.GetSchemaTable(); + foreach (System.Data.DataRow row in schemaTable.Rows) + { + DbColumnInfo column = new DbColumnInfo() + { + TableName = tableName, + DataType = row["DataType"].ToString().Replace("System.", "").Trim(), + IsNullable = (bool)row["AllowDBNull"], + //IsIdentity = (bool)row["IsAutoIncrement"], + ColumnDescription = GetFieldComment(tableName, row["ColumnName"].ToString()), + DbColumnName = row["ColumnName"].ToString(), + //DefaultValue = row["defaultValue"].ToString(), + IsPrimarykey = GetPrimaryKeyByTableNames(tableName).Any(it => it.Equals(row["ColumnName"].ToString(), StringComparison.CurrentCultureIgnoreCase)), + Length = row["ColumnSize"].ObjToInt(), + Scale = row["numericscale"].ObjToInt() + }; + var current = columns.FirstOrDefault(it => it.DbColumnName.EqualCase(column.DbColumnName)); + if (current != null) + { + column.OracleDataType = current.DataType; + if (current.DataType.EqualCase("number")) + { + column.Length = row["numericprecision"].ObjToInt(); + column.Scale = row["numericscale"].ObjToInt(); + column.DecimalDigits = row["numericscale"].ObjToInt(); + if (column.Length == 38 && column.Scale == 0) + { + column.Length = 22; + } + } + if (current.DefaultValue != null) + { + column.DefaultValue = current.DefaultValue.TrimEnd('\'').TrimStart('\''); + } + } + result.Add(column); + } + return result; + } + } + + private List GetOracleDbType(string tableName) + { + var sql0 = $@"select + t1.table_name as TableName, + t6.comments, + t1.column_id, + t1.column_name as DbColumnName, + t5.comments, + t1.data_type as DataType, + t1.data_length as Length, + t1.char_length, + t1.data_precision, + t1.data_scale, + t1.nullable, + t1.data_default as DefaultValue, + t4.index_name, + t4.column_position, + t4.descend + from user_tab_columns t1 + left join (select t2.table_name, + t2.column_name, + t2.column_position, + t2.descend, + t3.index_name + from user_ind_columns t2 + left join user_indexes t3 + on t2.table_name = t3.table_name and t2.index_name = t3.index_name + and t3.status = 'valid' and t3.uniqueness = 'unique') t4 --unique:唯一索引 + on t1.table_name = t4.table_name and t1.column_name = t4.column_name + left join user_col_comments t5 on t1.table_name = t5.table_name and t1.column_name = t5.column_name + left join user_tab_comments t6 on t1.table_name = t6.table_name + where upper(t1.table_name)=upper('{tableName}') + order by t1.table_name, t1.column_id"; + + var columns = this.Context.Ado.SqlQuery(sql0); + return columns; + } + + private List GetPrimaryKeyByTableNames(string tableName) + { + string cacheKey = "DbMaintenanceProvider.GetPrimaryKeyByTableNames." + this.SqlBuilder.GetNoTranslationColumnName(tableName).ToLower(); + cacheKey = GetCacheKey(cacheKey); + return this.Context.Utilities.GetReflectionInoCacheInstance().GetOrCreate(cacheKey, + () => + { + var oldIsEnableLog = this.Context.Ado.IsEnableLogEvent; + this.Context.Ado.IsEnableLogEvent = false; + string sql = @" select distinct cu.COLUMN_name KEYNAME from user_cons_columns cu, user_constraints au + where cu.constraint_name = au.constraint_name + and au.constraint_type = 'P' and au.table_name = '" + tableName.ToUpper(IsUppper) + @"'"; + var pks = this.Context.Ado.SqlQuery(sql); + this.Context.Ado.IsEnableLogEvent = oldIsEnableLog; + return pks; + }); + } + + public string GetTableComment(string tableName) + { + string cacheKey = "DbMaintenanceProvider.GetTableComment." + tableName; + var comments = this.Context.Utilities.GetReflectionInoCacheInstance().GetOrCreate(cacheKey, + () => + { + string sql = "SELECT COMMENTS FROM USER_TAB_COMMENTS WHERE TABLE_NAME =@tableName ORDER BY TABLE_NAME"; + var oldIsEnableLog = this.Context.Ado.IsEnableLogEvent; + this.Context.Ado.IsEnableLogEvent = false; + var pks = this.Context.Ado.SqlQuery(sql, new { tableName = tableName.ToUpper(IsUppper) }); + this.Context.Ado.IsEnableLogEvent = oldIsEnableLog; + return pks; + }); + return comments.HasValue() ? comments.First() : ""; + } + + public string GetFieldComment(string tableName, string filedName) + { + string cacheKey = "DbMaintenanceProvider.GetFieldComment." + tableName; + var comments = this.Context.Utilities.GetReflectionInoCacheInstance().GetOrCreate(cacheKey, + () => + { + string sql = "SELECT TABLE_NAME AS TableName, COLUMN_NAME AS DbColumnName,COMMENTS AS ColumnDescription FROM user_col_comments WHERE TABLE_NAME =@tableName ORDER BY TABLE_NAME"; + var oldIsEnableLog = this.Context.Ado.IsEnableLogEvent; + this.Context.Ado.IsEnableLogEvent = false; + var pks = this.Context.Ado.SqlQuery(sql, new { tableName = tableName.ToUpper(IsUppper) }); + this.Context.Ado.IsEnableLogEvent = oldIsEnableLog; + return pks; + }); + return comments.HasValue() ? comments.FirstOrDefault(it => it.DbColumnName.EqualCase(filedName))?.ColumnDescription : ""; + + } + + public override bool CreateTable(string tableName, List columns, bool isCreatePrimaryKey = true) + { + if (columns.HasValue()) + { + foreach (var item in columns) + { + ConvertCreateColumnInfo(item); + if (item.DbColumnName.Equals("GUID", StringComparison.CurrentCultureIgnoreCase) && item.Length == 0) + { + item.Length = 50; + } + if (item.DataType == "varchar" && item.Length == 0) + { + item.Length = 50; + } + if (item.IsIdentity && this.Context.CurrentConnectionConfig?.MoreSettings?.EnableOracleIdentity == true) + { + item.DataType = "NUMBER GENERATED ALWAYS AS IDENTITY"; + } + if (item.DataType != null && this.Context.CurrentConnectionConfig?.MoreSettings?.OracleCodeFirstNvarchar2 == true) + { + if (!item.DataType.Contains("nvarchar2", StringComparison.CurrentCultureIgnoreCase)) + { + item.DataType = item.DataType.ToLower().Replace("varchar", "nvarchar2"); + } + } + } + } + string sql = GetCreateTableSql(tableName, columns); + this.Context.Ado.ExecuteCommand(sql); + if (isCreatePrimaryKey) + { + var pkColumns = columns.Where(it => it.IsPrimarykey).ToList(); + if (pkColumns.Count <= 1) + { + foreach (var item in pkColumns) + { + this.Context.DbMaintenance.AddPrimaryKey(tableName, item.DbColumnName); + } + } + else + { + var addItems = pkColumns.Select(it => it.DbColumnName).ToArray(); + this.Context.DbMaintenance.AddPrimaryKeys(tableName, addItems); + } + } + return true; + } + public override bool IsAnyIndex(string indexName) + { + string sql = string.Format(this.IsAnyIndexSql, indexName); + return this.Context.Ado.GetInt(sql) == 1; + } + public override bool IsAnyConstraint(string constraintName) + { + string sql = string.Format(this.IsAnyIndexSql, constraintName); + int res = this.Context.Ado.GetInt(sql); + return res == 2 || res == 3; + } + public override bool DropIndex(string indexName, string tableName) + { + return DropIndex(indexName); + } + #endregion + + #region Helper + public bool IsUppper + { + get + { + if (this.Context.CurrentConnectionConfig.MoreSettings == null) + { + return true; + } + else + { + return this.Context.CurrentConnectionConfig.MoreSettings.IsAutoToUpper == true; + } + } + } + private static void ConvertCreateColumnInfo(DbColumnInfo x) + { + string[] array = new string[] { "int", "date", "clob", "nclob" }; + if (x.OracleDataType.HasValue()) + { + x.DataType = x.OracleDataType; + } + if (array.Contains(x.DataType?.ToLower())) + { + x.Length = 0; + x.DecimalDigits = 0; + } + if (x.DecimalDigits > 0 && x.DataType?.ToLower()?.IsIn("varchar", "clob", "varchar2", "nvarchar2", "nvarchar") == true) + { + x.DecimalDigits = 0; + } + } + #endregion + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/Deleteable/OracleDeleteable.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/Deleteable/OracleDeleteable.cs new file mode 100644 index 000000000..75112813c --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/Deleteable/OracleDeleteable.cs @@ -0,0 +1,10 @@ +namespace SqlSugar +{ + public class OracleDeleteable : DeleteableProvider where T : class, new() + { + protected override List GetIdentityKeys() + { + return this.EntityInfo.Columns.Where(it => it.OracleSequenceName.HasValue()).Select(it => it.DbColumnName).ToList(); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/Insertable/OracleInsertable.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/Insertable/OracleInsertable.cs new file mode 100644 index 000000000..ef89e87c0 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/Insertable/OracleInsertable.cs @@ -0,0 +1,219 @@ +using System.Text; + +namespace SqlSugar +{ + public class OracleInsertable : InsertableProvider where T : class, new() + { + + protected override List GetIdentityKeys() + { + return this.EntityInfo.Columns.Where(it => it.OracleSequenceName.HasValue()).Select(it => it.DbColumnName).ToList(); + } + protected string GetSeqName() + { + return this.EntityInfo.Columns.Where(it => it.OracleSequenceName.HasValue()).Select(it => it.OracleSequenceName).First(); + } + protected List GetSeqNames() + { + return this.EntityInfo.Columns.Where(it => it.OracleSequenceName.HasValue()).Select(it => it.OracleSequenceName).ToList(); + } + public override int ExecuteReturnIdentity() + { + bool oldIsAuto = AutoBegin(); + + InsertBuilder.IsReturnIdentity = true; + PreToSql(); + string sql = InsertBuilder.ToSqlString(); + if (isIdEntityEnable()) + { + if (sql?.StartsWith("INSERT ALL") == true) + { + return this.UseParameter().ExecuteCommand(); + } + else + { + sql = sql + " RETURNING ID INTO :newId01 "; + } + InsertBuilder.Parameters.Add(new SugarParameter(":newId01", 0, true)); + } + RestoreMapping(); + var isDisableMasterSlaveSeparation = this.Context.Ado.IsDisableMasterSlaveSeparation; + this.Context.Ado.IsDisableMasterSlaveSeparation = true; + var count = Ado.ExecuteCommand(sql, InsertBuilder.Parameters == null ? null : InsertBuilder.Parameters.ToArray()); + var result = (this.GetIdentityKeys().IsNullOrEmpty() || count == 0) ? 0 : GetSeqValue(GetSeqName()).ObjToInt(); + this.Context.Ado.IsDisableMasterSlaveSeparation = isDisableMasterSlaveSeparation; + After(sql, result); + AutoEnd(oldIsAuto); + if (isIdEntityEnable()) + { + return this.InsertBuilder.Parameters.FirstOrDefault(it => it.ParameterName == ":newId01")?.Value?.ObjToInt() ?? 0; + } + return result; + } + private bool isIdEntityEnable() + { + return this.Context.CurrentConnectionConfig?.MoreSettings?.EnableOracleIdentity == true; + } + + public override long ExecuteReturnBigIdentity() + { + bool oldIsAuto = AutoBegin(); + + InsertBuilder.IsReturnIdentity = true; + PreToSql(); + string sql = InsertBuilder.ToSqlString(); + if (isIdEntityEnable()) + { + if (sql?.StartsWith("INSERT ALL") == true) + { + return this.UseParameter().ExecuteCommand(); + } + else + { + sql = sql + " RETURNING ID INTO :newId01 "; + } + InsertBuilder.Parameters.Add(new SugarParameter(":newId01", Convert.ToInt64(0), true)); + } + RestoreMapping(); + var isDisableMasterSlaveSeparation = this.Context.Ado.IsDisableMasterSlaveSeparation; + this.Context.Ado.IsDisableMasterSlaveSeparation = true; + var count = Ado.ExecuteCommand(sql, InsertBuilder.Parameters == null ? null : InsertBuilder.Parameters.ToArray()); + var result = (this.GetIdentityKeys().IsNullOrEmpty() || count == 0) ? 0 : Convert.ToInt64(GetSeqValue(GetSeqName())); + this.Context.Ado.IsDisableMasterSlaveSeparation = isDisableMasterSlaveSeparation; + After(sql, result); + AutoEnd(oldIsAuto); + if (isIdEntityEnable()) + { + return this.InsertBuilder.Parameters.FirstOrDefault(it => it.ParameterName == ":newId01")?.Value?.ObjToLong() ?? 0; + } + return result; + } + + public async override Task ExecuteReturnIdentityAsync() + { + bool oldIsAuto = AutoBegin(); + + InsertBuilder.IsReturnIdentity = true; + PreToSql(); + string sql = InsertBuilder.ToSqlString(); + if (isIdEntityEnable()) + { + if (sql?.StartsWith("INSERT ALL") == true) + { + return await UseParameter().ExecuteCommandAsync().ConfigureAwait(false); + } + else + { + sql = sql + " RETURNING ID INTO :newId01 "; + } + InsertBuilder.Parameters.Add(new SugarParameter(":newId01", 0, true)); + } + RestoreMapping(); + var isDisableMasterSlaveSeparation = this.Context.Ado.IsDisableMasterSlaveSeparation; + this.Context.Ado.IsDisableMasterSlaveSeparation = true; + var count = await Ado.ExecuteCommandAsync(sql, InsertBuilder.Parameters == null ? null : InsertBuilder.Parameters.ToArray()).ConfigureAwait(false); + var result = (this.GetIdentityKeys().IsNullOrEmpty() || count == 0) ? 0 : GetSeqValue(GetSeqName()).ObjToInt(); + this.Context.Ado.IsDisableMasterSlaveSeparation = isDisableMasterSlaveSeparation; + After(sql, result); + AutoEnd(oldIsAuto); + if (isIdEntityEnable()) + { + return this.InsertBuilder.Parameters.FirstOrDefault(it => it.ParameterName == ":newId01")?.Value?.ObjToInt() ?? 0; + } + return result; + } + + public async override Task ExecuteReturnBigIdentityAsync() + { + bool oldIsAuto = AutoBegin(); + + InsertBuilder.IsReturnIdentity = true; + PreToSql(); + string sql = InsertBuilder.ToSqlString(); + if (isIdEntityEnable()) + { + if (sql?.StartsWith("INSERT ALL") == true) + { + return await UseParameter().ExecuteCommandAsync().ConfigureAwait(false); + } + else + { + sql = sql + " RETURNING ID INTO :newId01 "; + } + InsertBuilder.Parameters.Add(new SugarParameter(":newId01", Convert.ToInt64(0), true)); + } + RestoreMapping(); + var isDisableMasterSlaveSeparation = this.Context.Ado.IsDisableMasterSlaveSeparation; + this.Context.Ado.IsDisableMasterSlaveSeparation = true; + var count = await Ado.ExecuteCommandAsync(sql, InsertBuilder.Parameters == null ? null : InsertBuilder.Parameters.ToArray()).ConfigureAwait(false); + var result = (this.GetIdentityKeys().IsNullOrEmpty() || count == 0) ? 0 : Convert.ToInt64(GetSeqValue(GetSeqName())); + this.Context.Ado.IsDisableMasterSlaveSeparation = isDisableMasterSlaveSeparation; + After(sql, result); + AutoEnd(oldIsAuto); + if (isIdEntityEnable()) + { + return this.InsertBuilder.Parameters.FirstOrDefault(it => it.ParameterName == ":newId01")?.Value?.ObjToLong() ?? 0; + } + return result; + } + + private void AutoEnd(bool oldIsAuto) + { + if (oldIsAuto) + { + this.Context.Context.CurrentConnectionConfig.IsAutoCloseConnection = oldIsAuto; + if (this.Ado.Transaction == null) + this.Context.Ado.Close(); + } + } + + private bool AutoBegin() + { + var oldIsAuto = this.Context.Context.CurrentConnectionConfig.IsAutoCloseConnection; + if (this.Context.Context.CurrentConnectionConfig.IsAutoCloseConnection) + { + this.Context.Context.CurrentConnectionConfig.IsAutoCloseConnection = false; + } + + return oldIsAuto; + } + private object GetSeqValue(string seqName) + { + return Ado.GetScalar(" SELECT " + seqName + ".currval FROM DUAL"); + } + protected override void PreToSql() + { + var identities = GetSeqNames(); + var insertCount = InsertObjs.Length; + InsertBuilder.OracleSeqInfoList = new Dictionary(); + if ((identities.HasValue() && insertCount > 1) || InsertBuilder.IsBlukCopy) + { + Check.Exception(identities.Count != identities.Distinct().Count(), "The field sequence needs to be unique"); + foreach (var seqName in identities) + { + int seqBeginValue = 0; + seqBeginValue = this.Ado.GetInt("select " + seqName + ".Nextval from dual"); + //Console.WriteLine(seqBeginValue); + var nextLength = insertCount - 1; + if (nextLength > 0) + { + StringBuilder sb = new StringBuilder(); + sb.AppendLine(" select " + seqName + ".nextval,t.* from ("); + for (int i = 0; i < nextLength; i++) + { + sb.AppendLine(" select 1 from dual"); + if (i < (nextLength - 1)) + { + sb.AppendLine("union all"); + } + } + sb.AppendLine(" )t"); + this.Ado.SqlQuery(sb.ToString()); + } + InsertBuilder.OracleSeqInfoList.Add(seqName, seqBeginValue); + } + } + base.PreToSql(); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/OracleProvider.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/OracleProvider.cs new file mode 100644 index 000000000..000310789 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/OracleProvider.cs @@ -0,0 +1,341 @@ +using Oracle.ManagedDataAccess.Client; + +using System.Data; +using System.Data.Common; +using System.Text.RegularExpressions; +namespace SqlSugar +{ + public class OracleProvider : AdoProvider + { + public OracleProvider() + { + //this.FormatSql = sql => + //{ + // sql = sql.Replace("+@", "+:"); + // if (sql.HasValue()&&sql.Contains('@')) { + // var exceptionalCaseInfo = Regex.Matches(sql, @"\'[^\=]*?\@.*?\'|[\.,\w]+\@[\.,\w]+ | [\.,\w]+\@[\.,\w]+|[\.,\w]+\@[\.,\w]+ |\d+\@\d|\@\@|\w{1,25}\.""\w{1,25}""\@\w{1,25}"); + // if (exceptionalCaseInfo != null) { + // foreach (var item in exceptionalCaseInfo.Cast()) + // { + // if (item.Value != null && item.Value.IndexOf(",") == 1&&Regex.IsMatch(item.Value, @"^ \,\@\w+$")) + // { + // continue; + // } + // else if (item.Value != null &&Regex.IsMatch(item.Value.Trim(), @"^\w+\,\@\w+\,$")) + // { + // continue; + // } + // else if (item.Value != null && item.Value.ObjToString().Contains("||") && Regex.IsMatch(item.Value.Replace(" ","").Trim(), @"\|\|@\w+\|\|")) + // { + // continue; + // } + // else if (item.Value != null&& Regex.IsMatch(item.Value.Replace(" ", "").Trim(), @"\(\@\w+\,")) + // { + // continue; + // } + // else if (item.Value != null &&item.Value.Contains("=")&& Regex.IsMatch(item.Value, @"\w+ \@\w+[ ]{0,1}\=[ ]{0,1}\'")) + // { + // continue; + // } + // sql = sql.Replace(item.Value, item.Value.Replace("@", UtilConstants.ReplaceKey)); + // } + // } + // sql = sql .Replace("@",":"); + // sql = sql.Replace(UtilConstants.ReplaceKey, "@"); + // } + // return sql; + //}; + } + public override string SqlParameterKeyWord + { + get + { + return ":"; + } + } + public override IDbConnection Connection + { + get + { + try + { + if (base._DbConnection == null) + { + base._DbConnection = new OracleConnection(base.Context.CurrentConnectionConfig.ConnectionString); + } + } + catch (Exception ex) + { + + Check.Exception(true, ErrorMessage.ConnnectionOpen, ex.Message); + } + return base._DbConnection; + } + set + { + base._DbConnection = value; + } + } + /// + /// Only SqlServer + /// + /// + public override void BeginTran(string transactionName) + { + ((OracleConnection)this.Connection).BeginTransaction(); + } + + /// + /// Only SqlServer + /// + /// + /// + public override void BeginTran(IsolationLevel iso, string transactionName) + { + ((OracleConnection)this.Connection).BeginTransaction(iso); + } + + public override Func> ProcessingEventStartingSQL => (sql, parameter) => + { + + if (sql == "-- No table ") + { + sql = " SELECT 'No table' FROM DUAL WHERE 1=2 "; + } + if (base.ProcessingEventStartingSQL != null) + { + return base.ProcessingEventStartingSQL(sql, parameter); + } + else + { + return new KeyValuePair(sql, parameter); + } + }; + + + public override IDataAdapter GetAdapter() + { + return new MyOracleDataAdapter(); + } + public override DbCommand GetCommand(string sql, SugarParameter[] parameters) + { + sql = ReplaceKeyWordParameterName(sql, parameters); + if (sql?.EndsWith(';') == true && sql?.TrimStart()?.ToLower().StartsWith("begin", StringComparison.CurrentCultureIgnoreCase) != true && sql?.TrimStart()?.ToLower().Contains("begin", StringComparison.CurrentCultureIgnoreCase) != true) + { + sql = sql.TrimEnd(';'); + } + OracleCommand sqlCommand = new OracleCommand(sql, (OracleConnection)this.Connection); + sqlCommand.BindByName = true; + sqlCommand.CommandType = this.CommandType; + sqlCommand.CommandTimeout = this.CommandTimeOut; + sqlCommand.InitialLONGFetchSize = -1; + if (this.Transaction != null) + { + sqlCommand.Transaction = (OracleTransaction)this.Transaction; + } + if (parameters.HasValue()) + { + IDataParameter[] ipars = ToIDbDataParameter(parameters); + sqlCommand.Parameters.AddRange((OracleParameter[])ipars); + } + CheckConnection(); + return sqlCommand; + } + private static string[] KeyWord = new string[] { "@month", ":month", ":day", "@day", "@group", ":group", ":index", "@index", "@order", ":order", "@user", "@level", ":user", ":level", ":type", "@type", ":year", "@year", "@date", ":date" }; + private string ReplaceKeyWordParameterName(string sql, SugarParameter[] parameters) + { + sql = ReplaceKeyWordWithAd(sql, parameters); + if (parameters.HasValue() && this.CommandType != CommandType.StoredProcedure) + { + foreach (var Parameter in parameters.OrderByDescending(x => x.ParameterName?.Length)) + { + if (Parameter.ParameterName?.ToLower().IsContainsStartWithIn(KeyWord) == true) + { + if (parameters.Count(it => it.ParameterName.StartsWith(Parameter.ParameterName)) == 1) + { + var newName = Parameter.ParameterName + "_01"; + newName = newName.Insert(1, "KW"); + sql = Regex.Replace(sql, Parameter.ParameterName, newName, RegexOptions.IgnoreCase); + Parameter.ParameterName = newName; + } + else if (Parameter.ParameterName.ToLower().IsContainsIn(KeyWord)) + { + Check.ExceptionEasy($" {Parameter.ParameterName} is key word", $"{Parameter.ParameterName}ǹؼ"); + } + } + } + } + + return sql; + } + + private static string ReplaceKeyWordWithAd(string sql, SugarParameter[] parameters) + { + if (parameters != null && sql?.Contains('@') == true) + { + foreach (var item in parameters.OrderByDescending(it => it.ParameterName.Length)) + { + if (item.ParameterName.StartsWith('@')) + { + item.ParameterName = ":" + item.ParameterName.TrimStart('@'); + } + sql = Regex.Replace(sql, "@" + item.ParameterName.TrimStart(':'), item.ParameterName, RegexOptions.IgnoreCase); + } + } + + return sql; + } + + public override Action ErrorEvent => it => + { + if (base.ErrorEvent != null) + { + base.ErrorEvent(it); + } + if (it.Message?.Contains("Ч/󶨱") == true) + { + Check.ExceptionEasy(it.Message, $"{it.Message}ԭ 1.DzΪؼʣ @user 2. SQL"); + } + }; + public override void SetCommandToAdapter(IDataAdapter dataAdapter, DbCommand command) + { + ((MyOracleDataAdapter)dataAdapter).SelectCommand = (OracleCommand)command; + } + /// + /// if mysql return MySqlParameter[] pars + /// if sqlerver return SqlParameter[] pars ... + /// + /// + /// + public override IDataParameter[] ToIDbDataParameter(params SugarParameter[] parameters) + { + if (parameters == null || parameters.Length == 0) return null; + OracleParameter[] result = new OracleParameter[parameters.Length]; + int index = 0; + var isVarchar = this.Context.IsVarchar(); + foreach (var parameter in parameters) + { + if (parameter.Value == null) parameter.Value = DBNull.Value; + var sqlParameter = new OracleParameter(); + sqlParameter.Size = parameter.Size == -1 ? 0 : parameter.Size; + sqlParameter.ParameterName = parameter.ParameterName; + if (sqlParameter.ParameterName[0] == '@') + { + sqlParameter.ParameterName = string.Concat(":", sqlParameter.ParameterName.AsSpan(1, sqlParameter.ParameterName.Length - 1)); + } + if (this.CommandType == CommandType.StoredProcedure) + { + sqlParameter.ParameterName = sqlParameter.ParameterName.TrimStart(':'); + } + if (parameter.IsRefCursor) + { + sqlParameter.OracleDbType = OracleDbType.RefCursor; + } + if (parameter.IsNvarchar2 && parameter.DbType == System.Data.DbType.String) + { + sqlParameter.OracleDbType = OracleDbType.NVarchar2; + } + if (parameter.IsClob) + { + sqlParameter.OracleDbType = OracleDbType.Clob; + sqlParameter.Value = parameter.Value; + } + if (parameter.IsNClob) + { + sqlParameter.OracleDbType = OracleDbType.NClob; + sqlParameter.Value = parameter.Value; + } + if (parameter.IsArray) + { + sqlParameter.OracleDbType = OracleDbType.Varchar2; + sqlParameter.CollectionType = OracleCollectionType.PLSQLAssociativeArray; + } + if (sqlParameter.DbType == System.Data.DbType.Guid) + { + sqlParameter.DbType = System.Data.DbType.String; + sqlParameter.Value = sqlParameter.Value.ObjToString(); + } + else if (parameter.DbType == System.Data.DbType.DateTimeOffset) + { + if (parameter.Value != DBNull.Value) + sqlParameter.Value = UtilMethods.ConvertFromDateTimeOffset((DateTimeOffset)parameter.Value); + sqlParameter.DbType = System.Data.DbType.DateTime; + } + else if (parameter.DbType == System.Data.DbType.Boolean) + { + sqlParameter.DbType = System.Data.DbType.Int16; + if (parameter.Value == DBNull.Value) + { + parameter.Value = 0; + } + else + { + sqlParameter.Value = (bool)parameter.Value ? 1 : 0; + } + } + else if (parameter.DbType == System.Data.DbType.DateTime) + { + sqlParameter.Value = parameter.Value; + sqlParameter.DbType = System.Data.DbType.DateTime; + } + else if (parameter.DbType == System.Data.DbType.Date) + { + sqlParameter.Value = parameter.Value; + sqlParameter.DbType = System.Data.DbType.Date; + } + else if (parameter.DbType == System.Data.DbType.AnsiStringFixedLength) + { + sqlParameter.DbType = System.Data.DbType.AnsiStringFixedLength; + sqlParameter.Value = parameter.Value; + } + else if (parameter.DbType == System.Data.DbType.AnsiString) + { + sqlParameter.DbType = System.Data.DbType.AnsiString; + sqlParameter.Value = parameter.Value; + } + else if (parameter.DbType == System.Data.DbType.UInt32) + { + sqlParameter.DbType = System.Data.DbType.Int32; + sqlParameter.Value = parameter.Value; + } + else if (parameter.DbType == System.Data.DbType.UInt16) + { + sqlParameter.DbType = System.Data.DbType.Int16; + sqlParameter.Value = parameter.Value; + } + else if (parameter.DbType == System.Data.DbType.UInt64) + { + sqlParameter.DbType = System.Data.DbType.Int64; + sqlParameter.Value = parameter.Value; + } + else + { + if (parameter.Value != null && parameter.Value.GetType() == UtilConstants.GuidType) + { + parameter.Value = parameter.Value.ToString(); + } + sqlParameter.Value = parameter.Value; + } + if (parameter.Direction != 0) + sqlParameter.Direction = parameter.Direction; + result[index] = sqlParameter; + if (sqlParameter.Direction.IsIn(ParameterDirection.Output, ParameterDirection.InputOutput, ParameterDirection.ReturnValue)) + { + if (this.OutputParameters == null) this.OutputParameters = new List(); + this.OutputParameters.RemoveAll(it => it.ParameterName == sqlParameter.ParameterName); + this.OutputParameters.Add(sqlParameter); + } + if (isVarchar && sqlParameter.DbType == System.Data.DbType.String) + { + sqlParameter.DbType = System.Data.DbType.AnsiString; + } + if (parameter.CustomDbType != null && parameter.CustomDbType is OracleDbType) + { + sqlParameter.OracleDbType = ((OracleDbType)parameter.CustomDbType); + } + ++index; + } + return result; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/Queryable/OracleQueryable.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/Queryable/OracleQueryable.cs new file mode 100644 index 000000000..d12764276 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/Queryable/OracleQueryable.cs @@ -0,0 +1,60 @@ +using SqlSugar; + +namespace SqlSugar +{ + public class OracleQueryable : QueryableProvider + { + public override ISugarQueryable With(string withString) + { + return this; + } + protected override List GetIdentityKeys() + { + return this.EntityInfo.Columns.Where(it => it.OracleSequenceName.HasValue()).Select(it => it.DbColumnName).ToList(); + } + } + public class OracleQueryable : QueryableProvider + { + + } + public class OracleQueryable : QueryableProvider + { + + } + public class OracleQueryable : QueryableProvider + { + + } + public class OracleQueryable : QueryableProvider + { + + } + public class OracleQueryable : QueryableProvider + { + + } + public class OracleQueryable : QueryableProvider + { + + } + public class OracleQueryable : QueryableProvider + { + + } + public class OracleQueryable : QueryableProvider + { + + } + public class OracleQueryable : QueryableProvider + { + + } + public class OracleQueryable : QueryableProvider + { + + } + public class OracleQueryable : QueryableProvider + { + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/SqlBuilder/OracleBlukCopy.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/SqlBuilder/OracleBlukCopy.cs new file mode 100644 index 000000000..6b3ce18d4 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/SqlBuilder/OracleBlukCopy.cs @@ -0,0 +1,318 @@ +using Oracle.ManagedDataAccess.Client; + +using System.Data; + + +namespace SqlSugar +{ + + public partial class OracleBlukCopy + + { + + internal List> DbColumnInfoList { get; set; } + + internal SqlSugarProvider Context { get; set; } + + internal ISqlBuilder Builder { get; set; } + + internal InsertBuilder InsertBuilder { get; set; } + + internal object[] Inserts { get; set; } + + + + public int ExecuteBulkCopy() + + { + + if (DbColumnInfoList == null || DbColumnInfoList.Count == 0) return 0; + + + + if (Inserts.First().GetType() == typeof(DataTable)) + + { + + return WriteToServer(); + + } + + DataTable dt = GetCopyData(); + + OracleBulkCopy bulkCopy = GetBulkCopyInstance(); + + bulkCopy.DestinationTableName = InsertBuilder.GetTableNameString; + + try + + { + + bulkCopy.WriteToServer(dt); + + } + + catch (Exception) + + { + + CloseDb(); + + throw; + + } + + CloseDb(); + + return DbColumnInfoList.Count; + + } + + + + public async Task ExecuteBulkCopyAsync() + + { + + if (DbColumnInfoList == null || DbColumnInfoList.Count == 0) return 0; + + + + if (Inserts.First().GetType() == typeof(DataTable)) + + { + + return WriteToServer(); + + } + + DataTable dt = GetCopyData(); + + OracleBulkCopy bulkCopy = GetBulkCopyInstance(); + + bulkCopy.DestinationTableName = InsertBuilder.GetTableNameString; + + try + + { + + await Task.Run(() => bulkCopy.WriteToServer(dt)).ConfigureAwait(false); + + } + + catch (Exception) + + { + + CloseDb(); + + throw; + + } + + CloseDb(); + + return DbColumnInfoList.Count; + + } + + + + private int WriteToServer() + + { + + var dt = this.Inserts.First() as DataTable; + + if (dt == null) + + return 0; + + Check.Exception(dt.TableName == "Table", "dt.TableName can't be null "); + + dt = GetCopyWriteDataTable(dt); + + OracleBulkCopy copy = GetBulkCopyInstance(); + + copy.DestinationTableName = this.Builder.GetTranslationColumnName(dt.TableName); + + copy.WriteToServer(dt); + + CloseDb(); + + return dt.Rows.Count; + + } + + private DataTable GetCopyWriteDataTable(DataTable dt) + + { + + var result = this.Context.Ado.GetDataTable("select * from " + this.Builder.GetTranslationColumnName(dt.TableName) + " where 1 > 2 "); + + foreach (DataRow item in dt.Rows) + + { + + DataRow dr = result.NewRow(); + + foreach (DataColumn column in result.Columns) + + { + + + + if (dt.Columns.Cast().Select(it => it.ColumnName.ToLower()).Contains(column.ColumnName.ToLower())) + + { + + dr[column.ColumnName] = item[column.ColumnName]; + + if (dr[column.ColumnName] == null) + + { + + dr[column.ColumnName] = DBNull.Value; + + } + + } + + } + + result.Rows.Add(dr); + + } + + result.TableName = dt.TableName; + + return result; + + } + + private OracleBulkCopy GetBulkCopyInstance() + + { + if (this.Context.Ado.Connection.State == ConnectionState.Closed) + + { + + this.Context.Ado.Connection.Open(); + + } + + OracleBulkCopy copy; + + if (this.Context.Ado.Transaction == null) + + { + + copy = new OracleBulkCopy((OracleConnection)this.Context.Ado.Connection, Oracle.ManagedDataAccess.Client.OracleBulkCopyOptions.Default); + + } + + else + + { + + copy = new OracleBulkCopy((OracleConnection)this.Context.Ado.Connection, OracleBulkCopyOptions.UseInternalTransaction); + + } + return copy; + + } + + private DataTable GetCopyData() + + { + + var dt = this.Context.Ado.GetDataTable("select * from " + InsertBuilder.GetTableNameString + " where 1 > 2 "); + + foreach (var rowInfos in DbColumnInfoList) + + { + + var dr = dt.NewRow(); + + foreach (DataColumn item in dt.Columns) + + { + + var rows = rowInfos.ToList(); + + var value = rows.FirstOrDefault(it => + + it.DbColumnName.Equals(item.ColumnName, StringComparison.CurrentCultureIgnoreCase) || + + it.PropertyName.Equals(item.ColumnName, StringComparison.CurrentCultureIgnoreCase) + + ); + + if (value != null) + + { + + if (value.Value != null && UtilMethods.GetUnderType(value.Value.GetType()) == UtilConstants.DateType) + + { + + if (value.Value != null && value.Value.ToString() == DateTime.MinValue.ToString()) + + { + + value.Value = Convert.ToDateTime("1900/01/01"); + + } + + } + + if (value.Value == null) + + { + + value.Value = DBNull.Value; + + } + + dr[item.ColumnName] = value.Value; + + } + + } + + dt.Rows.Add(dr); + + } + if (this.InsertBuilder.OracleSeqInfoList != null && this.InsertBuilder.OracleSeqInfoList.Count != 0) + { + var ids = this.InsertBuilder.OracleSeqInfoList.Select(it => it.Value).ToList(); + var columnInfo = this.InsertBuilder.EntityInfo.Columns.Where(it => !string.IsNullOrEmpty(it.OracleSequenceName)).First(); + var identityName = columnInfo.DbColumnName; + ids.Add(this.Context.Ado.GetInt(" select " + columnInfo.OracleSequenceName + ".nextval from dual")); + int i = 0; + foreach (DataRow item in dt.Rows) + { + item[identityName] = ids[i]; + ++i; + } + } + return dt; + + } + + private void CloseDb() + + { + + if (this.Context.CurrentConnectionConfig.IsAutoCloseConnection && this.Context.Ado.Transaction == null) + + { + + this.Context.Ado.Connection.Close(); + + } + + } + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/SqlBuilder/OracleBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/SqlBuilder/OracleBuilder.cs new file mode 100644 index 000000000..db9c642e2 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/SqlBuilder/OracleBuilder.cs @@ -0,0 +1,85 @@ +using System.Text; + +namespace SqlSugar +{ + public class OracleBuilder : SqlBuilderProvider + { + public override string SqlParameterKeyWord + { + get + { + return ":"; + } + } + public override string SqlDateNow + { + get + { + return "sysdate"; + } + } + public override string FullSqlDateNow + { + get + { + return "select systimestamp from dual"; + } + } + public override string SqlTranslationLeft { get { return "\""; } } + public override string SqlTranslationRight { get { return "\""; } } + public override string GetTranslationTableName(string name) + { + var result = base.GetTranslationTableName(name); + if (result.Contains('(') && result.Contains(')')) + return result; + else + return result.ToUpper(IsUppper); + } + public override string GetTranslationColumnName(string entityName, string propertyName) + { + var result = base.GetTranslationColumnName(entityName, propertyName); + return result.ToUpper(IsUppper); + } + public override string GetTranslationColumnName(string propertyName) + { + var result = base.GetTranslationColumnName(propertyName); + return result.ToUpper(IsUppper); + } + public override string RemoveParentheses(string sql) + { + if (sql.StartsWith('(') && sql.EndsWith(')')) + { + sql = sql.Substring(1, sql.Length - 2); + } + + return sql; + } + public override void FormatSaveQueueSql(StringBuilder sqlBuilder) + { + var sql = sqlBuilder?.ToString(); + if (sql?.TrimStart()?.Substring(0, 5)?.EqualCase("begin") != true) + { + sqlBuilder.Clear(); + sqlBuilder.AppendLine("begin"); + sqlBuilder.Append(sql); + sqlBuilder.AppendLine("end; "); + } + } + #region Helper + public bool IsUppper + { + get + { + if (this.Context.CurrentConnectionConfig.MoreSettings == null) + { + return true; + } + else + { + return this.Context.CurrentConnectionConfig.MoreSettings.IsAutoToUpper == true; + } + } + } + #endregion + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/SqlBuilder/OracleDeleteBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/SqlBuilder/OracleDeleteBuilder.cs new file mode 100644 index 000000000..4c0815f9a --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/SqlBuilder/OracleDeleteBuilder.cs @@ -0,0 +1,7 @@ +namespace SqlSugar +{ + public class OracleDeleteBuilder : DeleteBuilder + { + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/SqlBuilder/OracleExpressionContext.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/SqlBuilder/OracleExpressionContext.cs new file mode 100644 index 000000000..00ed75b7e --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/SqlBuilder/OracleExpressionContext.cs @@ -0,0 +1,453 @@ +using System.Collections; + +namespace SqlSugar +{ + public partial class OracleExpressionContext : ExpressionContext, ILambdaExpressions + { + public SqlSugarProvider Context { get; set; } + public OracleExpressionContext() + { + base.DbMehtods = new OracleMethod(); + } + public override string SqlParameterKeyWord + { + get + { + return ":"; + } + } + public override string SqlTranslationLeft { get { return "\""; } } + public override string SqlTranslationRight { get { return "\""; } } + public override string GetTranslationTableName(string entityName, bool isMapping = true) + { + return base.GetTranslationTableName(entityName, isMapping).ToUpper(IsUppper); + } + public override string GetTranslationColumnName(string columnName) + { + if (columnName == "systimestamp") + { + return columnName; + } + if (columnName.Contains(':')) + return base.GetTranslationColumnName(columnName); + else if (columnName.Contains("\".\"")) + { + return columnName; + } + else + return base.GetTranslationColumnName(columnName).ToUpper(IsUppper); + } + public override string GetDbColumnName(string entityName, string propertyName) + { + return base.GetDbColumnName(entityName, propertyName).ToUpper(IsUppper); + } + public override bool IsTranslationText(string name) + { + if (!string.IsNullOrEmpty(name) && name.Equals("sysdate", StringComparison.CurrentCultureIgnoreCase)) + { + return true; + } + var result = name.IsContainsIn(SqlTranslationLeft, SqlTranslationRight, UtilConstants.Space, ExpressionConst.LeftParenthesis, ExpressionConst.RightParenthesis); + return result; + } + public bool IsUppper + { + get + { + if (this.SugarContext?.Context?.Context?.CurrentConnectionConfig?.MoreSettings == null) + { + return true; + } + else + { + return this.SugarContext?.Context?.Context?.CurrentConnectionConfig?.MoreSettings.IsAutoToUpper == true; + } + } + } + } + public partial class OracleMethod : DefaultDbMethod, IDbMethods + { + public override string UNIX_TIMESTAMP(MethodCallExpressionModel model) + { + var parameterNameA = model.Args[0].MemberName; + return $" (CAST({parameterNameA} AS DATE) - DATE '1970-01-01') * 86400 "; + } + public override string IsNullOrEmpty(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format("( {0} IS NULL )", parameter.MemberName); + } + public override string WeekOfYear(MethodCallExpressionModel mode) + { + var parameterNameA = mode.Args[0].MemberName; + return $"TO_NUMBER(TO_CHAR({parameterNameA}, 'WW')) "; + } + public override string BitwiseAnd(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + return string.Format(" BITAND({0},{1}) ", parameter.MemberName, parameter2.MemberName); + } + public override string BitwiseInclusiveOR(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + return string.Format(" BITOR({0},{1}) ", parameter.MemberName, parameter2.MemberName); + } + public override string ParameterKeyWord { get; set; } = ":"; + public override string Modulo(MethodCallExpressionModel model) + { + return " MOD(" + model.Args[0].MemberName + " , " + model.Args[1].MemberName + ")"; + } + public override string GetStringJoinSelector(string result, string separator) + { + if (result.Contains(',')) + { + return $"listagg(to_char({result.Split(',').First()}),'{separator}') within group(order by {result.Split(',').Last()}) "; + } + else + { + return $"listagg(to_char({result}),'{separator}') within group(order by {result}) "; + } + } + public override string HasValue(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format("( {0} IS NOT NULL ) ", parameter.MemberName); + } + + public override string DateDiff(MethodCallExpressionModel model) + { + var parameter = (DateType)(Enum.Parse(typeof(DateType), model.Args[0].MemberValue.ObjToString())); + var begin = model.Args[1].MemberName; + var end = model.Args[2].MemberName; + switch (parameter) + { + case DateType.Year: + return $" ( cast((months_between( {end} , {begin}))/12 as number(9,0) ) )"; + case DateType.Month: + return $" ( cast((months_between( {end} , {begin})) as number(9,0) ) )"; + case DateType.Day: + return $" ( ROUND(TO_NUMBER(cast({end} as date) - cast({begin} as date))) )"; + case DateType.Hour: + return $" ( ROUND(TO_NUMBER(cast({end} as date) - cast({begin} as date)) * 24) )"; + case DateType.Minute: + return $" ( ROUND(TO_NUMBER(cast({end} as date) - cast({begin} as date)) * 24 * 60) )"; + case DateType.Second: + return $" ( ROUND(TO_NUMBER(cast({end} as date) - cast({begin} as date)) * 24 * 60 * 60) )"; + case DateType.Millisecond: + return $" ( ROUND(TO_NUMBER(cast({end} as date) - cast({begin} as date)) * 24 * 60 * 60 * 60) )"; + default: + break; + } + throw new Exception(parameter + " datediff no support"); + } + private void PageEach(IEnumerable pageItems, int pageSize, Action> action) + { + if (pageItems?.Any() == true) + { + int totalRecord = pageItems.Count(); + int pageCount = (totalRecord + pageSize - 1) / pageSize; + for (int i = 1; i <= pageCount; i++) + { + var list = pageItems.Skip((i - 1) * pageSize).Take(pageSize).ToList(); + action(list); + } + } + } + public override string ContainsArray(MethodCallExpressionModel model) + { + if (model.Args[0].MemberValue == null) + { + return base.ContainsArray(model); + } + var inValueIEnumerable = ((IEnumerable)model.Args[0].MemberValue).Cast().ToArray(); + if (inValueIEnumerable.Length < 1000) + { + return base.ContainsArray(model); + } + else + { + string result = ""; + PageEach(inValueIEnumerable, 999, it => + { + model.Args.First().MemberValue = it; + result += (base.ContainsArray(model) + " OR "); + + }); + return " ( " + result.TrimEnd(' ').TrimEnd('R').TrimEnd('O') + " ) "; + } + } + public override string ToInt64(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS Number)", parameter.MemberName); + } + + public override string ToTime(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" to_timestamp({0},'0000-01-01 hh24:mi:ss') ", parameter.MemberName); + } + public override string Substring(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + var parameter3 = model.Args[2]; + return string.Format("SUBSTR({0},1 + {1},{2})", parameter.MemberName, parameter2.MemberName, parameter3.MemberName); + } + public override string DateValue(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + var type = (DateType)Enum.Parse(typeof(DateType), parameter2.MemberValue.ObjToString(), false); + switch (type) + { + case DateType.Year: + return string.Format("(CAST(TO_CHAR({0},'yyyy') AS NUMBER))", parameter.MemberName); + case DateType.Month: + return string.Format("(CAST(TO_CHAR({0},'mm') AS NUMBER))", parameter.MemberName); + case DateType.Hour: + return string.Format("(CAST(TO_CHAR({0},'hh24') AS NUMBER))", parameter.MemberName); + case DateType.Second: + return string.Format("(CAST(TO_CHAR({0},'ss') AS NUMBER))", parameter.MemberName); + case DateType.Minute: + return string.Format("(CAST(TO_CHAR({0},'mi') AS NUMBER))", parameter.MemberName); + case DateType.Millisecond: + return string.Format("(CAST(TO_CHAR({0},'ff3') AS NUMBER))", parameter.MemberName); + case DateType.Quarter: + return string.Format("(CAST(TO_CHAR({0},'q') AS NUMBER))", parameter.MemberName); + case DateType.Weekday: + return $" (TO_NUMBER(TO_CHAR({parameter.MemberName}, 'D'))-1) "; + case DateType.Day: + default: + return string.Format("(CAST(TO_CHAR({0},'dd') AS NUMBER))", parameter.MemberName); + } + } + public override string DateAddByType(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + var parameter3 = model.Args[2]; + var type = (DateType)Enum.Parse(typeof(DateType), parameter3.MemberValue.ObjToString(), false); + double time = 1; + switch (type) + { + case DateType.Year: + // 每年 = 12 个月 + return $"ADD_MONTHS({parameter.MemberName}, ({parameter2.MemberName}) * 12)"; + case DateType.Month: + return $"ADD_MONTHS({parameter.MemberName}, {parameter2.MemberName})"; + case DateType.Day: + break; + case DateType.Hour: + time = 1 / 24.0; + break; + case DateType.Second: + time = 1 / 24.0 / 60.0 / 60.0; + break; + case DateType.Minute: + time = 1 / 24.0 / 60.0; + break; + case DateType.Millisecond: + time = 1 / 24.0 / 60.0 / 60.0 / 1000; + break; + } + return string.Format("({0}+({1}*{2})) ", parameter.MemberName, time, parameter2.MemberName); + } + + public override string DateAddDay(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + return string.Format("({0}+(1*{1})) ", parameter.MemberName, parameter2.MemberName); + } + + public override string ToString(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS VARCHAR2(4000))", parameter.MemberName); + } + + public override string ToDecimal(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS Number)", parameter.MemberName); + } + + public override string ToDate(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" TO_TIMESTAMP({0}, 'YYYY-MM-DD HH24:MI:SS.FF') ", parameter.MemberName); + } + + public override string ToDateShort(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" TRUNC({0},'dd') ", parameter.MemberName); + } + public override string Contains(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + return string.Format(" ({0} like '%'||{1}||'%') ", parameter.MemberName, parameter2.MemberName); + } + public override string StartsWith(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + return string.Format(" ({0} like {1}||'%') ", parameter.MemberName, parameter2.MemberName); + } + public override string EndsWith(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + return string.Format(" ({0} like '%'||{1}) ", parameter.MemberName, parameter2.MemberName); + } + public override string Trim(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" trim({0}) ", parameter.MemberName); + } + public override string DateIsSameDay(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + return string.Format(" ( cast({0} as date)= cast( {1} as date) ) ", parameter.MemberName, parameter2.MemberName); ; + } + public override string DateIsSameByType(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + var parameter3 = model.Args[2]; + + var dateType = parameter3.MemberValue.ObjToString().ToLower(); + var date1 = parameter.MemberName; + var date2 = parameter2.MemberName; + + if (dateType == "year") + { + return string.Format("(EXTRACT(YEAR FROM {0}) = EXTRACT(YEAR FROM {1}))", date1, date2); + } + else if (dateType == "month") + { + return string.Format("(EXTRACT(YEAR FROM {0}) = EXTRACT(YEAR FROM {1}) AND EXTRACT(MONTH FROM {0}) = EXTRACT(MONTH FROM {1}))", date1, date2); + } + else if (dateType == "day") + { + return string.Format("(TRUNC({0}) = TRUNC({1}))", date1, date2); + } + else if (dateType == "hour") + { + return string.Format("(TRUNC({0}, 'HH24') = TRUNC({1}, 'HH24'))", date1, date2); + } + else if (dateType == "minute") + { + return string.Format("(TRUNC({0}, 'MI') = TRUNC({1}, 'MI'))", date1, date2); + } + else if (dateType == "second") + { + return string.Format("(TRUNC({0}, 'SS') = TRUNC({1}, 'SS'))", date1, date2); + } + else if (dateType == "week" || dateType == "weekday") + { + return string.Format("(TRUNC({0}, 'IW') = TRUNC({1}, 'IW'))", date1, date2); + } + else + { + // 默认按天比较 + return string.Format("(TRUNC({0}) = TRUNC({1}))", date1, date2); + } + } + public override string Length(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" LENGTH({0}) ", parameter.MemberName); + } + + public override string IsNull(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter1 = model.Args[1]; + return string.Format("NVL({0},{1})", parameter.MemberName, parameter1.MemberName); + } + + public override string MergeString(params string[] strings) + { + return string.Join("||", strings); + } + + public override string GetDate() + { + return "systimestamp"; + } + + public override string GetRandom() + { + return "dbms_random.value"; + } + + public override string Collate(MethodCallExpressionModel model) + { + var name = model.Args[0].MemberName; + return $" NLSSORT({0}, 'NLS_SORT = Latin_CI') "; + } + + public override string JsonField(MethodCallExpressionModel model) + { + return $"JSON_VALUE({model.Args[0].MemberName}, '$.{model.Args[1].MemberValue.ToString().ToSqlFilter()}')"; + //"JSON_VALUE(j.kingorder, '$.Id') = '1'"; + } + + public override string CharIndex(MethodCallExpressionModel model) + { + return string.Format("instr ({0},{1},1,1) ", model.Args[0].MemberName, model.Args[1].MemberName); + } + public override string TrimEnd(MethodCallExpressionModel mode) + { + var parameterNameA = mode.Args[0].MemberName; + var parameterNameB = mode.Args[1].MemberName; + return $" RTRIM({parameterNameA}, {parameterNameB}) "; + } + public override string TrimStart(MethodCallExpressionModel mode) + { + + var parameterNameA = mode.Args[0].MemberName; + var parameterNameB = mode.Args[1].MemberName; + return $" LTRIM({parameterNameA}, {parameterNameB}) "; + } + public override string Left(MethodCallExpressionModel mode) + { + var parameterNameA = mode.Args[0].MemberName; + var parameterNameB = mode.Args[1].MemberName; + return $" SUBSTR({parameterNameA}, 1, {parameterNameB}) "; + } + public override string Right(MethodCallExpressionModel mode) + { + var parameterNameA = mode.Args[0].MemberName; + var parameterNameB = mode.Args[1].MemberName; + return $" SUBSTR({parameterNameA}, (LENGTH({parameterNameA})-2), {parameterNameB}) "; + } + + public override string Ceil(MethodCallExpressionModel mode) + { + var parameterNameA = mode.Args[0].MemberName; + return $" CEIL({parameterNameA}) "; + } + + public override string NewUid(MethodCallExpressionModel mode) + { + return " SUBSTR(LOWER(RAWTOHEX(SYS_GUID())), 1, 8) ||\r\n '-' ||\r\n SUBSTR(LOWER(RAWTOHEX(SYS_GUID())), 9, 4) ||\r\n '-' ||\r\n SUBSTR(LOWER(RAWTOHEX(SYS_GUID())), 13, 4) ||\r\n '-' ||\r\n SUBSTR(LOWER(RAWTOHEX(SYS_GUID())), 17, 4) ||\r\n '-' ||\r\n SUBSTR(LOWER(RAWTOHEX(SYS_GUID())), 21) "; + } + public override string FullTextContains(MethodCallExpressionModel mode) + { + var columns = mode.Args[0].MemberName; + if (mode.Args[0].MemberValue is List) + { + columns = "(" + string.Join(",", mode.Args[0].MemberValue as List) + ")"; + } + var searchWord = mode.Args[1].MemberName; + return $" CONTAINS({columns}, {searchWord}, 1) "; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/SqlBuilder/OracleFastBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/SqlBuilder/OracleFastBuilder.cs new file mode 100644 index 000000000..689058f70 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/SqlBuilder/OracleFastBuilder.cs @@ -0,0 +1,122 @@ +using Oracle.ManagedDataAccess.Client; + +using System.Data; + +namespace SqlSugar +{ + public class OracleFastBuilder : FastBuilder, IFastBuilder + { + public override bool IsActionUpdateColumns { get; set; } = true; + public override DbFastestProperties DbFastestProperties { get; set; } = new DbFastestProperties() + { + IsMerge = true + }; + private EntityInfo entityInfo; + + public OracleFastBuilder(EntityInfo entityInfo) + { + this.entityInfo = entityInfo; + } + + public override string UpdateSql { get; set; } = "UPDATE (SELECT {4} FROM {2} TM,{3} TE WHERE {1})SET {0}"; + public override async Task CreateTempAsync(DataTable dt) + { + var sqlBuilder = this.Context.Queryable().SqlBuilder; + var dts = dt.Columns.Cast().Select(it => sqlBuilder.GetTranslationColumnName(it.ColumnName)).ToList(); + //await Task.FromResult(0); + //throw new Exception("Oracle no support BulkUpdate"); + var oldTableName = dt.TableName; + var columns = this.Context.EntityMaintenance.GetEntityInfo().Columns.Where(it => it.IsPrimarykey).Select(it => it.DbColumnName).ToList(); + dt.TableName = "Temp" + SnowFlakeSingle.instance.getID().ToString(); + if (columns.Count == 0 && DbFastestProperties?.WhereColumns.HasValue() == true) + { + columns.AddRange(DbFastestProperties.WhereColumns); + } + var sql = this.Context.Queryable().AS(oldTableName).Where(it => false).Select(string.Join(",", dts)).ToSql().Key; + await Context.Ado.ExecuteCommandAsync($"create table {dt.TableName} as {sql} ").ConfigureAwait(false); + this.Context.DbMaintenance.AddPrimaryKeys(dt.TableName, columns.ToArray(), "Pk_" + SnowFlakeSingle.instance.getID().ToString()); + } + public override async Task UpdateByTempAsync(string tableName, string tempName, string[] updateColumns, string[] whereColumns) + { + var sqlBuilder = this.Context.Queryable().SqlBuilder; + Check.ArgumentNullException(updateColumns.Length == 0, "update columns count is 0"); + Check.ArgumentNullException(whereColumns.Length == 0, "where columns count is 0"); + var sets = string.Join(",", updateColumns.Select(it => $"TM{it}=TE{it}")); + var wheres = string.Join(" AND ", whereColumns.Select(it => $"TM.{sqlBuilder.GetTranslationColumnName(it)}=TE.{sqlBuilder.GetTranslationColumnName(it)}")); + var forms = string.Join(",", updateColumns.Select(it => $" TM.{sqlBuilder.GetTranslationColumnName(it)} TM{it},TE.{sqlBuilder.GetTranslationColumnName(it)} TE{it}")); ; + string sql = string.Format(UpdateSql, sets, wheres, tableName, tempName, forms); + return await Context.Ado.ExecuteCommandAsync(sql).ConfigureAwait(false); + } + private OracleBulkCopy GetBulkCopyInstance() + + { + if (this.Context.Ado.Connection.State == ConnectionState.Closed) + { + this.Context.Ado.Connection.Open(); + } + + OracleBulkCopy copy; + if (this.Context.Ado.Transaction == null) + { + copy = new OracleBulkCopy((OracleConnection)this.Context.Ado.Connection, Oracle.ManagedDataAccess.Client.OracleBulkCopyOptions.Default); + } + else + { + copy = new OracleBulkCopy((OracleConnection)this.Context.Ado.Connection, OracleBulkCopyOptions.UseInternalTransaction); + } + copy.BulkCopyTimeout = this.Context.Ado.CommandTimeOut; + return copy; + + } + + public override Task Merge(string tableName, DataTable dt, EntityInfo entityInfo, string[] whereColumns, string[] updateColumns, List datas) where T : class + { + Check.Exception(this.entityInfo.Columns.Any(it => it.OracleSequenceName.HasValue()), "The BulkMerge method cannot be used for sequence", "BulkMerge方法不能用序列"); + var sqlBuilder = this.Context.Queryable().SqlBuilder; + var insertColumns = entityInfo.Columns + .Where(it => it.IsIgnore == false) + .Where(it => it.IsIdentity == false) + .Where(it => it.InsertServerTime == false) + .Where(it => it.InsertSql == null) + .Where(it => it.OracleSequenceName == null) + .Where(it => it.IsOnlyIgnoreInsert == false); + var whereSql = string.Join(" AND ", whereColumns.Select(it => $"tgt.{sqlBuilder.GetTranslationColumnName(it)}=src.{sqlBuilder.GetTranslationColumnName(it)}")); + var updateColumnsSql = string.Join(" , ", updateColumns.Select(it => $"tgt.{sqlBuilder.GetTranslationColumnName(it)}=src.{sqlBuilder.GetTranslationColumnName(it)}")); + var insertColumnsSqlTgt = string.Join(" , ", insertColumns.Select(it => "tgt." + sqlBuilder.GetTranslationColumnName(it.DbColumnName))); + var insertColumnsSqlsrc = string.Join(" , ", insertColumns.Select(it => "src." + sqlBuilder.GetTranslationColumnName(it.DbColumnName))); + var sql = $@"MERGE INTO {sqlBuilder.GetTranslationColumnName(tableName)} tgt +USING {sqlBuilder.GetTranslationColumnName(dt.TableName)} src +ON ({whereSql}) +WHEN MATCHED THEN + UPDATE SET {updateColumnsSql} +WHEN NOT MATCHED THEN + INSERT ({insertColumnsSqlTgt}) + VALUES ({insertColumnsSqlsrc})"; + + return this.Context.Ado.ExecuteCommandAsync(sql); + } + public Task ExecuteBulkCopyAsync(DataTable dt) + { + var identityColumnInfo = this.entityInfo.Columns.FirstOrDefault(it => it.IsIdentity); + if (identityColumnInfo != null) + { + throw new Exception("Oracle bulkcopy no support identity"); + } + OracleBulkCopy copy = GetBulkCopyInstance(); + try + { + copy.DestinationTableName = dt.TableName; + copy.WriteToServer(dt); + } + catch (Exception) + { + throw; + } + finally + { + CloseDb(); + } + return Task.FromResult(dt.Rows.Count); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/SqlBuilder/OracleInsertBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/SqlBuilder/OracleInsertBuilder.cs new file mode 100644 index 000000000..78319b993 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/SqlBuilder/OracleInsertBuilder.cs @@ -0,0 +1,222 @@ +using System.Text; + +namespace SqlSugar +{ + public class OracleInsertBuilder : InsertBuilder + { + public override string SqlTemplate + { + get + { + return @"INSERT INTO {0} + ({1}) + VALUES + ({2}) "; + + } + } + public override string SqlTemplateBatch + { + get + { + return "INSERT INTO {0} ({1})"; + } + } + public override string ToSqlString() + { + var identities = this.EntityInfo.Columns.Where(it => it.OracleSequenceName.HasValue()).ToList(); + if (IsNoInsertNull) + { + DbColumnInfoList = DbColumnInfoList.Where(it => it.Value != null).ToList(); + } + if (this.Context.CurrentConnectionConfig?.MoreSettings?.EnableOracleIdentity == true) + { + this.DbColumnInfoList = this.DbColumnInfoList.Where(it => it.IsIdentity == false).ToList(); + } + var groupList = DbColumnInfoList.GroupBy(it => it.TableId).ToList(); + var isSingle = groupList.Count == 1; + string columnsString = string.Join(",", groupList.First().Select(it => Builder.GetTranslationColumnName(it.DbColumnName))); + if (isSingle && this.EntityInfo.EntityName != "Dictionary`2") + { + + string columnParametersString = string.Join(",", this.DbColumnInfoList.Select(it => base.GetDbColumn(it, Builder.SqlParameterKeyWord + it.DbColumnName))); + if (identities.HasValue()) + { + columnsString = columnsString.TrimEnd(',') + "," + string.Join(",", identities.Select(it => Builder.GetTranslationColumnName(it.DbColumnName))); + columnParametersString = columnParametersString.TrimEnd(',') + "," + string.Join(",", identities.Select(it => it.OracleSequenceName + ".nextval")); + } + ActionMinDate(); + return string.Format(SqlTemplate, GetTableNameString, columnsString, columnParametersString); + } + else + { + var bigSize = 500; + if (groupList.Count < bigSize || this.Context?.CurrentConnectionConfig?.MoreSettings?.EnableOracleIdentity == true) + { + string result = Small(identities, groupList, columnsString); + return result; + } + else + { + string result = Big(identities, groupList, columnsString); + return result; + } + } + } + private string Big(List identities, List> groupList, string columnsString) + { + this.Context.Utilities.PageEach(groupList, 100, groupListPasge => + { + this.Parameters = new List(); + var sql = Small(identities, groupListPasge, columnsString); + this.Context.Ado.ExecuteCommand(sql, this.Parameters); + }); + if (identities?.Count > 0 && this.OracleSeqInfoList != null && this.OracleSeqInfoList.Count != 0) + { + return $"SELECT {this.OracleSeqInfoList.First().Value - 1} FROM DUAL"; + } + else + { + return $"SELECT {groupList.Count} FROM DUAL"; + } + } + + private string Small(List identities, List> groupList, string columnsString) + { + StringBuilder batchInsetrSql = new StringBuilder(); + batchInsetrSql.AppendLine("INSERT ALL"); + foreach (var item in groupList) + { + batchInsetrSql.Append("INTO " + GetTableNameString + " "); + string insertColumns = ""; + + batchInsetrSql.Append('('); + batchInsetrSql.Append(columnsString); + if (identities.HasValue() && this.IsOffIdentity == false) + { + batchInsetrSql.Append("," + string.Join(",", identities.Select(it => Builder.GetTranslationColumnName(it.DbColumnName)))); + } + batchInsetrSql.Append(") VALUES"); + + + batchInsetrSql.Append('('); + insertColumns = string.Join(",", item.Select(it => GetDbColumn(it, FormatValue(it.Value, it.PropertyName)))); + batchInsetrSql.Append(insertColumns); + if (this.IsOffIdentity == false) + { + if (identities.HasValue()) + { + batchInsetrSql.Append(','); + foreach (var idn in identities) + { + var seqvalue = this.OracleSeqInfoList[idn.OracleSequenceName]; + this.OracleSeqInfoList[idn.OracleSequenceName] = this.OracleSeqInfoList[idn.OracleSequenceName] + 1; + if (identities.Last() == idn) + { + batchInsetrSql.Append(seqvalue); + } + else + { + batchInsetrSql.Append(seqvalue + ","); + } + } + } + } + batchInsetrSql.AppendLine(") "); + + } + if (identities.HasValue()) + { + batchInsetrSql.AppendLine("SELECT " + (this.OracleSeqInfoList.First().Value - 1) + " FROM DUAL"); + } + else + { + batchInsetrSql.AppendLine("SELECT 1 FROM DUAL"); + } + var result = batchInsetrSql.ToString(); + return result; + } + + int i = 0; + public object FormatValue(object value, string name) + { + if (value == null) + { + return "NULL"; + } + else + { + string N = this.Context.GetN(); + var type = UtilMethods.GetUnderType(value.GetType()); + if (type == UtilConstants.StringType && value.ToString().Contains("{SugarSeq:=}")) + { + return value.ToString().Replace("{SugarSeq:=}", ""); + } + if (type == UtilConstants.DateType) + { + var date = value.ObjToDate(); + if (date < UtilMethods.GetMinDate(this.Context.CurrentConnectionConfig)) + { + date = UtilMethods.GetMinDate(this.Context.CurrentConnectionConfig); + } + if (this.Context.CurrentConnectionConfig?.MoreSettings?.DisableMillisecond == true) + { + return "to_date('" + date.ToString("yyyy-MM-dd HH:mm:ss") + "', 'YYYY-MM-DD HH24:MI:SS') "; + } + else + { + return "to_timestamp('" + date.ToString("yyyy-MM-dd HH:mm:ss.ffffff") + "', 'YYYY-MM-DD HH24:MI:SS.FF') "; + } + } + else if (type.IsEnum()) + { + return Convert.ToInt64(value); + } + else if (value is TimeSpan ts) + { + return string.Format( + "INTERVAL '{0} {1:D2}:{2:D2}:{3:D2}.{4:D3}' DAY TO SECOND(3)", + ts.Days, + ts.Hours, + ts.Minutes, + ts.Seconds, + ts.Milliseconds); + } + else if (type == UtilConstants.ByteArrayType) + { + ++i; + var parameterName = this.Builder.SqlParameterKeyWord + name + i; + this.Parameters.Add(new SugarParameter(parameterName, value, System.Data.DbType.Binary)); + return parameterName; + } + else if (type == UtilConstants.BoolType) + { + return value.ObjToBool() ? "1" : "0"; + } + else if (type == UtilConstants.DateTimeOffsetType) + { + var date = UtilMethods.ConvertFromDateTimeOffset((DateTimeOffset)value); + return "to_timestamp('" + date.ToString("yyyy-MM-dd HH:mm:ss.ffffff") + "', 'YYYY-MM-DD HH24:MI:SS.FF') "; + } + else if (type == UtilConstants.StringType || type == UtilConstants.ObjType) + { + if (value.ToString().Length > 1000) + { + ++i; + var parameterName = this.Builder.SqlParameterKeyWord + name + i; + this.Parameters.Add(new SugarParameter(parameterName, value)); + return parameterName; + } + else + { + return N + "'" + value.ToString().ToSqlFilter() + "'"; + } + } + else + { + return N + "'" + value.ToString() + "'"; + } + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/SqlBuilder/OracleQueryBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/SqlBuilder/OracleQueryBuilder.cs new file mode 100644 index 000000000..5e0b1636c --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/SqlBuilder/OracleQueryBuilder.cs @@ -0,0 +1,136 @@ +using System.Text; +using System.Text.RegularExpressions; + +namespace SqlSugar +{ + public class OracleQueryBuilder : QueryBuilder + { + public override bool IsSelectNoAll { get; set; } = true; + public override bool IsComplexModel(string sql) + { + return Regex.IsMatch(sql, @"AS ""\w+\.\w+""") || Regex.IsMatch(sql, @"AS ""\w+\.\w+\.\w+"""); + } + public override string SqlTemplate + { + get + { + return "SELECT {0}{" + UtilConstants.ReplaceKey + "} FROM {1}{2}{3}{4}"; + } + } + public override string ToSqlString() + { + if (this.Offset == "true") + { + return OffsetPage(); + } + var oldTake = Take; + var oldSkip = Skip; + var isDistinctPage = IsDistinct && (Take > 1 || Skip > 1); + if (isDistinctPage) + { + return OffsetPage(); + } + var result = _ToSqlString(); + //if (isDistinctPage) + //{ + // if (this.OrderByValue.HasValue()) + // { + // Take = int.MaxValue; + // result = result.Replace("DISTINCT", $" DISTINCT TOP {int.MaxValue} "); + // } + // Take = oldTake; + // Skip = oldSkip; + // result = this.Context.SqlQueryable(result).Skip(Skip??0).Take(Take??0).ToSql().Key; + + + //} + if (TranLock != null) + { + result = result + TranLock; + } + return result; + } + + private string OffsetPage() + { + var skip = this.Skip ?? 1; + var take = this.Take; + this.Skip = null; + this.Take = null; + this.Offset = null; + var pageSql = $"SELECT * FROM ( SELECT PAGETABLE1.*,ROWNUM PAGEINDEX FROM( {this.ToSqlString()}) PAGETABLE1 WHERE ROWNUM<={skip + take}) WHERE PAGEINDEX>={(skip == 0 ? skip : (skip + 1))}"; + return pageSql; + } + + public string _ToSqlString() + { + string oldOrderBy = this.OrderByValue; + string externalOrderBy = oldOrderBy; + var isIgnoreOrderBy = this.IsCount && this.PartitionByValue.IsNullOrEmpty(); + AppendFilter(); + sql = new StringBuilder(); + if (this.OrderByValue == null && (Skip != null || Take != null)) this.OrderByValue = " ORDER BY " + this.Builder.SqlDateNow + " "; + if (this.PartitionByValue.HasValue()) + { + this.OrderByValue = this.PartitionByValue + this.OrderByValue; + } + var isRowNumber = Skip != null || Take != null; + var rowNumberString = string.Format(",ROW_NUMBER() OVER({0}) AS RowIndex ", GetOrderByString); + string groupByValue = GetGroupByString + HavingInfos; + string orderByValue = (!isRowNumber && this.OrderByValue.HasValue()) ? GetOrderByString : null; + if (isIgnoreOrderBy) { orderByValue = null; } + sql.AppendFormat(SqlTemplate, GetSelectValue, GetTableNameString, GetWhereValueString, groupByValue, orderByValue); + sql.Replace(UtilConstants.ReplaceKey, isRowNumber ? (isIgnoreOrderBy ? null : rowNumberString) : null); + if (isIgnoreOrderBy) { this.OrderByValue = oldOrderBy; return sql.ToString(); } + var result = ToPageSql(sql.ToString(), this.Take, this.Skip); + if (this.GetGroupByString == null && this.Take == 1 && this.Skip == 0 && oldOrderBy == "ORDER BY sysdate ") + { + result = $" {sql.ToString()} {(this.WhereInfos.Count != 0 ? "AND" : "WHERE")} ROWNUM = 1 "; + result = result.Replace(rowNumberString, " "); + } + if (ExternalPageIndex > 0) + { + if (externalOrderBy.IsNullOrEmpty()) + { + externalOrderBy = " ORDER BY " + this.Builder.SqlDateNow + " "; + } + result = string.Format("SELECT ExternalTable.*,ROW_NUMBER() OVER({0}) AS RowIndex2 FROM ({1}) ExternalTable ", GetExternalOrderBy(externalOrderBy), result); + result = ToPageSql2(result, ExternalPageIndex, ExternalPageSize, true); + } + this.OrderByValue = oldOrderBy; + result = GetSqlQuerySql(result); + if (result.IndexOf("-- No table") > 0) + { + return "-- No table"; + } + return result; + } + public override string ToPageSql(string sql, int? take, int? skip, bool isExternal = false) + { + string temp = isExternal ? ExternalPageTempalte : PageTempalte; + if (skip != null && take == null) + { + return string.Format(temp, sql.ToString(), skip.ObjToInt() + 1, long.MaxValue); + } + else if (skip == null && take != null) + { + return string.Format(temp, sql.ToString(), 1, take.ObjToInt()); + } + else if (skip != null && take != null) + { + return string.Format(temp, sql.ToString(), skip.ObjToInt() + 1, skip.ObjToInt() + take.ObjToInt()); + } + else + { + return sql.ToString(); + } + } + + public override string ToPageSql2(string sql, int? pageIndex, int? pageSize, bool isExternal = false) + { + string temp = isExternal ? ExternalPageTempalte : PageTempalte; + return string.Format(temp, sql.ToString(), (pageIndex - 1) * pageSize + 1, pageIndex * pageSize); + } + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/SqlBuilder/OracleUpdateBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/SqlBuilder/OracleUpdateBuilder.cs new file mode 100644 index 000000000..115e0b2e9 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/SqlBuilder/OracleUpdateBuilder.cs @@ -0,0 +1,168 @@ +using System.Text; + +namespace SqlSugar +{ + public class OracleUpdateBuilder : UpdateBuilder + { + public override string ReSetValueBySqlExpListType { get; set; } = "oracle"; + protected override string TomultipleSqlString(List> groupList) + { + if (groupList.Count == 0) + { + return " select 0 from dual"; + } + StringBuilder sb = new StringBuilder(); + sb.AppendLine("Begin"); + sb.AppendLine(string.Join("\r\n", groupList.Select(t => + { + var updateTable = string.Format("UPDATE {0} SET", base.GetTableNameStringNoWith); + var setValues = string.Join(",", t.Where(s => !s.IsPrimarykey).Where(s => OldPrimaryKeys?.Contains(s.DbColumnName) != true).Select(m => GetOracleUpdateColums(m)).ToArray()); + var pkList = t.Where(s => s.IsPrimarykey).ToList(); + if (this.IsWhereColumns && this.PrimaryKeys?.Count > 0) + { + var whereColumns = pkList.Where(it => this.PrimaryKeys?.Any(p => p.EqualCase(it.PropertyName) || p.EqualCase(it.DbColumnName)) == true).ToList(); + if (whereColumns.Count != 0) + { + pkList = whereColumns; + } + } + List whereList = new List(); + foreach (var item in pkList) + { + var isFirst = pkList.First() == item; + var whereString = isFirst ? " " : " AND "; + whereString += GetOracleUpdateColums(item, true); + whereList.Add(whereString); + } + return string.Format("{0} {1} WHERE {2};", updateTable, setValues, string.Join("", whereList)); + }).ToArray())); + sb.AppendLine("End;"); + return sb.ToString(); + } + + private string GetOracleUpdateColums(DbColumnInfo m, bool isWhere = false) + { + + var result = string.Format("\"{0}\"={1} ", m.DbColumnName.ToUpper(IsUppper), base.GetDbColumn(m, FormatValue(m.Value, m.IsPrimarykey, m.PropertyName))); + if (isWhere && m.Value == null) + { + result = result.Replace("=NULL ", " is NULL "); + } + return result; + } + int i = 0; + public object FormatValue(object value, bool isPrimaryKey, string name) + { + if (value == null) + { + return "NULL"; + } + else + { + string N = this.Context.GetN(); + if (isPrimaryKey) + { + N = ""; + } + var type = UtilMethods.GetUnderType(value.GetType()); + if (type == UtilConstants.DateType) + { + var date = value.ObjToDate(); + if (date < UtilMethods.GetMinDate(this.Context.CurrentConnectionConfig)) + { + date = UtilMethods.GetMinDate(this.Context.CurrentConnectionConfig); + } + if (this.Context.CurrentConnectionConfig?.MoreSettings?.DisableMillisecond == true) + { + return "to_date('" + date.ToString("yyyy-MM-dd HH:mm:ss") + "', 'YYYY-MM-DD HH24:MI:SS') "; + } + else + { + return "to_timestamp('" + date.ToString("yyyy-MM-dd HH:mm:ss.ffffff") + "', 'YYYY-MM-DD HH24:MI:SS.FF') "; + } + } + else if (type.IsEnum()) + { + return Convert.ToInt64(value); + } + else if (type.IsIn(UtilConstants.IntType, UtilConstants.LongType, UtilConstants.ShortType)) + { + return value; + } + else if (type == UtilConstants.GuidType) + { + return "'" + value.ToString() + "'"; + } + else if (type == UtilConstants.ByteArrayType) + { + string bytesString = "0x" + Convert.ToHexString((byte[])value); + return bytesString; + } + else if (type == UtilConstants.BoolType) + { + return value.ObjToBool() ? "1" : "0"; + } + else if (value is TimeSpan ts) + { + return string.Format( + "INTERVAL '{0} {1:D2}:{2:D2}:{3:D2}.{4:D3}' DAY TO SECOND(3)", + ts.Days, + ts.Hours, + ts.Minutes, + ts.Seconds, + ts.Milliseconds); + } + else if (type == UtilConstants.DateTimeOffsetType) + { + var date = UtilMethods.ConvertFromDateTimeOffset((DateTimeOffset)value); + return "to_timestamp('" + date.ToString("yyyy-MM-dd HH:mm:ss.ffffff") + "', 'YYYY-MM-DD HH24:MI:SS.FF') "; + } + else if (type == UtilConstants.StringType || type == UtilConstants.ObjType) + { + if (value.ToString().Length > 1000) + { + ++i; + var parameterName = this.Builder.SqlParameterKeyWord + name + i; + this.Parameters.Add(new SugarParameter(parameterName, value)); + return parameterName; + } + else + { + return N + "'" + value.ToString().ToSqlFilter() + "'"; + } + } + else + { + return N + "'" + value.ToString() + "'"; + } + } + } + protected override string GetJoinUpdate(string columnsString, ref string whereString) + { + var joinString = $" {Builder.GetTranslationColumnName(this.TableName)} {Builder.GetTranslationColumnName(this.ShortName)} "; + foreach (var item in this.JoinInfos) + { + joinString += $"\r\n USING {Builder.GetTranslationColumnName(item.TableName)} {Builder.GetTranslationColumnName(item.ShortName)} ON {item.JoinWhere} "; + } + var tableName = joinString + "\r\n "; + var newTemp = SqlTemplate.Replace("UPDATE", "MERGE INTO").Replace("SET", "WHEN MATCHED THEN \r\nUPDATE SET"); + return string.Format(newTemp, tableName, columnsString, whereString); + } + #region Helper + public bool IsUppper + { + get + { + if (this.Context.CurrentConnectionConfig.MoreSettings == null) + { + return true; + } + else + { + return this.Context.CurrentConnectionConfig.MoreSettings.IsAutoToUpper == true; + } + } + } + #endregion + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/Updateable/OracleUpdateable.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/Updateable/OracleUpdateable.cs new file mode 100644 index 000000000..54c9d1d42 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oracle/Updateable/OracleUpdateable.cs @@ -0,0 +1,58 @@ +namespace SqlSugar +{ + public class OracleUpdateable : UpdateableProvider where T : class, new() + { + protected override List GetIdentityKeys() + { + return this.EntityInfo.Columns.Where(it => it.OracleSequenceName.HasValue()).Select(it => it.DbColumnName).ToList(); + } + public override int ExecuteCommand() + { + if (base.UpdateObjs.Length == 1) + { + var resultl = base.ExecuteCommand(); + if (resultl == -1) + { + return 1; + } + else + { + return resultl; + } + } + else if (base.UpdateObjs.Length == 0) + { + return 0; + } + else + { + base.ExecuteCommand(); + return base.UpdateObjs.Length; + } + } + public async override Task ExecuteCommandAsync() + { + if (base.UpdateObjs.Length == 1) + { + var result = await base.ExecuteCommandAsync().ConfigureAwait(false); + if (result == -1) + { + return 1; + } + else + { + return result; + } + } + else if (base.UpdateObjs.Length == 0) + { + return 0; + } + else + { + await base.ExecuteCommandAsync().ConfigureAwait(false); + return base.UpdateObjs.Length; + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oscar/CodeFirst/OscarCodeFirst.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oscar/CodeFirst/OscarCodeFirst.cs new file mode 100644 index 000000000..166b9c10d --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oscar/CodeFirst/OscarCodeFirst.cs @@ -0,0 +1,73 @@ +namespace SqlSugar +{ + public class OscarCodeFirst : CodeFirstProvider + { + public override void NoExistLogic(EntityInfo entityInfo) + { + var tableName = GetTableName(entityInfo); + //Check.Exception(entityInfo.Columns.Where(it => it.IsPrimarykey).Count() > 1, "Use Code First ,The primary key must not exceed 1"); + List columns = new List(); + if (entityInfo.Columns.HasValue()) + { + foreach (var item in entityInfo.Columns.Where(it => it.IsIgnore == false)) + { + DbColumnInfo dbColumnInfo = this.EntityColumnToDbColumn(entityInfo, tableName, item); + columns.Add(dbColumnInfo); + } + if (entityInfo.IsCreateTableFiledSort) + { + columns = columns.OrderBy(c => c.CreateTableFieldSort).ToList(); + } + } + this.Context.DbMaintenance.CreateTable(tableName, columns, true); + } + protected override DbColumnInfo EntityColumnToDbColumn(EntityInfo entityInfo, string tableName, EntityColumnInfo item) + { + var propertyType = UtilMethods.GetUnderType(item.PropertyInfo); + var result = new DbColumnInfo() + { + TableId = entityInfo.Columns.IndexOf(item), + DbColumnName = item.DbColumnName.HasValue() ? item.DbColumnName : item.PropertyName, + IsPrimarykey = item.IsPrimarykey, + IsIdentity = item.IsIdentity, + TableName = tableName, + IsNullable = item.IsNullable, + DefaultValue = item.DefaultValue, + ColumnDescription = item.ColumnDescription, + Length = item.Length + }; + if (propertyType == UtilConstants.DecType) + { + result.Scale = item.DecimalDigits; + result.DecimalDigits = item.DecimalDigits; + } + GetDbType(item, propertyType, result); + if (result.DataType.Equals("varchar", StringComparison.CurrentCultureIgnoreCase) && result.Length == 0) + { + result.Length = 1; + } + return result; + } + + protected override void ConvertColumns(List dbColumns) + { + foreach (var item in dbColumns) + { + if (item.DataType == "DateTime") + { + item.Length = 0; + } + } + } + + protected override void ChangeKey(EntityInfo entityInfo, string tableName, EntityColumnInfo item) + { + //this.Context.DbMaintenance.UpdateColumn(tableName, EntityColumnToDbColumn(entityInfo, tableName, item)); + //if (!item.IsPrimarykey) + // this.Context.DbMaintenance.DropConstraint(tableName,null); + //if (item.IsPrimarykey) + // this.Context.DbMaintenance.AddPrimaryKey(tableName, item.DbColumnName); + } + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oscar/DbBind/OscarDbBind.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oscar/DbBind/OscarDbBind.cs new file mode 100644 index 000000000..a584bd88d --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oscar/DbBind/OscarDbBind.cs @@ -0,0 +1,136 @@ +namespace SqlSugar +{ + public class OscarDbBind : DbBindProvider + { + public override string GetDbTypeName(string csharpTypeName) + { + if (csharpTypeName == UtilConstants.ByteArrayType.Name) + return "bytea"; + + var result = base.GetDbTypeName(csharpTypeName); + return result; + } + public override string GetPropertyTypeName(string dbTypeName) + { + dbTypeName = dbTypeName.ToLower(); + var propertyTypes = MappingTypes.Where(it => it.Value.ToString().Equals(dbTypeName, StringComparison.CurrentCultureIgnoreCase) || it.Key.Equals(dbTypeName, StringComparison.CurrentCultureIgnoreCase)); + if (propertyTypes == null) + { + return "other"; + } + else if (dbTypeName == "xml" || dbTypeName == "string" || dbTypeName == "jsonb" || dbTypeName == "json") + { + return "string"; + } + else if (dbTypeName == "bpchar")//数据库char datatype 查询出来的时候是 bpchar + { + return "char"; + } + if (dbTypeName == "byte[]") + { + return "byte[]"; + } + else if (propertyTypes?.Any() != true) + { + if (dbTypeName.StartsWith('_')) + { + var dbTypeName2 = dbTypeName.TrimStart('_'); + return MappingTypes.Where(it => it.Value.ToString().Equals(dbTypeName2, StringComparison.CurrentCultureIgnoreCase) || it.Key.Equals(dbTypeName2, StringComparison.CurrentCultureIgnoreCase)).Select(it => it.Value + "[]").First(); + } + Check.ThrowNotSupportedException(string.Format(" \"{0}\" Type NotSupported, DbBindProvider.GetPropertyTypeName error.", dbTypeName)); + return null; + } + else if (propertyTypes.First().Value == CSharpDataType.byteArray) + { + return "byte[]"; + } + else + { + return propertyTypes.First().Value.ToString(); + } + } + public override List> MappingTypes + { + get + { + var extService = this.Context.CurrentConnectionConfig.ConfigureExternalServices; + if (extService?.AppendDataReaderTypeMappings.HasValue() == true) + { + return extService.AppendDataReaderTypeMappings.Union(MappingTypesConst).ToList(); + } + else + { + return MappingTypesConst; + } + } + } + public static List> MappingTypesConst = new List>(){ + + new KeyValuePair("int2",CSharpDataType.@short), + new KeyValuePair("int1",CSharpDataType.@byte), + new KeyValuePair("smallint",CSharpDataType.@short), + new KeyValuePair("int4",CSharpDataType.@int), + new KeyValuePair("integer",CSharpDataType.@int), + new KeyValuePair("int8",CSharpDataType.@long), + new KeyValuePair("bigint",CSharpDataType.@long), + new KeyValuePair("float4",CSharpDataType.@float), + new KeyValuePair("real",CSharpDataType.@float), + new KeyValuePair("float8",CSharpDataType.@double), + new KeyValuePair("double precision",CSharpDataType.@int), + new KeyValuePair("numeric",CSharpDataType.@decimal), + new KeyValuePair("decimal",CSharpDataType.@decimal), + new KeyValuePair("path",CSharpDataType.@decimal), + new KeyValuePair("point",CSharpDataType.@decimal), + new KeyValuePair("polygon",CSharpDataType.@decimal), + + new KeyValuePair("boolean",CSharpDataType.@bool), + new KeyValuePair("bool",CSharpDataType.@bool), + new KeyValuePair("box",CSharpDataType.@bool), + new KeyValuePair("bytea",CSharpDataType.byteArray), + + new KeyValuePair("varchar",CSharpDataType.@string), + new KeyValuePair("character varying",CSharpDataType.@string), + new KeyValuePair("geometry",CSharpDataType.@string), + new KeyValuePair("name",CSharpDataType.@string), + new KeyValuePair("text",CSharpDataType.@string), + new KeyValuePair("blob",CSharpDataType.byteArray), + new KeyValuePair("clob",CSharpDataType.@string), + new KeyValuePair("char",CSharpDataType.@string), + new KeyValuePair("character",CSharpDataType.@string), + new KeyValuePair("cidr",CSharpDataType.@string), + new KeyValuePair("circle",CSharpDataType.@string), + new KeyValuePair("tsquery",CSharpDataType.@string), + new KeyValuePair("tsvector",CSharpDataType.@string), + new KeyValuePair("txid_snapshot",CSharpDataType.@string), + new KeyValuePair("uuid",CSharpDataType.Guid), + new KeyValuePair("xml",CSharpDataType.@string), + new KeyValuePair("json",CSharpDataType.@string), + + new KeyValuePair("interval",CSharpDataType.@decimal), + new KeyValuePair("lseg",CSharpDataType.@decimal), + new KeyValuePair("macaddr",CSharpDataType.@decimal), + new KeyValuePair("money",CSharpDataType.@decimal), + new KeyValuePair("timestamp",CSharpDataType.DateTime), + new KeyValuePair("timestamp with time zone",CSharpDataType.DateTime), + new KeyValuePair("timestamptz",CSharpDataType.DateTime), + new KeyValuePair("timestamp without time zone",CSharpDataType.DateTime), + new KeyValuePair("date",CSharpDataType.DateTime), + new KeyValuePair("time",CSharpDataType.DateTime), + new KeyValuePair("time with time zone",CSharpDataType.DateTime), + new KeyValuePair("timetz",CSharpDataType.DateTime), + new KeyValuePair("time without time zone",CSharpDataType.DateTime), + + new KeyValuePair("bit",CSharpDataType.byteArray), + new KeyValuePair("bit varying",CSharpDataType.byteArray), + new KeyValuePair("varbit",CSharpDataType.@byte), + new KeyValuePair("time",CSharpDataType.TimeSpan), + }; + public override List StringThrow + { + get + { + return new List() { "int32", "datetime", "decimal", "double", "byte" }; + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oscar/DbFirst/OscarDbFirst.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oscar/DbFirst/OscarDbFirst.cs new file mode 100644 index 000000000..23a27c7e9 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oscar/DbFirst/OscarDbFirst.cs @@ -0,0 +1,6 @@ +namespace SqlSugar +{ + public class OscarDbFirst : DbFirstProvider + { + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oscar/DbMaintenance/OscarDbMaintenance.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oscar/DbMaintenance/OscarDbMaintenance.cs new file mode 100644 index 000000000..d3e8c2170 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oscar/DbMaintenance/OscarDbMaintenance.cs @@ -0,0 +1,415 @@ +using System.Data.Common; + +namespace SqlSugar +{ + public class OscarDbMaintenance : DbMaintenanceProvider + { + #region DML + protected override string GetDataBaseSql + { + get + { + return "SELECT datname FROM sys_database "; + } + } + protected override string GetColumnInfosByTableNameSql + { + get + { + string sql = @" SELECT + A.COLUMN_NAME AS DbColumnName, + A.TABLE_NAME AS TableName, + A.DATA_TYPE AS DataType, + case when DATA_DEFAULT like 'NEXTVAL%' then true else false end as IsIdentity, + case when A.NULLABLE = 'Y' then true else false end as IsNullable , + A.DATA_LENGTH AS LENGTH, + B.COMMENTS AS ColumnDescription, + CASE WHEN K.COLUMN_NAME IS NULL THEN FALSE ELSE TRUE END AS IsPrimarykey, + DATA_SCALE AS DecimalDigits, + A.DATA_PRECISION AS SCALE, + A.DATA_DEFAULT as DefaultValue + FROM + INFO_SCHEM.ALL_TAB_COLUMNS A + LEFT JOIN INFO_SCHEM.SYS_CLASS T ON T.RELNAME=A.TABLE_NAME + LEFT JOIN INFO_SCHEM.ALL_COL_COMMENTS B ON A.TABLE_NAME=B.TABLE_NAME AND A.COLUMN_NAME=B.COLUMN_NAME + LEFT JOIN INFO_SCHEM.SYS_ATTRIBUTE C ON C.ATTNAME=A.COLUMN_NAME AND C.ATTRELID=T.OID + LEFT JOIN INFO_SCHEM.V_SYS_PRIMARY_KEYS K ON A.TABLE_NAME=K.TABLE_NAME AND K.COLUMN_NAME=A.COLUMN_NAME + WHERE upper(A.TABLE_NAME)=upper('{0}') + ORDER BY c.ATTNUM + "; + return sql; + } + } + protected override string GetTableInfoListSql + { + get + { + //AND t.relnamespace=1 表空间限制。 + return @" select cast(relname as varchar(500)) as Name , DESCRIPTION AS Description FROM sys_class t + LEFT JOIN sys_description d ON t.OID=d.OBJOID AND d.OBJSUBID=0 + WHERE t.relvbase>0 AND t.relkind = 'r' AND t.relnamespace=(SELECT OID FROM sys_namespace WHERE nspname =USER) + order by relname"; + } + } + protected override string GetViewInfoListSql + { + get + { + return @" select cast(relname as varchar(500)) as Name , DESCRIPTION AS Description FROM sys_class t + LEFT JOIN sys_description d ON t.OID=d.OBJOID AND d.OBJSUBID=0 + WHERE t.relvbase>0 AND t.relkind = 'v' AND t.relnamespace=(SELECT OID FROM sys_namespace WHERE nspname =USER) + order by relname"; + } + } + #endregion + + #region DDL + protected override string CreateDataBaseSql + { + get + { + return "CREATE DATABASE {0}"; + } + } + protected override string AddPrimaryKeySql + { + get + { + return "ALTER TABLE {0} ADD PRIMARY KEY({2}) /*{1}*/"; + } + } + protected override string AddColumnToTableSql + { + get + { + return "ALTER TABLE {0} ADD COLUMN {1} {2}{3} {4} {5} {6}"; + } + } + protected override string AlterColumnToTableSql + { + get + { + return "alter table {0} ALTER COLUMN {1} {2}{3} {4} {5} {6}"; + } + } + protected override string BackupDataBaseSql + { + get + { + return "mysqldump.exe {0} -uroot -p > {1} "; + } + } + protected override string CreateTableSql + { + get + { + return "CREATE TABLE {0}(\r\n{1} $PrimaryKey)"; + } + } + protected override string CreateTableColumn + { + get + { + return "{0} {1}{2} {3} {4} {5}"; + } + } + protected override string TruncateTableSql + { + get + { + return "TRUNCATE TABLE {0}"; + } + } + protected override string BackupTableSql + { + get + { + return "create table {0} as (select * from {1} limit {2} offset 0)"; + } + } + protected override string DropTableSql + { + get + { + return "DROP TABLE {0}"; + } + } + protected override string DropColumnToTableSql + { + get + { + return "ALTER TABLE {0} DROP COLUMN {1}"; + } + } + protected override string DropConstraintSql + { + get + { + return "ALTER TABLE {0} DROP CONSTRAINT {1}"; + } + } + protected override string RenameColumnSql + { + get + { + return "ALTER TABLE {0} RENAME {1} TO {2}"; + } + } + protected override string AddColumnRemarkSql => "comment on column {1}.{0} is '{2}'"; + + protected override string DeleteColumnRemarkSql => "comment on column {1}.{0} is ''"; + + protected override string IsAnyColumnRemarkSql { get { throw new NotSupportedException(); } } + + protected override string AddTableRemarkSql => "comment on table {0} is '{1}'"; + + protected override string DeleteTableRemarkSql => "comment on table {0} is ''"; + + protected override string IsAnyTableRemarkSql { get { throw new NotSupportedException(); } } + + protected override string RenameTableSql => "alter table 表名 {0} to {1}"; + + protected override string CreateIndexSql + { + get + { + return "CREATE {3} INDEX Index_{0}_{2} ON {0} ({1})"; + } + } + protected override string AddDefaultValueSql + { + get + { + return "ALTER TABLE {0} ALTER COLUMN {1} SET DEFAULT {2}"; + } + } + protected override string IsAnyIndexSql + { + get + { + return " Select count(1) from (SELECT to_regclass('{0}') as c ) t where t.c is not null"; + } + } + protected override string IsAnyProcedureSql => throw new NotImplementedException(); + + #endregion + + #region Check + protected override string CheckSystemTablePermissionsSql + { + get + { + return "select 1 from INFO_SCHEM.ALL_TAB_COLUMNS limit 1 offset 0"; + } + } + #endregion + + #region Scattered + protected override string CreateTableNull + { + get + { + return "DEFAULT NULL"; + } + } + protected override string CreateTableNotNull + { + get + { + return "NOT NULL"; + } + } + protected override string CreateTablePirmaryKey + { + get + { + return "PRIMARY KEY"; + } + } + protected override string CreateTableIdentity + { + get + { + return "serial"; + } + } + #endregion + + #region Methods + public override bool UpdateColumn(string tableName, DbColumnInfo columnInfo) + { + + tableName = this.SqlBuilder.GetTranslationTableName(tableName); + var columnName = this.SqlBuilder.GetTranslationColumnName(columnInfo.DbColumnName); + string type = GetType(tableName, columnInfo); + //this.Context.Ado.ExecuteCommand(sql); + + string sql = @"ALTER TABLE {table} ALTER TYPE {column} {type};ALTER TABLE {table} ALTER COLUMN {column} {null}"; + + var isnull = columnInfo.IsNullable ? " DROP NOT NULL " : " SET NOT NULL "; + + sql = sql.Replace("{table}", tableName) + .Replace("{type}", type) + .Replace("{column}", columnName) + .Replace("{null}", isnull); + this.Context.Ado.ExecuteCommand(sql); + return true; + } + + protected string GetType(string tableName, DbColumnInfo columnInfo) + { + string columnName = this.SqlBuilder.GetTranslationColumnName(columnInfo.DbColumnName); + tableName = this.SqlBuilder.GetTranslationTableName(tableName); + string dataSize = GetSize(columnInfo); + string dataType = columnInfo.DataType; + //if (!string.IsNullOrEmpty(dataType)) + //{ + // dataType = dataType; + //} + return dataType + "" + dataSize; + } + protected override string GetUpdateColumnSql(string tableName, DbColumnInfo columnInfo) + { + string columnName = this.SqlBuilder.GetTranslationColumnName(columnInfo.DbColumnName); + tableName = this.SqlBuilder.GetTranslationTableName(tableName); + string dataSize = GetSize(columnInfo); + string dataType = columnInfo.DataType; + if (!string.IsNullOrEmpty(dataType)) + { + dataType = " type " + dataType; + } + string nullType = ""; + string primaryKey = null; + string identity = null; + string result = string.Format(this.AlterColumnToTableSql, tableName, columnName, dataType, dataSize, nullType, primaryKey, identity); + return result; + } + + /// + ///by current connection string + /// + /// + /// + /// + public override bool CreateDatabase(string databaseName, string databaseDirectory = null) + { + throw new NotSupportedException("Not Supported CreateDatabase"); + } + public override bool AddRemark(EntityInfo entity) + { + var db = this.Context; + var columns = entity.Columns.Where(it => it.IsIgnore == false).ToList(); + + foreach (var item in columns) + { + if (item.ColumnDescription != null) + { + db.DbMaintenance.AddColumnRemark(item.DbColumnName, item.DbTableName, item.ColumnDescription); + + } + } + //table remak + if (entity.TableDescription != null) + { + db.DbMaintenance.AddTableRemark(entity.DbTableName, entity.TableDescription); + } + return true; + } + public override bool CreateTable(string tableName, List columns, bool isCreatePrimaryKey = true) + { + if (columns.HasValue()) + { + foreach (var item in columns) + { + if (item.DbColumnName.Equals("GUID", StringComparison.CurrentCultureIgnoreCase) && item.Length == 0) + { + item.Length = 10; + } + } + } + string sql = GetCreateTableSql(tableName, columns); + string primaryKeyInfo = null; + if (columns.Any(it => it.IsPrimarykey) && isCreatePrimaryKey) + { + primaryKeyInfo = string.Format(", Primary key({0})", string.Join(",", columns.Where(it => it.IsPrimarykey).Select(it => this.SqlBuilder.GetTranslationColumnName(it.DbColumnName.ToLower())))); + + } + sql = sql.Replace("$PrimaryKey", primaryKeyInfo); + this.Context.Ado.ExecuteCommand(sql); + return true; + } + protected override string GetCreateTableSql(string tableName, List columns) + { + List columnArray = new List(); + Check.Exception(columns.IsNullOrEmpty(), "No columns found "); + foreach (var item in columns) + { + string columnName = item.DbColumnName; + string dataType = item.DataType; + if (dataType == "varchar" && item.Length == 0) + { + item.Length = 1; + } + if (dataType == "uuid") + { + item.Length = 50; + dataType = "varchar"; + } + string dataSize = item.Length > 0 ? string.Format("({0})", item.Length) : null; + if (item.DecimalDigits > 0 && item.Length > 0 && dataType == "numeric") + { + dataSize = $"({item.Length},{item.DecimalDigits})"; + } + string nullType = item.IsNullable ? this.CreateTableNull : CreateTableNotNull; + string primaryKey = null; + string addItem = string.Format(this.CreateTableColumn, this.SqlBuilder.GetTranslationColumnName(columnName.ToLower()), dataType, dataSize, nullType, primaryKey, ""); + if (item.IsIdentity) + { + string length = dataType.Substring(dataType.Length - 1); + string identityDataType = "serial" + length; + addItem = addItem.Replace(dataType, identityDataType); + } + columnArray.Add(addItem); + } + string tableString = string.Format(this.CreateTableSql, this.SqlBuilder.GetTranslationTableName(tableName.ToLower()), string.Join(",\r\n", columnArray)); + return tableString; + } + public override bool IsAnyConstraint(string constraintName) + { + throw new NotSupportedException("PgSql IsAnyConstraint NotSupportedException"); + } + public override bool BackupDataBase(string databaseName, string fullFileName) + { + Check.ThrowNotSupportedException("PgSql BackupDataBase NotSupported"); + return false; + } + + public override List GetColumnInfosByTableName(string tableName, bool isCache = true) + { + var result = base.GetColumnInfosByTableName(tableName.TrimStart('"').TrimEnd('"'), isCache); + foreach (var columnInfo in result) + { + if (columnInfo.IsIdentity && !columnInfo.DataType.ObjToString().Contains("int", StringComparison.CurrentCultureIgnoreCase)) + { + columnInfo.IsIdentity = false; + } + } + string sql = "select * from " + SqlBuilder.GetTranslationTableName(tableName) + " WHERE 1=2 "; + var oldIsEnableLog = this.Context.Ado.IsEnableLogEvent; + this.Context.Ado.IsEnableLogEvent = false; + using (DbDataReader reader = (DbDataReader)this.Context.Ado.GetDataReader(sql)) + { + this.Context.Ado.IsEnableLogEvent = oldIsEnableLog; + var schemaTable = reader.GetSchemaTable(); + foreach (System.Data.DataRow row in schemaTable.Rows) + { + var name = row["columnname"] + ""; + var data = result.First(it => it.DbColumnName.Equals(name, StringComparison.OrdinalIgnoreCase)); + data.IsPrimarykey = row["iskey"].ToString() == "True" ? true : false; + } + } + return result; + } + #endregion + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oscar/Insertable/OscarInserttable.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oscar/Insertable/OscarInserttable.cs new file mode 100644 index 000000000..b2e87c471 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oscar/Insertable/OscarInserttable.cs @@ -0,0 +1,67 @@ +namespace SqlSugar +{ + public class OscarInserttable : InsertableProvider where T : class, new() + { + public override int ExecuteReturnIdentity() + { + InsertBuilder.IsReturnIdentity = true; + PreToSql(); + string sql = InsertBuilder.ToSqlString().Replace("$PrimaryKey", this.SqlBuilder.GetTranslationColumnName(GetIdentityKeys().FirstOrDefault())); + RestoreMapping(); + var result = Ado.GetScalar(sql, InsertBuilder.Parameters == null ? null : InsertBuilder.Parameters.ToArray()).ObjToInt(); + return result; + } + public override async Task ExecuteReturnIdentityAsync() + { + InsertBuilder.IsReturnIdentity = true; + PreToSql(); + string sql = InsertBuilder.ToSqlString().Replace("$PrimaryKey", this.SqlBuilder.GetTranslationColumnName(GetIdentityKeys().FirstOrDefault())); + RestoreMapping(); + var obj = await Ado.GetScalarAsync(sql, InsertBuilder.Parameters == null ? null : InsertBuilder.Parameters.ToArray()).ConfigureAwait(false); + var result = obj.ObjToInt(); + return result; + } + public override KeyValuePair> ToSql() + { + var result = base.ToSql(); + return new KeyValuePair>(result.Key.Replace("$PrimaryKey", GetPrimaryKeys().FirstOrDefault()), result.Value); + } + + public override long ExecuteReturnBigIdentity() + { + InsertBuilder.IsReturnIdentity = true; + PreToSql(); + string sql = InsertBuilder.ToSqlString().Replace("$PrimaryKey", this.SqlBuilder.GetTranslationColumnName(GetIdentityKeys().FirstOrDefault())); + RestoreMapping(); + var result = Convert.ToInt64(Ado.GetScalar(sql, InsertBuilder.Parameters == null ? null : InsertBuilder.Parameters.ToArray()) ?? "0"); + return result; + } + public override async Task ExecuteReturnBigIdentityAsync() + { + InsertBuilder.IsReturnIdentity = true; + PreToSql(); + string sql = InsertBuilder.ToSqlString().Replace("$PrimaryKey", this.SqlBuilder.GetTranslationColumnName(GetIdentityKeys().FirstOrDefault())); + RestoreMapping(); + var result = Convert.ToInt64(await Ado.GetScalarAsync(sql, InsertBuilder.Parameters == null ? null : InsertBuilder.Parameters.ToArray()).ConfigureAwait(false) ?? "0"); + return result; + } + + public override bool ExecuteCommandIdentityIntoEntity() + { + var result = InsertObjs.First(); + var identityKeys = GetIdentityKeys(); + if (identityKeys.Count == 0) { return this.ExecuteCommand() > 0; } + var idValue = ExecuteReturnBigIdentity(); + Check.Exception(identityKeys.Count > 1, "ExecuteCommandIdentityIntoEntity does not support multiple identity keys"); + var identityKey = identityKeys.First(); + object setValue = 0; + if (idValue > int.MaxValue) + setValue = idValue; + else + setValue = Convert.ToInt32(idValue); + var propertyName = this.Context.EntityMaintenance.GetPropertyName(identityKey); + typeof(T).GetProperties().First(t => t.Name.Equals(propertyName, StringComparison.CurrentCultureIgnoreCase)).SetValue(result, setValue, null); + return idValue > 0; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oscar/OscarProvider.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oscar/OscarProvider.cs new file mode 100644 index 000000000..8fbebcd60 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oscar/OscarProvider.cs @@ -0,0 +1,158 @@ +using System.Data; +using System.Data.Common; +using System.Data.OscarClient; + +namespace SqlSugar +{ + public partial class OscarProvider : AdoProvider + { + public OscarProvider() { } + public override IDbConnection Connection + { + get + { + if (base._DbConnection == null) + { + try + { + var OscarConnectionString = base.Context.CurrentConnectionConfig.ConnectionString; + base._DbConnection = new OscarConnection(OscarConnectionString); + } + catch (Exception ex) + { + Check.Exception(true, ErrorMessage.ConnnectionOpen, ex.Message); + } + } + return base._DbConnection; + } + set + { + base._DbConnection = value; + } + } + + public override void BeginTran(string transactionName) + { + base.BeginTran(); + } + /// + /// Only SqlServer + /// + /// + /// + public override void BeginTran(IsolationLevel iso, string transactionName) + { + base.BeginTran(iso); + } + public override IDataAdapter GetAdapter() + { + return new OscarDataAdapter(); + } + public override DbCommand GetCommand(string sql, SugarParameter[] parameters) + { + OscarCommand sqlCommand = new OscarCommand(sql, (OscarConnection)this.Connection); + sqlCommand.CommandType = this.CommandType; + sqlCommand.CommandTimeout = this.CommandTimeOut; + if (this.Transaction != null) + { + sqlCommand.Transaction = (OscarTransaction)this.Transaction; + } + if (parameters.HasValue()) + { + IDataParameter[] ipars = ToIDbDataParameter(parameters); + sqlCommand.Parameters.AddRange((OscarParameter[])ipars); + } + CheckConnection(); + return sqlCommand; + } + public override void SetCommandToAdapter(IDataAdapter dataAdapter, DbCommand command) + { + ((OscarDataAdapter)dataAdapter).SelectCommand = (OscarCommand)command; + } + /// + /// if mysql return MySqlParameter[] pars + /// if sqlerver return SqlParameter[] pars ... + /// + /// + /// + public override IDataParameter[] ToIDbDataParameter(params SugarParameter[] parameters) + { + if (parameters == null || parameters.Length == 0) return null; + OscarParameter[] result = new OscarParameter[parameters.Length]; + int index = 0; + var isVarchar = this.Context.IsVarchar(); + foreach (var parameter in parameters) + { + if (parameter.Value == null) parameter.Value = DBNull.Value; + var sqlParameter = new OscarParameter(); + sqlParameter.ParameterName = parameter.ParameterName; + sqlParameter.Size = parameter.Size; + sqlParameter.Value = parameter.Value; + sqlParameter.DbType = parameter.DbType; + sqlParameter.Direction = parameter.Direction; + if (parameter.IsJson) + { + sqlParameter.OscarDbType = OscarDbType.Text; + } + if (parameter.IsArray) + { + // sqlParameter.Value = this.Context.Utilities.SerializeObject(sqlParameter.Value); + var type = sqlParameter.Value.GetType(); + if (ArrayMapping.TryGetValue(type, out OscarDbType value)) + { + sqlParameter.OscarDbType = value | OscarDbType.Array; + } + else + { + Check.Exception(true, sqlParameter.Value.GetType().Name + " No Support"); + } + } + if (sqlParameter.Direction == 0) + { + sqlParameter.Direction = ParameterDirection.Input; + } + result[index] = sqlParameter; + if (sqlParameter.Direction.IsIn(ParameterDirection.Output, ParameterDirection.InputOutput, ParameterDirection.ReturnValue)) + { + if (this.OutputParameters == null) this.OutputParameters = new List(); + this.OutputParameters.RemoveAll(it => it.ParameterName == sqlParameter.ParameterName); + this.OutputParameters.Add(sqlParameter); + } + if (isVarchar && sqlParameter.DbType == System.Data.DbType.String) + { + sqlParameter.DbType = System.Data.DbType.AnsiString; + } + ++index; + } + return result; + } + + + static readonly Dictionary ArrayMapping = new Dictionary() + { + { typeof(int[]),OscarDbType.Integer}, + { typeof(short[]),OscarDbType.SmallInt}, + { typeof(long[]),OscarDbType.BigInt}, + { typeof(decimal[]),OscarDbType.Numeric}, + { typeof(char[]),OscarDbType.Text}, + { typeof(byte[]),OscarDbType.Bytea}, + { typeof(bool[]),OscarDbType.Boolean}, + {typeof(DateTime[]),OscarDbType.Date}, + {typeof(float[]),OscarDbType.Real}, + + + { typeof(int?[]),OscarDbType.Integer}, + { typeof(short?[]),OscarDbType.SmallInt}, + { typeof(long?[]),OscarDbType.BigInt}, + { typeof(decimal?[]),OscarDbType.Numeric}, + { typeof(char?[]),OscarDbType.Text}, + { typeof(byte?[]),OscarDbType.Bytea}, + { typeof(bool?[]),OscarDbType.Boolean}, + {typeof(DateTime?[]),OscarDbType.Date}, + + + { typeof(string[]), OscarDbType.Text}, + {typeof(float?[]),OscarDbType.Real}, + }; + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oscar/Queryable/OscarQueryable.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oscar/Queryable/OscarQueryable.cs new file mode 100644 index 000000000..216a2056c --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oscar/Queryable/OscarQueryable.cs @@ -0,0 +1,63 @@ +namespace SqlSugar +{ + public class OscarQueryable : QueryableProvider + { + public override ISugarQueryable With(string withString) + { + return this; + } + + public override ISugarQueryable PartitionBy(string groupFileds) + { + this.GroupBy(groupFileds); + return this; + } + } + public class OscarQueryable : QueryableProvider + { + public new ISugarQueryable With(string withString) + { + return this; + } + } + public class OscarQueryable : QueryableProvider + { + + } + public class OscarQueryable : QueryableProvider + { + + } + public class OscarQueryable : QueryableProvider + { + + } + public class OscarQueryable : QueryableProvider + { + + } + public class OscarQueryable : QueryableProvider + { + + } + public class OscarQueryable : QueryableProvider + { + + } + public class OscarQueryable : QueryableProvider + { + + } + public class OscarQueryable : QueryableProvider + { + + } + public class OscarQueryable : QueryableProvider + { + + } + public class OscarQueryable : QueryableProvider + { + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oscar/SqlBuilder/OscarBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oscar/SqlBuilder/OscarBuilder.cs new file mode 100644 index 000000000..f18ea4e20 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oscar/SqlBuilder/OscarBuilder.cs @@ -0,0 +1,97 @@ +namespace SqlSugar +{ + public class OscarBuilder : SqlBuilderProvider + { + public override string SqlTranslationLeft + { + get + { + return "\""; + } + } + public override string SqlTranslationRight + { + get + { + return "\""; + } + } + public override string SqlDateNow + { + get + { + return "current_date"; + } + } + public override string FullSqlDateNow + { + get + { + return "select current_date"; + } + } + + public bool isAutoToUpper + { + get + { + if (this.Context?.CurrentConnectionConfig?.MoreSettings == null) return true; + return this.Context.CurrentConnectionConfig.MoreSettings.IsAutoToUpper; + } + } + public override string GetTranslationColumnName(string propertyName) + { + if (propertyName.Contains('.') && !propertyName.Contains(SqlTranslationLeft)) + { + return string.Join(".", propertyName.Split('.').Select(it => $"{SqlTranslationLeft}{it.ToUpper(isAutoToUpper)}{SqlTranslationRight}")); + } + if (propertyName.Contains(SqlTranslationLeft)) return propertyName; + else + return SqlTranslationLeft + propertyName.ToUpper(isAutoToUpper) + SqlTranslationRight; + } + + //public override string GetNoTranslationColumnName(string name) + //{ + // return name.TrimEnd(Convert.ToChar(SqlTranslationRight)).TrimStart(Convert.ToChar(SqlTranslationLeft)).ToLower(); + //} + public override string GetTranslationColumnName(string entityName, string propertyName) + { + Check.ArgumentNullException(entityName, string.Format(null, ErrorMessage.ObjNotExistCompositeFormat, "Table Name")); + Check.ArgumentNullException(propertyName, string.Format(null, ErrorMessage.ObjNotExistCompositeFormat, "Column Name")); + var context = this.Context; + var mappingInfo = context + .MappingColumns + .FirstOrDefault(it => + it.EntityName.Equals(entityName, StringComparison.CurrentCultureIgnoreCase) && + it.PropertyName.Equals(propertyName, StringComparison.CurrentCultureIgnoreCase)); + return (mappingInfo == null ? SqlTranslationLeft + propertyName.ToUpper(isAutoToUpper) + SqlTranslationRight : SqlTranslationLeft + mappingInfo.DbColumnName.ToUpper(isAutoToUpper) + SqlTranslationRight); + } + + public override string GetTranslationTableName(string name) + { + Check.ArgumentNullException(name, string.Format(null, ErrorMessage.ObjNotExistCompositeFormat, "Table Name")); + var context = this.Context; + + var mappingInfo = context + .MappingTables + .FirstOrDefault(it => it.EntityName.Equals(name, StringComparison.CurrentCultureIgnoreCase)); + name = (mappingInfo == null ? name : mappingInfo.DbTableName); + if (name.Contains('.') && !name.Contains('(')) + { + return string.Join(".", name.ToUpper(isAutoToUpper).Split('.').Select(it => SqlTranslationLeft + it + SqlTranslationRight)); + } + else if (name.Contains('(')) + { + return name; + } + else if (name.Contains(SqlTranslationLeft) && name.Contains(SqlTranslationRight)) + { + return name; + } + else + { + return SqlTranslationLeft + name.ToUpper(isAutoToUpper).TrimEnd('"').TrimStart('"') + SqlTranslationRight; + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oscar/SqlBuilder/OscarDeleteBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oscar/SqlBuilder/OscarDeleteBuilder.cs new file mode 100644 index 000000000..2b3f59941 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oscar/SqlBuilder/OscarDeleteBuilder.cs @@ -0,0 +1,7 @@ +namespace SqlSugar +{ + public class OscarDeleteBuilder : DeleteBuilder + { + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oscar/SqlBuilder/OscarExpressionContext.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oscar/SqlBuilder/OscarExpressionContext.cs new file mode 100644 index 000000000..20f8e6548 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oscar/SqlBuilder/OscarExpressionContext.cs @@ -0,0 +1,258 @@ +namespace SqlSugar +{ + public class OscarExpressionContext : ExpressionContext, ILambdaExpressions + { + public SqlSugarProvider Context { get; set; } + public bool isAutoToUpper + { + get + { + if (this?.SugarContext?.Context?.CurrentConnectionConfig?.MoreSettings == null) return true; + return this.SugarContext.Context.CurrentConnectionConfig.MoreSettings.IsAutoToUpper; + } + } + public OscarExpressionContext() + { + base.DbMehtods = new OscarLMethod(); + } + public override string SqlTranslationLeft + { + get + { + return "\""; + } + } + public override string SqlTranslationRight + { + get + { + return "\""; + } + } + public override string GetTranslationText(string name) + { + return SqlTranslationLeft + name.ToUpper(isAutoToUpper) + SqlTranslationRight; + } + + public override string GetTranslationTableName(string entityName, bool isMapping = true) + { + Check.ArgumentNullException(entityName, string.Format(null, ErrorMessage.ObjNotExistCompositeFormat, "Table Name")); + if (IsTranslationText(entityName)) return entityName; + isMapping = isMapping && this.MappingTables.HasValue(); + var isComplex = entityName.Contains(UtilConstants.Dot); + if (isMapping && isComplex) + { + var columnInfo = entityName.Split(UtilConstants.DotChar); + var mappingInfo = this.MappingTables.FirstOrDefault(it => it.EntityName.Equals(columnInfo.Last(), StringComparison.CurrentCultureIgnoreCase)); + if (mappingInfo != null) + { + columnInfo[columnInfo.Length - 1] = mappingInfo.EntityName; + } + return string.Join(UtilConstants.Dot, columnInfo.Select(it => GetTranslationText(it))); + } + else if (isMapping) + { + var mappingInfo = this.MappingTables.FirstOrDefault(it => it.EntityName.Equals(entityName, StringComparison.CurrentCultureIgnoreCase)); + + var tableName = mappingInfo?.DbTableName + ""; + if (tableName.Contains('.')) + { + tableName = string.Join(UtilConstants.Dot, tableName.Split(UtilConstants.DotChar).Select(it => GetTranslationText(it))); + return tableName; + } + + return SqlTranslationLeft + (mappingInfo == null ? entityName : mappingInfo.DbTableName).ToUpper(isAutoToUpper) + SqlTranslationRight; + } + else if (isComplex) + { + return string.Join(UtilConstants.Dot, entityName.Split(UtilConstants.DotChar).Select(it => GetTranslationText(it))); + } + else + { + return GetTranslationText(entityName); + } + } + public override string GetTranslationColumnName(string columnName) + { + Check.ArgumentNullException(columnName, string.Format(null, ErrorMessage.ObjNotExistCompositeFormat, "Column Name")); + if (columnName.Substring(0, 1) == this.SqlParameterKeyWord) + { + return columnName; + } + if (IsTranslationText(columnName)) return columnName; + if (columnName.Contains(UtilConstants.Dot)) + { + return string.Join(UtilConstants.Dot, columnName.Split(UtilConstants.DotChar).Select(it => GetTranslationText(it))); + } + else + { + return GetTranslationText(columnName); + } + } + public override string GetDbColumnName(string entityName, string propertyName) + { + if (this.MappingColumns.HasValue()) + { + var mappingInfo = this.MappingColumns.SingleOrDefault(it => it.EntityName == entityName && it.PropertyName == propertyName); + return (mappingInfo == null ? propertyName : mappingInfo.DbColumnName).ToUpper(isAutoToUpper); + } + else + { + return propertyName.ToUpper(isAutoToUpper); + } + } + } + public class OscarLMethod : DefaultDbMethod, IDbMethods + { + public override string CharIndex(MethodCallExpressionModel model) + { + return string.Format(" (strpos ({1},{0})-1)", model.Args[0].MemberName, model.Args[1].MemberName); + } + public override string IIF(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + var parameter3 = model.Args[2]; + if (parameter.Type == UtilConstants.BoolType) + { + parameter.MemberName = parameter.MemberName.ToString().Replace("=1", "=true"); + parameter2.MemberName = false; + parameter3.MemberName = true; + } + return string.Format("( CASE WHEN {0} THEN {1} ELSE {2} END )", parameter.MemberName, parameter2.MemberName, parameter3.MemberName); + } + public override string DateValue(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + if (parameter.MemberName != null && parameter.MemberName is DateTime) + { + return string.Format(" datepart({0},'{1}') ", parameter2.MemberValue, parameter.MemberName); + } + else + { + return string.Format(" datepart({0},{1}) ", parameter2.MemberValue, parameter.MemberName); + } + } + + public override string Contains(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + return string.Format(" ({0} like concat('%',{1},'%')) ", parameter.MemberName, parameter2.MemberName); + } + + public override string StartsWith(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + return string.Format(" ({0} like concat({1},'%')) ", parameter.MemberName, parameter2.MemberName); + } + + public override string EndsWith(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + return string.Format(" ({0} like concat('%',{1}))", parameter.MemberName, parameter2.MemberName); + } + + public override string DateIsSameDay(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + return string.Format(" (date_part('day',{0}-{1})=0) ", parameter.MemberName, parameter2.MemberName); ; + } + + public override string DateIsSameByType(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + var parameter3 = model.Args[2]; + return string.Format(" (date_part('{2}',{0}-{1})=0) ", parameter.MemberName, parameter2.MemberName, parameter3.MemberValue); + } + + public override string ToDate(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS timestamp)", parameter.MemberName); + } + + public override string ToInt32(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS INT4)", parameter.MemberName); + } + + public override string ToInt64(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS INT8)", parameter.MemberName); + } + + public override string ToString(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS VARCHAR(1024))", parameter.MemberName); + } + + public override string ToGuid(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS VARCHAR)", parameter.MemberName); + } + + public override string ToDouble(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS DECIMAL(18,4))", parameter.MemberName); + } + + public override string ToBool(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS boolean)", parameter.MemberName); + } + + public override string ToDecimal(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS DECIMAL(18,4))", parameter.MemberName); + } + + public override string Length(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" LENGTH({0})", parameter.MemberName); + } + public override string MergeString(params string[] strings) + { + return " concat(" + string.Join(",", strings).Replace("+", "") + ") "; + } + public override string IsNull(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter1 = model.Args[1]; + return string.Format("(CASE WHEN {0} IS NULL THEN {1} ELSE {0} END)", parameter.MemberName, parameter1.MemberName); + } + public override string GetDate() + { + return "SYSDATE()"; + } + public override string GetRandom() + { + return "RANDOM()"; + } + + public override string EqualTrue(string fieldName) + { + return "( " + fieldName + "=true )"; + } + public override string DateDiff(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + var parameter3 = model.Args[2]; + return string.Format(" DATEDIFF('{0}',{1},{2}) ", parameter.MemberValue?.ToString().ToSqlFilter(), parameter2.MemberName, parameter3.MemberName); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oscar/SqlBuilder/OscarInsertBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oscar/SqlBuilder/OscarInsertBuilder.cs new file mode 100644 index 000000000..e387d39e3 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oscar/SqlBuilder/OscarInsertBuilder.cs @@ -0,0 +1,95 @@ +using System.Text; + +namespace SqlSugar +{ + public class OscarInsertBuilder : InsertBuilder + { + public override string SqlTemplate + { + get + { + if (IsReturnIdentity) + { + return @"INSERT INTO {0} + ({1}) + VALUES + ({2}) returning $PrimaryKey"; + } + else + { + return @"INSERT INTO {0} + ({1}) + VALUES + ({2}) ;"; + + } + } + } + public override string SqlTemplateBatch => "INSERT INTO {0} ({1})"; + public override string SqlTemplateBatchUnion => " VALUES "; + + public override string SqlTemplateBatchSelect => " {0} "; + + public override string ToSqlString() + { + if (IsNoInsertNull) + { + DbColumnInfoList = DbColumnInfoList.Where(it => it.Value != null).ToList(); + } + var groupList = DbColumnInfoList.GroupBy(it => it.TableId).ToList(); + var isSingle = groupList.Count == 1; + string columnsString = string.Join(",", groupList.First().Select(it => base.GetDbColumn(it, Builder.GetTranslationColumnName(it.DbColumnName)))); + if (isSingle) + { + string columnParametersString = string.Join(",", this.DbColumnInfoList.Select(it => Builder.SqlParameterKeyWord + it.DbColumnName)); + return string.Format(SqlTemplate, GetTableNameString, columnsString, columnParametersString); + } + else + { + StringBuilder batchInsetrSql = new StringBuilder(); + int pageSize = 200; + int pageIndex = 1; + int totalRecord = groupList.Count; + int pageCount = (totalRecord + pageSize - 1) / pageSize; + while (pageCount >= pageIndex) + { + batchInsetrSql.AppendFormat(SqlTemplateBatch, GetTableNameString, columnsString); + int i = 0; + foreach (var columns in groupList.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList()) + { + var isFirst = i == 0; + if (isFirst) + { + batchInsetrSql.Append(SqlTemplateBatchUnion); + } + batchInsetrSql.Append("\r\n ( " + string.Join(",", columns.Select(it => + { + if (it.InsertServerTime || it.InsertSql.HasValue()) + { + return GetDbColumn(it, null); + } + object value = null; + if (it.Value is DateTime) + { + value = ((DateTime)it.Value).ToString("O"); + } + else + { + value = it.Value; + } + if (value == null || value == DBNull.Value) + { + return string.Format(SqlTemplateBatchSelect, "NULL"); + } + return string.Format(SqlTemplateBatchSelect, "'" + value.ObjToString().ToSqlFilter() + "'"); + })) + "),"); + ++i; + } + pageIndex++; + batchInsetrSql.Remove(batchInsetrSql.Length - 1, 1).Append("\r\n;\r\n"); + } + return batchInsetrSql.ToString(); + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oscar/SqlBuilder/OscarQueryBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oscar/SqlBuilder/OscarQueryBuilder.cs new file mode 100644 index 000000000..9efdfd344 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oscar/SqlBuilder/OscarQueryBuilder.cs @@ -0,0 +1,93 @@ +using System.Text; +using System.Text.RegularExpressions; + +namespace SqlSugar +{ + public partial class OscarQueryBuilder : QueryBuilder + { + #region Sql Template + public override string PageTempalte + { + get + { + /* + SELECT * FROM TABLE WHERE CONDITION ORDER BY ID DESC LIMIT 10 offset 0 + */ + var template = "SELECT {0} FROM {1} {2} {3} {4} LIMIT {6} offset {5}"; + return template; + } + } + public override string DefaultOrderByTemplate + { + get + { + return "ORDER BY NOW() "; + } + } + + #endregion + + #region Common Methods + public override bool IsComplexModel(string sql) + { + return Regex.IsMatch(sql, @"AS ""\w+\.\w+""") || Regex.IsMatch(sql, @"AS ""\w+\.\w+\.\w+"""); + } + public override string ToSqlString() + { + base.AppendFilter(); + string oldOrderValue = this.OrderByValue; + string result = null; + sql = new StringBuilder(); + sql.AppendFormat(SqlTemplate, GetSelectValue, GetTableNameString, GetWhereValueString, GetGroupByString + HavingInfos, (Skip != null || Take != null) ? null : GetOrderByString); + if (IsCount) { return sql.ToString(); } + if (Skip != null && Take == null) + { + if (this.OrderByValue == "ORDER BY ") this.OrderByValue += GetSelectValue.Split(',')[0]; + result = string.Format(PageTempalte, GetSelectValue, GetTableNameString, GetWhereValueString, GetGroupByString + HavingInfos, (Skip != null || Take != null) ? null : GetOrderByString, Skip.ObjToInt(), long.MaxValue); + } + else if (Skip == null && Take != null) + { + if (this.OrderByValue == "ORDER BY ") this.OrderByValue += GetSelectValue.Split(',')[0]; + result = string.Format(PageTempalte, GetSelectValue, GetTableNameString, GetWhereValueString, GetGroupByString + HavingInfos, GetOrderByString, 0, Take.ObjToInt()); + } + else if (Skip != null && Take != null) + { + if (this.OrderByValue == "ORDER BY ") this.OrderByValue += GetSelectValue.Split(',')[0]; + result = string.Format(PageTempalte, GetSelectValue, GetTableNameString, GetWhereValueString, GetGroupByString + HavingInfos, GetOrderByString, Skip.ObjToInt() > 0 ? Skip.ObjToInt() : 0, Take); + } + else + { + result = sql.ToString(); + } + this.OrderByValue = oldOrderValue; + result = GetSqlQuerySql(result); + return result; + } + + #endregion + + #region Get SQL Partial + public override string GetSelectValue + { + get + { + string result = string.Empty; + if (this.SelectValue == null || this.SelectValue is string) + { + result = GetSelectValueByString(); + } + else + { + result = GetSelectValueByExpression(); + } + if (this.SelectType == ResolveExpressType.SelectMultiple) + { + this.SelectCacheKey = this.SelectCacheKey + string.Join("-", this.JoinQueryInfos.Select(it => it.TableName)); + } + return result; + } + } + + #endregion + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oscar/SqlBuilder/OscarUpdateBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oscar/SqlBuilder/OscarUpdateBuilder.cs new file mode 100644 index 000000000..a612f2153 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Oscar/SqlBuilder/OscarUpdateBuilder.cs @@ -0,0 +1,104 @@ +using System.Text; + +namespace SqlSugar +{ + public class OscarUpdateBuilder : UpdateBuilder + { + public override string SqlTemplateBatch + { + get + { + return @"UPDATE {1} {2} SET {0} FROM ${{0}} "; + } + } + public override string SqlTemplateJoin + { + get + { + return @" (VALUES + {0} + + ) AS T ({2}) WHERE {1} + "; + } + } + + public override string SqlTemplateBatchUnion + { + get + { + return ","; + } + } + + public override object FormatValue(object value) + { + if (value == null) + { + return "NULL"; + } + else + { + var type = value.GetType(); + if (type == UtilConstants.DateType) + { + var date = value.ObjToDate(); + if (date < Convert.ToDateTime("1900-1-1")) + { + date = Convert.ToDateTime("1900-1-1"); + } + return "'" + date.ToString("yyyy-MM-dd HH:mm:ss.fff") + "'"; + } + else if (type == UtilConstants.ByteArrayType) + { + string bytesString = "0x" + BitConverter.ToString((byte[])value); + return bytesString; + } + else if (type.IsEnum()) + { + return Convert.ToInt64(value); + } + else if (type == UtilConstants.BoolType) + { + return value.ObjToBool() ? "1" : "0"; + } + else if (type == UtilConstants.StringType || type == UtilConstants.ObjType) + { + return "'" + value.ToString().ToSqlFilter() + "'"; + } + else + { + return "'" + value.ToString() + "'"; + } + } + } + + protected override string TomultipleSqlString(List> groupList) + { + StringBuilder sb = new StringBuilder(); + int i = 0; + sb.AppendLine(string.Join("\r\n", groupList.Select(t => + { + var updateTable = string.Format("UPDATE {0} SET", base.GetTableNameStringNoWith); + var setValues = string.Join(",", t.Where(s => !s.IsPrimarykey).Select(m => GetOracleUpdateColums(i, m)).ToArray()); + var pkList = t.Where(s => s.IsPrimarykey).ToList(); + List whereList = new List(); + foreach (var item in pkList) + { + var isFirst = pkList.First() == item; + var whereString = ""; + whereString += GetOracleUpdateColums(i, item); + whereList.Add(whereString); + } + i++; + return string.Format("{0} {1} WHERE {2};", updateTable, setValues, string.Join("AND", whereList)); + }).ToArray())); + return sb.ToString(); + } + private string GetOracleUpdateColums(int i, DbColumnInfo m) + { + return string.Format("\"{0}\"={1}", m.DbColumnName.ToUpper(), base.GetDbColumn(m, FormatValue(m.Value))); + } + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/PostgreSQL/CodeFirst/PostgreSQLCodeFirst.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/PostgreSQL/CodeFirst/PostgreSQLCodeFirst.cs new file mode 100644 index 000000000..7594b889f --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/PostgreSQL/CodeFirst/PostgreSQLCodeFirst.cs @@ -0,0 +1,85 @@ +namespace SqlSugar +{ + public class PostgreSQLCodeFirst : CodeFirstProvider + { + protected override void ExistLogicEnd(List dbColumns) + { + foreach (EntityColumnInfo column in dbColumns) + { + if (column.DefaultValue != null) + { + this.Context.DbMaintenance.AddDefaultValue(column.DbTableName, column.DbColumnName, column.DefaultValue.ToSqlValue()); + } + } + } + public override void NoExistLogic(EntityInfo entityInfo) + { + var tableName = GetTableName(entityInfo); + //Check.Exception(entityInfo.Columns.Where(it => it.IsPrimarykey).Count() > 1, "Use Code First ,The primary key must not exceed 1"); + List columns = new List(); + if (entityInfo.Columns.HasValue()) + { + foreach (var item in entityInfo.Columns.Where(it => it.IsIgnore == false)) + { + DbColumnInfo dbColumnInfo = this.EntityColumnToDbColumn(entityInfo, tableName, item); + columns.Add(dbColumnInfo); + } + if (entityInfo.IsCreateTableFiledSort) + { + columns = columns.OrderBy(c => c.CreateTableFieldSort).ToList(); + } + } + columns = columns.OrderBy(it => it.IsPrimarykey ? 0 : 1).ToList(); + this.Context.DbMaintenance.CreateTable(tableName, columns, true); + } + protected override DbColumnInfo EntityColumnToDbColumn(EntityInfo entityInfo, string tableName, EntityColumnInfo item) + { + var propertyType = UtilMethods.GetUnderType(item.PropertyInfo); + var result = new DbColumnInfo() + { + TableId = entityInfo.Columns.IndexOf(item), + DbColumnName = item.DbColumnName.HasValue() ? item.DbColumnName : item.PropertyName, + IsPrimarykey = item.IsPrimarykey, + IsIdentity = item.IsIdentity, + TableName = tableName, + IsNullable = item.IsNullable, + DefaultValue = item.DefaultValue, + ColumnDescription = item.ColumnDescription, + Length = item.Length, + CreateTableFieldSort = item.CreateTableFieldSort + }; + if (propertyType == UtilConstants.DecType) + { + result.Scale = item.DecimalDigits; + result.DecimalDigits = item.DecimalDigits; + } + GetDbType(item, propertyType, result); + if (result.DataType.Equals("varchar", StringComparison.CurrentCultureIgnoreCase) && result.Length == 0) + { + result.Length = 1; + } + return result; + } + + protected override void ConvertColumns(List dbColumns) + { + foreach (var item in dbColumns) + { + if (item.DataType == "DateTime") + { + item.Length = 0; + } + } + } + + protected override void ChangeKey(EntityInfo entityInfo, string tableName, EntityColumnInfo item) + { + this.Context.DbMaintenance.UpdateColumn(tableName, EntityColumnToDbColumn(entityInfo, tableName, item)); + if (!item.IsPrimarykey) + this.Context.DbMaintenance.DropConstraint(tableName, null); + if (item.IsPrimarykey) + this.Context.DbMaintenance.AddPrimaryKey(tableName, item.DbColumnName); + } + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/PostgreSQL/DbBind/PostgreSQLDbBind.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/PostgreSQL/DbBind/PostgreSQLDbBind.cs new file mode 100644 index 000000000..433ca562a --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/PostgreSQL/DbBind/PostgreSQLDbBind.cs @@ -0,0 +1,189 @@ +namespace SqlSugar +{ + public class PostgreSQLDbBind : DbBindProvider + { + public override string GetDbTypeName(string csharpTypeName) + { + csharpTypeName = GetValidCsharpTypeName(csharpTypeName); + if (csharpTypeName == UtilConstants.ByteArrayType.Name) + return "bytea"; + if (csharpTypeName.Equals("int32", StringComparison.CurrentCultureIgnoreCase)) + csharpTypeName = "int"; + if (csharpTypeName.Equals("int16", StringComparison.CurrentCultureIgnoreCase)) + csharpTypeName = "short"; + if (csharpTypeName.Equals("int64", StringComparison.CurrentCultureIgnoreCase)) + csharpTypeName = "long"; + if (csharpTypeName.ToLower().IsIn("boolean", "bool")) + csharpTypeName = "bool"; + if (csharpTypeName == "DateTimeOffset") + csharpTypeName = "DateTime"; + var mappings = this.MappingTypes.Where(it => it.Value.ToString().Equals(csharpTypeName, StringComparison.CurrentCultureIgnoreCase)).ToList(); + if (mappings?.Count > 0) + return mappings.First().Key; + else + return "varchar"; + } + + private string GetValidCsharpTypeName(string csharpTypeName) + { + if (csharpTypeName?.StartsWith("ora") == true && this.Context.CurrentConnectionConfig?.MoreSettings?.DatabaseModel == DbType.Vastbase) + { + csharpTypeName = csharpTypeName.Replace("ora", ""); + } + else if (csharpTypeName?.StartsWith("mssql_") == true && this.Context.CurrentConnectionConfig?.MoreSettings?.DatabaseModel == DbType.Vastbase) + { + csharpTypeName = csharpTypeName.Replace("mssql_", ""); + } + else if (csharpTypeName?.StartsWith("sys.") == true) + { + csharpTypeName = csharpTypeName.Replace("sys.", ""); + } + return csharpTypeName; + } + + public override string GetPropertyTypeName(string dbTypeName) + { + dbTypeName = dbTypeName.ToLower(); + dbTypeName = GetValidCsharpTypeName(dbTypeName); + var propertyTypes = MappingTypes.Where(it => it.Value.ToString().Equals(dbTypeName, StringComparison.CurrentCultureIgnoreCase) || it.Key.Equals(dbTypeName, StringComparison.CurrentCultureIgnoreCase)); + if (propertyTypes == null) + { + return "other"; + } + else if (dbTypeName == "xml" || dbTypeName == "string" || dbTypeName == "jsonb" || dbTypeName == "json") + { + return "string"; + } + else if (dbTypeName == "bpchar")//数据库char datatype 查询出来的时候是 bpchar + { + return "char"; + } + if (dbTypeName == "byte[]") + { + return "byte[]"; + } + else if (propertyTypes?.Any() != true) + { + if (dbTypeName.StartsWith('_')) + { + var dbTypeName2 = dbTypeName.TrimStart('_'); + return MappingTypes.Where(it => it.Value.ToString().Equals(dbTypeName2, StringComparison.CurrentCultureIgnoreCase) || it.Key.Equals(dbTypeName2, StringComparison.CurrentCultureIgnoreCase)).Select(it => it.Value + "[]").First(); + } + else if (dbTypeName.EndsWith("geometry") || dbTypeName.EndsWith("geography")) + { + return CSharpDataType.@string.ToString(); + } + Check.ThrowNotSupportedException(string.Format(" \"{0}\" Type NotSupported, DbBindProvider.GetPropertyTypeName error.", dbTypeName)); + return null; + } + else if (propertyTypes.First().Value == CSharpDataType.byteArray) + { + return "byte[]"; + } + else + { + return propertyTypes.First().Value.ToString(); + } + } + public override List> MappingTypes + { + get + { + var extService = this.Context.CurrentConnectionConfig.ConfigureExternalServices; + if (extService?.AppendDataReaderTypeMappings.HasValue() == true) + { + return extService.AppendDataReaderTypeMappings.Union(MappingTypesConst).ToList(); + } + else + { + return MappingTypesConst; + } + } + } + public static List> MappingTypesConst = new List>(){ + + new KeyValuePair("int2",CSharpDataType.@short), + //new KeyValuePair("int1",CSharpDataType.@byte), + new KeyValuePair("smallint",CSharpDataType.@short), + new KeyValuePair("smallint",CSharpDataType.@byte), + new KeyValuePair("int4",CSharpDataType.@int), + new KeyValuePair("serial",CSharpDataType.@int), + new KeyValuePair("integer",CSharpDataType.@int), + new KeyValuePair("int8",CSharpDataType.@long), + new KeyValuePair("long",CSharpDataType.@long), + new KeyValuePair("bigint",CSharpDataType.@long), + new KeyValuePair("float4",CSharpDataType.@float), + new KeyValuePair("float4",CSharpDataType.Single), + new KeyValuePair("real",CSharpDataType.@float), + new KeyValuePair("float8",CSharpDataType.@double), + new KeyValuePair("double precision",CSharpDataType.@int), + new KeyValuePair("numeric",CSharpDataType.@decimal), + new KeyValuePair("decimal",CSharpDataType.@decimal), + new KeyValuePair("path",CSharpDataType.@decimal), + new KeyValuePair("point",CSharpDataType.@decimal), + new KeyValuePair("polygon",CSharpDataType.@decimal), + + new KeyValuePair("boolean",CSharpDataType.@bool), + new KeyValuePair("bool",CSharpDataType.@bool), + new KeyValuePair("box",CSharpDataType.@bool), + new KeyValuePair("bytea",CSharpDataType.byteArray), + + new KeyValuePair("varchar",CSharpDataType.@string), + new KeyValuePair("clob",CSharpDataType.@string), + new KeyValuePair("nvarchar2",CSharpDataType.@string), + new KeyValuePair("varchar2",CSharpDataType.@string), + new KeyValuePair("character varying",CSharpDataType.@string), + new KeyValuePair("geometry",CSharpDataType.@string), + new KeyValuePair("name",CSharpDataType.@string), + new KeyValuePair("text",CSharpDataType.@string), + new KeyValuePair("char",CSharpDataType.@string), + new KeyValuePair("character",CSharpDataType.@string), + new KeyValuePair("cidr",CSharpDataType.@string), + new KeyValuePair("circle",CSharpDataType.@string), + new KeyValuePair("tsquery",CSharpDataType.@string), + new KeyValuePair("tsvector",CSharpDataType.@string), + new KeyValuePair("txid_snapshot",CSharpDataType.@string), + new KeyValuePair("uuid",CSharpDataType.Guid), + new KeyValuePair("xml",CSharpDataType.@string), + new KeyValuePair("json",CSharpDataType.@string), + + new KeyValuePair("interval",CSharpDataType.@decimal), + new KeyValuePair("lseg",CSharpDataType.@decimal), + new KeyValuePair("macaddr",CSharpDataType.@decimal), + new KeyValuePair("money",CSharpDataType.@decimal), + new KeyValuePair("timestamp",CSharpDataType.DateTime), + new KeyValuePair("timestamp with time zone",CSharpDataType.DateTime), + new KeyValuePair("timestamptz",CSharpDataType.DateTime), + new KeyValuePair("timestamp without time zone",CSharpDataType.DateTime), + new KeyValuePair("date",CSharpDataType.DateTime), + new KeyValuePair("time",CSharpDataType.DateTime), + new KeyValuePair("time with time zone",CSharpDataType.DateTime), + new KeyValuePair("timetz",CSharpDataType.DateTime), + new KeyValuePair("time without time zone",CSharpDataType.DateTime), + + new KeyValuePair("bit",CSharpDataType.byteArray), + new KeyValuePair("bit varying",CSharpDataType.byteArray), + new KeyValuePair("varbit",CSharpDataType.@byte), + new KeyValuePair("time",CSharpDataType.TimeSpan), + new KeyValuePair("public.geometry",CSharpDataType.@object), + new KeyValuePair("public.geography",CSharpDataType.@object), + new KeyValuePair("inet",CSharpDataType.@object), + + new KeyValuePair("number",CSharpDataType.@int), + new KeyValuePair("number",CSharpDataType.@float), + new KeyValuePair("number",CSharpDataType.@short), + new KeyValuePair("number",CSharpDataType.@byte), + new KeyValuePair("number",CSharpDataType.@double), + new KeyValuePair("number",CSharpDataType.@long), + new KeyValuePair("number",CSharpDataType.@bool), + new KeyValuePair("number",CSharpDataType.@decimal), + }; + public override List StringThrow + { + get + { + return new List() { "int32", "datetime", "decimal", "double", "byte" }; + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/PostgreSQL/DbFirst/PostgreSQLDbFirst.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/PostgreSQL/DbFirst/PostgreSQLDbFirst.cs new file mode 100644 index 000000000..f9bf212ca --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/PostgreSQL/DbFirst/PostgreSQLDbFirst.cs @@ -0,0 +1,6 @@ +namespace SqlSugar +{ + public class PostgreSQLDbFirst : DbFirstProvider + { + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/PostgreSQL/DbMaintenance/PostgreSQLDbMaintenance.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/PostgreSQL/DbMaintenance/PostgreSQLDbMaintenance.cs new file mode 100644 index 000000000..0a1e47221 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/PostgreSQL/DbMaintenance/PostgreSQLDbMaintenance.cs @@ -0,0 +1,656 @@ +using System.Text.RegularExpressions; + +namespace SqlSugar +{ + public class PostgreSQLDbMaintenance : DbMaintenanceProvider + { + #region DML + protected override string GetDataBaseSql + { + get + { + return "SELECT datname FROM pg_database"; + } + } + protected override string GetColumnInfosByTableNameSql + { + get + { + string schema = GetSchema(); + string sql = @"select cast (pclass.oid as int4) as TableId,cast(ptables.tablename as varchar) as TableName, + pcolumn.column_name as DbColumnName,pcolumn.udt_name as DataType, + CASE WHEN pcolumn.numeric_scale >0 THEN pcolumn.numeric_precision ELSE pcolumn.character_maximum_length END as Length, + pcolumn.column_default as DefaultValue, + pcolumn.numeric_scale as DecimalDigits, + pcolumn.numeric_scale as Scale, + col_description(pclass.oid, pcolumn.ordinal_position) as ColumnDescription, + case when pkey.colname = pcolumn.column_name + then true else false end as IsPrimaryKey, + CASE + WHEN (current_setting('server_version_num')::INT >= 100000 AND pcolumn.is_identity = 'YES') THEN true + WHEN pcolumn.column_default LIKE 'nextval%' THEN true + ELSE false + END AS IsIdentity, + case when pcolumn.is_nullable = 'YES' + then true else false end as IsNullable + from (select * from pg_tables where upper(tablename) = upper('{0}') and schemaname='" + schema + @"') ptables inner join pg_class pclass + on ptables.tablename = pclass.relname inner join (SELECT * + FROM information_schema.columns where table_schema='" + schema + @"' + ) pcolumn on pcolumn.table_name = ptables.tablename and upper(pcolumn.table_name) = upper('{0}') + left join ( + select pg_class.relname,pg_attribute.attname as colname from + pg_constraint inner join pg_class + on pg_constraint.conrelid = pg_class.oid + inner join pg_attribute on pg_attribute.attrelid = pg_class.oid + and pg_attribute.attnum = pg_constraint.conkey[1] + inner join pg_type on pg_type.oid = pg_attribute.atttypid + where pg_constraint.contype='p' + ) pkey on pcolumn.table_name = pkey.relname + order by table_catalog, table_schema, ordinal_position"; + return sql; + } + } + + protected override string GetTableInfoListSql + { + get + { + var schema = GetSchema(); + return @"select cast(relname as varchar) as Name, + cast(obj_description(c.oid,'pg_class') as varchar) as Description from pg_class c + inner join + pg_namespace n on n.oid = c.relnamespace and nspname='" + schema + @"' + inner join + pg_tables z on z.tablename=c.relname + where relkind in('p', 'r') and relname not like 'pg\_%' and relname not like 'sql\_%' and schemaname='" + schema + "' order by relname"; + } + } + protected override string GetViewInfoListSql + { + get + { + return @"select table_name as name from information_schema.views where table_schema ='" + GetSchema() + "' "; + } + } + #endregion + + #region DDL + protected override string CreateDataBaseSql + { + get + { + return "CREATE DATABASE {0}"; + } + } + protected override string AddPrimaryKeySql + { + get + { + return "ALTER TABLE {0} ADD PRIMARY KEY({2}) /*{1}*/"; + } + } + protected override string AddColumnToTableSql + { + get + { + return "ALTER TABLE {0} ADD COLUMN {1} {2}{3} {4} {5} {6}"; + } + } + protected override string AlterColumnToTableSql + { + get + { + return "alter table {0} ALTER COLUMN {1} {2}{3} {4} {5} {6}"; + } + } + protected override string BackupDataBaseSql + { + get + { + return "mysqldump.exe {0} -uroot -p > {1} "; + } + } + protected override string CreateTableSql + { + get + { + return "CREATE TABLE {0}(\r\n{1} $PrimaryKey)"; + } + } + protected override string CreateTableColumn + { + get + { + return "{0} {1}{2} {3} {4} {5}"; + } + } + protected override string TruncateTableSql + { + get + { + return "TRUNCATE TABLE {0}"; + } + } + protected override string BackupTableSql + { + get + { + return "create table {0} as (select * from {1} limit {2} offset 0)"; + } + } + protected override string DropTableSql + { + get + { + return "DROP TABLE {0}"; + } + } + protected override string DropColumnToTableSql + { + get + { + return "ALTER TABLE {0} DROP COLUMN {1}"; + } + } + protected override string DropConstraintSql + { + get + { + return "ALTER TABLE {0} DROP CONSTRAINT {1}"; + } + } + protected override string RenameColumnSql + { + get + { + return "ALTER TABLE {0} RENAME {1} TO {2}"; + } + } + protected override string AddColumnRemarkSql => "comment on column {1}.{0} is '{2}'"; + + protected override string DeleteColumnRemarkSql => "comment on column {1}.{0} is ''"; + + protected override string IsAnyColumnRemarkSql { get { throw new NotSupportedException(); } } + + protected override string AddTableRemarkSql => "comment on table {0} is '{1}'"; + + protected override string DeleteTableRemarkSql => "comment on table {0} is ''"; + + protected override string IsAnyTableRemarkSql { get { throw new NotSupportedException(); } } + + protected override string RenameTableSql => "alter table {0} rename to {1}"; + + protected override string CreateIndexSql + { + get + { + return "CREATE {3} INDEX Index_{0}_{2} ON {0} ({1})"; + } + } + protected override string AddDefaultValueSql + { + get + { + return "ALTER TABLE {0} ALTER COLUMN {1} SET DEFAULT {2}"; + } + } + protected override string IsAnyIndexSql + { + get + { + return " SELECT count(1) WHERE upper('{0}') IN ( SELECT upper(indexname) FROM pg_indexes )"; + } + } + protected override string IsAnyProcedureSql => throw new NotImplementedException(); + #endregion + + #region Check + protected override string CheckSystemTablePermissionsSql + { + get + { + return "select 1 from information_schema.columns limit 1 offset 0"; + } + } + #endregion + + #region Scattered + protected override string CreateTableNull + { + get + { + return "DEFAULT NULL"; + } + } + protected override string CreateTableNotNull + { + get + { + return "NOT NULL"; + } + } + protected override string CreateTablePirmaryKey + { + get + { + return "PRIMARY KEY"; + } + } + protected override string CreateTableIdentity + { + get + { + return "serial"; + } + } + #endregion + + #region Methods + public override bool IsAnyTable(string tableName, bool isCache = true) + { + if (isCache == false) + { + var sql = $" SELECT 1 FROM pg_catalog.pg_tables \r\n WHERE schemaname = '" + GetSchema() + "' \r\n AND Lower(tablename) = '" + tableName.ToLower() + "' "; + return this.Context.Ado.GetInt(sql) > 0; + } + else + { + return base.IsAnyTable(tableName, isCache); + } + } + public override List GetDbTypes() + { + var result = this.Context.Ado.SqlQuery(@"SELECT DISTINCT data_type +FROM information_schema.columns"); + result.Add("varchar"); + result.Add("timestamp"); + result.Add("uuid"); + result.Add("int2"); + result.Add("int4"); + result.Add("int8"); + result.Add("time"); + result.Add("date"); + result.Add("float8"); + result.Add("float4"); + result.Add("json"); + result.Add("jsonp"); + return result.Distinct().ToList(); + } + public override List GetTriggerNames(string tableName) + { + return this.Context.Ado.SqlQuery(@"SELECT tgname +FROM pg_trigger +WHERE tgrelid = '" + tableName + "'::regclass"); + } + public override List GetFuncList() + { + return this.Context.Ado.SqlQuery(" SELECT routine_name\r\nFROM information_schema.routines\r\nWHERE lower( routine_schema ) = '" + GetSchema().ToLower() + "' AND routine_type = 'FUNCTION' "); + } + public override List GetIndexList(string tableName) + { + var sql = $"SELECT indexname, indexdef FROM pg_indexes WHERE upper(tablename) = upper('{tableName}') AND upper(schemaname) = upper('{GetSchema()}')"; + return this.Context.Ado.SqlQuery(sql); + } + public override List GetProcList(string dbName) + { + var sql = $"SELECT proname FROM pg_proc p JOIN pg_namespace n ON p.pronamespace = n.oid WHERE n.nspname = '{dbName}'"; + return this.Context.Ado.SqlQuery(sql); + } + public override bool AddDefaultValue(string tableName, string columnName, string defaultValue) + { + if (defaultValue?.StartsWith('\'') == true && defaultValue?.EndsWith('\'') == true && defaultValue?.Contains('(') == false + && !defaultValue.EqualCase("'current_timestamp'") && !defaultValue.EqualCase("'current_date'")) + { + string sql = string.Format(AddDefaultValueSql, this.SqlBuilder.GetTranslationColumnName(tableName), this.SqlBuilder.GetTranslationColumnName(columnName), defaultValue); + return this.Context.Ado.ExecuteCommand(sql) > 0; + } + else if (defaultValue.EqualCase("current_timestamp") || defaultValue.EqualCase("current_date")) + { + string sql = string.Format(AddDefaultValueSql, this.SqlBuilder.GetTranslationColumnName(tableName), this.SqlBuilder.GetTranslationColumnName(columnName), defaultValue); + return this.Context.Ado.ExecuteCommand(sql) > 0; + } + else if (defaultValue?.Contains('(') == false + && !defaultValue.EqualCase("'current_timestamp'") && !defaultValue.EqualCase("'current_date'")) + { + string sql = string.Format(AddDefaultValueSql, this.SqlBuilder.GetTranslationColumnName(tableName), this.SqlBuilder.GetTranslationColumnName(columnName), "'" + defaultValue + "'"); + return this.Context.Ado.ExecuteCommand(sql) > 0; + } + else if (defaultValue?.ToLower()?.Contains("cast(") == true && defaultValue?.StartsWith('\'') == true && defaultValue?.EndsWith('\'') == true) + { + string sql = string.Format(AddDefaultValueSql, this.SqlBuilder.GetTranslationColumnName(tableName), this.SqlBuilder.GetTranslationColumnName(columnName), defaultValue.Replace("''", "'").TrimEnd('\'').TrimStart('\'')); + return this.Context.Ado.ExecuteCommand(sql) > 0; + } + else if (defaultValue?.ToLower()?.Contains("now()") == true) + { + string sql = string.Format(AddDefaultValueSql, this.SqlBuilder.GetTranslationColumnName(tableName), this.SqlBuilder.GetTranslationColumnName(columnName), defaultValue.TrimEnd('\'').TrimStart('\'')); + return this.Context.Ado.ExecuteCommand(sql) > 0; + } + else + { + return base.AddDefaultValue(this.SqlBuilder.GetTranslationTableName(tableName), this.SqlBuilder.GetTranslationTableName(columnName), defaultValue); + } + } + + public override bool RenameTable(string oldTableName, string newTableName) + { + return base.RenameTable(this.SqlBuilder.GetTranslationTableName(oldTableName), this.SqlBuilder.GetTranslationTableName(newTableName)); + } + + public override bool AddColumnRemark(string columnName, string tableName, string description) + { + tableName = this.SqlBuilder.GetTranslationTableName(tableName); + string sql = string.Format(this.AddColumnRemarkSql, this.SqlBuilder.GetTranslationColumnName(columnName.ToLower(isAutoToLowerCodeFirst)), tableName, description); + this.Context.Ado.ExecuteCommand(sql); + return true; + } + public override bool AddTableRemark(string tableName, string description) + { + tableName = this.SqlBuilder.GetTranslationTableName(tableName); + return base.AddTableRemark(tableName, description); + } + public override bool UpdateColumn(string tableName, DbColumnInfo columnInfo) + { + ConvertCreateColumnInfo(columnInfo); + tableName = this.SqlBuilder.GetTranslationTableName(tableName); + var columnName = this.SqlBuilder.GetTranslationColumnName(columnInfo.DbColumnName); + string sql = GetUpdateColumnSql(tableName, columnInfo); + this.Context.Ado.ExecuteCommand(sql); + var isnull = columnInfo.IsNullable ? " DROP NOT NULL " : " SET NOT NULL "; + this.Context.Ado.ExecuteCommand(string.Format("alter table {0} alter {1} {2}", tableName, columnName, isnull)); + return true; + } + + protected override string GetUpdateColumnSql(string tableName, DbColumnInfo columnInfo) + { + string columnName = this.SqlBuilder.GetTranslationColumnName(columnInfo.DbColumnName); + tableName = this.SqlBuilder.GetTranslationTableName(tableName); + string dataSize = GetSize(columnInfo); + string dataType = columnInfo.DataType; + if (!string.IsNullOrEmpty(dataType)) + { + dataType = " type " + dataType; + } + string nullType = ""; + string primaryKey = null; + string identity = null; + string result = string.Format(this.AlterColumnToTableSql, tableName, columnName, dataType, dataSize, nullType, primaryKey, identity); + return result; + } + + /// + ///by current connection string + /// + /// + /// + /// + public override bool CreateDatabase(string databaseName, string databaseDirectory = null) + { + if (databaseDirectory != null) + { + if (!FileHelper.IsExistDirectory(databaseDirectory)) + { + FileHelper.CreateDirectory(databaseDirectory); + } + } + var oldDatabaseName = this.Context.Ado.Connection.Database; + var connection = this.Context.CurrentConnectionConfig.ConnectionString; + if (Regex.Matches(connection, oldDatabaseName).Count > 1) + { + var builder = new Npgsql.NpgsqlConnectionStringBuilder(connection); + builder.Database = "postgres"; + connection = builder.ConnectionString; + } + else + { + connection = connection.Replace(oldDatabaseName, "postgres"); + } + var newDb = new SqlSugarClient(new ConnectionConfig() + { + DbType = this.Context.CurrentConnectionConfig.DbType, + IsAutoCloseConnection = true, + ConnectionString = connection + }); + if (!GetDataBaseList(newDb).Any(it => it.Equals(databaseName, StringComparison.CurrentCultureIgnoreCase))) + { + var isVast = this.Context?.CurrentConnectionConfig?.MoreSettings?.DatabaseModel == DbType.Vastbase; + var dbcompatibility = ""; + if (isVast) + { + dbcompatibility = " dbcompatibility = 'PG'"; + } + newDb.Ado.ExecuteCommand(string.Format(CreateDataBaseSql, this.SqlBuilder.SqlTranslationLeft + databaseName + this.SqlBuilder.SqlTranslationRight, databaseDirectory) + dbcompatibility); + } + return true; + } + public override bool AddRemark(EntityInfo entity) + { + var db = this.Context; + var columns = entity.Columns.Where(it => it.IsIgnore == false).ToList(); + + foreach (var item in columns) + { + if (item.ColumnDescription != null) + { + db.DbMaintenance.AddColumnRemark(item.DbColumnName, item.DbTableName, item.ColumnDescription); + + } + } + //table remak + if (entity.TableDescription != null) + { + db.DbMaintenance.AddTableRemark(entity.DbTableName, entity.TableDescription); + } + return true; + } + public override bool CreateTable(string tableName, List columns, bool isCreatePrimaryKey = true) + { + if (columns.HasValue()) + { + foreach (var item in columns) + { + ConvertCreateColumnInfo(item); + if (item.DbColumnName.Equals("GUID", StringComparison.CurrentCultureIgnoreCase) && item.Length == 0) + { + if (item.DataType?.ToLower() != "uuid") + { + item.Length = 10; + } + } + } + } + string sql = GetCreateTableSql(tableName, columns); + string primaryKeyInfo = null; + if (columns.Any(it => it.IsPrimarykey) && isCreatePrimaryKey) + { + primaryKeyInfo = string.Format(", Primary key({0})", string.Join(",", columns.Where(it => it.IsPrimarykey).Select(it => this.SqlBuilder.GetTranslationColumnName(it.DbColumnName.ToLower(isAutoToLowerCodeFirst))))); + + } + sql = sql.Replace("$PrimaryKey", primaryKeyInfo); + this.Context.Ado.ExecuteCommand(sql); + return true; + } + protected override bool IsAnyDefaultValue(string tableName, string columnName, List columns) + { + var defaultValue = columns.Where(it => it.DbColumnName.Equals(columnName, StringComparison.CurrentCultureIgnoreCase)).First().DefaultValue; + if (defaultValue?.StartsWith("NULL::") == true) + { + return false; + } + return defaultValue.HasValue(); + } + protected override string GetCreateTableSql(string tableName, List columns) + { + List columnArray = new List(); + Check.Exception(columns.IsNullOrEmpty(), "No columns found "); + foreach (var item in columns) + { + string columnName = item.DbColumnName; + string dataType = item.DataType; + if (dataType == "varchar" && item.Length == 0) + { + item.Length = 1; + } + //if (dataType == "uuid") + //{ + // item.Length = 50; + // dataType = "varchar"; + //} + string dataSize = item.Length > 0 ? string.Format("({0})", item.Length) : null; + if (item.DecimalDigits > 0 && item.Length > 0 && dataType == "numeric") + { + dataSize = $"({item.Length},{item.DecimalDigits})"; + } + string nullType = item.IsNullable ? this.CreateTableNull : CreateTableNotNull; + string primaryKey = null; + string addItem = string.Format(this.CreateTableColumn, this.SqlBuilder.GetTranslationColumnName(columnName.ToLower(isAutoToLowerCodeFirst)), dataType, dataSize, nullType, primaryKey, ""); + if (item.IsIdentity) + { + if (dataType?.ToLower() == "int") + { + dataSize = "int4"; + } + else if (dataType?.ToLower() == "long") + { + dataSize = "int8"; + } + string length = dataType.Substring(dataType.Length - 1); + string identityDataType = "serial" + length; + addItem = addItem.Replace(dataType, identityDataType); + } + columnArray.Add(addItem); + } + string tableString = string.Format(this.CreateTableSql, this.SqlBuilder.GetTranslationTableName(tableName.ToLower(isAutoToLowerCodeFirst)), string.Join(",\r\n", columnArray)); + return tableString; + } + public override bool IsAnyConstraint(string constraintName) + { + throw new NotSupportedException("PgSql IsAnyConstraint NotSupportedException"); + } + public override bool BackupDataBase(string databaseName, string fullFileName) + { + Check.ThrowNotSupportedException("PgSql BackupDataBase NotSupported"); + return false; + } + + public override List GetColumnInfosByTableName(string tableName, bool isCache = true) + { + var result = base.GetColumnInfosByTableName(tableName.TrimEnd('"').TrimStart('"').ToLower(), isCache); + if (result == null || result.Count == 0) + { + result = base.GetColumnInfosByTableName(tableName, isCache); + } + try + { + string sql = $@"select + kcu.column_name as key_column + from information_schema.table_constraints tco + join information_schema.key_column_usage kcu + on kcu.constraint_name = tco.constraint_name + and kcu.constraint_schema = tco.constraint_schema + and kcu.constraint_name = tco.constraint_name + where tco.constraint_type = 'PRIMARY KEY' + and kcu.table_schema='{GetSchema()}' and + upper(kcu.table_name)=upper('{tableName.TrimEnd('"').TrimStart('"')}')"; + List pkList = new List(); + if (isCache) + { + pkList = GetListOrCache("GetColumnInfosByTableName_N_Pk" + tableName, sql); + } + else + { + pkList = this.Context.Ado.SqlQuery(sql); + } + if (pkList.Count > 1) + { + foreach (var item in result) + { + if (pkList.Select(it => it.ToUpper()).Contains(item.DbColumnName.ToUpper())) + { + item.IsPrimarykey = true; + } + } + } + } + catch + { + + } + return result; + } + #endregion + + #region Helper + private bool isAutoToLowerCodeFirst + { + get + { + if (this.Context.CurrentConnectionConfig.MoreSettings == null) return true; + else if ( + this.Context.CurrentConnectionConfig.MoreSettings.PgSqlIsAutoToLower == false && + this.Context.CurrentConnectionConfig.MoreSettings?.PgSqlIsAutoToLowerCodeFirst == false) + { + return false; + } + else + { + return true; + } + } + } + private string GetSchema() + { + var schema = "public"; + var pgSqlIsAutoToLowerSchema = this.Context?.CurrentConnectionConfig?.MoreSettings?.PgSqlIsAutoToLowerSchema == false; + if (pgSqlIsAutoToLowerSchema) + { + if (System.Text.RegularExpressions.Regex.IsMatch(this.Context.CurrentConnectionConfig.ConnectionString, "searchpath=", RegexOptions.IgnoreCase)) + { + var regValue = System.Text.RegularExpressions.Regex.Match(this.Context.CurrentConnectionConfig.ConnectionString, @"searchpath\=(\w+)").Groups[1].Value; + if (regValue.HasValue()) + { + schema = regValue; + } + } + else if (System.Text.RegularExpressions.Regex.IsMatch(this.Context.CurrentConnectionConfig.ConnectionString, "search path=", RegexOptions.IgnoreCase)) + { + var regValue = System.Text.RegularExpressions.Regex.Match(this.Context.CurrentConnectionConfig.ConnectionString.ToLower(), @"search path\=(\w+)").Groups[1].Value; + if (regValue.HasValue()) + { + schema = regValue; + } + } + } + else + { + if (System.Text.RegularExpressions.Regex.IsMatch(this.Context.CurrentConnectionConfig.ConnectionString.ToLower(), "searchpath=")) + { + var regValue = System.Text.RegularExpressions.Regex.Match(this.Context.CurrentConnectionConfig.ConnectionString.ToLower(), @"searchpath\=(\w+)").Groups[1].Value; + if (regValue.HasValue()) + { + schema = regValue; + } + } + else if (System.Text.RegularExpressions.Regex.IsMatch(this.Context.CurrentConnectionConfig.ConnectionString.ToLower(), "search path=")) + { + var regValue = System.Text.RegularExpressions.Regex.Match(this.Context.CurrentConnectionConfig.ConnectionString.ToLower(), @"search path\=(\w+)").Groups[1].Value; + if (regValue.HasValue()) + { + schema = regValue; + } + } + } + return schema; + } + private static void ConvertCreateColumnInfo(DbColumnInfo x) + { + string[] array = new string[] { "uuid", "int4", "text", "int2", "int8", "date", "bit", "text", "timestamp" }; + + if (array.Contains(x.DataType?.ToLower())) + { + x.Length = 0; + x.DecimalDigits = 0; + } + } + #endregion + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/PostgreSQL/Insertable/PostgreSQLInserttable.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/PostgreSQL/Insertable/PostgreSQLInserttable.cs new file mode 100644 index 000000000..fd2c3c042 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/PostgreSQL/Insertable/PostgreSQLInserttable.cs @@ -0,0 +1,98 @@ +namespace SqlSugar +{ + public class PostgreSQLInserttable : InsertableProvider where T : class, new() + { + public override int ExecuteReturnIdentity() + { + InsertBuilder.IsReturnIdentity = true; + PreToSql(); + string identityColumn = GetIdentityColumn(); + string sql = InsertBuilder.ToSqlString().Replace("$PrimaryKey", this.SqlBuilder.GetTranslationColumnName(identityColumn)); + RestoreMapping(); + AutoRemoveDataCache(); + var result = Ado.GetScalar(sql, InsertBuilder.Parameters == null ? null : InsertBuilder.Parameters.ToArray()).ObjToInt(); + After(sql, result); + return result; + } + public override async Task ExecuteReturnIdentityAsync() + { + InsertBuilder.IsReturnIdentity = true; + PreToSql(); + string identityColumn = GetIdentityColumn(); + string sql = InsertBuilder.ToSqlString().Replace("$PrimaryKey", this.SqlBuilder.GetTranslationColumnName(identityColumn)); + RestoreMapping(); + AutoRemoveDataCache(); + var obj = await Ado.GetScalarAsync(sql, InsertBuilder.Parameters == null ? null : InsertBuilder.Parameters.ToArray()).ConfigureAwait(false); + var result = obj.ObjToInt(); + After(sql, result); + return result; + } + public override KeyValuePair> ToSql() + { + var result = base.ToSql(); + var primaryKey = GetPrimaryKeys().FirstOrDefault(); + if (primaryKey != null) + { + primaryKey = this.SqlBuilder.GetTranslationColumnName(primaryKey); + } + else if (result.Key?.EndsWith(" returning $PrimaryKey") == true) + { + result = new KeyValuePair>(result.Key.Replace(" returning $PrimaryKey", null), result.Value); + } + return new KeyValuePair>(result.Key.Replace("$PrimaryKey", primaryKey), result.Value); + } + + public override long ExecuteReturnBigIdentity() + { + InsertBuilder.IsReturnIdentity = true; + PreToSql(); + string sql = InsertBuilder.ToSqlString().Replace("$PrimaryKey", this.SqlBuilder.GetTranslationColumnName(GetIdentityKeys().FirstOrDefault())); + RestoreMapping(); + AutoRemoveDataCache(); + var result = Convert.ToInt64(Ado.GetScalar(sql, InsertBuilder.Parameters == null ? null : InsertBuilder.Parameters.ToArray()) ?? "0"); + After(sql, result); + return result; + } + public override async Task ExecuteReturnBigIdentityAsync() + { + InsertBuilder.IsReturnIdentity = true; + PreToSql(); + string sql = InsertBuilder.ToSqlString().Replace("$PrimaryKey", this.SqlBuilder.GetTranslationColumnName(GetIdentityKeys().FirstOrDefault())); + RestoreMapping(); + AutoRemoveDataCache(); + var result = Convert.ToInt64(await Ado.GetScalarAsync(sql, InsertBuilder.Parameters == null ? null : InsertBuilder.Parameters.ToArray()).ConfigureAwait(false) ?? "0"); + After(sql, result); + return result; + } + + public override bool ExecuteCommandIdentityIntoEntity() + { + var result = InsertObjs.First(); + var identityKeys = GetIdentityKeys(); + if (identityKeys.Count == 0) { return this.ExecuteCommand() > 0; } + var idValue = ExecuteReturnBigIdentity(); + Check.Exception(identityKeys.Count > 1, "ExecuteCommandIdentityIntoEntity does not support multiple identity keys"); + var identityKey = identityKeys.First(); + object setValue = 0; + if (idValue > int.MaxValue) + setValue = idValue; + else + setValue = Convert.ToInt32(idValue); + var propertyName = this.Context.EntityMaintenance.GetPropertyName(identityKey); + typeof(T).GetProperties().First(t => t.Name.Equals(propertyName, StringComparison.CurrentCultureIgnoreCase)).SetValue(result, setValue, null); + return idValue > 0; + } + + private string GetIdentityColumn() + { + var identityColumn = GetIdentityKeys().FirstOrDefault(); + if (identityColumn == null) + { + var columns = this.Context.DbMaintenance.GetColumnInfosByTableName(InsertBuilder.GetTableNameString); + identityColumn = columns.First(it => it.IsIdentity || it.IsPrimarykey).DbColumnName; + } + return identityColumn; + } + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/PostgreSQL/PostgreSQLProvider.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/PostgreSQL/PostgreSQLProvider.cs new file mode 100644 index 000000000..7e367e872 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/PostgreSQL/PostgreSQLProvider.cs @@ -0,0 +1,275 @@ +using Npgsql; + +using NpgsqlTypes; + +using System.Data; +using System.Data.Common; + +namespace SqlSugar +{ + public partial class PostgreSQLProvider : AdoProvider + { + public PostgreSQLProvider() + { + + if (StaticConfig.AppContext_ConvertInfinityDateTime == false) + { + AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true); + AppContext.SetSwitch("Npgsql.DisableDateTimeInfinityConversions", true); + } + + } + public override IDbConnection Connection + { + get + { + if (base._DbConnection == null) + { + try + { + var npgsqlConnectionString = base.Context.CurrentConnectionConfig.ConnectionString; + base._DbConnection = new NpgsqlConnection(npgsqlConnectionString); + } + catch (Exception ex) + { + Check.Exception(true, ErrorMessage.ConnnectionOpen, ex.Message); + } + } + return base._DbConnection; + } + set + { + base._DbConnection = value; + } + } + + public override void BeginTran(string transactionName) + { + base.BeginTran(); + } + /// + /// Only SqlServer + /// + /// + /// + public override void BeginTran(IsolationLevel iso, string transactionName) + { + base.BeginTran(iso); + } + public override IDataAdapter GetAdapter() + { + return new NpgsqlDataAdapter(); + } + public override DbCommand GetCommand(string sql, SugarParameter[] parameters) + { + NpgsqlCommand sqlCommand = new NpgsqlCommand(sql, (NpgsqlConnection)this.Connection); + sqlCommand.CommandType = this.CommandType; + sqlCommand.CommandTimeout = this.CommandTimeOut; + if (this.Transaction != null) + { + sqlCommand.Transaction = (NpgsqlTransaction)this.Transaction; + } + if (parameters.HasValue()) + { + IDataParameter[] ipars = ToIDbDataParameter(parameters); + sqlCommand.Parameters.AddRange((NpgsqlParameter[])ipars); + } + CheckConnection(); + return sqlCommand; + } + public override void SetCommandToAdapter(IDataAdapter dataAdapter, DbCommand command) + { + ((NpgsqlDataAdapter)dataAdapter).SelectCommand = (NpgsqlCommand)command; + } + /// + /// if mysql return MySqlParameter[] pars + /// if sqlerver return SqlParameter[] pars ... + /// + /// + /// + public override IDataParameter[] ToIDbDataParameter(params SugarParameter[] parameters) + { + if (parameters == null || parameters.Length == 0) return null; + NpgsqlParameter[] result = new NpgsqlParameter[parameters.Length]; + int index = 0; + var isVarchar = this.Context.IsVarchar(); + foreach (var parameter in parameters) + { + if (parameter.DbType == System.Data.DbType.Int64 && parameter.Value?.Equals("Result%") == true) + { + parameter.DbType = System.Data.DbType.AnsiString; + } + UNumber(parameter); + if (parameter.Value == null) parameter.Value = DBNull.Value; + if (parameter.Value is System.Data.SqlTypes.SqlDateTime && parameter.DbType == System.Data.DbType.AnsiString) + { + parameter.DbType = System.Data.DbType.DateTime; + parameter.Value = DBNull.Value; + } + var sqlParameter = new NpgsqlParameter(); + sqlParameter.ParameterName = parameter.ParameterName; + sqlParameter.Size = parameter.Size; + sqlParameter.Value = parameter.Value; + sqlParameter.DbType = parameter.DbType; + sqlParameter.Direction = parameter.Direction; + if (parameter.IsJson) + { + sqlParameter.NpgsqlDbType = NpgsqlDbType.Json; + } + if (parameter.IsArray) + { + Array(parameter, sqlParameter); + } + if (sqlParameter.Direction == 0) + { + sqlParameter.Direction = ParameterDirection.Input; + } + result[index] = sqlParameter; + if (sqlParameter.Direction.IsIn(ParameterDirection.Output, ParameterDirection.InputOutput, ParameterDirection.ReturnValue)) + { + if (this.OutputParameters == null) this.OutputParameters = new List(); + this.OutputParameters.RemoveAll(it => it.ParameterName == sqlParameter.ParameterName); + this.OutputParameters.Add(sqlParameter); + } + if (isVarchar && sqlParameter.DbType == System.Data.DbType.String) + { + sqlParameter.DbType = System.Data.DbType.AnsiString; + } + else if (sqlParameter.Value is DateTime && sqlParameter.DbType == System.Data.DbType.AnsiString) + { + sqlParameter.DbType = System.Data.DbType.DateTime; + } + ++index; + if (parameter.CustomDbType != null && parameter.CustomDbType is NpgsqlDbType) + { + sqlParameter.NpgsqlDbType = ((NpgsqlDbType)parameter.CustomDbType); + } + } + return result; + } + + //private static void ConvertUNumber(SugarParameter parameter) + //{ + // if (parameter.DbType == System.Data.DbType.UInt32) + // { + // parameter.DbType = System.Data.DbType.Int32; + // } + // else if (parameter.DbType == System.Data.DbType.UInt64) + // { + // parameter.DbType = System.Data.DbType.UInt64; + // } + //} + + private static void Array(SugarParameter parameter, NpgsqlParameter sqlParameter) + { + // sqlParameter.Value = this.Context.Utilities.SerializeObject(sqlParameter.Value); + var type = sqlParameter.Value.GetType(); + if (ArrayMapping.TryGetValue(type, out NpgsqlDbType value)) + { + sqlParameter.NpgsqlDbType = value | NpgsqlDbType.Array; + } + else if (type == DBNull.Value.GetType()) + { + DbNullParametrerArray(parameter, sqlParameter); + + } + else + { + Check.Exception(true, sqlParameter.Value.GetType().Name + " No Support"); + } + } + + private static void DbNullParametrerArray(SugarParameter parameter, NpgsqlParameter sqlParameter) + { + if (parameter.DbType.IsIn(System.Data.DbType.Int32)) + { + sqlParameter.NpgsqlDbType = NpgsqlDbType.Integer | NpgsqlDbType.Array; + } + else if (parameter.DbType.IsIn(System.Data.DbType.Int16)) + { + sqlParameter.NpgsqlDbType = NpgsqlDbType.Smallint | NpgsqlDbType.Array; + } + else if (parameter.DbType.IsIn(System.Data.DbType.Int64)) + { + sqlParameter.NpgsqlDbType = NpgsqlDbType.Bigint | NpgsqlDbType.Array; + } + else if (parameter.DbType.IsIn(System.Data.DbType.Guid)) + { + sqlParameter.NpgsqlDbType = NpgsqlDbType.Uuid | NpgsqlDbType.Array; + } + else + { + sqlParameter.NpgsqlDbType = NpgsqlDbType.Text | NpgsqlDbType.Array; + } + } + + private static void UNumber(SugarParameter parameter) + { + if (parameter.DbType == System.Data.DbType.UInt16) + { + parameter.DbType = System.Data.DbType.Int16; + parameter.Value = Convert.ToInt16(parameter.Value); + } + else if (parameter.DbType == System.Data.DbType.UInt32) + { + parameter.DbType = System.Data.DbType.Int32; + parameter.Value = Convert.ToInt32(parameter.Value); + } + else if (parameter.DbType == System.Data.DbType.UInt64) + { + parameter.DbType = System.Data.DbType.Int64; + parameter.Value = Convert.ToInt64(parameter.Value); + } + if (parameter.Value is uint) + { + parameter.Value = Convert.ToInt32(parameter.Value); + } + else if (parameter.Value is ulong) + { + parameter.Value = Convert.ToInt64(parameter.Value); + } + } + public override Action ErrorEvent => it => + { + if (base.ErrorEvent != null) + { + base.ErrorEvent(it); + } + if (it.Message?.StartsWith("42883: function uuid_generate_v4() does not exist") == true) + { + Check.ExceptionEasy(it.Message, $"使用uuid_generate_v4()函数需要创建 CREATE EXTENSION IF NOT EXISTS pgcrypto;CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\" "); + } + }; + + static readonly Dictionary ArrayMapping = new Dictionary() + { + { typeof(int[]),NpgsqlDbType.Integer}, + { typeof(short[]),NpgsqlDbType.Smallint}, + { typeof(long[]),NpgsqlDbType.Bigint}, + { typeof(decimal[]),NpgsqlDbType.Numeric}, + { typeof(double[]),NpgsqlDbType.Double}, + { typeof(char[]),NpgsqlDbType.Text}, + { typeof(byte[]),NpgsqlDbType.Bytea}, + { typeof(bool[]),NpgsqlDbType.Boolean}, + {typeof(DateTime[]),NpgsqlDbType.Date}, + {typeof(float[]),NpgsqlDbType.Real}, + {typeof(Guid[]),NpgsqlDbType.Uuid}, + + + { typeof(int?[]),NpgsqlDbType.Integer}, + { typeof(short?[]),NpgsqlDbType.Smallint}, + { typeof(long?[]),NpgsqlDbType.Bigint}, + { typeof(decimal?[]),NpgsqlDbType.Numeric}, + { typeof(double?[]),NpgsqlDbType.Double}, + { typeof(char?[]),NpgsqlDbType.Text}, + { typeof(byte?[]),NpgsqlDbType.Bytea}, + { typeof(bool?[]),NpgsqlDbType.Boolean}, + {typeof(DateTime?[]),NpgsqlDbType.Date}, + {typeof(Guid?[]),NpgsqlDbType.Uuid}, + + + { typeof(string[]), NpgsqlDbType.Text}, + {typeof(float?[]),NpgsqlDbType.Real}, + }; + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/PostgreSQL/Queryable/PostgreSqlQueryable.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/PostgreSQL/Queryable/PostgreSqlQueryable.cs new file mode 100644 index 000000000..f9306301a --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/PostgreSQL/Queryable/PostgreSqlQueryable.cs @@ -0,0 +1,63 @@ +namespace SqlSugar +{ + public class PostgreSQLQueryable : QueryableProvider + { + public override ISugarQueryable With(string withString) + { + return this; + } + + public override ISugarQueryable PartitionBy(string groupFileds) + { + this.GroupBy(groupFileds); + return this; + } + } + public class PostgreSQLQueryable : QueryableProvider + { + public new ISugarQueryable With(string withString) + { + return this; + } + } + public class PostgreSQLQueryable : QueryableProvider + { + + } + public class PostgreSQLQueryable : QueryableProvider + { + + } + public class PostgreSQLQueryable : QueryableProvider + { + + } + public class PostgreSQLQueryable : QueryableProvider + { + + } + public class PostgreSQLQueryable : QueryableProvider + { + + } + public class PostgreSQLQueryable : QueryableProvider + { + + } + public class PostgreSQLQueryable : QueryableProvider + { + + } + public class PostgreSQLQueryable : QueryableProvider + { + + } + public class PostgreSQLQueryable : QueryableProvider + { + + } + public class PostgreSQLQueryable : QueryableProvider + { + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/PostgreSQL/SqlBuilder/PostgreSQLBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/PostgreSQL/SqlBuilder/PostgreSQLBuilder.cs new file mode 100644 index 000000000..f60c3ce7c --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/PostgreSQL/SqlBuilder/PostgreSQLBuilder.cs @@ -0,0 +1,124 @@ +namespace SqlSugar +{ + public class PostgreSQLBuilder : SqlBuilderProvider + { + public override string SqlTranslationLeft + { + get + { + return "\""; + } + } + public override string SqlTranslationRight + { + get + { + return "\""; + } + } + public override string SqlDateNow + { + get + { + return "CURRENT_TIMESTAMP"; + } + } + public override string FullSqlDateNow + { + get + { + return "select CURRENT_TIMESTAMP"; + } + } + + public bool isAutoToLower + { + get + { + if (this.Context.CurrentConnectionConfig.MoreSettings == null) return true; + return this.Context.CurrentConnectionConfig.MoreSettings.PgSqlIsAutoToLower; + } + } + public override string GetTranslationColumnName(string propertyName) + { + if (propertyName.Contains('.') && !propertyName.Contains(SqlTranslationLeft)) + { + return string.Join(".", propertyName.Split('.').Select(it => $"{SqlTranslationLeft}{it.ToLower(isAutoToLower)}{SqlTranslationRight}")); + } + + if (propertyName.Contains(SqlTranslationLeft)) return propertyName; + else + return SqlTranslationLeft + propertyName.ToLower(isAutoToLower) + SqlTranslationRight; + } + + //public override string GetNoTranslationColumnName(string name) + //{ + // return name.TrimEnd(Convert.ToChar(SqlTranslationRight)).TrimStart(Convert.ToChar(SqlTranslationLeft)).ToLower(); + //} + public override string GetTranslationColumnName(string entityName, string propertyName) + { + Check.ArgumentNullException(entityName, string.Format(null, ErrorMessage.ObjNotExistCompositeFormat, "Table Name")); + Check.ArgumentNullException(propertyName, string.Format(null, ErrorMessage.ObjNotExistCompositeFormat, "Column Name")); + var context = this.Context; + var mappingInfo = context + .MappingColumns + .FirstOrDefault(it => + it.EntityName.Equals(entityName, StringComparison.CurrentCultureIgnoreCase) && + it.PropertyName.Equals(propertyName, StringComparison.CurrentCultureIgnoreCase)); + return (mappingInfo == null ? SqlTranslationLeft + propertyName.ToLower(isAutoToLower) + SqlTranslationRight : SqlTranslationLeft + mappingInfo.DbColumnName.ToLower(isAutoToLower) + SqlTranslationRight); + } + + public override string GetTranslationTableName(string name) + { + Check.ArgumentNullException(name, string.Format(null, ErrorMessage.ObjNotExistCompositeFormat, "Table Name")); + var context = this.Context; + + var mappingInfo = context + .MappingTables + .FirstOrDefault(it => it.EntityName.Equals(name, StringComparison.CurrentCultureIgnoreCase)); + if (mappingInfo == null && name.Contains('.') && name.Contains('"')) + { + return name; + } + name = (mappingInfo == null ? name : mappingInfo.DbTableName); + if (name.Contains('.') && !name.Contains('(') && !name.Contains("\".\"")) + { + return string.Join(".", name.ToLower(isAutoToLower).Split('.').Select(it => SqlTranslationLeft + it + SqlTranslationRight)); + } + else if (name.Contains('(')) + { + return name; + } + else if (name.Contains(SqlTranslationLeft) && name.Contains(SqlTranslationRight)) + { + return name; + } + else + { + return SqlTranslationLeft + name.ToLower(isAutoToLower).TrimEnd('"').TrimStart('"') + SqlTranslationRight; + } + } + public override string GetUnionFomatSql(string sql) + { + return " ( " + sql + " ) "; + } + + public override Type GetNullType(string tableName, string columnName) + { + if (tableName != null) + tableName = tableName.Trim(); + var columnInfo = this.Context.DbMaintenance.GetColumnInfosByTableName(tableName).FirstOrDefault(z => z.DbColumnName.EqualCase(columnName)); + if (columnInfo != null) + { + var cTypeName = this.Context.Ado.DbBind.GetCsharpTypeNameByDbTypeName(columnInfo.DataType); + var value = UtilMethods.GetTypeByTypeName(cTypeName); + if (value != null) + { + var key = "GetNullType_" + tableName + columnName; + return new ReflectionInoCacheService().GetOrCreate(key, () => value); + } + } + return null; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/PostgreSQL/SqlBuilder/PostgreSQLDeleteBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/PostgreSQL/SqlBuilder/PostgreSQLDeleteBuilder.cs new file mode 100644 index 000000000..57f4ba33a --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/PostgreSQL/SqlBuilder/PostgreSQLDeleteBuilder.cs @@ -0,0 +1,7 @@ +namespace SqlSugar +{ + public class PostgreSQLDeleteBuilder : DeleteBuilder + { + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/PostgreSQL/SqlBuilder/PostgreSQLExpressionContext.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/PostgreSQL/SqlBuilder/PostgreSQLExpressionContext.cs new file mode 100644 index 000000000..3fdc3a94c --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/PostgreSQL/SqlBuilder/PostgreSQLExpressionContext.cs @@ -0,0 +1,521 @@ +namespace SqlSugar +{ + public partial class PostgreSQLExpressionContext : ExpressionContext, ILambdaExpressions + { + public SqlSugarProvider Context { get; set; } + public PostgreSQLExpressionContext() + { + base.DbMehtods = new PostgreSQLMethod(); + } + public override string SqlTranslationLeft + { + get + { + return "\""; + } + } + public override string SqlTranslationRight + { + get + { + return "\""; + } + } + public override string GetTranslationText(string name) + { + return SqlTranslationLeft + name.ToLower(isAutoToLower) + SqlTranslationRight; + } + public bool isAutoToLower + { + get + { + return base.PgSqlIsAutoToLower; + } + } + public override string GetTranslationTableName(string entityName, bool isMapping = true) + { + Check.ArgumentNullException(entityName, string.Format(null, ErrorMessage.ObjNotExistCompositeFormat, "Table Name")); + if (IsTranslationText(entityName)) return entityName; + isMapping = isMapping && this.MappingTables.HasValue(); + var isComplex = entityName.Contains(UtilConstants.Dot); + if (isMapping && isComplex) + { + var columnInfo = entityName.Split(UtilConstants.DotChar); + var mappingInfo = this.MappingTables.FirstOrDefault(it => it.EntityName.Equals(columnInfo.Last(), StringComparison.CurrentCultureIgnoreCase)); + if (mappingInfo != null) + { + columnInfo[columnInfo.Length - 1] = mappingInfo.EntityName; + } + return string.Join(UtilConstants.Dot, columnInfo.Select(it => GetTranslationText(it))); + } + else if (isMapping) + { + var mappingInfo = this.MappingTables.FirstOrDefault(it => it.EntityName.Equals(entityName, StringComparison.CurrentCultureIgnoreCase)); + + var tableName = mappingInfo?.DbTableName + ""; + if (tableName.Contains('.')) + { + tableName = string.Join(UtilConstants.Dot, tableName.Split(UtilConstants.DotChar).Select(it => GetTranslationText(it))); + return tableName; + } + + return SqlTranslationLeft + (mappingInfo == null ? entityName : mappingInfo.DbTableName).ToLower(isAutoToLower) + SqlTranslationRight; + } + else if (isComplex) + { + return string.Join(UtilConstants.Dot, entityName.Split(UtilConstants.DotChar).Select(it => GetTranslationText(it))); + } + else + { + return GetTranslationText(entityName); + } + } + public override string GetTranslationColumnName(string columnName) + { + Check.ArgumentNullException(columnName, string.Format(null, ErrorMessage.ObjNotExistCompositeFormat, "Column Name")); + if (columnName.Substring(0, 1) == this.SqlParameterKeyWord) + { + return columnName; + } + if (IsTranslationText(columnName)) return columnName; + if (columnName.Contains(UtilConstants.Dot)) + { + return string.Join(UtilConstants.Dot, columnName.Split(UtilConstants.DotChar).Select(it => GetTranslationText(it))); + } + else + { + return GetTranslationText(columnName); + } + } + public override string GetDbColumnName(string entityName, string propertyName) + { + if (this.MappingColumns.HasValue()) + { + var mappingInfo = this.MappingColumns.SingleOrDefault(it => it.EntityName == entityName && it.PropertyName == propertyName); + return (mappingInfo == null ? propertyName : mappingInfo.DbColumnName).ToLower(isAutoToLower); + } + else + { + return propertyName.ToLower(isAutoToLower); + } + } + + public string GetValue(object entityValue) + { + if (entityValue == null) + return null; + var type = UtilMethods.GetUnderType(entityValue.GetType()); + if (UtilConstants.NumericalTypes.Contains(type)) + { + return entityValue.ToString(); + } + else if (type == UtilConstants.DateType) + { + return this.DbMehtods.ToDate(new MethodCallExpressionModel() + { + Args = new System.Collections.Generic.List() { + new MethodCallExpressionArgs(){ MemberName=$"'{entityValue}'" } + } + }); + } + else + { + return this.DbMehtods.ToString(new MethodCallExpressionModel() + { + Args = new System.Collections.Generic.List() { + new MethodCallExpressionArgs(){ MemberName=$"'{entityValue}'" } + } + }); + } + } + } + public class PostgreSQLMethod : DefaultDbMethod, IDbMethods + { + public override string UNIX_TIMESTAMP(MethodCallExpressionModel model) + { + var parameterNameA = model.Args[0].MemberName; + return $" EXTRACT(EPOCH FROM {parameterNameA})::BIGINT "; + } + public override string CharIndex(MethodCallExpressionModel model) + { + return string.Format(" (strpos ({1},{0})-1)", model.Args[0].MemberName, model.Args[1].MemberName); + } + public override string CharIndexNew(MethodCallExpressionModel model) + { + return string.Format(" (strpos ({0},{1}))", model.Args[0].MemberName, model.Args[1].MemberName); + } + public override string TrueValue() + { + return "true"; + } + public override string FalseValue() + { + return "false"; + } + public override string DateDiff(MethodCallExpressionModel model) + { + var parameter = (DateType)(Enum.Parse(typeof(DateType), model.Args[0].MemberValue.ObjToString())); + var begin = model.Args[1].MemberName; + var end = model.Args[2].MemberName; + switch (parameter) + { + case DateType.Year: + return $" ( DATE_PART('Year', {end} ) - DATE_PART('Year', {begin}) )"; + case DateType.Month: + return $" ( ( DATE_PART('Year', {end} ) - DATE_PART('Year', {begin}) ) * 12 + (DATE_PART('month', {end}) - DATE_PART('month', {begin})) )"; + case DateType.Day: + return $" ( DATE_PART('day', {end} - {begin}) )"; + case DateType.Hour: + return $" ( ( DATE_PART('day', {end} - {begin}) ) * 24 + DATE_PART('hour', {end} - {begin} ) )"; + case DateType.Minute: + return $" ( ( ( DATE_PART('day', {end} - {begin}) ) * 24 + DATE_PART('hour', {end} - {begin} ) ) * 60 + DATE_PART('minute', {end} - {begin} ) )"; + case DateType.Second: + return $" ( ( ( DATE_PART('day', {end} - {begin}) ) * 24 + DATE_PART('hour', {end} - {begin} ) ) * 60 + DATE_PART('minute', {end} - {begin} ) ) * 60 + DATE_PART('second', {end} - {begin} )"; + case DateType.Millisecond: + break; + default: + break; + } + throw new Exception(parameter + " datediff no support"); + } + public override string IIF(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + var parameter3 = model.Args[2]; + if (parameter.Type == UtilConstants.BoolType) + { + parameter.MemberName = parameter.MemberName.ToString().Replace("=1", "=true"); + parameter2.MemberName = false; + parameter3.MemberName = true; + } + return string.Format("( CASE WHEN {0} THEN {1} ELSE {2} END )", parameter.MemberName, parameter2.MemberName, parameter3.MemberName); + } + public override string DateValue(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + var format = "dd"; + if (parameter2.MemberValue.ObjToString() == DateType.Year.ToString()) + { + format = "yyyy"; + } + if (parameter2.MemberValue.ObjToString() == DateType.Month.ToString()) + { + format = "MM"; + } + if (parameter2.MemberValue.ObjToString() == DateType.Day.ToString()) + { + format = "dd"; + } + if (parameter2.MemberValue.ObjToString() == DateType.Hour.ToString()) + { + format = "hh24"; + } + if (parameter2.MemberValue.ObjToString() == DateType.Minute.ToString()) + { + format = "mi"; + } + if (parameter2.MemberValue.ObjToString() == DateType.Second.ToString()) + { + format = "ss"; + } + if (parameter2.MemberValue.ObjToString() == DateType.Millisecond.ToString()) + { + format = "ms"; + } + if (parameter2.MemberValue.ObjToString() == DateType.Quarter.ToString()) + { + format = "q"; + } + if (parameter2.MemberValue.ObjToString() == DateType.Weekday.ToString()) + { + return $" extract(DOW FROM cast({parameter.MemberName} as TIMESTAMP)) "; + } + + return string.Format(" cast( to_char({1},'{0}')as integer ) ", format, parameter.MemberName); + } + + public override string Contains(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + return string.Format(" ({0} like concat('%',{1},'%')) ", parameter.MemberName, parameter2.MemberName); + } + + public override string StartsWith(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + var parameter2Info = model.Parameters?.FirstOrDefault(it => it.ParameterName.EqualCase(parameter2.MemberName + "")); + if (parameter2Info != null && parameter2.MemberName?.ToString()?.StartsWith("@MethodConst") == true) + { + parameter2Info.Value = parameter2.MemberValue + "%"; + return string.Format(" ({0} like {1} ) ", parameter.MemberName, parameter2.MemberName); + } + return string.Format(" ({0} like concat({1},'%')) ", parameter.MemberName, parameter2.MemberName); + } + + public override string EndsWith(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + var parameter2Info = model.Parameters?.FirstOrDefault(it => it.ParameterName.EqualCase(parameter2.MemberName + "")); + if (parameter2Info != null && parameter2.MemberName?.ToString()?.StartsWith("@MethodConst") == true) + { + parameter2Info.Value = "%" + parameter2.MemberValue; + return string.Format(" ({0} like {1} ) ", parameter.MemberName, parameter2.MemberName); + } + return string.Format(" ({0} like concat('%',{1}))", parameter.MemberName, parameter2.MemberName); + } + + public override string DateIsSameDay(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + return string.Format(" ( to_char({0},'yyyy-MM-dd')=to_char({1},'yyyy-MM-dd') ) ", parameter.MemberName, parameter2.MemberName); ; + } + + public override string HasValue(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format("( {0} IS NOT NULL )", parameter.MemberName); + } + + public override string DateIsSameByType(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + var parameter3 = model.Args[2]; + DateType dateType = (DateType)parameter3.MemberValue; + var format = "yyyy-MM-dd"; + if (dateType == DateType.Quarter) + { + return string.Format(" (date_trunc('quarter',{0})=date_trunc('quarter',{1}) ) ", parameter.MemberName, parameter2.MemberName, format); + } + switch (dateType) + { + case DateType.Year: + format = "yyyy"; + break; + case DateType.Month: + format = "yyyy-MM"; + break; + case DateType.Day: + break; + case DateType.Hour: + format = "yyyy-MM-dd HH"; + break; + case DateType.Second: + format = "yyyy-MM-dd HH:mm:ss"; + break; + case DateType.Minute: + format = "yyyy-MM-dd HH:mm"; + break; + case DateType.Millisecond: + format = "yyyy-MM-dd HH:mm.ms"; + break; + default: + break; + } + return string.Format(" ( to_char({0},'{2}')=to_char({1},'{2}') ) ", parameter.MemberName, parameter2.MemberName, format); + } + + public override string ToDate(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS timestamp)", parameter.MemberName); + } + public override string DateAddByType(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + var parameter3 = model.Args[2]; + return string.Format(" ({1} + ({2}||'{0}')::INTERVAL) ", parameter3.MemberValue, parameter.MemberName, parameter2.MemberName); + } + + public override string DateAddDay(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + return string.Format(" ({0} + ({1}||'day')::INTERVAL) ", parameter.MemberName, parameter2.MemberName); + } + + public override string ToInt32(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS INT4)", parameter.MemberName); + } + + public override string ToInt64(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS INT8)", parameter.MemberName); + } + + public override string ToString(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS VARCHAR)", parameter.MemberName); + } + + public override string ToGuid(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS UUID)", parameter.MemberName); + } + + public override string ToDouble(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS DECIMAL(18,4))", parameter.MemberName); + } + + public override string ToBool(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS boolean)", parameter.MemberName); + } + + public override string ToDecimal(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS DECIMAL(18,4))", parameter.MemberName); + } + + public override string Length(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" LENGTH({0})", parameter.MemberName); + } + public override string IsNullOrEmpty(MethodCallExpressionModel model) + { + if ( + model.Conext?.SugarContext?.Context?.CurrentConnectionConfig?.MoreSettings?.DatabaseModel == DbType.Vastbase || + model.Conext?.SugarContext?.Context?.CurrentConnectionConfig?.MoreSettings?.DatabaseModel == DbType.GaussDB) + { + var parameter = model.Args[0]; + return string.Format("( {0} IS NULL )", parameter.MemberName); + } + else + { + return base.IsNullOrEmpty(model); + } + } + public override string MergeString(params string[] strings) + { + var key = Guid.NewGuid() + ""; + return " concat(" + string.Join(",", strings.Select(it => it?.Replace("+", key))).Replace("+", "").Replace(key, "+") + ") "; + } + public override string IsNull(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter1 = model.Args[1]; + return string.Format("(CASE WHEN {0} IS NULL THEN {1} ELSE {0} END)", parameter.MemberName, parameter1.MemberName); + } + public override string GetDate() + { + return "NOW()"; + } + public override string GetRandom() + { + return "RANDOM()"; + } + + public override string EqualTrue(string fieldName) + { + return "( " + fieldName + "=true )"; + } + + public override string JsonField(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter1 = model.Args[1]; + //var parameter2 = model.Args[2]; + //var parameter3= model.Args[3]; + var result = GetJson(parameter.MemberName, parameter1.MemberName, model.Args.Count == 2); + if (model.Args.Count > 2) + { + result = GetJson(result, model.Args[2].MemberName, model.Args.Count == 3); + } + if (model.Args.Count > 3) + { + result = GetJson(result, model.Args[3].MemberName, model.Args.Count == 4); + } + if (model.Args.Count > 4) + { + result = GetJson(result, model.Args[4].MemberName, model.Args.Count == 5); + } + if (model.Args.Count > 5) + { + result = GetJson(result, model.Args[5].MemberName, model.Args.Count == 6); + } + return result; + } + + public override string JsonContainsFieldName(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter1 = model.Args[1]; + return $"({parameter.MemberName}::jsonb ?{parameter1.MemberName})"; + } + + private string GetJson(object memberName1, object memberName2, bool isLast) + { + if (isLast) + { + return $"({memberName1}::json->>{memberName2})"; + } + else + { + return $"({memberName1}->{memberName2})"; + } + } + + public override string JsonArrayLength(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + //var parameter1 = model.Args[1]; + return $" json_array_length({parameter.MemberName}::json) "; + } + + public override string JsonParse(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + //var parameter1 = model.Args[1]; + return $" ({parameter.MemberName}::json) "; + } + + public override string JsonArrayAny(MethodCallExpressionModel model) + { + if (UtilMethods.IsNumber(model.Args[1].MemberValue.GetType().Name)) + { + return $" {model.Args[0].MemberName}::jsonb @> '[{model.Args[1].MemberValue.ObjToStringNoTrim().ToSqlFilter()}]'::jsonb "; + } + else + { + return $" {model.Args[0].MemberName}::jsonb @> '[\"{model.Args[1].MemberValue}\"]'::jsonb "; + } + } + public override string GetStringJoinSelector(string result, string separator) + { + if (result?.ToLower()?.Contains("distinct") == true) + { + return $"string_agg({result},'{separator}') "; + } + return $"string_agg(({result})::text,'{separator}') "; + } + + public override string JsonListObjectAny(MethodCallExpressionModel model) + { + if (UtilMethods.IsNumber(model.Args[2].MemberValue.GetType().Name)) + { + return $" {model.Args[0].MemberName}::jsonb @> '[{{\"{model.Args[1].MemberValue}\":{model.Args[2].MemberValue}}}]'::jsonb "; + } + else + { + return $" {model.Args[0].MemberName}::jsonb @> '[{{\"{model.Args[1].MemberValue}\":\"{model.Args[2].MemberValue.ObjToStringNoTrim().ToSqlFilter()}\"}}]'::jsonb "; + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/PostgreSQL/SqlBuilder/PostgreSQLFastBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/PostgreSQL/SqlBuilder/PostgreSQLFastBuilder.cs new file mode 100644 index 000000000..1725d0d4d --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/PostgreSQL/SqlBuilder/PostgreSQLFastBuilder.cs @@ -0,0 +1,166 @@ +using Npgsql; + +using NpgsqlTypes; + +using System.Data; + +namespace SqlSugar +{ + public class PostgreSQLFastBuilder : FastBuilder, IFastBuilder + { + public static Dictionary PgSqlType = UtilMethods.EnumToDictionary(); + private EntityInfo entityInfo; + + public PostgreSQLFastBuilder(EntityInfo entityInfo) + { + this.entityInfo = entityInfo; + } + + public override string UpdateSql { get; set; } = @"UPDATE {1} SET {0} FROM {2} AS TE WHERE {3} +"; + + //public virtual async Task UpdateByTempAsync(string tableName, string tempName, string[] updateColumns, string[] whereColumns) + //{ + // Check.ArgumentNullException(!updateColumns.Any(), "update columns count is 0"); + // Check.ArgumentNullException(!whereColumns.Any(), "where columns count is 0"); + // var sets = string.Join(",", updateColumns.Select(it => $"TM.{it}=TE.{it}")); + // var wheres = string.Join(",", whereColumns.Select(it => $"TM.{it}=TE.{it}")); + // string sql = string.Format(UpdateSql, sets, tableName, tempName, wheres); + // return await this.Context.Ado.ExecuteCommandAsync(sql); + //} + public async Task ExecuteBulkCopyAsync(DataTable dt) + { + List lsColNames = new List(); + for (int i = 0; i < dt.Columns.Count; i++) + { + lsColNames.Add($"\"{dt.Columns[i].ColumnName}\""); + } + string copyString = $"COPY {dt.TableName} ( {string.Join(",", lsColNames)} ) FROM STDIN (FORMAT BINARY)"; + if (this.Context?.CurrentConnectionConfig?.MoreSettings?.DatabaseModel == DbType.OpenGauss) + { + copyString = copyString.Replace("(FORMAT BINARY)", "(FORMAT 'BINARY')"); + } + NpgsqlConnection conn = (NpgsqlConnection)this.Context.Ado.Connection; + var columns = this.Context.DbMaintenance.GetColumnInfosByTableName(this.entityInfo.DbTableName); + try + { + var identityColumnInfo = this.entityInfo.Columns.FirstOrDefault(it => it.IsIdentity); + if (identityColumnInfo != null) + { + throw new Exception("PgSql bulkcopy no support identity"); + } + BulkCopy(dt, copyString, conn, columns); + } + catch (Exception) + { + throw; + } + finally + { + base.CloseDb(); + } + return await Task.FromResult(dt.Rows.Count).ConfigureAwait(false); + } + + private void BulkCopy(DataTable dt, string copyString, NpgsqlConnection conn, List columns) + { + if (conn.State == ConnectionState.Closed) + conn.Open(); + List columnViews = new List(); + foreach (DataColumn item in dt.Columns) + { + ColumnView result = new ColumnView(); + result.DbColumnInfo = columns.FirstOrDefault(it => it.DbColumnName.EqualCase(item.ColumnName)); + result.DataColumn = item; + result.EntityColumnInfo = this.entityInfo.Columns.FirstOrDefault(it => it.DbColumnName.EqualCase(item.ColumnName)); + var key = result.DbColumnInfo?.DataType?.ToLower(); + if (result.DbColumnInfo == null) + { + result.Type = null; + } + else if (PgSqlType.TryGetValue(key, out NpgsqlDbType value)) + { + result.Type = value; + } + else if (key?.First() == '_') + { + if (key == "_int4") + { + result.Type = NpgsqlDbType.Array | NpgsqlDbType.Integer; + } + else if (key == "_int2") + { + result.Type = NpgsqlDbType.Array | NpgsqlDbType.Smallint; + } + else if (key == "_int8") + { + result.Type = NpgsqlDbType.Array | NpgsqlDbType.Bigint; + } + else + { + var type = PgSqlType[key.Substring(1)]; + result.Type = NpgsqlDbType.Array | type; + } + } + else + { + result.Type = null; + } + columnViews.Add(result); + } + using (var writer = conn.BeginBinaryImport(copyString)) + { + foreach (DataRow row in dt.Rows) + { + writer.StartRow(); + foreach (var column in columnViews) + { + var value = row[column.DataColumn.ColumnName]; + if (value == null) + { + value = DBNull.Value; + } + //else if (value is double&&this.Context?.CurrentConnectionConfig?.MoreSettings?.DatabaseModel==null) + //{ + // column.Type = NpgsqlDbType.Double; + //} + if (column.Type == null) + { + writer.Write(value); + } + else + { + writer.Write(value, column.Type.Value); + } + } + } + writer.Complete(); + } + } + + + public override async Task UpdateByTempAsync(string tableName, string tempName, string[] updateColumns, string[] whereColumns) + { + var sqlquerybulder = this.Context.Queryable().SqlBuilder; + Check.ArgumentNullException(updateColumns.Length == 0, "update columns count is 0"); + Check.ArgumentNullException(whereColumns.Length == 0, "where columns count is 0"); + var sets = string.Join(",", updateColumns.Select(it => $"{sqlquerybulder.GetTranslationColumnName(it)}=TE.{sqlquerybulder.GetTranslationColumnName(it)}")); + var wheres = string.Join(" AND ", whereColumns.Select(it => $"{tableName}.{sqlquerybulder.GetTranslationColumnName(it)}=TE.{sqlquerybulder.GetTranslationColumnName(it)}")); + string sql = string.Format(UpdateSql, sets, tableName, tempName, wheres); + return await Context.Ado.ExecuteCommandAsync(sql).ConfigureAwait(false); + } + public override async Task CreateTempAsync(DataTable dt) + { + await Context.Queryable().Where(it => false).AS(dt.TableName).Select(" * into temp mytemptable").ToListAsync().ConfigureAwait(false); + dt.TableName = "mytemptable"; + } + + public class ColumnView + { + public DataColumn DataColumn { get; set; } + public EntityColumnInfo EntityColumnInfo { get; set; } + public DbColumnInfo DbColumnInfo { get; set; } + public NpgsqlDbType? Type { get; set; } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/PostgreSQL/SqlBuilder/PostgreSQLInsertBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/PostgreSQL/SqlBuilder/PostgreSQLInsertBuilder.cs new file mode 100644 index 000000000..dcbc2193d --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/PostgreSQL/SqlBuilder/PostgreSQLInsertBuilder.cs @@ -0,0 +1,268 @@ +using System.Globalization; +using System.Text; + +namespace SqlSugar +{ + public class PostgreSQLInsertBuilder : InsertBuilder + { + public override string SqlTemplate + { + get + { + if (IsReturnIdentity) + { + return @"INSERT INTO {0} + ({1}) + VALUES + ({2}) returning $PrimaryKey"; + } + else + { + return @"INSERT INTO {0} + ({1}) + VALUES + ({2}) ;"; + + } + } + } + public override string SqlTemplateBatch => "INSERT INTO {0} ({1})"; + public override string SqlTemplateBatchUnion => " VALUES "; + + public override string SqlTemplateBatchSelect => " {0} "; + + public override Func ConvertInsertReturnIdFunc { get; set; } = (name, sql) => + { + return sql.Trim().TrimEnd(';') + $"returning {name} "; + }; + public override string ToSqlString() + { + if (IsNoInsertNull) + { + DbColumnInfoList = DbColumnInfoList.Where(it => it.Value != null).ToList(); + } + var groupList = DbColumnInfoList.GroupBy(it => it.TableId).ToList(); + var isSingle = groupList.Count == 1; + string columnsString = string.Join(",", groupList.First().Select(it => Builder.GetTranslationColumnName(it.DbColumnName))); + if (isSingle) + { + string columnParametersString = string.Join(",", this.DbColumnInfoList.Select(it => base.GetDbColumn(it, Builder.SqlParameterKeyWord + it.DbColumnName))); + ActionMinDate(); + return GetIgnoreSql(string.Format(SqlTemplate, GetTableNameString, columnsString, columnParametersString)); + } + else + { + StringBuilder batchInsetrSql = new StringBuilder(); + int pageSize = 200; + int pageIndex = 1; + if (IsNoPage && IsReturnPkList) + { + pageSize = groupList.Count; + } + int totalRecord = groupList.Count; + int pageCount = (totalRecord + pageSize - 1) / pageSize; + while (pageCount >= pageIndex) + { + batchInsetrSql.AppendFormat(SqlTemplateBatch, GetTableNameString, columnsString); + int i = 0; + foreach (var columns in groupList.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList()) + { + var isFirst = i == 0; + if (isFirst) + { + batchInsetrSql.Append(SqlTemplateBatchUnion); + } + batchInsetrSql.Append("\r\n ( " + string.Join(",", columns.Select(it => + { + if (it.InsertServerTime || it.InsertSql.HasValue() || it.SqlParameterDbType is Type || it?.PropertyType?.Name == "DateOnly" || it?.PropertyType?.Name == "TimeOnly") + { + return GetDbColumn(it, null); + } + object value = null; + if (it.Value is DateTime) + { + var date = ((DateTime)it.Value); + value = date.ToString("O"); + if (date == DateTime.MaxValue) + { + value = "9999-12-31T23:59:59.999999"; + } + } + else if (it.Value is DateTimeOffset) + { + return FormatDateTimeOffset(it.Value); + } + else if (it.Value is decimal v) + { + return v.ToString(CultureInfo.InvariantCulture); + } + else if (it.IsArray && it.Value != null) + { + return FormatValue(it.Value, it.PropertyName, i, it); + } + else if (it.Value is byte[]) + { + return FormatValue(it.Value, it.PropertyName, i, it); + } + else + { + value = it.Value; + } + if (value == null || value == DBNull.Value) + { + return string.Format(SqlTemplateBatchSelect, "NULL"); + } + return string.Format(SqlTemplateBatchSelect, "'" + value.ObjToStringNoTrim().ToSqlFilter() + "'"); + })) + "),"); + ++i; + } + pageIndex++; + batchInsetrSql.Remove(batchInsetrSql.Length - 1, 1).Append("\r\n;\r\n"); + } + return GetIgnoreSql(batchInsetrSql.ToString()); + } + } + + public object FormatValue(object value, string name, int i, DbColumnInfo columnInfo) + { + if (value == null) + { + return "NULL"; + } + else + { + var type = value.GetType(); + if (type == UtilConstants.ByteArrayType || type == UtilConstants.DateType || columnInfo.IsArray || columnInfo.IsJson) + { + var parameterName = this.Builder.SqlParameterKeyWord + name + "_" + i; + var paramter = new SugarParameter(parameterName, value); + if (columnInfo.IsJson) + { + paramter.IsJson = true; + } + if (columnInfo.IsArray) + { + paramter.IsArray = true; + } + this.Parameters.Add(paramter); + return parameterName; + } + else if (type == UtilConstants.ByteArrayType) + { + string bytesString = "0x" + BitConverter.ToString((byte[])value); + return bytesString; + } + else if (type.IsEnum()) + { + if (this.Context.CurrentConnectionConfig.MoreSettings?.TableEnumIsString == true) + { + return value.ToSqlValue(); + } + else + { + return Convert.ToInt64(value); + } + } + else if (type == UtilConstants.DateTimeOffsetType) + { + return FormatDateTimeOffset(value); + } + else if (type == UtilConstants.BoolType) + { + return value.ObjToBool() ? "1" : "0"; + } + else if (type == UtilConstants.StringType || type == UtilConstants.ObjType) + { + return "'" + value.ToString().ToSqlFilter() + "'"; + } + else if (value is decimal v) + { + return v.ToString(CultureInfo.InvariantCulture); + } + else + { + return "'" + value.ToString() + "'"; + } + } + } + public override object FormatValue(object value) + { + var N = string.Empty; + if (value == null) + { + return "NULL"; + } + else + { + var type = UtilMethods.GetUnderType(value.GetType()); + if (type == UtilConstants.DateType) + { + var date = value.ObjToDate(); + if (date < UtilMethods.GetMinDate(this.Context.CurrentConnectionConfig)) + { + date = UtilMethods.GetMinDate(this.Context.CurrentConnectionConfig); + } + return "'" + date.ToString("yyyy-MM-dd HH:mm:ss.fff") + "'"; + } + else if (type == UtilConstants.ByteArrayType) + { + string bytesString = "0x" + Convert.ToHexString((byte[])value); + return bytesString; + } + else if (type.IsEnum()) + { + if (this.Context.CurrentConnectionConfig.MoreSettings?.TableEnumIsString == true) + { + return value.ToSqlValue(); + } + else + { + return Convert.ToInt64(value); + } + } + else if (type == UtilConstants.BoolType) + { + return value.ObjToBool() ? "1" : "0"; + } + else if (type == UtilConstants.StringType || type == UtilConstants.ObjType) + { + return N + "'" + value.ToString().ToSqlFilter() + "'"; + } + else if (type == UtilConstants.DateTimeOffsetType) + { + return FormatDateTimeOffset(value); + } + else if (type == UtilConstants.FloatType) + { + return N + "'" + Convert.ToDouble(value).ToString() + "'"; + } + else if (value is decimal v) + { + return v.ToString(CultureInfo.InvariantCulture); + } + else if (value is double dou) + { + return dou.ToString(CultureInfo.InvariantCulture); + } + else + { + return N + "'" + value.ToString() + "'"; + } + } + } + public override string FormatDateTimeOffset(object value) + { + return "'" + ((DateTimeOffset)value).ToString("o") + "'"; + } + + private string GetIgnoreSql(string sql) + { + if (this.ConflictNothing.Length > 0) + { + sql = sql.Replace(";", $" ON CONFLICT ({string.Join(",", this.ConflictNothing)}) DO NOTHING;"); + } + + return sql; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/PostgreSQL/SqlBuilder/PostgreSQLQueryBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/PostgreSQL/SqlBuilder/PostgreSQLQueryBuilder.cs new file mode 100644 index 000000000..97968651d --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/PostgreSQL/SqlBuilder/PostgreSQLQueryBuilder.cs @@ -0,0 +1,129 @@ +using System.Text; +using System.Text.RegularExpressions; + +namespace SqlSugar +{ + public partial class PostgreSQLQueryBuilder : QueryBuilder + { + #region Sql Template + public override string PageTempalte + { + get + { + /* + SELECT * FROM TABLE WHERE CONDITION ORDER BY ID DESC LIMIT 10 offset 0 + */ + var template = "SELECT {0} FROM {1} {2} {3} {4} LIMIT {6} offset {5}"; + return template; + } + } + public override string DefaultOrderByTemplate + { + get + { + return "ORDER BY NOW() "; + } + } + + #endregion + + #region Common Methods + public override string GetTableNameString + { + get + { + if (this.TableShortName != null && this.Context.CurrentConnectionConfig?.MoreSettings?.PgSqlIsAutoToLower == false) + { + this.TableShortName = Builder.GetTranslationColumnName(this.TableShortName); + } + return base.GetTableNameString; + } + } + public override bool IsComplexModel(string sql) + { + return Regex.IsMatch(sql, @"AS ""\w+\.\w+""") || Regex.IsMatch(sql, @"AS ""\w+\.\w+\.\w+"""); + } + public override string ToSqlString() + { + base.AppendFilter(); + string oldOrderValue = this.OrderByValue; + var isNullOrderValue = Skip == 0 && Take == 1 && oldOrderValue == "ORDER BY NOW() "; + if (isNullOrderValue) + { + this.OrderByValue = null; + } + string result = null; + sql = new StringBuilder(); + sql.AppendFormat(SqlTemplate, GetSelectValue, GetTableNameString, GetWhereValueString, GetGroupByString + HavingInfos, (Skip != null || Take != null) ? null : GetOrderByString); + if (IsCount) { return sql.ToString(); } + if (Skip != null && Take == null) + { + if (this.OrderByValue == "ORDER BY ") this.OrderByValue += GetSelectValue.Split(',')[0]; + result = string.Format(PageTempalte, GetSelectValue, GetTableNameString, GetWhereValueString, GetGroupByString + HavingInfos, (Skip != null || Take != null) ? null : GetOrderByString, Skip.ObjToInt(), long.MaxValue); + } + else if (Skip == null && Take != null) + { + if (this.OrderByValue == "ORDER BY ") this.OrderByValue += GetSelectValue.Split(',')[0]; + result = string.Format(PageTempalte, GetSelectValue, GetTableNameString, GetWhereValueString, GetGroupByString + HavingInfos, GetOrderByString, 0, Take.ObjToInt()); + } + else if (Skip != null && Take != null) + { + if (this.OrderByValue == "ORDER BY ") this.OrderByValue += GetSelectValue.Split(',')[0]; + result = string.Format(PageTempalte, GetSelectValue, GetTableNameString, GetWhereValueString, GetGroupByString + HavingInfos, GetOrderByString, Skip.ObjToInt() > 0 ? Skip.ObjToInt() : 0, Take); + } + else + { + result = sql.ToString(); + } + this.OrderByValue = oldOrderValue; + result = GetSqlQuerySql(result); + if (result.IndexOf("-- No table") > 0) + { + return "-- No table"; + } + if (TranLock != null) + { + result = result + TranLock; + } + //if (result.Contains("uuid_generate_v4()")) + //{ + // result=" CREATE EXTENSION IF NOT EXISTS pgcrypto;CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\"; "+ result; + //} + return result; + } + + #endregion + + #region Get SQL Partial + public override string GetSelectValue + { + get + { + string result = string.Empty; + if (this.SelectValue == null || this.SelectValue is string) + { + result = GetSelectValueByString(); + } + else + { + result = GetSelectValueByExpression(); + } + if (this.SelectType == ResolveExpressType.SelectMultiple) + { + this.SelectCacheKey = this.SelectCacheKey + string.Join("-", this.JoinQueryInfos.Select(it => it.TableName)); + } + if (IsDistinct && result?.TrimStart()?.StartsWith("distinct ") != true) + { + result = "distinct " + result; + } + if (this.SubToListParameters != null && this.SubToListParameters.Count != 0) + { + result = SubToListMethod(result); + } + return result; + } + } + + #endregion + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/PostgreSQL/SqlBuilder/PostgreSQLUpdateBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/PostgreSQL/SqlBuilder/PostgreSQLUpdateBuilder.cs new file mode 100644 index 000000000..662020a31 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/PostgreSQL/SqlBuilder/PostgreSQLUpdateBuilder.cs @@ -0,0 +1,288 @@ +using System.Globalization; +using System.Text; + +namespace SqlSugar +{ + public class PostgreSQLUpdateBuilder : UpdateBuilder + { + public override string SqlTemplateBatch + { + get + { + return @"UPDATE {1} {2} SET {0} FROM ${{0}} "; + } + } + public override string SqlTemplateJoin + { + get + { + return @" (VALUES + {0} + + ) AS T ({2}) WHERE {1} + "; + } + } + + public override string SqlTemplateBatchUnion + { + get + { + return ","; + } + } + + public object FormatValue(object value, string name, int i, DbColumnInfo columnInfo) + { + if (value == null) + { + return "NULL"; + } + else + { + var type = UtilMethods.GetUnderType(value.GetType()); + if (type == UtilConstants.ByteArrayType || type == UtilConstants.DateType || columnInfo.IsArray || columnInfo.IsJson) + { + var parameterName = this.Builder.SqlParameterKeyWord + name + "_" + i; + var paramter = new SugarParameter(parameterName, value); + if (columnInfo.IsJson) + { + paramter.IsJson = true; + } + if (columnInfo.IsArray) + { + paramter.IsArray = true; + } + this.Parameters.Add(paramter); + return parameterName; + } + else if (type == UtilConstants.DateTimeOffsetType) + { + return FormatDateTimeOffset(value); + } + else if (type == UtilConstants.ByteArrayType) + { + string bytesString = "0x" + BitConverter.ToString((byte[])value); + return bytesString; + } + else if (type.IsEnum()) + { + if (this.Context.CurrentConnectionConfig.MoreSettings?.TableEnumIsString == true) + { + return value.ToSqlValue(); + } + else + { + return Convert.ToInt64(value); + } + } + else if (type == UtilConstants.BoolType) + { + return value.ObjToBool() ? "1" : "0"; + } + else if (type == UtilConstants.StringType || type == UtilConstants.ObjType) + { + return "'" + value.ToString().ToSqlFilter() + "'"; + } + else if (value is decimal v) + { + return v.ToString(CultureInfo.InvariantCulture); + } + else if (value is double dou) + { + return dou.ToString(CultureInfo.InvariantCulture); + } + else + { + return "'" + value.ToString() + "'"; + } + } + } + + protected override string TomultipleSqlString(List> groupList) + { + Check.Exception(PrimaryKeys == null || PrimaryKeys.Count == 0, " Update List need Primary key"); + int pageSize = 200; + int pageIndex = 1; + int totalRecord = groupList.Count; + int pageCount = (totalRecord + pageSize - 1) / pageSize; + StringBuilder batchUpdateSql = new StringBuilder(); + while (pageCount >= pageIndex) + { + StringBuilder updateTable = new StringBuilder(); + string setValues = string.Join(",", groupList.First().Where(it => it.IsPrimarykey == false && (it.IsIdentity == false || (IsOffIdentity && it.IsIdentity))).Select(it => + { + if (SetValues.IsValuable()) + { + var setValue = SetValues.Where(sv => sv.Key == Builder.GetTranslationColumnName(it.DbColumnName)); + if (setValue?.Any() == true) + { + return setValue.First().Value; + } + } + var result = string.Format("{0}=T.{0}", Builder.GetTranslationColumnName(it.DbColumnName)); + return result; + })); + string tempColumnValue = string.Join(",", groupList.First().Select(it => + { + if (SetValues.IsValuable()) + { + var setValue = SetValues.Where(sv => sv.Key == Builder.GetTranslationColumnName(it.DbColumnName)); + if (setValue?.Any() == true) + { + return setValue.First().Value; + } + } + var result = Builder.GetTranslationColumnName(it.DbColumnName); + return result; + })); + batchUpdateSql.AppendFormat(SqlTemplateBatch.ToString(), setValues, GetTableNameStringNoWith, TableWithString); + int i = 0; + var tableColumnList = this.Context.DbMaintenance.GetColumnInfosByTableName(GetTableNameStringNoWith); + + foreach (var columns in groupList.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList()) + { + var isFirst = i == 0; + if (!isFirst) + { + updateTable.Append(SqlTemplateBatchUnion); + } + updateTable.Append("\r\n (" + string.Join(",", columns.Select(it => + { + var columnInfo = tableColumnList.FirstOrDefault(x => x.DbColumnName.Equals(it.DbColumnName, StringComparison.OrdinalIgnoreCase)); + var dbType = columnInfo?.DataType; + if (dbType == null) + { + var typeName = it.PropertyType.Name.ToLower(); + if (columnInfo == null && it.PropertyType.IsEnum) + { + if (this.Context.CurrentConnectionConfig?.MoreSettings?.TableEnumIsString != true) + { + typeName = "int"; + } + } + if (typeName == "int32") + typeName = "int"; + if (typeName == "int64") + typeName = "long"; + if (typeName == "int16") + typeName = "short"; + if (typeName == "boolean") + typeName = "bool"; + + var isAnyType = PostgreSQLDbBind.MappingTypesConst.Where(x => x.Value.ToString().Equals(typeName, StringComparison.CurrentCultureIgnoreCase)).Any(); + if (isAnyType) + { + dbType = PostgreSQLDbBind.MappingTypesConst.Where(x => x.Value.ToString().Equals(typeName, StringComparison.CurrentCultureIgnoreCase)).FirstOrDefault().Key; + } + else + { + dbType = "varchar"; + } + } + if (it?.PropertyType?.FullName == "NetTopologySuite.Geometries.Geometry") + { + return string.Format(" {0} ", base.GetDbColumn(it, FormatValue(it.Value, it.DbColumnName, i + (pageIndex - 1) * 100000, it)), dbType); + } + return string.Format("CAST({0} AS {1})", base.GetDbColumn(it, FormatValue(it.Value, it.DbColumnName, i + (pageIndex - 1) * 100000, it)), dbType); + + })) + ")"); + ++i; + } + pageIndex++; + updateTable.Append("\r\n"); + string whereString = null; + if (this.WhereValues.HasValue()) + { + foreach (var item in WhereValues) + { + var isFirst = whereString == null; + whereString += (isFirst ? null : " AND "); + whereString += item; + } + } + else if (PrimaryKeys.HasValue()) + { + foreach (var item in PrimaryKeys) + { + var isFirst = whereString == null; + whereString += (isFirst ? null : " AND "); + whereString += string.Format("{0}.{1}=T.{1}", GetTableNameStringNoWith, Builder.GetTranslationColumnName(item)); + } + } + var format = string.Format(SqlTemplateJoin, updateTable, whereString, tempColumnValue); + batchUpdateSql.Replace("${0}", format); + batchUpdateSql.Append(';'); + } + batchUpdateSql = GetBatchUpdateSql(batchUpdateSql); + return batchUpdateSql.ToString(); + } + + private StringBuilder GetBatchUpdateSql(StringBuilder batchUpdateSql) + { + if (ReSetValueBySqlExpListType == null && ReSetValueBySqlExpList != null) + { + var result = batchUpdateSql.ToString(); + foreach (var item in ReSetValueBySqlExpList) + { + var dbColumnName = item.Value.DbColumnName; + if (item.Value.Type == ReSetValueBySqlExpListModelType.List) + { + result = result.Replace($"{dbColumnName}=T.{dbColumnName}", $"{dbColumnName}={GetTableNameString}.{dbColumnName}{item.Value.Sql}T.{dbColumnName}"); + } + else + { + if (item.Value?.Sql?.StartsWith("( CASE WHEN") == true) + { + result = result.Replace($"{dbColumnName}=T.{dbColumnName}", $"{dbColumnName}={item.Value.Sql.Replace(" \"", $" {Builder.GetTranslationColumnName(this.TableName)}.\"")}"); + } + else + { + result = result.Replace($"{dbColumnName}=T.{dbColumnName}", $"{dbColumnName}={item.Value.Sql.Replace(dbColumnName, $"{Builder.GetTranslationColumnName(this.TableName)}.{dbColumnName}")}"); + } + } + batchUpdateSql = new StringBuilder(result); + } + } + + return batchUpdateSql; + } + protected override string GetJoinUpdate(string columnsString, ref string whereString) + { + if (this.JoinInfos?.Count > 1) + { + return this.GetJoinUpdateMany(columnsString, whereString); + } + var formString = $" {Builder.GetTranslationColumnName(this.TableName)} AS {Builder.GetTranslationColumnName(this.ShortName)} "; + var joinString = ""; + foreach (var item in this.JoinInfos) + { + whereString += " AND " + item.JoinWhere; + joinString += $"\r\n FROM {Builder.GetTranslationColumnName(item.TableName)} {Builder.GetTranslationColumnName(item.ShortName)} "; + } + var tableName = formString + "\r\n "; + columnsString = columnsString.Replace(Builder.GetTranslationColumnName(this.ShortName) + ".", "") + joinString; + return string.Format(SqlTemplate, tableName, columnsString, whereString); + } + private string GetJoinUpdateMany(string columnsString, string where) + { + var formString = $" {Builder.GetTranslationColumnName(this.TableName)} AS {Builder.GetTranslationColumnName(this.ShortName)} "; + var joinString = ""; + var i = 0; + foreach (var item in this.JoinInfos) + { + var whereString = " ON " + item.JoinWhere; + joinString += $"\r\n JOIN {Builder.GetTranslationColumnName(item.TableName)} {Builder.GetTranslationColumnName(item.ShortName)} "; + joinString = joinString + whereString; + i++; + } + var tableName = Builder.GetTranslationColumnName(this.TableName) + "\r\n "; + columnsString = columnsString.Replace(Builder.GetTranslationColumnName(this.ShortName) + ".", "") + $" FROM {Builder.GetTranslationColumnName(this.TableName)} {Builder.GetTranslationColumnName(this.ShortName)}\r\n " + joinString; + return string.Format(SqlTemplate, tableName, columnsString, where); + } + public override string FormatDateTimeOffset(object value) + { + return "'" + ((DateTimeOffset)value).ToString("o") + "'"; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/QuestDB/CodeFirst/QuestDBCodeFirst.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/QuestDB/CodeFirst/QuestDBCodeFirst.cs new file mode 100644 index 000000000..23058ae0a --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/QuestDB/CodeFirst/QuestDBCodeFirst.cs @@ -0,0 +1,101 @@ +using System.Reflection; + +namespace SqlSugar +{ + public class QuestDBCodeFirst : CodeFirstProvider + { + protected override void ExistLogicEnd(List dbColumns) + { + foreach (EntityColumnInfo column in dbColumns) + { + if (column.DefaultValue != null) + { + this.Context.DbMaintenance.AddDefaultValue(column.DbTableName, column.DbColumnName, column.DefaultValue.ToSqlValue()); + } + } + } + public override void NoExistLogic(EntityInfo entityInfo) + { + var tableName = GetTableName(entityInfo); + //Check.Exception(entityInfo.Columns.Where(it => it.IsPrimarykey).Count() > 1, "Use Code First ,The primary key must not exceed 1"); + List columns = new List(); + if (entityInfo.Columns.HasValue()) + { + foreach (var item in entityInfo.Columns.Where(it => it.IsIgnore == false)) + { + DbColumnInfo dbColumnInfo = this.EntityColumnToDbColumn(entityInfo, tableName, item); + columns.Add(dbColumnInfo); + } + if (entityInfo.IsCreateTableFiledSort) + { + columns = columns.OrderBy(c => c.CreateTableFieldSort).ToList(); + } + } + columns = columns.OrderBy(it => it.IsPrimarykey ? 0 : 1).ToList(); + foreach (var propety in entityInfo.Type.GetProperties()) + { + var timeAttr = propety.GetCustomAttribute(); + if (timeAttr != null) + { + var colName = columns.FirstOrDefault(it => it.PropertyName == propety.Name)?.DbColumnName; + tableName += $"_TIMESTAMP({colName}) PARTITION BY {timeAttr.DateType} "; + } + } + this.Context.DbMaintenance.CreateTable(tableName, columns, true); + } + protected override DbColumnInfo EntityColumnToDbColumn(EntityInfo entityInfo, string tableName, EntityColumnInfo item) + { + var propertyType = UtilMethods.GetUnderType(item.PropertyInfo); + var result = new DbColumnInfo() + { + TableId = entityInfo.Columns.IndexOf(item), + DbColumnName = item.DbColumnName.HasValue() ? item.DbColumnName : item.PropertyName, + IsPrimarykey = item.IsPrimarykey, + IsIdentity = item.IsIdentity, + TableName = tableName, + IsNullable = item.IsNullable, + DefaultValue = item.DefaultValue, + ColumnDescription = item.ColumnDescription, + PropertyName = item.PropertyName, + Length = item.Length, + CreateTableFieldSort = item.CreateTableFieldSort + }; + if (propertyType == UtilConstants.DecType) + { + result.Scale = item.DecimalDigits; + result.DecimalDigits = item.DecimalDigits; + } + GetDbType(item, propertyType, result); + if (item.IsJson) + { + result.DataType = "string"; + } + if (result.DataType.Equals("varchar", StringComparison.CurrentCultureIgnoreCase) && result.Length == 0) + { + result.Length = 1; + } + return result; + } + + protected override void ConvertColumns(List dbColumns) + { + foreach (var item in dbColumns) + { + if (item.DataType == "DateTime") + { + item.Length = 0; + } + } + } + + protected override void ChangeKey(EntityInfo entityInfo, string tableName, EntityColumnInfo item) + { + this.Context.DbMaintenance.UpdateColumn(tableName, EntityColumnToDbColumn(entityInfo, tableName, item)); + if (!item.IsPrimarykey) + this.Context.DbMaintenance.DropConstraint(tableName, null); + if (item.IsPrimarykey) + this.Context.DbMaintenance.AddPrimaryKey(tableName, item.DbColumnName); + } + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/QuestDB/DbBind/QuestDBDbBind.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/QuestDB/DbBind/QuestDBDbBind.cs new file mode 100644 index 000000000..c2f7d57c4 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/QuestDB/DbBind/QuestDBDbBind.cs @@ -0,0 +1,144 @@ +namespace SqlSugar +{ + public class QuestDBDbBind : DbBindProvider + { + public override string GetDbTypeName(string csharpTypeName) + { + if (csharpTypeName == UtilConstants.ByteArrayType.Name) + return "binary"; + + var result = base.GetDbTypeName(csharpTypeName); + if (csharpTypeName == "Single") + { + result = "double"; + } + return result; + } + public override string GetPropertyTypeName(string dbTypeName) + { + dbTypeName = dbTypeName.ToLower(); + var propertyTypes = MappingTypes.Where(it => it.Value.ToString().Equals(dbTypeName, StringComparison.CurrentCultureIgnoreCase) || it.Key.Equals(dbTypeName, StringComparison.CurrentCultureIgnoreCase)); + if (propertyTypes == null) + { + return "other"; + } + else if (dbTypeName == "xml" || dbTypeName == "string" || dbTypeName == "jsonb" || dbTypeName == "json") + { + return "string"; + } + else if (dbTypeName == "bpchar")//数据库char datatype 查询出来的时候是 bpchar + { + return "char"; + } + if (dbTypeName == "byte[]") + { + return "byte[]"; + } + else if (propertyTypes?.Any() != true) + { + if (dbTypeName.StartsWith('_')) + { + var dbTypeName2 = dbTypeName.TrimStart('_'); + return MappingTypes.Where(it => it.Value.ToString().Equals(dbTypeName2, StringComparison.CurrentCultureIgnoreCase) || it.Key.Equals(dbTypeName2, StringComparison.CurrentCultureIgnoreCase)).Select(it => it.Value + "[]").First(); + } + Check.ThrowNotSupportedException(string.Format(" \"{0}\" Type NotSupported, DbBindProvider.GetPropertyTypeName error.", dbTypeName)); + return null; + } + else if (propertyTypes.First().Value == CSharpDataType.byteArray) + { + return "byte[]"; + } + else + { + return propertyTypes.First().Value.ToString(); + } + } + public override List> MappingTypes + { + get + { + var extService = this.Context.CurrentConnectionConfig.ConfigureExternalServices; + if (extService?.AppendDataReaderTypeMappings.HasValue() == true) + { + return extService.AppendDataReaderTypeMappings.Union(MappingTypesConst).ToList(); + } + else + { + return MappingTypesConst; + } + } + } + public static List> MappingTypesConst = new List>(){ + + new KeyValuePair("byte",CSharpDataType.@byte), + new KeyValuePair("short",CSharpDataType.@short), + new KeyValuePair("smallint",CSharpDataType.@short), + new KeyValuePair("int",CSharpDataType.@int), + new KeyValuePair("integer",CSharpDataType.@int), + new KeyValuePair("long",CSharpDataType.@long), + new KeyValuePair("bigint",CSharpDataType.@long), + new KeyValuePair("long256",CSharpDataType.@long), + new KeyValuePair("float",CSharpDataType.@float), + new KeyValuePair("real",CSharpDataType.@float), + new KeyValuePair("double",CSharpDataType.@double), + new KeyValuePair("double precision",CSharpDataType.@int), + new KeyValuePair("double",CSharpDataType.@decimal), + new KeyValuePair("decimal",CSharpDataType.@decimal), + new KeyValuePair("path",CSharpDataType.@decimal), + new KeyValuePair("point",CSharpDataType.@decimal), + new KeyValuePair("polygon",CSharpDataType.@decimal), + + //new KeyValuePair("int",CSharpDataType.@bool), + new KeyValuePair("boolean",CSharpDataType.@bool), + new KeyValuePair("bool",CSharpDataType.@bool), + new KeyValuePair("box",CSharpDataType.@bool), + new KeyValuePair("bytea",CSharpDataType.@bool), + + new KeyValuePair("string",CSharpDataType.@string), + new KeyValuePair("varchar",CSharpDataType.@string), + new KeyValuePair("character varying",CSharpDataType.@string), + new KeyValuePair("geometry",CSharpDataType.@string), + new KeyValuePair("name",CSharpDataType.@string), + new KeyValuePair("text",CSharpDataType.@string), + new KeyValuePair("symbol",CSharpDataType.@string), + new KeyValuePair("char",CSharpDataType.@string), + new KeyValuePair("character",CSharpDataType.@string), + new KeyValuePair("cidr",CSharpDataType.@string), + new KeyValuePair("circle",CSharpDataType.@string), + new KeyValuePair("tsquery",CSharpDataType.@string), + new KeyValuePair("tsvector",CSharpDataType.@string), + new KeyValuePair("txid_snapshot",CSharpDataType.@string), + new KeyValuePair("string",CSharpDataType.Guid), + new KeyValuePair("uuid",CSharpDataType.Guid), + new KeyValuePair("xml",CSharpDataType.@string), + new KeyValuePair("json",CSharpDataType.@string), + + new KeyValuePair("interval",CSharpDataType.@decimal), + new KeyValuePair("lseg",CSharpDataType.@decimal), + new KeyValuePair("macaddr",CSharpDataType.@decimal), + new KeyValuePair("money",CSharpDataType.@decimal), + new KeyValuePair("timestamp",CSharpDataType.DateTime), + new KeyValuePair("timestamptz",CSharpDataType.DateTime), + new KeyValuePair("timestamp without time zone",CSharpDataType.DateTime), + new KeyValuePair("date",CSharpDataType.DateTime), + new KeyValuePair("time",CSharpDataType.DateTime), + new KeyValuePair("time with time zone",CSharpDataType.DateTime), + new KeyValuePair("timetz",CSharpDataType.DateTime), + new KeyValuePair("time without time zone",CSharpDataType.DateTime), + + new KeyValuePair("binary",CSharpDataType.byteArray), + new KeyValuePair("bit varying",CSharpDataType.byteArray), + new KeyValuePair("varbit",CSharpDataType.@byte), + new KeyValuePair("time",CSharpDataType.TimeSpan), + //new KeyValuePair("public.geometry",CSharpDataType.@object), + new KeyValuePair("geohash",CSharpDataType.@string) + }; + public override List StringThrow + { + get + { + return new List() { "int32", "datetime", "decimal", "double", "byte" }; + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/QuestDB/DbFirst/QuestDBDbFirst.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/QuestDB/DbFirst/QuestDBDbFirst.cs new file mode 100644 index 000000000..943dcebf9 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/QuestDB/DbFirst/QuestDBDbFirst.cs @@ -0,0 +1,6 @@ +namespace SqlSugar +{ + public class QuestDBDbFirst : DbFirstProvider + { + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/QuestDB/DbMaintenance/QuestDBDbMaintenance.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/QuestDB/DbMaintenance/QuestDBDbMaintenance.cs new file mode 100644 index 000000000..b601890a9 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/QuestDB/DbMaintenance/QuestDBDbMaintenance.cs @@ -0,0 +1,503 @@ +using System.Text.RegularExpressions; + +namespace SqlSugar +{ + public class QuestDBDbMaintenance : DbMaintenanceProvider + { + #region DML + protected override string GetDataBaseSql + { + get + { + return CreateDataBaseSql; + } + } + protected override string GetColumnInfosByTableNameSql + { + get + { + string schema = GetSchema(); + string sql = @"SHOW COLUMNS FROM {0}"; + return sql; + } + } + + protected override string GetTableInfoListSql + { + get + { + var schema = GetSchema(); + return @"SHOW TABLES"; + } + } + protected override string GetViewInfoListSql + { + get + { + return "select * from (select 1 as id) t where id=0"; + } + } + #endregion + + #region DDL + protected override string CreateDataBaseSql + { + get + { + return "select * from (select 1 as id) t where id=0"; + } + } + protected override string AddPrimaryKeySql + { + get + { + return ""; + } + } + protected override string AddColumnToTableSql + { + get + { + return "ALTER TABLE {0} ADD COLUMN {1} {2}"; + } + } + protected override string AlterColumnToTableSql + { + get + { + throw new NotSupportedException("Alter Column "); + } + } + protected override string BackupDataBaseSql + { + get + { + return "BACKUP DATABASE"; + } + } + protected override string CreateTableSql + { + get + { + return "CREATE TABLE {0}(\r\n{1})"; + } + } + protected override string CreateTableColumn + { + get + { + return "{0} {1}{2} {3} {4} {5}"; + } + } + protected override string TruncateTableSql + { + get + { + return "TRUNCATE TABLE {0}"; + } + } + protected override string BackupTableSql + { + get + { + return "create table {0} as (select * from {1} limit {2} offset 0)"; + } + } + protected override string DropTableSql + { + get + { + return "DROP TABLE {0}"; + } + } + protected override string DropColumnToTableSql + { + get + { + return "ALTER TABLE {0} DROP COLUMN {1}"; + } + } + protected override string DropConstraintSql + { + get + { + return " "; + } + } + protected override string RenameColumnSql + { + get + { + return "ALTER TABLE {0} RENAME COLUMN {1} TO {2}"; + } + } + protected override string AddColumnRemarkSql => " "; + + protected override string DeleteColumnRemarkSql => " "; + + protected override string IsAnyColumnRemarkSql { get { throw new NotSupportedException(); } } + + protected override string AddTableRemarkSql => " "; + + protected override string DeleteTableRemarkSql => " "; + + protected override string IsAnyTableRemarkSql { get { throw new NotSupportedException(); } } + + protected override string RenameTableSql => "alter table 表名 {0} to {1}"; + + protected override string CreateIndexSql + { + get + { + return "CREATE {3} INDEX Index_{0}_{2} ON {0} ({1})"; + } + } + protected override string AddDefaultValueSql + { + get + { + return "ALTER TABLE {0} ALTER COLUMN {1} SET DEFAULT {2}"; + } + } + protected override string IsAnyIndexSql + { + get + { + return " Select count(1) from (SELECT to_regclass('{0}') as c ) t where t.c is not null"; + } + } + protected override string IsAnyProcedureSql => throw new NotImplementedException(); + #endregion + + #region Check + protected override string CheckSystemTablePermissionsSql + { + get + { + return "select 1 "; + } + } + #endregion + + #region Scattered + protected override string CreateTableNull + { + get + { + return "DEFAULT NULL"; + } + } + protected override string CreateTableNotNull + { + get + { + return "NOT NULL"; + } + } + protected override string CreateTablePirmaryKey + { + get + { + return "PRIMARY KEY"; + } + } + protected override string CreateTableIdentity + { + get + { + return "serial"; + } + } + #endregion + + #region Methods + public override bool IsAnySystemTablePermissions() + { + return true; + } + public override void AddIndex(EntityInfo entityInfo) + { + if (entityInfo.Indexs != null) + { + foreach (var item in entityInfo.Indexs) + { + CreateIndex(entityInfo.DbTableName, item.IndexFields.Select(it => it.Key).ToArray(), item.IsUnique); + } + } + } + public override bool CreateIndex(string tableName, string[] columnNames, bool isUnique = false) + { + if (isUnique) + { + this.Context.Ado.ExecuteCommand($"ALTER TABLE {tableName} DEDUP ENABLE UPSERT KEYS({string.Join(",", columnNames)})"); + return true; + } + var columnInfos = this.Context.Ado.SqlQuery("SHOW COLUMNS FROM '" + tableName + "'"); + foreach (var columnInfo in columnInfos) + { + if (columnNames.Any(z => z.EqualCase(columnInfo.Column))) + { + if (!columnInfo.Type.EqualCase("SYMBOL")) + { + Check.ExceptionEasy(true, "Only the SYMBOL type can be indexed", $"字段{columnInfo.Column} 不是SYMBOL并且实体是string才能添加索引,CodeFirst需要指定类型: SYMBOL"); + } + if (columnInfo.Indexed == false) + { + var indexSql = $"ALTER TABLE '{tableName}' ALTER COLUMN {columnInfo.Column} ADD INDEX "; + this.Context.Ado.ExecuteCommand(indexSql); + } + } + } + return true; + } + public override bool CreateIndex(string tableName, string[] columnNames, string IndexName, bool isUnique = false) + { + if (isUnique) + throw new Exception("no support unique index"); + return CreateIndex(tableName, columnNames, isUnique); + } + + public override bool CreateUniqueIndex(string tableName, string[] columnNames) + { + throw new Exception("no support unique index"); + } + public override bool IsAnyIndex(string indexName) + { + return false; + } + public override List GetTableInfoList(bool isCache = true) + { + var dt = this.Context.Ado.GetDataTable(GetTableInfoListSql); + List result = new List(); + foreach (System.Data.DataRow dr in dt.Rows) + { + DbTableInfo di = new DbTableInfo(); + di.Name = dr[0] + ""; + if (!di.Name.Contains("sys.") && !di.Name.IsIn("telemetry", "telemetry_config")) + { + result.Add(di); + } + + } + return result; + } + public override bool AddDefaultValue(string tableName, string columnName, string defaultValue) + { + return base.AddDefaultValue(this.SqlBuilder.GetTranslationTableName(tableName), this.SqlBuilder.GetTranslationTableName(columnName), defaultValue); + } + public override bool AddColumnRemark(string columnName, string tableName, string description) + { + //tableName = this.SqlBuilder.GetTranslationTableName(tableName); + //string sql = string.Format(this.AddColumnRemarkSql, columnName, tableName, description); + //this.Context.Ado.ExecuteCommand(sql); + return true; + } + public override bool AddTableRemark(string tableName, string description) + { + //tableName = this.SqlBuilder.GetTranslationTableName(tableName); + //return base.AddTableRemark(tableName, description); + return true; + } + public override bool UpdateColumn(string tableName, DbColumnInfo columnInfo) + { + //no support + return false; + } + public override bool AddPrimaryKey(string tableName, string columnName) + { + return true; + } + //protected override string GetUpdateColumnSql(string tableName, DbColumnInfo columnInfo) + //{ + // string columnName = this.SqlBuilder.GetTranslationColumnName(columnInfo.DbColumnName); + // tableName = this.SqlBuilder.GetTranslationTableName(tableName); + // string dataSize = GetSize(columnInfo); + // string dataType = columnInfo.DataType; + // if (!string.IsNullOrEmpty(dataType)) + // { + // dataType = " type " + dataType; + // } + // string nullType = ""; + // string primaryKey = null; + // string identity = null; + // string result = string.Format(this., tableName, columnName, dataType, dataSize, nullType, primaryKey, identity); + // return result; + //} + + /// + ///by current connection string + /// + /// + /// + /// + public override bool CreateDatabase(string databaseName, string databaseDirectory = null) + { + if (databaseDirectory != null) + { + if (!FileHelper.IsExistDirectory(databaseDirectory)) + { + FileHelper.CreateDirectory(databaseDirectory); + } + } + var oldDatabaseName = this.Context.Ado.Connection.Database; + var connection = this.Context.CurrentConnectionConfig.ConnectionString; + connection = connection.Replace(oldDatabaseName, "postgres"); + var newDb = new SqlSugarClient(new ConnectionConfig() + { + DbType = this.Context.CurrentConnectionConfig.DbType, + IsAutoCloseConnection = true, + ConnectionString = connection + }); + if (!GetDataBaseList(newDb).Any(it => it.Equals(databaseName, StringComparison.CurrentCultureIgnoreCase))) + { + newDb.Ado.ExecuteCommand(string.Format(CreateDataBaseSql, this.SqlBuilder.SqlTranslationLeft + databaseName + this.SqlBuilder.SqlTranslationRight, databaseDirectory)); + } + return true; + } + public override bool AddRemark(EntityInfo entity) + { + var db = this.Context; + var columns = entity.Columns.Where(it => it.IsIgnore == false).ToList(); + + foreach (var item in columns) + { + if (item.ColumnDescription != null) + { + db.DbMaintenance.AddColumnRemark(item.DbColumnName, item.DbTableName, item.ColumnDescription); + + } + } + //table remak + if (entity.TableDescription != null) + { + db.DbMaintenance.AddTableRemark(entity.DbTableName, entity.TableDescription); + } + return true; + } + public override bool CreateTable(string tableName, List columns, bool isCreatePrimaryKey = true) + { + var splitSql = ""; + if (tableName.Contains("_TIMESTAMP(")) + { + splitSql = Regex.Match(tableName, @"_TIMESTAMP\(.+$").Value; + tableName = tableName.Replace(splitSql, ""); + } + if (columns.HasValue()) + { + foreach (var item in columns) + { + if (item.DbColumnName.Equals("GUID", StringComparison.CurrentCultureIgnoreCase) && item.Length == 0) + { + item.Length = 10; + } + } + } + string sql = GetCreateTableSql(tableName, columns); + string primaryKeyInfo = null; + if (columns.Any(it => it.IsPrimarykey) && isCreatePrimaryKey) + { + primaryKeyInfo = string.Format(", Primary key({0})", string.Join(",", columns.Where(it => it.IsPrimarykey).Select(it => this.SqlBuilder.GetTranslationColumnName(it.DbColumnName.ToLower())))); + + } + sql = sql.Replace("$PrimaryKey", primaryKeyInfo); + this.Context.Ado.ExecuteCommand(sql + splitSql.TrimStart('_')); + return true; + } + protected override string GetCreateTableSql(string tableName, List columns) + { + List columnArray = new List(); + Check.Exception(columns.IsNullOrEmpty(), "No columns found "); + foreach (var item in columns) + { + string columnName = item.DbColumnName; + string dataType = item.DataType; + //if (dataType == "varchar" && item.Length == 0) + //{ + // item.Length = 1; + //} + //if (dataType == "uuid") + //{ + // item.Length = 50; + // dataType = "varchar"; + //} + string dataSize = ""; + //if (item.DecimalDigits > 0&&item.Length>0 && dataType == "numeric") + //{ + // dataSize = $"({item.Length},{item.DecimalDigits})"; + //} + string nullType = ""; + string primaryKey = null; + string addItem = string.Format(this.CreateTableColumn, this.SqlBuilder.GetTranslationColumnName(columnName.ToLower()), dataType, dataSize, nullType, primaryKey, ""); + //if (item.IsIdentity) + //{ + // string length = dataType.Substring(dataType.Length - 1); + // string identityDataType = "serial" + length; + // addItem = addItem.Replace(dataType, identityDataType); + //} + columnArray.Add(addItem); + } + string tableString = string.Format(this.CreateTableSql, this.SqlBuilder.GetTranslationTableName(tableName), string.Join(",\r\n", columnArray)); + return tableString; + } + public override bool IsAnyConstraint(string constraintName) + { + throw new NotSupportedException("PgSql IsAnyConstraint NotSupportedException"); + } + public override bool BackupDataBase(string databaseName, string fullFileName) + { + Check.ThrowNotSupportedException("PgSql BackupDataBase NotSupported"); + return false; + } + + public override List GetColumnInfosByTableName(string tableName, bool isCache = true) + { + var sql = String.Format(GetColumnInfosByTableNameSql, tableName); + List result = new List(); + var dt = this.Context.Ado.GetDataTable(sql); + foreach (System.Data.DataRow column in dt.Rows) + { + DbColumnInfo dbColumnInfo = new DbColumnInfo(); + dbColumnInfo.DbColumnName = column[0] + ""; + dbColumnInfo.DataType = column[1] + ""; + result.Add(dbColumnInfo); + } + return result; + } + #endregion + + #region Helper + private string GetSchema() + { + var schema = "public"; + if (System.Text.RegularExpressions.Regex.IsMatch(this.Context.CurrentConnectionConfig.ConnectionString.ToLower(), "searchpath=")) + { + var regValue = System.Text.RegularExpressions.Regex.Match(this.Context.CurrentConnectionConfig.ConnectionString.ToLower(), @"searchpath\=(\w+)").Groups[1].Value; + if (regValue.HasValue()) + { + schema = regValue; + } + } + else if (System.Text.RegularExpressions.Regex.IsMatch(this.Context.CurrentConnectionConfig.ConnectionString.ToLower(), "search path=")) + { + var regValue = System.Text.RegularExpressions.Regex.Match(this.Context.CurrentConnectionConfig.ConnectionString.ToLower(), @"search path\=(\w+)").Groups[1].Value; + if (regValue.HasValue()) + { + schema = regValue; + } + } + + return schema; + } + + #endregion + + #region HelperClass + internal class QuestDbColumn + { + public string Column { get; set; } + public string Type { get; set; } + public bool Indexed { get; set; } + } + #endregion + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/QuestDB/Queryable/QuestDBQueryable.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/QuestDB/Queryable/QuestDBQueryable.cs new file mode 100644 index 000000000..d3e4c27d0 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/QuestDB/Queryable/QuestDBQueryable.cs @@ -0,0 +1,67 @@ +namespace SqlSugar +{ + public class QuestDBQueryable : QueryableProvider + { + public override ISugarQueryable With(string withString) + { + return this; + } + + public override ISugarQueryable PartitionBy(string groupFileds) + { + if (this.QueryBuilder.Take == 1) + { + this.QueryBuilder.Take = null; + } + this.QueryBuilder.PartitionByValue = groupFileds; + return this; + } + } + public class QuestDBQueryable : QueryableProvider + { + public new ISugarQueryable With(string withString) + { + return this; + } + } + public class QuestDBQueryable : QueryableProvider + { + + } + public class QuestDBQueryable : QueryableProvider + { + + } + public class QuestDBQueryable : QueryableProvider + { + + } + public class QuestDBQueryable : QueryableProvider + { + + } + public class QuestDBQueryable : QueryableProvider + { + + } + public class QuestDBQueryable : QueryableProvider + { + + } + public class QuestDBQueryable : QueryableProvider + { + + } + public class QuestDBQueryable : QueryableProvider + { + + } + public class QuestDBQueryable : QueryableProvider + { + + } + public class QuestDBQueryable : QueryableProvider + { + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/QuestDB/QuestDBProvider.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/QuestDB/QuestDBProvider.cs new file mode 100644 index 000000000..9009ba421 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/QuestDB/QuestDBProvider.cs @@ -0,0 +1,214 @@ +using Npgsql; + +using NpgsqlTypes; + +using System.Data; +using System.Data.Common; + +namespace SqlSugar +{ + public partial class QuestDBProvider : AdoProvider + { + public QuestDBProvider() { } + public override IDbConnection Connection + { + get + { + if (base._DbConnection == null) + { + try + { + var npgsqlConnectionString = base.Context.CurrentConnectionConfig.ConnectionString; + base._DbConnection = new NpgsqlConnection(npgsqlConnectionString); + } + catch (Exception ex) + { + Check.Exception(true, ErrorMessage.ConnnectionOpen, ex.Message); + } + } + return base._DbConnection; + } + set + { + base._DbConnection = value; + } + } + + public override void BeginTran(string transactionName) + { + base.BeginTran(); + } + /// + /// Only SqlServer + /// + /// + /// + public override void BeginTran(IsolationLevel iso, string transactionName) + { + base.BeginTran(iso); + } + public override IDataAdapter GetAdapter() + { + return new NpgsqlDataAdapter(); + } + public override DbCommand GetCommand(string sql, SugarParameter[] parameters) + { + if (sql == Environment.NewLine) + { + sql = "SELECT 0"; + } + NpgsqlCommand sqlCommand = new NpgsqlCommand(sql, (NpgsqlConnection)this.Connection); + sqlCommand.CommandType = this.CommandType; + sqlCommand.CommandTimeout = this.CommandTimeOut; + if (this.Transaction != null) + { + sqlCommand.Transaction = (NpgsqlTransaction)this.Transaction; + } + if (parameters.HasValue()) + { + IDataParameter[] ipars = ToIDbDataParameter(parameters); + sqlCommand.Parameters.AddRange((NpgsqlParameter[])ipars); + } + CheckConnection(); + return sqlCommand; + } + /// + /// Check connection + /// + public override void CheckConnection() + { + this.CheckConnectionBefore(this.Connection); + if (this.Connection.State != ConnectionState.Open) + { + try + { + int i = 0; + while (i < 15) + { + try + { + //QuestDb loss problem + this.Connection.Open(); + break; + } + catch + { + i++; + } + } + } + catch (Exception ex) + { + Check.Exception(true, ErrorMessage.ConnnectionOpen, ex.Message); + } + } + this.CheckConnectionAfter(this.Connection); + } + public override void SetCommandToAdapter(IDataAdapter dataAdapter, DbCommand command) + { + ((NpgsqlDataAdapter)dataAdapter).SelectCommand = (NpgsqlCommand)command; + } + /// + /// if mysql return MySqlParameter[] pars + /// if sqlerver return SqlParameter[] pars ... + /// + /// + /// + public override IDataParameter[] ToIDbDataParameter(params SugarParameter[] parameters) + { + if (parameters == null || parameters.Length == 0) return null; + NpgsqlParameter[] result = new NpgsqlParameter[parameters.Length]; + int index = 0; + var isVarchar = this.Context.IsVarchar(); + foreach (var parameter in parameters) + { + if (parameter.Value == null) parameter.Value = DBNull.Value; + if (parameter.Value is System.Data.SqlTypes.SqlDateTime && parameter.DbType == System.Data.DbType.AnsiString) + { + parameter.DbType = System.Data.DbType.DateTime; + parameter.Value = DBNull.Value; + } + var sqlParameter = new NpgsqlParameter(); + sqlParameter.ParameterName = parameter.ParameterName; + sqlParameter.Size = parameter.Size; + sqlParameter.Value = parameter.Value; + sqlParameter.DbType = parameter.DbType; + sqlParameter.Direction = parameter.Direction; + if (parameter.IsJson) + { + sqlParameter.DbType = System.Data.DbType.String; + } + if (sqlParameter.Direction == 0) + { + sqlParameter.Direction = ParameterDirection.Input; + } + result[index] = sqlParameter; + if (sqlParameter.Direction.IsIn(ParameterDirection.Output, ParameterDirection.InputOutput, ParameterDirection.ReturnValue)) + { + if (this.OutputParameters == null) this.OutputParameters = new List(); + this.OutputParameters.RemoveAll(it => it.ParameterName == sqlParameter.ParameterName); + this.OutputParameters.Add(sqlParameter); + } + if (sqlParameter.DbType == System.Data.DbType.String) + { + sqlParameter.DbType = System.Data.DbType.AnsiString; + } + else if (sqlParameter.Value is DateTime && sqlParameter.DbType == System.Data.DbType.AnsiString) + { + sqlParameter.DbType = System.Data.DbType.DateTime; + } + else if (sqlParameter.DbType == System.Data.DbType.Decimal) + { + sqlParameter.DbType = System.Data.DbType.Double; + sqlParameter.Value = Convert.ToDouble(sqlParameter.Value); + } + else if (sqlParameter.DbType == System.Data.DbType.Guid) + { + sqlParameter.DbType = System.Data.DbType.String; + if (sqlParameter.Value != null) + { + sqlParameter.Value = (sqlParameter.Value).ToString(); + } + } + else if (sqlParameter.DbType == System.Data.DbType.Boolean) + { + sqlParameter.DbType = System.Data.DbType.String; + if (sqlParameter.Value != null) + { + sqlParameter.Value = sqlParameter.Value.ObjToString().ToLower(); + } + } + ++index; + } + return result; + } + + + static readonly Dictionary ArrayMapping = new Dictionary() + { + { typeof(int[]),NpgsqlDbType.Integer}, + { typeof(short[]),NpgsqlDbType.Smallint}, + { typeof(long[]),NpgsqlDbType.Bigint}, + { typeof(decimal[]),NpgsqlDbType.Numeric}, + { typeof(char[]),NpgsqlDbType.Text}, + { typeof(byte[]),NpgsqlDbType.Bytea}, + { typeof(bool[]),NpgsqlDbType.Boolean}, + {typeof(DateTime[]),NpgsqlDbType.Date}, + {typeof(float[]),NpgsqlDbType.Real}, + + + { typeof(int?[]),NpgsqlDbType.Integer}, + { typeof(short?[]),NpgsqlDbType.Smallint}, + { typeof(long?[]),NpgsqlDbType.Bigint}, + { typeof(decimal?[]),NpgsqlDbType.Numeric}, + { typeof(char?[]),NpgsqlDbType.Text}, + { typeof(byte?[]),NpgsqlDbType.Bytea}, + { typeof(bool?[]),NpgsqlDbType.Boolean}, + {typeof(DateTime?[]),NpgsqlDbType.Date}, + + + { typeof(string[]), NpgsqlDbType.Text}, + {typeof(float?[]),NpgsqlDbType.Real}, + }; + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/QuestDB/SqlBuilder/QuestDBBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/QuestDB/SqlBuilder/QuestDBBuilder.cs new file mode 100644 index 000000000..a9fce5130 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/QuestDB/SqlBuilder/QuestDBBuilder.cs @@ -0,0 +1,113 @@ +namespace SqlSugar +{ + public class QuestDBBuilder : SqlBuilderProvider + { + public override string SqlTranslationLeft + { + get + { + return "\""; + } + } + public override string SqlTranslationRight + { + get + { + return "\""; + } + } + public override string SqlDateNow + { + get + { + //https://questdb.io/docs/guides/working-with-timestamps-timezones/#using-utc-offset-for-conversions + //https://questdb.io/docs/reference/function/date-time/#to_timezone + //SELECT + // now(), --2022-10-21T07:19:50.680134Z + // systimestamp(), --2022-10-21T07:19:50.680278Z + // sysdate(), --2022-10-21T07:19:50.679Z + // to_timezone(NOW(), 'Asia/ShangHai'), --2022-10-21T15:19:50.680134Z + // to_timezone(NOW(), 'HKT'), --2022-10-21T15:19:50.680134Z + // to_timezone(NOW(),'+08') --2022-10-21T15:19:50.680134Z + return $"to_timezone(NOW(),'{TimeZoneInfo.Local.BaseUtcOffset.Hours:00}')"; + } + } + public override string FullSqlDateNow + { + get + { + //https://questdb.io/docs/guides/working-with-timestamps-timezones/#using-utc-offset-for-conversions + //https://questdb.io/docs/reference/function/date-time/#to_timezone + //SELECT + // now(), --2022-10-21T07:19:50.680134Z + // systimestamp(), --2022-10-21T07:19:50.680278Z + // sysdate(), --2022-10-21T07:19:50.679Z + // to_timezone(NOW(), 'Asia/ShangHai'), --2022-10-21T15:19:50.680134Z + // to_timezone(NOW(), 'HKT'), --2022-10-21T15:19:50.680134Z + // to_timezone(NOW(),'+08') --2022-10-21T15:19:50.680134Z + return $"SELECT to_timezone(NOW(),'{TimeZoneInfo.Local.BaseUtcOffset.Hours:00}')"; + } + } + + public bool isAutoToLower => false; + public override string GetTranslationColumnName(string propertyName) + { + if (propertyName.Contains('.') && !propertyName.Contains(SqlTranslationLeft)) + { + return string.Join(".", propertyName.Split('.').Select(it => $"{SqlTranslationLeft}{it.ToLower(isAutoToLower)}{SqlTranslationRight}")); + } + + if (propertyName.Contains(SqlTranslationLeft)) return propertyName; + else + return SqlTranslationLeft + propertyName.ToLower(isAutoToLower) + SqlTranslationRight; + } + + //public override string GetNoTranslationColumnName(string name) + //{ + // return name.TrimEnd(Convert.ToChar(SqlTranslationRight)).TrimStart(Convert.ToChar(SqlTranslationLeft)).ToLower(); + //} + public override string GetTranslationColumnName(string entityName, string propertyName) + { + Check.ArgumentNullException(entityName, string.Format(null, ErrorMessage.ObjNotExistCompositeFormat, "Table Name")); + Check.ArgumentNullException(propertyName, string.Format(null, ErrorMessage.ObjNotExistCompositeFormat, "Column Name")); + var context = this.Context; + var mappingInfo = context + .MappingColumns + .FirstOrDefault(it => + it.EntityName.Equals(entityName, StringComparison.CurrentCultureIgnoreCase) && + it.PropertyName.Equals(propertyName, StringComparison.CurrentCultureIgnoreCase)); + return (mappingInfo == null ? SqlTranslationLeft + propertyName.ToLower(isAutoToLower) + SqlTranslationRight : SqlTranslationLeft + mappingInfo.DbColumnName.ToLower(isAutoToLower) + SqlTranslationRight); + } + + public override string GetTranslationTableName(string name) + { + Check.ArgumentNullException(name, string.Format(null, ErrorMessage.ObjNotExistCompositeFormat, "Table Name")); + var context = this.Context; + + var mappingInfo = context + .MappingTables + .FirstOrDefault(it => it.EntityName.Equals(name, StringComparison.CurrentCultureIgnoreCase)); + name = (mappingInfo == null ? name : mappingInfo.DbTableName); + if (name.Contains('.') && !name.Contains('(') && !name.Contains("\".\"")) + { + return string.Join(".", name.ToLower(isAutoToLower).Split('.').Select(it => SqlTranslationLeft + it + SqlTranslationRight)); + } + else if (name.Contains('(')) + { + return name; + } + else if (name.Contains(SqlTranslationLeft) && name.Contains(SqlTranslationRight)) + { + return name; + } + else + { + return SqlTranslationLeft + name.ToLower(isAutoToLower).TrimEnd('"').TrimStart('"') + SqlTranslationRight; + } + } + public override string GetUnionFomatSql(string sql) + { + return " ( " + sql + " ) "; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/QuestDB/SqlBuilder/QuestDBDeleteBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/QuestDB/SqlBuilder/QuestDBDeleteBuilder.cs new file mode 100644 index 000000000..904eb59fd --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/QuestDB/SqlBuilder/QuestDBDeleteBuilder.cs @@ -0,0 +1,7 @@ +namespace SqlSugar +{ + public class QuestDBDeleteBuilder : DeleteBuilder + { + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/QuestDB/SqlBuilder/QuestDBExpressionContext.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/QuestDB/SqlBuilder/QuestDBExpressionContext.cs new file mode 100644 index 000000000..42c905bf0 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/QuestDB/SqlBuilder/QuestDBExpressionContext.cs @@ -0,0 +1,379 @@ +namespace SqlSugar +{ + public class QuestDBExpressionContext : ExpressionContext, ILambdaExpressions + { + public SqlSugarProvider Context { get; set; } + public QuestDBExpressionContext() + { + base.DbMehtods = new QuestDBMethod(); + } + public override string SqlTranslationLeft + { + get + { + return "\""; + } + } + public override string SqlTranslationRight + { + get + { + return "\""; + } + } + public override string GetTranslationText(string name) + { + return SqlTranslationLeft + name.ToLower(isAutoToLower) + SqlTranslationRight; + } + public bool isAutoToLower + { + get + { + return false; + } + } + public override string GetTranslationTableName(string entityName, bool isMapping = true) + { + Check.ArgumentNullException(entityName, string.Format(null, ErrorMessage.ObjNotExistCompositeFormat, "Table Name")); + if (IsTranslationText(entityName)) return entityName; + isMapping = isMapping && this.MappingTables.HasValue(); + var isComplex = entityName.Contains(UtilConstants.Dot); + if (isMapping && isComplex) + { + var columnInfo = entityName.Split(UtilConstants.DotChar); + var mappingInfo = this.MappingTables.FirstOrDefault(it => it.EntityName.Equals(columnInfo.Last(), StringComparison.CurrentCultureIgnoreCase)); + if (mappingInfo != null) + { + columnInfo[columnInfo.Length - 1] = mappingInfo.EntityName; + } + return string.Join(UtilConstants.Dot, columnInfo.Select(it => GetTranslationText(it))); + } + else if (isMapping) + { + var mappingInfo = this.MappingTables.FirstOrDefault(it => it.EntityName.Equals(entityName, StringComparison.CurrentCultureIgnoreCase)); + + var tableName = mappingInfo?.DbTableName + ""; + if (tableName.Contains('.')) + { + tableName = string.Join(UtilConstants.Dot, tableName.Split(UtilConstants.DotChar).Select(it => GetTranslationText(it))); + return tableName; + } + + return SqlTranslationLeft + (mappingInfo == null ? entityName : mappingInfo.DbTableName).ToLower(isAutoToLower) + SqlTranslationRight; + } + else if (isComplex) + { + return string.Join(UtilConstants.Dot, entityName.Split(UtilConstants.DotChar).Select(it => GetTranslationText(it))); + } + else + { + return GetTranslationText(entityName); + } + } + public override string GetTranslationColumnName(string columnName) + { + Check.ArgumentNullException(columnName, string.Format(null, ErrorMessage.ObjNotExistCompositeFormat, "Column Name")); + if (columnName.Substring(0, 1) == this.SqlParameterKeyWord) + { + return columnName; + } + if (IsTranslationText(columnName)) return columnName; + if (columnName.Contains(UtilConstants.Dot)) + { + return string.Join(UtilConstants.Dot, columnName.Split(UtilConstants.DotChar).Select(it => GetTranslationText(it))); + } + else + { + return GetTranslationText(columnName); + } + } + public override string GetDbColumnName(string entityName, string propertyName) + { + if (this.MappingColumns.HasValue()) + { + var mappingInfo = this.MappingColumns.SingleOrDefault(it => it.EntityName == entityName && it.PropertyName == propertyName); + return (mappingInfo == null ? propertyName : mappingInfo.DbColumnName).ToLower(isAutoToLower); + } + else + { + return propertyName.ToLower(isAutoToLower); + } + } + + public string GetValue(object entityValue) + { + if (entityValue == null) + return null; + var type = UtilMethods.GetUnderType(entityValue.GetType()); + if (UtilConstants.NumericalTypes.Contains(type)) + { + return entityValue.ToString(); + } + else if (type == UtilConstants.DateType) + { + return this.DbMehtods.ToDate(new MethodCallExpressionModel() + { + Args = new System.Collections.Generic.List() { + new MethodCallExpressionArgs(){ MemberName=$"'{entityValue}'" } + } + }); + } + else + { + return this.DbMehtods.ToString(new MethodCallExpressionModel() + { + Args = new System.Collections.Generic.List() { + new MethodCallExpressionArgs(){ MemberName=$"'{entityValue}'" } + } + }); + } + } + } + public class QuestDBMethod : DefaultDbMethod, IDbMethods + { + //public override string DateIsSameByType(MethodCallExpressionModel model) + //{ + // var parameter = model.Args[0]; + // var parameter2 = model.Args[1]; + // var parameter3 = model.Args[2]; + + // return string.Format(" (DATEDIFF('{2}',{0},{1})=0) ", parameter.MemberName, parameter2.MemberName, parameter3.MemberValue); + //} + public override string AggregateCount(MethodCallExpressionModel model) + { + return " COUNT(*) "; + } + public override string Contains(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + return string.Format(" ({0}~{1} ) ", parameter.MemberName, parameter2.MemberName); + } + + public override string DateDiff(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + var parameter3 = model.Args[2]; + return string.Format(" DATEDIFF('{0}',{1},{2}) ", parameter.MemberValue.ObjToString().ToLower().First(), parameter2.MemberName, parameter3.MemberName); + } + public override string TrueValue() + { + return "1"; + } + public override string FalseValue() + { + return "0"; + } + //public override string DateDiff(MethodCallExpressionModel model) + //{ + // var parameter = (DateType)(Enum.Parse(typeof(DateType), model.Args[0].MemberValue.ObjToString())); + // var begin = model.Args[1].MemberName; + // var end = model.Args[2].MemberName; + // switch (parameter) + // { + // case DateType.Year: + // return $" ( DATE_PART('Year', {end} ) - DATE_PART('Year', {begin}) )"; + // case DateType.Month: + // return $" ( ( DATE_PART('Year', {end} ) - DATE_PART('Year', {begin}) ) * 12 + (DATE_PART('month', {end}) - DATE_PART('month', {begin})) )"; + // case DateType.Day: + // return $" ( DATE_PART('day', {end} - {begin}) )"; + // case DateType.Hour: + // return $" ( ( DATE_PART('day', {end} - {begin}) ) * 24 + DATE_PART('hour', {end} - {begin} ) )"; + // case DateType.Minute: + // return $" ( ( ( DATE_PART('day', {end} - {begin}) ) * 24 + DATE_PART('hour', {end} - {begin} ) ) * 60 + DATE_PART('minute', {end} - {begin} ) )"; + // case DateType.Second: + // return $" ( ( ( DATE_PART('day', {end} - {begin}) ) * 24 + DATE_PART('hour', {end} - {begin} ) ) * 60 + DATE_PART('minute', {end} - {begin} ) ) * 60 + DATE_PART('minute', {end} - {begin} )"; + // case DateType.Millisecond: + // break; + // default: + // break; + // } + // throw new Exception(parameter + " datediff no support"); + //} + public override string IIF(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + var parameter3 = model.Args[2]; + if (parameter.Type == UtilConstants.BoolType) + { + parameter.MemberName = parameter.MemberName.ToString().Replace("=1", "=true"); + parameter2.MemberName = false; + parameter3.MemberName = true; + } + return string.Format("( CASE WHEN {0} THEN {1} ELSE {2} END )", parameter.MemberName, parameter2.MemberName, parameter3.MemberName); + } + public override string DateValue(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + var format = parameter2.MemberValue.ObjToString(); + return string.Format(" {0}({1}) ", format, parameter.MemberName); + } + + + public override string StartsWith(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + return string.Format(" ({0} like concat({1},'%')) ", parameter.MemberName, parameter2.MemberName); + } + + public override string EndsWith(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + return string.Format(" ({0} like concat('%',{1}))", parameter.MemberName, parameter2.MemberName); + } + + public override string DateIsSameDay(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + return string.Format(" ( to_char({0},'yyyy-MM-dd')=to_char({1},'yyyy-MM-dd') ) ", parameter.MemberName, parameter2.MemberName); ; + } + + public override string HasValue(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format("( {0} IS NOT NULL )", parameter.MemberName); + } + + public override string DateIsSameByType(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + var parameter3 = model.Args[2]; + DateType dateType = (DateType)parameter3.MemberValue; + var format = "yyyy-MM-dd"; + switch (dateType) + { + case DateType.Year: + format = "yyyy"; + break; + case DateType.Month: + format = "yyyy-MM"; + break; + case DateType.Day: + break; + case DateType.Hour: + format = "yyyy-MM-dd HH"; + break; + case DateType.Second: + format = "yyyy-MM-dd HH:mm:ss"; + break; + case DateType.Minute: + format = "yyyy-MM-dd HH:mm"; + break; + case DateType.Millisecond: + format = "yyyy-MM-dd HH:mm.ms"; + break; + default: + break; + } + return string.Format(" ( to_char({0},'{2}')=to_char({1},'{2}') ) ", parameter.MemberName, parameter2.MemberName, format); + } + public override string ToDateShort(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format("to_date (to_str({0}, 'yyyy-MM-dd'),'yyyy-MM-dd')", parameter.MemberName); + } + + public override string ToDate(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format("CAST({0} AS timestamp)", parameter.MemberName); + } + public override string DateAddByType(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + var parameter3 = model.Args[2]; + return string.Format(" (DATEADD('{0}',{2}, {1})) ", parameter3.MemberValue.ObjToString().ToLower().First(), parameter.MemberName, parameter2.MemberValue); + } + + public override string DateAddDay(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + return string.Format(" (DATEADD('d',{1}, {0})) ", parameter.MemberName, parameter2.MemberName); + } + + public override string ToInt32(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS INT)", parameter.MemberName); + } + + public override string ToInt64(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS long)", parameter.MemberName); + } + + public override string ToString(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS VARCHAR)", parameter.MemberName); + } + + public override string ToGuid(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS VARCHAR)", parameter.MemberName); + } + + public override string ToDouble(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS double )", parameter.MemberName); + } + + public override string ToBool(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS boolean)", parameter.MemberName); + } + + public override string ToDecimal(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS double )", parameter.MemberName); + } + + public override string Length(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" LENGTH({0})", parameter.MemberName); + } + public override string MergeString(params string[] strings) + { + return " concat(" + string.Join(",", strings).Replace("+", "") + ") "; + } + public override string IsNull(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter1 = model.Args[1]; + return string.Format("(CASE WHEN {0} IS NULL THEN {1} ELSE {0} END)", parameter.MemberName, parameter1.MemberName); + } + public override string GetDate() + { + return "now()"; + } + public override string GetRandom() + { + return "now()"; + } + + public override string EqualTrue(string fieldName) + { + return "( " + fieldName + "=true )"; + } + + public override string GetDateString(string dateValue, string format) + { + return $" to_str({dateValue},'{format}') "; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/QuestDB/SqlBuilder/QuestDBFastBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/QuestDB/SqlBuilder/QuestDBFastBuilder.cs new file mode 100644 index 000000000..c6f11fe79 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/QuestDB/SqlBuilder/QuestDBFastBuilder.cs @@ -0,0 +1,121 @@ +using Npgsql; + +using NpgsqlTypes; + +using System.Data; + +namespace SqlSugar +{ + public class QuestDBFastBuilder : FastBuilder, IFastBuilder + { + public static Dictionary PgSqlType = UtilMethods.EnumToDictionary(); + private EntityInfo entityInfo; + + public QuestDBFastBuilder(EntityInfo entityInfo) + { + this.entityInfo = entityInfo; + } + + public override string UpdateSql { get; set; } = @"UPDATE {1} SET {0} FROM {2} AS TE WHERE {3} +"; + + //public virtual async Task UpdateByTempAsync(string tableName, string tempName, string[] updateColumns, string[] whereColumns) + //{ + // Check.ArgumentNullException(!updateColumns.Any(), "update columns count is 0"); + // Check.ArgumentNullException(!whereColumns.Any(), "where columns count is 0"); + // var sets = string.Join(",", updateColumns.Select(it => $"TM.{it}=TE.{it}")); + // var wheres = string.Join(",", whereColumns.Select(it => $"TM.{it}=TE.{it}")); + // string sql = string.Format(UpdateSql, sets, tableName, tempName, wheres); + // return await this.Context.Ado.ExecuteCommandAsync(sql); + //} + public async Task ExecuteBulkCopyAsync(DataTable dt) + { + Check.ExceptionEasy( + "Nuget install: SqlSugar.QuestDb.RestAPI, use: await db.RestApi().BulkCopyAsync(list)", + "Nuget安装:SqlSugar.QuestDb.RestAPI ,QuestDb中请使用:await db.RestApi().BulkCopyAsync(list) 注意是db.RestApi()不是db.Fastest"); + return await Task.FromResult(0).ConfigureAwait(false); + } + + private void BulkCopy(DataTable dt, string copyString, NpgsqlConnection conn, List columns) + { + if (conn.State == ConnectionState.Closed) + conn.Open(); + List columnViews = new List(); + foreach (DataColumn item in dt.Columns) + { + ColumnView result = new ColumnView(); + result.DbColumnInfo = columns.FirstOrDefault(it => it.DbColumnName.EqualCase(item.ColumnName)); + result.DataColumn = item; + result.EntityColumnInfo = this.entityInfo.Columns.FirstOrDefault(it => it.DbColumnName.EqualCase(item.ColumnName)); + var key = result.DbColumnInfo?.DataType?.ToLower(); + if (result.DbColumnInfo == null) + { + result.Type = null; + } + else if (PgSqlType.TryGetValue(key, out NpgsqlDbType value)) + { + result.Type = value; + } + else if (key?.First() == '_') + { + var type = PgSqlType[key.Substring(1)]; + result.Type = NpgsqlDbType.Array | type; + } + else + { + result.Type = null; + } + columnViews.Add(result); + } + using (var writer = conn.BeginBinaryImport(copyString)) + { + foreach (DataRow row in dt.Rows) + { + writer.StartRow(); + foreach (var column in columnViews) + { + var value = row[column.DataColumn.ColumnName]; + if (value == null) + { + value = DBNull.Value; + } + if (column.Type == null) + { + writer.Write(value); + } + else + { + writer.Write(value, column.Type.Value); + } + } + } + writer.Complete(); + } + } + + + public override async Task UpdateByTempAsync(string tableName, string tempName, string[] updateColumns, string[] whereColumns) + { + var sqlquerybulder = this.Context.Queryable().SqlBuilder; + Check.ArgumentNullException(updateColumns.Length == 0, "update columns count is 0"); + Check.ArgumentNullException(whereColumns.Length == 0, "where columns count is 0"); + var sets = string.Join(",", updateColumns.Select(it => $"{sqlquerybulder.GetTranslationColumnName(it)}=TE.{sqlquerybulder.GetTranslationColumnName(it)}")); + var wheres = string.Join(" AND ", whereColumns.Select(it => $"{tableName}.{sqlquerybulder.GetTranslationColumnName(it)}=TE.{sqlquerybulder.GetTranslationColumnName(it)}")); + string sql = string.Format(UpdateSql, sets, tableName, tempName, wheres); + return await Context.Ado.ExecuteCommandAsync(sql).ConfigureAwait(false); + } + public override async Task CreateTempAsync(DataTable dt) + { + await Context.Queryable().Where(it => false).AS(dt.TableName).Select(" * into temp mytemptable").ToListAsync().ConfigureAwait(false); + dt.TableName = "mytemptable"; + } + + public class ColumnView + { + public DataColumn DataColumn { get; set; } + public EntityColumnInfo EntityColumnInfo { get; set; } + public DbColumnInfo DbColumnInfo { get; set; } + public NpgsqlDbType? Type { get; set; } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/QuestDB/SqlBuilder/QuestDBInsertBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/QuestDB/SqlBuilder/QuestDBInsertBuilder.cs new file mode 100644 index 000000000..2a6a9e3e0 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/QuestDB/SqlBuilder/QuestDBInsertBuilder.cs @@ -0,0 +1,115 @@ +using System.Text; + +namespace SqlSugar +{ + public class QuestDBInsertBuilder : InsertBuilder + { + public override string SqlTemplate + { + get + { + if (IsReturnIdentity) + { + return @"INSERT INTO {0} + ({1}) + VALUES + ({2})"; + } + else + { + return @"INSERT INTO {0} + ({1}) + VALUES + ({2}) ;"; + + } + } + } + public override string SqlTemplateBatch => "INSERT INTO {0} ({1})"; + public override string SqlTemplateBatchUnion => " VALUES "; + + public override string SqlTemplateBatchSelect => " {0} "; + + public override string ToSqlString() + { + if (IsNoInsertNull) + { + DbColumnInfoList = DbColumnInfoList.Where(it => it.Value != null).ToList(); + } + var groupList = DbColumnInfoList.GroupBy(it => it.TableId).ToList(); + var isSingle = groupList.Count == 1; + string columnsString = string.Join(",", groupList.First().Select(it => Builder.GetTranslationColumnName(it.DbColumnName))); + //if (isSingle) + //{ + // string columnParametersString = string.Join(",", this.DbColumnInfoList.Select(it => + // { + // var spk = Builder.SqlParameterKeyWord + it.DbColumnName; + // //if (it.Value is DateTime) + // //{ + // // return $"to_timestamp('{it.Value.ObjToString("yyyy-MM-ddTHH:mm:ss")}', 'yyyy-MM-ddTHH:mm:ss')"; + // //} + // return GetDbColumn(it, spk); + // } + + // )); + // ActionMinDate(); + // return string.Format(SqlTemplate, GetTableNameString, columnsString, columnParametersString); + //} + //else + { + StringBuilder batchInsetrSql = new StringBuilder(); + int pageSize = 200; + int pageIndex = 1; + int totalRecord = groupList.Count; + int pageCount = (totalRecord + pageSize - 1) / pageSize; + while (pageCount >= pageIndex) + { + batchInsetrSql.AppendFormat(SqlTemplateBatch, GetTableNameString, columnsString); + int i = 0; + foreach (var columns in groupList.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList()) + { + var isFirst = i == 0; + if (isFirst) + { + batchInsetrSql.Append(SqlTemplateBatchUnion); + } + batchInsetrSql.Append("\r\n ( " + string.Join(",", columns.Select(it => + { + if (it.InsertServerTime || it.InsertSql.HasValue()) + { + return GetDbColumn(it, null); + } + object value = null; + if (it.Value is DateTime) + { + return $" cast('{it.Value.ObjToDate().ToString("yyyy-MM-dd HH:mm:ss.ffffff")}' as timestamp)"; + } + else if (it.Value is int || it.Value is long || it.Value is short || it.Value is short || it.Value is byte || it.Value is double) + { + return it.Value; + } + else if (it.Value is bool) + { + return it.Value; + //return it.Value.ObjToString().ToLower(); + } + else + { + value = it.Value; + } + if (value == null || value == DBNull.Value) + { + return string.Format(SqlTemplateBatchSelect, "NULL"); + } + return string.Format(SqlTemplateBatchSelect, "'" + value.ObjToString().ToSqlFilter() + "'"); + })) + "),"); + ++i; + } + pageIndex++; + batchInsetrSql.Remove(batchInsetrSql.Length - 1, 1).Append("\r\n;\r\n"); + } + return batchInsetrSql.ToString(); + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/QuestDB/SqlBuilder/QuestDBQueryBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/QuestDB/SqlBuilder/QuestDBQueryBuilder.cs new file mode 100644 index 000000000..19810f242 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/QuestDB/SqlBuilder/QuestDBQueryBuilder.cs @@ -0,0 +1,169 @@ +using System.Text; +using System.Text.RegularExpressions; + +namespace SqlSugar +{ + public partial class QuestDBQueryBuilder : QueryBuilder + { + #region Sql Template + public override string PageTempalte + { + get + { + /* + SELECT * FROM TABLE WHERE CONDITION ORDER BY ID DESC LIMIT 0,10 + */ + var template = "SELECT {0} FROM {1} {2} {3} {4} LIMIT {5},{6}"; + if (this.SampleBy.HasValue()) + { + template = "SELECT {0} FROM {1} {2} " + this.SampleBy + " {3} {4} LIMIT {5},{6}"; + } + return template; + } + } + public override string DefaultOrderByTemplate + { + get + { + return "ORDER BY NOW() "; + } + } + + #endregion + + #region Common Methods + public override bool IsComplexModel(string sql) + { + return Regex.IsMatch(sql, @"AS \`\w+\.\w+\`") || Regex.IsMatch(sql, @"AS \`\w+\.\w+\.\w+\`"); + } + public override string ToSqlString() + { + base.AppendFilter(); + string oldOrderValue = this.OrderByValue; + string result = null; + sql = new StringBuilder(); + sql.AppendFormat(SqlTemplate, GetSelectValue, GetTableNameString, GetWhereValueString, GetGroupByString + HavingInfos, (Skip != null || Take != null) ? null : GetOrderByString); + if (IsCount) { return sql.ToString(); } + if (Skip != null && Take == null) + { + if (this.OrderByValue == "ORDER BY ") this.OrderByValue += GetSelectValue.Split(',')[0]; + result = string.Format(PageTempalte, GetSelectValue, GetTableNameString, GetWhereValueString, GetGroupByString + HavingInfos, (Skip != null || Take != null) ? null : GetOrderByString, Skip.ObjToInt(), long.MaxValue); + } + else if (Skip == null && Take != null) + { + if (this.OrderByValue == "ORDER BY ") this.OrderByValue += GetSelectValue.Split(',')[0]; + result = string.Format(PageTempalte, GetSelectValue, GetTableNameString, GetWhereValueString, GetGroupByString + HavingInfos, GetOrderByString, 0, Take.ObjToInt()); + } + else if (Skip != null && Take != null) + { + if (Skip == 0 && Take == 1 && this.OrderByValue == "ORDER BY NOW() ") + { + this.OrderByValue = null; + } + if (this.OrderByValue == "ORDER BY ") this.OrderByValue += GetSelectValue.Split(',')[0]; + result = string.Format(PageTempalte, GetSelectValue, GetTableNameString, GetWhereValueString, GetGroupByString + HavingInfos, GetOrderByString, Skip.ObjToInt() > 0 ? Skip.ObjToInt() : 0, Skip + Take); + } + else + { + result = sql.ToString(); + } + this.OrderByValue = oldOrderValue; + result = GetSqlQuerySql(result); + if (result.IndexOf("-- No table") > 0) + { + return "-- No table"; + } + if (TranLock != null) + { + result = result + TranLock; + } + if (this.PartitionByValue.HasValue()) + { + var addSql = " LATEST ON " + UtilMethods.ReplaceFirstMatch(this.PartitionByValue, ",", " PARTITION BY "); + result = result + addSql; + } + return result; + } + private string ToCountSqlString() + { + //base.AppendFilter(); + string oldOrderValue = this.OrderByValue; + string result = null; + sql = new StringBuilder(); + sql.AppendFormat(SqlTemplate, "Count(*)", GetTableNameString, GetWhereValueString, GetGroupByString + HavingInfos, (Skip != null || Take != null) ? null : GetOrderByString); + if (IsCount) + { + if (sql.ToString().Contains("-- No table")) + { + return "-- No table"; + } + return sql.ToString(); + } + if (Skip != null && Take == null) + { + if (this.OrderByValue == "ORDER BY ") this.OrderByValue += GetSelectValue.Split(',')[0]; + result = string.Format(PageTempalte, GetSelectValue, GetTableNameString, GetWhereValueString, GetGroupByString + HavingInfos, (Skip != null || Take != null) ? null : GetOrderByString, Skip.ObjToInt(), long.MaxValue); + } + else if (Skip == null && Take != null) + { + if (this.OrderByValue == "ORDER BY ") this.OrderByValue += GetSelectValue.Split(',')[0]; + result = string.Format(PageTempalte, GetSelectValue, GetTableNameString, GetWhereValueString, GetGroupByString + HavingInfos, GetOrderByString, 0, Take.ObjToInt()); + } + else if (Skip != null && Take != null) + { + if (this.OrderByValue == "ORDER BY ") this.OrderByValue += GetSelectValue.Split(',')[0]; + result = string.Format(PageTempalte, GetSelectValue, GetTableNameString, GetWhereValueString, GetGroupByString + HavingInfos, GetOrderByString, Skip.ObjToInt() > 0 ? Skip.ObjToInt() : 0, Take); + } + else + { + result = sql.ToString(); + } + this.OrderByValue = oldOrderValue; + return result; + } + public override string ToCountSql(string sql) + { + if (this.GroupByValue.HasValue()) + { + return base.ToCountSql(sql); + } + else + { + return ToCountSqlString(); + } + } + #endregion + + #region Get SQL Partial + public override string GetSelectValue + { + get + { + string result = string.Empty; + if (this.SelectValue == null || this.SelectValue is string) + { + result = GetSelectValueByString(); + } + else + { + result = GetSelectValueByExpression(); + } + if (this.SelectType == ResolveExpressType.SelectMultiple) + { + this.SelectCacheKey = this.SelectCacheKey + string.Join("-", this.JoinQueryInfos.Select(it => it.TableName)); + } + if (IsDistinct) + { + result = " DISTINCT " + result; + } + if (this.SubToListParameters != null && this.SubToListParameters.Count != 0) + { + result = SubToListMethod(result); + } + return result; + } + } + + #endregion + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/QuestDB/SqlBuilder/QuestDBUpdateBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/QuestDB/SqlBuilder/QuestDBUpdateBuilder.cs new file mode 100644 index 000000000..54c7ff815 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/QuestDB/SqlBuilder/QuestDBUpdateBuilder.cs @@ -0,0 +1,103 @@ +using System.Text; + +namespace SqlSugar +{ + public class QuestDBUpdateBuilder : UpdateBuilder + { + public override string ReSetValueBySqlExpListType { get; set; } = "questdb"; + protected override string TomultipleSqlString(List> groupList) + { + StringBuilder sb = new StringBuilder(); + int i = 0; + sb.AppendLine(string.Join("\r\n", groupList.Select(t => + { + var updateTable = string.Format("UPDATE {0} SET", base.GetTableNameStringNoWith); + var setValues = string.Join(",", t.Where(s => !s.IsPrimarykey).Select(m => GetOracleUpdateColums(i, m, false)).ToArray()); + var pkList = t.Where(s => s.IsPrimarykey).ToList(); + List whereList = new List(); + foreach (var item in pkList) + { + var isFirst = pkList.First() == item; + var whereString = ""; + whereString += GetOracleUpdateColums(i, item, true); + whereList.Add(whereString); + } + i++; + return string.Format("{0} {1} WHERE {2};", updateTable, setValues, string.Join(" AND", whereList)); + }).ToArray())); + return sb.ToString(); + } + + private string GetOracleUpdateColums(int i, DbColumnInfo m, bool iswhere) + { + return string.Format(" \"{0}\"={1}", m.DbColumnName.ToUpper(), base.GetDbColumn(m, FormatValue(i, m.DbColumnName, m.Value, iswhere))); + } + + public object FormatValue(int i, string name, object value, bool iswhere) + { + if (value == null) + { + return "NULL"; + } + else + { + var type = UtilMethods.GetUnderType(value.GetType()); + if (type == UtilConstants.DateType && iswhere == false) + { + var date = value.ObjToDate(); + if (date < UtilMethods.GetMinDate(this.Context.CurrentConnectionConfig)) + { + date = UtilMethods.GetMinDate(this.Context.CurrentConnectionConfig); + } + if (this.Context.CurrentConnectionConfig?.MoreSettings?.DisableMillisecond == true) + { + return "'" + date.ToString("yyyy-MM-dd HH:mm:ss") + "'"; + } + else + { + return "'" + date.ToString("yyyy-MM-dd HH:mm:ss.fff") + "'"; + } + } + else if (type == UtilConstants.DateType && iswhere) + { + var parameterName = this.Builder.SqlParameterKeyWord + name + i; + this.Parameters.Add(new SugarParameter(parameterName, value)); + return parameterName; + } + else if (type.IsEnum()) + { + if (this.Context.CurrentConnectionConfig.MoreSettings?.TableEnumIsString == true) + { + return value.ToSqlValue(); + } + else + { + return Convert.ToInt64(value); + } + } + else if (type == UtilConstants.ByteArrayType) + { + var parameterName = this.Builder.SqlParameterKeyWord + name + i; + this.Parameters.Add(new SugarParameter(parameterName, value)); + return parameterName; + } + else if (value is decimal || value is double || value is int || value is long || value is short || value is short || value is byte) + { + return value; + } + else if (value is bool) + { + return value.ObjToString().ToLower(); + } + else if (type == UtilConstants.StringType || type == UtilConstants.ObjType) + { + return "'" + value.ToString().ToSqlFilter() + "'"; + } + else + { + return "'" + value.ToString() + "'"; + } + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/SqlServer/CodeFirst/SqlServerCodeFirst.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/SqlServer/CodeFirst/SqlServerCodeFirst.cs new file mode 100644 index 000000000..699864a11 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/SqlServer/CodeFirst/SqlServerCodeFirst.cs @@ -0,0 +1,50 @@ +namespace SqlSugar +{ + public class SqlServerCodeFirst : CodeFirstProvider + { + protected override string GetTableName(EntityInfo entityInfo) + { + var table = this.Context.EntityMaintenance.GetTableName(entityInfo.EntityName); + var tableArray = table.Split('.'); + var noFormat = table.Split(']').Length == 1; + if (tableArray.Length > 1 && noFormat) + { + var dbMain = new SqlServerDbMaintenance() { Context = this.Context }; + var schmes = dbMain.GetSchemas(); + if (!schmes.Any(it => it.EqualCase(tableArray.First()))) + { + return tableArray.Last(); + } + else + { + return dbMain.SqlBuilder.GetTranslationTableName(table); + } + } + else + { + return table; + } + } + + protected override void GetDbType(EntityColumnInfo item, Type propertyType, DbColumnInfo result) + { + if (!string.IsNullOrEmpty(item.DataType)) + { + result.DataType = item.DataType; + } + else if (propertyType.IsEnum()) + { + result.DataType = this.Context.Ado.DbBind.GetDbTypeName(item.Length > 9 ? UtilConstants.LongType.Name : UtilConstants.IntType.Name); + } + else + { + var name = GetType(propertyType.Name); + result.DataType = this.Context.Ado.DbBind.GetDbTypeName(name); + if (result.DataType == "varbinary" && item.Length == 0) + { + result.DataType = "varbinary(max)"; + } + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/SqlServer/DbBind/SqlServerDbBind.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/SqlServer/DbBind/SqlServerDbBind.cs new file mode 100644 index 000000000..6e10be28e --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/SqlServer/DbBind/SqlServerDbBind.cs @@ -0,0 +1,69 @@ +namespace SqlSugar +{ + public class SqlServerDbBind : DbBindProvider + { + public override string GetDbTypeName(string csharpTypeName) + { + if (csharpTypeName == nameof(DateTimeOffset)) + { + return nameof(DateTimeOffset); + } + else + { + return base.GetDbTypeName(csharpTypeName); + } + } + public override List> MappingTypes + { + get + { + var extService = this.Context.CurrentConnectionConfig.ConfigureExternalServices; + if (extService?.AppendDataReaderTypeMappings.HasValue() == true) + { + return extService.AppendDataReaderTypeMappings.Union(MappingTypesConst).ToList(); + } + else + { + return MappingTypesConst; + } + } + } + public static List> MappingTypesConst = new List>() + { + new KeyValuePair("int",CSharpDataType.@int), + new KeyValuePair("varchar",CSharpDataType.@string), + new KeyValuePair("nvarchar",CSharpDataType.@string), + new KeyValuePair("sql_variant",CSharpDataType.@string), + new KeyValuePair("varcharmax",CSharpDataType.@string), + new KeyValuePair("text",CSharpDataType.@string), + new KeyValuePair("char",CSharpDataType.@string), + new KeyValuePair("ntext",CSharpDataType.@string), + new KeyValuePair("nchar",CSharpDataType.@string), + new KeyValuePair("hierarchyid",CSharpDataType.@string), + new KeyValuePair("bigint",CSharpDataType.@long), + new KeyValuePair("bit",CSharpDataType.@bool), + new KeyValuePair("datetime",CSharpDataType.DateTime), + new KeyValuePair("time",CSharpDataType.DateTime), + new KeyValuePair("smalldatetime",CSharpDataType.DateTime), + new KeyValuePair("timestamp",CSharpDataType.byteArray), + new KeyValuePair("datetime2",CSharpDataType.DateTime), + new KeyValuePair("date",CSharpDataType.DateTime), + new KeyValuePair("decimal",CSharpDataType.@decimal), + new KeyValuePair("single",CSharpDataType.@decimal), + new KeyValuePair("money",CSharpDataType.@decimal), + new KeyValuePair("numeric",CSharpDataType.@decimal), + new KeyValuePair("smallmoney",CSharpDataType.@decimal), + new KeyValuePair("float",CSharpDataType.@double), + new KeyValuePair("float",CSharpDataType.Single), + new KeyValuePair("real",CSharpDataType.@float), + new KeyValuePair("smallint",CSharpDataType.@short), + new KeyValuePair("tinyint",CSharpDataType.@byte), + new KeyValuePair("uniqueidentifier",CSharpDataType.Guid), + new KeyValuePair("image",CSharpDataType.byteArray), + new KeyValuePair("binary",CSharpDataType.byteArray), + new KeyValuePair("varbinary",CSharpDataType.byteArray), + new KeyValuePair("datetimeoffset", CSharpDataType.DateTimeOffset), + new KeyValuePair("datetimeoffset", CSharpDataType.DateTime)}; + }; + +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/SqlServer/DbFirst/SqlServerDbFirst.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/SqlServer/DbFirst/SqlServerDbFirst.cs new file mode 100644 index 000000000..a79a21d0c --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/SqlServer/DbFirst/SqlServerDbFirst.cs @@ -0,0 +1,6 @@ +namespace SqlSugar +{ + public class SqlServerDbFirst : DbFirstProvider + { + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/SqlServer/DbMaintenance/SqlServerDbMaintenance.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/SqlServer/DbMaintenance/SqlServerDbMaintenance.cs new file mode 100644 index 000000000..b2ab0f5b3 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/SqlServer/DbMaintenance/SqlServerDbMaintenance.cs @@ -0,0 +1,786 @@ +using System.Text.RegularExpressions; + +namespace SqlSugar +{ + public class SqlServerDbMaintenance : DbMaintenanceProvider + { + #region DML + protected override string GetDataBaseSql + { + get + { + return "SELECT NAME FROM master.dbo.sysdatabases ORDER BY NAME"; + } + } + protected override string GetColumnInfosByTableNameSql + { + get + { + string sql = @"SELECT sysobjects.name AS TableName, + syscolumns.Id AS TableId, + syscolumns.name AS DbColumnName, + systypes.name AS DataType, + COLUMNPROPERTY(syscolumns.id,syscolumns.name,'PRECISION') as [length], + isnull(COLUMNPROPERTY(syscolumns.id,syscolumns.name,'Scale'),0) as Scale, + isnull(COLUMNPROPERTY(syscolumns.id,syscolumns.name,'Scale'),0) as DecimalDigits, + Cast( sys.extended_properties.[value] as nvarchar(2000)) AS [ColumnDescription], + syscomments.text AS DefaultValue, + syscolumns.isnullable AS IsNullable, + columnproperty(syscolumns.id,syscolumns.name,'IsIdentity')as IsIdentity, + (CASE + WHEN EXISTS + ( + select 1 + from sysindexes i + join sysindexkeys k on i.id = k.id and i.indid = k.indid + join sysobjects o on i.id = o.id + join syscolumns c on i.id=c.id and k.colid = c.colid + where o.xtype = 'U' + and exists(select 1 from sysobjects where xtype = 'PK' and name = i.name) + and o.name=sysobjects.name and c.name=syscolumns.name + ) THEN 1 + ELSE 0 + END) AS IsPrimaryKey + FROM syscolumns + INNER JOIN systypes ON syscolumns.xtype = systypes.xtype + LEFT JOIN sysobjects ON syscolumns.id = sysobjects.id + LEFT OUTER JOIN sys.extended_properties ON (sys.extended_properties.minor_id = syscolumns.colid + AND sys.extended_properties.major_id = syscolumns.id) + LEFT OUTER JOIN syscomments ON syscolumns.cdefault = syscomments.id + WHERE syscolumns.id IN + (SELECT id + FROM sysobjects + WHERE upper(xtype) IN('U', + 'V') ) + AND (systypes.name <> 'sysname') + AND sysobjects.name=N'{0}' + AND systypes.name<>'geometry' + AND systypes.name<>'geography' + ORDER BY syscolumns.colid"; + return sql; + } + } + protected override string GetTableInfoListSql + { + get + { + return @"SELECT s.Name,Convert(nvarchar(max),tbp.value) as Description + FROM sysobjects s + LEFT JOIN sys.extended_properties as tbp ON s.id=tbp.major_id and tbp.minor_id=0 AND (tbp.Name='MS_Description' OR tbp.Name is null) WHERE s.xtype IN('U') "; + } + } + protected override string GetViewInfoListSql + { + get + { + return @"SELECT s.Name,Convert(varchar(max),tbp.value) as Description + FROM sysobjects s + LEFT JOIN sys.extended_properties as tbp ON s.id=tbp.major_id and tbp.minor_id=0 AND (tbp.Name='MS_Description' OR tbp.Name is null) WHERE s.xtype IN('V') "; + } + } + #endregion + + #region DDL + protected override string CreateDataBaseSql + { + get + { + return @"create database {0} "; + } + } + protected override string AddPrimaryKeySql + { + get + { + return "ALTER TABLE {0} ADD CONSTRAINT {1} PRIMARY KEY({2})"; + } + } + protected override string AddColumnToTableSql + { + get + { + return "ALTER TABLE {0} ADD {1} {2}{3} {4} {5} {6}"; + } + } + protected override string AlterColumnToTableSql + { + get + { + return "ALTER TABLE {0} ALTER COLUMN {1} {2}{3} {4} {5} {6}"; + } + } + protected override string BackupDataBaseSql + { + get + { + return @"USE master;BACKUP DATABASE {0} TO disk = '{1}'"; + } + } + protected override string CreateTableSql + { + get + { + return "CREATE TABLE {0}(\r\n{1})"; + } + } + protected override string CreateTableColumn + { + get + { + return "{0} {1}{2} {3} {4} {5}"; + } + } + protected override string TruncateTableSql + { + get + { + return "TRUNCATE TABLE {0}"; + } + } + protected override string BackupTableSql + { + get + { + return "SELECT TOP {0} * INTO {1} FROM {2}"; + } + } + protected override string DropTableSql + { + get + { + return "DROP TABLE {0}"; + } + } + protected override string DropColumnToTableSql + { + get + { + return "ALTER TABLE {0} DROP COLUMN {1}"; + } + } + protected override string DropConstraintSql + { + get + { + return "ALTER TABLE {0} DROP CONSTRAINT {1}"; + } + } + protected override string RenameColumnSql + { + get + { + return "exec sp_rename '{0}.{1}','{2}','column';"; + } + } + protected override string AddColumnRemarkSql + { + get + { + return "EXECUTE sp_addextendedproperty N'MS_Description', N'{2}', N'user', N'dbo', N'table', N'{1}', N'column', N'{0}'"; ; + } + } + + protected override string DeleteColumnRemarkSql + { + get + { + return "EXEC sp_dropextendedproperty 'MS_Description','user',dbo,'table','{1}','column','{0}'"; + } + + } + + protected override string IsAnyColumnRemarkSql + { + get + { + return @"SELECT" + + " A.name AS table_name," + + " B.name AS column_name," + + " C.value AS column_description" + + " FROM sys.tables A" + + " LEFT JOIN sys.extended_properties C ON C.major_id = A.object_id" + + " LEFT JOIN sys.columns B ON B.object_id = A.object_id AND C.minor_id = B.column_id" + + " INNER JOIN sys.schemas SC ON SC.schema_id = A.schema_id AND SC.name = 'dbo'" + + " WHERE A.name = '{1}' and B.name = '{0}'"; + + } + } + + protected override string AddTableRemarkSql + { + get + { + return "EXECUTE sp_addextendedproperty N'MS_Description', N'{1}', N'user', N'dbo', N'table', N'{0}', NULL, NULL"; + } + } + + protected override string DeleteTableRemarkSql + { + get + { + return "EXEC sp_dropextendedproperty 'MS_Description','user',dbo,'table','{0}' "; + } + + } + + protected override string IsAnyTableRemarkSql + { + get + { + return @"SELECT C.class_desc + FROM sys.tables A + LEFT JOIN sys.extended_properties C ON C.major_id = A.object_id + INNER JOIN sys.schemas SC ON SC.schema_id=A.schema_id AND SC.name='dbo' + WHERE A.name = '{0}' AND minor_id=0"; + } + + } + + protected override string RenameTableSql + { + get + { + return "EXEC sp_rename '{0}','{1}'"; + } + } + + protected override string CreateIndexSql + { + get + { + return "CREATE {3} NONCLUSTERED INDEX Index_{0}_{2} ON {0}({1})"; + } + } + protected override string AddDefaultValueSql + { + get + { + return "alter table {0} ADD DEFAULT '{2}' FOR {1}"; + } + } + protected override string IsAnyIndexSql + { + get + { + return "select count(*) from sys.indexes where name='{0}'"; + } + } + protected override string IsAnyProcedureSql + { + get + { + return "select count(*) from sys.objects where [object_id] = OBJECT_ID(N'{0}') and [type] in (N'P')"; + } + } + #endregion + + #region Check + protected override string CheckSystemTablePermissionsSql + { + get + { + return "select top 1 id from sysobjects"; + } + } + #endregion + + #region Scattered + protected override string CreateTableNull + { + get + { + return "NULL"; + } + } + protected override string CreateTableNotNull + { + get + { + return "NOT NULL"; + } + } + protected override string CreateTablePirmaryKey + { + get + { + return "PRIMARY KEY"; + } + } + protected override string CreateTableIdentity + { + get + { + return "IDENTITY(1,1)"; + } + } + + #endregion + + #region Methods + public override bool DropIndex(string indexName, string tableName) + { + indexName = this.SqlBuilder.GetNoTranslationColumnName(indexName); + tableName = this.SqlBuilder.GetNoTranslationColumnName(tableName); + this.Context.Ado.ExecuteCommand($" DROP INDEX {indexName} ON {tableName}"); + return true; + } + public override bool SetAutoIncrementInitialValue(string tableName, int initialValue) + { + this.Context.Ado.ExecuteCommand($"DBCC CHECKIDENT ('" + tableName + $"', RESEED, {initialValue})"); + return true; + } + public override bool SetAutoIncrementInitialValue(Type entityType, int initialValue) + { + return this.SetAutoIncrementInitialValue(this.Context.EntityMaintenance.GetEntityInfo(entityType).DbTableName, initialValue); + } + public override List GetSchemaTables(EntityInfo entityInfo) + { + if (entityInfo.DbTableName.Contains('.') && this.Context.CurrentConnectionConfig.DbType == DbType.SqlServer) + { + var schema = entityInfo.DbTableName.Split('.').First(); + var isAny = GetSchemas().Any(it => it.EqualCase(schema)) || schema.EqualCase("dbo"); + if (isAny) + { + var tableInfos = this.Context.Ado.SqlQuery(@"SELECT schem.name+'.'+tb.name Name,tb.Description from + ( SELECT obj.name,Convert(nvarchar(max),prop.value)as Description,obj.schema_id FROM sys.objects obj + LEFT JOIN sys.extended_properties prop + ON obj.object_id=prop.major_id + and prop.minor_id=0 + AND (prop.Name='MS_Description' OR prop.Name is null) + WHERE obj.type IN('U')) tb + inner join sys.schemas as schem + on tb.schema_id=schem.schema_id "); + return tableInfos; + } + } + return null; + } + + public override bool DropColumn(string tableName, string columnName) + { + if (Regex.IsMatch(tableName, @"^\w+$") && Regex.IsMatch(columnName, @"^\w+$")) + { + var sql = $"SELECT distinct dc.name AS ConstraintName \r\nFROM sys.default_constraints dc\r\nJOIN sys.columns c ON dc.parent_column_id = c.column_id\r\nWHERE dc.parent_object_id = OBJECT_ID('{tableName}')\r\nAND c.name = '{columnName}';"; + var checks = this.Context.Ado.SqlQuery(sql); + foreach (var checkName in checks) + { + if (checkName?.ToUpper()?.StartsWith("DF__") == true) + { + this.Context.Ado.ExecuteCommand($"ALTER TABLE {SqlBuilder.GetTranslationColumnName(tableName)} DROP CONSTRAINT {checkName}"); + } + } + } + return base.DropColumn(tableName, columnName); + } + public override List GetDbTypes() + { + return this.Context.Ado.SqlQuery(@"SELECT name +FROM sys.types +WHERE is_user_defined = 0;"); + } + public override List GetTriggerNames(string tableName) + { + return this.Context.Ado.SqlQuery(@"SELECT DISTINCT sysobjects.name AS TriggerName +FROM sysobjects +JOIN syscomments ON sysobjects.id = syscomments.id +WHERE sysobjects.xtype = 'TR' +AND syscomments.text LIKE '%" + tableName + "%'"); + } + public override List GetFuncList() + { + return this.Context.Ado.SqlQuery("SELECT name\r\nFROM sys.objects\r\nWHERE type_desc IN( 'SQL_SCALAR_FUNCTION','SQL_INLINE_TABLE_VALUED_FUNCTION') "); + } + private bool IsAnySchemaTable(string tableName) + { + if (tableName?.Contains('.') != true) + { + return false; + } + var list = GetSchemas() ?? new List(); + list.Add("dbo"); + var isAnySchemas = list.Any(it => it.EqualCase(tableName?.Split('.').FirstOrDefault())); + return isAnySchemas; + } + public override bool IsAnyColumnRemark(string columnName, string tableName) + { + if (tableName?.Contains('.') == true && tableName.Contains(SqlBuilder.SqlTranslationLeft)) + { + tableName = string.Join(".", tableName.Split('.').Select(it => SqlBuilder.GetNoTranslationColumnName(it))); + } + if (IsAnySchemaTable(tableName)) + { + var schema = tableName.Split('.').First(); + tableName = tableName.Split('.').Last(); + var temp = this.IsAnyColumnRemarkSql.Replace("'dbo'", $"'{schema}'"); + string sql = string.Format(temp, columnName, tableName); + var dt = this.Context.Ado.GetDataTable(sql); + return dt.Rows?.Count > 0; + } + return base.IsAnyColumnRemark(columnName, tableName); + } + public override bool DeleteColumnRemark(string columnName, string tableName) + { + if (tableName?.Contains('.') == true && tableName.Contains(SqlBuilder.SqlTranslationLeft)) + { + tableName = string.Join(".", tableName.Split('.').Select(it => SqlBuilder.GetNoTranslationColumnName(it))); + } + if (IsAnySchemaTable(tableName)) + { + var schema = tableName.Split('.').First(); + tableName = tableName.Split('.').Last(); + var temp = this.DeleteColumnRemarkSql.Replace(",dbo,", $",{schema},"); + if (!schema.EqualCase("dbo")) + { + temp = temp.Replace("N'user'", $"N'schema'"); + temp = temp.Replace("'user'", $"N'schema'"); + } + string sql = string.Format(temp, columnName, tableName); + this.Context.Ado.ExecuteCommand(sql); + return true; + } + return base.DeleteColumnRemark(columnName, tableName); + } + public override bool AddColumnRemark(string columnName, string tableName, string description) + { + if (tableName?.Contains('.') == true && tableName.Contains(SqlBuilder.SqlTranslationLeft)) + { + tableName = string.Join(".", tableName.Split('.').Select(it => SqlBuilder.GetNoTranslationColumnName(it))); + } + if (IsAnySchemaTable(tableName)) + { + var schema = tableName.Split('.').First(); + tableName = tableName.Split('.').Last(); + var temp = this.AddColumnRemarkSql.Replace("N'dbo'", $"N'{schema}'"); + if (!schema.EqualCase("dbo")) + { + temp = temp.Replace("N'user'", $"N'schema'"); + } + string sql = string.Format(temp, columnName, tableName, description); + this.Context.Ado.ExecuteCommand(sql); + return true; + } + return base.AddColumnRemark(columnName, tableName, description); + } + + public override void AddDefaultValue(EntityInfo entityInfo) + { + var dbColumns = this.GetColumnInfosByTableName(entityInfo.DbTableName, false); + var db = this.Context; + var columns = entityInfo.Columns.Where(it => it.IsIgnore == false).ToList(); + foreach (var item in columns) + { + if (item.DefaultValue.HasValue() || (string.IsNullOrEmpty(item.DefaultValue) && item.UnderType == UtilConstants.StringType)) + { + if (!IsAnyDefaultValue(entityInfo.DbTableName, item.DbColumnName, dbColumns)) + { + this.AddDefaultValue(entityInfo.DbTableName, item.DbColumnName, item.DefaultValue); + } + } + } + } + + public override List GetIndexList(string tableName) + { + return this.Context.Ado.SqlQuery($"SELECT indexname = i.name FROM sys.indexes i\r\nJOIN sys.index_columns ic ON i.object_id = ic.object_id AND i.index_id = ic.index_id\r\nJOIN sys.columns c ON ic.object_id = c.object_id AND ic.column_id = c.column_id\r\nWHERE i.object_id = OBJECT_ID('{tableName}')"); + } + public override List GetProcList(string dbName) + { + var sql = $"SELECT name FROM {dbName}.sys.procedures"; + return this.Context.Ado.SqlQuery(sql); + } + public override bool UpdateColumn(string tableName, DbColumnInfo column) + { + ConvertCreateColumnInfo(column); + if (column.DataType != null && this.Context.CurrentConnectionConfig?.MoreSettings?.SqlServerCodeFirstNvarchar == true) + { + if (!column.DataType.Contains("nvarchar", StringComparison.CurrentCultureIgnoreCase)) + { + column.DataType = column.DataType.ToLower().Replace("varchar", "nvarchar"); + } + } + return base.UpdateColumn(tableName, column); + } + public override bool IsAnyTable(string tableName, bool isCache = true) + { + if (tableName.Contains('.')) + { + var schemas = GetSchemas(); + var first = this.SqlBuilder.GetNoTranslationColumnName(tableName.Split('.').First()); + var schemaInfo = schemas.FirstOrDefault(it => it.EqualCase(first)); + if (schemaInfo == null) + { + return base.IsAnyTable(tableName, isCache); + } + else + { + var result = this.Context.Ado.GetInt($"select object_id(N'{tableName}')"); + return result > 0; + } + } + else if (isCache) + { + return base.IsAnyTable(tableName, isCache); + } + else + { + if (tableName.Contains(SqlBuilder.SqlTranslationLeft)) + { + tableName = SqlBuilder.GetNoTranslationColumnName(tableName); + } + var sql = @"IF EXISTS (SELECT * FROM sys.objects with(nolock) + WHERE type='u' AND name=N'" + tableName.ToSqlFilter() + @"') + SELECT 1 AS res ELSE SELECT 0 AS res;"; + return this.Context.Ado.GetInt(sql) > 0; + } + } + public List GetSchemas() + { + return this.Context.Ado.SqlQuery("SELECT name FROM sys.schemas where name <> 'dbo'"); + } + public override bool DeleteTableRemark(string tableName) + { + string sql = string.Format(this.DeleteTableRemarkSql, tableName); + if (tableName.Contains('.')) + { + var schemas = GetSchemas(); + var tableSchemas = this.SqlBuilder.GetNoTranslationColumnName(tableName.Split('.').First()); + if (schemas.Any(y => y.EqualCase(tableSchemas))) + { + sql = string.Format(this.DeleteTableRemarkSql, this.SqlBuilder.GetNoTranslationColumnName(tableName.Split('.').Last())); + if (tableSchemas.EqualCase("user")) + { + sql = sql.Replace("'user'", "'SCHEMA'").Replace("dbo", $"'{tableSchemas}'"); + } + else + { + sql = sql.Replace(",dbo,", $",{tableSchemas},").Replace("'user'", "'SCHEMA'"); + } + } + } + this.Context.Ado.ExecuteCommand(sql); + return true; + } + public override bool IsAnyTableRemark(string tableName) + { + string sql = string.Format(this.IsAnyTableRemarkSql, tableName); + if (tableName.Contains('.')) + { + var schemas = GetSchemas(); + var tableSchemas = this.SqlBuilder.GetNoTranslationColumnName(tableName.Split('.').First()); + if (schemas.Any(y => y.EqualCase(tableSchemas))) + { + sql = string.Format(this.IsAnyTableRemarkSql, this.SqlBuilder.GetNoTranslationColumnName(tableName.Split('.').Last())); + sql = sql.Replace("'dbo'", $"'{tableSchemas}'"); + } + } + var dt = this.Context.Ado.GetDataTable(sql); + return dt.Rows?.Count > 0; + } + public override bool AddTableRemark(string tableName, string description) + { + string sql = string.Format(this.AddTableRemarkSql, tableName, description); + if (tableName.Contains('.')) + { + var schemas = GetSchemas(); + var tableSchemas = this.SqlBuilder.GetNoTranslationColumnName(tableName.Split('.').First()); + if (schemas.Any(y => y.EqualCase(tableSchemas))) + { + sql = string.Format(this.AddTableRemarkSql, this.SqlBuilder.GetNoTranslationColumnName(tableName.Split('.').Last()), description); + if (tableSchemas.EqualCase("user")) + { + sql = sql.Replace("N'user', N'dbo'", $"N'user', '{tableSchemas}'").Replace("N'user'", "N'SCHEMA'"); + } + else + { + sql = sql.Replace("N'dbo'", $"N'{tableSchemas}'").Replace("N'user'", "N'SCHEMA'"); + } + } + } + this.Context.Ado.ExecuteCommand(sql); + return true; + } + public override bool AddDefaultValue(string tableName, string columnName, string defaultValue) + { + if (defaultValue == "''") + { + defaultValue = ""; + } + var template = AddDefaultValueSql; + if (defaultValue?.Replace(" ", "").Contains("()") == true) + { + template = template.Replace("'{2}'", "{2}"); + } + tableName = SqlBuilder.GetTranslationTableName(tableName); + columnName = SqlBuilder.GetTranslationTableName(columnName); + string sql = string.Format(template, tableName, columnName, defaultValue); + this.Context.Ado.ExecuteCommand(sql); + return true; + } + + /// + ///by current connection string + /// + /// + /// + /// + public override bool CreateDatabase(string databaseName, string databaseDirectory = null) + { + if (databaseDirectory != null) + { + try + { + if (!FileHelper.IsExistDirectory(databaseDirectory)) + { + if (FileHelper.IsExistDirectory(Path.GetPathRoot(databaseDirectory))) + { + FileHelper.CreateDirectory(databaseDirectory); + } + } + } + catch + { + //Databases and sites are not in the same service + } + } + var oldDatabaseName = this.Context.Ado.Connection.Database; + var connection = this.Context.CurrentConnectionConfig.ConnectionString; + if (Regex.Split(connection, oldDatabaseName).Length > 2) + { + var name = Regex.Match(connection, @"database\=\w+|datasource\=\w+", RegexOptions.IgnoreCase).Value; + if (!string.IsNullOrEmpty(name)) + { + connection = connection.Replace(name, "database=master"); + } + else + { + Check.ExceptionEasy("Failed to create the database. The database name has a keyword. Please change the name", "建库失败,库名存在关键字,请换一个名字"); + } + } + else + { + connection = connection.Replace(oldDatabaseName, "master"); + } + var newDb = new SqlSugarClient(new ConnectionConfig() + { + DbType = this.Context.CurrentConnectionConfig.DbType, + IsAutoCloseConnection = true, + ConnectionString = connection + }); + if (!GetDataBaseList(newDb).Any(it => it.Equals(databaseName, StringComparison.CurrentCultureIgnoreCase))) + { + var separatorChar = UtilMethods.GetSeparatorChar(); + var sql = CreateDataBaseSql; + if (databaseDirectory.HasValue()) + { + sql += @"on primary + ( + name = N'{0}', + filename = N'{1}\{0}.mdf', + size = 10mb, + maxsize = 5000mb, + filegrowth = 1mb + ), + ( + name = N'{0}_ndf', + filename = N'{1}\{0}.ndf', + size = 10mb, + maxsize = 5000mb, + filegrowth =10mb + ) + log on + ( + name = N'{0}_log', + filename = N'{1}\{0}.ldf', + size = 100mb, + maxsize = 1gb, + filegrowth = 10mb + ); "; + databaseDirectory = databaseDirectory.Replace("\\", separatorChar); + } + if (databaseName.Contains('.')) + { + databaseName = $"[{databaseName}]"; + } + else if (Regex.IsMatch(databaseName, @"^\d.*")) + { + databaseName = $"[{databaseName}]"; + } + newDb.Ado.ExecuteCommand(string.Format(sql, databaseName, databaseDirectory)); + } + return true; + } + public override bool CreateTable(string tableName, List columns, bool isCreatePrimaryKey = true) + { + tableName = this.SqlBuilder.GetTranslationTableName(tableName); + foreach (var item in columns) + { + ConvertCreateColumnInfo(item); + if (item.DataType == "decimal" && item.DecimalDigits == 0 && item.Length == 0) + { + item.DecimalDigits = 4; + item.Length = 18; + } + else if (item.DataType != null && this.Context.CurrentConnectionConfig?.MoreSettings?.SqlServerCodeFirstNvarchar == true) + { + if (!item.DataType.Contains("nvarchar", StringComparison.CurrentCultureIgnoreCase)) + { + item.DataType = item.DataType.ToLower().Replace("varchar", "nvarchar"); + } + } + + } + string sql = GetCreateTableSql(tableName, columns); + this.Context.Ado.ExecuteCommand(sql); + if (isCreatePrimaryKey) + { + var pkColumns = columns.Where(it => it.IsPrimarykey).ToList(); + if (pkColumns.Count > 1) + { + this.Context.DbMaintenance.AddPrimaryKeys(tableName, pkColumns.Select(it => it.DbColumnName).ToArray()); + } + else + { + foreach (var item in pkColumns) + { + this.Context.DbMaintenance.AddPrimaryKey(tableName, item.DbColumnName); + } + } + } + return true; + } + + private static void ConvertCreateColumnInfo(DbColumnInfo x) + { + string[] array = new string[] { "int", "text", "image", "smallint", "bigint", "date", "bit", "ntext", "datetime", "datetime2", "uniqueidentifier", "tinyint", "rowversion", "timestamp", "money" }; + if (x.DataType.EqualCase("nvarchar") || x.DataType.EqualCase("varchar")) + { + if (x.Length < 1) + { + x.DataType = $"{x.DataType}(max)"; + } + } + else if (array.Contains(x.DataType?.ToLower())) + { + x.Length = 0; + x.DecimalDigits = 0; + } + } + + public override List GetColumnInfosByTableName(string tableName, bool isCache = true) + { + tableName = SqlBuilder.GetNoTranslationColumnName(tableName); + var result = base.GetColumnInfosByTableName(tableName, isCache); + return result; + } + public override bool RenameColumn(string tableName, string oldColumnName, string newColumnName) + { + tableName = this.SqlBuilder.GetTranslationTableName(tableName); + oldColumnName = this.SqlBuilder.GetTranslationColumnName(oldColumnName); + newColumnName = this.SqlBuilder.GetNoTranslationColumnName(newColumnName); + string sql = string.Format(this.RenameColumnSql, tableName, oldColumnName, newColumnName); + this.Context.Ado.ExecuteCommand(sql); + return true; + } + #endregion + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/SqlServer/Queryable/SqlServerQueryable.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/SqlServer/Queryable/SqlServerQueryable.cs new file mode 100644 index 000000000..ba7278fcf --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/SqlServer/Queryable/SqlServerQueryable.cs @@ -0,0 +1,51 @@ +namespace SqlSugar +{ + public class SqlServerQueryable : QueryableProvider + { + + } + public class SqlServerQueryable : QueryableProvider + { + + } + public class SqlServerQueryable : QueryableProvider + { + + } + public class SqlServerQueryable : QueryableProvider + { + + } + public class SqlServerQueryable : QueryableProvider + { + + } + public class SqlServerQueryable : QueryableProvider + { + + } + public class SqlServerQueryable : QueryableProvider + { + + } + public class SqlServerQueryable : QueryableProvider + { + + } + public class SqlServerQueryable : QueryableProvider + { + + } + public class SqlServerQueryable : QueryableProvider + { + + } + public class SqlServerQueryable : QueryableProvider + { + + } + public class SqlServerQueryable : QueryableProvider + { + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/SqlServer/SqlBuilder/SqlServerBlukCopy.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/SqlServer/SqlBuilder/SqlServerBlukCopy.cs new file mode 100644 index 000000000..8e1976400 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/SqlServer/SqlBuilder/SqlServerBlukCopy.cs @@ -0,0 +1,150 @@ +using Microsoft.Data.SqlClient; + +using System.Data; + +namespace SqlSugar +{ + public partial class SqlServerBlukCopy + { + internal List> DbColumnInfoList { get; set; } + internal SqlSugarProvider Context { get; set; } + internal ISqlBuilder Builder { get; set; } + internal InsertBuilder InsertBuilder { get; set; } + internal object[] Inserts { get; set; } + + public int ExecuteBulkCopy() + { + if (DbColumnInfoList == null || DbColumnInfoList.Count == 0) return 0; + + if (Inserts.First().GetType() == typeof(DataTable)) + { + return WriteToServer(); + } + DataTable dt = GetCopyData(); + SqlBulkCopy bulkCopy = GetBulkCopyInstance(); + bulkCopy.DestinationTableName = InsertBuilder.GetTableNameString; + try + { + bulkCopy.WriteToServer(dt); + } + catch (Exception) + { + CloseDb(); + throw; + } + CloseDb(); + return DbColumnInfoList.Count; + } + + public async Task ExecuteBulkCopyAsync() + { + if (DbColumnInfoList == null || DbColumnInfoList.Count == 0) return 0; + + if (Inserts.First().GetType() == typeof(DataTable)) + { + return WriteToServer(); + } + DataTable dt = GetCopyData(); + SqlBulkCopy bulkCopy = GetBulkCopyInstance(); + bulkCopy.DestinationTableName = InsertBuilder.GetTableNameString; + try + { + await bulkCopy.WriteToServerAsync(dt).ConfigureAwait(false); + } + catch (Exception) + { + CloseDb(); + throw; + } + CloseDb(); + return DbColumnInfoList.Count; + } + + private int WriteToServer() + { + var dt = this.Inserts.First() as DataTable; + if (dt == null) + return 0; + Check.Exception(dt.TableName == "Table", "dt.TableName can't be null "); + dt = GetCopyWriteDataTable(dt); + SqlBulkCopy copy = GetBulkCopyInstance(); + copy.DestinationTableName = this.Builder.GetTranslationColumnName(dt.TableName); + copy.WriteToServer(dt); + CloseDb(); + return dt.Rows.Count; + } + private DataTable GetCopyWriteDataTable(DataTable dt) + { + var result = this.Context.Ado.GetDataTable("select top 0 * from " + this.Builder.GetTranslationColumnName(dt.TableName)); + foreach (DataRow item in dt.Rows) + { + DataRow dr = result.NewRow(); + foreach (DataColumn column in result.Columns) + { + + if (dt.Columns.Cast().Select(it => it.ColumnName.ToLower()).Contains(column.ColumnName.ToLower())) + { + dr[column.ColumnName] = item[column.ColumnName]; + if (dr[column.ColumnName] == null) + { + dr[column.ColumnName] = DBNull.Value; + } + } + } + result.Rows.Add(dr); + } + result.TableName = dt.TableName; + return result; + } + private SqlBulkCopy GetBulkCopyInstance() + { + SqlBulkCopy copy; + if (this.Context.Ado.Transaction == null) + { + copy = new SqlBulkCopy((SqlConnection)this.Context.Ado.Connection); + } + else + { + copy = new SqlBulkCopy((SqlConnection)this.Context.Ado.Connection, SqlBulkCopyOptions.CheckConstraints, (SqlTransaction)this.Context.Ado.Transaction); + } + if (this.Context.Ado.Connection.State == ConnectionState.Closed) + { + this.Context.Ado.Connection.Open(); + } + copy.BulkCopyTimeout = this.Context.Ado.CommandTimeOut; + return copy; + } + private DataTable GetCopyData() + { + var dt = this.Context.Ado.GetDataTable("select top 0 * from " + InsertBuilder.GetTableNameString); + foreach (var rowInfos in DbColumnInfoList) + { + var dr = dt.NewRow(); + foreach (var value in rowInfos) + { + if (value.Value != null && UtilMethods.GetUnderType(value.Value.GetType()) == UtilConstants.DateType) + { + if (value.Value != null && value.Value.ToString() == DateTime.MinValue.ToString()) + { + value.Value = Convert.ToDateTime("1753/01/01"); + } + } + if (value.Value == null) + { + value.Value = DBNull.Value; + } + dr[value.DbColumnName] = value.Value; + } + dt.Rows.Add(dr); + } + return dt; + } + private void CloseDb() + { + if (this.Context.CurrentConnectionConfig.IsAutoCloseConnection && this.Context.Ado.Transaction == null) + { + this.Context.Ado.Connection.Close(); + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/SqlServer/SqlBuilder/SqlServerBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/SqlServer/SqlBuilder/SqlServerBuilder.cs new file mode 100644 index 000000000..601974322 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/SqlServer/SqlBuilder/SqlServerBuilder.cs @@ -0,0 +1,33 @@ +namespace SqlSugar +{ + public class SqlServerBuilder : SqlBuilderProvider + { + public override string SqlTranslationLeft { get { return "["; } } + public override string SqlTranslationRight { get { return "]"; } } + public override bool SupportReadToken { get; set; } = true; + + public override string RemoveParentheses(string sql) + { + if (sql.Contains("ORDER BY") && !sql.StartsWith("(SELECT TOP 1") && !sql.Contains("(SELECT TOP 1000000")) + { + sql = $"SELECT * FROM {sql.Replace("(SELECT ", "(SELECT TOP 1000000")} TEMP"; + } + else if (sql.Contains("ORDER BY") && sql.StartsWith("(SELECT TOP 1")) + { + sql = $"SELECT * FROM ({sql}) TEMP"; + } + return sql; + } + public override void ChangeJsonType(SugarParameter paramter) + { + if (paramter.DbType == System.Data.DbType.AnsiString) + { + paramter.DbType = System.Data.DbType.String; + } + } + public override Task GetReaderByToken(System.Data.IDataReader dataReader, System.Threading.CancellationToken cancellationToken) + { + return ((Microsoft.Data.SqlClient.SqlDataReader)dataReader).ReadAsync(this.Context.Ado.CancellationToken.Value); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/SqlServer/SqlBuilder/SqlServerDeleteBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/SqlServer/SqlBuilder/SqlServerDeleteBuilder.cs new file mode 100644 index 000000000..bf9398faa --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/SqlServer/SqlBuilder/SqlServerDeleteBuilder.cs @@ -0,0 +1,7 @@ +namespace SqlSugar +{ + public class SqlServerDeleteBuilder : DeleteBuilder + { + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/SqlServer/SqlBuilder/SqlServerExpressionContext.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/SqlServer/SqlBuilder/SqlServerExpressionContext.cs new file mode 100644 index 000000000..fd8977ee5 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/SqlServer/SqlBuilder/SqlServerExpressionContext.cs @@ -0,0 +1,192 @@ +namespace SqlSugar +{ + public partial class SqlServerExpressionContext : ExpressionContext, ILambdaExpressions + { + public SqlSugarProvider Context { get; set; } + public SqlServerExpressionContext() + { + base.DbMehtods = new SqlServerMethod(); + } + + } + public partial class SqlServerMethod : DefaultDbMethod, IDbMethods + { + public override string UNIX_TIMESTAMP(MethodCallExpressionModel model) + { + var parameterNameA = model.Args[0].MemberName; + return $" DATEDIFF(SECOND, '1970-01-01', {parameterNameA}) "; + } + public override string ToDecimal(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS DECIMAL(18,6)) ", parameter.MemberName); + } + public override string JsonArrayLength(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return $" (SELECT COUNT(*) FROM OPENJSON({parameter.MemberName})) "; + } + + public override string JsonIndex(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter1 = model.Args[1]; + return $"JSON_VALUE({parameter.MemberName}, '$[{parameter1.MemberValue}]')"; + } + public override string CharIndexNew(MethodCallExpressionModel model) + { + return string.Format("CHARINDEX ({1},{0})", model.Args[0].MemberName, model.Args[1].MemberName); + } + public override string WeekOfYear(MethodCallExpressionModel mode) + { + var parameterNameA = mode.Args[0].MemberName; + return $"DATEPART(WEEK, {parameterNameA}) "; + } + public override string GetTableWithDataBase(string dataBaseName, string tableName) + { + return $"{dataBaseName}.dbo.{tableName}"; + } + public override string GetForXmlPath() + { + return " FOR XML PATH('')),1,len(N','),'') "; + } + public override string GetStringJoinSelector(string result, string separator) + { + if (result.ObjToString().Trim().StartsWith("DISTINCT ", StringComparison.OrdinalIgnoreCase)) + { + int index = result.IndexOf(result, StringComparison.Ordinal); // 找到去掉前缀空格后的位置 + result = result.Substring(index + 9); // 9 是 "DISTINCT " 的长度 + return $"stuff((SELECT DISTINCT cast(N'{separator}' as nvarchar(max)) + cast({result} as nvarchar(max))"; + } + else + { + return $"stuff((SELECT cast(N'{separator}' as nvarchar(max)) + cast({result} as nvarchar(max))"; + } + } + public override string DateValue(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + if (parameter.MemberName != null && parameter.MemberName is DateTime) + { + if (parameter2.MemberValue?.ToString() == DateType.Weekday.ToString()) + { + return string.Format(" (datepart({0},'{1}')-1) ", parameter2.MemberValue, parameter.MemberName); + } + else + { + return string.Format(" datepart({0},'{1}') ", parameter2.MemberValue, parameter.MemberName); + } + } + else + { + if (parameter2.MemberValue?.ToString() == DateType.Weekday.ToString()) + { + return string.Format(" (datepart({0},{1})-1) ", parameter2.MemberValue, parameter.MemberName); + } + else + { + return string.Format(" datepart({0},{1}) ", parameter2.MemberValue, parameter.MemberName); + } + } + } + public override string HasValue(MethodCallExpressionModel model) + { + if (model.Args[0].Type == UtilConstants.GuidType) + { + var parameter = model.Args[0]; + return string.Format("( {0} IS NOT NULL )", parameter.MemberName); + } + else + { + var parameter = model.Args[0]; + return string.Format("( {0}<>'' AND {0} IS NOT NULL )", parameter.MemberName); + } + } + + public override string JsonField(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter1 = model.Args[1]; + //var parameter2 = model.Args[2]; + //var parameter3= model.Args[3]; + var result = GetJson(parameter.MemberName, parameter1.MemberName, model.Args.Count == 2); + if (model.Args.Count > 2) + { + result = GetJson(result, model.Args[2].MemberName, model.Args.Count == 3); + } + if (model.Args.Count > 3) + { + result = GetJson(result, model.Args[3].MemberName, model.Args.Count == 4); + } + if (model.Args.Count > 4) + { + result = GetJson(result, model.Args[4].MemberName, model.Args.Count == 5); + } + if (model.Args.Count > 5) + { + result = GetJson(result, model.Args[5].MemberName, model.Args.Count == 6); + } + return result; + } + + private string GetJson(object memberName1, object memberName2, bool isLast) + { + return $"JSON_VALUE({memberName1}, '$.'+" + memberName2 + ")"; + } + + public override string JsonListObjectAny(MethodCallExpressionModel model) + { + return $"(EXISTS (SELECT * from OPENJSON({model.Args[0].MemberName}) " + + $"WITH([value] NVARCHAR(MAX) '$.{model.Args[1].MemberValue.ToString().ToSqlFilter()}') " + + $"WHERE [value] = {model.Args[2].MemberName}))"; + } + + public override string JsonArrayAny(MethodCallExpressionModel model) + { + return string.Format("(EXISTS(SELECT * from OPENJSON({0}) WHERE [value] = {1}))" + , model.Args[0].MemberName + , model.Args[1].MemberName + ); + } + public override string TrimEnd(MethodCallExpressionModel mode) + { + var parameterNameA = mode.Args[0].MemberName; + var parameterNameB = mode.Args[1].MemberName; + return $"CASE WHEN RIGHT({parameterNameA}, 1) = {parameterNameB} THEN LEFT({parameterNameA}, LEN({parameterNameA}) - 1) ELSE {parameterNameA} END"; + } + public override string TrimStart(MethodCallExpressionModel mode) + { + + var parameterNameA = mode.Args[0].MemberName; + var parameterNameB = mode.Args[1].MemberName; + return $" CASE WHEN LEFT({parameterNameA}, 1) = {parameterNameB} THEN RIGHT({parameterNameA}, LEN({parameterNameA}) - 1) ELSE {parameterNameA} END "; + } + + public override string PadLeft(MethodCallExpressionModel mode) + { + var parameterNameA = mode.Args[0].MemberName; + var parameterNameB = mode.Args[1].MemberName; + var parameterNameC = mode.Args[2].MemberName; + return $" CONCAT(REPLICATE({parameterNameC}, {parameterNameB} - LEN({parameterNameA})), {parameterNameA}) "; + } + + public override string NewUid(MethodCallExpressionModel mode) + { + return " NEWID() "; + } + + public override string FullTextContains(MethodCallExpressionModel mode) + { + var columns = mode.Args[0].MemberName; + if (mode.Args[0].MemberValue is List) + { + columns = "(" + string.Join(",", mode.Args[0].MemberValue as List) + ")"; + } + var searchWord = mode.Args[1].MemberName; + return $" CONTAINS({columns},{searchWord}) "; + } + } + + +} \ No newline at end of file diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/SqlServer/SqlBuilder/SqlServerFastBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/SqlServer/SqlBuilder/SqlServerFastBuilder.cs new file mode 100644 index 000000000..f4359b4dc --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/SqlServer/SqlBuilder/SqlServerFastBuilder.cs @@ -0,0 +1,92 @@ +using Microsoft.Data.SqlClient; + +using System.Data; + +namespace SqlSugar +{ + + public class SqlServerFastBuilder : FastBuilder, IFastBuilder + { + public override bool IsActionUpdateColumns { get; set; } = true; + public override DbFastestProperties DbFastestProperties { get; set; } = new DbFastestProperties() + { + HasOffsetTime = true, + IsMerge = true + }; + public async Task ExecuteBulkCopyAsync(DataTable dt) + { + + SqlBulkCopy bulkCopy = GetBulkCopyInstance(); + bulkCopy.DestinationTableName = dt.TableName; + try + { + await bulkCopy.WriteToServerAsync(dt).ConfigureAwait(false); + } + catch (Exception) + { + CloseDb(); + throw; + } + CloseDb(); + return dt.Rows.Count; + } + public SqlBulkCopy GetBulkCopyInstance() + { + SqlBulkCopy copy; + if (this.Context.Ado.Transaction == null) + { + if (this.DbFastestProperties?.IsOffIdentity == true) + { + copy = new SqlBulkCopy((SqlConnection)this.Context.Ado.Connection, SqlBulkCopyOptions.KeepIdentity, null); + } + else + { + copy = new SqlBulkCopy((SqlConnection)this.Context.Ado.Connection); + } + } + else + { + if (this.DbFastestProperties?.IsOffIdentity == true) + { + copy = new SqlBulkCopy((SqlConnection)this.Context.Ado.Connection, SqlBulkCopyOptions.KeepIdentity, (SqlTransaction)this.Context.Ado.Transaction); + } + else + { + + copy = new SqlBulkCopy((SqlConnection)this.Context.Ado.Connection, SqlBulkCopyOptions.CheckConstraints, (SqlTransaction)this.Context.Ado.Transaction); + } + } + if (this.Context.Ado.Connection.State == ConnectionState.Closed) + { + this.Context.Ado.Connection.Open(); + } + copy.BulkCopyTimeout = this.Context.Ado.CommandTimeOut; + return copy; + } + public override Task Merge(string tableName, DataTable dt, EntityInfo entityInfo, string[] whereColumns, string[] updateColumns, List datas) + { + var sqlBuilder = this.Context.Queryable().SqlBuilder; + var insertColumns = entityInfo.Columns + .Where(it => it.IsIgnore == false) + .Where(it => it.IsIdentity == false) + .Where(it => it.OracleSequenceName == null) + .Where(it => it.InsertServerTime == false) + .Where(it => it.InsertSql == null) + .Where(it => it.IsOnlyIgnoreInsert == false); + var whereSql = string.Join(" AND ", whereColumns.Select(it => $"tgt.{sqlBuilder.GetTranslationColumnName(it)}=src.{sqlBuilder.GetTranslationColumnName(it)}")); + var updateColumnsSql = string.Join(" , ", updateColumns.Select(it => $"tgt.{sqlBuilder.GetTranslationColumnName(it)}=src.{sqlBuilder.GetTranslationColumnName(it)}")); + var insertColumnsSqlTgt = string.Join(" , ", insertColumns.Select(it => sqlBuilder.GetTranslationColumnName(it.DbColumnName))); + var insertColumnsSqlsrc = string.Join(" , ", insertColumns.Select(it => "src." + sqlBuilder.GetTranslationColumnName(it.DbColumnName))); + var sql = $@"MERGE INTO {sqlBuilder.GetTranslationColumnName(tableName)} tgt +USING {sqlBuilder.GetTranslationColumnName(dt.TableName)} src +ON ({whereSql}) +WHEN MATCHED THEN + UPDATE SET {updateColumnsSql} +WHEN NOT MATCHED THEN + INSERT ({insertColumnsSqlTgt}) + VALUES ({insertColumnsSqlsrc});"; + + return this.Context.Ado.ExecuteCommandAsync(sql); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/SqlServer/SqlBuilder/SqlServerInsertBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/SqlServer/SqlBuilder/SqlServerInsertBuilder.cs new file mode 100644 index 000000000..2787b3503 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/SqlServer/SqlBuilder/SqlServerInsertBuilder.cs @@ -0,0 +1,82 @@ +using System.Text; + +namespace SqlSugar +{ + public class SqlServerInsertBuilder : InsertBuilder + { + public override Func ConvertInsertReturnIdFunc { get; set; } = (name, sql) => + { + return sql.Replace("select SCOPE_IDENTITY();", "").Replace(")\r\n SELECT", $")\r\n OUTPUT INSERTED.{name} as {name} \r\nSELECT"); + }; + public override bool IsNoPage { get; set; } = true; + public override string ToSqlString() + { + if (IsNoInsertNull) + { + DbColumnInfoList = DbColumnInfoList.Where(it => it.Value != null).ToList(); + } + var groupList = DbColumnInfoList.GroupBy(it => it.TableId).ToList(); + var isSingle = groupList.Count == 1; + string columnsString = string.Join(",", groupList.First().Select(it => Builder.GetTranslationColumnName(it.DbColumnName))); + var result = ""; + if (isSingle) + { + string columnParametersString = string.Join(",", this.DbColumnInfoList.Select(it => base.GetDbColumn(it, Builder.SqlParameterKeyWord + it.DbColumnName))); + result = string.Format(SqlTemplate, GetTableNameString, columnsString, columnParametersString); + } + else + { + StringBuilder batchInsetrSql = new StringBuilder(); + int pageSize = 200; + if (this.EntityInfo.Columns.Count > 30) + { + pageSize = 50; + } + else if (this.EntityInfo.Columns.Count > 20) + { + pageSize = 100; + } + if (IsNoPage && IsReturnPkList) + { + pageSize = groupList.Count; + } + int pageIndex = 1; + int totalRecord = groupList.Count; + int pageCount = (totalRecord + pageSize - 1) / pageSize; + while (pageCount >= pageIndex) + { + batchInsetrSql.AppendFormat(SqlTemplateBatch, GetTableNameString, columnsString); + int i = 0; + foreach (var columns in groupList.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList()) + { + var isFirst = i == 0; + if (!isFirst) + { + batchInsetrSql.Append(SqlTemplateBatchUnion); + } + batchInsetrSql.Append("\r\n SELECT " + string.Join(",", columns.Select(it => string.Format(SqlTemplateBatchSelect, base.GetDbColumn(it, FormatValue(it.Value)), Builder.GetTranslationColumnName(it.DbColumnName))))); + ++i; + } + pageIndex++; + batchInsetrSql.Append("\r\n;\r\n"); + } + result = batchInsetrSql.ToString(); + if (this.Context.CurrentConnectionConfig.DbType == DbType.SqlServer) + { + result += "select SCOPE_IDENTITY();"; + } + } + + if (this.IsOffIdentity) + { + var tableName = this.GetTableNameString; + result = $"SET IDENTITY_INSERT {tableName} ON;" + result.TrimEnd(';') + $";SET IDENTITY_INSERT {tableName} OFF"; ; + } + return result; + } + public override string FormatDateTimeOffset(object value) + { + return "'" + ((DateTimeOffset)value).ToString("o") + "'"; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/SqlServer/SqlBuilder/SqlServerQueryBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/SqlServer/SqlBuilder/SqlServerQueryBuilder.cs new file mode 100644 index 000000000..07a2f09aa --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/SqlServer/SqlBuilder/SqlServerQueryBuilder.cs @@ -0,0 +1,130 @@ +using System.Text; + +namespace SqlSugar +{ + public class SqlServerQueryBuilder : QueryBuilder + { + public override string SqlTemplate + { + get + { + return "SELECT {0}{" + UtilConstants.ReplaceKey + "} FROM {1}{2}{3}{4}"; + } + } + public override string ToSqlString() + { + var oldTake = Take; + var oldSkip = Skip; + var isDistinctPage = IsDistinct && (Take > 1 || Skip > 1); + if (isDistinctPage) + { + Take = null; + Skip = null; + } + var result = _ToSqlString(); + if (isDistinctPage) + { + if (this.OrderByValue.HasValue()) + { + Take = int.MaxValue; + result = result.Replace("DISTINCT", $" DISTINCT TOP {int.MaxValue} "); + } + Take = oldTake; + Skip = oldSkip; + result = this.Context.SqlQueryable(result).Skip(Skip ?? 0).Take(Take ?? 0).ToSql().Key; + + + } + if (IsDistinct && oldTake == 1 && oldSkip == null) + { + result = result.Replace(" TOP 1 DISTINCT", " TOP 1 "); + } + if (result.IndexOf("-- No table") > 0) + { + return "-- No table"; + } + return result; + } + public string _ToSqlString() + { + string oldOrderBy = this.OrderByValue; + string externalOrderBy = oldOrderBy; + var isIgnoreOrderBy = this.IsCount && this.PartitionByValue.IsNullOrEmpty(); + AppendFilter(); + sql = new StringBuilder(); + var oldOrderByValue = this.OrderByValue; + if (this.OrderByValue == null && (Skip != null || Take != null)) this.OrderByValue = " ORDER BY GetDate() "; + if (this.PartitionByValue.HasValue()) + { + this.OrderByValue = this.PartitionByValue + this.OrderByValue; + } + var isFirst = (Skip == 0 || Skip == null) && Take == 1 && DisableTop == false; + var isTop = (Skip == null && Take != null && DisableTop == false); + var isRowNumber = (Skip != null || Take != null) && !isFirst && !isTop; + if (!isRowNumber && oldOrderByValue == null) { this.OrderByValue = null; } + if (isFirst && oldOrderByValue == "ORDER BY GETDATE() ") { this.OrderByValue = null; } + var rowNumberString = string.Format(",ROW_NUMBER() OVER({0}) AS RowIndex ", GetOrderByString); + string groupByValue = GetGroupByString + HavingInfos; + string orderByValue = (!isRowNumber && this.OrderByValue.HasValue()) ? GetOrderByString : null; + if (isIgnoreOrderBy) { orderByValue = null; } + sql.AppendFormat(SqlTemplate, GetSelect(isFirst, isTop), base.GetTableNameString, base.GetWhereValueString, groupByValue, orderByValue); + sql.Replace(UtilConstants.ReplaceKey, isRowNumber ? (isIgnoreOrderBy ? null : rowNumberString) : null); + if (isIgnoreOrderBy) { this.OrderByValue = oldOrderBy; return sql.ToString(); } + var result = (isFirst || isTop) ? sql.ToString() : ToPageSql(sql.ToString(), this.Take, this.Skip); + if (ExternalPageIndex > 0) + { + if (externalOrderBy.IsNullOrEmpty()) + { + externalOrderBy = " ORDER BY GetDate() "; + } + result = string.Format("SELECT *,ROW_NUMBER() OVER({0}) AS RowIndex2 FROM ({1}) ExternalTable ", GetExternalOrderBy(externalOrderBy), result); + result = ToPageSql2(result, ExternalPageIndex, ExternalPageSize, true); + } + this.OrderByValue = oldOrderBy; + if (!string.IsNullOrEmpty(this.Offset)) + { + if (this.OrderByValue.IsNullOrEmpty()) + { + result += " ORDER BY GETDATE() "; + if (this.OldSql.HasValue()) + this.OldSql += " ORDER BY GETDATE() "; + } + else + { + if (this.OldSql.HasValue()) + this.OldSql += (" " + this.GetOrderByString); + } + result += this.Offset; + + if (this.OldSql.HasValue()) + this.OldSql += this.Offset; + } + result = GetSqlQuerySql(result); + if (isFirst && IsDistinct) + { + result = result.Replace("TOP 1 DISTINCT", "TOP 1 "); + } + if (this.GroupParameters?.Count > 0 && this.GroupByIsReplace) + { + result = result.Replace(this.GroupBySqlOld, this.GroupBySql); + } + return result; + } + + private string GetSelect(bool isFirst, bool isTop) + { + if (isFirst) + { + return (" TOP 1 " + GetSelectValue); + } + else if (isTop) + { + return ($" TOP {this.Take} " + GetSelectValue); + } + else + { + return GetSelectValue; + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/SqlServer/SqlBuilder/SqlServerUpdateBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/SqlServer/SqlBuilder/SqlServerUpdateBuilder.cs new file mode 100644 index 000000000..01bdaeb98 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/SqlServer/SqlBuilder/SqlServerUpdateBuilder.cs @@ -0,0 +1,118 @@ +using System.Text; +using System.Text.RegularExpressions; + +namespace SqlSugar +{ + public class SqlServerUpdateBuilder : UpdateBuilder + { + protected override string TomultipleSqlString(List> groupList) + { + Check.Exception(PrimaryKeys == null || PrimaryKeys.Count == 0, " Update List need Primary key"); + int pageSize = 200; + int pageIndex = 1; + int totalRecord = groupList.Count; + int pageCount = (totalRecord + pageSize - 1) / pageSize; + StringBuilder batchUpdateSql = new StringBuilder(); + while (pageCount >= pageIndex) + { + StringBuilder updateTable = new StringBuilder(); + string setValues = string.Join(",", groupList.First().Where(it => it.IsPrimarykey == false && (it.IsIdentity == false || (IsOffIdentity && it.IsIdentity))).Select(it => + { + if (SetValues.IsValuable()) + { + var setValue = SetValues.Where(sv => sv.Key == Builder.GetTranslationColumnName(it.DbColumnName)); + if (setValue?.Any() == true) + { + return setValue.First().Value; + } + } + var result = string.Format("S.{0}=T.{0}", Builder.GetTranslationColumnName(it.DbColumnName)); + return result; + })); + batchUpdateSql.AppendFormat(SqlTemplateBatch.ToString(), setValues, GetTableNameStringNoWith, TableWithString); + int i = 0; + foreach (var columns in groupList.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList()) + { + var isFirst = i == 0; + if (!isFirst) + { + updateTable.Append(SqlTemplateBatchUnion); + } + updateTable.Append("\r\n SELECT " + string.Join(",", columns.Select(it => string.Format(base.SqlTemplateBatchSelect, base.GetDbColumn(it, GetValue(it)), Builder.GetTranslationColumnName(it.DbColumnName))))); + ++i; + } + pageIndex++; + updateTable.Append("\r\n"); + string whereString = null; + if (this.WhereValues.HasValue()) + { + foreach (var item in WhereValues) + { + var isFirst = whereString == null; + whereString += (isFirst ? null : " AND "); + whereString += Regex.Replace(item, "\\" + this.Builder.SqlTranslationLeft, "S." + this.Builder.SqlTranslationLeft); + } + } + if (PrimaryKeys.HasValue()) + { + foreach (var item in PrimaryKeys) + { + var isFirst = whereString == null; + whereString += (isFirst ? null : " AND "); + whereString += string.Format("S.{0}=T.{0}", Builder.GetTranslationColumnName(item)); + } + } + batchUpdateSql.AppendFormat(SqlTemplateJoin, updateTable, whereString); + } + batchUpdateSql = GetBatchUpdateSql(batchUpdateSql); + return batchUpdateSql.ToString(); + } + + private StringBuilder GetBatchUpdateSql(StringBuilder batchUpdateSql) + { + if (ReSetValueBySqlExpListType == null && ReSetValueBySqlExpList != null) + { + var result = batchUpdateSql.ToString(); + foreach (var item in ReSetValueBySqlExpList) + { + var dbColumnName = item.Value.DbColumnName; + if (item.Value.Type == ReSetValueBySqlExpListModelType.List) + { + result = result.Replace($"T.{dbColumnName}", $" S.{dbColumnName}{item.Value.Sql}T.{dbColumnName}"); + } + else + { + result = result.Replace($"T.{dbColumnName}", item.Value.Sql.Replace(dbColumnName, "S." + dbColumnName)); + } + batchUpdateSql = new StringBuilder(result); + } + } + + return batchUpdateSql; + } + + private object GetValue(DbColumnInfo it) + { + if (it.SqlParameterDbType?.Equals(System.Data.DbType.AnsiString) == true) + { + var value = FormatValue(it.Value); + if (value is string && value.ObjToString().StartsWith("N'")) + { + return value.ObjToString().TrimStart('N'); + } + else + { + return value; + } + } + else + { + return FormatValue(it.Value); + } + } + public override string FormatDateTimeOffset(object value) + { + return "'" + ((DateTimeOffset)value).ToString("o") + "'"; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/SqlServer/SqlServerProvider.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/SqlServer/SqlServerProvider.cs new file mode 100644 index 000000000..ae8ea03ae --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/SqlServer/SqlServerProvider.cs @@ -0,0 +1,234 @@ +using Microsoft.Data.SqlClient; + +using System.Data; +using System.Data.Common; +using System.Xml.Linq; + +namespace SqlSugar +{ + public class SqlServerProvider : AdoProvider + { + + public SqlServerProvider() + { + this.IsOpenAsync = true; + } + public override IDbConnection Connection + { + get + { + if (base._DbConnection == null) + { + try + { + base._DbConnection = new SqlConnection(base.Context.CurrentConnectionConfig.ConnectionString); + } + catch (Exception ex) + { + Check.Exception(true, ErrorMessage.ConnnectionOpen, ex.Message); + } + } + return base._DbConnection; + } + set + { + base._DbConnection = value; + } + } + /// + /// Only SqlServer + /// + /// + public override void BeginTran(string transactionName) + { + CheckConnection(); + base.Transaction = ((SqlConnection)this.Connection).BeginTransaction(transactionName); + } + /// + /// Only SqlServer + /// + /// + /// + public override void BeginTran(IsolationLevel iso, string transactionName) + { + CheckConnection(); + base.Transaction = ((SqlConnection)this.Connection).BeginTransaction(iso, transactionName); + } + public override IDataAdapter GetAdapter() + { + return new SqlDataAdapter(); + } + public override DbCommand GetCommand(string sql, SugarParameter[] parameters) + { + SqlCommand sqlCommand = new SqlCommand(sql, (SqlConnection)this.Connection); + sqlCommand.CommandType = this.CommandType; + sqlCommand.CommandTimeout = this.CommandTimeOut; + if (this.Transaction != null) + { + sqlCommand.Transaction = (SqlTransaction)this.Transaction; + } + if (parameters.HasValue()) + { + SqlParameter[] ipars = GetSqlParameter(parameters); + sqlCommand.Parameters.AddRange(ipars); + } + CheckConnection(); + return sqlCommand; + } + public override async Task GetCommandAsync(string sql, SugarParameter[] parameters) + { + SqlCommand sqlCommand = new SqlCommand(sql, (SqlConnection)this.Connection); + sqlCommand.CommandType = this.CommandType; + sqlCommand.CommandTimeout = this.CommandTimeOut; + if (this.Transaction != null) + { + sqlCommand.Transaction = (SqlTransaction)this.Transaction; + } + if (parameters.HasValue()) + { + SqlParameter[] ipars = GetSqlParameter(parameters); + sqlCommand.Parameters.AddRange(ipars); + } + await CheckConnectionAsync().ConfigureAwait(false); + return sqlCommand; + } + public override void SetCommandToAdapter(IDataAdapter dataAdapter, DbCommand command) + { + ((SqlDataAdapter)dataAdapter).SelectCommand = (SqlCommand)command; + } + /// + /// if mysql return MySqlParameter[] pars + /// if sqlerver return SqlParameter[] pars ... + /// + /// + /// + public override IDataParameter[] ToIDbDataParameter(params SugarParameter[] parameters) + { + if (parameters == null || parameters.Length == 0) return null; + SqlParameter[] result = new SqlParameter[parameters.Length]; + int index = 0; + foreach (var parameter in parameters) + { + if (parameter.Value == null) parameter.Value = DBNull.Value; + var sqlParameter = new SqlParameter(); + sqlParameter.ParameterName = parameter.ParameterName; + //sqlParameter.UdtTypeName = parameter.UdtTypeName; + sqlParameter.Size = parameter.Size; + sqlParameter.Value = parameter.Value; + sqlParameter.DbType = parameter.DbType; + sqlParameter.Direction = parameter.Direction; + result[index] = sqlParameter; + if (sqlParameter.Direction.IsIn(ParameterDirection.Output, ParameterDirection.InputOutput, ParameterDirection.ReturnValue)) + { + if (this.OutputParameters == null) this.OutputParameters = new List(); + this.OutputParameters.RemoveAll(it => it.ParameterName == sqlParameter.ParameterName); + this.OutputParameters.Add(sqlParameter); + } + ++index; + } + return result; + } + /// + /// if mysql return MySqlParameter[] pars + /// if sqlerver return SqlParameter[] pars ... + /// + /// + /// + public SqlParameter[] GetSqlParameter(params SugarParameter[] parameters) + { + var isVarchar = this.Context.IsVarchar(); + if (parameters == null || parameters.Length == 0) return null; + SqlParameter[] result = new SqlParameter[parameters.Length]; + int index = 0; + foreach (var parameter in parameters) + { + if (parameter.Value == null) parameter.Value = DBNull.Value; + var sqlParameter = new SqlParameter(); + sqlParameter.ParameterName = parameter.ParameterName; + if (!string.IsNullOrEmpty(parameter.UdtTypeName)) + { + sqlParameter.UdtTypeName = parameter.UdtTypeName; + } + //sqlParameter.UdtTypeName = parameter.UdtTypeName; + sqlParameter.Size = parameter.Size; + sqlParameter.Value = parameter.Value; + sqlParameter.DbType = GetDbType(parameter); + var isTime = parameter.DbType == System.Data.DbType.Time; + if (isTime) + { + sqlParameter.SqlDbType = SqlDbType.Time; + if (sqlParameter.Value != DBNull.Value) + { + sqlParameter.Value = DateTime.Parse(parameter.Value?.ToString()).TimeOfDay; + } + } + else if (parameter.Value != null && parameter.Value is XElement) + { + sqlParameter.SqlDbType = SqlDbType.Xml; + sqlParameter.Value = (parameter.Value as XElement).ToString(); + } + if (sqlParameter.Value != null && sqlParameter.Value != DBNull.Value && sqlParameter.DbType == System.Data.DbType.DateTime) + { + var date = Convert.ToDateTime(sqlParameter.Value); + if (date == DateTime.MinValue) + { + sqlParameter.Value = UtilMethods.GetMinDate(this.Context.CurrentConnectionConfig); + } + } + if (parameter.Direction == 0) + { + parameter.Direction = ParameterDirection.Input; + } + sqlParameter.Direction = parameter.Direction; + result[index] = sqlParameter; + if (parameter.TypeName.HasValue()) + { + sqlParameter.TypeName = parameter.TypeName; + sqlParameter.SqlDbType = SqlDbType.Structured; + sqlParameter.DbType = System.Data.DbType.Object; + } + if (sqlParameter.Direction.IsIn(ParameterDirection.Output, ParameterDirection.InputOutput, ParameterDirection.ReturnValue)) + { + if (this.OutputParameters == null) this.OutputParameters = new List(); + this.OutputParameters.RemoveAll(it => it.ParameterName == sqlParameter.ParameterName); + this.OutputParameters.Add(sqlParameter); + } + + if (isVarchar && sqlParameter.DbType == System.Data.DbType.String) + { + sqlParameter.DbType = System.Data.DbType.AnsiString; + } + if (parameter.CustomDbType != null && parameter.CustomDbType is SqlDbType) + { + sqlParameter.SqlDbType = ((SqlDbType)parameter.CustomDbType); + } + if (sqlParameter.Direction == ParameterDirection.Output && parameter.Scale > 0) + { + sqlParameter.Scale = parameter.Scale; + } + ++index; + } + return result; + } + + private static System.Data.DbType GetDbType(SugarParameter parameter) + { + if (parameter.DbType == System.Data.DbType.UInt16) + { + return System.Data.DbType.Int16; + } + else if (parameter.DbType == System.Data.DbType.UInt32) + { + return System.Data.DbType.Int32; + } + else if (parameter.DbType == System.Data.DbType.UInt64) + { + return System.Data.DbType.Int64; + } + else + { + return parameter.DbType; + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Sqlite/CodeFirst/SqliteCodeFirst.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Sqlite/CodeFirst/SqliteCodeFirst.cs new file mode 100644 index 000000000..84192bbb3 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Sqlite/CodeFirst/SqliteCodeFirst.cs @@ -0,0 +1,178 @@ +namespace SqlSugar +{ + public class SqliteCodeFirst : CodeFirstProvider + { + public override void ExistLogic(EntityInfo entityInfo) + { + if (entityInfo.Columns.HasValue() && entityInfo.IsDisabledUpdateAll == false) + { + if (entityInfo.Columns.Where(it => it.IsPrimarykey).Count() > 1) + { + AddColumn(entityInfo); + return; + } + + var tableName = GetTableName(entityInfo); + var dbColumns = this.Context.DbMaintenance.GetColumnInfosByTableName(tableName, false); + ConvertColumns(dbColumns); + var entityColumns = entityInfo.Columns.Where(it => it.IsIgnore == false).ToList(); + var dropColumns = dbColumns + .Where(dc => !entityColumns.Any(ec => dc.DbColumnName.Equals(ec.OldDbColumnName, StringComparison.CurrentCultureIgnoreCase))) + .Where(dc => !entityColumns.Any(ec => dc.DbColumnName.Equals(ec.DbColumnName, StringComparison.CurrentCultureIgnoreCase))) + .ToList(); + var addColumns = entityColumns + .Where(ec => ec.OldDbColumnName.IsNullOrEmpty() || !dbColumns.Any(dc => dc.DbColumnName.Equals(ec.OldDbColumnName, StringComparison.CurrentCultureIgnoreCase))) + .Where(ec => !dbColumns.Any(dc => ec.DbColumnName.Equals(dc.DbColumnName, StringComparison.CurrentCultureIgnoreCase))).ToList(); + //var alterColumns = entityColumns + // .Where(ec => !dbColumns.Any(dc => dc.DbColumnName.Equals(ec.OldDbColumnName, StringComparison.CurrentCultureIgnoreCase))) + // .Where(ec => + // dbColumns.Any(dc => dc.DbColumnName.Equals(ec.DbColumnName) + // && ((!UtilMethods.GetUnderType(ec.PropertyInfo).IsEnum() && UtilMethods.GetUnderType(ec.PropertyInfo).IsIn(UtilConstants.StringType)) || + + // IsSamgeType(ec, dc)))).ToList(); + var renameColumns = entityColumns + .Where(it => !string.IsNullOrEmpty(it.OldDbColumnName)) + .Where(entityColumn => dbColumns.Any(dbColumn => entityColumn.OldDbColumnName.Equals(dbColumn.DbColumnName, StringComparison.CurrentCultureIgnoreCase))) + .ToList(); + + + var isChange = false; + foreach (var item in addColumns) + { + this.Context.DbMaintenance.AddColumn(tableName, EntityColumnToDbColumn(entityInfo, tableName, item)); + isChange = true; + } + foreach (var item in dropColumns) + { + //only support .net core + if (this.Context.CurrentConnectionConfig?.MoreSettings?.SqliteCodeFirstEnableDropColumn == true) + { + this.Context.DbMaintenance.DropColumn(tableName, item.DbColumnName); + isChange = true; + } + } + //foreach (var item in alterColumns) + //{ + // //this.Context.DbMaintenance.AddColumn(tableName, EntityColumnToDbColumn(entityInfo, tableName, item)); + // //isChange = true; + //} + foreach (var item in renameColumns) + { + throw new NotSupportedException("rename Column"); + } + + foreach (var item in entityColumns) + { + var dbColumn = dbColumns.FirstOrDefault(dc => dc.DbColumnName.Equals(item.DbColumnName, StringComparison.CurrentCultureIgnoreCase)); + if (dbColumn == null) continue; + bool pkDiff, idEntityDiff; + KeyAction(item, dbColumn, out pkDiff, out idEntityDiff); + if (dbColumn != null && pkDiff && !idEntityDiff) + { + var isAdd = item.IsPrimarykey; + if (isAdd) + { + this.Context.DbMaintenance.AddPrimaryKey(tableName, item.DbColumnName); + } + else + { + this.Context.DbMaintenance.DropConstraint(tableName, string.Format("PK_{0}_{1}", tableName, item.DbColumnName)); + } + } + else if (pkDiff || idEntityDiff) + { + ChangeKey(entityInfo, tableName, item); + } + } + if (isChange && base.IsBackupTable) + { + this.Context.DbMaintenance.BackupTable(tableName, tableName + DateTime.Now.ToString("yyyyMMddHHmmss"), MaxBackupDataRows); + } + } + } + + private void AddColumn(EntityInfo entityInfo) + { + var tableName2 = GetTableName(entityInfo); + var dbColumns2 = this.Context.DbMaintenance.GetColumnInfosByTableName(tableName2, false); + var entityColumns2 = entityInfo.Columns.Where(it => it.IsIgnore == false).ToList(); + ConvertColumns(dbColumns2); + var addColumns2 = entityColumns2 + .Where(ec => ec.OldDbColumnName.IsNullOrEmpty() || !dbColumns2.Any(dc => dc.DbColumnName.Equals(ec.OldDbColumnName, StringComparison.CurrentCultureIgnoreCase))) + .Where(ec => !dbColumns2.Any(dc => ec.DbColumnName.Equals(dc.DbColumnName, StringComparison.CurrentCultureIgnoreCase))).ToList(); + foreach (var item in addColumns2) + { + if (item.IsPrimarykey || item.IsIdentity) + { + Check.ExceptionEasy("Multiple primary keys cannot be modified", "多主键不能修改"); + } + this.Context.DbMaintenance.AddColumn(tableName2, EntityColumnToDbColumn(entityInfo, tableName2, item)); + } + } + + public override void NoExistLogic(EntityInfo entityInfo) + { + var tableName = GetTableName(entityInfo); + string backupName = tableName + DateTime.Now.ToString("yyyyMMddHHmmss"); + //Check.Exception(entityInfo.Columns.Where(it => it.IsPrimarykey).Count() > 1, "Use Code First ,The primary key must not exceed 1"); + List columns = new List(); + if (entityInfo.Columns.HasValue()) + { + foreach (var item in entityInfo.Columns.OrderBy(it => it.IsPrimarykey ? 0 : 1).Where(it => it.IsIgnore == false)) + { + DbColumnInfo dbColumnInfo = this.EntityColumnToDbColumn(entityInfo, tableName, item); + columns.Add(dbColumnInfo); + } + if (entityInfo.IsCreateTableFiledSort) + { + columns = columns.OrderBy(c => c.CreateTableFieldSort).ToList(); + columns = columns.OrderBy(it => it.IsPrimarykey ? 0 : 1).ToList(); + } + } + this.Context.DbMaintenance.CreateTable(tableName, columns, true); + } + protected override DbColumnInfo EntityColumnToDbColumn(EntityInfo entityInfo, string tableName, EntityColumnInfo item) + { + var propertyType = UtilMethods.GetUnderType(item.PropertyInfo); + var result = new DbColumnInfo() + { + TableId = entityInfo.Columns.IndexOf(item), + DbColumnName = item.DbColumnName.HasValue() ? item.DbColumnName : item.PropertyName, + IsPrimarykey = item.IsPrimarykey, + IsIdentity = item.IsIdentity, + TableName = tableName, + IsNullable = item.IsNullable, + DefaultValue = item.DefaultValue, + ColumnDescription = item.ColumnDescription, + Length = item.Length, + CreateTableFieldSort = item.CreateTableFieldSort + }; + GetDbType(item, propertyType, result); + if (result.DataType.Equals("varchar", StringComparison.CurrentCultureIgnoreCase) && result.Length == 0) + { + result.Length = 1; + } + return result; + } + + protected override void ConvertColumns(List dbColumns) + { + foreach (var item in dbColumns) + { + if (item.DataType == "DateTime") + { + item.Length = 0; + } + } + } + + protected override void ChangeKey(EntityInfo entityInfo, string tableName, EntityColumnInfo item) + { + this.Context.DbMaintenance.UpdateColumn(tableName, EntityColumnToDbColumn(entityInfo, tableName, item)); + if (!item.IsPrimarykey) + this.Context.DbMaintenance.DropConstraint(tableName, null); + if (item.IsPrimarykey) + this.Context.DbMaintenance.AddPrimaryKey(tableName, item.DbColumnName); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Sqlite/DbBind/SqliteDbBind.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Sqlite/DbBind/SqliteDbBind.cs new file mode 100644 index 000000000..79393d422 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Sqlite/DbBind/SqliteDbBind.cs @@ -0,0 +1,109 @@ +namespace SqlSugar +{ + public class SqliteDbBind : DbBindProvider + { + public override string GetDbTypeName(string csharpTypeName) + { + if (csharpTypeName == UtilConstants.ByteArrayType.Name) + { + return "blob"; + } + if (csharpTypeName == "Int32") + csharpTypeName = "int"; + if (csharpTypeName == "Int16") + csharpTypeName = "short"; + if (csharpTypeName == "Int64") + csharpTypeName = "long"; + if (csharpTypeName == "Boolean") + csharpTypeName = "bool"; + if (csharpTypeName == "DateTimeOffset") + csharpTypeName = "DateTime"; + var mappings = this.MappingTypes.Where(it => it.Value.ToString().Equals(csharpTypeName, StringComparison.CurrentCultureIgnoreCase)); + return mappings.Any() ? mappings.First().Key : "varchar"; + } + public override List> MappingTypes + { + get + { + var extService = this.Context.CurrentConnectionConfig.ConfigureExternalServices; + if (extService?.AppendDataReaderTypeMappings.HasValue() == true) + { + return extService.AppendDataReaderTypeMappings.Union(MappingTypesConst).ToList(); + } + else + { + return MappingTypesConst; + } + } + } + public static List> MappingTypesConst = new List>() + { + + new KeyValuePair("integer",CSharpDataType.@int), + new KeyValuePair("int",CSharpDataType.@int), + new KeyValuePair("int32",CSharpDataType.@int), + new KeyValuePair("integer32",CSharpDataType.@int), + new KeyValuePair("number",CSharpDataType.@int), + + new KeyValuePair("varchar",CSharpDataType.@string), + new KeyValuePair("varchar2",CSharpDataType.@string), + new KeyValuePair("nvarchar",CSharpDataType.@string), + new KeyValuePair("nvarchar2",CSharpDataType.@string), + new KeyValuePair("text",CSharpDataType.@string), + new KeyValuePair("ntext",CSharpDataType.@string), + new KeyValuePair("blob_text",CSharpDataType.@string), + new KeyValuePair("char",CSharpDataType.@string), + new KeyValuePair("nchar",CSharpDataType.@string), + new KeyValuePair("num",CSharpDataType.@string), + new KeyValuePair("currency",CSharpDataType.@string), + new KeyValuePair("datetext",CSharpDataType.@string), + new KeyValuePair("word",CSharpDataType.@string), + new KeyValuePair("graphic",CSharpDataType.@string), + + new KeyValuePair("tinyint",CSharpDataType.@byte), + new KeyValuePair("unsignedinteger8",CSharpDataType.@byte), + new KeyValuePair("smallint",CSharpDataType.@short), + new KeyValuePair("int16",CSharpDataType.@short), + new KeyValuePair("bigint",CSharpDataType.@long), + new KeyValuePair("int64",CSharpDataType.@long), + new KeyValuePair("long",CSharpDataType.@long), + new KeyValuePair("integer64",CSharpDataType.@long), + new KeyValuePair("bit",CSharpDataType.@bool), + new KeyValuePair("bool",CSharpDataType.@bool), + new KeyValuePair("boolean",CSharpDataType.@bool), + new KeyValuePair("real",CSharpDataType.@double), + new KeyValuePair("double",CSharpDataType.@double), + new KeyValuePair("float",CSharpDataType.@float), + new KeyValuePair("float",CSharpDataType.Single), + new KeyValuePair("decimal",CSharpDataType.@decimal), + new KeyValuePair("dec",CSharpDataType.@decimal), + new KeyValuePair("numeric",CSharpDataType.@decimal), + new KeyValuePair("money",CSharpDataType.@decimal), + new KeyValuePair("smallmoney",CSharpDataType.@decimal), + + new KeyValuePair("datetime",CSharpDataType.DateTime), + new KeyValuePair("timestamp",CSharpDataType.DateTime), + new KeyValuePair("date",CSharpDataType.DateTime), + new KeyValuePair("time",CSharpDataType.DateTime), + + new KeyValuePair("blob",CSharpDataType.byteArray), + new KeyValuePair("clob",CSharpDataType.byteArray), + new KeyValuePair("raw",CSharpDataType.byteArray), + new KeyValuePair("oleobject",CSharpDataType.byteArray), + new KeyValuePair("binary",CSharpDataType.byteArray), + new KeyValuePair("photo",CSharpDataType.byteArray), + new KeyValuePair("picture",CSharpDataType.byteArray), + + new KeyValuePair("uniqueidentifier",CSharpDataType.Guid), + new KeyValuePair("varchar",CSharpDataType.Guid), + new KeyValuePair("guid",CSharpDataType.Guid) + }; + public override List StringThrow + { + get + { + return new List() { "int32", "datetime", "decimal", "double", "byte" }; + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Sqlite/DbFirst/SqliteDbFirst.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Sqlite/DbFirst/SqliteDbFirst.cs new file mode 100644 index 000000000..528a8c111 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Sqlite/DbFirst/SqliteDbFirst.cs @@ -0,0 +1,6 @@ +namespace SqlSugar +{ + public class SqliteDbFirst : DbFirstProvider + { + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Sqlite/DbMaintenance/SqliteDbMaintenance.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Sqlite/DbMaintenance/SqliteDbMaintenance.cs new file mode 100644 index 000000000..5acf1377e --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Sqlite/DbMaintenance/SqliteDbMaintenance.cs @@ -0,0 +1,610 @@ +using Microsoft.Data.Sqlite; + +using System.Data; +using System.Data.Common; +using System.Text.RegularExpressions; + +namespace SqlSugar +{ + public class SqliteDbMaintenance : DbMaintenanceProvider + { + #region DML + protected override string GetDataBaseSql + { + get + { + throw new NotSupportedException(); + } + } + protected override string GetColumnInfosByTableNameSql + { + get + { + throw new NotSupportedException(); + } + } + protected override string GetTableInfoListSql + { + get + { + return @"select Name from sqlite_master where type='table' and name<>'sqlite_sequence' order by name;"; + } + } + protected override string GetViewInfoListSql + { + get + { + return @"select Name from sqlite_master where type='view' order by name;"; + } + } + #endregion + + #region DDL + protected override string CreateDataBaseSql + { + get + { + return "CREATE DATABASE {0}"; + } + } + protected override string AddPrimaryKeySql + { + get + { + throw new NotSupportedException(); + } + } + protected override string AddColumnToTableSql + { + get + { + return "ALTER TABLE {0} ADD COLUMN {1} {2}{3}"; + } + } + protected override string AlterColumnToTableSql + { + get + { + // return "ALTER TABLE {0} ALTER COLUMN {1} {2}{3} {4} {5} {6}"; + throw new NotSupportedException(); + } + } + protected override string BackupDataBaseSql + { + get + { + throw new NotSupportedException(); + } + } + protected override string CreateTableSql + { + get + { + return "CREATE TABLE IF NOT EXISTS {0}(\r\n{1} $PrimaryKey )"; + } + } + protected override string CreateTableColumn + { + get + { + return "{0} {1}{2} {3} {4} {5}"; + } + } + protected override string TruncateTableSql + { + get + { + return "DELETE FROM {0}"; + } + } + protected override string BackupTableSql + { + get + { + return " CREATE TABLE {0} AS SELECT * FROM {1} limit 0,{2}"; + } + } + protected override string DropTableSql + { + get + { + return "DROP TABLE {0}"; + } + } + protected override string DropColumnToTableSql + { + get + { + return "ALTER TABLE {0} DROP {1}"; + } + } + protected override string DropConstraintSql + { + get + { + throw new NotSupportedException(); + } + } + protected override string RenameColumnSql + { + get + { + throw new NotSupportedException(); + } + } + protected override string IsAnyProcedureSql => throw new NotImplementedException(); + #endregion + + #region Check + protected override string CheckSystemTablePermissionsSql + { + get + { + return "select Name from sqlite_master limit 0,1"; + } + } + #endregion + + #region Scattered + protected override string CreateTableNull + { + get + { + return "NULL"; + } + } + protected override string CreateTableNotNull + { + get + { + return "NOT NULL"; + } + } + protected override string CreateTablePirmaryKey + { + get + { + return "PRIMARY KEY"; + } + } + protected override string CreateTableIdentity + { + get + { + return "AUTOINCREMENT"; + } + } + + protected override string AddColumnRemarkSql + { + get + { + throw new NotSupportedException(); + } + } + + protected override string DeleteColumnRemarkSql + { + get + { + throw new NotSupportedException(); + } + } + + protected override string IsAnyColumnRemarkSql + { + get + { + throw new NotSupportedException(); + } + } + + protected override string AddTableRemarkSql + { + get + { + throw new NotSupportedException(); + } + } + + protected override string DeleteTableRemarkSql + { + get + { + throw new NotSupportedException(); + } + } + + protected override string IsAnyTableRemarkSql + { + get + { + throw new NotSupportedException(); + } + } + + protected override string RenameTableSql + { + get + { + return "alter table {0} rename to {1}"; + } + } + + protected override string CreateIndexSql + { + get + { + return "CREATE {3} INDEX Index_{0}_{2} ON {0}({1})"; + } + } + + protected override string IsAnyIndexSql + { + get + { + return "SELECT count(*) FROM sqlite_master WHERE name = '{0}'"; + } + } + + protected override string AddDefaultValueSql => throw new NotSupportedException(" Sqlite no support default value"); + #endregion + + #region Methods + public override bool UpdateColumn(string tableName, DbColumnInfo column) + { + var isTran = this.Context.Ado.IsNoTran(); + try + { + if (column.IsPrimarykey) + { + Check.ExceptionEasy("Sqlite no support alter column primary key", "Sqlite不支持修改主键"); + } + + if (isTran) + // Start a transaction + this.Context.Ado.BeginTran(); + + tableName = tableName ?? column.TableName; + var oldColumn = column.DbColumnName; + var tempColumn = "Column" + SnowFlakeSingle.Instance.NextId(); + + // Step 1: Add a new column + column.DbColumnName = tempColumn; + column.DefaultValue = null; + this.AddColumn(tableName, column); + + // Step 2: Update values from old column to new column + this.Context.Ado.ExecuteCommand($"UPDATE {SqlBuilder.GetTranslationColumnName(tableName)} SET {SqlBuilder.GetTranslationColumnName(column.DbColumnName)}={SqlBuilder.GetTranslationColumnName(oldColumn)}"); + + // Step 3: Drop the old column + this.DropColumn(tableName, oldColumn); + + // Step 4: Rename the new column to the old column name + column.DbColumnName = oldColumn; + this.AddColumn(tableName, column); + + // Step 5: Update values from temporary column to the new column + this.Context.Ado.ExecuteCommand($"UPDATE {SqlBuilder.GetTranslationColumnName(tableName)} SET {SqlBuilder.GetTranslationColumnName(column.DbColumnName)}={SqlBuilder.GetTranslationColumnName(tempColumn)}"); + + //Step 6: Drop the temporary column + this.DropColumn(tableName, tempColumn); + + if (isTran) + // Commit the transaction + this.Context.Ado.CommitTran(); + + return true; + } + catch (Exception) + { + if (isTran) + // Handle exceptions, log, or rollback the transaction if necessary + this.Context.Ado.RollbackTran(); + // Log the exception or throw it again based on your requirements + throw; + } + } + public override List GetDbTypes() + { + return this.Context.Ado.SqlQuery(@"SELECT 'TEXT' AS Data_Type +UNION +SELECT 'INTEGER' +UNION +SELECT 'REAL' +UNION +SELECT 'BLOB' +UNION SELECT 'bigint' + UNION SELECT 'binary' + UNION SELECT 'bit' + UNION SELECT 'char' + UNION SELECT 'date' + UNION SELECT 'datetime' + UNION SELECT 'datetime2' + UNION SELECT 'datetimeoffset' + UNION SELECT 'decimal' + UNION SELECT 'float' + UNION SELECT 'image' + UNION SELECT 'int' + UNION SELECT 'money' + UNION SELECT 'nchar' + UNION SELECT 'ntext' + UNION SELECT 'numeric' + UNION SELECT 'nvarchar' + UNION SELECT 'smalldatetime' + UNION SELECT 'smallint' + UNION SELECT 'text' + UNION SELECT 'time' + UNION SELECT 'timestamp' + UNION SELECT 'tinyint' + UNION SELECT 'uniqueidentifier' + UNION SELECT 'varbinary' + UNION SELECT 'varchar' + UNION SELECT 'xml' "); + } + public override List GetTriggerNames(string tableName) + { + return this.Context.Ado.SqlQuery(@"SELECT name +FROM sqlite_master +WHERE type = 'trigger' +AND sql LIKE '%" + tableName + "%'"); + } + public override List GetFuncList() + { + return new List(); + } + public override List GetIndexList(string tableName) + { + var sql = $"PRAGMA index_list('{tableName}');"; + return this.Context.Ado.SqlQuery(sql); + } + public override void AddDefaultValue(EntityInfo entityInfo) + { + //sqlite no support AddDefaultValue + } + public override bool AddDefaultValue(string tableName, string columnName, string defaultValue) + { + Console.WriteLine("sqlite no support AddDefaultValue"); + return true; + } + public override bool TruncateTable(string tableName) + { + base.TruncateTable(tableName);//delete data + try + { + //clear sqlite identity + return this.Context.Ado.ExecuteCommand($"UPDATE sqlite_sequence SET seq = 0 WHERE name = '{tableName}'") > 0; + } + catch + { + //if no identity sqlite_sequence + return true; + } + } + /// + ///by current connection string + /// + /// + /// + /// + public override bool CreateDatabase(string databaseName, string databaseDirectory = null) + { + var connString = this.Context.CurrentConnectionConfig.ConnectionString; + var path = Regex.Match(connString, @"[a-z,A-Z]\:\\.+\\").Value; + if (path.IsNullOrEmpty()) + { + path = Regex.Match(connString, @"\/.+\/").Value; + } + if (path.IsNullOrEmpty()) + { + path = Regex.Match(connString, @"[a-z,A-Z]\:\\").Value; + } + if (!FileHelper.IsExistDirectory(path)) + { + FileHelper.CreateDirectory(path); + } + this.Context.Ado.Connection.Open(); + this.Context.Ado.Connection.Close(); + return true; + } + public override List GetColumnInfosByTableName(string tableName, bool isCache = true) + { + string cacheKey = "DbMaintenanceProvider.GetColumnInfosByTableName." + this.SqlBuilder.GetNoTranslationColumnName(tableName).ToLower(); + cacheKey = GetCacheKey(cacheKey); + if (isCache) + { + return this.Context.Utilities.GetReflectionInoCacheInstance().GetOrCreate>(cacheKey, () => + { + return GetColumnInfosByTableName(tableName); + + }); + } + else + { + return GetColumnInfosByTableName(tableName); + } + } + + private List GetColumnInfosByTableName(string tableName) + { + //var columns = GetColumnsByTableName2(tableName); + string sql = "PRAGMA table_info(" + SqlBuilder.GetTranslationColumnName(tableName) + ")"; + var oldIsEnableLog = this.Context.Ado.IsEnableLogEvent; + this.Context.Ado.IsEnableLogEvent = false; + var tableSript = this.Context.Ado.GetString($"SELECT sql FROM sqlite_master WHERE name='{tableName}' AND type='table'"); + using (DbDataReader dataReader = (SqliteDataReader)this.Context.Ado.GetDataReader(sql)) + { + this.Context.Ado.IsEnableLogEvent = oldIsEnableLog; + List result = new List(); + while (dataReader.Read()) + { + var type = dataReader.GetValue(2).ObjToString(); + var length = 0; + var decimalDigits = 0; + if (type.Contains('(')) + { + if (type.Contains(',')) + { + var digit = type.Split('(').Last().TrimEnd(')'); + decimalDigits = digit.Split(',').Last().ObjToInt(); + length = digit.Split(',').First().ObjToInt(); + } + else + { + length = type.Split('(').Last().TrimEnd(')').ObjToInt(); + } + type = type.Split('(').First(); + } + //bool isIdentity = columns.FirstOrDefault(it => it.DbColumnName.Equals(dataReader.GetString(1),StringComparison.CurrentCultureIgnoreCase)).IsIdentity; + DbColumnInfo column = new DbColumnInfo() + { + TableName = this.SqlBuilder.GetNoTranslationColumnName(tableName + ""), + DataType = type, + IsNullable = !dataReader.GetBoolean(3), + IsIdentity = tableSript.Contains("AUTOINCREMENT") && dataReader.GetBoolean(5).ObjToBool(), + ColumnDescription = null, + DbColumnName = dataReader.GetString(1), + DefaultValue = dataReader.GetValue(4).ObjToString(), + IsPrimarykey = dataReader.GetBoolean(5).ObjToBool(), + Length = length, + DecimalDigits = decimalDigits, + Scale = decimalDigits + }; + result.Add(column); + } + return result; + } + } + private List GetColumnsByTableName2(string tableName) + { + tableName = SqlBuilder.GetTranslationTableName(tableName); + string sql = "select * from " + tableName + " limit 0,1"; + var oldIsEnableLog = this.Context.Ado.IsEnableLogEvent; + this.Context.Ado.IsEnableLogEvent = false; + using (DbDataReader reader = (SqliteDataReader)this.Context.Ado.GetDataReader(sql)) + { + this.Context.Ado.IsEnableLogEvent = oldIsEnableLog; + List result = new List(); + var schemaTable = reader.GetSchemaTable(); + foreach (DataRow row in schemaTable.Rows) + { + DbColumnInfo column = new DbColumnInfo() + { + TableName = tableName, + IsIdentity = (bool)row["IsAutoIncrement"], + DbColumnName = row["ColumnName"].ToString(), + }; + result.Add(column); + } + return result; + } + } + public override bool AddRemark(EntityInfo entity) + { + return true; + } + + + public override bool BackupTable(string oldTableName, string newTableName, int maxBackupDataRows = int.MaxValue) + { + oldTableName = this.SqlBuilder.GetTranslationTableName(oldTableName); + newTableName = this.SqlBuilder.GetTranslationTableName(newTableName); + string sql = string.Format(this.BackupTableSql, newTableName, oldTableName, maxBackupDataRows); + this.Context.Ado.ExecuteCommand(sql); + return true; + } + public override bool CreateTable(string tableName, List columns, bool isCreatePrimaryKey = true) + { + if (columns.HasValue()) + { + foreach (var item in columns) + { + //if (item.DbColumnName.Equals("GUID", StringComparison.CurrentCultureIgnoreCase)) + //{ + // item.Length = 20; + //} + if (item.IsIdentity && !item.IsPrimarykey) + { + Check.Exception(true, "Identity only primary key"); + } + } + } + string sql = GetCreateTableSql(tableName, columns); + string primaryKeyInfo = null; + + if (!isCreatePrimaryKey || columns.Count(it => it.IsPrimarykey) > 1) + { + sql = sql.Replace("PRIMARY KEY AUTOINCREMENT", "").Replace("PRIMARY KEY", ""); + } + + if (columns.Count(it => it.IsPrimarykey) > 1 && isCreatePrimaryKey) + { + primaryKeyInfo = string.Format(",\r\n Primary key({0})", string.Join(",", columns.Where(it => it.IsPrimarykey).Select(it => this.SqlBuilder.GetTranslationColumnName(it.DbColumnName)))); + primaryKeyInfo = primaryKeyInfo.Replace('`', '\"'); + } + + sql = sql.Replace("$PrimaryKey", primaryKeyInfo); + + this.Context.Ado.ExecuteCommand(sql); + return true; + } + protected override string GetCreateTableSql(string tableName, List columns) + { + List columnArray = new List(); + Check.Exception(columns.IsNullOrEmpty(), "No columns found "); + foreach (var item in columns) + { + string columnName = item.DbColumnName; + string dataType = item.DataType; + if (dataType == "varchar" && item.Length == 0) + { + item.Length = 1; + } + string dataSize = item.Length > 0 ? string.Format("({0})", item.Length) : null; + string nullType = item.IsNullable ? this.CreateTableNull : CreateTableNotNull; + string primaryKey = item.IsPrimarykey ? this.CreateTablePirmaryKey : null; + string identity = item.IsIdentity ? this.CreateTableIdentity : null; + string addItem = string.Format(this.CreateTableColumn, this.SqlBuilder.GetTranslationColumnName(columnName), dataType, dataSize, nullType, primaryKey, identity); + if (item.DefaultValue.HasValue() && this.Context.CurrentConnectionConfig?.MoreSettings?.SqliteCodeFirstEnableDefaultValue == true) + { + var value = item.DefaultValue; + if (!value.Contains('(') && !value.EqualCase("CURRENT_TIMESTAMP") && !value.StartsWith('\'')) + { + value = value.ToSqlValue(); + } + addItem = $"{addItem} DEFAULT {value}"; + } + if (item.ColumnDescription.HasValue() && this.Context.CurrentConnectionConfig?.MoreSettings?.SqliteCodeFirstEnableDescription == true) + { + addItem = $"{addItem} /*{item.ColumnDescription.Replace("\r", "").Replace("\n", "")}*/ "; + } + columnArray.Add(addItem); + } + string tableString = string.Format(this.CreateTableSql, this.SqlBuilder.GetTranslationTableName(tableName), string.Join(",\r\n", columnArray)); + tableString = tableString.Replace('`', '\"'); + return tableString; + } + public override bool IsAnyConstraint(string constraintName) + { + throw new NotSupportedException("MySql IsAnyConstraint NotSupportedException"); + } + public override bool BackupDataBase(string databaseName, string fullFileName) + { + this.Context.Ado.ExecuteCommand($"PRAGMA main.page_size=1024; PRAGMA main.locking_mode=EXCLUSIVE; PRAGMA main.cache_size=5000; PRAGMA main.synchronous=NORMAL; PRAGMA main.journal_mode=WAL; VACUUM INTO '{fullFileName.ToSqlFilter()}'"); + return false; + } + //private List GetListOrCache(string cacheKey, string sql) + //{ + // return this.Context.Utilities.GetReflectionInoCacheInstance().GetOrCreate(cacheKey, + // () => + // { + // var isEnableLogEvent = this.Context.Ado.IsEnableLogEvent; + // this.Context.Ado.IsEnableLogEvent = false; + // var reval = this.Context.Ado.SqlQuery(sql); + // this.Context.Ado.IsEnableLogEvent = isEnableLogEvent; + // return reval; + // }); + //} + #endregion + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Sqlite/Queryable/SqliteQueryable.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Sqlite/Queryable/SqliteQueryable.cs new file mode 100644 index 000000000..b1c052800 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Sqlite/Queryable/SqliteQueryable.cs @@ -0,0 +1,60 @@ +namespace SqlSugar +{ + public class SqliteQueryable : QueryableProvider + { + public override ISugarQueryable With(string withString) + { + return this; + } + + public override ISugarQueryable PartitionBy(string groupFileds) + { + this.GroupBy(groupFileds); + return this; + } + } + public class SqliteQueryable : QueryableProvider + { + + } + public class SqliteQueryable : QueryableProvider + { + + } + public class SqliteQueryable : QueryableProvider + { + + } + public class SqliteQueryable : QueryableProvider + { + + } + public class SqliteQueryable : QueryableProvider + { + + } + public class SqliteQueryable : QueryableProvider + { + + } + public class SqliteQueryable : QueryableProvider + { + + } + public class SqliteQueryable : QueryableProvider + { + + } + public class SqliteQueryable : QueryableProvider + { + + } + public class SqliteQueryable : QueryableProvider + { + + } + public class SqliteQueryable : QueryableProvider + { + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Sqlite/SqlBuilder/SqliteBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Sqlite/SqlBuilder/SqliteBuilder.cs new file mode 100644 index 000000000..95bb0def9 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Sqlite/SqlBuilder/SqliteBuilder.cs @@ -0,0 +1,39 @@ +namespace SqlSugar +{ + public class SqliteBuilder : SqlBuilderProvider + { + public override string SqlTranslationLeft { get { return "`"; } } + public override string SqlTranslationRight { get { return "`"; } } + public override string SqlDateNow + { + get + { + return " DATETIME('now', 'localtime') "; + } + } + public override string FullSqlDateNow + { + get + { + return "select DATETIME('now', 'localtime') "; + } + } + public override string RemoveParentheses(string sql) + { + if (sql.StartsWith('(') && sql.EndsWith(')') && sql.Contains("limit", StringComparison.CurrentCultureIgnoreCase)) + { + sql = $" select * from {sql} sqlite_temp_table "; + } + if (sql.StartsWith('(') && sql.EndsWith(')')) + { + sql = sql.Substring(1, sql.Length - 2); + } + + return sql; + } + public override string RemoveN(string sql) + { + return sql?.Replace("N", ""); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Sqlite/SqlBuilder/SqliteDeleteBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Sqlite/SqlBuilder/SqliteDeleteBuilder.cs new file mode 100644 index 000000000..9c70f83af --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Sqlite/SqlBuilder/SqliteDeleteBuilder.cs @@ -0,0 +1,7 @@ +namespace SqlSugar +{ + public class SqliteDeleteBuilder : DeleteBuilder + { + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Sqlite/SqlBuilder/SqliteExpressionContext.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Sqlite/SqlBuilder/SqliteExpressionContext.cs new file mode 100644 index 000000000..8e8cb74a0 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Sqlite/SqlBuilder/SqliteExpressionContext.cs @@ -0,0 +1,347 @@ +namespace SqlSugar +{ + public class SqliteExpressionContext : ExpressionContext, ILambdaExpressions + { + public override ExpressionContextCase Case { get; set; } = new ExpressionContextCase() + { + IsDateString = true, + }; + public SqlSugarProvider Context { get; set; } + public SqliteExpressionContext() + { + base.DbMehtods = new SqliteMethod(); + } + public override string SqlTranslationLeft { get { return "`"; } } + public override string SqlTranslationRight { get { return "`"; } } + } + public class SqliteMethod : DefaultDbMethod, IDbMethods + { + public override string UNIX_TIMESTAMP(MethodCallExpressionModel model) + { + var parameterNameA = model.Args[0].MemberName; + return $" CAST(STRFTIME('%s', {parameterNameA}) AS INTEGER) "; + } + public override string WeekOfYear(MethodCallExpressionModel mode) + { + var parameterNameA = mode.Args[0].MemberName; + return $"STRFTIME('%W', {parameterNameA})+1 "; + } + public override string Equals(MethodCallExpressionModel model) + { + var result = base.Equals(model); + if (model.Args.Count == 3 && result.Trim().Last() == ')') + { + result = (" " + result.Trim().TrimEnd(')') + " COLLATE NOCASE ) "); + } + return result; + } + public override string JsonIndex(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter1 = model.Args[1]; + return $"json_extract({parameter.MemberName}, '$[{parameter1.MemberValue}]')"; + } + public override string JsonField(MethodCallExpressionModel model) + { + var hasPrefix = model.Args[1].MemberValue is string v ? v[0] == '$' : false; + model.Parameters.RemoveAll(item => item.ParameterName == model.Args[1].MemberName + ""); + return string.Format("json_extract({0},'{1}{2}')", + model.Args[0].MemberName, + hasPrefix ? string.Empty : string.Intern("$."), + model.Args[1].MemberValue); + } + public override string GetStringJoinSelector(string result, string separator) + { + return $"group_concat({result},'{separator}') "; + } + public override string DateDiff(MethodCallExpressionModel model) + { + var parameter = (DateType)(Enum.Parse(typeof(DateType), model.Args[0].MemberValue.ObjToString())); + var begin = model.Args[1].MemberName; + var end = model.Args[2].MemberName; + switch (parameter) + { + case DateType.Year: + return $" ( strftime('%Y',{end}) - strftime('%Y',{begin}) )"; + case DateType.Month: + return $" ( (strftime('%m',{end}) - strftime('%m',{begin}))+(strftime('%Y',{end}) - strftime('%Y',{begin}))*12 )"; + case DateType.Day: + return $" (julianday( strftime('%Y-%m-%d',datetime({end})) )-julianday(strftime('%Y-%m-%d',datetime({begin})))) "; + case DateType.Hour: + return $" ((julianday( strftime('%Y-%m-%d %H:%M',datetime({end})) )- julianday(strftime('%Y-%m-%d %H:%M',datetime({begin}))))*24 )"; + case DateType.Minute: + return $" ((julianday( strftime('%Y-%m-%d %H:%M',datetime({end})) )- julianday(strftime('%Y-%m-%d %H:%M',datetime({begin}))))*24*60 )"; + case DateType.Second: + return $" ((julianday( strftime('%Y-%m-%d %H:%M:%S',datetime({end})) )- julianday(strftime('%Y-%m-%d %H:%M:%S',datetime({begin}))))*24*60*60 )"; + case DateType.Millisecond: + break; + default: + break; + } + throw new Exception(parameter + " datediff no support"); + } + public override string Length(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format("LENGTH({0})", parameter.MemberName); + } + + public override string Substring(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + var parameter3 = model.Args[2]; + return string.Format("SUBSTR({0},1 + {1},{2})", parameter.MemberName, parameter2.MemberName, parameter3.MemberName); + } + + public override string Contains(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + return string.Format(" ({0} like '%'||{1}||'%') ", parameter.MemberName, parameter2.MemberName); + } + + public override string StartsWith(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + return string.Format(" ({0} like {1}||'%') ", parameter.MemberName, parameter2.MemberName); + } + + public override string EndsWith(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + return string.Format(" ({0} like '%'||{1}) ", parameter.MemberName, parameter2.MemberName); + } + + public override string ToInt32(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS INTEGER)", parameter.MemberName); + } + + public override string ToInt64(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS INTEGER)", parameter.MemberName); + } + + public override string ToString(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS TEXT)", parameter.MemberName); + } + + public override string ToGuid(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS TEXT)", parameter.MemberName); + } + + public override string ToDouble(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS DECIMAL(18,4))", parameter.MemberName); + } + + public override string ToBool(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS SIGNED)", parameter.MemberName); + } + + public override string ToDecimal(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS DECIMAL(18,4))", parameter.MemberName); + } + + public override string ToDate(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" DATETIME({0})", parameter.MemberName); + } + + public override string ToDateShort(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" strftime('%Y-%m-%d', {0})", parameter.MemberName); + } + + public override string DateAddDay(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + if (parameter2.MemberValue.ObjToInt() < 0) + { + return string.Format(" DATE(DATETIME({0}), '-{1} days')", parameter.MemberName, Math.Abs(parameter2.MemberValue.ObjToInt())); + } + else + { + return string.Format(" DATE(DATETIME({0}), '+{1} days')", parameter.MemberName, parameter2.MemberValue); + } + } + + public override string DateAddByType(MethodCallExpressionModel model) + { + var parameter = model.Args[0].MemberName; + var parameter2 = model.Args[1].MemberValue; + var parameter3 = model.Args[2].MemberValue; + if (parameter2.ObjToInt() < 0) + { + return string.Format(" DATETIME(DATETIME({0}), '-{1} {2}s')", parameter, Math.Abs(parameter2.ObjToInt()), parameter3); + } + else + { + return string.Format(" DATETIME(DATETIME({0}), '+{1} {2}s')", parameter, parameter2, parameter3); + } + } + + public override string DateValue(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var typeName = model.Args[1].MemberValue.ToString(); + var parameter2 = typeName; + var type = (DateType)Enum.Parse(typeof(DateType), parameter2, false); + switch (type) + { + case DateType.Year: + parameter2 = "%Y"; + break; + case DateType.Month: + parameter2 = "%m"; + break; + case DateType.Day: + parameter2 = "%d"; + return string.Format(" CAST(STRFTIME('{1}', DATETIME(DATETIME(strftime('%Y-%m-%d', {0})), 'LOCALTIME')) AS INTEGER)", parameter.MemberName, parameter2); + case DateType.Hour: + parameter2 = "%H"; + break; + case DateType.Second: + parameter2 = "%S"; + break; + case DateType.Minute: + parameter2 = "%M"; + break; + case DateType.Weekday: + return $" cast (strftime('%w', {parameter.MemberName}) as integer) "; + case DateType.Millisecond: + default: + Check.ThrowNotSupportedException(typeName); + break; + } + return string.Format(" CAST(STRFTIME('{1}', DATETIME({0}) ) AS INTEGER)", parameter.MemberName, parameter2); + } + + public override string DateIsSameDay(MethodCallExpressionModel model) + { + var parameter = model.Args[0].MemberName; + var parameter2 = model.Args[1].MemberName; + int time = 1; + return string.Format(" strftime('%Y-%m-%d', {0})= strftime('%Y-%m-%d', {1}) ", parameter, parameter2, time); + } + public override string DateIsSameByType(MethodCallExpressionModel model) + { + var parameter = model.Args[0].MemberName; + var parameter2 = model.Args[1].MemberName; + var parameter3 = model.Args[2].MemberValue; + var type = (DateType)Enum.Parse(typeof(DateType), parameter3.ObjToString(), false); + int time = 1; + switch (type) + { + case DateType.Year: + time = time * 1 / 365; + break; + case DateType.Month: + time = time * 1 / 30; + break; + case DateType.Day: + break; + case DateType.Hour: + time = time * 24; + break; + case DateType.Second: + time = time * 24 * 60 * 60; + break; + case DateType.Minute: + time = time * 24 * 60; + break; + case DateType.Millisecond: + time = time * 24 * 60 * 60 * 1000; + break; + } + return string.Format(" Cast((JulianDay({0}) - JulianDay({1})) *{2} As INTEGER)", parameter, parameter2, time); + } + + public override string MergeString(params string[] strings) + { + return string.Join("||", strings).Replace("+", ""); + } + + public override string IsNull(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter1 = model.Args[1]; + return string.Format("IFNULL({0},{1})", parameter.MemberName, parameter1.MemberName); + } + + public override string GetDate() + { + return "DATETIME('now', 'localtime')"; + } + + public override string GetRandom() + { + return "RANDOM()"; + } + + public override string CharIndex(MethodCallExpressionModel model) + { + var parameterNameA = model.Args[0].MemberName; + var parameterNameB = model.Args[1].MemberName; + return $" INSTR(LOWER({parameterNameA}), LOWER({parameterNameB})) "; + } + + public override string TrimEnd(MethodCallExpressionModel mode) + { + var parameterNameA = mode.Args[0].MemberName; + var parameterNameB = mode.Args[1].MemberName; + return $" CASE WHEN SUBSTR({parameterNameA}, -1) = {parameterNameB} THEN SUBSTR({parameterNameA}, 1, LENGTH({parameterNameA}) - 1) ELSE {parameterNameA} END "; + } + public override string TrimStart(MethodCallExpressionModel mode) + { + + var parameterNameA = mode.Args[0].MemberName; + var parameterNameB = mode.Args[1].MemberName; + return $" CASE WHEN SUBSTR({parameterNameA}, 1, 1) ={parameterNameB} THEN SUBSTR({parameterNameA}, 2) ELSE {parameterNameA} END "; + } + + public override string PadLeft(MethodCallExpressionModel mode) + { + var parameterNameA = mode.Args[0].MemberName; + var parameterNameB = mode.Args[1].MemberName; + var parameterNameC = mode.Args[2].MemberName; + var value = new string[mode.Args[1].MemberValue.ObjToInt()].Select(it => parameterNameC); + return $"substr({string.Join("||", value)} || {parameterNameA}, {parameterNameB}*-1) "; + } + + public override string Left(MethodCallExpressionModel mode) + { + var parameterNameA = mode.Args[0].MemberName; + var parameterNameB = mode.Args[1].MemberName; + return $" SUBSTR({parameterNameA}, 1, {parameterNameB}) "; + } + public override string Right(MethodCallExpressionModel mode) + { + var parameterNameA = mode.Args[0].MemberName; + var parameterNameB = mode.Args[1].MemberName; + return $" SUBSTR({parameterNameA}, -2, {parameterNameB}) "; + } + + public override string NewUid(MethodCallExpressionModel mode) + { + return " substr(upper(hex(randomblob(4))), 1, 8) || '-' ||\r\n substr(upper(hex(randomblob(2))), 1, 4) || '-' ||\r\n '4' || substr(upper(hex(randomblob(2))), 2, 3) || '-' ||\r\n substr('89ab', 1 + (abs(random()) % 4), 1) || substr(upper(hex(randomblob(2))), 2, 3) || '-' ||\r\n substr(upper(hex(randomblob(6))), 1, 12) "; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Sqlite/SqlBuilder/SqliteFastBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Sqlite/SqlBuilder/SqliteFastBuilder.cs new file mode 100644 index 000000000..1ef722003 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Sqlite/SqlBuilder/SqliteFastBuilder.cs @@ -0,0 +1,212 @@ +using Microsoft.Data.Sqlite; + +using System.Data; + +namespace SqlSugar +{ + public class SqliteFastBuilder : IFastBuilder + { + public EntityInfo FastEntityInfo { get; set; } + private EntityInfo entityInfo; + private bool IsUpdate = false; + public string CharacterSet { get; set; } + private DataTable UpdateDataTable { get; set; } + public bool IsActionUpdateColumns { get; set; } + public DbFastestProperties DbFastestProperties { get; set; } = new DbFastestProperties() { IsNoCopyDataTable = true }; + public SqliteFastBuilder(EntityInfo entityInfo) + { + this.entityInfo = entityInfo; + } + + public SqlSugarProvider Context { get; set; } + + public void CloseDb() + { + if (this.Context.CurrentConnectionConfig.IsAutoCloseConnection && this.Context.Ado.Transaction == null) + { + this.Context.Close(); + } + } + + public async Task CreateTempAsync(DataTable dt) where T : class, new() + { + await Task.Delay(0).ConfigureAwait(false); + IsUpdate = true; + } + + + public async Task ExecuteBulkCopyAsync(DataTable dt) + { + if (dt.Rows.Count == 0 || IsUpdate) + { + this.UpdateDataTable = dt; + return 0; + } + foreach (var item in this.entityInfo.Columns) + { + if (item.IsIdentity && dt.Columns.Contains(item.DbColumnName)) + { + dt.Columns.Remove(item.DbColumnName); + } + } + var dictionary = this.Context.Utilities.DataTableToDictionaryList(dt.Rows.Cast().Take(1).CopyToDataTable()); + int result = 0; + var cn = this.Context.Ado.Connection as SqliteConnection; + Open(cn); + if (this.Context.Ado.Transaction == null) + { + using (var transaction = cn.BeginTransaction()) + { + result = await _BulkCopy(dt, dictionary, result, cn).ConfigureAwait(false); + transaction.Commit(); + } + } + else + { + result = await _BulkCopy(dt, dictionary, result, cn).ConfigureAwait(false); + } + return result; + } + + private async Task _BulkCopy(DataTable dt, List> dictionary, int i, SqliteConnection cn) + { + using (var cmd = cn.CreateCommand()) + { + if (this.Context?.CurrentConnectionConfig?.MoreSettings?.IsCorrectErrorSqlParameterName == true) + { + foreach (DataRow item in dt.Rows) + { + cmd.CommandText = this.Context.Insertable(UtilMethods.DataRowToDictionary(item)).AS(dt.TableName).ToSqlString().Replace(";SELECT LAST_INSERT_ROWID();", ""); + i += await cmd.ExecuteNonQueryAsync().ConfigureAwait(false); + } + } + else + { + cmd.CommandText = this.Context.Insertable(dictionary.First()).AS(dt.TableName).ToSql().Key.Replace(";SELECT LAST_INSERT_ROWID();", ""); + foreach (DataRow dataRow in dt.Rows) + { + foreach (DataColumn item in dt.Columns) + { + if (IsBoolTrue(dataRow, item)) + { + cmd.Parameters.AddWithValue("@" + item.ColumnName, true); + } + else if (IsBoolFalse(dataRow, item)) + { + cmd.Parameters.AddWithValue("@" + item.ColumnName, false); + } + else + { + cmd.Parameters.AddWithValue("@" + item.ColumnName, dataRow[item.ColumnName]); + } + } + i += await cmd.ExecuteNonQueryAsync().ConfigureAwait(false); + cmd.Parameters.Clear(); + } + } + } + return i; + } + private async Task _BulkUpdate(DataTable dt, List> dictionary, int i, string[] whereColums, string[] updateColums, SqliteConnection cn) + { + using (var cmd = cn.CreateCommand()) + { + if (this.Context?.CurrentConnectionConfig?.MoreSettings?.IsCorrectErrorSqlParameterName == true) + { + foreach (DataRow item in dt.Rows) + { + cmd.CommandText = this.Context.Updateable(UtilMethods.DataRowToDictionary(item)) + .WhereColumns(whereColums) + .UpdateColumns(updateColums) + .AS(dt.TableName).ToSqlString(); + i += await cmd.ExecuteNonQueryAsync().ConfigureAwait(false); + } + } + else + { + cmd.CommandText = this.Context.Updateable(dictionary.First()) + .WhereColumns(whereColums) + .UpdateColumns(updateColums) + .AS(dt.TableName).ToSql().Key; + + foreach (DataRow dataRow in dt.Rows) + { + foreach (DataColumn item in dt.Columns) + { + if (IsBoolTrue(dataRow, item)) + { + cmd.Parameters.AddWithValue("@" + item.ColumnName, true); + } + else if (IsBoolFalse(dataRow, item)) + { + cmd.Parameters.AddWithValue("@" + item.ColumnName, false); + } + else + { + cmd.Parameters.AddWithValue("@" + item.ColumnName, dataRow[item.ColumnName]); + } + } + i += await cmd.ExecuteNonQueryAsync().ConfigureAwait(false); + cmd.Parameters.Clear(); + } + } + } + return i; + } + + private static bool IsBoolFalse(DataRow dataRow, DataColumn item) + { + return dataRow[item.ColumnName] != null && dataRow[item.ColumnName] is string && dataRow[item.ColumnName].ToString() == ("isSqliteCore_False"); + } + + private static bool IsBoolTrue(DataRow dataRow, DataColumn item) + { + return dataRow[item.ColumnName] != null && dataRow[item.ColumnName] is string && dataRow[item.ColumnName].ToString() == ("isSqliteCore_True"); + } + + private static void Open(SqliteConnection cn) + { + if (cn.State != ConnectionState.Open) + cn.Open(); + } + + public async Task UpdateByTempAsync(string tableName, string tempName, string[] updateColumns, string[] whereColumns) + { + var dt = UpdateDataTable; + if (dt.Rows.Count == 0) + { + return 0; + } + var dictionary = this.Context.Utilities.DataTableToDictionaryList(dt.Rows.Cast().Take(1).CopyToDataTable()); + int result = 0; + var cn = this.Context.Ado.Connection as SqliteConnection; + Open(cn); + if (this.Context.Ado.Transaction == null) + { + using (var transaction = cn.BeginTransaction()) + { + result = await _BulkUpdate(dt, dictionary, result, whereColumns, updateColumns, cn).ConfigureAwait(false); + transaction.Commit(); + } + } + else + { + result = await _BulkUpdate(dt, dictionary, result, whereColumns, updateColumns, cn).ConfigureAwait(false); + } + return result; + } + + public async Task Merge(string tableName, DataTable dt, EntityInfo entityInfo, string[] whereColumns, string[] updateColumns, List datas) where T : class, new() + { + var result = 0; + await Context.Utilities.PageEachAsync(datas, 2000, async pageItems => + { + var x = await Context.Storageable(pageItems).As(tableName).WhereColumns(whereColumns).ToStorageAsync().ConfigureAwait(false); + result += await x.BulkCopyAsync().ConfigureAwait(false); + result += await x.BulkUpdateAsync(updateColumns).ConfigureAwait(false); + return result; + }).ConfigureAwait(false); + return result; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Sqlite/SqlBuilder/SqliteInsertBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Sqlite/SqlBuilder/SqliteInsertBuilder.cs new file mode 100644 index 000000000..495819d60 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Sqlite/SqlBuilder/SqliteInsertBuilder.cs @@ -0,0 +1,162 @@ +using System.Text; + +namespace SqlSugar +{ + public class SqliteInsertBuilder : InsertBuilder + { + public override string SqlTemplate + { + get + { + if (IsReturnIdentity) + { + return @"INSERT INTO {0} + ({1}) + VALUES + ({2}) ;SELECT LAST_INSERT_ROWID();"; + } + else + { + return @"INSERT INTO {0} + ({1}) + VALUES + ({2}) ;"; + + } + } + } + + public override string SqlTemplateBatch + { + get + { + return "INSERT INTO {0} ({1})"; + } + } + public override string ToSqlString() + { + if (IsNoInsertNull) + { + DbColumnInfoList = DbColumnInfoList.Where(it => it.Value != null).ToList(); + } + var groupList = DbColumnInfoList.GroupBy(it => it.TableId).ToList(); + var isSingle = groupList.Count == 1; + string columnsString = string.Join(",", groupList.First().Select(it => Builder.GetTranslationColumnName(it.DbColumnName))); + if (isSingle) + { + string columnParametersString = string.Join(",", this.DbColumnInfoList.Select(it => base.GetDbColumn(it, Builder.SqlParameterKeyWord + it.DbColumnName))); + ActionMinDate(); + return string.Format(SqlTemplate, GetTableNameString, columnsString, columnParametersString); + } + else + { + StringBuilder batchInsetrSql = new StringBuilder(); + batchInsetrSql.Append("INSERT INTO " + GetTableNameString + " "); + batchInsetrSql.Append('('); + batchInsetrSql.Append(columnsString); + batchInsetrSql.Append(") VALUES"); + string insertColumns = ""; + int i = 0; + foreach (var item in groupList) + { + batchInsetrSql.Append('('); + insertColumns = string.Join(",", item.Select(it => base.GetDbColumn(it, FormatValue(i, it.DbColumnName, it.Value)))); + batchInsetrSql.Append(insertColumns); + if (groupList.Last() == item) + { + batchInsetrSql.Append(") "); + } + else + { + batchInsetrSql.Append("), "); + } + i++; + } + + batchInsetrSql.AppendLine(";SELECT LAST_INSERT_ROWID();"); + var result = batchInsetrSql.ToString(); + return result; + } + } + public object FormatValue(int i, string name, object value) + { + if (value == null) + { + return "NULL"; + } + else + { + var type = UtilMethods.GetUnderType(value.GetType()); + if (type == UtilConstants.DateType) + { + var date = value.ObjToDate(); + if (date < UtilMethods.GetMinDate(this.Context.CurrentConnectionConfig)) + { + date = UtilMethods.GetMinDate(this.Context.CurrentConnectionConfig); + } + if (this.Context.CurrentConnectionConfig?.MoreSettings?.DisableMillisecond == true) + { + return "'" + date.ToString("yyyy-MM-dd HH:mm:ss") + "'"; + } + else + { + return "'" + date.ToString("yyyy-MM-dd HH:mm:ss.fff") + "'"; + } + } + else if (type.IsEnum()) + { + if (this.Context.CurrentConnectionConfig.MoreSettings?.TableEnumIsString == true) + { + return value.ToSqlValue(); + } + else + { + return Convert.ToInt64(value); + } + } + else if (type == UtilConstants.DateTimeOffsetType) + { + return GetDateTimeOffsetString(value); + } + else if (type == UtilConstants.ByteArrayType) + { + var parameterName = this.Builder.SqlParameterKeyWord + name + i; + this.Parameters.Add(new SugarParameter(parameterName, value)); + return parameterName; + } + else if (type == UtilConstants.BoolType) + { + return value.ObjToBool() ? "1" : "0"; + } + else if (type == UtilConstants.StringType || type == UtilConstants.ObjType) + { + return "'" + value.ToString().ToSqlFilter() + "'"; + } + else + { + return "'" + value.ToString() + "'"; + } + } + } + + private object GetDateTimeOffsetString(object value) + { + var date = UtilMethods.ConvertFromDateTimeOffset((DateTimeOffset)value); + if (date < UtilMethods.GetMinDate(this.Context.CurrentConnectionConfig)) + { + date = UtilMethods.GetMinDate(this.Context.CurrentConnectionConfig); + } + return "'" + date.ToString("yyyy-MM-dd HH:mm:ss.fffffff") + "'"; + } + + private object GetDateTimeString(object value) + { + var date = value.ObjToDate(); + if (date < UtilMethods.GetMinDate(this.Context.CurrentConnectionConfig)) + { + date = UtilMethods.GetMinDate(this.Context.CurrentConnectionConfig); + } + return "'" + date.ToString("yyyy-MM-dd HH:mm:ss.fff") + "'"; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Sqlite/SqlBuilder/SqliteQueryBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Sqlite/SqlBuilder/SqliteQueryBuilder.cs new file mode 100644 index 000000000..8174f1929 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Sqlite/SqlBuilder/SqliteQueryBuilder.cs @@ -0,0 +1,105 @@ +using System.Text; +using System.Text.RegularExpressions; + +namespace SqlSugar +{ + public partial class SqliteQueryBuilder : QueryBuilder + { + #region Sql Template + public override string PageTempalte + { + get + { + /* + SELECT * FROM TABLE WHERE CONDITION ORDER BY ID DESC LIMIT 0,10 + */ + var template = "SELECT {0} FROM {1} {2} {3} {4} LIMIT {5},{6}"; + return template; + } + } + public override string DefaultOrderByTemplate + { + get + { + return "ORDER BY DATETIME('now') "; + } + } + + #endregion + + #region Common Methods + public override bool IsComplexModel(string sql) + { + return Regex.IsMatch(sql, @"AS \`\w+\.\w+\`") || Regex.IsMatch(sql, @"AS \`\w+\.\w+\.\w+\`"); + } + public override string ToSqlString() + { + base.AppendFilter(); + string result = null; + string oldOrderBy = this.OrderByValue; + sql = new StringBuilder(); + sql.AppendFormat(SqlTemplate, GetSelectValue, GetTableNameString, GetWhereValueString, GetGroupByString + HavingInfos, (Skip != null || Take != null) ? null : GetOrderByString); + if (IsCount) { return sql.ToString(); } + if (Skip != null && Take == null) + { + if (this.OrderByValue == "ORDER BY ") this.OrderByValue += GetSelectValue.Split(',')[0]; + result = string.Format(PageTempalte, GetSelectValue, GetTableNameString, GetWhereValueString, GetGroupByString + HavingInfos, (Skip != null || Take != null) ? null : GetOrderByString, Skip.ObjToInt(), long.MaxValue); + } + else if (Skip == null && Take != null) + { + if (this.OrderByValue == "ORDER BY ") this.OrderByValue += GetSelectValue.Split(',')[0]; + result = string.Format(PageTempalte, GetSelectValue, GetTableNameString, GetWhereValueString, GetGroupByString + HavingInfos, GetOrderByString, 0, Take.ObjToInt()); + } + else if (Skip != null && Take != null) + { + if (this.OrderByValue == "ORDER BY ") this.OrderByValue += GetSelectValue.Split(',')[0]; + result = string.Format(PageTempalte, GetSelectValue, GetTableNameString, GetWhereValueString, GetGroupByString + HavingInfos, GetOrderByString, Skip.ObjToInt() > 0 ? Skip.ObjToInt() : 0, Take); + } + else + { + result = sql.ToString(); + } + this.OrderByValue = oldOrderBy; + result = GetSqlQuerySql(result); + if (result.IndexOf("-- No table") > 0) + { + return "-- No table"; + } + return result; + } + + #endregion + + #region Get SQL Partial + public override string GetSelectValue + { + get + { + string reval = string.Empty; + if (this.SelectValue == null || this.SelectValue is string) + { + reval = GetSelectValueByString(); + } + else + { + reval = GetSelectValueByExpression(); + } + if (this.SelectType == ResolveExpressType.SelectMultiple) + { + this.SelectCacheKey = this.SelectCacheKey + string.Join("-", this.JoinQueryInfos.Select(it => it.TableName)); + } + if (IsDistinct) + { + reval = " DISTINCT " + reval; + } + if (this.SubToListParameters != null && this.SubToListParameters.Count != 0) + { + reval = SubToListMethod(reval); + } + return reval; + } + } + + #endregion + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Sqlite/SqlBuilder/SqliteUpdateBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Sqlite/SqlBuilder/SqliteUpdateBuilder.cs new file mode 100644 index 000000000..3f22ec02d --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Sqlite/SqlBuilder/SqliteUpdateBuilder.cs @@ -0,0 +1,126 @@ +using System.Text; + +namespace SqlSugar +{ + public class SqliteUpdateBuilder : UpdateBuilder + { + public override string ReSetValueBySqlExpListType { get; set; } = "sqlite"; + protected override string TomultipleSqlString(List> groupList) + { + StringBuilder sb = new StringBuilder(); + int i = 0; + sb.AppendLine(string.Join("\r\n", groupList.Select(t => + { + var updateTable = string.Format("UPDATE {0} SET", base.GetTableNameStringNoWith); + var setValues = string.Join(",", t.Where(s => !s.IsPrimarykey).Where(s => OldPrimaryKeys?.Contains(s.DbColumnName) != true).Select(m => GetOracleUpdateColums(i, m, false)).ToArray()); + var pkList = t.Where(s => s.IsPrimarykey).ToList(); + if (this.IsWhereColumns && this.PrimaryKeys?.Count > 0) + { + var whereColumns = pkList.Where(it => this.PrimaryKeys?.Any(p => p.EqualCase(it.PropertyName) || p.EqualCase(it.DbColumnName)) == true).ToList(); + if (whereColumns.Count != 0) + { + pkList = whereColumns; + } + } + List whereList = new List(); + foreach (var item in pkList) + { + var isFirst = pkList.First() == item; + var whereString = ""; + whereString += GetOracleUpdateColums(i, item, true); + whereList.Add(whereString); + } + i++; + return string.Format("{0} {1} WHERE {2};", updateTable, setValues, string.Join(" AND", whereList)); + }).ToArray())); + return sb.ToString(); + } + + private string GetOracleUpdateColums(int i, DbColumnInfo m, bool iswhere) + { + var result = string.Format("\"{0}\"={1}", m.DbColumnName.ToUpper(), base.GetDbColumn(m, FormatValue(i, m.DbColumnName, m.Value, iswhere))); + if (iswhere && m.Value == null) + { + result = result.Replace("=NULL", " is NULL "); + } + return result; + } + + public object FormatValue(int i, string name, object value, bool iswhere) + { + if (value == null) + { + return "NULL"; + } + else + { + var type = UtilMethods.GetUnderType(value.GetType()); + if (type == UtilConstants.DateType && iswhere == false) + { + var date = value.ObjToDate(); + if (date < UtilMethods.GetMinDate(this.Context.CurrentConnectionConfig)) + { + date = UtilMethods.GetMinDate(this.Context.CurrentConnectionConfig); + } + if (this.Context.CurrentConnectionConfig?.MoreSettings?.DisableMillisecond == true) + { + return "'" + date.ToString("yyyy-MM-dd HH:mm:ss") + "'"; + } + else + { + return "'" + date.ToString("yyyy-MM-dd HH:mm:ss.fff") + "'"; + } + } + else if (type == UtilConstants.DateTimeOffsetType) + { + return GetDateTimeOffsetString(value); + } + else if (type == UtilConstants.DateType && iswhere) + { + var parameterName = this.Builder.SqlParameterKeyWord + name + i; + this.Parameters.Add(new SugarParameter(parameterName, value)); + return parameterName; + } + else if (type.IsEnum()) + { + if (this.Context.CurrentConnectionConfig.MoreSettings?.TableEnumIsString == true) + { + return value.ToSqlValue(); + } + else + { + return Convert.ToInt64(value); + } + } + else if (type == UtilConstants.ByteArrayType) + { + var parameterName = this.Builder.SqlParameterKeyWord + name + i; + this.Parameters.Add(new SugarParameter(parameterName, value)); + return parameterName; + } + else if (type == UtilConstants.BoolType) + { + return value.ObjToBool() ? "1" : "0"; + } + else if (type == UtilConstants.StringType || type == UtilConstants.ObjType) + { + return "'" + value.ToString().ToSqlFilter() + "'"; + } + else + { + return "'" + value.ToString() + "'"; + } + } + } + + private object GetDateTimeOffsetString(object value) + { + var date = UtilMethods.ConvertFromDateTimeOffset((DateTimeOffset)value); + if (date < UtilMethods.GetMinDate(this.Context.CurrentConnectionConfig)) + { + date = UtilMethods.GetMinDate(this.Context.CurrentConnectionConfig); + } + return "'" + date.ToString("yyyy-MM-dd HH:mm:ss.fffffff") + "'"; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Sqlite/SqliteProvider.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Sqlite/SqliteProvider.cs new file mode 100644 index 000000000..c36e7a8ab --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Realization/Sqlite/SqliteProvider.cs @@ -0,0 +1,137 @@ +using Microsoft.Data.Sqlite; + +using System.Data; +using System.Data.Common; +namespace SqlSugar +{ + public partial class SqliteProvider : AdoProvider + { + public SqliteProvider() { } + public override IDbConnection Connection + { + get + { + if (base._DbConnection == null) + { + try + { + var SQLiteConnectionString = base.Context.CurrentConnectionConfig.ConnectionString; + if (SQLiteConnectionString?.Contains('=') == false) + { + Check.ExceptionEasy("ConnString format error . Correct format DataSource=...", "字符串格式错误,应该是DataSource=..."); + } + base._DbConnection = new SqliteConnection(SQLiteConnectionString); + } + catch (Exception ex) + { + if (ex.InnerException != null) + { + Check.Exception(true, ErrorMessage.ConnnectionOpen, ex.Message + "\r\n" + ex.InnerException.Message); + } + else + { + Check.Exception(true, ErrorMessage.ConnnectionOpen, ex.Message); + } + } + } + return base._DbConnection; + } + set + { + base._DbConnection = value; + } + } + + public override void BeginTran(string transactionName) + { + base.BeginTran(); + } + /// + /// Only SqlServer + /// + /// + /// + public override void BeginTran(IsolationLevel iso, string transactionName) + { + base.BeginTran(iso); + } + public override IDataAdapter GetAdapter() + { + return new SqliteDataAdapter(); + } + public override DbCommand GetCommand(string sql, SugarParameter[] parameters) + { + SqliteCommand sqlCommand = new SqliteCommand(sql, (SqliteConnection)this.Connection); + sqlCommand.CommandType = this.CommandType; + sqlCommand.CommandTimeout = this.CommandTimeOut; + if (this.Transaction != null) + { + sqlCommand.Transaction = (SqliteTransaction)this.Transaction; + } + if (parameters.HasValue()) + { + IDataParameter[] ipars = ToIDbDataParameter(parameters); + sqlCommand.Parameters.AddRange((SqliteParameter[])ipars); + } + CheckConnection(); + return sqlCommand; + } + public override void SetCommandToAdapter(IDataAdapter dataAdapter, DbCommand command) + { + ((SqliteDataAdapter)dataAdapter).SelectCommand = (SqliteCommand)command; + } + /// + /// if SQLite return SQLiteParameter[] pars + /// if sqlerver return SqlParameter[] pars ... + /// + /// + /// + public override IDataParameter[] ToIDbDataParameter(params SugarParameter[] parameters) + { + if (parameters == null || parameters.Length == 0) return null; + SqliteParameter[] result = new SqliteParameter[parameters.Length]; + int index = 0; + var isVarchar = this.Context.IsVarchar(); + foreach (var parameter in parameters) + { + if (parameter.Value == null) parameter.Value = DBNull.Value; + if (parameter.Value.GetType() == UtilConstants.GuidType) + { + parameter.Value = parameter.Value.ToString(); + } + var sqlParameter = new SqliteParameter(); + sqlParameter.ParameterName = parameter.ParameterName; + sqlParameter.Size = parameter.Size; + sqlParameter.Value = parameter.Value; + sqlParameter.DbType = parameter.DbType; + result[index] = sqlParameter; + if (sqlParameter.Direction.IsIn(ParameterDirection.Output, ParameterDirection.InputOutput, ParameterDirection.ReturnValue)) + { + if (this.OutputParameters == null) this.OutputParameters = new List(); + this.OutputParameters.RemoveAll(it => it.ParameterName == sqlParameter.ParameterName); + this.OutputParameters.Add(sqlParameter); + } + if (sqlParameter.DbType == System.Data.DbType.Guid) + { + sqlParameter.DbType = System.Data.DbType.String; + if (sqlParameter.Value != DBNull.Value) + { + sqlParameter.Value = sqlParameter.Value.ObjToString(); + } + } + if (isVarchar && sqlParameter.DbType == System.Data.DbType.String) + { + sqlParameter.DbType = System.Data.DbType.AnsiString; + } + else if (parameter.DbType == System.Data.DbType.DateTimeOffset) + { + if (sqlParameter.Value != DBNull.Value) + sqlParameter.Value = UtilMethods.ConvertFromDateTimeOffset((DateTimeOffset)sqlParameter.Value); + sqlParameter.DbType = System.Data.DbType.DateTime; + } + ++index; + } + return result; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/SimpleClient.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/SimpleClient.cs new file mode 100644 index 000000000..48095cd1e --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/SimpleClient.cs @@ -0,0 +1,758 @@ +using System.Linq.Expressions; +using System.Reflection; + +namespace SqlSugar +{ + + public partial class SimpleClient : ISugarRepository, ISimpleClient where T : class, new() + { + #region Interface + public virtual ISqlSugarClient Context { get; set; } + + public virtual ITenant AsTenant() + { + var result = this.Context as ITenant; + if (result == null && this.Context is SqlSugarProvider) + { + result = (this.Context as SqlSugarProvider).Root as ITenant; + } + else if (result == null && this.Context is SqlSugarScopeProvider) + { + result = (this.Context as SqlSugarScopeProvider).conn.Root as ITenant; + } + return result; + } + public virtual ISqlSugarClient AsSugarClient() + { + return this.Context; + } + + public SimpleClient() + { + + } + public SimpleClient(ISqlSugarClient context) + { + this.Context = context; + } + public SimpleClient Change() where ChangeType : class, new() + { + return this.Context.GetSimpleClient(); + } + public SimpleClient CopyNew() + { + SimpleClient sm = new SimpleClient(); + sm.Context = this.Context.CopyNew(); + return sm; + } + public virtual RepositoryType CopyNew() where RepositoryType : ISugarRepository + { + Type type = typeof(RepositoryType); + var isAnyParamter = type.GetConstructors().Any(z => z.GetParameters().Length != 0); + object o = null; + if (isAnyParamter) + { + object[] pars = type.GetConstructors().First().GetParameters() + .Select(it => (object)null).ToArray(); + o = Activator.CreateInstance(type, pars); + } + else + { + o = Activator.CreateInstance(type); + } + var result = (RepositoryType)o; + if (result.Context != null) + { + result.Context = result.Context.CopyNew(); + } + else + { + result.Context = this.Context.CopyNew(); + } + return result; + } + public virtual RepositoryType CopyNew(IServiceProvider serviceProvider) where RepositoryType : ISugarRepository + { + var instance = handleDependencies(typeof(RepositoryType), serviceProvider, true); + return (RepositoryType)instance; + } + + private object handleDependencies(Type type, IServiceProvider serviceProvider, bool needNewCopy = false) + { + ConstructorInfo constructorInfo = null; + var newInstanceType = type; + if (type.IsInterface && IsAssignableToBaseRepository(type)) + { + var dependencyInstanceType = serviceProvider.GetService(type)?.GetType(); + newInstanceType = dependencyInstanceType; + constructorInfo = dependencyInstanceType.GetConstructors().FirstOrDefault(); + } + else + { + constructorInfo = type.GetConstructors().FirstOrDefault(); + } + var parameters = constructorInfo?.GetParameters(); + if (parameters == null || parameters.Length == 0) + { + object dependencyInstance = serviceProvider.GetService(type); + if (dependencyInstance is ISugarRepository sugarRepository) + { + return setContext(sugarRepository, needNewCopy); + } + else + { + return dependencyInstance; + } + } + + var conParas = new List(); + foreach (var parameter in parameters) + { + Type dependencyType = parameter.ParameterType; + conParas.Add(handleDependencies(dependencyType, serviceProvider)); + } + + object instance = null; + if (conParas?.Count > 0) + { + instance = Activator.CreateInstance(newInstanceType, conParas.ToArray()); + } + else + { + instance = Activator.CreateInstance(newInstanceType); + } + return instance; + } + private ISugarRepository setContext(ISugarRepository sugarRepository, bool needNewCopy) + { + if (sugarRepository.Context != null) + { + if (needNewCopy) + { + sugarRepository.Context = sugarRepository.Context.CopyNew(); + } + } + else + { + if (needNewCopy) + { + sugarRepository.Context = this.Context.CopyNew(); + } + else + { + sugarRepository.Context = this.Context; + } + } + return sugarRepository; + } + private bool IsAssignableToBaseRepository(Type type) + { + var baseInterfaces = type.GetInterfaces(); + foreach (Type interfaceType in baseInterfaces) + { + if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(ISimpleClient<>)) + { + Type genericArgument = interfaceType.GetGenericArguments()[0]; + Type baseRepositoryGenericType = typeof(ISimpleClient<>).MakeGenericType(genericArgument); + + if (baseRepositoryGenericType.IsAssignableFrom(type)) + { + return true; + } + } + } + return false; + } + public virtual RepositoryType ChangeRepository() where RepositoryType : ISugarRepository + { + Type type = typeof(RepositoryType); + var isAnyParamter = type.GetConstructors().Any(z => z.GetParameters().Length != 0); + object o = null; + if (isAnyParamter) + { + o = Activator.CreateInstance(type, new string[] { null }); + } + else + { + o = Activator.CreateInstance(type); + } + var result = (RepositoryType)o; + if (result.Context == null) + { + result.Context = this.Context; + } + return result; + } + public virtual RepositoryType ChangeRepository(IServiceProvider serviceProvider) where RepositoryType : ISugarRepository + { + var instance = handleDependencies(typeof(RepositoryType), serviceProvider, false); + return (RepositoryType)instance; + } + public virtual ISugarQueryable AsQueryable() + { + return Context.Queryable(); + } + public virtual IInsertable AsInsertable(T insertObj) + { + return Context.Insertable(insertObj); + } + public virtual IInsertable AsInsertable(T[] insertObjs) + { + return Context.Insertable(insertObjs); + } + public virtual IInsertable AsInsertable(List insertObjs) + { + return Context.Insertable(insertObjs); + } + public virtual IUpdateable AsUpdateable(T updateObj) + { + return Context.Updateable(updateObj); + } + public virtual IUpdateable AsUpdateable(T[] updateObjs) + { + return Context.Updateable(updateObjs); + } + public virtual IUpdateable AsUpdateable(List updateObjs) + { + return Context.Updateable(updateObjs); + } + public virtual IUpdateable AsUpdateable() + { + return Context.Updateable(); + } + public virtual IDeleteable AsDeleteable() + { + return Context.Deleteable(); + } + #endregion + + #region Method + public virtual T GetById(dynamic id) + { + return Context.Queryable().InSingle(id); + } + public virtual List GetList() + { + return Context.Queryable().ToList(); + } + + public virtual List GetList(Expression> whereExpression) + { + return Context.Queryable().Where(whereExpression).ToList(); + } + public virtual T GetSingle(Expression> whereExpression) + { + return Context.Queryable().Single(whereExpression); + } + public virtual T GetFirst(Expression> whereExpression) + { + return Context.Queryable().First(whereExpression); + } + public virtual List GetPageList(Expression> whereExpression, PageModel page) + { + int count = 0; + var result = Context.Queryable().Where(whereExpression).ToPageList(page.PageIndex, page.PageSize, ref count); + page.TotalCount = count; + return result; + } + public virtual List GetPageList(Expression> whereExpression, PageModel page, Expression> orderByExpression = null, OrderByType orderByType = OrderByType.Asc) + { + int count = 0; + var result = Context.Queryable().OrderByIF(orderByExpression != null, orderByExpression, orderByType).Where(whereExpression).ToPageList(page.PageIndex, page.PageSize, ref count); + page.TotalCount = count; + return result; + } + public virtual List GetPageList(List conditionalList, PageModel page) + { + int count = 0; + var result = Context.Queryable().Where(conditionalList).ToPageList(page.PageIndex, page.PageSize, ref count); + page.TotalCount = count; + return result; + } + public virtual List GetPageList(List conditionalList, PageModel page, Expression> orderByExpression = null, OrderByType orderByType = OrderByType.Asc) + { + int count = 0; + var result = Context.Queryable().OrderByIF(orderByExpression != null, orderByExpression, orderByType).Where(conditionalList).ToPageList(page.PageIndex, page.PageSize, ref count); + page.TotalCount = count; + return result; + } + public virtual bool IsAny(Expression> whereExpression) + { + return Context.Queryable().Where(whereExpression).Any(); + } + public virtual int Count(Expression> whereExpression) + { + + return Context.Queryable().Where(whereExpression).Count(); + } + + public virtual bool Insert(T insertObj) + { + return this.Context.Insertable(insertObj).ExecuteCommand() > 0; + } + + public virtual bool InsertOrUpdate(T data) + { + return this.Context.Storageable(data).ExecuteCommand() > 0; + } + public virtual bool InsertOrUpdate(List datas) + { + return this.Context.Storageable(datas).ExecuteCommand() > 0; + } + + public virtual int InsertReturnIdentity(T insertObj) + { + return this.Context.Insertable(insertObj).ExecuteReturnIdentity(); + } + public virtual long InsertReturnBigIdentity(T insertObj) + { + return this.Context.Insertable(insertObj).ExecuteReturnBigIdentity(); + } + public virtual long InsertReturnSnowflakeId(T insertObj) + { + return this.Context.Insertable(insertObj).ExecuteReturnSnowflakeId(); + } + public virtual List InsertReturnSnowflakeId(List insertObjs) + { + return this.Context.Insertable(insertObjs).ExecuteReturnSnowflakeIdList(); + } + public virtual Task InsertReturnSnowflakeIdAsync(T insertObj) + { + return this.Context.Insertable(insertObj).ExecuteReturnSnowflakeIdAsync(); + } + public virtual Task> InsertReturnSnowflakeIdAsync(List insertObjs) + { + return this.Context.Insertable(insertObjs).ExecuteReturnSnowflakeIdListAsync(); + } + + public virtual T InsertReturnEntity(T insertObj) + { + return this.Context.Insertable(insertObj).ExecuteReturnEntity(); + } + + public virtual bool InsertRange(T[] insertObjs) + { + return this.Context.Insertable(insertObjs).ExecuteCommand() > 0; + } + public virtual bool InsertRange(List insertObjs) + { + return this.Context.Insertable(insertObjs).ExecuteCommand() > 0; + } + public virtual bool Update(T updateObj) + { + return this.Context.Updateable(updateObj).ExecuteCommand() > 0; + } + public virtual bool UpdateRange(T[] updateObjs) + { + return this.Context.Updateable(updateObjs).ExecuteCommand() > 0; + } + public virtual bool UpdateRange(List updateObjs) + { + return this.Context.Updateable(updateObjs).ExecuteCommand() > 0; + } + public virtual bool Update(Expression> columns, Expression> whereExpression) + { + return this.Context.Updateable().SetColumns(columns).Where(whereExpression).ExecuteCommand() > 0; + } + public virtual bool UpdateSetColumnsTrue(Expression> columns, Expression> whereExpression) + { + return this.Context.Updateable().SetColumns(columns, true).Where(whereExpression).ExecuteCommand() > 0; + } + public virtual bool Delete(T deleteObj) + { + return this.Context.Deleteable().Where(deleteObj).ExecuteCommand() > 0; + } + public virtual bool Delete(List deleteObjs) + { + return this.Context.Deleteable().Where(deleteObjs).ExecuteCommand() > 0; + } + public virtual bool Delete(Expression> whereExpression) + { + return this.Context.Deleteable().Where(whereExpression).ExecuteCommand() > 0; + } + public virtual bool DeleteById(dynamic id) + { + return this.Context.Deleteable().In(id).ExecuteCommand() > 0; + } + public virtual bool DeleteByIds(dynamic[] ids) + { + return this.Context.Deleteable().In(ids).ExecuteCommand() > 0; + } + #endregion + + #region Async Method + public virtual Task GetByIdAsync(dynamic id) + { + return Context.Queryable().InSingleAsync(id); + } + public virtual Task> GetListAsync() + { + return Context.Queryable().ToListAsync(); + } + + public virtual Task> GetListAsync(Expression> whereExpression) + { + return Context.Queryable().Where(whereExpression).ToListAsync(); + } + public virtual Task GetSingleAsync(Expression> whereExpression) + { + return Context.Queryable().SingleAsync(whereExpression); + } + public virtual Task GetFirstAsync(Expression> whereExpression) + { + return Context.Queryable().FirstAsync(whereExpression); + } + public virtual async Task> GetPageListAsync(Expression> whereExpression, PageModel page) + { + RefAsync count = 0; + var result = await Context.Queryable().Where(whereExpression).ToPageListAsync(page.PageIndex, page.PageSize, count).ConfigureAwait(false); + page.TotalCount = count; + return result; + } + public virtual async Task> GetPageListAsync(Expression> whereExpression, PageModel page, Expression> orderByExpression = null, OrderByType orderByType = OrderByType.Asc) + { + RefAsync count = 0; + var result = await Context.Queryable().OrderByIF(orderByExpression != null, orderByExpression, orderByType).Where(whereExpression).ToPageListAsync(page.PageIndex, page.PageSize, count).ConfigureAwait(false); + page.TotalCount = count; + return result; + } + public virtual async Task> GetPageListAsync(List conditionalList, PageModel page) + { + RefAsync count = 0; + var result = await Context.Queryable().Where(conditionalList).ToPageListAsync(page.PageIndex, page.PageSize, count).ConfigureAwait(false); + page.TotalCount = count; + return result; + } + public virtual async Task> GetPageListAsync(List conditionalList, PageModel page, Expression> orderByExpression = null, OrderByType orderByType = OrderByType.Asc) + { + RefAsync count = 0; + var result = await Context.Queryable().OrderByIF(orderByExpression != null, orderByExpression, orderByType).Where(conditionalList).ToPageListAsync(page.PageIndex, page.PageSize, count).ConfigureAwait(false); + page.TotalCount = count; + return result; + } + public virtual Task IsAnyAsync(Expression> whereExpression) + { + return Context.Queryable().Where(whereExpression).AnyAsync(); + } + public virtual Task CountAsync(Expression> whereExpression) + { + + return Context.Queryable().Where(whereExpression).CountAsync(); + } + + public virtual async Task InsertOrUpdateAsync(T data) + { + return await Context.Storageable(data).ExecuteCommandAsync().ConfigureAwait(false) > 0; + } + public virtual async Task InsertOrUpdateAsync(List datas) + { + return await Context.Storageable(datas).ExecuteCommandAsync().ConfigureAwait(false) > 0; + } + public virtual async Task InsertAsync(T insertObj) + { + return await Context.Insertable(insertObj).ExecuteCommandAsync().ConfigureAwait(false) > 0; + } + public virtual Task InsertReturnIdentityAsync(T insertObj) + { + return this.Context.Insertable(insertObj).ExecuteReturnIdentityAsync(); + } + public virtual Task InsertReturnBigIdentityAsync(T insertObj) + { + return this.Context.Insertable(insertObj).ExecuteReturnBigIdentityAsync(); + } + public virtual async Task InsertReturnEntityAsync(T insertObj) + { + return await Context.Insertable(insertObj).ExecuteReturnEntityAsync().ConfigureAwait(false); + } + public virtual async Task InsertRangeAsync(T[] insertObjs) + { + return await Context.Insertable(insertObjs).ExecuteCommandAsync().ConfigureAwait(false) > 0; + } + public virtual async Task InsertRangeAsync(List insertObjs) + { + return await Context.Insertable(insertObjs).ExecuteCommandAsync().ConfigureAwait(false) > 0; + } + public virtual async Task UpdateAsync(T updateObj) + { + return await Context.Updateable(updateObj).ExecuteCommandAsync().ConfigureAwait(false) > 0; + } + public virtual async Task UpdateRangeAsync(T[] updateObjs) + { + return await Context.Updateable(updateObjs).ExecuteCommandAsync().ConfigureAwait(false) > 0; + } + public virtual async Task UpdateRangeAsync(List updateObjs) + { + return await Context.Updateable(updateObjs).ExecuteCommandAsync().ConfigureAwait(false) > 0; + } + public virtual async Task UpdateAsync(Expression> columns, Expression> whereExpression) + { + return await Context.Updateable().SetColumns(columns).Where(whereExpression).ExecuteCommandAsync().ConfigureAwait(false) > 0; + } + public virtual async Task UpdateSetColumnsTrueAsync(Expression> columns, Expression> whereExpression) + { + return await Context.Updateable().SetColumns(columns, true).Where(whereExpression).ExecuteCommandAsync().ConfigureAwait(false) > 0; + } + public virtual async Task DeleteAsync(T deleteObj) + { + return await Context.Deleteable().Where(deleteObj).ExecuteCommandAsync().ConfigureAwait(false) > 0; + } + public virtual async Task DeleteAsync(List deleteObjs) + { + return await Context.Deleteable().Where(deleteObjs).ExecuteCommandAsync().ConfigureAwait(false) > 0; + } + public virtual async Task DeleteAsync(Expression> whereExpression) + { + return await Context.Deleteable().Where(whereExpression).ExecuteCommandAsync().ConfigureAwait(false) > 0; + } + public virtual async Task DeleteByIdAsync(dynamic id) + { + return await this.Context.Deleteable().In(id).ExecuteCommandAsync() > 0; + } + public virtual async Task DeleteByIdsAsync(dynamic[] ids) + { + return await Context.Deleteable().In(ids).ExecuteCommandAsync().ConfigureAwait(false) > 0; + } + #endregion + + #region Async Method CancellationToken + public virtual Task InsertReturnSnowflakeIdAsync(T insertObj, CancellationToken cancellationToken) + { + this.Context.Ado.CancellationToken = cancellationToken; + return this.Context.Insertable(insertObj).ExecuteReturnSnowflakeIdAsync(cancellationToken); + } + public virtual Task> InsertReturnSnowflakeIdAsync(List insertObjs, CancellationToken cancellationToken) + { + this.Context.Ado.CancellationToken = cancellationToken; + return this.Context.Insertable(insertObjs).ExecuteReturnSnowflakeIdListAsync(cancellationToken); + } + + public virtual Task GetByIdAsync(dynamic id, CancellationToken cancellationToken) + { + this.Context.Ado.CancellationToken = cancellationToken; + return Context.Queryable().InSingleAsync(id); + } + public virtual Task> GetListAsync(CancellationToken cancellationToken) + { + this.Context.Ado.CancellationToken = cancellationToken; + return Context.Queryable().ToListAsync(cancellationToken); + } + + public virtual Task> GetListAsync(Expression> whereExpression, CancellationToken cancellationToken) + { + this.Context.Ado.CancellationToken = cancellationToken; + return Context.Queryable().Where(whereExpression).ToListAsync(cancellationToken); + } + public virtual Task GetSingleAsync(Expression> whereExpression, CancellationToken cancellationToken) + { + this.Context.Ado.CancellationToken = cancellationToken; + return Context.Queryable().SingleAsync(whereExpression); + } + public virtual Task GetFirstAsync(Expression> whereExpression, CancellationToken cancellationToken) + { + this.Context.Ado.CancellationToken = cancellationToken; + return Context.Queryable().FirstAsync(whereExpression, cancellationToken); + } + public virtual async Task> GetPageListAsync(Expression> whereExpression, PageModel page, CancellationToken cancellationToken) + { + this.Context.Ado.CancellationToken = cancellationToken; + RefAsync count = 0; + var result = await Context.Queryable().Where(whereExpression).ToPageListAsync(page.PageIndex, page.PageSize, count, cancellationToken).ConfigureAwait(false); + page.TotalCount = count; + return result; + } + public virtual async Task> GetPageListAsync(Expression> whereExpression, PageModel page, Expression> orderByExpression = null, OrderByType orderByType = OrderByType.Asc, CancellationToken cancellationToken = default) + { + this.Context.Ado.CancellationToken = cancellationToken; + RefAsync count = 0; + var result = await Context.Queryable().OrderByIF(orderByExpression != null, orderByExpression, orderByType).Where(whereExpression).ToPageListAsync(page.PageIndex, page.PageSize, count, cancellationToken).ConfigureAwait(false); + page.TotalCount = count; + return result; + } + public virtual async Task> GetPageListAsync(List conditionalList, PageModel page, CancellationToken cancellationToken) + { + this.Context.Ado.CancellationToken = cancellationToken; + RefAsync count = 0; + var result = await Context.Queryable().Where(conditionalList).ToPageListAsync(page.PageIndex, page.PageSize, count, cancellationToken).ConfigureAwait(false); + page.TotalCount = count; + return result; + } + public virtual async Task> GetPageListAsync(List conditionalList, PageModel page, Expression> orderByExpression = null, OrderByType orderByType = OrderByType.Asc, CancellationToken cancellationToken = default) + { + this.Context.Ado.CancellationToken = cancellationToken; + RefAsync count = 0; + var result = await Context.Queryable().OrderByIF(orderByExpression != null, orderByExpression, orderByType).Where(conditionalList).ToPageListAsync(page.PageIndex, page.PageSize, count, cancellationToken).ConfigureAwait(false); + page.TotalCount = count; + return result; + } + public virtual Task IsAnyAsync(Expression> whereExpression, CancellationToken cancellationToken) + { + this.Context.Ado.CancellationToken = cancellationToken; + return Context.Queryable().Where(whereExpression).AnyAsync(); + } + public virtual Task CountAsync(Expression> whereExpression, CancellationToken cancellationToken) + { + this.Context.Ado.CancellationToken = cancellationToken; + return Context.Queryable().Where(whereExpression).CountAsync(cancellationToken); + } + + public virtual async Task InsertOrUpdateAsync(T data, CancellationToken cancellationToken) + { + this.Context.Ado.CancellationToken = cancellationToken; + return await Context.Storageable(data).ExecuteCommandAsync(cancellationToken).ConfigureAwait(false) > 0; + } + public virtual async Task InsertOrUpdateAsync(List datas, CancellationToken cancellationToken) + { + this.Context.Ado.CancellationToken = cancellationToken; + return await Context.Storageable(datas).ExecuteCommandAsync(cancellationToken).ConfigureAwait(false) > 0; + } + public virtual async Task InsertAsync(T insertObj, CancellationToken cancellationToken) + { + this.Context.Ado.CancellationToken = cancellationToken; + return await Context.Insertable(insertObj).ExecuteCommandAsync(cancellationToken).ConfigureAwait(false) > 0; + } + public virtual Task InsertReturnIdentityAsync(T insertObj, CancellationToken cancellationToken) + { + this.Context.Ado.CancellationToken = cancellationToken; + return this.Context.Insertable(insertObj).ExecuteReturnIdentityAsync(cancellationToken); + } + public virtual Task InsertReturnBigIdentityAsync(T insertObj, CancellationToken cancellationToken) + { + this.Context.Ado.CancellationToken = cancellationToken; + return this.Context.Insertable(insertObj).ExecuteReturnBigIdentityAsync(cancellationToken); + } + public virtual async Task InsertReturnEntityAsync(T insertObj, CancellationToken cancellationToken) + { + this.Context.Ado.CancellationToken = cancellationToken; + return await Context.Insertable(insertObj).ExecuteReturnEntityAsync().ConfigureAwait(false); + } + public virtual async Task InsertRangeAsync(T[] insertObjs, CancellationToken cancellationToken) + { + this.Context.Ado.CancellationToken = cancellationToken; + return await Context.Insertable(insertObjs).ExecuteCommandAsync(cancellationToken).ConfigureAwait(false) > 0; + } + public virtual async Task InsertRangeAsync(List insertObjs, CancellationToken cancellationToken) + { + this.Context.Ado.CancellationToken = cancellationToken; + return await Context.Insertable(insertObjs).ExecuteCommandAsync(cancellationToken).ConfigureAwait(false) > 0; + } + public virtual async Task UpdateAsync(T updateObj, CancellationToken cancellationToken) + { + this.Context.Ado.CancellationToken = cancellationToken; + return await Context.Updateable(updateObj).ExecuteCommandAsync(cancellationToken).ConfigureAwait(false) > 0; + } + public virtual async Task UpdateRangeAsync(T[] updateObjs, CancellationToken cancellationToken) + { + this.Context.Ado.CancellationToken = cancellationToken; + return await Context.Updateable(updateObjs).ExecuteCommandAsync(cancellationToken).ConfigureAwait(false) > 0; + } + public virtual async Task UpdateRangeAsync(List updateObjs, CancellationToken cancellationToken) + { + this.Context.Ado.CancellationToken = cancellationToken; + return await Context.Updateable(updateObjs).ExecuteCommandAsync(cancellationToken).ConfigureAwait(false) > 0; + } + public virtual async Task UpdateAsync(Expression> columns, Expression> whereExpression, CancellationToken cancellationToken) + { + this.Context.Ado.CancellationToken = cancellationToken; + return await Context.Updateable().SetColumns(columns).Where(whereExpression).ExecuteCommandAsync(cancellationToken).ConfigureAwait(false) > 0; + } + public virtual async Task UpdateSetColumnsTrueAsync(Expression> columns, Expression> whereExpression, CancellationToken cancellationToken) + { + this.Context.Ado.CancellationToken = cancellationToken; + return await Context.Updateable().SetColumns(columns, true).Where(whereExpression).ExecuteCommandAsync(cancellationToken).ConfigureAwait(false) > 0; + } + public virtual async Task DeleteAsync(T deleteObj, CancellationToken cancellationToken) + { + this.Context.Ado.CancellationToken = cancellationToken; + return await Context.Deleteable().Where(deleteObj).ExecuteCommandAsync(cancellationToken).ConfigureAwait(false) > 0; + } + public virtual async Task DeleteAsync(List deleteObjs, CancellationToken cancellationToken) + { + this.Context.Ado.CancellationToken = cancellationToken; + return await Context.Deleteable().Where(deleteObjs).ExecuteCommandAsync(cancellationToken).ConfigureAwait(false) > 0; + } + public virtual async Task DeleteAsync(Expression> whereExpression, CancellationToken cancellationToken) + { + this.Context.Ado.CancellationToken = cancellationToken; + return await Context.Deleteable().Where(whereExpression).ExecuteCommandAsync(cancellationToken).ConfigureAwait(false) > 0; + } + public virtual async Task DeleteByIdAsync(dynamic id, CancellationToken cancellationToken) + { + this.Context.Ado.CancellationToken = cancellationToken; + return await this.Context.Deleteable().In(id).ExecuteCommandAsync() > 0; + } + public virtual async Task DeleteByIdsAsync(dynamic[] ids, CancellationToken cancellationToken) + { + this.Context.Ado.CancellationToken = cancellationToken; + return await Context.Deleteable().In(ids).ExecuteCommandAsync(cancellationToken).ConfigureAwait(false) > 0; + } + + public int Count(List conditionalModels) + { + return Context.Queryable().Where(conditionalModels).Count(); + } + + public bool Delete(List conditionalModels) + { + return Context.Deleteable().Where(conditionalModels).ExecuteCommandHasChange(); + } + + public List GetList(Expression> whereExpression, List orderByModels) + { + return Context.Queryable().Where(whereExpression).OrderBy(orderByModels).ToList(); + } + + public List GetList(List conditionalList) + { + return Context.Queryable().Where(conditionalList).ToList(); + } + + public List GetList(List conditionalList, List orderByModels) + { + return Context.Queryable().Where(conditionalList).OrderBy(orderByModels).ToList(); + } + + public List GetPageList(Expression> whereExpression, PageModel page, List orderByModels) + { + var total = 0; + var list = Context.Queryable().Where(whereExpression).OrderBy(orderByModels).ToPageList(page.PageIndex, page.PageSize, ref total); + page.TotalCount = total; + return list; + } + + public List GetPageList(List conditionalList, PageModel page, List orderByModels) + { + var total = 0; + var list = Context.Queryable().Where(conditionalList).OrderBy(orderByModels).ToPageList(page.PageIndex, page.PageSize, ref total); + page.TotalCount = total; + return list; + } + + public T GetSingle(List conditionalModels) + { + return Context.Queryable().Where(conditionalModels).Single(); + } + + public T GetFirst(List conditionalModels) + { + return Context.Queryable().Where(conditionalModels).First(); + } + + public bool IsAny(List conditionalModels) + { + return Context.Queryable().Where(conditionalModels).Any(); + } + + public T GetFirst(Expression> whereExpression, List orderByModels) + { + return Context.Queryable().Where(whereExpression).OrderBy(orderByModels).First(); + } + + public T GetFirst(List conditionalModels, List orderByModels) + { + return Context.Queryable().Where(conditionalModels).OrderBy(orderByModels).First(); + } + + #endregion + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/SpliteTable/SplitTableAttribute.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/SpliteTable/SplitTableAttribute.cs new file mode 100644 index 000000000..aee95d6f3 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/SpliteTable/SplitTableAttribute.cs @@ -0,0 +1,42 @@ +namespace SqlSugar +{ + [AttributeUsage(AttributeTargets.Class, Inherited = true)] + public class SplitTableAttribute : Attribute + { + public SplitType SplitType { get; set; } + public Type CustomSplitTableService { get; set; } + public SplitTableAttribute(SplitType splitType) + { + this.SplitType = splitType; + } + public SplitTableAttribute(SplitType splitType, Type customSplitTableService) + { + this.SplitType = splitType; + if (!customSplitTableService.GetInterfaces().Any(it => it == typeof(ISplitTableService))) + { + Check.ExceptionEasy("customSplitTableService in SplitTableAttribute(SplitType splitType,Type customSplitTableService) must be inherited ISplitTableService", " SplitTableAttribute(SplitType splitType,Type customSplitTableService) 中的 customSplitTableService 必须继承 ISplitTableService"); + } + this.CustomSplitTableService = customSplitTableService; + } + } + + [AttributeUsage(AttributeTargets.Property, Inherited = true)] + public class SplitFieldAttribute : Attribute + { + + public SplitFieldAttribute() + { + + } + } + + [AttributeUsage(AttributeTargets.Property, Inherited = true)] + public class TimeDbSplitFieldAttribute : Attribute + { + public DateType? DateType { get; set; } + public TimeDbSplitFieldAttribute(DateType type) + { + DateType = type; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/SpliteTable/SplitTableContext.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/SpliteTable/SplitTableContext.cs new file mode 100644 index 000000000..d12303c52 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/SpliteTable/SplitTableContext.cs @@ -0,0 +1,108 @@ +using System.Reflection; + +namespace SqlSugar +{ + public class SplitTableContextResult + { + public List Items { get; set; } + public SplitTableContext Helper { get; set; } + + public string GetTableName() + { + return GetTableNames().First(); + } + public string[] GetTableNames() + { + List result = new List(); + var attribute = typeof(T).GetCustomAttribute() as SplitTableAttribute; + Check.Exception(attribute == null, $" {typeof(T).Name} need SplitTableAttribute"); + foreach (var item in Items) + { + result.Add(Helper.GetTableName(Helper.GetValue(attribute.SplitType, item))); + } + return result.Distinct().ToArray(); + } + public string[] GetTableNames(SplitType splitType) + { + List result = new List(); + foreach (var item in Items) + { + result.Add(Helper.GetTableName(Helper.GetValue(splitType, item))); + } + return result.ToArray(); + } + } + public class SplitTableContext + { + internal SqlSugarProvider Context { get; set; } + internal EntityInfo EntityInfo { get; set; } + internal ISplitTableService Service { get; set; } + private SplitTableContext() { } + internal SplitTableContext(SqlSugarProvider context) + { + this.Context = context; + if (this.Context.CurrentConnectionConfig.ConfigureExternalServices?.SplitTableService != null) + { + Service = this.Context.CurrentConnectionConfig.ConfigureExternalServices.SplitTableService; + } + else + { + Service = new DateSplitTableService(); + } + } + public List GetTables() + { + if (StaticConfig.SplitTableGetTablesFunc != null) + { + return GetCustomGetTables(); + } + var oldIsEnableLogEvent = this.Context.Ado.IsEnableLogEvent; + this.Context.Ado.IsEnableLogEvent = false; + List tableInfos = ((DbMaintenanceProvider)this.Context.DbMaintenance).GetSchemaTables(EntityInfo); + if (tableInfos == null) + { + tableInfos = this.Context.DbMaintenance.GetTableInfoList(false); + } + List result = Service.GetAllTables(this.Context, EntityInfo, tableInfos); + this.Context.Ado.IsEnableLogEvent = oldIsEnableLogEvent; + return result; + } + + private List GetCustomGetTables() + { + var oldIsEnableLogEvent = this.Context.Ado.IsEnableLogEvent; + this.Context.Ado.IsEnableLogEvent = false; + var tables = StaticConfig.SplitTableGetTablesFunc(); + List result = Service.GetAllTables(this.Context, EntityInfo, tables.Select(it => new DbTableInfo() { Name = it.TableName }).ToList()); + this.Context.Ado.IsEnableLogEvent = oldIsEnableLogEvent; + return result.ToList(); + } + + public string GetDefaultTableName() + { + return Service.GetTableName(this.Context, EntityInfo); + } + public string GetTableName(SplitType splitType) + { + return Service.GetTableName(this.Context, EntityInfo, splitType); + } + public string GetTableName(SplitType splitType, object fieldValue) + { + return Service.GetTableName(this.Context, EntityInfo, splitType, fieldValue); + } + public string GetTableName(object fieldValue) + { + var attribute = EntityInfo.Type.GetCustomAttribute() as SplitTableAttribute; + Check.Exception(attribute == null, $" {EntityInfo.EntityName} need SplitTableAttribute"); + return Service.GetTableName(this.Context, EntityInfo, attribute.SplitType, fieldValue); + } + public object GetValue(SplitType splitType, object entityValue) + { + return Service.GetFieldValue(this.Context, EntityInfo, splitType, entityValue); + } + internal void CheckPrimaryKey() + { + Check.Exception(EntityInfo.Columns.Any(it => it.IsIdentity == true), ErrorMessage.GetThrowMessage("Split table can't IsIdentity=true", "分表禁止使用自增列")); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/SpliteTable/SplitTableExtensions.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/SpliteTable/SplitTableExtensions.cs new file mode 100644 index 000000000..e9ff055f5 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/SpliteTable/SplitTableExtensions.cs @@ -0,0 +1,14 @@ +namespace SqlSugar.SplitTableExtensions +{ + public static class Extensions + { + public static List SplitTable(this List thisValue, Func, IEnumerable> getTableNamesFunc) + { + return thisValue; + } + public static List SplitTable(this T thisValue, Func, IEnumerable> getTableNamesFunc) + { + return new List(); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/SpliteTable/SplitTableInfo.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/SpliteTable/SplitTableInfo.cs new file mode 100644 index 000000000..ab1d47ef9 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/SpliteTable/SplitTableInfo.cs @@ -0,0 +1,13 @@ +namespace SqlSugar +{ + public class SplitTableInfo + { + public string TableName { get; set; } + public DateTime Date { get; set; } + public String String { get; set; } + public decimal Decimal { get; set; } + public long Long { get; set; } + public int Int { get; set; } + public Byte[] ByteArray { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/SpliteTable/SplitType.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/SpliteTable/SplitType.cs new file mode 100644 index 000000000..1259880b4 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/SpliteTable/SplitType.cs @@ -0,0 +1,18 @@ +namespace SqlSugar +{ + public enum SplitType + { + Day = 0, + Week = 1, + Month = 2, + Month_6 = 1000, + Season = 3, + Year = 4, + _Custom01 = 5, + _Custom02 = 6, + _Custom03 = 7, + _Custom04 = 8, + _Custom05 = 9, + _Custom06 = 10, + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/SpliteTable/SplitTypeExtensions.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/SpliteTable/SplitTypeExtensions.cs new file mode 100644 index 000000000..da6f758af --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/SpliteTable/SplitTypeExtensions.cs @@ -0,0 +1,38 @@ +namespace SqlSugar +{ + public static class SplitTableInfoExtensions + { + public static IEnumerable InTableNames(this List tables, params string[] tableNames) + { + return tables.Where(it => tableNames.Any(y => y.Equals(it.TableName, StringComparison.OrdinalIgnoreCase))); + } + public static IEnumerable ContainsTableNames(this List tables, params string[] tableNames) + { + List result = new List(); + foreach (var item in tables) + { + if (tableNames.Any(it => item.TableName.ObjToString().Contains(it.ObjToString(), StringComparison.CurrentCultureIgnoreCase))) + { + result.Add(item); + } + } + return result; + } + public static IEnumerable ContainsTableNamesIfNullDefaultFirst(this List tables, params string[] tableNames) + { + List result = new List(); + foreach (var item in tables) + { + if (tableNames.Any(it => item.TableName.ObjToString().Contains(it.ObjToString(), StringComparison.CurrentCultureIgnoreCase))) + { + result.Add(item); + } + } + if (result.Count == 0 && tables.Count != 0) + { + result.Add(tables.First()); + } + return result; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/SqlSugarClient.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/SqlSugarClient.cs new file mode 100644 index 000000000..7e6f90904 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/SqlSugarClient.cs @@ -0,0 +1,1620 @@ +using System.Data; +using System.Dynamic; +using System.Linq.Expressions; +using System.Reflection; + +namespace SqlSugar +{ + public partial class SqlSugarClient : ISqlSugarClient, ITenant + { + #region Gobal Property + private SqlSugarProvider _Context = null; + private string _ThreadId; + private ConnectionConfig _CurrentConnectionConfig; + internal List _AllClients; + private bool _IsAllTran = false; + private bool _IsOpen = false; + private MappingTableList _MappingTables; + private MappingColumnList _MappingColumns; + private IgnoreColumnList _IgnoreColumns; + private IgnoreColumnList _IgnoreInsertColumns; + private Action _configAction; + + internal Guid? AsyncId { get; set; } + internal bool? IsSingleInstance { get; set; } + + #endregion + + #region Constructor + public SqlSugarClient(ConnectionConfig config) + { + Check.Exception(config == null, "ConnectionConfig config is null"); + InitContext(config); + } + + public SqlSugarClient(List configs) + { + Check.Exception(configs.IsNullOrEmpty(), "List configs is null or count=0"); + InitConfigs(configs); + var config = configs.First(); + InitContext(config); + _AllClients = configs.Select(it => new SugarTenant() { ConnectionConfig = it }).ToList(); ; + _AllClients.First(it => it.ConnectionConfig.ConfigId == config.ConfigId).Context = this.Context; + } + public SqlSugarClient(ConnectionConfig config, Action configAction) + { + _configAction = configAction; + Check.Exception(config == null, "ConnectionConfig config is null"); + InitContext(config); + configAction(this); + } + + public SqlSugarClient(List configs, Action configAction) + { + _configAction = configAction; + Check.Exception(configs.IsNullOrEmpty(), "List configs is null or count=0"); + InitConfigs(configs); + var config = configs.First(); + InitContext(config); + _AllClients = configs.Select(it => new SugarTenant() { ConnectionConfig = it }).ToList(); ; + _AllClients.First(it => it.ConnectionConfig.ConfigId == config.ConfigId).Context = this.Context; + configAction(this); + } + #endregion + + #region Global variable + public SugarActionType SugarActionType { get { return this.Context.SugarActionType; } set { this.Context.SugarActionType = value; } } + public SqlSugarProvider Context { get { return GetContext(); } } + public ConnectionConfig CurrentConnectionConfig { get { return _CurrentConnectionConfig; } set { _CurrentConnectionConfig = value; } } + public Guid ContextID { get { return this.Context.ContextID; } set { this.Context.ContextID = value; } } + public ConfigQuery ConfigQuery { get { return this.Context.ConfigQuery; } set { this.Context.ConfigQuery = value; } } + + public MappingTableList MappingTables { get { return _MappingTables; } set { _MappingTables = value; } } + public MappingColumnList MappingColumns { get { return _MappingColumns; } set { _MappingColumns = value; } } + public IgnoreColumnList IgnoreColumns { get { return _IgnoreColumns; } set { _IgnoreColumns = value; } } + public IgnoreColumnList IgnoreInsertColumns { get { return _IgnoreInsertColumns; } set { _IgnoreInsertColumns = value; } } + public Dictionary TempItems { get { return this.Context.TempItems; } set { this.Context.TempItems = value; } } + #endregion + + #region SimpleClient + + public T CreateContext(bool isTran = true) where T : SugarUnitOfWork, new() + { + T result = new T(); + _CreateContext(isTran, result); + var type = typeof(T); + var ps = type.GetProperties(); + var cacheKey = "SugarUnitOfWork" + typeof(T).FullName + typeof(T).GetHashCode(); + var properies = new ReflectionInoCacheService().GetOrCreate(cacheKey, + () => + ps.Where(it => + + (it.PropertyType.BaseType?.Name.StartsWith("SimpleClient`") == true) + || + it.PropertyType.Name.StartsWith("SimpleClient`") + + )); + foreach (var item in properies) + { + var value = Activator.CreateInstance(item.PropertyType); + TenantAttribute tenantAttribute = item.PropertyType.GetGenericArguments()[0].GetCustomAttribute(); + if (tenantAttribute == null) + { + value.GetType().GetProperty("Context").SetValue(value, this); + } + else + { + value.GetType().GetProperty("Context").SetValue(value, this.GetConnection(tenantAttribute.configId)); + } + item.SetValue(result, value); + } + return result; + } + public SugarUnitOfWork CreateContext(bool isTran = true) + { + SugarUnitOfWork sugarUnitOf = new SugarUnitOfWork(); + return _CreateContext(isTran, sugarUnitOf); + } + + private SugarUnitOfWork _CreateContext(bool isTran, SugarUnitOfWork sugarUnitOf) + { + sugarUnitOf.Db = this; + sugarUnitOf.Tenant = this; + sugarUnitOf.IsTran = isTran; + this.Open(); + if (isTran) + this.BeginTran(); + return sugarUnitOf; + } + public SimpleClient GetSimpleClient() where T : class, new() + { + return this.Context.GetSimpleClient(); + } + public RepositoryType GetRepository() where RepositoryType : ISugarRepository, new() + { + Type type = typeof(RepositoryType); + var isAnyParamter = type.GetConstructors().Any(z => z.GetParameters().Length != 0); + object o = null; + if (isAnyParamter) + { + o = Activator.CreateInstance(type, new string[] { null }); + } + else + { + o = Activator.CreateInstance(type); + } + var result = (RepositoryType)o; + if (result.Context == null) + { + result.Context = this.Context; + } + return result; + } + #endregion + + #region Insertable + public IInsertable> InsertableByDynamic(object insertDynamicObject) + { + return this.Context.InsertableByDynamic(insertDynamicObject); + } + public InsertMethodInfo InsertableByObject(object singleEntityObjectOrListObject) + { + return this.Context.InsertableByObject(singleEntityObjectOrListObject); + } + public IInsertable Insertable(Dictionary columnDictionary) where T : class, new() + { + return this.Context.Insertable(columnDictionary); + } + + public IInsertable Insertable(dynamic insertDynamicObject) where T : class, new() + { + if (insertDynamicObject is IList) + { + return this.Context.Insertable((insertDynamicObject as IList).ToList()); + } + return this.Context.Insertable(insertDynamicObject); + } + + public IInsertable Insertable(List insertObjs) where T : class, new() + { + return this.Context.Insertable(insertObjs); + } + + public IInsertable Insertable(T insertObj) where T : class, new() + { + Check.ExceptionEasy(typeof(T).FullName.Contains("System.Collections.Generic.List`"), " need where T: class, new() ", "需要Class,new()约束,并且类属性中不能有required修饰符"); + if (typeof(T).Name == "Object") + { + Check.ExceptionEasy("Object type use db.InsertableByObject(obj).ExecuteCommand()", "检测到T为Object类型,请使用 db.InsertableByObject(obj).ExecuteCommand(),Insertable不支持object,InsertableByObject可以(缺点:功能比较少)"); + } + return this.Context.Insertable(insertObj); + } + + public IInsertable Insertable(T[] insertObjs) where T : class, new() + { + return this.Context.Insertable(insertObjs); + } + + #endregion + + #region Queryable + + #region Nav CUD + public InsertNavTaskInit InsertNav(T data) where T : class, new() + { + return this.Context.InsertNav(data); + } + public InsertNavTaskInit InsertNav(List datas) where T : class, new() + { + return this.Context.InsertNav(datas); + } + public InsertNavTaskInit InsertNav(T data, InsertNavRootOptions rootOptions) where T : class, new() + { + return this.Context.InsertNav(data, rootOptions); + } + public InsertNavTaskInit InsertNav(List datas, InsertNavRootOptions rootOptions) where T : class, new() + { + return this.Context.InsertNav(datas, rootOptions); + } + public DeleteNavTaskInit DeleteNav(T data) where T : class, new() + { + return this.Context.DeleteNav(data); + } + public DeleteNavTaskInit DeleteNav(List datas) where T : class, new() + { + return this.Context.DeleteNav(datas); + } + public DeleteNavTaskInit DeleteNav(Expression> whereExpression) where T : class, new() + { + return this.Context.DeleteNav(whereExpression); + } + + public DeleteNavTaskInit DeleteNav(T data, DeleteNavRootOptions options) where T : class, new() + { + return this.Context.DeleteNav(data, options); + } + public DeleteNavTaskInit DeleteNav(List datas, DeleteNavRootOptions options) where T : class, new() + { + return this.Context.DeleteNav(datas, options); + } + public DeleteNavTaskInit DeleteNav(Expression> whereExpression, DeleteNavRootOptions options) where T : class, new() + { + return this.Context.DeleteNav(whereExpression, options); + } + + public UpdateNavTaskInit UpdateNav(T data) where T : class, new() + { + return this.Context.UpdateNav(data); + } + public UpdateNavTaskInit UpdateNav(List datas, UpdateNavRootOptions rootOptions) where T : class, new() + { + return this.Context.UpdateNav(datas, rootOptions); + } + public UpdateNavTaskInit UpdateNav(T data, UpdateNavRootOptions rootOptions) where T : class, new() + { + return this.Context.UpdateNav(data, rootOptions); + } + public UpdateNavTaskInit UpdateNav(List datas) where T : class, new() + { + return this.Context.UpdateNav(datas); + } + #endregion + + #region Union + public ISugarQueryable Union(List> queryables) where T : class + { + return this.Context.Union(queryables); + } + + public ISugarQueryable Union(params ISugarQueryable[] queryables) where T : class + { + return this.Context.Union(queryables); + } + + public ISugarQueryable UnionAll(List> queryables) where T : class + { + return this.Context.UnionAll(queryables); + } + + public ISugarQueryable UnionAll(params ISugarQueryable[] queryables) where T : class + { + return this.Context.UnionAll(queryables); + } + #endregion + + public QueryMethodInfo QueryableByObject(Type entityType) + { + return this.Context.QueryableByObject(entityType); + } + public QueryMethodInfo QueryableByObject(Type entityType, string shortName) + { + return this.Context.QueryableByObject(entityType, shortName); + } + public ISugarQueryable MasterQueryable() + { + return this.Context.MasterQueryable(); + } + public ISugarQueryable SlaveQueryable() + { + return this.Context.SlaveQueryable(); + } + public ISugarQueryable SqlQueryable(string sql) where T : class, new() + { + return this.Context.SqlQueryable(sql); + } + public ISugarQueryable Queryable(string tableName, string shortName) + { + return this.Context.Queryable(tableName, shortName); + } + + public ISugarQueryable Queryable(Expression> joinExpression) where T : class, new() + { + return this.Context.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return this.Context.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return this.Context.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) where T : class, new() + { + return this.Context.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return this.Context.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return this.Context.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) where T : class, new() + { + return this.Context.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return this.Context.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return this.Context.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) where T : class, new() + { + return this.Context.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return this.Context.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return this.Context.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) where T : class, new() + { + return this.Context.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return this.Context.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return this.Context.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) where T : class, new() + { + return this.Context.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return this.Context.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return this.Context.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) where T : class, new() + { + return this.Context.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return this.Context.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return this.Context.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) where T : class, new() + { + return this.Context.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return this.Context.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return this.Context.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) where T : class, new() + { + return this.Context.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return this.Context.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return this.Context.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) where T : class, new() + { + return this.Context.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return this.Context.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return this.Context.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) where T : class, new() + { + return this.Context.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return this.Context.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return this.Context.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(ISugarQueryable joinQueryable1, ISugarQueryable joinQueryable2, Expression> joinExpression) + where T : class, new() + where T2 : class, new() + { + return this.Context.Queryable(joinQueryable1, joinQueryable2, joinExpression).With(SqlWith.Null); + } + + public ISugarQueryable Queryable(ISugarQueryable joinQueryable1, ISugarQueryable joinQueryable2, JoinType joinType, Expression> joinExpression) + where T : class, new() + where T2 : class, new() + { + return this.Context.Queryable(joinQueryable1, joinQueryable2, joinType, joinExpression).With(SqlWith.Null); + } + + + public ISugarQueryable Queryable(ISugarQueryable joinQueryable1, ISugarQueryable joinQueryable2, ISugarQueryable joinQueryable3, + JoinType joinType1, Expression> joinExpression1, + JoinType joinType2, Expression> joinExpression2) + where T : class, new() + where T2 : class, new() + where T3 : class, new() + { + return this.Context.Queryable(joinQueryable1, joinQueryable2, joinQueryable3, joinType1, joinExpression1, joinType2, joinExpression2).With(SqlWith.Null); + } + public ISugarQueryable Queryable(ISugarQueryable joinQueryable1, ISugarQueryable joinQueryable2, ISugarQueryable joinQueryable3, ISugarQueryable joinQueryable4, + JoinType joinType1, Expression> joinExpression1, + JoinType joinType2, Expression> joinExpression2, + JoinType joinType3, Expression> joinExpression3) + where T : class, new() + where T2 : class, new() + where T3 : class, new() + where T4 : class, new() + { + return this.Context.Queryable(joinQueryable1, joinQueryable2, joinQueryable3, joinQueryable4, joinType1, joinExpression1, joinType2, joinExpression2, joinType3, joinExpression3).With(SqlWith.Null); + } + + public ISugarQueryable Queryable() + { + return this.Context.Queryable(); + } + + public ISugarQueryable Queryable(ISugarQueryable queryable) + { + + var result = this.Context.Queryable(queryable); + var QueryBuilder = queryable.QueryBuilder; + result.QueryBuilder.IsQueryInQuery = true; + var appendIndex = result.QueryBuilder.Parameters == null ? 1 : result.QueryBuilder.Parameters.Count + 1; + result.QueryBuilder.WhereIndex = (QueryBuilder.WhereIndex + 1); + result.QueryBuilder.LambdaExpressions.ParameterIndex = (QueryBuilder.LambdaExpressions.ParameterIndex + appendIndex); + return result; + } + public ISugarQueryable Queryable(ISugarQueryable queryable, string shortName) + { + return this.Context.Queryable(queryable, shortName); + } + + public ISugarQueryable Queryable(string shortName) + { + return this.Context.Queryable(shortName); + } + + #endregion + + #region Saveable + public GridSaveProvider GridSave(List saveList) where T : class, new() + { + return this.Context.GridSave(saveList); + } + public GridSaveProvider GridSave(List oldList, List saveList) where T : class, new() + { + return this.Context.GridSave(oldList, saveList); + } + public StorageableDataTable Storageable(DataTable data) + { + return this.Context.Storageable(data); + } + public StorageableDataTable Storageable(List> dictionaryList, string tableName) + { + DataTable dt = this.Context.Utilities.DictionaryListToDataTable(dictionaryList); + dt.TableName = tableName; + return this.Context.Storageable(dt); + } + public StorageableDataTable Storageable(Dictionary dictionary, string tableName) + { + DataTable dt = this.Context.Utilities.DictionaryListToDataTable(new List>() { dictionary }); + dt.TableName = tableName; + return this.Context.Storageable(dt); + } + + public IStorageable Storageable(List dataList) where T : class, new() + { + return this.Context.Storageable(dataList); + } + public IStorageable Storageable(IList dataList) where T : class, new() + { + return this.Context.Storageable(dataList.ToList()); + } + public IStorageable Storageable(T[] dataList) where T : class, new() + { + return this.Context.Storageable(dataList?.ToList()); + } + public IStorageable Storageable(T data) where T : class, new() + { + Check.Exception(typeof(T).FullName.Contains("System.Collections.Generic.List`"), " need where T: class, new() "); + return this.Context.Storageable(new List { data }); + } + + [Obsolete("use Storageable")] + public ISaveable Saveable(List saveObjects) where T : class, new() + { + return this.Context.Saveable(saveObjects); + } + [Obsolete("use Storageable")] + public ISaveable Saveable(T saveObject) where T : class, new() + { + return this.Context.Saveable(saveObject); + } + public StorageableMethodInfo StorageableByObject(object singleEntityObjectOrListObject) + { + return this.Context.StorageableByObject(singleEntityObjectOrListObject); + } + #endregion + + #region Reportable + public IReportable Reportable(T data) + { + return this.Context.Reportable(data); + } + public IReportable Reportable(List list) + { + return this.Context.Reportable(list); + } + public IReportable Reportable(T[] array) + { + return this.Context.Reportable(array); + } + #endregion + + #region Queue + public QueueList Queues { get { return this.Context.Queues; } set { this.Context.Queues = value; } } + public void AddQueue(string sql, object parsmeters = null) + { + this.Context.AddQueue(sql, parsmeters); + } + + public void AddQueue(string sql, List parsmeters) + { + this.Context.AddQueue(sql, parsmeters); + } + + public void AddQueue(string sql, SugarParameter parsmeter) + { + this.Context.AddQueue(sql, parsmeter); + } + public int SaveQueues(bool isTran = true) + { + return this.Context.SaveQueues(isTran); + } + + public Tuple, List, List, List, List, List, List> SaveQueues(bool isTran = true) + { + return this.Context.SaveQueues(isTran); + } + + public Tuple, List, List, List, List, List> SaveQueues(bool isTran = true) + { + return this.Context.SaveQueues(isTran); + } + + public Tuple, List, List, List, List> SaveQueues(bool isTran = true) + { + return this.Context.SaveQueues(isTran); + } + + public Tuple, List, List, List> SaveQueues(bool isTran = true) + { + return this.Context.SaveQueues(isTran); + } + + public Tuple, List, List> SaveQueues(bool isTran = true) + { + return this.Context.SaveQueues(isTran); + } + + public Tuple, List> SaveQueues(bool isTran = true) + { + return this.Context.SaveQueues(isTran); + } + + public List SaveQueues(bool isTran = true) + { + return this.Context.SaveQueues(isTran); + } + + public Task SaveQueuesAsync(bool isTran = true) + { + return this.Context.SaveQueuesAsync(isTran); + } + + public Task, List, List, List, List, List, List>> SaveQueuesAsync(bool isTran = true) + { + return this.Context.SaveQueuesAsync(isTran); + } + + public Task, List, List, List, List, List>> SaveQueuesAsync(bool isTran = true) + { + return this.Context.SaveQueuesAsync(isTran); + } + + public Task, List, List, List, List>> SaveQueuesAsync(bool isTran = true) + { + return this.Context.SaveQueuesAsync(isTran); + } + + public Task, List, List, List>> SaveQueuesAsync(bool isTran = true) + { + return this.Context.SaveQueuesAsync(isTran); + } + + public Task, List, List>> SaveQueuesAsync(bool isTran = true) + { + return this.Context.SaveQueuesAsync(isTran); + } + + public Task, List>> SaveQueuesAsync(bool isTran = true) + { + return this.Context.SaveQueuesAsync(isTran); + } + + public Task> SaveQueuesAsync(bool isTran = true) + { + return this.Context.SaveQueuesAsync(isTran); + } + #endregion + + #region Updateable + public IUpdateable> UpdateableByDynamic(object updateDynamicObject) + { + return this.Context.UpdateableByDynamic(updateDynamicObject); + } + public UpdateMethodInfo UpdateableByObject(object singleEntityObjectOrListObject) + { + return this.Context.UpdateableByObject(singleEntityObjectOrListObject); + } + public UpdateExpressionMethodInfo UpdateableByObject(Type entityType) + { + return this.Context.UpdateableByObject(entityType); + } + public IUpdateable Updateable() where T : class, new() + { + return this.Context.Updateable(); + } + + public IUpdateable Updateable(Dictionary columnDictionary) where T : class, new() + { + return this.Context.Updateable(columnDictionary); + } + + public IUpdateable Updateable(dynamic updateDynamicObject) where T : class, new() + { + if (updateDynamicObject is IList) + { + return this.Context.Updateable((updateDynamicObject as IList).ToList()); + } + return this.Context.Updateable(updateDynamicObject); + } + + public IUpdateable Updateable(Expression> columns) where T : class, new() + { + return this.Context.Updateable(columns); + } + + public IUpdateable Updateable(Expression> columns) where T : class, new() + { + return this.Context.Updateable(columns); + } + + public IUpdateable Updateable(List UpdateObjs) where T : class, new() + { + return this.Context.Updateable(UpdateObjs); + } + + public IUpdateable Updateable(T UpdateObj) where T : class, new() + { + return this.Context.Updateable(UpdateObj); + } + + public IUpdateable Updateable(T[] UpdateObjs) where T : class, new() + { + return this.Context.Updateable(UpdateObjs); + } + + #endregion + + #region Ado + public IAdo Ado => this.Context.Ado; + + #endregion + + #region Deleteable + public DeleteMethodInfo DeleteableByObject(object singleEntityObjectOrListObject) + { + return this.Context.DeleteableByObject(singleEntityObjectOrListObject); + } + public IDeleteable Deleteable() where T : class, new() + { + return this.Context.Deleteable(); + } + + public IDeleteable Deleteable(dynamic primaryKeyValue) where T : class, new() + { + return this.Context.Deleteable(primaryKeyValue); + } + + public IDeleteable Deleteable(dynamic[] primaryKeyValues) where T : class, new() + { + return this.Context.Deleteable(primaryKeyValues); + } + + public IDeleteable Deleteable(Expression> expression) where T : class, new() + { + return this.Context.Deleteable(expression); + } + + public IDeleteable Deleteable(List pkValue) where T : class, new() + { + return this.Context.Deleteable(pkValue); + } + + public IDeleteable Deleteable(List deleteObjs) where T : class, new() + { + return this.Context.Deleteable(deleteObjs); + } + + public IDeleteable Deleteable(T deleteObj) where T : class, new() + { + return this.Context.Deleteable(deleteObj); + } + + + #endregion + + #region Fastest + public IFastest Fastest() where T : class, new() + { + return this.Context.Fastest(); + } + #endregion + + #region ThenMapper + public void ThenMapper(IEnumerable list, Action action) + { + this.Context.ThenMapper(list, action); + } + public Task ThenMapperAsync(IEnumerable list, Func action) + { + return this.Context.ThenMapperAsync(list, action); + } + #endregion + + #region More api + public string[] GetCurrentConfigIds() + { + return _AllClients.Select(it => it.ConnectionConfig.ConfigId + string.Empty).ToArray(); + } + public IContextMethods Utilities { get { return this.Context.Utilities; } set { this.Context.Utilities = value; } } + public AopProvider Aop => this.Context.Aop; + public ICodeFirst CodeFirst => this.Context.CodeFirst; + public IDbFirst DbFirst => this.Context.DbFirst; + public IDbMaintenance DbMaintenance => this.Context.DbMaintenance; + public EntityMaintenance EntityMaintenance { get { return this.Context.EntityMaintenance; } set { this.Context.EntityMaintenance = value; } } + public QueryFilterProvider QueryFilter { get { return this.Context.QueryFilter; } set { this.Context.QueryFilter = value; } } + #endregion + + #region TenantManager + public ITenant AsTenant() + { + var tenant = this as ITenant; + return tenant; + } + public SqlSugarTransaction UseTran() + { + return new SqlSugarTransaction(this); + } + public void RemoveConnection(dynamic configId) + { + var removeData = this._AllClients.FirstOrDefault(it => ((object)it.ConnectionConfig.ConfigId).ObjToString() == ((object)configId).ObjToString()); + object currentId = this.CurrentConnectionConfig.ConfigId; + if (removeData != null) + { + if (removeData.Context.Ado.IsAnyTran()) + { + Check.ExceptionEasy("RemoveConnection error has tran", $"删除失败{removeData.ConnectionConfig.ConfigId}存在未提交事务"); + } + else if (((object)removeData.ConnectionConfig.ConfigId).ObjToString() == currentId.ObjToString()) + { + Check.ExceptionEasy("Default ConfigId cannot be deleted", $"默认库不能删除{removeData.ConnectionConfig.ConfigId}"); + } + this._AllClients.Remove(removeData); + } + } + public void AddConnection(ConnectionConfig connection) + { + Check.ArgumentNullException(connection, "AddConnection.connection can't be null"); + InitTenant(); + var db = this._AllClients.FirstOrDefault(it => ((object)it.ConnectionConfig.ConfigId).ObjToString() == ((object)connection.ConfigId).ObjToString()); + if (db == null) + { + if (this._AllClients == null) + { + this._AllClients = new List(); + } + var provider = new SqlSugarProvider(connection); + if (connection.AopEvents != null) + { + provider.Ado.IsEnableLogEvent = true; + } + this._AllClients.Add(new SugarTenant() + { + ConnectionConfig = connection, + Context = provider + }); + } + } + public SqlSugarProvider GetConnectionWithAttr() + { + var attr = typeof(T).GetCustomAttribute(); + if (attr == null) + return this.GetConnection(this.CurrentConnectionConfig.ConfigId); + var configId = attr.configId; + return this.GetConnection(configId); + } + public SqlSugarProvider GetConnectionWithAttr(Type type) + { + var attr = type.GetCustomAttribute(); + if (attr == null) + return this.GetConnection(this.CurrentConnectionConfig.ConfigId); + var configId = attr.configId; + return this.GetConnection(configId); + } + public SqlSugarScopeProvider GetConnectionScopeWithAttr() + { + var attr = typeof(T).GetCustomAttribute(); + if (attr == null) + return this.GetConnectionScope(this.CurrentConnectionConfig.ConfigId); + var configId = attr.configId; + return this.GetConnectionScope(configId); + } + public SqlSugarProvider GetConnection(object configId) + { + InitTenant(); + var db = this._AllClients.FirstOrDefault(it => Convert.ToString(it.ConnectionConfig.ConfigId) == Convert.ToString(configId)); + if (db == null) + { + Check.Exception(true, "ConfigId was not found {0}", configId + ""); + } + if (db.Context == null) + { + db.Context = new SqlSugarProvider(db.ConnectionConfig); + } + var intiAop = db.Context.Aop; + if (db.Context.CurrentConnectionConfig.AopEvents == null) + { + db.Context.CurrentConnectionConfig.AopEvents = new AopEvents(); + } + if (_IsAllTran && db.Context.Ado.Transaction == null) + { + db.Context.Ado.BeginTran(); + } + db.Context.Root = this; + if (db.Context.MappingTables == null) + { + db.Context.MappingTables = new MappingTableList(); + } + return db.Context; + } + + public SqlSugarScopeProvider GetConnectionScope(object configId) + { + var conn = GetConnection(configId); + return new SqlSugarScopeProvider(conn); + } + public bool IsAnyConnection(object configId) + { + InitTenant(); + var db = this._AllClients.FirstOrDefault(it => Convert.ToString(it.ConnectionConfig.ConfigId) == Convert.ToString(configId)); + return db != null; + + } + public void ChangeDatabase(object configId) + { + configId = Convert.ToString(configId); + var isLog = _Context.Ado.IsEnableLogEvent; + Check.Exception(!_AllClients.Any(it => Convert.ToString(it.ConnectionConfig.ConfigId) == Convert.ToString(configId)), "ConfigId was not found {0}", configId + ""); + InitTenant(_AllClients.First(it => Convert.ToString(it.ConnectionConfig.ConfigId) == Convert.ToString(configId))); + if (this._IsAllTran) + this.Ado.BeginTran(); + if (this._IsOpen) + this.Open(); + _Context.Ado.IsEnableLogEvent = isLog; + if (_CurrentConnectionConfig.AopEvents == null) + _CurrentConnectionConfig.AopEvents = new AopEvents(); + } + public void ChangeDatabase(Func changeExpression) + { + var isLog = _Context.Ado.IsEnableLogEvent; + var allConfigs = _AllClients.Select(it => it.ConnectionConfig); + Check.Exception(!allConfigs.Any(changeExpression), "changeExpression was not found {0}", changeExpression.ToString()); + InitTenant(_AllClients.First(it => it.ConnectionConfig == allConfigs.First(changeExpression))); + if (this._IsAllTran) + this.Ado.BeginTran(); + if (this._IsOpen) + this.Open(); + _Context.Ado.IsEnableLogEvent = isLog; + if (_CurrentConnectionConfig.AopEvents == null) + _CurrentConnectionConfig.AopEvents = new AopEvents(); + } + public void BeginTran() + { + _IsAllTran = true; + AllClientEach(it => it.Ado.BeginTran()); + } + + public void BeginTran(IsolationLevel iso) + { + _IsAllTran = true; + AllClientEach(it => it.Ado.BeginTran(iso)); + } + + public async Task BeginTranAsync() + { + _IsAllTran = true; + await AllClientEachAsync(async it => await it.Ado.BeginTranAsync().ConfigureAwait(false)).ConfigureAwait(false); + } + + public async Task BeginTranAsync(IsolationLevel iso) + { + _IsAllTran = true; + await AllClientEachAsync(async it => await it.Ado.BeginTranAsync(iso).ConfigureAwait(false)).ConfigureAwait(false); + } + + public void CommitTran() + { + this.Context.Ado.CommitTran(); + AllClientEach(it => + { + + try + { + it.Ado.CommitTran(); + } + catch + { + SugarRetry.Execute(() => it.Ado.CommitTran(), new TimeSpan(0, 0, 5), 3); + } + + }); + _IsAllTran = false; + } + + public async Task CommitTranAsync() + { + await Context.Ado.CommitTranAsync().ConfigureAwait(false); + await AllClientEachAsync(async it => + { + + try + { + await it.Ado.CommitTranAsync().ConfigureAwait(false); + } + catch + { + SugarRetry.Execute(() => it.Ado.CommitTran(), new TimeSpan(0, 0, 5), 3); + } + + }).ConfigureAwait(false); + _IsAllTran = false; + } + public DbResult UseTran(Action action, Action errorCallBack = null) + { + var result = new DbResult(); + try + { + this.BeginTran(); + if (action != null) + action(); + this.CommitTran(); + result.Data = result.IsSuccess = true; + } + catch (Exception ex) + { + result.ErrorException = ex; + result.ErrorMessage = ex.Message; + result.IsSuccess = false; + this.RollbackTran(); + if (errorCallBack != null) + { + errorCallBack(ex); + } + } + return result; + } + + public async Task> UseTranAsync(Func action, Action errorCallBack = null) + { + var result = new DbResult(); + try + { + await BeginTranAsync().ConfigureAwait(false); + if (action != null) + await action().ConfigureAwait(false); + await CommitTranAsync().ConfigureAwait(false); + result.Data = result.IsSuccess = true; + } + catch (Exception ex) + { + result.ErrorException = ex; + result.ErrorMessage = ex.Message; + result.IsSuccess = false; + await RollbackTranAsync().ConfigureAwait(false); + if (errorCallBack != null) + { + errorCallBack(ex); + } + } + return result; + } + + public DbResult UseTran(Func action, Action errorCallBack = null) + { + var result = new DbResult(); + try + { + this.BeginTran(); + if (action != null) + result.Data = action(); + this.CommitTran(); + result.IsSuccess = true; + } + catch (Exception ex) + { + result.ErrorException = ex; + result.ErrorMessage = ex.Message; + result.IsSuccess = false; + this.RollbackTran(); + if (errorCallBack != null) + { + errorCallBack(ex); + } + } + return result; + } + + public async Task> UseTranAsync(Func> action, Action errorCallBack = null) + { + var result = new DbResult(); + try + { + this.BeginTran(); + T data = default(T); + if (action != null) + data = await action().ConfigureAwait(false); + this.CommitTran(); + result.IsSuccess = true; + result.Data = data; + } + catch (Exception ex) + { + result.ErrorException = ex; + result.ErrorMessage = ex.Message; + result.IsSuccess = false; + this.RollbackTran(); + if (errorCallBack != null) + { + errorCallBack(ex); + } + } + return result; + } + + public void RollbackTran() + { + this.Context.Ado.RollbackTran(); + AllClientEach(it => + { + + try + { + it.Ado.RollbackTran(); + } + catch + { + SugarRetry.Execute(() => it.Ado.RollbackTran(), new TimeSpan(0, 0, 5), 3); + } + + }); + _IsAllTran = false; + } + public async Task RollbackTranAsync() + { + await Context.Ado.RollbackTranAsync().ConfigureAwait(false); + await AllClientEachAsync(async it => + { + + try + { + await it.Ado.RollbackTranAsync().ConfigureAwait(false); + } + catch + { + SugarRetry.Execute(() => it.Ado.RollbackTran(), new TimeSpan(0, 0, 5), 3); + } + + }).ConfigureAwait(false); + _IsAllTran = false; + } + public void Close() + { + this.Context.Close(); + AllClientEach(it => it.Close()); + _IsOpen = false; + } + public void Open() + { + this.Context.Open(); + _IsOpen = true; + } + + #endregion + + #region IDispose + public void Dispose() + { + AllClientEach(it => it.Ado.RollbackTran()); + AllClientEach(it => it.Dispose()); + } + + #endregion + + #region Cache + public SugarCacheProvider DataCache + { + get { return this.Context.DataCache; } + } + #endregion + + #region Other method + public DynamicBuilder DynamicBuilder() + { + return this.Context.DynamicBuilder(); + } + public void Tracking(T data) where T : class, new() + { + this.Context.Tracking(data); + } + public void ClearTracking() + { + this.Context.ClearTracking(); + } + public void Tracking(List datas) where T : class, new() + { + this.Context.Tracking(datas); + } + public SqlSugarClient CopyNew() + { + + if (_AllClients?.Count > 1 && _configAction != null) + { + List connections = new List(); + foreach (var item in _AllClients) + { + connections.Add(UtilMethods.CopyConfig(item.ConnectionConfig)); + } + var newDb = new SqlSugarClient(connections, _configAction); + newDb.QueryFilter = this.QueryFilter; + return newDb; + } + + SqlSugarClient result; + if (_configAction != null) + result = new SqlSugarClient(UtilMethods.CopyConfig(this.Ado.Context.CurrentConnectionConfig), _configAction); + else + result = new SqlSugarClient(UtilMethods.CopyConfig(this.Ado.Context.CurrentConnectionConfig)); + result.QueryFilter = this.QueryFilter; + if (_AllClients != null) + { + foreach (var item in _AllClients) + { + if (!result.IsAnyConnection(item.ConnectionConfig.ConfigId)) + { + result.AddConnection(UtilMethods.CopyConfig(item.ConnectionConfig)); + } + } + } + return result; + } + public DateTime GetDate() + { + return this.Context.GetDate(); + } + public void InitMappingInfo(Type type) + { + this.Context.InitMappingInfo(type); + } + public void InitMappingInfo() + { + this.Context.InitMappingInfo(typeof(T)); + } + #endregion + + #region Helper + public Task AsyncLock(int timeOutSeconds = 30) + { + return this.Context.AsyncLock(timeOutSeconds); + } + public SplitTableContext SplitHelper() where T : class, new() + { + return this.Context.SplitHelper(); + } + public SplitTableContext SplitHelper(Type entityType) + { + return this.Context.SplitHelper(entityType); + } + public SplitTableContextResult SplitHelper(T data) where T : class, new() + { + return this.Context.SplitHelper(data); + } + public SplitTableContextResult SplitHelper(List dataList) where T : class, new() + { + return this.Context.SplitHelper(dataList); + } + private SqlSugarProvider GetContext() + { + SqlSugarProvider result = null; + //if (IsSameThreadAndShard()) + //{ + // result = SameThreadAndShard(); + //} + //else if (IsNoSameThreadAndShard()) + //{ + // result = NoSameThreadAndShard(); + //} + //else + //{ + result = Synchronization(); + //} + //Because SqlSugarScope implements thread safety + //else if (IsSingleInstanceAsync()) + //{ + // result = Synchronization();//Async no support Single Instance + //} + //else if (IsAsync()) + //{ + // result = Synchronization(); + //} + //else + //{ + // StackTrace st = new StackTrace(true); + // var methods = st.GetFrames(); + // var isAsync = UtilMethods.IsAnyAsyncMethod(methods); + // if (isAsync) + // { + // result = Synchronization(); + // } + // else + // { + // result = NoSameThread(); + // } + //} + if (result.Root == null) + { + result.Root = this; + } + return result; + } + + private SqlSugarProvider NoSameThreadAsync() + { + var result = GetCallContext(); + return result; + } + private SqlSugarProvider NoSameThread() + { + if (CallContext.ContextList.Value == null) + { + var context = CopyClient(); + AddCallContext(context); + return context; + } + else + { + var result = GetCallContext(); + if (result == null) + { + var copy = CopyClient(); + AddCallContext(copy); + return copy; + } + else + { + return result; + } + } + } + private void InitTenant() + { + if (this._AllClients == null) + { + this._AllClients = new List(); + this._AllClients.Add(new SugarTenant() + { + ConnectionConfig = this.CurrentConnectionConfig, + Context = this.Context + }); + } + } + + private SqlSugarProvider Synchronization() + { + _Context.MappingColumns = _MappingColumns; + _Context.MappingTables = _MappingTables; + _Context.IgnoreColumns = _IgnoreColumns; + _Context.IgnoreInsertColumns = _IgnoreInsertColumns; + return _Context; + } + + private SqlSugarProvider NoSameThreadAndShard() + { + if (CallContext.ContextList.Value.IsNullOrEmpty()) + { + var copy = CopyClient(); + AddCallContext(copy); + return copy; + } + else + { + var result = GetCallContext(); + if (result == null) + { + var copy = CopyClient(); + AddCallContext(copy); + return copy; + } + else + { + return result; + } + } + } + + private SqlSugarProvider SameThreadAndShard() + { + if (CallContext.ContextList.Value.IsNullOrEmpty()) + { + AddCallContext(_Context); + return _Context; + } + else + { + var result = GetCallContext(); + if (result == null) + { + var copy = CopyClient(); + AddCallContext(copy); + return copy; + } + else + { + return result; + } + } + } + + private bool IsAsync() + { + return AsyncId != null; + } + + private bool IsSingleInstanceAsync() + { + return IsSingleInstance == true && AsyncId != null; + } + + private bool IsSynchronization() + { + return _ThreadId == Environment.CurrentManagedThreadId.ToString(); + } + + //private bool IsNoSameThreadAndShard() + //{ + // return CurrentConnectionConfig.IsShardSameThread && _ThreadId != Thread.CurrentThread.ManagedThreadId.ToString(); + //} + + //private bool IsSameThreadAndShard() + //{ + // return CurrentConnectionConfig.IsShardSameThread && _ThreadId == Thread.CurrentThread.ManagedThreadId.ToString(); + //} + + private SqlSugarProvider CopyClient() + { + var result = new SqlSugarProvider(this.CurrentConnectionConfig); + result.MappingColumns = _MappingColumns; + result.MappingTables = _MappingTables; + result.IgnoreColumns = _IgnoreColumns; + result.IgnoreInsertColumns = _IgnoreInsertColumns; + + return result; + } + private void AddCallContext(SqlSugarProvider context) + { + CallContext.ContextList.Value = new List(); + CallContext.ContextList.Value.Add(context); + } + + private SqlSugarProvider GetCallContext() + { + return CallContext.ContextList.Value.FirstOrDefault(it => + it.CurrentConnectionConfig.DbType == _Context.CurrentConnectionConfig.DbType && + it.CurrentConnectionConfig.ConnectionString == _Context.CurrentConnectionConfig.ConnectionString && + it.CurrentConnectionConfig.InitKeyType == _Context.CurrentConnectionConfig.InitKeyType && + it.CurrentConnectionConfig.IsAutoCloseConnection == _Context.CurrentConnectionConfig.IsAutoCloseConnection + ); + } + + protected virtual void InitContext(ConnectionConfig config) + { + var aopIsNull = config.AopEvents == null; + if (aopIsNull) + { + config.AopEvents = new AopEvents(); + } + _Context = new SqlSugarProvider(config); + if (!aopIsNull) + _Context.Ado.IsEnableLogEvent = true; + this.CurrentConnectionConfig = config; + _ThreadId = Environment.CurrentManagedThreadId.ToString(); + if (this.MappingTables == null) + this.MappingTables = new MappingTableList(); + if (this.MappingColumns == null) + this.MappingColumns = new MappingColumnList(); + if (this.IgnoreColumns == null) + this.IgnoreColumns = new IgnoreColumnList(); + if (this.IgnoreInsertColumns == null) + this.IgnoreInsertColumns = new IgnoreColumnList(); + } + + private void InitConfigs(List configs) + { + foreach (var item in configs) + { + if (item.ConfigId == null) + { + item.ConfigId = ""; + } + } + } + private void AllClientEach(Action action) + { + if (this._AllClients == null) + { + this._AllClients = new List(); + this._AllClients.Add(new SugarTenant() { ConnectionConfig = this.CurrentConnectionConfig, Context = this.Context }); + } + if (_AllClients.HasValue()) + { + foreach (var item in _AllClients.Where(it => it.Context.HasValue())) + { + action(item.Context); + } + } + } + + + private async Task AllClientEachAsync(Func action) + { + if (this._AllClients == null) + { + this._AllClients = new List(); + this._AllClients.Add(new SugarTenant() { ConnectionConfig = this.CurrentConnectionConfig, Context = this.Context }); + } + if (_AllClients.HasValue()) + { + foreach (var item in _AllClients.Where(it => it.Context.HasValue())) + { + await action(item.Context).ConfigureAwait(false); + } + } + } + private void InitTenant(SugarTenant Tenant) + { + if (Tenant.Context == null) + { + Tenant.Context = new SqlSugarProvider(Tenant.ConnectionConfig); + } + _Context = Tenant.Context; + this.CurrentConnectionConfig = Tenant.ConnectionConfig; + } + #endregion + + #region Tenant Crud + public ISugarQueryable QueryableWithAttr() + { + var result = this.GetConnectionWithAttr().Queryable(); + result.QueryBuilder.IsCrossQueryWithAttr = true; + return result; + } + public IInsertable InsertableWithAttr(T insertObj) where T : class, new() + { + var result = this.GetConnectionWithAttr().Insertable(insertObj); + result.InsertBuilder.IsWithAttr = true; + return result; + } + public IInsertable InsertableWithAttr(List insertObjs) where T : class, new() + { + var result = this.GetConnectionWithAttr().Insertable(insertObjs); + result.InsertBuilder.IsWithAttr = true; + return result; + } + public IUpdateable UpdateableWithAttr(T updateObj) where T : class, new() + { + return this.GetConnectionWithAttr().Updateable(updateObj); + } + public IUpdateable UpdateableWithAttr() where T : class, new() + { + return this.GetConnectionWithAttr().Updateable(); + } + public IUpdateable UpdateableWithAttr(List updateObjs) where T : class, new() + { + return this.GetConnectionWithAttr().Updateable(updateObjs); + } + public IDeleteable DeleteableWithAttr(T deleteObject) where T : class, new() + { + return this.GetConnectionWithAttr().Deleteable(deleteObject); + } + public IDeleteable DeleteableWithAttr() where T : class, new() + { + return this.GetConnectionWithAttr().Deleteable(); + } + public IDeleteable DeleteableWithAttr(List deleteObjects) where T : class, new() + { + return this.GetConnectionWithAttr().Deleteable(deleteObjects); + } + #endregion + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/SqlSugarScope.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/SqlSugarScope.cs new file mode 100644 index 000000000..8dcbf4f5c --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/SqlSugarScope.cs @@ -0,0 +1,917 @@ +using System.Data; +using System.Dynamic; +using System.Linq.Expressions; + +namespace SqlSugar +{ + public partial class SqlSugarScope : ISqlSugarClient, ITenant + { + private SqlSugarScope() + { + + } + public SqlSugarScope(ConnectionConfig config) + { + _configs = new List() { config }; + } + public SqlSugarScope(List configs) + { + _configs = configs; + } + public SqlSugarScope(ConnectionConfig config, Action configAction) + { + _configs = new List() { config }; + this._configAction = configAction; + } + public SqlSugarScope(List configs, Action configAction) + { + _configs = configs; + this._configAction = configAction; + } + public SqlSugarClient ScopedContext { get { return GetContext(); } } + public SugarActionType SugarActionType { get => ScopedContext.SugarActionType; set => ScopedContext.SugarActionType = value; } + public MappingTableList MappingTables { get => ScopedContext.MappingTables; set => ScopedContext.MappingTables = value; } + public MappingColumnList MappingColumns { get => ScopedContext.MappingColumns; set => ScopedContext.MappingColumns = value; } + public IgnoreColumnList IgnoreColumns { get => ScopedContext.IgnoreColumns; set => ScopedContext.IgnoreColumns = value; } + public IgnoreColumnList IgnoreInsertColumns { get => ScopedContext.IgnoreInsertColumns; set => ScopedContext.IgnoreInsertColumns = value; } + public Dictionary TempItems { get => ScopedContext.TempItems; set => ScopedContext.TempItems = value; } + public ConfigQuery ConfigQuery { get => ScopedContext.ConfigQuery; set => ScopedContext.ConfigQuery = value; } + + public Guid ContextID { get => ScopedContext.ContextID; set => ScopedContext.ContextID = value; } + public ConnectionConfig CurrentConnectionConfig { get => ScopedContext.CurrentConnectionConfig; set => ScopedContext.CurrentConnectionConfig = value; } + + public IAdo Ado => ScopedContext.Ado; + + public AopProvider Aop => ScopedContext.Aop; + + public ICodeFirst CodeFirst => ScopedContext.CodeFirst; + + public IDbFirst DbFirst => ScopedContext.DbFirst; + + public IDbMaintenance DbMaintenance => ScopedContext.DbMaintenance; + + public EntityMaintenance EntityMaintenance { get => ScopedContext.EntityMaintenance; set => ScopedContext.EntityMaintenance = value; } + public QueryFilterProvider QueryFilter { get => ScopedContext.QueryFilter; set => ScopedContext.QueryFilter = value; } + public IContextMethods Utilities { get => ScopedContext.Utilities; set => ScopedContext.Utilities = value; } + public QueueList Queues { get => ScopedContext.Queues; set => ScopedContext.Queues = value; } + + public SugarCacheProvider DataCache => ScopedContext.DataCache; + + public ITenant AsTenant() + { + return ScopedContext.AsTenant(); + } + public void AddConnection(ConnectionConfig connection) + { + ScopedContext.AddConnection(connection); + } + + public void AddQueue(string sql, object parsmeters = null) + { + ScopedContext.AddQueue(sql, parsmeters); + } + + public void AddQueue(string sql, List parsmeters) + { + ScopedContext.AddQueue(sql, parsmeters); + } + + public void AddQueue(string sql, SugarParameter parsmeter) + { + ScopedContext.AddQueue(sql, parsmeter); + } + + public void BeginTran() + { + ScopedContext.BeginTran(); + } + public void BeginTran(IsolationLevel iso) + { + ScopedContext.BeginTran(iso); + } + public Task BeginTranAsync() + { + return ScopedContext.BeginTranAsync(); + } + public async Task BeginTranAsync(IsolationLevel iso) + { + await ScopedContext.BeginTranAsync(iso).ConfigureAwait(false); + } + public void ChangeDatabase(object configId) + { + ScopedContext.ChangeDatabase(configId); + } + + public void ChangeDatabase(Func changeExpression) + { + ScopedContext.ChangeDatabase(changeExpression); + } + + public void Close() + { + ScopedContext.Close(); + } + + public void CommitTran() + { + ScopedContext.CommitTran(); + } + public Task CommitTranAsync() + { + return ScopedContext.CommitTranAsync(); + } + public DeleteMethodInfo DeleteableByObject(object singleEntityObjectOrListObject) + { + return ScopedContext.DeleteableByObject(singleEntityObjectOrListObject); + } + public IDeleteable Deleteable() where T : class, new() + { + return ScopedContext.Deleteable(); + } + + public IDeleteable Deleteable(dynamic primaryKeyValue) where T : class, new() + { + return ScopedContext.Deleteable(primaryKeyValue); + } + + public IDeleteable Deleteable(dynamic[] primaryKeyValues) where T : class, new() + { + return ScopedContext.Deleteable(primaryKeyValues); + } + + public IDeleteable Deleteable(Expression> expression) where T : class, new() + { + return ScopedContext.Deleteable(expression); + } + + public IDeleteable Deleteable(List pkValue) where T : class, new() + { + return ScopedContext.Deleteable(pkValue); + } + + public IDeleteable Deleteable(List deleteObjs) where T : class, new() + { + return ScopedContext.Deleteable(deleteObjs); + } + + public IDeleteable Deleteable(T deleteObj) where T : class, new() + { + return ScopedContext.Deleteable(deleteObj); + } + + public void Dispose() + { + ScopedContext.Dispose(); + } + + public SqlSugarProvider GetConnection(object configId) + { + return ScopedContext.GetConnection(configId); + } + public SqlSugarScopeProvider GetConnectionScope(object configId) + { + return ScopedContext.GetConnectionScope(configId); + } + public SqlSugarProvider GetConnectionWithAttr() + { + return ScopedContext.GetConnectionWithAttr(); + } + public SqlSugarScopeProvider GetConnectionScopeWithAttr() + { + return ScopedContext.GetConnectionScopeWithAttr(); + } + public DateTime GetDate() + { + return ScopedContext.GetDate(); + } + + public T CreateContext(bool isTran = true) where T : SugarUnitOfWork, new() + { + return ScopedContext.CreateContext(isTran); + } + public SugarUnitOfWork CreateContext(bool isTran = true) + { + return ScopedContext.CreateContext(isTran); + } + + public SimpleClient GetSimpleClient() where T : class, new() + { + return ScopedContext.GetSimpleClient(); + } + public RepositoryType GetRepository() where RepositoryType : ISugarRepository, new() + { + return ScopedContext.GetRepository(); + } + public void InitMappingInfo(Type type) + { + ScopedContext.InitMappingInfo(type); + } + + public void InitMappingInfo() + { + ScopedContext.InitMappingInfo(); + } + + public IInsertable> InsertableByDynamic(object insertDynamicObject) + { + return ScopedContext.InsertableByDynamic(insertDynamicObject); + } + public IInsertable Insertable(Dictionary columnDictionary) where T : class, new() + { + return ScopedContext.Insertable(columnDictionary); + } + + public IInsertable Insertable(dynamic insertDynamicObject) where T : class, new() + { + return ScopedContext.Insertable((object)insertDynamicObject); + } + + public IInsertable Insertable(List insertObjs) where T : class, new() + { + return ScopedContext.Insertable(insertObjs); + } + + public IInsertable Insertable(T insertObj) where T : class, new() + { + return ScopedContext.Insertable(insertObj); + } + + public IInsertable Insertable(T[] insertObjs) where T : class, new() + { + return ScopedContext.Insertable(insertObjs); + } + public InsertMethodInfo InsertableByObject(object singleEntityObjectOrListObject) + { + return ScopedContext.InsertableByObject(singleEntityObjectOrListObject); + } + public void Open() + { + ScopedContext.Open(); + } + public ISugarQueryable SlaveQueryable() + { + return ScopedContext.SlaveQueryable(); + } + public ISugarQueryable MasterQueryable() + { + return ScopedContext.MasterQueryable(); + } + public ISugarQueryable Queryable(string tableName, string shortName) + { + return ScopedContext.Queryable(tableName, shortName); + } + + public ISugarQueryable Queryable(Expression> joinExpression) where T : class, new() + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) where T : class, new() + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) where T : class, new() + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) where T : class, new() + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) where T : class, new() + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) where T : class, new() + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) where T : class, new() + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) where T : class, new() + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) where T : class, new() + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) where T : class, new() + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) where T : class, new() + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(Expression> joinExpression) + { + return ScopedContext.Queryable(joinExpression); + } + + public ISugarQueryable Queryable(ISugarQueryable joinQueryable1, ISugarQueryable joinQueryable2, Expression> joinExpression) + where T : class, new() + where T2 : class, new() + { + return ScopedContext.Queryable(joinQueryable1, joinQueryable2, joinExpression); + } + + public ISugarQueryable Queryable(ISugarQueryable joinQueryable1, ISugarQueryable joinQueryable2, JoinType joinType, Expression> joinExpression) + where T : class, new() + where T2 : class, new() + { + return ScopedContext.Queryable(joinQueryable1, joinQueryable2, joinType, joinExpression); + } + + public ISugarQueryable Queryable(ISugarQueryable joinQueryable1, ISugarQueryable joinQueryable2, ISugarQueryable joinQueryable3, JoinType joinType1, Expression> joinExpression1, JoinType joinType2, Expression> joinExpression2) + where T : class, new() + where T2 : class, new() + where T3 : class, new() + { + return ScopedContext.Queryable(joinQueryable1, joinQueryable2, joinQueryable3, joinType1, joinExpression1, joinType2, joinExpression2); + } + public ISugarQueryable Queryable(ISugarQueryable joinQueryable1, ISugarQueryable joinQueryable2, ISugarQueryable joinQueryable3, ISugarQueryable joinQueryable4, JoinType joinType1, Expression> joinExpression1, JoinType joinType2, Expression> joinExpression2, JoinType joinType3, Expression> joinExpression3) + where T : class, new() + where T2 : class, new() + where T3 : class, new() + where T4 : class, new() + { + return ScopedContext.Queryable(joinQueryable1, joinQueryable2, joinQueryable3, joinQueryable4, joinType1, joinExpression1, joinType2, joinExpression2, joinType3, joinExpression3); + } + public ISugarQueryable Queryable() + { + return ScopedContext.Queryable(); + } + + public ISugarQueryable Queryable(ISugarQueryable queryable) + { + return ScopedContext.Queryable(queryable); + } + public ISugarQueryable Queryable(ISugarQueryable queryable, string shortName) + { + return ScopedContext.Queryable(queryable, shortName); + } + + public ISugarQueryable Queryable(string shortName) + { + return ScopedContext.Queryable(shortName); + } + + public IReportable Reportable(T data) + { + return ScopedContext.Reportable(data); + } + + public IReportable Reportable(List list) + { + return ScopedContext.Reportable(list); + } + + public IReportable Reportable(T[] array) + { + return ScopedContext.Reportable(array); + } + + public void RollbackTran() + { + ScopedContext.RollbackTran(); + } + public Task RollbackTranAsync() + { + return ScopedContext.RollbackTranAsync(); + } + + [Obsolete("use Storageable")] + public ISaveable Saveable(List saveObjects) where T : class, new() + { + return ScopedContext.Saveable(saveObjects); + } + [Obsolete("use Storageable")] + public ISaveable Saveable(T saveObject) where T : class, new() + { + return ScopedContext.Saveable(saveObject); + } + + public int SaveQueues(bool isTran = true) + { + return ScopedContext.SaveQueues(isTran); + } + + public Tuple, List, List, List, List, List, List> SaveQueues(bool isTran = true) + { + return ScopedContext.SaveQueues(isTran); + } + + public Tuple, List, List, List, List, List> SaveQueues(bool isTran = true) + { + return ScopedContext.SaveQueues(isTran); + } + + public Tuple, List, List, List, List> SaveQueues(bool isTran = true) + { + return ScopedContext.SaveQueues(isTran); + } + + public Tuple, List, List, List> SaveQueues(bool isTran = true) + { + return ScopedContext.SaveQueues(isTran); + } + + public Tuple, List, List> SaveQueues(bool isTran = true) + { + return ScopedContext.SaveQueues(isTran); + } + + public Tuple, List> SaveQueues(bool isTran = true) + { + return ScopedContext.SaveQueues(isTran); + } + + public List SaveQueues(bool isTran = true) + { + return ScopedContext.SaveQueues(isTran); + } + + public Task SaveQueuesAsync(bool isTran = true) + { + return ScopedContext.SaveQueuesAsync(isTran); + } + + public Task, List, List, List, List, List, List>> SaveQueuesAsync(bool isTran = true) + { + return ScopedContext.SaveQueuesAsync(isTran); + } + + public Task, List, List, List, List, List>> SaveQueuesAsync(bool isTran = true) + { + return ScopedContext.SaveQueuesAsync(isTran); + } + + public Task, List, List, List, List>> SaveQueuesAsync(bool isTran = true) + { + return ScopedContext.SaveQueuesAsync(isTran); + } + + public Task, List, List, List>> SaveQueuesAsync(bool isTran = true) + { + return ScopedContext.SaveQueuesAsync(isTran); + } + + public Task, List, List>> SaveQueuesAsync(bool isTran = true) + { + return ScopedContext.SaveQueuesAsync(isTran); + } + + public Task, List>> SaveQueuesAsync(bool isTran = true) + { + return ScopedContext.SaveQueuesAsync(isTran); + } + + public Task> SaveQueuesAsync(bool isTran = true) + { + return ScopedContext.SaveQueuesAsync(isTran); + } + + public ISugarQueryable SqlQueryable(string sql) where T : class, new() + { + return ScopedContext.SqlQueryable(sql); + } + public IStorageable Storageable(T[] dataList) where T : class, new() + { + return ScopedContext.Storageable(dataList); + } + public StorageableDataTable Storageable(List> dictionaryList, string tableName) + { + return ScopedContext.Storageable(dictionaryList, tableName); + } + public StorageableDataTable Storageable(Dictionary dictionary, string tableName) + { + return ScopedContext.Storageable(dictionary, tableName); + } + public IStorageable Storageable(List dataList) where T : class, new() + { + return ScopedContext.Storageable(dataList); + } + public IStorageable Storageable(IList dataList) where T : class, new() + { + return ScopedContext.Storageable(dataList?.ToList()); + } + public IStorageable Storageable(T data) where T : class, new() + { + return ScopedContext.Storageable(data); + } + public StorageableDataTable Storageable(DataTable data) + { + return ScopedContext.Storageable(data); + } + public StorageableMethodInfo StorageableByObject(object singleEntityObjectOrListObject) + { + return this.ScopedContext.StorageableByObject(singleEntityObjectOrListObject); + } + + public ISugarQueryable Union(List> queryables) where T : class + { + return ScopedContext.Union(queryables); + } + + public ISugarQueryable Union(params ISugarQueryable[] queryables) where T : class + { + return ScopedContext.Union(queryables); + } + + public ISugarQueryable UnionAll(List> queryables) where T : class + { + return ScopedContext.UnionAll(queryables); + } + + public ISugarQueryable UnionAll(params ISugarQueryable[] queryables) where T : class + { + return ScopedContext.UnionAll(queryables); + } + public UpdateMethodInfo UpdateableByObject(object singleEntityObjectOrListObject) + { + return ScopedContext.UpdateableByObject(singleEntityObjectOrListObject); + } + public UpdateExpressionMethodInfo UpdateableByObject(Type entityType) + { + return ScopedContext.UpdateableByObject(entityType); + } + public IUpdateable> UpdateableByDynamic(object updateDynamicObject) + { + return ScopedContext.UpdateableByDynamic(updateDynamicObject); + } + public IUpdateable Updateable() where T : class, new() + { + return ScopedContext.Updateable(); + } + + public IUpdateable Updateable(Dictionary columnDictionary) where T : class, new() + { + return ScopedContext.Updateable(columnDictionary); + } + + public IUpdateable Updateable(dynamic updateDynamicObject) where T : class, new() + { + return ScopedContext.Updateable((object)updateDynamicObject); + } + + public IUpdateable Updateable(Expression> columns) where T : class, new() + { + return ScopedContext.Updateable(columns); + } + + public IUpdateable Updateable(Expression> columns) where T : class, new() + { + return ScopedContext.Updateable(columns); + } + + public IUpdateable Updateable(List UpdateObjs) where T : class, new() + { + return ScopedContext.Updateable(UpdateObjs); + } + + public IUpdateable Updateable(T UpdateObj) where T : class, new() + { + return ScopedContext.Updateable(UpdateObj); + } + + public IUpdateable Updateable(T[] UpdateObjs) where T : class, new() + { + return ScopedContext.Updateable(UpdateObjs); + } + public SplitTableContext SplitHelper() where T : class, new() + { + return ScopedContext.SplitHelper(); + } + public SplitTableContext SplitHelper(Type entityType) + { + return ScopedContext.SplitHelper(entityType); + } + public SplitTableContextResult SplitHelper(T data) where T : class, new() + { + return ScopedContext.SplitHelper(data); + } + public SplitTableContextResult SplitHelper(List dataList) where T : class, new() + { + return ScopedContext.SplitHelper(dataList); + } + public SqlSugarTransaction UseTran() + { + return ScopedContext.UseTran(); + } + public DbResult UseTran(Action action, Action errorCallBack = null) + { + return ScopedContext.UseTran(action, errorCallBack); + } + + public DbResult UseTran(Func action, Action errorCallBack = null) + { + return ScopedContext.UseTran(action, errorCallBack); + } + + public Task> UseTranAsync(Func action, Action errorCallBack = null) + { + return ScopedContext.UseTranAsync(action, errorCallBack); + } + + public Task> UseTranAsync(Func> action, Action errorCallBack = null) + { + return ScopedContext.UseTranAsync(action, errorCallBack); + } + + public bool IsAnyConnection(object configId) + { + return ScopedContext.IsAnyConnection(configId); + } + + public IFastest Fastest() where T : class, new() + { + return ScopedContext.Fastest(); + } + + public void ThenMapper(IEnumerable list, Action action) + { + ScopedContext.ThenMapper(list, action); + } + + public Task ThenMapperAsync(IEnumerable list, Func action) + { + return ScopedContext.ThenMapperAsync(list, action); + } + public ISugarQueryable QueryableWithAttr() + { + return ScopedContext.QueryableWithAttr(); + } + public IInsertable InsertableWithAttr(T insertObj) where T : class, new() + { + return ScopedContext.InsertableWithAttr(insertObj); + } + public IInsertable InsertableWithAttr(List insertObjs) where T : class, new() + { + return ScopedContext.InsertableWithAttr(insertObjs); + } + public IUpdateable UpdateableWithAttr(T updateObj) where T : class, new() + { + return ScopedContext.UpdateableWithAttr(updateObj); + } + + public IUpdateable UpdateableWithAttr() where T : class, new() + { + return ScopedContext.UpdateableWithAttr(); + } + + public IUpdateable UpdateableWithAttr(List updateObjs) where T : class, new() + { + return ScopedContext.UpdateableWithAttr(updateObjs); + } + public IDeleteable DeleteableWithAttr(T deleteObj) where T : class, new() + { + return ScopedContext.DeleteableWithAttr(deleteObj); + } + public IDeleteable DeleteableWithAttr() where T : class, new() + { + return ScopedContext.DeleteableWithAttr(); + } + public IDeleteable DeleteableWithAttr(List deleteObjs) where T : class, new() + { + return ScopedContext.DeleteableWithAttr(deleteObjs); + } + + public InsertNavTaskInit InsertNav(T data) where T : class, new() + { + return ScopedContext.InsertNav(data); + } + public InsertNavTaskInit InsertNav(List datas) where T : class, new() + { + return ScopedContext.InsertNav(datas); + } + public InsertNavTaskInit InsertNav(T data, InsertNavRootOptions rootOptions) where T : class, new() + { + return ScopedContext.InsertNav(data, rootOptions); + } + public InsertNavTaskInit InsertNav(List datas, InsertNavRootOptions rootOptions) where T : class, new() + { + return ScopedContext.InsertNav(datas, rootOptions); + } + public DeleteNavTaskInit DeleteNav(T data) where T : class, new() + { + return ScopedContext.DeleteNav(data); + } + public DeleteNavTaskInit DeleteNav(List datas) where T : class, new() + { + return ScopedContext.DeleteNav(datas); + } + public DeleteNavTaskInit DeleteNav(Expression> whereExpression) where T : class, new() + { + return ScopedContext.DeleteNav(whereExpression); + } + + + public DeleteNavTaskInit DeleteNav(T data, DeleteNavRootOptions options) where T : class, new() + { + return ScopedContext.DeleteNav(data, options); + } + public DeleteNavTaskInit DeleteNav(List datas, DeleteNavRootOptions options) where T : class, new() + { + return ScopedContext.DeleteNav(datas, options); + } + public DeleteNavTaskInit DeleteNav(Expression> whereExpression, DeleteNavRootOptions options) where T : class, new() + { + return ScopedContext.DeleteNav(whereExpression, options); + } + + public UpdateNavTaskInit UpdateNav(T data) where T : class, new() + { + return ScopedContext.UpdateNav(data); + } + public UpdateNavTaskInit UpdateNav(List datas) where T : class, new() + { + return ScopedContext.UpdateNav(datas); + } + public UpdateNavTaskInit UpdateNav(T data, UpdateNavRootOptions rootOptions) where T : class, new() + { + return ScopedContext.UpdateNav(data, rootOptions); + } + public UpdateNavTaskInit UpdateNav(List datas, UpdateNavRootOptions rootOptions) where T : class, new() + { + return ScopedContext.UpdateNav(datas, rootOptions); + } + public SqlSugarClient CopyNew() + { + var result = new SqlSugarClient(UtilMethods.CopyConfig(this.Ado.Context.CurrentConnectionConfig)); + result.QueryFilter = this.QueryFilter; + if (this.ScopedContext._AllClients != null) + { + foreach (var item in this.ScopedContext._AllClients) + { + if (!result.IsAnyConnection(item.ConnectionConfig.ConfigId)) + { + result.AddConnection(UtilMethods.CopyConfig(item.ConnectionConfig)); + } + } + } + return result; + } + public DynamicBuilder DynamicBuilder() + { + return ScopedContext.DynamicBuilder(); + } + public void Tracking(T data) where T : class, new() + { + ScopedContext.Tracking(data); + } + public void Tracking(List datas) where T : class, new() + { + ScopedContext.Tracking(datas); + } + public void RemoveConnection(dynamic configId) + { + ScopedContext.RemoveConnection(configId); + } + public Task AsyncLock(int timeOutSeconds = 30) + { + return ScopedContext.AsyncLock(timeOutSeconds); + } + public QueryMethodInfo QueryableByObject(Type entityType) + { + return ScopedContext.QueryableByObject(entityType); + } + public QueryMethodInfo QueryableByObject(Type entityType, string shortName) + { + return ScopedContext.QueryableByObject(entityType, shortName); + } + public GridSaveProvider GridSave(List oldList, List saveList) where T : class, new() + { + return ScopedContext.GridSave(oldList, saveList); + } + public GridSaveProvider GridSave(List saveList) where T : class, new() + { + return ScopedContext.GridSave(saveList); + } + public void ClearTracking() + { + ScopedContext.ClearTracking(); + } + public string[] GetCurrentConfigIds() + { + return ScopedContext.GetCurrentConfigIds(); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/SugarUnitOfWork.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/SugarUnitOfWork.cs new file mode 100644 index 000000000..e606ee6c8 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/SugarUnitOfWork.cs @@ -0,0 +1,109 @@ +using System.Reflection; + +namespace SqlSugar +{ + public interface ISugarUnitOfWork where T : SugarUnitOfWork, new() + { + ISqlSugarClient Db { get; set; } + T CreateContext(bool isTran = true); + } + public class SugarUnitOfWork : ISugarUnitOfWork where T : SugarUnitOfWork, new() + { + public SugarUnitOfWork(ISqlSugarClient db) + { + this.Db = db; + } + public ISqlSugarClient Db { get; set; } + public T CreateContext(bool isTran = true) + { + return Db.CreateContext(isTran); + } + } + public interface ISugarUnitOfWorkClear + { + RepositoryType GetMyRepository() where RepositoryType : new(); + + bool Commit(); + } + /// + /// ISugarUnitOfWorkClear not exists SqlSugar method + /// ISugarUnitOfWork exists SqlSugar method + /// + public interface ISugarUnitOfWork : ISugarUnitOfWorkClear + { + ISqlSugarClient Db { get; } + ITenant Tenant { get; } + + SimpleClient GetRepository() where T : class, new(); + } + /// + /// SugarUnitOfWork->ISugarUnitOfWork->ISaugarUnitOfWorkClear + /// ISaugarUnitOfWorkClear not exists SqlSugar method + /// ISugarUnitOfWork exists SqlSugar method + /// + public class SugarUnitOfWork : IDisposable, ISugarUnitOfWork + { + public ISqlSugarClient Db { get; internal set; } + public ITenant Tenant { get; internal set; } + public bool IsTran { get; internal set; } + public bool IsCommit { get; internal set; } + public bool IsClose { get; internal set; } + + public void Dispose() + { + + if (this.IsTran && IsCommit == false) + { + this.Tenant.RollbackTran(); + } + if (this.Db.Ado.Transaction == null && IsClose == false) + { + this.Db.Close(); + } + } + + public SimpleClient GetRepository() where T : class, new() + { + TenantAttribute tenantAttribute = typeof(T).GetCustomAttribute(); + if (tenantAttribute == null) + { + return new SimpleClient(Db); + } + else + { + return new SimpleClient(Db.AsTenant().GetConnection(tenantAttribute.configId)); + } + } + + public RepositoryType GetMyRepository() where RepositoryType : new() + { + var result = (ISugarRepository)new RepositoryType(); + var type = typeof(RepositoryType).GetGenericArguments().FirstOrDefault(); + TenantAttribute tenantAttribute = type?.GetCustomAttribute(); + if (tenantAttribute == null) + { + result.Context = this.Db; + } + else + { + result.Context = this.Db.AsTenant().GetConnection(tenantAttribute.configId); + } + return (RepositoryType)result; + } + + public bool Commit() + { + if (this.IsTran && this.IsCommit == false) + { + this.Tenant.CommitTran(); + IsCommit = true; + } + if (this.Db.Ado.Transaction == null && this.IsClose == false) + { + this.Db.Close(); + IsClose = true; + } + return IsCommit; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/CallContext.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/CallContext.cs new file mode 100644 index 000000000..69c3e3e2d --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/CallContext.cs @@ -0,0 +1,8 @@ +namespace SqlSugar +{ + internal static class CallContext + { + public static ThreadLocal> ContextList = new ThreadLocal>(); + public static ThreadLocal> MapperExpression = new ThreadLocal>(); + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/CallContextAsync.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/CallContextAsync.cs new file mode 100644 index 000000000..f47a0a89a --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/CallContextAsync.cs @@ -0,0 +1,22 @@ +using System.Collections.Concurrent; + +namespace SqlSugar +{ + public static class CallContextAsync + { + static ConcurrentDictionary> state = new ConcurrentDictionary>(); + public static void SetData(string name, T data) => + state.GetOrAdd(name, _ => new AsyncLocal()).Value = data; + public static T GetData(string name) => + state.TryGetValue(name, out AsyncLocal data) ? data.Value : default(T); + } + + public static class CallContextThread + { + static ConcurrentDictionary> state = new ConcurrentDictionary>(); + public static void SetData(string name, T data) => + state.GetOrAdd(name, _ => new ThreadLocal()).Value = data; + public static T GetData(string name) => + state.TryGetValue(name, out ThreadLocal data) ? data.Value : default(T); + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/Check.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/Check.cs new file mode 100644 index 000000000..52f63f25f --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/Check.cs @@ -0,0 +1,43 @@ +using System.Text; +namespace SqlSugar +{ + public static class Check + { + public static void ThrowNotSupportedException(string message) + { + message = message.IsNullOrEmpty() ? new NotSupportedException().Message : message; + throw new SqlSugarException("SqlSugarException.NotSupportedException:" + message); + } + + public static void ArgumentNullException(object checkObj, string message) + { + if (checkObj == null) + throw new SqlSugarException("SqlSugarException.ArgumentNullException:" + message); + } + + public static void ArgumentNullException(object[] checkObj, string message) + { + if (checkObj == null || checkObj.Length == 0) + throw new SqlSugarException("SqlSugarException.ArgumentNullException:" + message); + } + public static void Exception(bool isException, CompositeFormat compositeFormat, params string[] args) + { + if (isException) + throw new SqlSugarException(string.Format(null, compositeFormat, args)); + } + public static void Exception(bool isException, string message, params string[] args) + { + if (isException) + throw new SqlSugarException(string.Format(message, args)); + } + public static void ExceptionEasy(string enMessage, string cnMessage) + { + throw new SqlSugarException(ErrorMessage.GetThrowMessage(enMessage, cnMessage)); + } + public static void ExceptionEasy(bool isException, string enMessage, string cnMessage) + { + if (isException) + throw new SqlSugarException(ErrorMessage.GetThrowMessage(enMessage, cnMessage)); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/CommonExtensions.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/CommonExtensions.cs new file mode 100644 index 000000000..c4b0f90a6 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/CommonExtensions.cs @@ -0,0 +1,156 @@ +using System.Reflection; + +namespace SqlSugar +{ + public static class CommonExtensions + { + + public static string GetNonNegativeHashCodeString(this string input) + { + // 获取哈希码,然后取绝对值,转换为字符串 + string hashCode = "hs" + Math.Abs(input.GetHashCode()); + return hashCode; + } + public static string SafeSubstring(this string str, int startIndex, int length) + { + if (str == null) + { + throw new ArgumentNullException(nameof(str)); + } + + if (startIndex < 0) + { + startIndex = 0; + } + + if (startIndex >= str.Length) + { + return string.Empty; // 返回空字符串,因为起始索引超过字符串长度 + } + + if (length < 0) + { + length = 0; + } + + // 截取字符串时,确保不超过字符串的长度 + return str.Substring(startIndex, Math.Min(length, str.Length - startIndex)); + } + public static Dictionary ToDictionary(this List list, string keyPropertyName, string valuePropertyName) + { + var keyProperty = typeof(T).GetProperty(keyPropertyName); + var valueProperty = typeof(T).GetProperty(valuePropertyName); + + return list.ToDictionary( + item => keyProperty.GetValue(item) + "", + item => valueProperty.GetValue(item) + ); + } + public static MethodInfo GetMyMethod(this Type type, string name, int argCount) + { + return type.GetMethods().FirstOrDefault(it => it.Name == name && it.GetParameters().Length == argCount); + } + public static MethodInfo GetMyMethod(this Type type, string name, int argCount, Type parameterType) + { + return type.GetMethods().FirstOrDefault(it => + it.Name == name && + it.GetParameters().Length == argCount && + it.GetParameters().First().ParameterType == parameterType); + } + public static MethodInfo GetMyMethod(this Type type, string name, int argCount, bool isList) + { + var methods = type.GetMethods().Where(it => it.Name == name).Where(it => + it.GetParameters().Length == argCount && + it.GetParameters()[0].ToString().Contains("List`") == isList).ToList(); + return methods.First(); + } + + public static MethodInfo GetMyMethod(this Type type, string name, int argCount, Type parameterType, Type parameterType2) + { + return type.GetMethods().Where(it => it.Name == name).FirstOrDefault(it => + it.GetParameters().Length == argCount && + it.GetParameters().First().ParameterType == parameterType && + it.GetParameters()[1].ParameterType == parameterType2); + } + + public static MethodInfo GetMyMethodNoGen(this Type type, string name, int argCount, Type parameterType, Type parameterType2) + { + return type.GetMethods().Where(it => it.Name == name && it?.GetGenericArguments()?.Length == 0).FirstOrDefault(it => + it.GetParameters().Length == argCount && + it.GetParameters().First().ParameterType == parameterType && + it.GetParameters()[1].ParameterType == parameterType2); + } + public static MethodInfo GetMyMethod(this Type type, string name, int argCount, Type parameterType, Type parameterType2, Type parameterType3) + { + return type.GetMethods().Where(it => it.Name == name).FirstOrDefault(it => + it.GetParameters().Length == argCount && + it.GetParameters().First().ParameterType == parameterType && + it.GetParameters()[1].ParameterType == parameterType2 && + it.GetParameters()[2].ParameterType == parameterType3); + } + public static MethodInfo GetMyMethod(this Type type, string name, int argCount, Type parameterType, Type parameterType2, Type parameterType3, Type parameterType4) + { + return type.GetMethods().Where(it => it.Name == name).FirstOrDefault(it => + it.GetParameters().Length == argCount && + it.GetParameters().First().ParameterType == parameterType && + it.GetParameters()[1].ParameterType == parameterType2 && + it.GetParameters()[2].ParameterType == parameterType3 && + it.GetParameters()[3].ParameterType == parameterType4); + } + public static MethodInfo GetMyMethod(this Type type, string name, int argCount, Type parameterType, Type parameterType2, Type parameterType3, Type parameterType4, Type parameterType5) + { + return type.GetMethods().Where(it => it.Name == name).FirstOrDefault(it => + it.GetParameters().Length == argCount && + it.GetParameters().First().ParameterType == parameterType && + it.GetParameters()[1].ParameterType == parameterType2 && + it.GetParameters()[2].ParameterType == parameterType3 && + it.GetParameters()[3].ParameterType == parameterType4 && + it.GetParameters()[4].ParameterType == parameterType5); + } + public static List ToList(this T thisValue, Func action) where T : class, new() + { + return new List { thisValue }; + } + public static List ToList(this IEnumerable thisValue, Func action) where T : class, new() + { + return thisValue.ToList(); + } + public static IEnumerable WhereIF(this IEnumerable thisValue, bool isOk, Func predicate) + { + if (isOk) + { + return thisValue.Where(predicate); + } + else + { + return thisValue; + } + } + public static IEnumerable MappingField(this IEnumerable thisValue, Func leftField, Func rightField) + { + return thisValue; + } + public static List MappingField(this T thisValue, Func leftField, Func rightField) where T : class + { + return new List() { thisValue }; + } + public static bool Any(this IEnumerable thisValue, List conditionalModels) + { + throw new Exception("Can only be used in expressions"); + } + public static IEnumerable Where(this IEnumerable thisValue, List conditionalModels) + { + throw new Exception("Can only be used in expressions"); + } + } +} +namespace System.Collections.Generic +{ + public static class EnumerableExtensions + { + public static bool Contains(this IEnumerable thisValue, T likeKey, bool isNvarchar) + { + return thisValue.Contains(likeKey); + } + } +} \ No newline at end of file diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/DataTableExtensions.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/DataTableExtensions.cs new file mode 100644 index 000000000..b3901f5b1 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/DataTableExtensions.cs @@ -0,0 +1,125 @@ +using System.Data; +using System.Dynamic; +using System.Linq.Expressions; + +namespace SqlSugar +{ + internal static class DataTableExtensions + { + public static DataTable ToPivotTable( + this IEnumerable source, + Func columnSelector, + Expression> rowSelector, + Func, TData> dataSelector) + { + + DataTable table = new DataTable(); + + var rowName = new List(); + var memberName = string.Empty; + if (rowSelector.Body is MemberExpression) + { + memberName = ((MemberExpression)rowSelector.Body).Member.Name; + rowName.Add(memberName); + } + else + rowName.AddRange(((NewExpression)rowSelector.Body).Arguments.Select(it => it as MemberExpression).Select(it => it.Member.Name)); + + + table.Columns.AddRange(rowName.Select(x => new DataColumn(x)).ToArray()); + var columns = source.Select(columnSelector).Distinct(); + table.Columns.AddRange(columns.Select(x => new DataColumn(x?.ToString())).ToArray()); + + Action> action; + if (string.IsNullOrEmpty(memberName)) + { + action = (row, rowGroup) => + { + var properties = rowGroup.Key.GetType().GetProperties(); + foreach (var item in properties) + row[item.Name] = item.GetValue(rowGroup.Key, null); + }; + } + else + { + action = (row, rowGroup) => row[memberName] = rowGroup.Key; + } + + var rows = source.GroupBy(rowSelector.Compile()) + .Select(rowGroup => + { + var row = table.NewRow(); + action(row, rowGroup); + foreach (var x in columns.GroupJoin(rowGroup, c => c, r => columnSelector(r), + (c, columnGroup) => + { + var dic = new Dictionary(); + if (c != null) + dic[c.ToString()] = dataSelector(columnGroup); + return dic; + }) + .SelectMany(x => x)) + { + row[x.Key] = x.Value; + } + table.Rows.Add(row); + return row; + }) + .ToList(); + + return table; + } + + public static IEnumerable ToPivotList( + this IEnumerable source, + Func columnSelector, + Expression> rowSelector, + Func, TData> dataSelector) + { + + var memberName = string.Empty; + + if (rowSelector.Body is MemberExpression) + memberName = ((MemberExpression)rowSelector.Body).Member.Name; + + var columns = source.Select(columnSelector).Distinct(); + + Action, IGrouping> action; + if (string.IsNullOrEmpty(memberName)) + { + action = (row, rowGroup) => + { + var properties = rowGroup.Key.GetType().GetProperties(); + foreach (var item in properties) + row[item.Name] = item.GetValue(rowGroup.Key, null); + }; + } + else + { + action = (row, rowGroup) => row[memberName] = rowGroup.Key; + } + + var rows = source.GroupBy(rowSelector.Compile()) + .Select(rowGroup => + { + IDictionary row = new ExpandoObject(); + action(row, rowGroup); + foreach (var x in columns.GroupJoin(rowGroup, c => c, r => columnSelector(r), + (c, columnGroup) => + { + var dic = new Dictionary(); + if (c != null) + dic[c.ToString()] = dataSelector(columnGroup); + return dic; + }) + .SelectMany(x => x)) + { + row[x.Key] = x.Value; + } + return row; + }); + return rows; + + } + } +} \ No newline at end of file diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/DbExtensions.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/DbExtensions.cs new file mode 100644 index 000000000..7de50e166 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/DbExtensions.cs @@ -0,0 +1,172 @@ +using System.Text.RegularExpressions; +namespace SqlSugar +{ + public static class DbExtensions + { + public static string ToJoinSqlInVals(this T[] array) + { + if (array == null || array.Length == 0) + { + return ToSqlValue(string.Empty); + } + else + { + return string.Join(",", array.Where(c => c != null).Select(it => it.ToSqlValue())); + } + } + public static string ToJoinSqlInValsByVarchar(this T[] array) + { + if (array == null || array.Length == 0) + { + return ToSqlValue(string.Empty); + } + else + { + return string.Join(",", array.Where(c => c != null).Select(it => "N" + it.ToSqlValue())); + } + } + public static string ToJoinSqlInValsN(this T[] array) + { + if (array == null || array.Length == 0) + { + return ToSqlValue(string.Empty); + } + else + { + return string.Join(",", array.Where(c => c != null).Select(it => "N" + it.ToSqlValue())); + } + } + public static string ToSqlValue(this object value) + { + if (value != null && UtilConstants.NumericalTypes.Contains(value.GetType())) + return value.ToString(); + + var str = value + ""; + return str.ToSqlValue(); + } + + public static string ToSqlValue(this string value) + { + return string.Format("'{0}'", value.ToSqlFilter()); + } + + /// + ///Sql Filter + /// + /// + /// + public static string ToSqlFilter(this string value) + { + if (!value.IsNullOrEmpty()) + { + var oldLength = value.Length; + value = value.Replace("'", "''"); + if (oldLength != value.Length && value.IndexOf(')') > 0 && value.IndexOf(@"\''") > 0) value = value.Replace("\\", "\\\\"); + } + return value; + } + + /// + /// Check field format + /// + /// + /// + public static string ToCheckField(this string value) + { + //You can override it because the default security level is very high + if (StaticConfig.Check_FieldFunc != null) + { + return StaticConfig.Check_FieldFunc(value); + } + + //Default method + else if (value != null) + { + if (value.IsContainsIn(";", "--")) + { + throw new Exception($"{value} format error "); + } + else if (value.IsContainsIn("//") && (value.Length - value.Replace("/", "").Length) >= 4) + { + throw new Exception($"{value} format error "); + } + else if (value.IsContainsIn('\'') && (value.Length - value.Replace("'", "").Length) % 2 != 0) + { + throw new Exception($"{value} format error "); + } + else if (IsUpdateSql(value, "/", "/")) + { + Check.ExceptionEasy($"{value} format error ", value + "不能存在 /+【update drop 等】+/ "); + } + else if (IsUpdateSql(value, "/", " ")) + { + Check.ExceptionEasy($"{value} format error ", value + "不能存在 /+【update drop 等】+空格 "); + } + else if (IsUpdateSql(value, " ", "/")) + { + Check.ExceptionEasy($"{value} format error ", value + "不能存在 空格+【update drop 等】+/ "); + } + else if (value.Contains(" update ", StringComparison.CurrentCultureIgnoreCase) + || value.Contains(" delete ", StringComparison.CurrentCultureIgnoreCase) + || value.Contains(" drop ", StringComparison.CurrentCultureIgnoreCase) + || value.Contains(" alert ", StringComparison.CurrentCultureIgnoreCase) + || value.Contains(" create ", StringComparison.CurrentCultureIgnoreCase) + || value.Contains(" insert ", StringComparison.CurrentCultureIgnoreCase)) + { + Check.ExceptionEasy($"{value} format error ", value + "不能存在 空格+【update drop 等】+空格 "); + } + } + return value; + } + + private static bool IsUpdateSql(string value, string left, string right) + { + return value.Contains(left + "update" + right, StringComparison.CurrentCultureIgnoreCase) + || value.Contains(left + "delete" + right, StringComparison.CurrentCultureIgnoreCase) + || value.Contains(left + "drop" + right, StringComparison.CurrentCultureIgnoreCase) + || value.Contains(left + "alert" + right, StringComparison.CurrentCultureIgnoreCase) + || value.Contains(left + "create" + right, StringComparison.CurrentCultureIgnoreCase) + || value.Contains(left + "insert" + right, StringComparison.CurrentCultureIgnoreCase); + } + public static bool ContainsChinese(string input) + { + // 正则表达式:匹配包含至少一个中文字符的字符串 + string pattern = @"[\u4e00-\u9fa5]"; + return Regex.IsMatch(input, pattern); + } + public static bool IsRegexWNoContainsChinese(this string value) + { + if (!ContainsChinese(value) && Regex.IsMatch(value, @"^\w+$")) + { + return true; + } + else + { + return false; + } + } + public static string ToCheckRegexW(this string value) + { + if (Regex.IsMatch(value, @"^\w+$")) + { + return value; + } + else + { + throw new Exception($"ToCheckRegexW {value} format error "); + } + } + internal static string ToLower(this string value, bool isAutoToLower) + { + if (value == null) return null; + if (isAutoToLower == false) return value; + return value.ToLower(); + } + internal static string ToUpper(this string value, bool isAutoToUpper) + { + if (value == null) return null; + if (isAutoToUpper == false) return value; + return value.ToUpper(); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/ErrorMessage.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/ErrorMessage.cs new file mode 100644 index 000000000..0661bd2a4 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/ErrorMessage.cs @@ -0,0 +1,45 @@ +using System.Text; +namespace SqlSugar +{ + internal static partial class ErrorMessage + { + internal static LanguageType SugarLanguageType { get; set; } = LanguageType.Chinese; + + internal static CompositeFormat ObjNotExistCompositeFormat => SugarLanguageType == LanguageType.English ? privateEObjNotExistCompositeFormat : privateCObjNotExistCompositeFormat; + private static readonly CompositeFormat privateEObjNotExistCompositeFormat = CompositeFormat.Parse("{0} does not exist."); + private static readonly CompositeFormat privateCObjNotExistCompositeFormat = CompositeFormat.Parse("{0}不存在。"); + + internal static CompositeFormat EntityMappingErrorCompositeFormat => SugarLanguageType == LanguageType.English ? privateEEntityMappingErrorCompositeFormat : privateCEntityMappingErrorCompositeFormat; + private static readonly CompositeFormat privateEEntityMappingErrorCompositeFormat = CompositeFormat.Parse("Entity mapping error.{0}"); + private static readonly CompositeFormat privateCEntityMappingErrorCompositeFormat = CompositeFormat.Parse("Select 实体与表映射出错,可以注释实体类中的字段排查具体哪一个字段。【注意:如果用CodeFirt先配置禁止删列或更新】 {0}"); + + internal static CompositeFormat NotSupportedDictionaryCompositeFormat => SugarLanguageType == LanguageType.English ? privateENotSupportedDictionaryCompositeFormat : privateCNotSupportedDictionaryCompositeFormat; + private static readonly CompositeFormat privateENotSupportedDictionaryCompositeFormat = CompositeFormat.Parse("This type of Dictionary is not supported for the time being. You can try Dictionary, or contact the author!"); + private static readonly CompositeFormat privateCNotSupportedDictionaryCompositeFormat = CompositeFormat.Parse("暂时不支持该类型的Dictionary 你可以试试 Dictionary或者联系作者!"); + + internal static CompositeFormat NotSupportedArrayCompositeFormat => SugarLanguageType == LanguageType.English ? privateENotSupportedArrayCompositeFormat : privateCNotSupportedArrayCompositeFormat; + private static readonly CompositeFormat privateENotSupportedArrayCompositeFormat = CompositeFormat.Parse("This type of Array is not supported for the time being. You can try object[] or contact the author!"); + private static readonly CompositeFormat privateCNotSupportedArrayCompositeFormat = CompositeFormat.Parse("暂时不支持该类型的Array 你可以试试 object[] 或者联系作者!"); + + + + internal static string GetThrowMessage(string enMessage, string cnMessage, params string[] args) + { + if (SugarLanguageType == LanguageType.Default) + { + List formatArgs = new List() { enMessage, cnMessage }; + formatArgs.AddRange(args); + return string.Format(@"中文提示 : {1} +English Message : {0}", formatArgs.ToArray()); + } + else if (SugarLanguageType == LanguageType.English) + { + return enMessage; + } + else + { + return cnMessage; + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/ExpressionBuilderHelper.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/ExpressionBuilderHelper.cs new file mode 100644 index 000000000..5f85e96e8 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/ExpressionBuilderHelper.cs @@ -0,0 +1,205 @@ +using System.Linq.Expressions; +using System.Reflection; +using System.Reflection.Emit; + +namespace SqlSugar +{ + public static class ExpressionBuilderHelper + { + public static object CallFunc(Type type, object[] param, object methodData, string methodName) + { + MethodInfo mi = methodData.GetType().GetMethod(methodName).MakeGenericMethod(new Type[] { type }); + var ret = mi.Invoke(methodData, param); + return ret; + } + public static T CallFunc(object param, object methodData, string methodName) + { + Type type = param.GetType(); + MethodInfo mi = methodData.GetType().GetMethod(methodName).MakeGenericMethod(new Type[] { type }); + var ret = mi.Invoke(methodData, new object[] { param }); + return (T)ret; + } + + public static T CallStaticFunc(object param, Type methodType, string methodName) + { + Type type = param.GetType(); + MethodInfo mi = methodType.GetMethod(methodName).MakeGenericMethod(new Type[] { type }); + var ret = mi.Invoke(null, new object[] { param }); + return (T)ret; + } + /// + /// Create Expression + /// + public static Expression CreateExpression(Expression left, Expression value, ExpressionType type) + { + + if (type == ExpressionType.Equal) + { + return Expression.Equal(left, Expression.Convert(value, left.Type)); + } + else if (type == ExpressionType.NotEqual) + { + return Expression.NotEqual(left, Expression.Convert(value, left.Type)); + } + else + { + //Not implemented, later used in writing + return Expression.Equal(left, Expression.Convert(value, left.Type)); + } + } + public static Expression CreateExpressionLike(Type entityType, string propertyName, List list) + { + var parameter = Expression.Parameter(entityType, "p"); + MemberExpression memberProperty = Expression.PropertyOrField(parameter, propertyName); + MethodInfo method = typeof(List<>).MakeGenericType(typeof(ColumnType)).GetMethod("Contains"); + ConstantExpression constantCollection = Expression.Constant(list); + + MethodCallExpression methodCall = Expression.Call(constantCollection, method, memberProperty); + + var expression = Expression.Lambda(methodCall, parameter); + return expression; + } + public static Expression> CreateNewFields(EntityInfo entity, List propertyNames) + { + Type sourceType = typeof(T); + Dictionary sourceProperties = entity.Columns.Where(it => propertyNames.Contains(it.PropertyName)).ToDictionary(it => it.PropertyName, it => it.PropertyInfo); + + + if (StaticConfig.EnableAot) + { + Type dynamicType = typeof(T); + + ParameterExpression sourceItem = Expression.Parameter(sourceType, "t"); + IEnumerable bindings = dynamicType.GetProperties().Where(it => sourceProperties.Any(s => s.Key == it.Name)).Select(p => Expression.Bind(p, Expression.Property(sourceItem, sourceProperties[p.Name]))).OfType(); + + return Expression.Lambda>(Expression.MemberInit( + Expression.New(dynamicType.GetConstructor(Type.EmptyTypes)), bindings), sourceItem); + } + else + { + + + Type dynamicType = LinqRuntimeTypeBuilder.GetDynamicType(sourceProperties.Values); + + ParameterExpression sourceItem = Expression.Parameter(sourceType, "t"); + IEnumerable bindings = dynamicType.GetRuntimeProperties().Select(p => Expression.Bind(p, Expression.Property(sourceItem, sourceProperties[p.Name]))).OfType(); + + return Expression.Lambda>(Expression.MemberInit( + Expression.New(dynamicType.GetConstructor(Type.EmptyTypes)), bindings), sourceItem); + } + } + public static Expression CreateExpressionSelectField(Type classType, string propertyName, Type propertyType, string shortName = "it") + { + ParameterExpression parameter = Expression.Parameter(classType, shortName); + + // 创建属性表达式 + PropertyInfo propertyInfo = classType.GetProperty(propertyName); + MemberExpression property = Expression.Property(parameter, propertyInfo); + + // 创建Lambda表达式 + Type funcType = typeof(Func<,>).MakeGenericType(classType, propertyType); + LambdaExpression lambda = Expression.Lambda(funcType, property, parameter); + return lambda; + } + public static Expression CreateExpressionSelectFieldObject(Type classType, string propertyName) + { + ParameterExpression parameter = Expression.Parameter(classType, "it"); + + + PropertyInfo propertyInfo = classType.GetProperty(propertyName); + MemberExpression property = Expression.Property(parameter, propertyInfo); + + UnaryExpression convert = Expression.Convert(property, typeof(object)); + var funcType = typeof(Func<,>).MakeGenericType(classType, typeof(object)); + LambdaExpression lambda = Expression.Lambda(funcType, convert, parameter); + return lambda; + } + } + internal static class LinqRuntimeTypeBuilder + { + private static readonly AssemblyName AssemblyName = new AssemblyName() { Name = "LinqRuntimeTypes4iTheoChan" }; + private static readonly ModuleBuilder ModuleBuilder; + private static readonly Dictionary BuiltTypes = new Dictionary(); + + static LinqRuntimeTypeBuilder() + { + ModuleBuilder = AssemblyBuilder.DefineDynamicAssembly(AssemblyName, AssemblyBuilderAccess.Run).DefineDynamicModule(AssemblyName.Name); + } + + private static string GetTypeKey(Dictionary properties) + { + //TODO: optimize the type caching -- if fields are simply reordered, that doesn't mean that they're actually different types, so this needs to be smarter + string key = string.Empty; + foreach (var prop in properties) + key += prop.Key + ";" + prop.Value.Name + ";"; + + return key; + } + + private const MethodAttributes RuntimeGetSetAttrs = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig; + + public static Type BuildDynamicType(Dictionary properties) + { + if (null == properties) + throw new ArgumentNullException(nameof(properties)); + if (0 == properties.Count) + throw new ArgumentOutOfRangeException(nameof(properties), "fields must have at least 1 field definition"); + + try + { + // Acquires an exclusive lock on the specified object. + Monitor.Enter(BuiltTypes); + string className = GetTypeKey(properties); + + if (BuiltTypes.TryGetValue(className, out Type? value)) + return value; + + TypeBuilder typeBdr = ModuleBuilder.DefineType(className, TypeAttributes.Public | TypeAttributes.Class); + + foreach (var prop in properties) + { + var propertyBdr = typeBdr.DefineProperty(name: prop.Key, attributes: PropertyAttributes.None, returnType: prop.Value, parameterTypes: null); + var fieldBdr = typeBdr.DefineField("itheofield_" + prop.Key, prop.Value, FieldAttributes.Private); + + MethodBuilder getMethodBdr = typeBdr.DefineMethod("get_" + prop.Key, RuntimeGetSetAttrs, prop.Value, Type.EmptyTypes); + ILGenerator getIL = getMethodBdr.GetILGenerator(); + getIL.Emit(OpCodes.Ldarg_0); + getIL.Emit(OpCodes.Ldfld, fieldBdr); + getIL.Emit(OpCodes.Ret); + + MethodBuilder setMethodBdr = typeBdr.DefineMethod("set_" + prop.Key, RuntimeGetSetAttrs, null, new Type[] { prop.Value }); + ILGenerator setIL = setMethodBdr.GetILGenerator(); + setIL.Emit(OpCodes.Ldarg_0); + setIL.Emit(OpCodes.Ldarg_1); + setIL.Emit(OpCodes.Stfld, fieldBdr); + setIL.Emit(OpCodes.Ret); + + propertyBdr.SetGetMethod(getMethodBdr); + propertyBdr.SetSetMethod(setMethodBdr); + } + + BuiltTypes[className] = typeBdr.CreateType(); + + return BuiltTypes[className]; + } + catch + { + throw; + } + finally + { + Monitor.Exit(BuiltTypes); + } + } + + private static string GetTypeKey(IEnumerable properties) + { + return GetTypeKey(properties.ToDictionary(f => f.Name, f => f.PropertyType)); + } + + public static Type GetDynamicType(IEnumerable properties) + { + return BuildDynamicType(properties.ToDictionary(f => f.Name, f => f.PropertyType)); + } + } +} \ No newline at end of file diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/FastCopy.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/FastCopy.cs new file mode 100644 index 000000000..1fd00a6c3 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/FastCopy.cs @@ -0,0 +1,128 @@ +using System.Collections.Concurrent; +using System.Linq.Expressions; +using System.Reflection; + +using static System.Linq.Expressions.Expression; + +namespace SqlSugar +{ + internal static class FastCopy + { + static ConcurrentDictionary copiers = new ConcurrentDictionary(); + + /// + /// 复制两个对象同名属性值 + /// + /// + /// + /// 源对象 + /// 目标对象 + /// 源对象属性值为null时,是否将值复制给目标对象 + public static void Copy(S source, T target, bool copyNull = true) + { + string name = string.Format("{0}_{1}_{2}", typeof(S), typeof(T), copyNull); + + object targetCopier; + if (!copiers.TryGetValue(name, out targetCopier)) + { + Action copier = CreateCopier(copyNull); + copiers.TryAdd(name, copier); + targetCopier = copier; + } + + Action action = (Action)targetCopier; + action(source, target); + } + + /// + /// 为指定的两种类型编译生成属性复制委托 + /// + /// + /// + /// 源对象属性值为null时,是否将值复制给目标对象 + /// + private static Action CreateCopier(bool copyNull) + { + ParameterExpression source = Parameter(typeof(S)); + ParameterExpression target = Parameter(typeof(T)); + var sourceProps = typeof(S).GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(p => p.CanRead).ToList(); + var targetProps = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(p => p.CanWrite).ToList(); + + // 查找可进行赋值的属性 + var copyProps = targetProps.Where(tProp => sourceProps.Where(sProp => sProp.Name == tProp.Name// 名称一致 且 + && ( + sProp.PropertyType == tProp.PropertyType// 属性类型一致 或 + || sProp.PropertyType.IsAssignableFrom(tProp.PropertyType) // 源属性类型 为 目标属性类型 的 子类;eg:object target = string source; 或 + || (tProp.PropertyType.IsValueType && sProp.PropertyType.IsValueType && // 属性为值类型且基础类型一致,但目标属性为可空类型 eg:int? num = int num; + ((tProp.PropertyType.GenericTypeArguments.Length > 0 ? tProp.PropertyType.GenericTypeArguments[0] : tProp.PropertyType) == sProp.PropertyType)) + )).Any()); + + List expressionList = new List(); + foreach (var prop in copyProps) + { + if (prop.PropertyType.IsValueType)// 属性为值类型 + { + PropertyInfo sProp = typeof(S).GetProperty(prop.Name); + PropertyInfo tProp = typeof(T).GetProperty(prop.Name); + if (sProp.PropertyType == tProp.PropertyType)// 属性类型一致 eg:int num = int num; 或 int? num = int? num; + { + var assign = Assign(Property(target, prop.Name), Property(source, prop.Name)); + expressionList.Add(assign); + } + else if (sProp.PropertyType.GenericTypeArguments.Length <= 0 && tProp.PropertyType.GenericTypeArguments.Length > 0)// 属性类型不一致且目标属性类型为可空类型 eg:int? num = int num; + { + var convert = Convert(Expression.Property(source, prop.Name), tProp.PropertyType); + var cvAssign = Assign(Expression.Property(target, prop.Name), convert); + expressionList.Add(cvAssign); + } + } + else// 属性为引用类型 + { + var assign = Assign(Property(target, prop.Name), Property(source, prop.Name));// 编译生成属性赋值语句 target.{PropertyName} = source.{PropertyName}; + var sourcePropIsNull = Equal(Constant(null, prop.PropertyType), Property(source, prop.Name));// 判断源属性值是否为Null;编译生成 source.{PropertyName} == null + var setNull = IsTrue(Constant(copyNull));// 判断是否复制Null值 编译生成 copyNull == True + var setNullTest = IfThen(setNull, assign); + var condition = IfThenElse(sourcePropIsNull, setNullTest, assign); + + /* + * 编译生成 + * if(source.{PropertyName} == null) + * { + * if(setNull) + * { + * target.{PropertyName} = source.{PropertyName}; + * } + * } + * else + * { + * target.{PropertyName} = source.{PropertyName}; + * } + */ + expressionList.Add(condition); + } + } + var block = Block(expressionList.ToArray()); + Expression> lambda = Lambda>(block, source, target); + return lambda.Compile(); + } + + internal static List GetDiff(T item, T trackingData) where T : class, new() + { + List result = new List(); + foreach (var t in typeof(T).GetProperties()) + { + var leftvalue = t.GetValue(item); + var rightvalue = t.GetValue(trackingData); + if ((leftvalue == null && rightvalue != null)) + { + result.Add(t.Name); + } + else if (leftvalue?.Equals(rightvalue) == false) + { + result.Add(t.Name); + } + } + return result; + } + } +} \ No newline at end of file diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/FileHelper.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/FileHelper.cs new file mode 100644 index 000000000..6855d872b --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/FileHelper.cs @@ -0,0 +1,69 @@ +using System.Text; + +namespace SqlSugar +{ + internal static class FileHelper + { + public static void CreateFile(string filePath, string text, Encoding encoding) + { + try + { + if (IsExistFile(filePath)) + { + DeleteFile(filePath); + } + if (!IsExistFile(filePath)) + { + string directoryPath = GetDirectoryFromFilePath(filePath); + CreateDirectory(directoryPath); + + //Create File + FileInfo file = new FileInfo(filePath); + using (FileStream stream = file.Create()) + { + using (StreamWriter writer = new StreamWriter(stream, encoding)) + { + writer.Write(text); + writer.Flush(); + } + } + } + } + catch (Exception) + { + throw; + } + } + public static bool IsExistDirectory(string directoryPath) + { + return Directory.Exists(directoryPath); + } + public static void CreateDirectory(string directoryPath) + { + if (!IsExistDirectory(directoryPath)) + { + if (!string.IsNullOrEmpty(directoryPath)) + { + Directory.CreateDirectory(directoryPath); + } + } + } + public static void DeleteFile(string filePath) + { + if (IsExistFile(filePath)) + { + File.Delete(filePath); + } + } + public static string GetDirectoryFromFilePath(string filePath) + { + FileInfo file = new FileInfo(filePath); + DirectoryInfo directory = file.Directory; + return directory.FullName; + } + public static bool IsExistFile(string filePath) + { + return File.Exists(filePath); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/PropertyCallAdapterProvider.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/PropertyCallAdapterProvider.cs new file mode 100644 index 000000000..b40e78603 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/PropertyCallAdapterProvider.cs @@ -0,0 +1,76 @@ +using System.Reflection; +namespace SqlSugar +{ + public interface IPropertyCallAdapter + { + object InvokeGet(TThis @this); + //add void InvokeSet(TThis @this, object value) if necessary + } + public class PropertyCallAdapter : IPropertyCallAdapter + { + private readonly Func _getterInvocation; + + public PropertyCallAdapter(Func getterInvocation) + { + _getterInvocation = getterInvocation; + } + + public object InvokeGet(TThis @this) + { + return _getterInvocation.Invoke(@this); + } + } + public static class PropertyCallAdapterProvider + { + private static readonly System.Collections.Concurrent.ConcurrentDictionary> _instances = + new System.Collections.Concurrent.ConcurrentDictionary>(); + + public static IPropertyCallAdapter GetInstance(string forPropertyName) + { + IPropertyCallAdapter instance; + if (!_instances.TryGetValue(forPropertyName, out instance)) + { + PropertyInfo property = null; + var propertys = typeof(TThis).GetProperties( + + BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) + .Where(it => it.Name == forPropertyName); + if (propertys.Count() == 1) + { + property = propertys.First(); + } + else + { + property = propertys.First(); + } + + MethodInfo getMethod; + Delegate getterInvocation = null; + if (property != null && (getMethod = property.GetGetMethod(true)) != null) + { + var openGetterType = typeof(Func<,>); + var concreteGetterType = openGetterType + .MakeGenericType(typeof(TThis), property.PropertyType); + + getterInvocation = + Delegate.CreateDelegate(concreteGetterType, null, getMethod); + } + else + { + //throw exception or create a default getterInvocation returning null + } + + var openAdapterType = typeof(PropertyCallAdapter<,>); + var concreteAdapterType = openAdapterType + .MakeGenericType(typeof(TThis), property.PropertyType); + instance = Activator + .CreateInstance(concreteAdapterType, getterInvocation) + as IPropertyCallAdapter; + + _instances.GetOrAdd(forPropertyName, instance); + } + + return instance; + } + } +} \ No newline at end of file diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/ReflectionExtensions.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/ReflectionExtensions.cs new file mode 100644 index 000000000..c55857dc2 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/ReflectionExtensions.cs @@ -0,0 +1,77 @@ +using System.Reflection; + +namespace SqlSugar +{ + + public static class ReflectionExtensions + { + public static Type GetTypeInfo(this Type typeInfo) + { + return typeInfo; + } + + public static Type[] GetGenericArguments(this Type type) + { + var reval = type.GetTypeInfo().GetGenericArguments(); + return reval; + } + public static bool IsGenericType(this Type type) + { + var reval = type.GetTypeInfo().IsGenericType; + return reval; + } + public static PropertyInfo[] GetProperties(this Type type) + { + var reval = type.GetTypeInfo().GetProperties(); + return reval; + } + public static PropertyInfo GetProperty(this Type type, string name) + { + var reval = type.GetTypeInfo().GetProperty(name); + return reval; + } + + public static FieldInfo GetField(this Type type, string name) + { + var reval = type.GetTypeInfo().GetField(name); + return reval; + } + + public static bool IsEnum(this Type type) + { + var reval = type.GetTypeInfo().IsEnum; + return reval; + } + + public static MethodInfo GetMethod(this Type type, string name) + { + var reval = type.GetTypeInfo().GetMethod(name); + return reval; + } + public static MethodInfo GetMethod(this Type type, string name, Type[] types) + { + var reval = type.GetTypeInfo().GetMethod(name, types); + return reval; + } + public static ConstructorInfo GetConstructor(this Type type, Type[] types) + { + var reval = type.GetTypeInfo().GetConstructor(types); + return reval; + } + + public static bool IsValueType(this Type type) + { + return type.GetTypeInfo().IsValueType; + } + + public static bool IsEntity(this Type type) + { + return type.GetTypeInfo().IsClass; + } + + public static Type ReflectedType(this MethodInfo method) + { + return method.ReflectedType; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/SugarAsyncLock.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/SugarAsyncLock.cs new file mode 100644 index 000000000..7e743282b --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/SugarAsyncLock.cs @@ -0,0 +1,25 @@ +namespace SqlSugar +{ + public class SugarAsyncLock : IDisposable + { + static readonly SemaphoreSlim SemaphoreSlim = new SemaphoreSlim(1); + + + public SugarAsyncLock(SqlSugarProvider db) + { + + } + + public async Task AsyncLock(int timeOutSeconds) + { + TimeSpan timeout = TimeSpan.FromSeconds(timeOutSeconds); + await SemaphoreSlim.WaitAsync(timeout).ConfigureAwait(false); + return this; + } + + public void Dispose() + { + SemaphoreSlim.Release(); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/SugarRetry.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/SugarRetry.cs new file mode 100644 index 000000000..4d54b8edd --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/SugarRetry.cs @@ -0,0 +1,159 @@ +namespace SqlSugar +{ + public static class SugarRetry + { + + public static void Execute(Action action, TimeSpan retryInterval, int retryCount = 3) + { + Execute(() => + { + action(); + return null; + }, retryInterval, retryCount); + } + + + public static void Execute(Action action, T1 arg1, TimeSpan retryInterval, int retryCount = 3) + { + Execute((x1) => + { + action(arg1); + return null; + }, arg1, retryInterval, retryCount); + } + + + public static void Execute(Action action, T1 arg1, T2 arg2, TimeSpan retryInterval, int retryCount = 3) + { + Execute((x1, x2) => + { + action(arg1, arg2); + return null; + }, arg1, arg2, retryInterval, retryCount); + } + + + public static void Execute(Action action, T1 arg1, T2 arg2, T3 arg3, TimeSpan retryInterval, int retryCount = 3) + { + Execute((x1, x2, x3) => + { + action(arg1, arg2, arg3); + return null; + }, arg1, arg2, arg3, retryInterval, retryCount); + } + + + public static void Execute(Action action, T1 arg1, T2 arg2, T3 arg3, T4 arg4, TimeSpan retryInterval, int retryCount = 3) + { + Execute((x1, x2, x3, x4) => + { + action(arg1, arg2, arg3, arg4); + return null; + }, arg1, arg2, arg3, arg4, retryInterval, retryCount); + } + + + public static T Execute(Func func, TimeSpan retryInterval, int retryCount = 3) + { + var exceptions = new List(); + + for (int retry = 0; retry < retryCount; retry++) + { + try + { + return func(); + } + catch (Exception ex) + { + exceptions.Add(ex); + Thread.Sleep(retryInterval); + } + } + + throw new AggregateException(exceptions); + } + + + public static T Execute(Func func, T1 arg1, TimeSpan retryInterval, int retryCount = 3) + { + var exceptions = new List(); + + for (int retry = 0; retry < retryCount; retry++) + { + try + { + return func(arg1); + } + catch (Exception ex) + { + exceptions.Add(ex); + Thread.Sleep(retryInterval); + } + } + + throw new AggregateException(exceptions); + } + + public static T Execute(Func func, T1 arg1, T2 arg2, TimeSpan retryInterval, int retryCount = 3) + { + var exceptions = new List(); + + for (int retry = 0; retry < retryCount; retry++) + { + try + { + return func(arg1, arg2); + } + catch (Exception ex) + { + exceptions.Add(ex); + Thread.Sleep(retryInterval); + } + } + + throw new AggregateException(exceptions); + } + + + public static T Execute(Func func, T1 arg1, T2 arg2, T3 arg3, TimeSpan retryInterval, int retryCount = 3) + { + var exceptions = new List(); + + for (int retry = 0; retry < retryCount; retry++) + { + try + { + return func(arg1, arg2, arg3); + } + catch (Exception ex) + { + exceptions.Add(ex); + Thread.Sleep(retryInterval); + } + } + + throw new AggregateException(exceptions); + } + + + public static T Execute(Func func, T1 arg1, T2 arg2, T3 arg3, T4 arg4, TimeSpan retryInterval, int retryCount = 3) + { + var exceptions = new List(); + + for (int retry = 0; retry < retryCount; retry++) + { + try + { + return func(arg1, arg2, arg3, arg4); + } + catch (Exception ex) + { + exceptions.Add(ex); + Thread.Sleep(retryInterval); + } + } + + throw new AggregateException(exceptions); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/UtilConstants.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/UtilConstants.cs new file mode 100644 index 000000000..6abdfedee --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/UtilConstants.cs @@ -0,0 +1,80 @@ +using System.Dynamic; +using System.Linq.Expressions; +namespace SqlSugar +{ + internal static class UtilConstants + { + public const string Dot = "."; + public const char DotChar = '.'; + internal const string Space = " "; + internal const char SpaceChar = ' '; + internal const string AssemblyName = "SqlSugar"; + internal static string ReplaceKey = "{" + Guid.NewGuid() + "}"; + internal const string ReplaceCommaKey = "{112A689B-17A1-4A06-9D27-A39EAB8BC3D5}"; + internal const string GroupReplaceKey = "{GroupReplaceKey_l33asdysaas1231s}"; + + internal static Type UShortType = typeof(ushort); + internal static Type ULongType = typeof(ulong); + internal static Type UIntType = typeof(uint); + internal static Type IntType = typeof(int); + internal static Type LongType = typeof(long); + internal static Type GuidType = typeof(Guid); + internal static Type BoolType = typeof(bool); + internal static Type BoolTypeNull = typeof(bool?); + internal static Type ByteType = typeof(Byte); + internal static Type SByteType = typeof(sbyte); + internal static Type ObjType = typeof(object); + internal static Type DobType = typeof(double); + internal static Type FloatType = typeof(float); + internal static Type ShortType = typeof(short); + internal static Type DecType = typeof(decimal); + internal static Type StringType = typeof(string); + internal static Type DateType = typeof(DateTime); + internal static Type DateTimeOffsetType = typeof(DateTimeOffset); + internal static Type TimeSpanType = typeof(TimeSpan); + internal static Type ByteArrayType = typeof(byte[]); + internal static Type ModelType = typeof(ModelContext); + internal static Type DynamicType = typeof(ExpandoObject); + internal static Type Dicii = typeof(KeyValuePair); + internal static Type DicIS = typeof(KeyValuePair); + internal static Type DicSi = typeof(KeyValuePair); + internal static Type DicSS = typeof(KeyValuePair); + internal static Type DicOO = typeof(KeyValuePair); + internal static Type DicSo = typeof(KeyValuePair); + internal static Type DicArraySS = typeof(Dictionary); + internal static Type DicArraySO = typeof(Dictionary); + + public static Type SqlConvertType = typeof(SqlSugar.DbConvert.NoParameterCommonPropertyConvert); + + public static Type SugarType = typeof(SqlSugarProvider); + + + internal static Type[] NumericalTypes = new Type[] + { + typeof(int), + typeof(uint), + typeof(byte), + typeof(sbyte), + typeof(long), + typeof(ulong), + typeof(short), + typeof(ushort), + }; + //internal static CultureInfo EnCultureInfo = new CultureInfo("en"); + + internal static string[] DateTypeStringList = new string[] + { + "Year", + "Month", + "Day", + "Hour", + "Second" , + "Minute", + "Millisecond", + "Date" + }; + + public static ConstantExpression ExpTrue = Expression.Constant(true); + public static ConstantExpression ExpFalse = Expression.Constant(false); + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/UtilConvert.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/UtilConvert.cs new file mode 100644 index 000000000..168f2ab41 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/UtilConvert.cs @@ -0,0 +1,192 @@ +using System.Linq.Expressions; +namespace SqlSugar +{ + internal static class UtilConvert + { + public static int ObjToInt(this object thisValue) + { + int reval = 0; + if (thisValue == null) return 0; + if (thisValue is Enum) + { + return Convert.ToInt32(thisValue); + } + if (thisValue != null && thisValue != DBNull.Value && int.TryParse(thisValue.ToString(), out reval)) + { + return reval; + } + return reval; + } + + public static long ObjToLong(this object thisValue) + { + long reval = 0; + if (thisValue == null) return 0; + if (thisValue is Enum) + { + return Convert.ToInt64(thisValue); + } + if (thisValue != null && thisValue != DBNull.Value && long.TryParse(thisValue.ToString(), out reval)) + { + return reval; + } + return reval; + } + + public static int ObjToInt(this object thisValue, int errorValue) + { + int reval = 0; + if (thisValue is Enum) + { + return (int)thisValue; + } + if (thisValue != null && thisValue != DBNull.Value && int.TryParse(thisValue.ToString(), out reval)) + { + return reval; + } + return errorValue; + } + + public static double ObjToMoney(this object thisValue) + { + double reval = 0; + if (thisValue != null && thisValue != DBNull.Value && double.TryParse(thisValue.ToString(), out reval)) + { + return reval; + } + return 0; + } + + public static double ObjToMoney(this object thisValue, double errorValue) + { + double reval = 0; + if (thisValue != null && thisValue != DBNull.Value && double.TryParse(thisValue.ToString(), out reval)) + { + return reval; + } + return errorValue; + } + public static bool EqualCase(this string thisValue, string equalValue) + { + if (thisValue != null && equalValue != null) + { + return thisValue.Equals(equalValue, StringComparison.CurrentCultureIgnoreCase); + } + else + { + return thisValue == equalValue; + } + } + public static string ObjToString(this object thisValue, Func formatTime) + { + if (formatTime != null && thisValue is DateTime) + { + var dt = Convert.ToDateTime(thisValue); + return formatTime(dt); + } + else + { + return thisValue + string.Empty; + } + } + public static string ObjToString(this object thisValue) + { + if (thisValue != null) return thisValue.ToString().Trim(); + return ""; + } + public static string ObjToStringNoTrim(this object thisValue) + { + if (thisValue != null) return thisValue.ToString(); + return ""; + } + public static string ObjToStringNew(this object thisValue) + { + if (thisValue != null && thisValue is byte[]) + { + return string.Join("|", thisValue as byte[]); + } + if (thisValue != null && thisValue?.GetType()?.Name == "DateOnly") + { + return Convert.ToDateTime(thisValue.ToString()).ToString("yyyy-MM-dd"); + } + if (thisValue != null) return thisValue.ToString().Trim(); + return ""; + } + + public static string ObjToString(this object thisValue, string errorValue) + { + if (thisValue != null) return thisValue.ToString().Trim(); + return errorValue; + } + + public static Decimal ObjToDecimal(this object thisValue) + { + Decimal reval = 0; + if (thisValue != null && thisValue != DBNull.Value && decimal.TryParse(thisValue.ToString(), out reval)) + { + return reval; + } + return 0; + } + + public static Decimal ObjToDecimal(this object thisValue, decimal errorValue) + { + Decimal reval = 0; + if (thisValue != null && thisValue != DBNull.Value && decimal.TryParse(thisValue.ToString(), out reval)) + { + return reval; + } + return errorValue; + } + + public static DateTime ObjToDate(this object thisValue) + { + if (thisValue is DateTime) + { + return (DateTime)thisValue; + } + DateTime reval = DateTime.MinValue; + if (thisValue != null && thisValue != DBNull.Value && DateTime.TryParse(thisValue.ToString(), out reval)) + { + reval = Convert.ToDateTime(thisValue); + } + return reval; + } + + public static DateTime ObjToDate(this object thisValue, DateTime errorValue) + { + if (thisValue is DateTime) + { + return (DateTime)thisValue; + } + DateTime reval = DateTime.MinValue; + if (thisValue != null && thisValue != DBNull.Value && DateTime.TryParse(thisValue.ToString(), out reval)) + { + return reval; + } + return errorValue; + } + + public static bool ObjToBool(this object thisValue) + { + bool reval = false; + if (thisValue != null && thisValue != DBNull.Value && bool.TryParse(thisValue.ToString(), out reval)) + { + return reval; + } + return reval; + } + + internal static MemberExpression ToMemberExpression(Expression parentIdExpression) + { + if (parentIdExpression is UnaryExpression) + { + return (parentIdExpression as UnaryExpression).Operand as MemberExpression; + } + else + { + return parentIdExpression as MemberExpression; + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/UtilExceptions.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/UtilExceptions.cs new file mode 100644 index 000000000..467ec11d4 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/UtilExceptions.cs @@ -0,0 +1,83 @@ +using System.Reflection; +namespace SqlSugar +{ + public class SqlSugarException : Exception + { + public string Sql { get; set; } + public object Parametres { get; set; } + public new Exception InnerException; + public new string StackTrace; + public new MethodBase TargetSite; + public new string Source; + + public SqlSugarException(string message) + : base(message) { } + + public SqlSugarException(SqlSugarProvider context, string message, string sql) + : base(message) + { + this.Sql = sql; + } + + public SqlSugarException(SqlSugarProvider context, string message, string sql, object pars) + : base(message) + { + this.Sql = sql; + this.Parametres = pars; + } + + public SqlSugarException(SqlSugarProvider context, Exception ex, string sql, object pars) + : base(ex.Message) + { + this.Sql = sql; + this.Parametres = pars; + this.InnerException = ex.InnerException; + this.StackTrace = ex.StackTrace; + this.TargetSite = ex.TargetSite; + this.Source = ex.Source; + } + + public SqlSugarException(SqlSugarProvider context, string message, object pars) + : base(message) + { + this.Parametres = pars; + } + + public SqlSugarException() : base() + { + } + + public SqlSugarException(string? message, Exception? innerException) : base(message, innerException) + { + } + } + public class VersionExceptions : SqlSugarException + { + public VersionExceptions(string message) + : base(message) { } + + public VersionExceptions(SqlSugarProvider context, string message, string sql) : base(context, message, sql) + { + } + + public VersionExceptions(SqlSugarProvider context, string message, string sql, object pars) : base(context, message, sql, pars) + { + } + + public VersionExceptions(SqlSugarProvider context, Exception ex, string sql, object pars) : base(context, ex, sql, pars) + { + } + + public VersionExceptions(SqlSugarProvider context, string message, object pars) : base(context, message, pars) + { + } + + public VersionExceptions() : base() + { + } + + public VersionExceptions(string? message, Exception? innerException) : base(message, innerException) + { + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/UtilExtensions.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/UtilExtensions.cs new file mode 100644 index 000000000..4e76db6c9 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/UtilExtensions.cs @@ -0,0 +1,119 @@ +namespace SqlSugar.Extensions +{ + /// + ///Common Extensions for external users + /// + public static class UtilExtensions + { + public static int ObjToInt(this object thisValue) + { + int reval = 0; + if (thisValue == null) return 0; + if (thisValue is Enum) + { + return (int)thisValue; + } + if (thisValue != null && thisValue != DBNull.Value && int.TryParse(thisValue.ToString(), out reval)) + { + return reval; + } + return reval; + } + + public static int ObjToInt(this object thisValue, int errorValue) + { + int reval = 0; + if (thisValue is Enum) + { + return (int)thisValue; + } + if (thisValue != null && thisValue != DBNull.Value && int.TryParse(thisValue.ToString(), out reval)) + { + return reval; + } + return errorValue; + } + + public static double ObjToMoney(this object thisValue) + { + double reval = 0; + if (thisValue != null && thisValue != DBNull.Value && double.TryParse(thisValue.ToString(), out reval)) + { + return reval; + } + return 0; + } + + public static double ObjToMoney(this object thisValue, double errorValue) + { + double reval = 0; + if (thisValue != null && thisValue != DBNull.Value && double.TryParse(thisValue.ToString(), out reval)) + { + return reval; + } + return errorValue; + } + + public static string ObjToString(this object thisValue) + { + if (thisValue != null) return thisValue.ToString().Trim(); + return ""; + } + + public static string ObjToString(this object thisValue, string errorValue) + { + if (thisValue != null) return thisValue.ToString().Trim(); + return errorValue; + } + + public static Decimal ObjToDecimal(this object thisValue) + { + Decimal reval = 0; + if (thisValue != null && thisValue != DBNull.Value && decimal.TryParse(thisValue.ToString(), out reval)) + { + return reval; + } + return 0; + } + + public static Decimal ObjToDecimal(this object thisValue, decimal errorValue) + { + Decimal reval = 0; + if (thisValue != null && thisValue != DBNull.Value && decimal.TryParse(thisValue.ToString(), out reval)) + { + return reval; + } + return errorValue; + } + + public static DateTime ObjToDate(this object thisValue) + { + DateTime reval = DateTime.MinValue; + if (thisValue != null && thisValue != DBNull.Value && DateTime.TryParse(thisValue.ToString(), out reval)) + { + reval = Convert.ToDateTime(thisValue); + } + return reval; + } + + public static DateTime ObjToDate(this object thisValue, DateTime errorValue) + { + DateTime reval = DateTime.MinValue; + if (thisValue != null && thisValue != DBNull.Value && DateTime.TryParse(thisValue.ToString(), out reval)) + { + return reval; + } + return errorValue; + } + + public static bool ObjToBool(this object thisValue) + { + bool reval = false; + if (thisValue != null && thisValue != DBNull.Value && bool.TryParse(thisValue.ToString(), out reval)) + { + return reval; + } + return reval; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/UtilMethods.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/UtilMethods.cs new file mode 100644 index 000000000..67c1dbf77 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/UtilMethods.cs @@ -0,0 +1,1954 @@ +using Dm.util; + +using System.Collections; +using System.ComponentModel; +using System.Data; +using System.Diagnostics; +using System.Globalization; +using System.Linq.Expressions; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Text; +using System.Text.RegularExpressions; + +using ThingsGateway.NewLife; + +namespace SqlSugar +{ + public static class UtilMethods + { + public static DataTable ConvertDateTimeOffsetToDateTime(DataTable table) + { + if (!table.Columns.Cast().Any(it => it.DataType == typeof(DateTimeOffset))) + { + return table; + } + DataTable newTable = table.Clone(); + newTable.TableName = table.TableName; + // 替换所有 DateTimeOffset 列为 DateTime + foreach (DataColumn column in newTable.Columns) + { + if (column.DataType == typeof(DateTimeOffset)) + { + column.DataType = typeof(DateTime); // 会报错,不能直接改 + } + } + + // 需要重新构建新表结构 + DataTable finalTable = new DataTable(); + finalTable.TableName = table.TableName; + foreach (DataColumn column in table.Columns) + { + Type newType = column.DataType == typeof(DateTimeOffset) ? typeof(DateTime) : column.DataType; + finalTable.Columns.Add(column.ColumnName, newType); + } + + // 拷贝并转换数据 + foreach (DataRow row in table.Rows) + { + DataRow newRow = finalTable.NewRow(); + foreach (DataColumn column in table.Columns) + { + var value = row[column]; + if (value is DateTimeOffset dto) + newRow[column.ColumnName] = dto.DateTime; + else + newRow[column.ColumnName] = value; + } + finalTable.Rows.Add(newRow); + } + + return finalTable; + } + public static string EscapeLikeValue(ISqlSugarClient db, string value, char wildcard = '%') + { + var dbType = db.CurrentConnectionConfig.DbType; + if (db.CurrentConnectionConfig?.MoreSettings?.DatabaseModel != null) + { + dbType = db.CurrentConnectionConfig.MoreSettings.DatabaseModel.Value; + } + if (string.IsNullOrEmpty(value)) + return value; + + string wildcardStr = wildcard.ToString(); + + switch (dbType) + { + // 支持标准 SQL LIKE 转义,通常使用中括号 [] 或反斜杠 \ 进行转义 + case DbType.SqlServer: + case DbType.Access: + case DbType.Odbc: + case DbType.TDSQLForPGODBC: + // SQL Server 使用中括号转义 %, _ 等 + var keyLeft = "[[]"; + var keyRight = "[]]"; + var leftGuid = Guid.NewGuid().toString(); + var rightGuid = Guid.NewGuid().toString(); + value = value.Replace("[", leftGuid) + .Replace("]", rightGuid); + + value = value.Replace(leftGuid, keyLeft) + .Replace(rightGuid, keyRight); + value = value + .Replace(wildcardStr, $"[{wildcard}]"); + break; + + // PostgreSQL 风格数据库,使用反斜杠进行 LIKE 转义 + case DbType.PostgreSQL: + case DbType.OpenGauss: + case DbType.TDSQL: + case DbType.GaussDB: + case DbType.GaussDBNative: + // MySQL 和兼容库,使用反斜杠进行转义 + case DbType.MySql: + case DbType.MySqlConnector: + case DbType.Tidb: + case DbType.PolarDB: + case DbType.OceanBase: + case DbType.Oracle: + case DbType.OceanBaseForOracle: + case DbType.HG: + case DbType.Dm: + case DbType.GBase: + case DbType.DB2: + case DbType.HANA: + case DbType.GoldenDB: + case DbType.Sqlite: + case DbType.DuckDB: + case DbType.QuestDB: + case DbType.Doris: + case DbType.Xugu: + case DbType.Vastbase: + default: + value = value + .Replace(wildcardStr, "\\" + wildcard); + break; + } + + return value; + } + + + public static List CopySugarParameters(List pars) + { + if (pars == null) return null; + var newParameters = pars.Select(it => new SugarParameter(it.ParameterName, it.Value) + { + TypeName = it.TypeName, + Value = it.Value, + IsRefCursor = it.IsRefCursor, + IsArray = it.IsArray, + IsJson = it.IsJson, + ParameterName = it.ParameterName, + IsNvarchar2 = it.IsNvarchar2, + IsNClob = it.IsClob, + IsClob = it.IsClob, + UdtTypeName = it.UdtTypeName, + CustomDbType = it.CustomDbType, + DbType = it.DbType, + Direction = it.Direction, + Precision = it.Precision, + Size = it.Size, + Scale = it.Scale, + IsNullable = it.IsNullable, + SourceColumn = it.SourceColumn, + SourceColumnNullMapping = it.SourceColumnNullMapping, + SourceVersion = it.SourceVersion, + TempDate = it.TempDate, + _Size = it._Size + }); + return newParameters.ToList(); + } + public static bool IsTuple(Type tType, List classProperties) + { + if (classProperties == null || classProperties?.Count == 0) + { + return false; + } + return tType.FullName?.StartsWith("System.Tuple`") == true && classProperties.FirstOrDefault()?.Name == "Item1"; + } + + internal static string GetTableByDbLink(SqlSugarProvider context, string tableName, string oldTableName, TenantAttribute attr) + { + QueryBuilder queryBuilder = InstanceFactory.GetQueryBuilderWithContext(context); + var dbLinkName = context.Root.GetConnection(attr.configId).CurrentConnectionConfig.DbLinkName; + if (dbLinkName != null) + { + if (dbLinkName.First() == '@') + { + tableName = queryBuilder.Builder.GetTranslationColumnName(oldTableName) + dbLinkName; + } + else if (dbLinkName.Last() == '_') + { + tableName = dbLinkName + oldTableName; + } + else + { + tableName = dbLinkName + "." + queryBuilder.Builder.GetTranslationColumnName(oldTableName); + } + } + return tableName; + } + public static List> GetColumnInfo(IDataReader reader) + { + var columnInfo = new List>(); + + // 获取列的数量 + int columnCount = reader.FieldCount; + + // 遍历每一列 + for (int i = 0; i < columnCount; i++) + { + // 获取列名 + string columnName = reader.GetName(i); + + // 获取列的数据类型 + Type columnType = reader.GetFieldType(i); + + // 将列名和类型添加到列表中 + columnInfo.Add(Tuple.Create(columnName, columnType)); + } + + return columnInfo; + } + public static object ConvertToObjectList(Type targetType, List sourceList) + { + // 创建 List 类型的实例 + object resultList = Activator.CreateInstance(typeof(List<>).MakeGenericType(targetType)); + // 获取 Add 方法 + var addMethod = resultList.GetType().GetMethod("Add"); + foreach (var obj in sourceList) + { + addMethod.Invoke(resultList, new object[] { obj }); + } + return resultList; + } + public static Dictionary DataRowToDictionary(DataRow row) + { + Dictionary dictionary = new Dictionary(); + + // 遍历所有列并将其添加到字典中 + foreach (DataColumn column in row.Table.Columns) + { + if (column.ColumnName != "Items" && column.DataType.Name.IsCollectionsList() == false) + { + dictionary.Add(column.ColumnName, row[column]); + } + } + + return dictionary; + } + public static IEnumerable BuildTree(ISqlSugarClient db, IEnumerable list, string idName, string pIdName, string childName, object rootValue) + { + var entityInfo = db.EntityMaintenance.GetEntityInfo(); ; + var mainIdProp = entityInfo.Type.GetProperty(idName); + var pIdProp = entityInfo.Type.GetProperty(pIdName); + var childProp = entityInfo.Type.GetProperty(childName); + + Dictionary kvList; + IEnumerable> group; + BuildTreeGroup(list, mainIdProp, pIdProp, out kvList, out group); + + var root = rootValue != null ? group.FirstOrDefault(x => x.Key == rootValue.ObjToString()) : group.FirstOrDefault(x => x.Key == null || string.IsNullOrEmpty(x.Key) || x.Key == "0" || x.Key == Guid.Empty.ToString()); + + if (root != null) + { + foreach (var item in group) + { + if (kvList.TryGetValue(item.Key, out var parent)) + { + childProp.SetValue(parent, item.ToList()); + } + } + } + + return root; + } + + private static void BuildTreeGroup(IEnumerable list, PropertyInfo mainIdProp, PropertyInfo pIdProp, out Dictionary kvList, out IEnumerable> group) + { + kvList = list.ToDictionary(x => mainIdProp.GetValue(x).ObjToString()); + group = list.GroupBy(x => pIdProp.GetValue(x).ObjToString()); + } + + internal static bool? _IsErrorDecimalString { get; set; } + private static readonly char[] separator = new char[] { '9' }; + + internal static bool? IsErrorDecimalString() + { + if (_IsErrorDecimalString == null) + { + decimal dec = Convert.ToDecimal(1.1); + _IsErrorDecimalString = dec.ToString().Contains(','); + } + return _IsErrorDecimalString; + } + internal static bool IsParameterConverter(EntityColumnInfo columnInfo) + { + return columnInfo?.SqlParameterDbType != null && columnInfo.SqlParameterDbType is Type + && typeof(ISugarDataConverter).IsAssignableFrom(columnInfo.SqlParameterDbType as Type); + } + internal static SugarParameter GetParameterConverter(int index, ISqlSugarClient db, object value, Expression oppoSiteExpression, EntityColumnInfo columnInfo) + { + var entity = db.EntityMaintenance.GetEntityInfo(oppoSiteExpression.Type); + var type = columnInfo.SqlParameterDbType as Type; + var ParameterConverter = type.GetMethod("ParameterConverter").MakeGenericMethod(columnInfo.PropertyInfo.PropertyType); + var obj = Activator.CreateInstance(type); + var p = ParameterConverter.Invoke(obj, new object[] { value, 100 + index }) as SugarParameter; + return p; + } + internal static SugarParameter GetParameterConverter(int index, ISqlSugarClient db, object value, EntityInfo entity, EntityColumnInfo columnInfo) + { + var type = columnInfo.SqlParameterDbType as Type; + var ParameterConverter = type.GetMethod("ParameterConverter").MakeGenericMethod(columnInfo.PropertyInfo.PropertyType); + var obj = Activator.CreateInstance(type); + var p = ParameterConverter.Invoke(obj, new object[] { value, 100 + index }) as SugarParameter; + return p; + } + internal static object QueryConverter(int index, ISqlSugarClient db, IDataReader dataReader, EntityInfo entity, EntityColumnInfo columnInfo) + { + var type = columnInfo.SqlParameterDbType as Type; + var ParameterConverter = type.GetMethod("QueryConverter").MakeGenericMethod(columnInfo.PropertyInfo.PropertyType); + var obj = Activator.CreateInstance(type); + var p = ParameterConverter.Invoke(obj, new object[] { dataReader, index }); + return p; + } + internal static bool IsErrorParameterName(ConnectionConfig connectionConfig, DbColumnInfo columnInfo) + { + return connectionConfig.MoreSettings?.IsCorrectErrorSqlParameterName == true && + columnInfo.DbColumnName.IsContainsIn("-", " ", ".", "(", ")", "(", ")"); + } + + public static bool StringCheckFirstAndLast(string withString, string first, string last) + { + return withString.StartsWith(first) && withString.EndsWith(last); + } + public static bool HasInterface(Type targetType, Type interfaceType) + { + if (targetType == null || interfaceType?.IsInterface != true) + { + return false; + } + + return interfaceType.IsAssignableFrom(targetType); + } + public static void ClearPublicProperties(T obj, EntityInfo entity) + { + if (obj == null) + { + throw new ArgumentNullException(nameof(obj)); + } + + Type type = typeof(T); + + foreach (var column in entity.Columns) + { + if (column.PropertyInfo.CanWrite && column.PropertyInfo.GetSetMethod() != null) + { + Type propertyType = column.PropertyInfo.PropertyType; + object defaultValue = propertyType.IsValueType ? Activator.CreateInstance(propertyType) : null; + column.PropertyInfo.SetValue(obj, defaultValue); + } + } + } + + internal static Expression GetIncludeExpression(string navMemberName, EntityInfo entityInfo, out Type properyItemType, out bool isList) + { + var navInfo = entityInfo.Columns.Where(it => it.Navigat != null && it.PropertyName.EqualCase(navMemberName)).FirstOrDefault(); + var properyType = navInfo.PropertyInfo.PropertyType; + properyItemType = properyType; + if (properyType.FullName.IsCollectionsList()) + { + properyItemType = properyType.GetGenericArguments()[0]; + isList = true; + } + else + { + isList = false; + } + return ExpressionBuilderHelper.CreateExpressionSelectField(entityInfo.Type, navInfo.PropertyName, properyType); + } + public static string RemoveEqualOne(string value) + { + value = value.TrimEnd(' ').TrimEnd('1').TrimEnd('='); + return value; + } + /// + /// Available only in Select,Handles logic that cannot be completed by an expression + /// + /// + /// + /// + internal static object GetFormatValue(object addValue, QueryableFormat valueFomatInfo) + { + if (valueFomatInfo.MethodName == "ToString") + { + if (valueFomatInfo.Type == UtilConstants.GuidType) + { + addValue = Guid.Parse(addValue + "").ToString(valueFomatInfo.Format); + } + else if (valueFomatInfo.Type == UtilConstants.ByteType) + { + addValue = Convert.ToByte(addValue + "").ToString(valueFomatInfo.Format); + } + else if (valueFomatInfo.Type == UtilConstants.IntType) + { + addValue = Convert.ToInt32(addValue + "").ToString(valueFomatInfo.Format); + } + else if (valueFomatInfo.Type == UtilConstants.LongType) + { + addValue = Convert.ToInt64(addValue + "").ToString(valueFomatInfo.Format); + } + else if (valueFomatInfo.Type == UtilConstants.UIntType) + { + addValue = Convert.ToUInt32(addValue + "").ToString(valueFomatInfo.Format); + } + else if (valueFomatInfo.Type == UtilConstants.ULongType) + { + addValue = Convert.ToUInt64(addValue + "").ToString(valueFomatInfo.Format); + } + else if (valueFomatInfo.Type == UtilConstants.DecType) + { + addValue = Convert.ToDecimal(addValue + "").ToString(valueFomatInfo.Format); + } + else if (valueFomatInfo.Type == UtilConstants.DobType) + { + addValue = Convert.ToDouble(addValue + "").ToString(valueFomatInfo.Format); + } + else if (valueFomatInfo.TypeString == "Enum") + { + addValue = ChangeType2(addValue, valueFomatInfo.Type)?.ToString(); + } + } + else if (valueFomatInfo.MethodName == "OnlyInSelectConvertToString") + { + + var methodInfo = valueFomatInfo.MethodInfo; + if (methodInfo != null) + { + // 如果方法是静态的,传递null作为第一个参数,否则传递类的实例 + object instance = methodInfo.IsStatic ? null : Activator.CreateInstance(methodInfo.ReflectedType); + + // 创建一个包含参数值的object数组 + object[] parameters = new object[] { addValue }; + + // 调用方法 + addValue = methodInfo.Invoke(instance, parameters); + } + } + return addValue; + } + public static int CountSubstringOccurrences(string mainString, string searchString) + { + string[] substrings = mainString.Split(new string[] { searchString }, StringSplitOptions.None); + return substrings.Length - 1; + } + public static string RemoveBeforeFirstWhere(string query) + { + int whereIndex = query.IndexOf("WHERE", StringComparison.OrdinalIgnoreCase); + if (whereIndex >= 0) + { + return query.Substring(whereIndex + "WHERE".Length); + } + else + { + return query; + } + } + public static List ConvertToListOfObjects(object inValues) + { + // 创建一个新的List并逐个将元素转换并添加到其中 + List resultList = new List(); + foreach (var item in (IEnumerable)inValues) + { + resultList.Add(item); + } + + return resultList; + } + public static bool IsValueTypeArray(object memberValue) + { + return memberValue is List || + memberValue is string[] || + memberValue is List || + memberValue is int[] || + memberValue is List || + memberValue is Guid[] || + memberValue is List || + memberValue is long[] || + memberValue is List || + memberValue is int?[] || + memberValue is List || + memberValue is Guid?[] || + memberValue is List || + memberValue is long?[] || + memberValue is List || + memberValue is float[] || + memberValue is List || + memberValue is double[] || + memberValue is List || + memberValue is decimal[] || + memberValue is List || + memberValue is DateTime[] || + memberValue is List || + memberValue is TimeSpan[] || + memberValue is List || + memberValue is bool[] || + memberValue is List || + memberValue is byte[] || + memberValue is List || + memberValue is char[] || + memberValue is List || + memberValue is short[] || + memberValue is List || + memberValue is ushort[] || + memberValue is List || + memberValue is uint[] || + memberValue is List || + memberValue is ulong[] || + memberValue is List || + memberValue is sbyte[] || + memberValue is List || + memberValue is object[] || + memberValue is List || + memberValue is int?[] || + memberValue is List || + memberValue is Guid?[] || + memberValue is List || + memberValue is long?[]; + } + internal static void EndCustomSplitTable(ISqlSugarClient context, Type entityType) + { + if (context == null || entityType == null) + { + return; + } + var splitTableAttribute = entityType.GetCustomAttribute(); + if (splitTableAttribute == null) + { + return; + } + if (splitTableAttribute.CustomSplitTableService != null) + { + context.CurrentConnectionConfig.ConfigureExternalServices.SplitTableService = null; + } + } + + internal static void StartCustomSplitTable(ISqlSugarClient context, Type entityType) + { + if (context == null || entityType == null) + { + return; + } + var splitTableAttribute = entityType.GetCustomAttribute(); + if (splitTableAttribute == null) + { + return; + } + if (splitTableAttribute.CustomSplitTableService != null) + { + context.CurrentConnectionConfig.ConfigureExternalServices.SplitTableService + = (ISplitTableService)Activator.CreateInstance(splitTableAttribute.CustomSplitTableService); + } + if ( + context?.CurrentConnectionConfig?.ConfigureExternalServices?.SplitTableService != null + && splitTableAttribute.CustomSplitTableService == null + && context.EntityMaintenance.GetEntityInfo(entityType).DbTableName?.EndsWith("_{year}{month}{day}") == true + && !context.EntityMaintenance.GetEntityInfo(entityType).DbTableName?.Replace("_{year}{month}{day}", "")?.Contains('{') == true + ) + { + context.CurrentConnectionConfig.ConfigureExternalServices.SplitTableService + = null; + } + } + public static void ConvertParameter(SugarParameter p, ISqlBuilder builder) + { + if (!p.ParameterName.StartsWith(builder.SqlParameterKeyWord)) + { + p.ParameterName = (builder.SqlParameterKeyWord + p.ParameterName.TrimStart('@')); + } + } + public static object SetAnonymousObjectPropertyValue(object obj, string propertyName, object propertyValue) + { + if (obj.GetType().IsAnonymousType()) // 判断是否为匿名对象 + { + var objType = obj.GetType(); + var objFields = objType.GetFields(BindingFlags.Instance | BindingFlags.NonPublic); + foreach (var field in objFields) // 遍历字段列表,查找需要修改的属性 + { + if (field.Name == $"<{propertyName}>i__Field") + { + field.SetValue(obj, propertyValue); // 使用反射修改属性值 + break; + } + } + } + else + { + obj.GetType().GetProperty(propertyName).SetValue(obj, propertyValue); + } + return obj; + } + + internal static bool IsNumberArray(Type type) + { + + return type.IsIn(typeof(int[]), + typeof(long[]), + typeof(short[]), + typeof(uint[]), + typeof(ulong[]), + typeof(ushort[]), + typeof(int?[]), + typeof(long?[]), + typeof(short?[]), + typeof(uint?[]), + typeof(ulong?[]), + typeof(ushort?[]), + typeof(List), + typeof(List), + typeof(List), + typeof(List), + typeof(List), + typeof(List), + typeof(List), + typeof(List), + typeof(List), + typeof(List), + typeof(List), + typeof(List)); + } + public static string GetNativeSql(string sql, SugarParameter[] pars) + { + if (pars == null || pars.Length == 0) + return "\r\n[Sql]:" + sql + "\r\n"; + return $"\r\n[Sql]:{sql} \r\n[Pars]:{string.Join(" ", pars.Select(it => $"\r\n[Name]:{it.ParameterName} [Value]:{it.Value} [Type]:{it.DbType} {(it.IsNvarchar2 ? "nvarchar2" : "")} "))} \r\n"; + } + public static string ToUnderLine(string str, bool isToUpper = false) + { + if (str?.Contains('_') != false) + { + return str; + } + else if (isToUpper) + { + return string.Concat(str.Select((x, i) => i > 0 && char.IsUpper(x) ? "_" + x.ToString() : x.ToString())).ToUpper(); + } + else + { + return string.Concat(str.Select((x, i) => i > 0 && char.IsUpper(x) ? "_" + x.ToString() : x.ToString())).ToLower(); + } + } + internal static bool IsArrayMember(Expression expression, SqlSugarProvider context) + { + if (expression == null) + return false; + if (!(expression is LambdaExpression)) + return false; + var lambda = (LambdaExpression)expression; + if (!(lambda.Body is MemberExpression)) + return false; + var member = lambda.Body as MemberExpression; + if (!(member.Type.IsClass())) + return false; + if (member.Expression == null) + return false; + var entity = context.EntityMaintenance.GetEntityInfo(member.Expression.Type); + var json = entity.Columns.FirstOrDefault(z => z.IsArray && z.PropertyName == member.Member.Name); + return json != null; + } + internal static bool IsJsonMember(Expression expression, SqlSugarProvider context) + { + if (expression == null) + return false; + if (!(expression is LambdaExpression)) + return false; + var lambda = (LambdaExpression)expression; + if (!(lambda.Body is MemberExpression)) + return false; + var member = lambda.Body as MemberExpression; + if (!(member.Type.IsClass())) + return false; + if (member.Expression == null) + return false; + var entity = context.EntityMaintenance.GetEntityInfo(member.Expression.Type); + var json = entity.Columns.FirstOrDefault(z => z.IsJson && z.PropertyName == member.Member.Name); + return json != null; + } + public static string GetSeparatorChar() + { + return Path.Combine("a", "a").Replace("a", ""); + } + public static bool IsParentheses(object name) + { + return name.ObjToString().Trim().Last() == ')' && name.ObjToString().Trim().First() == '('; + } + + internal static bool IsDefaultValue(object value) + { + if (value == null) return true; + return value.Equals(UtilMethods.GetDefaultValue(value.GetType())); + } + + internal static DateTime ConvertFromDateTimeOffset(DateTimeOffset dateTime) + { + if (dateTime.Offset.Equals(TimeSpan.Zero)) + return dateTime.UtcDateTime; + else if (dateTime.Offset.Equals(TimeZoneInfo.Local.GetUtcOffset(dateTime.DateTime))) + return DateTime.SpecifyKind(dateTime.DateTime, DateTimeKind.Local); + else + return dateTime.DateTime; + } + + internal static object To(object value, Type destinationType) + { + return To(value, destinationType, CultureInfo.InvariantCulture); + } + + internal static object To(object value, Type destinationType, CultureInfo culture) + { + if (value != null) + { + destinationType = UtilMethods.GetUnderType(destinationType); + var sourceType = value.GetType(); + if (destinationType.Name == "DateOnly" && sourceType == typeof(string)) + { + var type = Type.GetType("System.DateOnly", true, true); + var method = type.GetMethods().FirstOrDefault(it => it.GetParameters().Length == 1 && it.Name == "FromDateTime"); + return method.Invoke(null, new object[] { Convert.ToDateTime(value) }); + } + else if (destinationType.FullName == "System.Ulid") + { + var method = destinationType.GetMyMethod("Parse", 1); + if (method != null) + { + var result = method.Invoke(null, new object[] { value }); + return result; + } + } + else if (value is byte[] bytes && bytes.Length == 1 && destinationType == typeof(char)) + { + return (char)(bytes)[0]; + } + else if (value is DateTime && destinationType == typeof(TimeSpan)) + { + value = Convert.ToDateTime(value).TimeOfDay; + } + else if (value is DateTime && destinationType.FullName == "System.TimeOnly") + { + value = Convert.ToDateTime(value).TimeOfDay; + } + var destinationConverter = TypeDescriptor.GetConverter(destinationType); + if (destinationConverter?.CanConvertFrom(value.GetType()) == true) + return destinationConverter.ConvertFrom(null, culture, value); + + var sourceConverter = TypeDescriptor.GetConverter(sourceType); + if (sourceConverter?.CanConvertTo(destinationType) == true) + return sourceConverter.ConvertTo(null, culture, value, destinationType); + + if (destinationType.IsEnum && value is int) + return Enum.ToObject(destinationType, (int)value); + + if (destinationType.Name == "TimeOnly" && sourceType.Name != "TimeOnly") + { + var type = Type.GetType("System.TimeOnly", true, true); + var method = type.GetMethods().FirstOrDefault(it => it.GetParameters().Length == 1 && it.Name == "FromTimeSpan"); + return method.Invoke(null, new object[] { value }); + } + if (destinationType.Name == "DateOnly" && sourceType.Name != "DateOnly") + { + var type = Type.GetType("System.DateOnly", true, true); + var method = type.GetMethods().FirstOrDefault(it => it.GetParameters().Length == 1 && it.Name == "FromDateTime"); + return method.Invoke(null, new object[] { value }); + } + + if (!destinationType.IsInstanceOfType(value)) + return Convert.ChangeType(value, destinationType, culture); + } + return value; + } + public static bool IsAnyAsyncMethod(StackFrame[] methods) + { + bool isAsync = false; + foreach (var item in methods) + { + if (UtilMethods.IsAsyncMethod(item.GetMethod())) + { + isAsync = true; + break; + } + } + return isAsync; + } + + + public static ConnectionConfig CopyConfig(ConnectionConfig it) + { + return new ConnectionConfig() + { + AopEvents = it.AopEvents == null ? null : new AopEvents() + { + DataExecuting = it.AopEvents?.DataExecuting, + OnDiffLogEvent = it.AopEvents?.OnDiffLogEvent, + OnError = it.AopEvents?.OnError, + OnExecutingChangeSql = it.AopEvents?.OnExecutingChangeSql, + OnLogExecuted = it.AopEvents?.OnLogExecuted, + OnLogExecuting = it.AopEvents?.OnLogExecuting, + DataExecuted = it.AopEvents?.DataExecuted, + CheckConnectionExecuted = it.AopEvents?.CheckConnectionExecuted, + CheckConnectionExecuting = it.AopEvents?.CheckConnectionExecuting, + OnGetDataReadered = it.AopEvents?.OnGetDataReadered, + OnGetDataReadering = it.AopEvents?.OnGetDataReadering, + }, + ConfigId = it.ConfigId, + ConfigureExternalServices = it.ConfigureExternalServices == null ? null : new ConfigureExternalServices() + { + AppendDataReaderTypeMappings = it.ConfigureExternalServices.AppendDataReaderTypeMappings, + DataInfoCacheService = it.ConfigureExternalServices.DataInfoCacheService, + EntityNameService = it.ConfigureExternalServices.EntityNameService, + EntityService = it.ConfigureExternalServices.EntityService, + RazorService = it.ConfigureExternalServices.RazorService, + ReflectionInoCacheService = it.ConfigureExternalServices.ReflectionInoCacheService, + SerializeService = it.ConfigureExternalServices.SerializeService, + SplitTableService = it.ConfigureExternalServices.SplitTableService, + SqlFuncServices = it.ConfigureExternalServices.SqlFuncServices + }, + ConnectionString = it.ConnectionString, + DbType = it.DbType, + DbLinkName = it.DbLinkName, + IndexSuffix = it.IndexSuffix, + InitKeyType = it.InitKeyType, + IsAutoCloseConnection = it.IsAutoCloseConnection, + LanguageType = it.LanguageType, + MoreSettings = it.MoreSettings == null ? null : new ConnMoreSettings() + { + DefaultCacheDurationInSeconds = it.MoreSettings.DefaultCacheDurationInSeconds, + DisableNvarchar = it.MoreSettings.DisableNvarchar, + PgSqlIsAutoToLower = it.MoreSettings.PgSqlIsAutoToLower, + PgSqlIsAutoToLowerCodeFirst = it.MoreSettings.PgSqlIsAutoToLowerCodeFirst, + IsAutoRemoveDataCache = it.MoreSettings.IsAutoRemoveDataCache, + IsWithNoLockQuery = it.MoreSettings.IsWithNoLockQuery, + DisableWithNoLockWithTran = it.MoreSettings.DisableWithNoLockWithTran, + TableEnumIsString = it.MoreSettings.TableEnumIsString, + DisableMillisecond = it.MoreSettings.DisableMillisecond, + DbMinDate = it.MoreSettings.DbMinDate, + IsNoReadXmlDescription = it.MoreSettings.IsNoReadXmlDescription, + SqlServerCodeFirstNvarchar = it.MoreSettings.SqlServerCodeFirstNvarchar, + OracleCodeFirstNvarchar2 = it.MoreSettings.OracleCodeFirstNvarchar2, + IsAutoToUpper = it.MoreSettings.IsAutoToUpper, + IsAutoDeleteQueryFilter = it.MoreSettings.IsAutoDeleteQueryFilter, + IsAutoUpdateQueryFilter = it.MoreSettings.IsAutoUpdateQueryFilter, + EnableModelFuncMappingColumn = it.MoreSettings.EnableModelFuncMappingColumn, + EnableOracleIdentity = it.MoreSettings.EnableOracleIdentity, + IsWithNoLockSubquery = it.MoreSettings.IsWithNoLockSubquery, + EnableCodeFirstUpdatePrecision = it.MoreSettings.EnableCodeFirstUpdatePrecision, + SqliteCodeFirstEnableDefaultValue = it.MoreSettings.SqliteCodeFirstEnableDefaultValue, + SqliteCodeFirstEnableDescription = it.MoreSettings.SqliteCodeFirstEnableDescription, + IsCorrectErrorSqlParameterName = it.MoreSettings.IsCorrectErrorSqlParameterName, + SqliteCodeFirstEnableDropColumn = it.MoreSettings.SqliteCodeFirstEnableDropColumn, + MaxParameterNameLength = it.MoreSettings.MaxParameterNameLength, + DisableQueryWhereColumnRemoveTrim = it.MoreSettings.DisableQueryWhereColumnRemoveTrim, + DatabaseModel = it.MoreSettings.DatabaseModel, + EnableILike = it.MoreSettings.EnableILike, + ClickHouseEnableFinal = it.MoreSettings.ClickHouseEnableFinal, + PgSqlIsAutoToLowerSchema = it.MoreSettings.PgSqlIsAutoToLowerSchema + + }, + SqlMiddle = it.SqlMiddle == null ? null : new SqlMiddle + { + IsSqlMiddle = it.SqlMiddle.IsSqlMiddle, + ExecuteCommand = it.SqlMiddle.ExecuteCommand, + ExecuteCommandAsync = it.SqlMiddle.ExecuteCommandAsync, + GetDataReader = it.SqlMiddle.GetDataReader, + GetDataReaderAsync = it.SqlMiddle.GetDataReaderAsync, + GetDataSetAll = it.SqlMiddle.GetDataSetAll, + GetDataSetAllAsync = it.SqlMiddle.GetDataSetAllAsync, + GetScalar = it.SqlMiddle.GetScalar, + GetScalarAsync = it.SqlMiddle.GetScalarAsync + }, + SlaveConnectionConfigs = it.SlaveConnectionConfigs + }; + } + + internal static object GetRandomByType(Type underType) + { + if (underType == UtilConstants.GuidType) + { + return Guid.NewGuid(); + } + else if (underType == UtilConstants.LongType) + { + return SnowFlakeSingle.Instance.NextId(); + } + else if (underType == UtilConstants.StringType) + { + return Guid.NewGuid() + ""; + } + else if (underType == UtilConstants.DateType) + { + return System.DateTime.Now; + } + else + { + return Guid.NewGuid() + ""; + } + } + + public static bool IsAsyncMethod(MethodBase method) + { + if (method == null) + { + return false; + } + if (method.DeclaringType != null) + { + if (method.DeclaringType.GetInterfaces().Contains(typeof(IAsyncStateMachine))) + { + return true; + } + } + var name = method.Name; + if (name.Contains("OutputAsyncCausalityEvents")) + { + return true; + } + if (name.Contains("OutputWaitEtwEvents")) + { + return true; + } + if (name.Contains("ExecuteAsync")) + { + return true; + } + //if (method?.DeclaringType?.FullName?.Contains("Furion.InternalApp")==true) + //{ + // return false; + //} + Type attType = typeof(AsyncStateMachineAttribute); + var attrib = (AsyncStateMachineAttribute)method.GetCustomAttribute(attType); + return (attrib != null); + } + + public static StackTraceInfo GetStackTrace() + { + + StackTrace st = new StackTrace(true); + StackTraceInfo info = new StackTraceInfo(); + info.MyStackTraceList = new List(); + info.SugarStackTraceList = new List(); + for (int i = 0; i < st.FrameCount; i++) + { + var frame = st.GetFrame(i); + if (!frame.GetMethod().Module.Name.Equals("sqlsugar.dll", StringComparison.CurrentCultureIgnoreCase) && frame.GetMethod().Name.First() != '<') + { + info.MyStackTraceList.Add(new StackTraceInfoItem() + { + FileName = frame.GetFileName(), + MethodName = frame.GetMethod().Name, + Line = frame.GetFileLineNumber() + }); + } + else + { + info.SugarStackTraceList.Add(new StackTraceInfoItem() + { + FileName = frame.GetFileName(), + MethodName = frame.GetMethod().Name, + Line = frame.GetFileLineNumber() + }); + } + } + return info; + } + + internal static object GetConvertValue(object entityValue) + { + if (entityValue != null && entityValue is DateTime) + { + entityValue = entityValue.ObjToDate().ToString("yyyy-MM-dd HH:mm:ss.fff"); + } + return entityValue; + } + + internal static T To(object value) + { + return (T)To(value, typeof(T)); + } + + internal static DateTime GetMinDate(ConnectionConfig currentConnectionConfig) + { + if (currentConnectionConfig.MoreSettings == null) + { + return Convert.ToDateTime("1900-01-01"); + } + else if (currentConnectionConfig.MoreSettings.DbMinDate == null) + { + return Convert.ToDateTime("1900-01-01"); + } + else + { + return currentConnectionConfig.MoreSettings.DbMinDate.Value; + } + } + + public static Type GetUnderType(Type oldType) + { + Type type = Nullable.GetUnderlyingType(oldType); + return type == null ? oldType : type; + } + public static object GetDefaultValue(Type type) + { + return type.IsValueType ? Activator.CreateInstance(type) : null; + } + public static string ReplaceFirstMatch(string input, string pattern, string replacement) + { + Regex regex = new Regex(pattern); + return regex.Replace(input, replacement, 1); + } + public static string ReplaceSqlParameter(string itemSql, SugarParameter itemParameter, string newName) + { + itemSql = Regex.Replace(itemSql, string.Format(@"{0} ", "\\" + itemParameter.ParameterName), newName + " ", RegexOptions.IgnoreCase); + itemSql = Regex.Replace(itemSql, string.Format(@"{0}\)", "\\" + itemParameter.ParameterName), newName + ")", RegexOptions.IgnoreCase); + itemSql = Regex.Replace(itemSql, string.Format(@"{0}\,", "\\" + itemParameter.ParameterName), newName + ",", RegexOptions.IgnoreCase); + itemSql = Regex.Replace(itemSql, string.Format(@"{0}$", "\\" + itemParameter.ParameterName), newName, RegexOptions.IgnoreCase); + itemSql = Regex.Replace(itemSql, string.Format(@"\+{0}\+", "\\" + itemParameter.ParameterName), "+" + newName + "+", RegexOptions.IgnoreCase); + itemSql = Regex.Replace(itemSql, string.Format(@"\+{0} ", "\\" + itemParameter.ParameterName), "+" + newName + " ", RegexOptions.IgnoreCase); + itemSql = Regex.Replace(itemSql, string.Format(@" {0}\+", "\\" + itemParameter.ParameterName), " " + newName + "+", RegexOptions.IgnoreCase); + itemSql = Regex.Replace(itemSql, string.Format(@"\|\|{0}\|\|", "\\" + itemParameter.ParameterName), "||" + newName + "||", RegexOptions.IgnoreCase); + itemSql = Regex.Replace(itemSql, string.Format(@"\={0}\+", "\\" + itemParameter.ParameterName), "=" + newName + "+", RegexOptions.IgnoreCase); + itemSql = Regex.Replace(itemSql, string.Format(@"{0}\|\|", "\\" + itemParameter.ParameterName), newName + "||", RegexOptions.IgnoreCase); + return itemSql; + } + internal static Type GetRootBaseType(Type entityType) + { + var baseType = entityType.BaseType; + while (baseType != null && baseType.BaseType != UtilConstants.ObjType) + { + baseType = baseType.BaseType; + } + return baseType; + } + + + internal static Type GetUnderType(PropertyInfo propertyInfo, ref bool isNullable) + { + Type unType = Nullable.GetUnderlyingType(propertyInfo.PropertyType); + isNullable = unType != null; + unType = unType ?? propertyInfo.PropertyType; + return unType; + } + + internal static Type GetUnderType(PropertyInfo propertyInfo) + { + Type unType = Nullable.GetUnderlyingType(propertyInfo.PropertyType); + unType = unType ?? propertyInfo.PropertyType; + return unType; + } + + internal static bool IsNullable(PropertyInfo propertyInfo) + { + Type unType = Nullable.GetUnderlyingType(propertyInfo.PropertyType); + return unType != null; + } + + internal static bool IsNullable(Type type) + { + Type unType = Nullable.GetUnderlyingType(type); + return unType != null; + } + internal static T IsNullReturnNew(T returnObj) where T : new() + { + if (returnObj.IsNullOrEmpty()) + { + returnObj = new T(); + } + return returnObj; + } + public static object ChangeType2(object value, Type type) + { + if (value is byte[] && type == UtilConstants.StringType) + { + return Encoding.UTF8.GetString(value as byte[]); + } + if (value == null && type.IsGenericType) return Activator.CreateInstance(type); + if (value == null) return null; + if (type == value.GetType()) return value; + if (type.IsEnum) + { + if (value is string) + return Enum.Parse(type, value as string); + else + return Enum.ToObject(type, value); + } + if (value is string && type == typeof(Guid?)) return value.IsNullOrEmpty() ? null : (Guid?)new Guid(value as string); + if (!type.IsInterface && type.IsGenericType) + { + Type innerType = type.GetGenericArguments()[0]; + object innerValue = ChangeType(value, innerType); + return Activator.CreateInstance(type, new object[] { innerValue }); + } + if (value is string && type == typeof(Guid)) return new Guid(value as string); + if (value is string && type == typeof(Version)) return new Version(value as string); + if (!(value is IConvertible)) return value; + if (value is DateTime && type.FullName == "System.DateOnly") + { + value = UtilMethods.DateTimeToDateOnly(value); + } + return Convert.ChangeType(value, type); + } + + internal static T ChangeType(T obj, Type type) + { + return (T)Convert.ChangeType(obj, type); + } + + internal static T ChangeType(T obj) + { + return (T)Convert.ChangeType(obj, typeof(T)); + } + + internal static DateTimeOffset GetDateTimeOffsetByDateTime(DateTime date) + { + date = DateTime.SpecifyKind(date, DateTimeKind.Utc); + DateTimeOffset utcTime2 = date; + return utcTime2; + } + + internal static void RepairReplicationParameters(ref string appendSql, SugarParameter[] parameters, int addIndex, string append = null) + { + if (appendSql.HasValue() && parameters.HasValue()) + { + foreach (var parameter in parameters.OrderByDescending(it => it.ParameterName.Length)) + { + //Compatible with.NET CORE parameters case + var name = parameter.ParameterName; + string newName = name + append + addIndex; + appendSql = ReplaceSqlParameter(appendSql, parameter, newName); + parameter.ParameterName = newName; + } + } + } + internal static void RepairReplicationParameters(ISqlSugarClient db, ref string appendSql, SugarParameter[] parameters, int addIndex, string append = null) + { + if (appendSql.HasValue() && parameters.HasValue()) + { + foreach (var parameter in parameters.OrderByDescending(it => it.ParameterName.Length)) + { + //Compatible with.NET CORE parameters case + var name = parameter.ParameterName; + string newName = name + append + addIndex; + var maxLength = db.CurrentConnectionConfig.MoreSettings.MaxParameterNameLength; + if (newName.Length > maxLength) + { + newName = (name.Substring(0, 1) + name.GetNonNegativeHashCodeString() + "_" + addIndex); + } + appendSql = ReplaceSqlParameter(appendSql, parameter, newName); + parameter.ParameterName = newName; + } + } + } + + internal static string GetPackTable(string sql, string shortName) + { + return string.Format(" ({0}) {1} ", sql, shortName); + } + + public static Func GetTypeConvert(object value) + { + if (value is int || value is uint || value is int? || value is uint?) + { + return x => Convert.ToInt32(x); + } + else if (value is short || value is ushort || value is short? || value is ushort?) + { + return x => Convert.ToInt16(x); + } + else if (value is long || value is long? || value is ulong? || value is long?) + { + return x => Convert.ToInt64(x); + } + else if (value is DateTime || value is DateTime?) + { + return x => Convert.ToDateTime(x); + } + else if (value is bool || value is bool?) + { + return x => Convert.ToBoolean(x); + } + return null; + } + + internal static string GetTypeName(object value) + { + if (value == null) + { + return null; + } + else + { + return value.GetType().Name; + } + } + + internal static string GetParenthesesValue(string dbTypeName) + { + if (Regex.IsMatch(dbTypeName, @"\(.+\)")) + { + if (Regex.IsMatch(dbTypeName, @"SimpleAggregateFunction")) + dbTypeName = Regex.Match(dbTypeName, @"((?<=,\s)[^Nullable]\w+)|((?<=Nullable\()\w+)").Value; + else + dbTypeName = Regex.Replace(dbTypeName, @"\(.+\)", ""); + } + dbTypeName = dbTypeName.Trim(); + return dbTypeName; + } + + internal static T GetOldValue(T value, Action action) + { + action(); + return value; + } + + internal static object DefaultForType(Type targetType) + { + return targetType.IsValueType ? Activator.CreateInstance(targetType) : null; + } + + internal static Int64 GetLong(byte[] bytes) + { + return Convert.ToInt64(string.Join("", bytes).PadRight(20, '0')); + } + public static object GetPropertyValue(T t, string PropertyName) + { + return t.GetType().GetProperty(PropertyName).GetValue(t, null); + } + internal static string GetMD5(string myString) + { + return myString.MD5(); + } + + public static string EncodeBase64(string code) + { + if (StaticConfig.Encode != null) + { + return StaticConfig.Encode(code); + } + if (code.IsNullOrEmpty()) return code; + string encode = ""; + byte[] bytes = Encoding.GetEncoding("utf-8").GetBytes(code); + try + { + encode = Convert.ToBase64String(bytes); + } + catch + { + encode = code; + } + return encode; + } + public static string ConvertNumbersToString(string value) + { + string[] splitInt = value.Split(separator, StringSplitOptions.RemoveEmptyEntries); + + var splitChars = splitInt.Select(s => Convert.ToChar( + Convert.ToInt32(s, 8) + ).ToString()); + + return string.Join("", splitChars); + } + public static string ConvertStringToNumbers(string value) + { + StringBuilder sb = new StringBuilder(); + + foreach (char c in value) + { + int cAscil = (int)c; + sb.Append(Convert.ToString(c, 8) + "9"); + } + + return sb.ToString(); + } + + public static string DecodeBase64(string code) + { + try + { + if (StaticConfig.Decode != null) + { + return StaticConfig.Decode(code); + } + if (code.IsNullOrEmpty()) return code; + string decode = ""; + byte[] bytes = Convert.FromBase64String(code); + try + { + decode = Encoding.GetEncoding("utf-8").GetString(bytes); + } + catch + { + decode = code; + } + return decode; + } + catch + { + return code; + } + } + + public static string GetSqlValue(object value) + { + if (value == null) + { + return "null"; + } + else if (UtilMethods.IsNumber(value.GetType().Name)) + { + return value.ObjToString(); + } + else if (value is DateTime) + { + return UtilMethods.GetConvertValue(value) + ""; + } + else + { + return value.ToSqlValue(); + } + } + + public static void DataInoveByExpresson(Type[] datas, MethodCallExpression callExpresion) + { + var methodInfo = callExpresion.Method; + foreach (var item in datas) + { + if (item != null) + { + if (callExpresion.Arguments.Count == 0) + { + methodInfo.Invoke(item, null); + } + else + { + List methodParameters = new List(); + foreach (var callItem in callExpresion.Arguments) + { + var parameter = callItem.GetType().GetProperties().FirstOrDefault(it => it.Name == "Value"); + if (parameter == null) + { + var value = LambdaExpression.Lambda(callItem).Compile().DynamicInvoke(); + methodParameters.Add(value); + } + else + { + var value = parameter.GetValue(callItem, null); + methodParameters.Add(value); + } + } + methodInfo.Invoke(item, methodParameters.ToArray()); + } + } + } + } + + public static Dictionary EnumToDictionary() + { + Dictionary dic = new Dictionary(); + if (!typeof(T).IsEnum) + { + return dic; + } + string desc = string.Empty; + foreach (var item in Enum.GetValues(typeof(T))) + { + var key = item.ToString().ToLower(); + if (!dic.ContainsKey(key)) + dic.Add(key, (T)item); + } + return dic; + } + + public static Type GetTypeByTypeName(string ctypename) + { + + if (ctypename.EqualCase(UtilConstants.DecType.Name)) + { + return UtilConstants.DecType; + } + else if (ctypename.EqualCase(UtilConstants.DobType.Name)) + { + return UtilConstants.DobType; + } + else if (ctypename.EqualCase(UtilConstants.DateType.Name)) + { + return UtilConstants.DateType; + } + else if (ctypename.EqualCase(UtilConstants.IntType.Name)) + { + return UtilConstants.IntType; + } + else if (ctypename.EqualCase(UtilConstants.BoolType.Name)) + { + return UtilConstants.BoolType; + } + else if (ctypename.EqualCase(UtilConstants.LongType.Name)) + { + return UtilConstants.LongType; + } + else if (ctypename.EqualCase(UtilConstants.ShortType.Name)) + { + return UtilConstants.ShortType; + } + else if (ctypename.EqualCase(UtilConstants.DateTimeOffsetType.Name)) + { + return UtilConstants.DateTimeOffsetType; + } + else if (ctypename.EqualCase(UtilConstants.GuidType.Name)) + { + return UtilConstants.GuidType; + } + else if (ctypename.EqualCase("int")) + { + return UtilConstants.IntType; + } + else if (ctypename.EqualCase("long")) + { + return UtilConstants.LongType; + } + else if (ctypename.EqualCase("short")) + { + return UtilConstants.ShortType; + } + else if (ctypename.EqualCase("byte")) + { + return UtilConstants.ByteType; + } + else if (ctypename.EqualCase("uint")) + { + return UtilConstants.UIntType; + } + else if (ctypename.EqualCase("ulong")) + { + return UtilConstants.ULongType; + } + else if (ctypename.EqualCase("ushort")) + { + return UtilConstants.UShortType; + } + else if (ctypename.EqualCase("uint32")) + { + return UtilConstants.UIntType; + } + else if (ctypename.EqualCase("uint64")) + { + return UtilConstants.ULongType; + } + else if (ctypename.EqualCase("bool")) + { + return UtilConstants.BoolType; + } + else if (ctypename.EqualCase("ToBoolean")) + { + return UtilConstants.BoolType; + } + else if (ctypename.EqualCase("uint16")) + { + return UtilConstants.UShortType; + } + else if (ctypename.EqualCase(UtilConstants.ByteArrayType.Name)) + { + return UtilConstants.ByteArrayType; + } + else + { + return UtilConstants.StringType; + } + } + public static object ConvertDataByTypeName(string ctypename, string value) + { + var item = new ConditionalModel() + { + CSharpTypeName = ctypename, + FieldValue = value + }; + if (string.IsNullOrEmpty(item.FieldValue) && item.CSharpTypeName.HasValue() && !item.CSharpTypeName.EqualCase("string")) + { + return null; + } + if (item.CSharpTypeName.EqualCase(UtilConstants.DecType.Name)) + { + return Convert.ToDecimal(item.FieldValue); + } + else if (item.CSharpTypeName.EqualCase(UtilConstants.DobType.Name)) + { + return Convert.ToDouble(item.FieldValue); + } + else if (item.CSharpTypeName.EqualCase(UtilConstants.DateType.Name)) + { + return Convert.ToDateTime(item.FieldValue); + } + else if (item.CSharpTypeName.EqualCase(UtilConstants.IntType.Name)) + { + return Convert.ToInt32(item.FieldValue); + } + else if (item.FieldValue != null && item.CSharpTypeName.EqualCase(UtilConstants.BoolType.Name)) + { + return Convert.ToBoolean(item.FieldValue.ToLower()); + } + else if (item.CSharpTypeName.EqualCase(UtilConstants.LongType.Name)) + { + return Convert.ToInt64(item.FieldValue); + } + else if (item.CSharpTypeName.EqualCase(UtilConstants.ShortType.Name)) + { + return Convert.ToInt16(item.FieldValue); + } + else if (item.CSharpTypeName.EqualCase(UtilConstants.DateTimeOffsetType.Name)) + { + DateTimeOffset dt; + if (DateTimeOffset.TryParse(item.FieldValue, out dt)) + { + return dt; + } + return UtilMethods.GetDateTimeOffsetByDateTime(Convert.ToDateTime(item.FieldValue)); + } + else if (item.CSharpTypeName.EqualCase(UtilConstants.GuidType.Name)) + { + return Guid.Parse(item.FieldValue); + } + else if (item.CSharpTypeName.EqualCase("int")) + { + return Convert.ToInt32(item.FieldValue); + } + else if (item.CSharpTypeName.EqualCase("long")) + { + return Convert.ToInt64(item.FieldValue); + } + else if (item.CSharpTypeName.EqualCase("float")) + { + return Convert.ToSingle(item.FieldValue); + } + else if (item.CSharpTypeName.EqualCase("single")) + { + return Convert.ToSingle(item.FieldValue); + } + else if (item.CSharpTypeName.EqualCase("short")) + { + return Convert.ToInt16(item.FieldValue); + } + else if (item.CSharpTypeName.EqualCase("byte")) + { + return Convert.ToByte(item.FieldValue); + } + else if (item.CSharpTypeName.EqualCase("uint")) + { + return Convert.ToUInt32(item.FieldValue); + } + else if (item.CSharpTypeName.EqualCase("ulong")) + { + return Convert.ToUInt64(item.FieldValue); + } + else if (item.CSharpTypeName.EqualCase("ushort")) + { + return Convert.ToUInt16(item.FieldValue); + } + else if (item.CSharpTypeName.EqualCase("uint32")) + { + return Convert.ToUInt32(item.FieldValue); + } + else if (item.CSharpTypeName.EqualCase("uint64")) + { + return Convert.ToUInt64(item.FieldValue); + } + else if (item.CSharpTypeName.EqualCase("bool")) + { + return Convert.ToBoolean(item.FieldValue); + } + else if (item.CSharpTypeName.EqualCase("ToBoolean")) + { + return Convert.ToBoolean(item.FieldValue); + } + else if (item.CSharpTypeName.EqualCase("uint16")) + { + return Convert.ToUInt16(item.FieldValue); + } + else if (item.CSharpTypeName.EqualCase("byte[]") && item.FieldValue?.Contains('|') == true) + { + return item.FieldValue.Split('|').Select(it => Convert.ToByte(it)).ToArray(); + } + else + { + return item.FieldValue; + } + } + + public static bool IsNumber(string ctypename) + { + if (ctypename.IsNullOrEmpty()) + { + return false; + } + var item = new ConditionalModel() + { + CSharpTypeName = ctypename, + }; + if (item.CSharpTypeName.EqualCase(UtilConstants.DecType.Name)) + { + return true; + } + else if (item.CSharpTypeName.EqualCase(UtilConstants.DobType.Name)) + { + return true; + } + else if (item.CSharpTypeName.EqualCase(UtilConstants.IntType.Name)) + { + return true; + } + else if (item.CSharpTypeName.EqualCase(UtilConstants.LongType.Name)) + { + return true; + } + else if (item.CSharpTypeName.EqualCase(UtilConstants.ShortType.Name)) + { + return true; + } + else if (item.CSharpTypeName.EqualCase("int")) + { + return true; + } + else if (item.CSharpTypeName.EqualCase("long")) + { + return true; + } + else if (item.CSharpTypeName.EqualCase("short")) + { + return true; + } + else if (item.CSharpTypeName.EqualCase("byte")) + { + return true; + } + else if (item.CSharpTypeName.EqualCase("uint")) + { + return true; + } + else if (item.CSharpTypeName.EqualCase("ulong")) + { + return true; + } + else if (item.CSharpTypeName.EqualCase("ushort")) + { + return true; + } + else if (item.CSharpTypeName.EqualCase("uint32")) + { + return true; + } + else if (item.CSharpTypeName.EqualCase("uint64")) + { + return true; + } + else if (item.CSharpTypeName.EqualCase("uint16")) + { + return true; + } + else + { + return false; + } + } + + /// + /// Get Week Last Day Sun + /// + /// + /// + public static DateTime GetWeekLastDaySun(DateTime datetime) + { + //星期天为最后一天 + int weeknow = Convert.ToInt32(datetime.DayOfWeek); + weeknow = (weeknow == 0 ? 7 : weeknow); + int daydiff = (7 - weeknow); + + //本周最后一天 + string LastDay = datetime.AddDays(daydiff).ToString("yyyy-MM-dd"); + return Convert.ToDateTime(LastDay); + } + /// + /// Get Week First Day Mon + /// + /// + /// + public static DateTime GetWeekFirstDayMon(DateTime datetime) + { + //星期一为第一天 + int weeknow = Convert.ToInt32(datetime.DayOfWeek); + + //因为是以星期一为第一天,所以要判断weeknow等于0时,要向前推6天。 + weeknow = (weeknow == 0 ? (7 - 1) : (weeknow - 1)); + int daydiff = (-1) * weeknow; + + //本周第一天 + string FirstDay = datetime.AddDays(daydiff).ToString("yyyy-MM-dd"); + return Convert.ToDateTime(FirstDay); + } + public static string GetSqlString(DbType dbType, string sql, SugarParameter[] parametres, bool DisableNvarchar = false) + { + if (parametres == null) + parametres = Array.Empty(); + return GetSqlString(new ConnectionConfig() + { + DbType = dbType, + MoreSettings = new ConnMoreSettings() + { + DisableNvarchar = DisableNvarchar + } + }, new KeyValuePair>(sql, parametres.ToList())); + } + public static string GetSqlString(ConnectionConfig connectionConfig, KeyValuePair> sqlObj) + { + var guid = Guid.NewGuid() + ""; + var result = sqlObj.Key; + if (sqlObj.Value != null) + { + foreach (var item in UtilMethods.CopySugarParameters(sqlObj.Value).OrderByDescending(it => it.ParameterName.Length)) + { + if (item.ParameterName.StartsWith(':') && !result.Contains(item.ParameterName)) + { + item.ParameterName = "@" + item.ParameterName.TrimStart(':'); + } + if (connectionConfig.MoreSettings == null) + { + connectionConfig.MoreSettings = new ConnMoreSettings(); + } + if (item.Value != null && item.Value is DateTime && ((DateTime)item.Value == DateTime.MinValue)) + { + item.Value = connectionConfig.MoreSettings.DbMinDate; + } + if (item.Value == null || item.Value == DBNull.Value) + { + result = result.Replace(item.ParameterName, "null"); + } + else if (UtilMethods.IsNumber(item.Value.GetType().Name)) + { + result = result.Replace(item.ParameterName, item.Value.ObjToString()); + } + else if (item.Value is DateTime && connectionConfig.DbType == DbType.SqlServer) + { + result = result.Replace(item.ParameterName, "CAST('" + item.Value.ObjToDate().ToString("yyyy-MM-dd HH:mm:ss.fff") + "' AS DATETIME)"); + } + else if (item.Value is DateTime && connectionConfig.DbType.IsIn(DbType.Dm, DbType.Oracle)) + { + if (item.DbType == System.Data.DbType.Date || connectionConfig?.MoreSettings?.DisableMillisecond == true) + { + var value = "to_date('" + item.Value.ObjToDate().ToString("yyyy-MM-dd HH:mm:ss") + "', 'YYYY-MM-DD HH24:MI:SS') "; ; + result = result.Replace(item.ParameterName, value); + } + else + { + var value = "to_timestamp('" + item.Value.ObjToDate().ToString("yyyy-MM-dd HH:mm:ss.ffffff") + "', 'YYYY-MM-DD HH24:MI:SS.FF') "; + result = result.Replace(item.ParameterName, value); + } + } + else if (item.Value is DateTime) + { + result = result.Replace(item.ParameterName, "'" + item.Value.ObjToDate().ToString("yyyy-MM-dd HH:mm:ss.fff") + "'"); + } + else if (item.IsArray) + { + result = result.Replace(item.ParameterName, "'{" + new SerializeService().SerializeObject(item.Value).TrimStart('[').TrimEnd(']') + "}'"); + } + else if (item.Value is byte[] && connectionConfig.DbType == DbType.PostgreSQL) + { + result = result.Replace(item.ParameterName, ByteArrayToPostgreByteaLiteral(item.Value as byte[])); + } + else if (item.Value is byte[]) + { + result = result.Replace(item.ParameterName, "0x" + Convert.ToHexString((byte[])item.Value)); + } + else if (item.Value is bool) + { + if (connectionConfig.DbType == DbType.PostgreSQL) + { + result = result.Replace(item.ParameterName, (Convert.ToBoolean(item.Value) ? "true" : "false")); + } + else + { + result = result.Replace(item.ParameterName, (Convert.ToBoolean(item.Value) ? 1 : 0) + ""); + } + } + else if (item.Value.GetType() != UtilConstants.StringType && connectionConfig.DbType == DbType.PostgreSQL && PostgreSQLDbBind.MappingTypesConst.Any(x => x.Value.ToString().EqualCase(item.Value.GetType().Name))) + { + var type = PostgreSQLDbBind.MappingTypesConst.First(x => x.Value.ToString().EqualCase(item.Value.GetType().Name)).Key; + var replaceValue = string.Format("CAST('{0}' AS {1})", item.Value, type); + result = result.Replace(item.ParameterName, replaceValue); + } + else if (connectionConfig.MoreSettings?.DisableNvarchar == true || item.DbType == System.Data.DbType.AnsiString || connectionConfig.DbType == DbType.Sqlite) + { + result = result.Replace(item.ParameterName, $"'{item.Value.ObjToString().Replace("@", guid).ToSqlFilter()}'"); + } + else + { + result = result.Replace(item.ParameterName, $"N'{item.Value.ObjToStringNoTrim().Replace("@", guid).ToSqlFilter()}'"); + } + } + } + result = result.Replace(guid, "@"); + return result; + } + public static string ByteArrayToPostgreByteaLiteral(byte[] data) + { + var sb = new StringBuilder("E'"); + + foreach (var b in data) + { + if (b >= 32 && b < 127 && !char.IsControl((char)b)) // 可打印的ASCII字符 + { + sb.Append((char)b); + } + else // 非打印字符或控制字符 + { + sb.Append("\\\\"); + sb.Append(Convert.ToString(b, 8).PadLeft(3, '0')); + } + } + + sb.Append("'::bytea"); + return sb.ToString(); + } + public static void CheckArray(T[] insertObjs) where T : class, new() + { + + if (insertObjs?.Length == 1 + && insertObjs.FirstOrDefault()?.GetType().FullName.Contains("System.Collections.Generic.List`") == true) + { + Check.ExceptionEasy("Insertable(T []) is an array and your argument is a List", "二次封装引起的进错重载,当前方法是 Insertable(T []) 参数是一个数组,而你的参数是一个List"); + } + } + + [Obsolete("请使用新名字:FieldNameSql")] + public static string FiledNameSql() + { + return $"[value=sql{UtilConstants.ReplaceKey}]"; + } + public static string FieldNameSql() + { + if (StaticConfig.TableQuerySqlKey != Guid.Empty) + { + return $"[value=sql{StaticConfig.TableQuerySqlKey}]"; + } + return $"[value=sql{UtilConstants.ReplaceKey}]"; + } + + internal static object TimeOnlyToTimeSpan(object value) + { + if (value == null) return null; + var method = value.GetType().GetMethods().First(it => it.GetParameters().Length == 0 && it.Name == "ToTimeSpan"); + return method.Invoke(value, Array.Empty()); + } + + internal static object DateOnlyToDateTime(object value) + { + if (value == null) return null; + var method = value.GetType().GetMethods().First(it => it.GetParameters().Length == 0 && it.Name == "ToShortDateString"); + return method.Invoke(value, Array.Empty()); + } + internal static object DateTimeToDateOnly(object value) + { + if (value == null) return null; + + // 获取DateOnly类型 + Type dateOnlyType = Type.GetType("System.DateOnly, System.Runtime", throwOnError: false); + if (dateOnlyType == null) + { + throw new InvalidOperationException("DateOnly type not found."); + } + + // 获取DateOnly的构造函数 + var constructor = dateOnlyType.GetConstructor(new[] { typeof(int), typeof(int), typeof(int) }); + if (constructor == null) + { + throw new InvalidOperationException("DateOnly constructor not found."); + } + + // 使用反射调用DateTime的属性 + var yearProperty = value.GetType().GetProperty("Year"); + var monthProperty = value.GetType().GetProperty("Month"); + var dayProperty = value.GetType().GetProperty("Day"); + + if (yearProperty == null || monthProperty == null || dayProperty == null) + { + throw new InvalidOperationException("DateTime properties not found."); + } + + int year = (int)yearProperty.GetValue(value); + int month = (int)monthProperty.GetValue(value); + int day = (int)dayProperty.GetValue(value); + + // 使用反射创建DateOnly实例 + return constructor.Invoke(new object[] { year, month, day }); + } + + + internal static void AddDiscrimator(Type type, ISugarQueryable queryable, string shortName = null) + { + var entityInfo = queryable.Context?.EntityMaintenance?.GetEntityInfoWithAttr(type); + if (entityInfo?.Discrimator.HasValue() == true) + { + Check.ExceptionEasy(!Regex.IsMatch(entityInfo.Discrimator, @"^(?:\w+:\w+)(?:,\w+:\w+)*$"), "The format should be type:cat for this type, and if there are multiple, it can be FieldName:cat,FieldName2:dog ", "格式错误应该是type:cat这种格式,如果是多个可以FieldName:cat,FieldName2:dog,不要有空格"); + var array = entityInfo.Discrimator.Split(','); + foreach (var disItem in array) + { + var name = disItem.Split(':').First(); + var value = disItem.Split(':').Last(); + queryable.Where(shortName + name, "=", value); + } + } + } + internal static string GetDiscrimator(EntityInfo entityInfo, ISqlBuilder builer) + { + List wheres = new List(); + if (entityInfo?.Discrimator.HasValue() == true) + { + Check.ExceptionEasy(!Regex.IsMatch(entityInfo.Discrimator, @"^(?:\w+:\w+)(?:,\w+:\w+)*$"), "The format should be type:cat for this type, and if there are multiple, it can be FieldName:cat,FieldName2:dog ", "格式错误应该是type:cat这种格式,如果是多个可以FieldName:cat,FieldName2:dog,不要有空格"); + var array = entityInfo.Discrimator.Split(','); + foreach (var disItem in array) + { + var name = disItem.Split(':').First(); + var value = disItem.Split(':').Last(); + wheres.Add($"{builer.GetTranslationColumnName(name)}={value.ToSqlValue()} "); + } + } + return string.Join(" AND ", wheres); + } + + internal static bool NoErrorParameter(string parameterName) + { + if (parameterName == null) + { + return false; + } + if (parameterName.Contains(' ')) + { + return false; + } + if (parameterName.Contains('(')) + { + return false; + } + if (parameterName.Contains('(')) + { + return false; + } + if (parameterName.Contains('.')) + { + return false; + } + return true; + } + + internal static ConnMoreSettings GetMoreSetting(ExpressionContext context) + { + return context?.SugarContext?.Context?.CurrentConnectionConfig?.MoreSettings ?? new ConnMoreSettings(); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/UtilRandom.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/UtilRandom.cs new file mode 100644 index 000000000..a45f14521 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/UtilRandom.cs @@ -0,0 +1,28 @@ +namespace SqlSugar +{ + public static class UtilRandom + { + public static Random Random = new Random(); + public static int GetRandomIndex(Dictionary pars) + { + int maxValue = 0; + foreach (var item in pars) + { + maxValue += item.Value; + } + var num = Random.Next(1, maxValue); + var result = 0; + var endValue = 0; + foreach (var item in pars) + { + var index = pars.ToList().IndexOf(item); + var beginValue = index == 0 ? 0 : pars[index - 1]; + endValue += item.Value; + result = item.Key; + if (num >= beginValue && num <= endValue) + break; + } + return result; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/ValidateExtensions.cs b/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/ValidateExtensions.cs new file mode 100644 index 000000000..eec763c2e --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/Utilities/ValidateExtensions.cs @@ -0,0 +1,186 @@ +using System.Text.RegularExpressions; +namespace SqlSugar +{ + internal static class ValidateExtensions + { + public static bool IsInRange(this int thisValue, int begin, int end) + { + return thisValue >= begin && thisValue <= end; + } + + public static bool IsInRange(this DateTime thisValue, DateTime begin, DateTime end) + { + return thisValue >= begin && thisValue <= end; + } + + public static bool IsIn(this T thisValue, params T[] values) + { + return values.Contains(thisValue); + } + + public static bool IsContainsIn(this string thisValue, params char[] inValues) + { + return inValues.Any(it => thisValue.Contains(it)); + } + public static bool IsContainsIn(this string thisValue, params string[] inValues) + { + return inValues.Any(it => thisValue.Contains(it)); + } + public static bool IsContainsStartWithIn(this string thisValue, params string[] inValues) + { + return inValues.Any(it => thisValue.StartsWith(it)); + } + + public static bool IsNullOrEmpty(this object thisValue) + { + if (thisValue == null || thisValue == DBNull.Value) return true; + return string.IsNullOrEmpty(thisValue.ToString()); + } + + public static bool IsNullOrEmpty(this Guid? thisValue) + { + if (thisValue == null) return true; + return thisValue == Guid.Empty; + } + + public static bool IsNullOrEmpty(this Guid thisValue) + { + return thisValue == Guid.Empty; + } + + public static bool IsNullOrEmpty(this IEnumerable thisValue) + { + if (thisValue?.Any() != true) return true; + return false; + } + + public static bool HasValue(this object thisValue) + { + if (thisValue == null || thisValue == DBNull.Value) return false; + return !string.IsNullOrEmpty(thisValue.ToString()); + } + + public static bool HasValue(this IEnumerable thisValue) + { + if (thisValue?.Any() != true) return false; + return true; + } + + public static bool IsValuable(this IEnumerable> thisValue) + { + if (thisValue?.Any() != true) return false; + return true; + } + + public static bool IsZero(this object thisValue) + { + return (thisValue == null || thisValue.ToString() == "0"); + } + + public static bool IsInt(this object thisValue) + { + if (thisValue == null) return false; + return Regex.IsMatch(thisValue.ToString(), @"^\d+$"); + } + + public static bool IsNoInt(this object thisValue) + { + if (thisValue == null) return true; + return !Regex.IsMatch(thisValue.ToString(), @"^\d+$"); + } + + public static bool IsMoney(this object thisValue) + { + if (thisValue == null) return false; + double outValue = 0; + return double.TryParse(thisValue.ToString(), out outValue); + } + public static bool IsGuid(this object thisValue) + { + if (thisValue == null) return false; + Guid outValue = Guid.Empty; + return Guid.TryParse(thisValue.ToString(), out outValue); + } + + public static bool IsDate(this object thisValue) + { + if (thisValue == null) return false; + DateTime outValue = DateTime.MinValue; + return DateTime.TryParse(thisValue.ToString(), out outValue); + } + + public static bool IsEamil(this object thisValue) + { + if (thisValue == null) return false; + return Regex.IsMatch(thisValue.ToString(), @"^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$"); + } + + public static bool IsMobile(this object thisValue) + { + if (thisValue == null) return false; + return Regex.IsMatch(thisValue.ToString(), @"^\d{11}$"); + } + + public static bool IsTelephone(this object thisValue) + { + if (thisValue == null) return false; + return System.Text.RegularExpressions.Regex.IsMatch(thisValue.ToString(), @"^(\(\d{3,4}\)|\d{3,4}-|\s)?\d{8}$"); + + } + + public static bool IsIDcard(this object thisValue) + { + if (thisValue == null) return false; + return System.Text.RegularExpressions.Regex.IsMatch(thisValue.ToString(), @"^(\d{15}$|^\d{18}$|^\d{17}(\d|X|x))$"); + } + + public static bool IsFax(this object thisValue) + { + if (thisValue == null) return false; + return System.Text.RegularExpressions.Regex.IsMatch(thisValue.ToString(), @"^[+]{0,1}(\d){1,3}[ ]?([-]?((\d)|[ ]){1,12})+$"); + } + + public static bool IsMatch(this object thisValue, string pattern) + { + if (thisValue == null) return false; + Regex reg = new Regex(pattern); + return reg.IsMatch(thisValue.ToString()); + } + public static bool IsAnonymousType(this Type type) + { + string typeName = type.Name; + return typeName.Contains("<>") && typeName.Contains("__") && typeName.Contains("AnonymousType"); + } + public static bool IsCollectionsList(this string thisValue) + { + return (thisValue + "").StartsWith("System.Collections.Generic.List") || (thisValue + "").StartsWith("System.Collections.Generic.IEnumerable"); + } + public static bool IsIterator(this Type type) + { + if (type.BaseType == null) + { + return false; + } + if (type.BaseType.IsGenericType) + { + return type.BaseType?.GetGenericTypeDefinition()?.FullName == "System.Linq.Enumerable+Iterator`1"; + } + return false; + } + public static bool IsStringArray(this string thisValue) + { + return (thisValue + "").IsMatch(@"System\.[a-z,A-Z,0-9]+?\[\]"); + } + public static bool IsEnumerable(this string thisValue) + { + return (thisValue + "").StartsWith("System.Linq.Enumerable"); + } + + public static Type StringType = typeof(string); + + public static bool IsClass(this Type thisValue) + { + return thisValue != StringType && thisValue.IsEntity() && thisValue != UtilConstants.ByteArrayType; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/dll/Dm.nuspec b/src/Admin/ThingsGateway.SqlSugar/Sugar/dll/Dm.nuspec new file mode 100644 index 000000000..22db9aaa3 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/dll/Dm.nuspec @@ -0,0 +1,22 @@ + + + + SqlSugarCore.Dm + 1.0 + sunkaixuan + Landa + http://www.apache.org/licenses/LICENSE-2.0.html + https://github.com/sunkaixuan/SqlSugar + https://secure.gravatar.com/avatar/a82c03402497b2e58fd65038a3699b30 + false + dm sqlsugar .net core + Copyright 2016 + Asp.net core orm + + + + + + + + \ No newline at end of file diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/dll/Kdbndp.nuspec b/src/Admin/ThingsGateway.SqlSugar/Sugar/dll/Kdbndp.nuspec new file mode 100644 index 000000000..487c66610 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/Sugar/dll/Kdbndp.nuspec @@ -0,0 +1,22 @@ + + + + SqlSugarCore.Kdbndp + 1.0 + sunkaixuan + Landa + http://www.apache.org/licenses/LICENSE-2.0.html + https://github.com/sunkaixuan/SqlSugar + https://secure.gravatar.com/avatar/a82c03402497b2e58fd65038a3699b30 + false + dm sqlsugar .net core + Copyright 2016 + Asp.net core orm + + + + + + + + \ No newline at end of file diff --git a/src/Admin/ThingsGateway.SqlSugar/Sugar/dll/NuGet.exe b/src/Admin/ThingsGateway.SqlSugar/Sugar/dll/NuGet.exe new file mode 100644 index 0000000000000000000000000000000000000000..6bb79fe5379d098fabcabf69f3e5c9e8214c229b GIT binary patch literal 3957976 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

u`~u-4R_QwH34fjNiJ#YXwiBMQTDPA`_yNKP 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

q?zIcAk{U|VH1Lr>$qLBDkJ%l8D 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%0Dr|q4 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®FbQ_ ztEK4YYR?5F()ZGI!=`Fu87w`9hJ&VQ&0)+jd>)d0YeF*;rkST~+qGyX`enKeBd+u@ z0K~3M^MMi`<`5!G=Mm@YG$%VMkXy*Hd)E8R@>jtI`Ro*h#F!(p7H6X1_E{9W&+`~F7Ab2qj+g@)z_!g{<^ZNK<0#(j z#V{r-cHh)wal?45?>P&T)oegkOVfAr^S}buOQv2kuvimZ$3q8erlOC7*z#G80W$ek z$>dOM=hQM~O9t9Gi*EJ1DhJ9poHL959&Lp&zdnxcyHR_8X0EL=+T)(lBLllE3gp zRp0Hpk}QwnNHu&=d6Z?u{Hz|S4w9Aji<4~#%-~ELi_MA;w`x!|Vl(dJZfk&@yf2u} z9&*fDAsdIp{^(d?`O$XDDigON6N*BC^;KgYDao{ZOJQ@*(u~I;yB8kW)>^c@zIkIv zj-|1q5v*8L6FWwW2C@dC3NTnURndxJVG%gnTKDhiW44X%K|N4%oDapuM+5o!v)YfZ z5Ji?K`VgOz16oAiAQ7^JKi7cU6(=uL(oQFSq%?_=R+lx-V>5g+V267vr5QO%djnyS zH5tm<;pS)IXya<1%me1Jl4Ng0W!hW_Xk23P^ePx&k>+hw#hGlzVqwmrWTgty!7ATg za6N;|Zj%+bdCt$EGVD}es%^c}WHR!|F*^k@K4ORqv-kv0u7RidTxwqB-4d(La5g_5 zuyrq?qLG4M_`vBO`a|_HS*agS0cF=dil7&pFP4`EK&@1eQ9a#x6rpg)**>}Ww0X)e?wzW%6raGx({u4 zLS01Ti5`IZh_QW&-2*&f3ldLYtz}pYN7IP6&#sZ9I!l!KaeSssip6SPtehcIWamdZPRKj&5JGKC#kE9^G^QrM!| zl_5Ctv|WiIUsWt4pX~VLgrMft5(nAHx^`b&|6P7Gg%N8f;T2mu>%}T$sYsnbA*Uzu zXzd6%=bid4WYS-aUJMV@t-N+pfX~>dm0U{!xOv6odYsu#)vjP@?4P%$EJ>0H2$(-i z?F+leC#wS$>$~gFL`J$FD-_8{TP!Ea#a3IdzSiY>q2@86cig&+SA8({h1X6Wz&^q5 zJi=}e9qOOjmu$UID|1G+zbAZz zK`A|nOjto$RsOn1aC_XNO1H`MbS~;S>14U=YA`NuNCCw1(B$#;XlToPf2LyVni=cQ zN6i4ZHl~x0ePKMy$iA>dE^j*(yGLUtG}8UEb={R@48ox+cr~uac-#!TB$lDIXa-r! zqkaAk+d!O?7LD_jd6#uF5An^*^XyuGX3Zx-9)F*;b>!vUJSnC_@R$53(^mV1n9cG~ z=Wq|gjp&@dQnf2NnJgwF!|tFBl+!IJo54z7vVb@D8@)&`gpaD9K3$?Ch)^Cdz#owb zBwK;for9F^t}z7P?7^J^X&<6B0os-ckcMb)4r=+-9TGifTbmjdOk$IsN|=Gg)=?Gw zBfr~b_I7ilN0F%_fTxZYk{yWmBAph!(;!qzPUpEyy3LvI_)^>s zo_iC-_GG^|+Z6gE70yrGQdFhn3_`9fkJ?;!@)6y^)jJcCJ#ZPqFk#`ES^kl+W3kR< z$cc7a){RcO&bw{b{a&Q+x!vz=^{x3q#-4PbmmSV`dH|I{h6T@rrN;eanH}RB?kgno z$O9C!i;k6bY~mWJ zKFcu(vqb^XNWJ?N{B=fSK53QB3BmNoDo^x2{<*$GeupSRUdE_SpO@}V`YB~)!@%mz z35Ycvm5LPvN99FJg(J%*1FQLnP{qmHgYuf+3tt#*?)EN9wgy3hPTftctFrc+=^$!5 zqGSMDzpyQ&ZrA%yH8A$MM#2gw?rW{4eXx#0k|xFSWD$>WSr)(KaR(X)q}k6my6(Ue zlyE+MYAvJTbrxPS5451Qmq2~3z4e-!ugTo&#lZ5mK6|@4(J#nDgtD}C(P`)&$5Ff4 zah@GToL|3w4ztsXw#P;&=OJGLPCll+0WQ;XD76Ve%`0Hvl^ta#Rq0#_nQz`5j_;sN zDbsTaH{^H7U^h=qj#EmyAab~`AHAjJ%8T>BvB^aa!Q+*{7OTvAV6Os}pKR$}?RYR`Dt=FN z#-lpQP-q7YgsaE})SZTsbLdQG?4gxxt%RYeq40Y7l%a3h0dIB2D$ZEN8Dlr(0wQU@ zv?p7yQMO(PbV5+`3a=pJ+WYh%T`Q!9kOVcaa0E!Vu2l z0EmlCkF`FxP)SnCLH3lC=hoG}fvz^W2?6dyC=0f*I^S%~oJA%2@jEH*{BjosD+m}j)$?RIRZ5z~Hn{kcgdQ0h87`*BE5|sqSV=C!r*#e4lxI%g zn$ii%oE9F8n{8gwQk=e<_W2656J>I849TwbH5Jg@qkhcl_#T|m&lk-gB7nt70<8CO8k#jEB;B^x81sMK(ux0 z^Wxdt?Gc^Ke0v`Jr6-ZI%nsWJ^zin^4sU{*m+{uRmXce~NTXIhKWKPpAJ{|YW|6T@ zMNsqVUh_3jZXeWx`zFIx#tUj*eg@zKsG^-}quct2q@n)y!9A4T+@U0>d37mKi(f;Y z5BK@2RB9?omc1y`-a-SRwW)RwSJG2e-5FO(t?R+5V~eoA-ns#&^lVj?lPiRX0T8=O zZFq3;A>;#tjg5mt3@`mnp)+%ai`g=cWN8YZElL1&rf+t|dYB*%&7C7_NHS0BNy zgg*3a0J_@;ptUq@tK$-QX8dF%>UF;rXlfQg``Z4fxSKkr+Elyoqwy&A6*JG_O{Q&P zr>w#qtW}bCqbxwwQ$1=7O7|obt=n4vn$l@%iYiqrOHX>ux-67!E$P!3L8m!|nlt|s zXTIj6XDu5&E7lo~&lP9N4drR$J*=lNFl#wy>2N}s0`G|%P~fewqB>cOy@o6cC#ohM z(o-kiqB`+fye0%SuW%Ab!^0$YIC&jDh4!I60d7$MYn_6cm$d+|DI<%w6wf1GQ>MfG z^d9Ct`4>L)#M9r?kQf(sr`&t0IbCKCL_>J8Cm8b!?ZbN5d~1hILCwq9 z9In^u&g$r5^{%y1XLUvvgXq;}7|%1VKY$)K|3cbHC@VN5W|*t!NwEKtyvtunpdfqz6yS&Xmlx`$;o_h&bk!Zlo9qQ zw;~BB6dgpvNKVHO_aSnh>F&eiK3i^d4+)W+D|aq0Dw!U_i!AS2P`6$K3kcWJMRcTi z?~(XeSYJu zMn)eeMfZ3HK)?RorWvOYRy$SWmZMFfjupzI;dH1Cmh8KdK7}|juMfrOMA$_jUrWz4 z`i0g_z4XtTM8Ext9^P;5@Ghu%8SfGl!^Aw}VScG8dK9YW?hlfe%7P<$$lN9})+q~W zUe?>G9+3AMK2-+!TQhCxVVMb@a65rPlzi++@Da@;XQ0!_Z4S)?=wc9=X6fh-H_m@o z@q(3^(y-~JW=F!!Dv&q+LRblLj04@_afp9pxktjJ?OB?`cx)b(!^{CpeH6<%`{nzh z;ch4QKE3+F2Shcad2}A$9E2jCV{(`|9CmCDGY2r8mAKE@i|&ilH&BsU$AOf`**aby z>1C*rg^wxTdibc&p?^Y7%N$N$*kjUxt+FR!e>*eWJTb>LhvS})5Xh58w_vAlyr?Az9VM z0dOg~7B{LZB3d5dCLP{v{(MBZCxAO|T(kN4Xstd0hz08?pL~6quQ+*#=lBnRteyt3 zIC)gx)?B$m0QXRTA^D*&ooSZ1;)+$?Cjl(OQa>HH*#5@|>Lk_Q!H zc_&129$w*K8itlS-1km0yk5Q=uDxDr5ms;f5Ghiv*gl!;&6h8qhgNz$AtnShuW%|2 z-HavWw~l0YK3BrW!S=%s<4HNRxQ=YH z#Shr8KQ3W*Lkz}ONk2t7T&Cp~_(g4ERPc<)M7(+qcIX@-T&TizS# zh*sqNz3#(%H)M#0aovhWgDTdvm6b3;PhGHLLUQ<=U>C{T-3G_t$7kj*7$0vWcgD5#SO}kmhxAzaGsGUvc29od=5Kn!Wd6MJ`dKAZ7%F$5aFq;mQ(a2rcE^YJl!5L)UJJAF~!cPNjf52MNzH%})b zjP{adrDti}JWT)$;~z)5&48@VfTZ~Cp#H<>RDbJARjuA84Xd^-g^kmjA=VOICuU0e z$xLmlEtUkNl+41dLm=H&vGC{XX%fa8X|^^0p3&TsyxMnkX2a7;0TzLkQtO}3CQ=&* z%t2iAspsS{a{$wxpV|^THZ{Kh$Qs4Bpki}~#-{h?lh!&eHI(p8JWYF=)?!)_D@!vo zsFcuYJ8!ZaRGk^#ZrJ8zY%sECe70#Un5FWPUvw4})3QA*CpvU{qR7;DHB|XkZ>Hzo z6I-sx3ql^>GTiJUb?wG=2k{d!?qH-h!`slzsEl z=|@p3Va9q6Xm)%d%oIJVSLbG^myq*0YBBR?f2J6k>LDb9tJs-ZHX1o&5T1f)G6ogK zuj#xHelkn2lY+ltJ+rRdj4-nlskigl(csqJ&ZP%^Jn3bI!n{RfA!UN?t%1g2m_Rea zdtsup)98jH*l8@SM3SqF%h6VX<|8^9P8nLoQB>!3miGpzGE>nxiFuGlL)b^R1$<;_ z*>p-Fd;>-%vtui*jGenS$`1W?m-9O}$Wo8RGxAsYq7i?^)lun8J}vKC1jY|{J=sjT zr!)*iXMwq!r(u3i8mu5-z8Jm;_x0*x4YzYn4`1R-R}y44+{g$_SnlxCJk##(aL6uh zA z4<2rx9-fUh466Ur{0cdd+Oq1M+U?h1rdqm=k$=jlJ$)Kk1+!3^v*x7m^(=kfAC&D* zBX$JxIZs!Pl=X{m?&%l3r$gML*bri6_uW|+g2f8VA64C$VafUrtES3JZ$HbT!2_bf zm++~4)Z9eBeCRks6Grthh4!l%0`djO4r&GOpa3TXHLq|csD29l-X7*!7u(38bUo9! zX$}U5nu4@1>7llxsJ&Cv1U0X~UMyD=?eaxnuEE*S+MpktZw!ld-OQPr3H>_qN=~^8+=n+aQIS z*SqzK?W{)YcDYffdXL_Ht(E2#Hw6yw<M;71LI}7n%LIgGgQ}hfnf;oL*0vqkKRgi&ovqMxvb@(RyDo z+TD#O#;fklJqqDoz!wF+Z7iqXh|n9z>tWiT567!G2<)d&sO3_!+>YLrJB=jkaeZZ* z^)xJm+rC9NNyIbRVKF)v+H9WJ(hmYn`4Z}hD`#ET0K^|7XOKd$Fz9ObQP^_7*EDSk?u2K_#NXmmd}cY7e@3HYVQTplV$^+yIjE%Tc6|I$^6gr zk$!BkLQeD&l*d!>%tv!l#-ul@>QO*Rk8|&&#;{E z?C~ixhl-7-=q0T9Y~(G5MJh=NYgkpO*$h>DvM%`uXZuwjRwYW1t@vaD^lH4TtLN~c z6=+^`k5P1$>S`rD!?C4r);|wKLo=dLFRwnk*Na`&T0Gly5w(y}KAIk4X|(Z!j(bMq zHy!uP#>fYAoLP-6I_~t0ttE;3E~m)y{L%Qe5JuRYt;MeIs5oBI2{YO_sN-Hw_`^KG zD88t(M#Jd#U| zlFD#a<7im0wboVi>9#&G2lVLIuFhfRaM)`F<80^acqbRI5>R?ohs%tThbXP-H;AP5 z8vOm(V|s-RG;!-52AWLr&?egSbow{eE#YM9PfK)5ar^bW@}cnz4LDChY(h};8VxI8 zzIC^TEYftPMym^9B5m+!i6`haL219ChtQcK^t1>GYF^bf32>GId`kfY zZR-(QqIzu)(%C}#wvYrhFBySekQqVnwq1?pkS}bBN{9G0uqz$n_ly0tvkCR#_W@5| z2hw1r-1>l!`i#X&xv3y%E1yj@OPA3UTlWI!TQZBxuXGfOU#skpZ?$Cpaj~+dbd`YL zfz5EaQgB^Q3eHgqo>2+}HLoxsr0aW-&K1&kg(RqXg)5ZID|uM4RpRyyJps;B04p{@ z%_}Rm4=PEpbhTKTTx|LLvCL_B?3j>W;Wdh6GuHUkxP4;}{a1?qvtme4^D2ecf;_-9 z;&436;dKgn63%R#(%2-;BsPL->FbHz#z?qf%jZL4in{a$F~zbLYa~}0v5eN$e6(-s ziT_oKe@}9GLQwMxuLr3USIO@6VdJKjUX#UjzsL1J7T2|kOIcL6`i#)s`F?|w$>c%R z2nlJ#&W-BMbp%n}c@PrlpFgBmU+ZDLnDczt-hHi)@WxOx9GXSs^aetyGJOP#t*;uBjuR(5tWY zNoA6k6&2=9$|cpqN!870UF=9$S3Qjsr~fIW>&c+>2vR~lIknLK87d}m5L=$U^k+&0 zX}m3N-`tbeT&7q`e~#aTpym~BP+m)(`!M)Ni{CeA{C-LtHmxsRQ+kUS{DtDakpST) z9xUU231Iaw0q1+fHo&ZzWosFe`3{zc>Zqt54oZKm_}#~D?TpT zm)Q#LUn`2^;d8Ns@c@1!%pu%Z$hMESjo^-`s2D2|hsAz*(xzoho zDPFR{O#X%Pn$o)j`?L6ai?Ze|Jz3LG*8Byp2|>*(+#;k~dXQ2f{Z&YUnpb!$NP`t? znM3`>L{kE@*RJuKwXB)1;H^GLd*y1e%zw--lUaFGo5haR-=NxA@Pemr;pgGaEJp8| zxYw<9x#+M-%zc2YgklbG3<4-6>XL1!9UHw~?M=r~?~o%u1410yAXQx27hwNB`TJVD z=zA=eosY-Ow~~17n*T3>nY$Gu?c}mo%^cgj*jxJteoHj|pIKVi3{4G!|=(re8+#pCb7-kxZkhINxs6Wrr%8F21cZ zUCe|wUHiZ+ajqa1W^rx3`daTGu<}ynM>$OZ?byj+PWw)IvunBf(QH+^&Y}aJi)#&3C6qKL>|Bdt?Z=!RU2QQzWDI47RU%Gv0`;R5Y1|qc=eS z5$&ERS+FFQ;cN`v5VUA>2m> zh1{psiOYPP2o=G3?u+)rU%z#%UUv;ZVhdmk_I%bc06D25u&{gstXjYL#72lsu0Dlu zjRFz>QrbfDIzF=AVtBi`(JID0^bhD;M+VZBduz^CXnmPZuTpG1$;)%6(lk(0kmMOM z3O!!4hMAeM>JK+SHsEq2s+7~~&cVuBX|$KhHq3=hP=v>fUXR-$lzYtV^-v7CM<`N}O%d2)bNvR6k(4{J zl{XYyc|)<4ji}CKx%nH*B{m6kt$GGBE29Rg#2nL7j``5T{P(?-fZ0_khUfL^Sroh;ytjIw^L)|ZW)RXdA~>Od{MlVF-xuA&H8pMO=a zVxQSrZeiVn*^Ig`Q~8LS>IO?qfw``!k6bcCUs>Dn28L`1HI1gomBaOC!G%}h9B?QU(K4du-x1Lf~6yc7mFh zu4e46?KX|Bg10k>!^@#)HZGC#Y4}n(^{bpTo|czrltVM-cA^Vafz<^S2hn*N;A^NK zX*ym=CaugR!*MbklNbcSD*(!!K<&yeA?T7;`WAwe< z&DfQG*0G#Ug}oKo2-IT&G&z#+r92L+275>sR^rzz3a`^<@XX_MV@R3NDDZrK`=V&t zZqb%sMHVR?5F`Zl>m5OI_jl+deK$QH2L__Klktnmw?WDRc(>{yeBAIoar98=RsTWF zMn8&0R~2jT`eETVcJ(sX)5zQbpS~eAfO=w!bR3!AUpCWFPY?YbLqaW9^`<@>ufmop_%wo*y@!&Nx?~O+5cM5FHXgT{mu}$W=0=h%|w;A!zbcqY(#G$D=dr+M9nwE4+H)U8c zUAYU0vyhoYRhcq#`7ETkiM91d=y2SM%&oTmplCY7=I8Rr2FjJDygY|8gVK*7lA=LP zzWG3HK90)KK}SULWvx{RnncRC;vt5EL*N$Z`G z**rq0sTMaLqezlhaXLGzh_;ZWmzwi}q)r${cd4rQl~Ijna^xKB-DbJ*#bwb2jX$V# zm)cIc_q9VdR-iysYYcKZx)(;#j114Sc;*+w^I}{rzgjP)KR}4hdCRi8M1B>&MLT&8 z@StsK%;yNXe7H6d=7v@WVOe&SlOKWg?4*-yy;ttl?Gmyn%M^N1G;6=8!km*vzbIOC zyyvT|DDH8NyM=Y}#Zh)AAMVRs@8Q1oojsk&J@B5+fRPD7%`2U$Pc`_l{mJ_~gu$i| z6AsJ!S?#;z#d^!_)#Yna-Sf04eLpx8hA_O{Ce}8@N50J)?#G+e-h+Deg@^URve!rK z-4`CQcPTu|+u8VM{-^Gq-5Iccarh8EW<_T1UM!k0EAs#VUygY1ZUDou z-o1{6&wNaqYPP*mI%8bv2D4K|P8jv6hal$)+ZI)JVUe)iDriTW&qJUsndwK)aGEGe zHfnK%8s#h}wz4$4F=~KGI+N8Nn`KHx0rUTA%gfSC<9?JxF)ZxAoDF~Pb0T*O4OiiKq$;RVi&MBw%8%cFRE3i7dLJ&z-&UQ{%pYwZlBBY zCbz@;YT{wO{G^QnaqF*iuFU=Zu2*ONq3xne#7{kZU5RqLzT7w^liFW%g3f!<_HHj- z53W$Z3TiIw628i(9=>D*uz^+NCWU{~hxA3K6Xy!i8TB|77q)i7lOA1`R|AoblQetd zGhGCA?wYinh)(y>cVvh)=g3Gd-W(!a zr)|4{jSh;P2<_6FTFMaS=8?5$(Mxv`3U;Mu#koZL)HB%YK{@&4icP z)V#ubg!E7k($hlPOh|&7S6B%W^RQK#XUNL{?T33peM_O{D3qY)72Zpz@Oy-3owhqQ z>Zi`MRcF?lq7U~_{I)306-7bYdW1W~>_>W#z9Xc~g(RqXg*%1x(H^8{gtUc_1U0X4 zmyjOmLHe$cwiJ?}<|Rw#3)&y+L3&n5TM0=}^9uKX6yC=J1&m#(?#Vp--C4@2sQNf> zO@%e3!>Qk0W9aIKy-p0;9UhlYZci)lE;Ur2N5bt?{A`M3@{+9Tmy>r<^EnntU3Mg{ zK{icb>*JbM?JJICEt3&Jl`-RX5B5FAwQ)76(aV(6Xg;P&|L1lvCg5!nmi5}c!tN$9 zzYkaU+77HfR|&|v1>DtV3z)RRw3g|#$R6b;C7B`CZ1R3}36=E6ny#(h0i=YMr5qtrl~ zvzfzTAJ1Xt0H%E{b)Z+MU-}`rtF`I(cvDw?pclkcSMz!1vGJ;*Ql_vMehk8$`qcdq z=v|*HoxAN#$TOZ#Hd~J?O5%xa9K2HTd@UPU2i5VdDScgC%nWpE(w{&uU*m=MtC9WG zAWC0TgpwapQ3}I_SR=y3r{ozZOg0p8Ht^L=nm}c^rq}=MgnN zaW#rMJ6d~YLC4!7ON5c-@sUQ*mOvI}LagC8tNmGX%xZte-dZ=x&em1LGnc_)VlKkINPFt&Qq?k2Um+qV zwTe}0k6rF$a|kEgR98jSkEj~Yg6WcR?f?17h#6r&yjGU2Tgmlj2BNcx1AVD=~WbS!QvpqXcYzZVh|e*^87BgKUah1*i7yr0=6zD%DU z17-bxSbGmJ$%-oO`|jGOPn|k- zih0ZkVE)-0IA^3-FO7BjZTGV7Mun5Ltqgz-ZN=fFc|-$6`&Dz0Ik4|#FmnKt{6hUm ze?DEM6?|k~!OQJ$MirFpZ?=FU*y`jKW~#U1Hp~rUC$C!?*4aO<(ki4yr|%9c6~eQ$7<5++h>)pZ!f49tDO#8z8O1cmBB|Q zboka)j<{Uk8C;EB_@Nnh)u-Ipjn5$Ox{Vol%%Ece)_1oM;}7^I|&21j^g_EGbkA|-$ zy)Ar)IN;7ua?PoeuIsm#*1!UZc)KYRMVIAdU z59F$yL)ySrlRa^F@i1)I3kRRQ`6P#dms=Ky!8#WD8<`VU?-*;hMwH8d`=Ui#JFg6X zTF9w>xFYE+XiKs}O|GZ;L&#W8g?<){9%@ULGH2_{$q}T!zLuO15vOvP1Y4N)^|na7 z{bhN%1}=KS)-81M<-nd8DZ^cqm2K_aeCkIFaF17v`f=Vi$B07l;o(ixr_WvHOrC*_ zEG1=_eE9Gw!k;aMA3XXfT_qk1l#&f$jjUmy6c4r>6Vy`kJ(6NBvkb;6hWf zDZXm|3H`}&0OnW2W{}fmV)qF(FPkUpo%yxS$pk7z^CZ zY5uejZEHn)hoT8;LE(!;Yd%?sw2dMiuSkMgP`FQ#epZOItsv+$Rfx2m zBE3_Q1ht@WKas)%{J^eyMOTv(iNf2NC-E75nP#%RV)8VcvTOmi4@(Bzo;(0wiA}ZV zX;!xk_){V@e_qIMd*x@fAgBddEi|7lMA|`-P8LyuT2T11h%$lbPuRI9zaopCHmQSb zLF7Wfa~2?l4$UX(r_#xi{{EG;Oa0dbOGja4?Q<$)`h+dPV1TsMeo-K0r<`6voBDBj ze_4pMvmzy;SJ0+@!dIY|jnr?*uYL+a;Xwe+X9}ryQL1+-m7o?BzN%76R?}HEUCL@= zHTbJSvR!kE1a0caDSEaLX*Wf(8WgmtAFIK^^gg-QOC{p?Kl+&ZEB5VArt!GMv8Cku z>ACObow#Fe-`X9=8If3ht5fD#4bw0}K7zpuehtzG4~~;>f+l&8pV8^J(@%5^J{Ud1 z{@O~yNl!i^cFap6Xj4Do>tLymQYqqZJ8g~veMIs)z!3>H`-Zd=a=_?5?Wx?t`l5Bf z`l*)O=tMSf8a|(s%sILUs!~%={Wh;^3fEtpoKDo_41Q9lvaFxUkMS=P%C@*wwu9rT zsL=Zdg}OMb7{36wvE+4h7_yIv#i_qrd2fhx<*ez=LWX<62;R{pRhj@{&z5PBDy$j{i*HQwogv6ioBkB!F3I z=+o9{Dmm?1GrfkZ)$dBlIRKMenI}^1gViJE65IIFvD8mbkH?fg85a^PlgfQAKc{M) zB96{X&lB*y{NR_2K=*V%uJL|8Fpc;3@kMFm{d{zk{QCVb6Q1rGrTY+J zN_Qb&(p|(y=`QA5A3!1zzFA1uZtpl;YvnQexC_>zxs*Ia+*6;@@*0n+Mk=i;t$!H5 z=g*i@IZyqxE+j(#Msc_VfASHFp>oBM#LU-`4`(5l0v2hP@y+Kt)@0!*g$-D`K$7Cj zc|XZ1_5;T!z)?yr7mB4?TamL|fhQALYBsM`-LSWd{l1oq{fx_1DUvyi-|O3LqnyTd zI&}2JUsqSO!+e@hd!JMAe^T{x+Rs%DmEj`*R7}J$tUatdPkWlc&dTt%K&p1>4qj@1 zN-`{7rRH+o*8FlgJ-KlB=Ha8C1lkuqrSkh*Y}g9@4ea*9UIp_j-zo6KPkzj#v!in$>w2}PUyVFbR1P#grRXK2bD%}%sqePxPp&8Q)U6oTC};^~{RVzy zeVA#WBiRdXarAbpT^-6LHvvR%>(dJRjNHj*`HlP2OwvO8n$lr+k#101kBNq1buS3H zS&3CuOIFTmCBRj8Afx}BkfEWX(E2TS!nYVO;bDH@>cY3>`3^s-s_*LS^{aVgE0A*_ zpXDV{AKK2KkO`$P@O2YPe)afP{8XU@SCZTC$(mTW9f!u7=%2e`kKI@0%%8o{9YUZb zFkDObuhUUG&g#z^;Z6cOhqRGGE$S{jn4-U%Pbs;FU#y|aQ#KC_lF9vg%EOYRbMYj( zmq3Proqr`?#53GZNadK`wxx_2u}@2-`vvOz2wZs0(&^?~*1r>t=EgKDQo;o4yzJW1 z$(Kfo6kX~~I%3_m{w0z()Wqy6)S0Xe<}01{6tah)zj(>c*(9Gd)Z`24^#$0zg56K@ zn-tIItEWsAIZhq`-25~BIpcIs*Sh{?pyPsCPJt)Ojn%n(r2y@sf^9&mwesswe+_kfcJlwoK>5QldIx2hepLu{T~w zaUpLqr*z_xhlCFaDS60!Vd|DlZGB)UYV_2XrE|i1;uXt{K}`+egT!QNcuiA-F1ZD} z*5-Y`Ekbe|p^xFZ&PbU|*Li&sqByjG4+s6GtE_%o4mU|9EmOLkMr>2-L0RWWrW;o) zVv~9WIod45B&~S}OP=D&k>}OMgnEL)H33dbS45jd=z*}tmc#i}1l>*PDuWpj>`WP% z<#0I}WLoR40>G$8{83Qj%M|B}Z(JVh_31}0R<+38zC%V-Dr?tyV9l-+pBjx1V zs(l*T^{B!71*wcm~9)_nlQ-nh_s+yVqVi25I))Fc|UG+ebO{j=+ClSm2!Ey z>{<^p^riZL84;aJcLYI~vkWZ_T3Cis$|%%Q4O-I*EiB$nWU3^S>cq0k*4##yk7jP+ zIeD1EMS~lUspwHzI3!`JwIXpX0LFGpF1w;fq<$xCo zZIN&>5cY^{Yoo!6AL7g4oo&mFd~=md52C(@cFd-xQLzj&ez(ic5768wl zwY6$R^ZXJ!vKB)ck7>FdUB+~r=)@XkD9y(D)n8JziJU%HoA@hfvK8Z_l&1bQtr&tf z^%K6Qm0tZ=bL#ETSnsr1qukh3vqs$5#6FcVd98*lpk%Gfw0Wg)ejS|Q`(R?q?$QMN z4FP@tp!rfE|Bor|H!W0S#n}Y&boU&PgLP8<)KalxystK^B?5KT@Zn zDi){@%}^iu9udNi0Yh#|uipo_M}4HoNU(ma=stkXB_P-S(&eC3c-wo4SWxw`5uIG- z5pryHwZI|_4w$?~=Um5@;?m^Vy#9Ttg;3-wKaflMAgu|%{-|JE#>rzU6YRa&;YOE3 z{!BGMX_qJ--_(|=se?*1_!+o6ZgwE*E( zcmI@6xRD<#6FlsHX$L#cGY9yw#rO)a)E49D{5vpuqVqlh)gGjIHA`a-hb_xs=5W|+ z8O$8Ou!Hj-VC&dKJ%4mlaL3_hktDTlXID?tPVWkXa z4q&=Fcnho0z4W2c9mzS|N5-zkC}T4xW}y5SfAp|hOFn7D;Zwx5)lce;7b~i?o3M%1 zg3lWLd<%X<3qE`F%Psh4fTvY!oL!33^dqH6{JV?_Crr9jwLA0NZppFo&hKh1xT?@G z)ly9M=|=sS5a%^ir|qL}Rw5OAbl+BTMVh>{huiX$?B7Xwc$U&BGj*-e(H0WLcN)0K z-_ihO+gk`QvW$0VZ>Y5P>NmPGv_D_{`B*3=8WG`seuj0D z=p&(&XgoMn%VHcf4oZoB9cmgXL8G9Q=+~f9NwjvpG#=LWqh#klOl!MoHy@AG1hcav z(_R!kWqY^sKH9%@+NBfj4SWf#S`RHP34Pt{wX^*%5x>4Wwr<%i+K|pH1B>j;En+jb zNGuu)PVq5+>ukyfme)_oWJd(2^Ya_)(LYE9=XxepdOZ@wnkKC@kGa?(e=~jjewESt z__IoKG;7FNrNABf1*yH(@Gw*&?v|eF=*{m-XN$Dgk4Pref|@=iCi@h(8os~~tAvM0 z-qqJ#N}gZxMN+y;feHid2 z<7_89DK@cuM@MVDKP%ss(J?nT-zmA1_Lg6!d~tFUU6rNk4mGxKvvlrNF+j#wmdyUz zR}{b5MlZBYmF-t@Igi1u|8REUT_@|Stn1c;$JBwR(1zBT>6PU9pwTl$KkW!lQM1VE zt47(kXh5FuY6n}kLVO2M{U=oExS$pkeomUMTC<}N?YoNhQ$-Whf|{L$@JAH>q{0QY zAiZs!p4l8+D15U*gs{}YMn|u^JX?edB@O$CH-}=~!4iM=Rh4hjf`GzizJl9iTS~Ny zCqIXX%A}P5xpo$0^YEr|_#yzFD<(1-!5^~wH$L$f`Zh<)cctt3xLU2n*NlsL*(B%<@dbg{HMJU)^5%(6CUQrRbFV~HIeQ<5zHMtN{L6HSQ!L>_-GcHpsC9DPmo z;Fx#Mlg8l}R3=umxN<;>`Y$O#_$8pu`ZGAYctL?r_t1q9im>BZJX*4nCyAUqCtsV5 zHYS7T<QLb#iQ47cQUL(c}hW*9*-Wa z=5GPQbC#6;=y%+|PCFnUon_L6xUq%yXQjql^-1~v`X|A|K4WYp^%wE^<{(dCGcuSt z9M+S;%;B(^8O$6Gn=Y96hD-5(13LVgnr(R`GWC98pyGBmyNO?&87F^KRrivn z6#j&>%L8i3pYha8*EbH&LG%1t@)tm6ek(l%x#0*_dmi8WHNm|Idqo14CMp&`d4bqP z8&i)mATr)yck)A_89U6$@ziee6c}(zsW^Gs+~G7n$uIbclNZ5ctY}9m<*jW#9clxM z?U#~glqSdT_KMJqrVcb5?Um+Ic%E{@ha}GuxiV-}BSSTBX0K@Hxc*l%Yp5h~cnPQO ze*TT`xa!O97fW{WE#Xf74m^8r;vcxN_L=;XZ+c^RiuWcoC-`|!HCjEjK|hy$^G?7Y z1}AJ3g<249XQ>Cxzl78t*sUfna^>!;o8$#!Ilzx>o{kBSo)wAvN zT~=8&ONqQnAPj%JS0ZYqpUWD#HtD~saweN0FQHrZhriGooy*IUxPt;)qr279SI+3~ zv^Ou%KSn08Ih66F^O2~Gb=KdLyf|`O-?}N2`zvJbd5&e$272Rg_)1yUjb9)LkqSNgDdHXE6DpQ3VxCl0T z8j9%seE~MS<3@R_VJXh=3x39x%+iki60f!J1s;H$4@ zZGlQ+aF^}ZBj@|r)iCa-t?Q!tYmhPCrv`h1<1ykl2JoaZJSwG&@m?_OO>QboX%=nvL?`KvIf65ot>Yr zYfrENg;}Ed@f0XE)JS5b;kvdZXprDN`WYB3FI!oT{0@HE>i6*p>Dx&^Kb=pzqT|5- zL>``W5J;bFu;z6Ry_ql`SI3PpZV_DLdaq^glj2&CYcD^xLJfJkwmh`_+QQz&`ao~A z{Xsq_9~O_P+DtRtok=G&4F0D%8*s*>t)x2d0zNLN1=YLdRYj+&+iNclpGF0GT#drJ zH}xuMbO+AHB3#m3gWp*)*+ki76a9lOqgZ*+{kkEeXdGit7ZH^I*5)bzGCF!i$4bgr zPz$Qh05(2H$l(cPqtJ!;$r=oa!DxFr_RYxD?Na#4MiTQtNAx{#qh*&KD-LTI_+|Z; zy|%uN>DfWO&KOJdp6P7^VRx28jy1J}#DIYHhdm5tx9jOtI!UM)DOvnfIYQ~Q{m=2v zSGLT<9if9EonH`dLu5L=oL+(QxzS^_=kq7)OIvCa$sVl73+GgnS+R^zaW+F<}*tUY=s02En!0-M2(1TLy$ zmCdC8(elsPO!AmGxs-7*da1bg_c2izrv8ql3k{(i2Wf_hoAby!nZ*^Tm9vX`sRB8X ziJ(5AF`&OAj&Qwkz45Xxe$>L@OZFh2`Du2a&$3$~j#XF9bk9pBU;C+WjNV7OfzU&h z`-7tcGot{jZPpc-hpDecVJ__1xTs~0Dw!VelS(_bIv`#Yz`WBSs&iaW3u=f?pK40QyEJQ+Fuh^JfEj2l;$-#4 zUTtl8wSO}Q^Z>Icf@N_t7~gfW_~!8V^D~$^9JX!-Gl#=M1~Uh+ottX{%AW6fXf5^2 z)#7mh=@0E@$LeX-?Xt>dDsC^80%L;3P5Juph3}Fm+^L@pfyH5wLY}}6tMNl2R6jeI z`EL^im%^L?UEU4!L zF^^llg;+xp)c1FI`HfO4ckPc(ztGoY?U}B;&fT0X+5jlkr@Q&s*|1fxmREVOHz*+luJ)qz?xtV+1#kLm`BV)Ku5wrdLAf**kDC%Vf+ z*I%DLh-7`Cx}wgJwgQ~|m)w;$^S_+>85fGrT<*5@pHaSHYm&fZ35BT&ZG*qQ8g!(* z!i#SWuW&=FKlvT=N~f`_^V8@_Cvlg}vHiTw)75CvCecc)l+z9#{SNc0XIQ+Sq&ZN! z0Nli2{uDWN6-8y6BOcF%e^=HIvB)$-tg<>j z&s7sEIRJX{d%%~b6P$|)51iQ&K-T9`w~f`+5lH*g3gV?&n}4SgvpKO`U!#?h^}0ka zPX(5hyIav5^_=>PtSV>e-kGv0C2M+yxqq!m{KgqxDB!8IXUvH9saFTz4 z`!hPTZ%*I(9*&J_qraRiGyppK@+XTt*laduX|u2m(y+|`JFVu_HT{1B^?$7y=5QD@ zxWmi=4E=G{Eu-vkdL#F;5A#l$!y9iR2^!rSDW&(FT}h|Ecuc@}OfigQdut4|4`$iY zSZMNPu+Lk2@yd$7j)4XaG-Po&Mp?z-SbbbB5}V!j=WVRk?dHb-s|+*9@S6IawVCu! zD|0r}WLcTLnR;_&waqH}%$vzOT&~|C89w{(2<84Bli+j=eFE=tWa(j{#l|mBAgq#n z0GdbC+xtiFew?Q!j}V0eerWBUYB(C$u#rs{g6-UM{=Mt%)WlowxSJw@u91=joO{ zOD6_5{V(lG1K~vK2A#xH^eKfC{BtrNU!Uolo2oadZ<>STz}}L<%;B)13}z08p%CwB z%>k@;!_}gG#OapjykEkQD2Z(v=1C8Qhb<5B z)~dJ(j8&NJ^8J@%VH4&#^QI*|oz9h(Lz|d+>p7OUrC2h-qsA9?%wdVwojHil+$y^a z>D<~fUqQRop)A*1nlB?_X9|K|X{n4wGWX0yQ=3zZ=y%jTSD;PRPv=I=Brdn<@2hVx zQ=-)Q{C3p_k~cxJ-cMi|=|)WYURW37J=)C*QM;ei@a)(S@lDY|J5#&K>qpodk)CJG zv&+1oG9eR}J~<{5`{Yt4XWsywz+isbVG~b-R=@*IxDmW`8#3>$`Yv}Si=NpBqxwF!10sCe4^zi(Ol0yi_xqeqp3jZ{SBpTYfTg zuN8gd?kvu#(ajILn|U>Q=*egs@f&lRo2ePCEp%rg8;gtT4H{_N0>!Iqf2u#8+@{WG z(ent#h~#_pLG@p)nf8m#vwmR?($H77$YADh*p?a09Kf=D-tRT`#Q5KTS$o%=eAb{@K#*FlD}izFxk6nUuDe z?;E9k!P4$HWxj&GUcPAL(nhVc#e7$kmbWd+^`B!ax@;CI!-v&rXm{jS)K7Y z#DTiccLrhlo4~Ry%2~FQ)oZQn5_3aPW z=ra($!6#o|cA_e5eK800(4TkCVCHbxE*Z=m4%;<@nFAR6nr(q)o8q&Sk=8B&RTtpi zElXn#hwYxh%;B&-GMG7lagV95zdyM_ICZBfb4wPeh2*blvv8S;7_Q*grrtgr>>`lz zB{wP|@idM%()-0o{XU(-*h}H3qEbLFb2TUC(MyK&T4D{u`$YpY+8np%gSg@Lc0SXHLXcI{LfSfz@cD*X0c>TL(QXKO&|oV;NKz#Vpu= zl{|&zlfTq$)hSLWqw0G(Rmem=3HCOOD)iwj&znVJWmuw#I4p;N)aS? zc^PSYz0IPr_YcbDRe)T=Y5z`+2imi@#Tir&kXYxw_wcPHZ)S<`TO)w(1(a5I@-eo6 z8LA~YDoDyInfPDK=}^0$10Dag`uzy-)6PdyNX-s{M*d1@NhX8aJ*6U%7wNh9}`MB_=yejr6%f!4GXc}kfn7xfy*_h4UZT*S4wa3<2 zd)e9pm%nK{S(cwxayjfq&Pd@!#$=JYdfbG~zK{GnmA%n}jwI8D`*D{ucQ1EVrF8ml zwMAOswAl5ZFn;^IS=-9>U)d1%KA8MoLpvSH=|02z)_<%l>h^ixdL-`8RpmZ67ZyyL)h{z)Cj zdXe`_WQahrdGuIkWYYVK;$9eUZ=p{SfjKof7mVR^Mi%cSjfquAp`N75ffRp-x&{}YN;KgUtzeG^XoL4kCpBL4Ni)K`7&ZPdX^ zjlFaXTyE^G52wQW=o>fo7Xov$e+Rr#F{hx5iv+{qD1o zXGeeO@Pmn3!iX!Zo$wQT(gBReFb9O7FF3^U-Qd=7$?rna$lpraT{6xE#Cc8Q0&>3# zyc9M?C;^i|yfU3+!u)uGwnW+dxKcEIRi7X2O@Y~KLCd!d^VV_r4i0g8hC}0HO&BiW zm%A4o!DIRAFB2m*_Ep!dV+pk*{8@?Xo5}e{b8ar@3+CKH&cB*-OF92$PF=$ckGGsZ zY&n06lT6e7%6?h>n1lSOv;8xeIUIIC1~Z4l4$NTYaM(c^%p49oID?tPVTWWeb2#kK z3}z08y)A>8!(oSIFmpKU?HSA*4m&)9nZsd6WH56$?8ppe4u>6;!OY>XqcfN}9Cl0w zGl#>D&0ywm*l`)m91eR&1~Z4lj?ZA`Fj!ck7GrPvgudISw)cO7C%x`Zn}t85asJ}p zu@f>p<^a#!4Y!TXM4k|}YtDZ}sR#Ip!=L2*6AZ%0o`gE4<~vEAF%&(T8tu}XpcWJs zO9k#|C7NrAlx_I4{C3oQ9Yfnst>f^01B+kK9yuKu|`i}qZ3z*8l5GkAk!doXWZMGt zc|o^p0g^+RJCf~Nfbghd?2rS(f92m1{}dC2^HH})_n$B=JwKQcolN~rqfP3_(Kyj&Tx*z8h^We8NzPVbik?>VEF+SI%t0_r1l=_`kZ-KM)I71! zS7wr|zP6Z8K`kg8MYl*b=F$IXo>Yk5n@1Paf-HI`x`oikTec?`V)x~-1+^fHJxIKy z5N~D0+l69}3u-~(FA$-4?<&L_$ngnkL5@#034clXN-AIasGo+;V5vD;NIy&IZD0s$ zK{haw3s8#cKZzbp|i5JYdBi_ZN7Ht9o<o3N$#KWHpcWKfLbukQ z2I?F-ouXiN5L&z7f6FYzBS)!TYolnb`fErI-OZsFPlUe3q1R7@wl)XPOsJm;Rg>TsIw9pL|afMCM|oUGqm9;V+~WCVN=|0+q^t_!RlB* z=nL(Dcy-UiLPfrGudsJ?B<)ZUP9c-hQTQGs5x2CU!VlcNr`(UpZF65G*-HS%@d95l zD#_kCfcIeK{;{XrSMHy>dq25<=I;IF{<*skkb9}S50v|t?mkHFU%C5Wxu0|QA#(rP z-EWio_wGJI?*GVLO1_M{zANL?_?C*zss36=(5mKuFrBJMi=kP+Vz;rwi}dg~Xt}8* zhY9Cj0hE&aaN4~=;_z8g6&o63is$kx?}{-+te({MCNxn0|8kF`bRCYP8XCHom%SKU>rIvURw1T|102 zOQ1pDKYA)_S+WP2&M#F)pT{$h>?sne2^$+kJYg}}cDL8yym45*6K+Ih`3^^YyJh*# zj9#TI?Ut_={rX;D414p_F|-vS?BMtDJf!*EMPY~d)B+*VG}Y1 z!F~iSyw|UUM9cawM`GdU20Zlz;mtD(EwUPT>-&>nTu=)Nf1@#)?=D1|r$`4VlAsn8 z{!XOiKw#ZNI=^gB4#I8sVh+X;4&kTWweO!dA}1V(X#JWd$r;v2XTBwx&Je>s!8*}8 zJ9|CJ0^GjGL@F=m3Ez$8zu<7mA+7_~q48L5i^ukbp0Fna}^?jTzT+jR5H(U z_NWs6MT)d9OHV`xKAmdsVGd2Ac-BQQXPAcTpLgiBm4SI1@RO`Vi8`wnA3w0Aw0i5T z8k{Xv!^>+{HPIayQ1}TR3meIWl>yUMCTihHg)aDw%>W8T|1+}1oi!2G3 zo6y#|QErsZU#2G72M%P2@Lc@O`S`9y@KDiowP76Ir`_TX5?UE$B_DqAR0FPD^AoY< z`h2<;EQ-2vxo9Pl9C9Vqd#kLgXJu`kT`0|(D$TWGmjtySvrLt@;q-`QI+`I|$?=*T zD*h~J0=t$S&PrFyNbv4O9D@kiUd?DV9@Kgk_kXLnH*^mC={4LJM^Zs4(?) zTaWMe^d{m+QJuOXR7!QH+pTDxQfTgeHMbbGaX~F8x9M?0oV=3;_6Kj0lW?XlWKqM? z$3N7Il@?=ok5-9RSb4*Aqcr|#?Vij=xNGa$+&;Qy4$bQ@Ys>6=sTbBY_Ds>&lRgYf zJClUubcge;lwp(S+teY!2sh3a zG>!l+gQG|CtsljY-Rnx{HGg|;rp6t&lKun1^<#))3652QuOwbJrPJ2lnQW~zey-EDi0KDA+VO1qS=JE#&aY^7@~ zlh}f0vry~tK;j9ei*iNKRfo-#i*v=_n+bG8-Xj4Cqty;B1 z#kM`e21FSS!dxA==)p=9%WOgahvjP9v%mgkkiSPB)H7Za9n~J}8JIKg7Zu|_255)U zyuoBCW$dV|yjUEY*9&(strok6771pJP)SaL`tJ6X7fS~x?i%Vd%Jc@o~@ zVV#nG+UJNcOLi; zWc3y`_A9eTF&<|`w)u!hkH=$4XGEpap5|>)xmODDLM(jT?>G2(+A>Zn-aDk}oo@?0 z`iqA**RLTO*m6U;Q_n%BYG{Sttbcj1*+`yLHG=!QELuk)hR98@*(e=oC!w<5_ZdCWx!n zPsZQfUgCH*!9@Yoe%K+9gui5)ePEIqeUd!qSYK4oVlj*8Q|u|e&CFduu3x6DhEu?V zL=UdJ!Y%(};R(zxM)%MfBx-l77~J${8=6~Vu9@wbLjsetT01JAu*-XRvqM z5f6KLs@W_osuD11>vG7=qK!|^_BI`MA7L)Ek3Pr~l;|+)CZgv(N`{@)zEJ1uPtzx- z;&hHc!?bFCIDhZgTbrDD6>!xzCO_^2sF`(-+e8@KQ60%gfB-N##)Z^Xa+w26m|-!l zaDWLjb^uLsr2|ZuA;3DOZpcNRR+Ote%AtImx*2K#lP?4zwb9>~+~(0t{IQi#f7}5k z12Mq04lrSf0Y2dXNk)=Sxm%KvZgPN$KCB$KIKbp32KbBvOlV?&n;mcn zAII?p1B7iw+FkD6UhaF`y|vt5GIwHP6|2CD1V}WNvUb=$`Vh0-sFwU+4O zZS9FI0bpWF0GQYk09w_omH;rhC5lnh5=5EU5~+VuOAu{hO8}VI5&$N)1c1CHaOW+7 zdtyrfnAj2kCbk5Ci7f$OVoTJxWRSH4?z|;%=Pf~^i7ipXBmx_(@QNgvUr}XP)5zYc zty?9#kG7iHrZ0R1p2}?<`lSa|5OagA30hBNX5G>~%%uTvHP2&G%uZ+ag0}i;jK^_7 zEht1K6UDe*wAS5j;`s%n`v~coLMmueKcOU~RYR&e`AzIM{_5{G!kX_ZB;Qxb&r)(h zEhv9pwLj^(mv1=T@2RM)d#{Z#ARzSU*M*&l*)@53_6fNL5hGg=*Jb|?A}?~KTFL%-F{`zvxdrn4 zmB38yy*~xxNFb%J%idgA1N!X2&^T#Ad|_Sqs?2@K9Gaxlv^|T2-Sny^R98pVXLS#4 zAvCQiJfB-DI%R6w9gpA6H)N2J*VA`!uSNR$IZ(jbq(vNyr~ESgZ(8&yB)OC39+ouC zW17GD!9vR%K&<*|k_8KDLE#yOOODgf+y|Nz!f~Ve(-1hry;M@Ac|if)fkHQ~h8EO< zLQTC%9fe8vJp`TQw1nM=YwP4(^vu2vzO~ilU zGx$aeT98_Oz6Gr?soGucG;JCG$o8#pr7EEHD+O%H(n`547oqB&v)4n>Kt(||R>ZQl z1YlEPK@gi_@z*T<%sXx0T67E<&sUj;O#pTp<4Z4dXgaPWzfp$F^|N5SQNsa}uO;WWTLQM^J#rW3JIOH?2^hr>RS!OY>XOEZ``9Cld-Gl#=2&tT?o*cBPf z9KhIjv6DH8b{UkLeV4nXca7Tg$qMM*a8A@DuKp(Iu(zIFOm*00QZ~J=SPlo17U{~N z`m}{3_2HD{FnyaJf{t*4oN+jaj~UJ~t0Jp!)ukJ*;gAW?0~;5L@~qEvMw%V<=SC?o z5p8HniI5E>M>5`A%x!o|iLm*$K9?`Mjm`DxBc;>sVTZ_Qu7MCml!VCC#RMChqn^R& z`j<*GMd?A>rxlkYABugCL*aX5lBad;cQb=9iI*HL_vqxr#ojNHe% z`#8DZ;qJG~eYm@ims_>w<8ifFXYcrTR9Clp> zGl#=Ik-^O2uuo<%b2#i%8O$6GyFP=N!(lgMFmpKUV}j|e8c8%3sSZmG!DtQRp%G0) zrPfgl$4<%nX@StsXdlvy+HWUF;X*3O<}B*r#w;Imcs@5}FmnKtEke@7$$Y;oO}bBK zam?ZAK9j-B;jqtUFmpKU<_u;IhkY)CnZseXWH56$?DHAS91iLcoFl9+8^<8=&>bu?R@{UwYz2-@m%>u1M*>6+;2H?;JPDbowOqVyNU;nNcnJ*UlH z3jF~=cCY_l9JXN;?Z?L94E%og+?@4p7BjKw*dPygX86)eobuPlVeS=Sqc|9@_0TdG zuFS7}Rnfm-Qn+p^6vNj~{UeSKD?kekLcK~E9vcIzM(;*u@h0kpd-S}VrFI-Xj4a)G z+Sbgu_Jq6pPnAD886?BQiWKgK>^OXu-_%ysnwIuTp;;5mocf+dVAbUvJCo^1C*VHL zpZk&5V{I`!M)fG+I{ zMzw1RT|?UfPj6tx;Y*~7!-w^Ym5Kd0j4s^9>}jjN2T0dc_bmsyAqT;xozB)8s-w6a z*=s|^WNlRO!#(^A`c~ks)>c4=9Qbgh;+YS?Kf=%J6yyXw5ds`LaGA>#|r8gjNp9Wv$oXfadno# zTa!WG5PV4vK7+_?*xK1_bwj7i0rXzr0wU>8R~6K~(pM^Wqn`I#sw1QM<-Wa^T8ur@ zQ{mjk(d^k#J3V+BV~B$w$tBvI`6v2>K31|WmacEfhp8jtR_*O&VUCka0L9I(6l(ZK zs^O2Qh6S~tFdfR94;CW*SdlJOBtb1GtVE>xDsb~LIG9p;L=!{@z_s7ZQKpFQ>IZc8+Cz$T#vbBxgO#eX-Z%jGb<&uh>yO!-1dM^7e@zO~YZSwkY zTTgCKPVb{`A8J$e(Xp?S!}5231%16csc)H-wsf`mx~pi>(l;a+v0Zhn$AU!_zdr1ML&` zT5o(`c$;%FBQR6m7k-H$z&`As`jo?8^kF;pqP}r>fsdKFX?$B5_lN%gu=RsIFud1y zf!e;-9F!l}BN@yb4*OmPGY2s42|o-hyC*F3euhUt;Rnz6voz-LG~X(u$?p~mn5Owb zmc|^8=g|yi4q)tIb&vQp6-E2m%q<-pjqSIp4Z~km16Bcc1IaEEuS`QA3e{!eh{nvv z_S(_eq+4w1RAuSJ6L{ni^X>d-SNCrMk}txL>He={_FSrQ_?u<+55DMfwcF|xY+ zp68z+Ze)h}Ei?ItPr;wIlLn$%FkFUXWF7%M*b@B~9Azp&mEsER*ufwfs+v2Tqcm;K zlb3O)7Ei>Jr$Yjsyr*pX`z0@{L|AO))Sp+%Mfoq%Tsa) zFn{CSGS0w<0`|l`9nD#ss3ZpgA@^%l^{40015}swKalH!SCi{RTj$V5qO>!7LcX+Z zd%9fcs{B&BqAF+xK3Ld0!zW3x@LR5C-np#*%Q@=yT&4kDWkcXqy$3)t}m1vu57!*4a)Xr&$fG6|5x&Cm)a~cre)BsS!8@tdD2@yPPk_xY2aJ`V+jDALVu+-y-~E{6_$P zY!ZAs(xYJ#+>fpWFy(-i|+FfT6;d!A8`n&!v9TtI27RR)CagEZicb?E}~wxM?EN_E{OMh14gx zyl(cKl2n`7!{>yTiG4bxO~Tw$o|QvexYJob=Nqba$8LMIk-gJZPCji7qSo*0sBgQC zDb?6;EQggfNA~m6I2Lup>X?4bs#?jV6xcZ5js*43fC4GSXZg&xy&y77F(tl&lQ-)a zEDz#o<{QcOGq?7N2a)p*w#G=VI;BnJ4BM`h{(m>aP;N^)Eyrv>)MK*x9kp)wC^BxT zqs1DDX6K%qA4+X66nUMEq77Jkb^=Fzdx|by;CE0fT&O=zpD}q%?!;>2>)UrB+9zdt zTa})c_Hg4{(xpF5mo5EsAiKS2^TTR0xst)XWO1fKHo96bch#6n?3T(BQ@-KOLC2{a zC{GJKS;e{jRXokU#7Np-=SnlV)-hGY{w0s<*u9ndW?FLKB6u;L`9OZuQ@&Adcm+qoSe4M8S|KLfx#1|rwAEcPdsC*Pr-MTIvbr+!NKbq6 zQ8irG(CYMl)}braa04`LFJ>@oZR++VQfndM#wq{Q<#t`B5CM=j;Z3hjM>TzjI1x*Z zQ1!9~X<1Gwv$c7p!V)zj&16P$gz!&y{NHf=(;feG$M0h=MdTJiykGw?@7KLOJw5d7 zo}LL^yQjwtFTlwfgBeY;v;6Jijn9lt{uf7=#5+FeZt1-+O+-6uTqHQv$9|GN*yA`^-?;HkKJ+9DTvpjtIi)r}EW((gyO=KYDx0+Ij(<1d-z~*|iV&6? z@6reSqx!~;gb(;Fa9L&d0)C4yrs%FPF#&&wZf2p~9sKEAx?*3Kx0BJOs(H^v2R>&vw%u!YGF~Vc0E*ZXFnQQ8C6NzcwvMyqUGMud41O2HV*{aD4GPwlj zT@neVHge=-e5H1I2$R?@4U=<1m_%G@n4A;BBz8-~bug@{*~ja6g6$un zE^Z4Z*#6NRCta_-G*<6r5R$pjh+HeVA4ewF!gl?F!4BjFx!l?Jo?4A0@2WpwsbpK2OVhZ0S?C6#r09}^8CY?X zucUD^JCb{=j>eWkRY8`_z>^(GL6LFpsSS8#inj*#nUvOKoNtg;^7}RE(_v8K%IrJU z55gIk{NFjB=&hLqGSvq;i<-{NS2unJsqvV82a;EifnvA(DLmmt zM%_pUxWWs#?7%G^QwZ$E`s8TO2PAtb?-U+>zLp5_zA!ub1!?!Saz0I};U`4an4HOj z)Fgs_bAJ^#$B^d!8t$&D9b$~AcR1c;9~Dyk@W@&Ks|Bd*j@W@}W%N4rA3f>J^Eji_ zQ}Qcz%gwv0%=pQ(IN>?++|z`1A-E9vZX#DLa?LYDfdP73*zO@h%_1PI<@TTIUFF3- zwyK87!(q99Pxqgjg{!{2woA!>s8_o0x*jFU0szD9{V|2jz9qy zORNEtmnFmgI<@ExcN%qOTSi-e8j9|T(laWfd2st;;)En%+&&JQkmcyT)M>IBSb-_; zBQ0m;{iO1?-B}@d9d@|VP>@HUs|2uc_?rT+k%I@4RdRP)uhI<= zIu%m+ufumg`cyf2h_t8P3zI0>u4ql}6OWg6j{9*dO*Sv7Vfu)%`|Qw<=kT=&U+UKB)m2 z)@J5u3!Ad^1P+%M43?X}P(|u&lcU=mR4bcEZB|eV3NN5|K#)XRErqy_(Z7Sd!5?o?%X>uV0FsyZnW{B(QfIsk50e0d`er` zP8F-vg+jF_>rjYZ&Oub|J>~j$y$)1wvTXv-9UVYD)b~hvFbwackw;Z$$$XM0kC3Rd z9ByLDFhe9=LtBxqz9(tA+bfMzNz-04vm}kva95Y8t-~60&q;f|(`f3#ZmqD_Q@$bJ z>-8_$f(=w5=J%kWr@Z8h<j~4Kexyz%fg{EQ7Q4+2mgU;*S*qqJ)QbV`^D?E>#L-% z@13^m8KlYD^-SFVH|?r?-=JMyw_TO*8>Igq+BGboNyN5Ze7s$sM#g-D`e<);1#R;3 zlx@;C*u$NbVw-5)TrfqO%mk(}(rS~HqC1FtP;1WFh(YUfjv}$P?88LUvGgLffF_B~ zs$Dc#@gz}2NNf|oFx70Jvj-QX-G+I`(yhSNTlO8z7h6oYBqQlU@Nee4R1IFH$1^o} zRTY!PQ;>0%&&}FB@ptmc(N>63ruA+(H~K#1JGfPzee?225G&nRJs=j2Am2fo0ZYwa z7N*Y~p|k!dBXV3&3kvIN;OxM(;ZtU*`Ai|fPI&@BEvS{ClXKV&bx5%$d0TEgIyQMC zHq?HjR973jpl%^Kj))oeG;Pga6>{8JIX)&51+}2CC?m0jr`wuKo3GImr~_-T$rCzZ z<5^gB(qvDVCh&4=0ta#_kiwJ>yRVxjhb&+JAcA0$1 z6B9F0z>WIB`2Uh0t}rb8h@iAk?LF=JQq&&M)L9VTq@RuW>Fwz0FkwQury~(M(|6o! zpx2+WEv!l`Je`AENy&%568{&_RywrE)*ZbmbUpsJSzlm^_npSR~t@6-;3H8<=YoBTF*fZaYP&e_j0mxPbO)+)}i?4AUed!&C4ES+#f zLjVFLrT_ST( zcRJUSy{4nUw}O`iU9iEJkG{wr-rl)en;kq8bd~9Uf2(5K%gxyg3I{oM*Ws=UI@#mR+z}XwD@cz56<*YK9QscuMXhA+7%6=5Gngwv7ji4iPsls0B6DtK#PG3b78!s2vy7 zf*O{AtF>DFYC+Zi{;B?FsqshQDL4Ma2RJkdfL{4ejYi}g3MnN|nwPdNM^`wBQS;A) zWF#J?%zq{l1+}2&Ukc$zEBq;i3u;02w*wphy}U0JVjh!Y64Zj4FBZa&%`pjTL5}GU zcqu|E6X?u!f_>_60dQ(#WTvXvx+F+a)Y4>gm1jveOft=|{zUAXMqfu7LA@z`G>Ir%xY()?SYgbkJOY1NgW z78EvNS)p(mYseL%V>EWXNCV+a;-}ZBP<7#4%Z+Y%S!$8&xE#BD{8E4N3q?)K?D3A9 zBHqA+colme{2dmrKg(Jo6_0J5{6ga39~wH%Hnl zq@|hDl3#&gxD8)*0HN)2X;1jy(n{c)r#&pK`)5HNkG%wS$#XadPv;dZh-U1cBHG`| z+7qB}feUkJ&q?j5@u2nNoua|;lz9L8c@eCwHune4V~3H|amCK$Pt4<%)3_!cql?HK0jnA~RndGvkv=hrY(Y#tgmL$+slk!=folE5^r(`9IC-Em$S zhpI9L5D!ur%76JwwU_yRP(%V~UDO5=G28s#>ot)Ovoqssm* zo;*8?>EFtp({Wgf4kNp-tNwOC-R;%Jd%@IRC`IFZWmB`}Y+qJ6JWJVC^Ac$CX&+N0 zGX~*KKWuN6<@9LY6*f&FhpI+MwWCqWGNI}2ZM9TNt+y1N?g%Szk*4>Fro1#qW;A8B zDw?Weiexmob23d^M)Th0H1VEg-e{tEF0$YU_|`>ZD+m*KBBT;FW(apxYi1m?@qTha z9~2Os{b*6LiIh`NY*~wv{<5T>@{6WCbg=icl@d zNLca$tCg!SxV;EKClK%H)cC+1%lDh9EDlTy>PM$UE1eUq1P!>ijt=^`_o+DQiazy) ztbaL=e5%T8hT@YyGv3nozWbJYGrXrKs-4dCE}7zQ!F!tZ%XmzE40CcEIO$d0I9yB* zXLf%J3&OA#kZSY4#K;(mO9`$2g?z>ZwV*IW)UKh!iCd`m?)ouQYj3&TDqcs>R^8tG zOyrjdkt&_$SGD()msO5cL%aa;Ff5eI*H;`4XWFQRH<6(EYJsfFg{=ygy*j8=n4Gd?e<1-XIp*9wt7sz@)X zWdv>NCu~kurgcI_n!7b`j*-vllgG$$Q;d-h7RSgtCXA70goV(oOI)dp|E7!uwV<$t zGCqWbqkH)-jx5~UqboZ6#8zJx|9%E^m24W;9`9J{Vfo}%XV#w8sH-MzpieK+|1 z)y*fE_*{hoEr!vq`^u-F#1MxKAlZ}{-ee9lL^27-Su4OX>wm)07Htn5W8kPCPm^FX zM#e{NX5vi+Gt1bxfQD=ShxUg>I6>`}n>V&XR!e3ZIAgq=A8n5of%k0H-)UcCuMgw8 zIsh9Hl|&=Lp0`f@>e=Q`%f?zSb~x#6KI2$v9nur)CPCDn19+^zLf{^JOj9~r`p zipBRTeYMY$T(SWT>AB&e#o^OP3&u76X4zGCRrVq(%B&$*21e`L@^SaL3M{-T&*?DX zdA*dZC2h42A9uQrR5%2elFj&?5Ckt@Gd-}T>caMD2=uQd9qaKlI(Q6g7tRyw5X)By zpKzSYmwk8%pzXuWVdWevXE1X(Oo=J8ta3-uIpl?%=~1;PdQunDfcf0OI;U)HRCv9G zYm_5rwBc&cTKAR(3y-nfD2~oW_vSkeUVle64xc1KYPDD^Ei=6!$-n6R_VP+{0g2}W zzDZztMpr?!?wFI8i8kNg5g~)D&xm&Y!MgU@8Nu{Q&|`q2!Q*uJV(oG4N>LhbdNp9Z znwTDMy6J=)t`^7LXs6)1mvt*EYs$^FZhob*ht@NYXkKX$kB-nmTpB?Y-AN`f#{;+P zTMqY{x6)`6=C*L3y!ErF_eRz7sx~V2tp(j#!pPwlh>@M!cM@c6YYwuay}L4)Ie_gQ zhtHA5jDE%8e(hk{Ra)HcEUq~mHa&xx!(k#$b}U4SzAI&M%;9lnWH568qo1e<$*B}Z z`DxBt$oDe_tTCjHSS$QaL69~!fsS!!IWo*6ej z!Ush@`I7r7ER!oSqd{vrhgXwNgDyFoP;`2weq9X@;bBfX8)Wr=)1ks!nV11I1o)2t zivcuyC`PupJWP`MD+G-TYC&NuBEiUR&BrXc#2jgfUsx*!Ovzu@g1oqrBTO(W7CVA!@;S;|O+L$S_%hjL z^*9%MF%ND&ueu z=&@p=IvEqPB$~l%bzYlsa*qf;0D_yn1(Lp#lO(7Gg>9jRE_#l-C~Qkd*bV?(-FFo? z7GQe-wnr%8>unoa6++>5%kouFBv0CK@xfYlhmlPn%xA~Kr+l+8@y+S!q7y5gB#p%YixMoPIgQWVfdKzM*wF|%Ovdpgp{$%SCF0}$|Z6KUaX`R%CJ z@@q=dISR|m$^hx@Ub-?_$Eb1G44;yC5+pv|)6JT*n80Y4XpNNrjHACt8?HUg_FJ6v z#|wtyJ|->nGG3c@$icB)M~XS={Ovih@G`efH~GW)Hn-jZTEAv8qLnor#{KmlQ5+jh zL-vw^P0x((Gw4d$L|`JMc&vj~bHje-^obe?(sT;!dQ1hlRq{F=$w~w=r+mhFqMBwtJ~&zOOb=aZThJ zy1+u=ipc(2dZ$m!RoJhpDIMn1b7{h)Ob*Uh%#6lyf57PZJjhZ#Xe+Yy{cJguxYO0F zQzI4gX+5`^HPFzyVV50|N6DQowRN?m^{r$S+}^0k4B7?8L!B8r6~MzqYcVs59@$0= zbgU0TPyJj{>+a4+nVsT#52+gJ2XBbGGNu$ZD|QYY4B1ti6}voPt>2c9k*x@D4x+ow zSb2`y#7~Tbpr_O8(bw4oJMnt7b;#y`Fs52`KcJ}f znG|DOPzwq>F$slT*n{_$k)kk>y7j4I65KP=$c5R z;odGf@s4cY*BPnrU7+^0@imZ~rfMzO98+>0Y%vtng2IuKUr2?fcuT?gW%gTr)K-4}nw`V;Zpd54l;J}4uko~hT|sZDjOjO7 zSyi06`*G7C@0rjb&He&W-xpEekXodm78G_!#lmImzFW&Hg|CVV(0`LY*Aa)u@m_d5YbVDrK+G9_K%4sccpBg85W6sa z;U`KSp_A-;o^*@zpIZtWnx{MpH&_Qz7cpC4bF;FV1G*^xDjCciz_yIT&q%7v6Nk`0 zt^PL${$VS_&k54G$81_@xpR+Tp_OC~fz0T>XmMKcAVo^&wHeW=;JPHoHL^S2!pT73 z=#}YE6xba?o4z{ib$9T>)alU#O_}MFV}Dp)@Dm#urJ1o2X-iWrJC`td(E8EpyZXGK zvlQQuhF_5*WnF3V{&Fs5X{<^mr9_&4HTm0$+sz&Gz2tIjk=Yd~Y&Y4eKBsb)!msry zhnMtWQ~Vcw2ZC7ixV{Qmhd)_JTVtNuNn?mMTRm$tb5Mc6*2rMy05%wpDGg&FZ~ry3 zIOZS@d(hv3f2uv`T3K9kcphtKFmpI;oeX9Uht1Dm<^ZNKrG@4rKBitFPsF*lG1JzQ zdniDLwc^BMs_RlES(U=WP`sdkb_D=?8P8eST%!9F8xU}*^(7%NO+YHMh+eE(Jf`;I zJWe?KwaZpL?jDCflBhJ`{MTyAkG}uB!ScS}WAfI^ERSai8VEh`6n?d-IOC^t^}1w~ zt#hvu-REjSoBAot)y`j07++($qpP6wa`MU67;VXXjrl9XaQPg4X-O9v6Cj$nGF@6; z%F`5=mXyj+pmJGGrJzmy6sRm%^=;LAyPaWQQK*wP>cly$0`HiXZSIDPqMhMoK5Fq* z<;gtiy2uZuOprBwvO0ksLq}2iO0ouSv;ZepJWL?BoYo|eE6P?TyldfYu2(48YkA28 zwV*=D{5Eqsi(E0qEMlF;sOcVh2h_A1HPWkha%SZH!0sK-EIUCMn0AWFPSIe2BF<^k z9wBH`KLv`|?&(fkdJ1%jku=fZv2|GI66rU_@2nS{cAd{`58NL5&#kKr>YniIoJPa-VI$WrcC=MO` zCMTjzm5%WXyje%($Qy6!sK{yB9xjhth6AuepDaMdl9h?v<`F>g#6^c6AGHM|osj z<==c$0srcTpH?0h)Pe$I1k}xq3encc$q>|nT4cbCc7nJK{z#n$5~LNHm9ITolR*7t z)hIJvk3g$8Q`)9pZTT2d-F)o+A+0(s_BUDl zUC6oG7o1F#`unVUU1<$N{HD8hH=!Qgp0+w@;2?WDtno25fxk?$Nuf_|p+04AcnjLp zPgqQg&_wT76WO!cKi27Z+Lw42MBTz(CoDX9m}=1V51y4y=NIN2F&*UkwJ4e?2wDFS zZ}SQDj}KS@{OL0*s6BnzxXNgi^<2pKpvcgvww+}g54;iYor}7&_=tH}dVx$4ZU9+{Y^@p0wctY^r`{zDV9x3NKfr({u-|1V;@ zxEzNC5ES-iQfO{Ucs5gSsXDfEc|k46-&xoEv>oX`B|0y3IvdxBurd95qjNmo;g*&6 z0r8**Sx!)Q(Io1!xxW;NgSuXLFHZ4ZYk2jWPS9yX-IzC2sri-y)o&Hm>uS;y)Plmk zP(ZF9R<7^V;61?xullm?KW_|AWY%%1l)20U0UB|QtN{vGo;{65X^2FtP$(JHHP`6`yPkf zj#**+xlNxNp^;tn3`#U?ac!J*m^`-RgN6aTpR&^8(SH%fUO4)04i&0vv8u}klAsn8 z7W>p@ede&VubszZl`ifu%c;ygO{u;%=kOEuR|{(nEk%2gk^bW5a3Pnil#2;~1hpVP z4LN|6&5=UPtrc@UHJ_js6b^LsM$fZx-VM}DadY!R;%$}Kcnd)-$c8Tuc|sm--F!3f zQaH#FzElba<0`19-QHraobwV-5F_y@_g|z5%?;2WlG3B{O6GDC?NDw#I1~@+M%ST4 za@B;L{y*y8J3NjeYaeZQ&-4UIwq?m9S+XqwmbMYe7?za`7?TXk5^RDo-X(d#fC&XN znBZXsky#j%b6PMa=bV!XOUC46la^)4IS283-%}lGW;CC_-+k`=Q&n%} z3^>sr#lp!-5_alyy&b{#b`njxPK{gJK^JxeG}?=YCUAsjIh3I2SZdn1n8-Vow+?k9 za~da4c}7*A!ob)`pYu$3J4e0*Wh`c~621cddFT)$ckUw1JcQPO8&z3Wli7jXCq zS%1J?9p79Vl*2rM<~8u+?E=M!<(ee?k%V1AsKcX^bKnm~I&Pls7t)rLP#^(xJnO9w z;Qy7xEc*)ZHg-qLO1&e$a>RJ}kj0pN#mx{z;vLAg%*SY(Phm&&g`~J1@@Kj(qGrE_ zba6FSzvR@%vWv&@;oDiPmE;nO#j!BDe?mwbQZWyY{b4&?uZ-IRTVpKgA%kG$ix&m* zwkHqw3#J?6i0a<(Szj@t)&@ zvq|@uz}zb7;%_`*i3!s#ZXp+2S2K^!-C8OWlxK7cah^^+iR{=@d>|>}lGfq<7!oXR zGlog#0ma1nB4)8pyhil>S?lWr!G8dPi&6fKIWn07Z`4Big7PR`DZhKfe6=NBdDX;e z!7(H4m+r<}8<5Q#Y4Hl>RtyMqPYGz~R0c%O`AoyO|5}6z+dNN4lTnw56)r~mW9$7F z(R8KX5+$w*<}VX@%K48XaXPm=trznT-V4wbHLHLhg4xn z?rDl6a74N>j`F+$rNSY1Q@qSe_Qf0H;TG33*ql-pl^Z)rSRCeXJ1+MdAPk6(w`UL@ zy?Yx425H5#zXsrV9e| zI>UOsnXlNtMmPL&043lc6Ujm5FmI==9;EvXaFaU|G2`0RB~10XTOkcFOkwMc)ppVn z>M^KMli;Vw^kyQSOnyZyyXKv8^~u35id!r-NqA3OMk*PpP5is&=>Ia#nd?dzn>zH7 zzEpIl-2ctpcK(Szp23c8ryGZK_K<#$z3ICN$p6eDO~|U)+c^aN~uW_`&^OH_JC(xCLKK`w z{W1j$!g&kBxe$5?{jEAc6Lf$sqjnh>?FCtHj<{xd#6A7q3P@8t;{-|0v$@t}=(-iU z{Anhh>Gq4<+--D49aj8LN!=s)y!t8<>YsqMA3iX}Rd?3A=yMb2kic;dDiG^7=7+gh z807v;H0#o?uy#<_aw$f;LS3?sAf%iMZdsNvr1aQS&Knd%Sgxr+}ChjPfH}lv<#8j5NYsSo1oNT1) zz98tFOscSC6%t+6nBH?_7uPrB_Y37o6_)M<_!XtbM4yek1DH78-biOX5R+3uwx52o zQXWFOE7bO|85g*pW{L)7yV2@Ky?7^d*^mc34VSW}H| zo@6}_@fVOAQ8uq+VTl^T1m1K`sIRaY1vBL{*uv^)OgJMxkH|Br;QG3qNhvOn=I z;&zqb<`nLNH@3e1fwhzkf3BuK+}iK0#@PzYzy-KF7F(6OnXl(=smp|(0(?};TbMq2 zZ~Z-#bC6$mCs3Os7T*3qCY-n1(7XKP)|;3DuC$5}q3+Oc3BM!UW3qO3KXnB+zOmoP0;xCp=#@PT z;aHf54~b9YK-4L2Y`~KZ5hhhwg7EMmPMB^nYc|T&1N29_g|^3)#c*`Xw6=9C43jD>qcU<@Cv&%yUEIuYaxk4tV;D#kmUl4q{>2bFghE?Wh*V*D zhd>CG&7qXt+>kvq32ei~k@Q#z`}VhiM`~OAFb`DngRxjJ zrmD1auN>}~kw5>iZZjGywKDp}rX6#7>D+eO6Wu1N>GXGd-(Q47>@zCDG1F8+rMVqM z0`fEI{;w7CY$Y@5L$;T44|I!n7y#+dnX~(*slPkb+I@`wXC#z zIh*?&M#*v`fp1?7JKh;cbPXid247AKddEVqzYuwM^+KeSCx2dS>h^kGjPF=1M4o?$ zVJL24Bw~q)cqRhUPpYuIc}QtYR&8mB&8OJk5aWJQg;m_j;GabPj^vXnEHBRtGu03} znL;~Jh*V*Dhf!#nA#@6bcBT-i!tx3bs!RCyiY)?K`X({FZ>4sDB<8EomT|IE_R@j6 zE2!8RmeS|x_!5Nr90}8RECg}5PVOgmdsn4RCqq+$t&AiAA;;y(jmgGx)Q?8MApJ+M zS9wD3u+>XLAkG1C3E!JI971}&sU6BmDTWl8-9Vq-I|3yCVy`uV=YcEsLSx8dTd5)I zZ>IN-+SUWst7qx8THdX)29z&i{pF~S5U=!7j1H_iTfGlUOG5FUri|<_mlfirU|Q18 z>_L9yoGJer8mzG>KSR(b%)T)mv)ug%7BWve)>Js(jvGliM)c-^{D0#?pnvF4t)J}? zcV<2UeYxwTJ+ce;pfy1^`Cfivh5Ps!aIa@`#JhmpJoyqE?*xWQ_pM^q>d^Bj_1xg= zxnJvfKr6U0Qb2~G0Pd)RXJ;9f$Dl)>d`Sbs?|>Q)Ijx7;==!Bx11;NoqU!8&@vT7Jb~{yA-}2lxLH?9^w>bDaAjjz=LP!EUZ287V)xykUcHh6v)k4ctF2 z`aC*wl5d~|nYY4C|D{%|^8kVe4#AbGyS; zKdHj%mw-w5bL(I`3-^SG4%Qo7Gkp%>D^7=av$urnEuElcH$X@iKaIY*%up-zcG9^b ze$s9~MMWNwWIDGMw+Ds$8T-XB-8&bd$o4~naMVEzG44pw^gC5FRVd-x(404cW{4a)}Sw}LI1i2ed>LK>%X!F{lgmc z&ie<~vu6$ZSvBa3YtXkjad7=V*N`9oz~J(`)u5kLgMMQT`fD}llO7!0-`O?j$JU_V zUxWUB4SL%{gZn$72K~|+c;&pbT1{&i~5 z52-=FyaxTl8uZRb2KRSX4f@qJ=-=0%PkD53{iPc8+iTFjuR-s8Y;gVi)SzEkgZ^y| z`uKkhuK(y7^!sbjt;Yw~vsn##u?GF_8uXuQ(7T_g;cpH4X*KAN*Pth#tf9XK{pK3< z*i(b+nO=i_LJj(@HRxZ~ps)P&;QsDegMLa4`rS3?-`Ai|d}eTecd0?YxduJ`?BIH) z)Sw?*gZ_98y7k=P`ZudVzp@7Xy&CkZ-?azrU$reB+~3V>(D$i9Ur>YoMh$xX^Mm_4 zz6O1_8uSxt(66jPf4>HO3tXBxSpW0i^9QF7ePM9=1~usW)S%y8gWmdLg?`?9v>OH` zi_!nadi)}cqqrXb90pLbO8}2c&Rq|?Wg1elK{lCEv(d4)=N^O8v$Al`$+_r#p@%s( z=R^~RpP%sH9=3I0GDQ7R&RbuC-zK;B5jYv)>PHB1Z?1lc@dgm>M4lcOXP_$|3?|w! zFwO13!33$o!W>Q?rpZPmy-{G|p({K_wIfV@*f3%>CS}_%Cw3ZBw)zM=etS8%_Eqm- zs2&}vCbh_)KsBD=qH0{XI+3dc@S$R!s2Oa;flnV!93Q^t=$5^HsJ9RZZkUI_ZK-Lz zV~9x?u59gr|2Oc@yTQ2>$3N0JkHGugx5Yo*^RIn;#(D?e=Q)OT^nAnhu){oy(dKlu zEQ@>hV9XPp3E`bG{!=(|62zZ6!o@W!$O;=Ly_2nzH=rt+Mag&%K!JHER%Vv;EC9|p zIEaSK18fH;*fc=}AkD$hYDu6Wnn>U(4&V6E2N-&MEjZqUskG>_tMb`3f8OOVYv>8V z{Z)&sp^LS-ef$^;d)2HLB(FBtT%=m0I3lad3~9ReVC1#trn=lykZ!7PtWOqRp*QuoAexicg;&Lv=SP>- zDR7{DPUZ=EaVI0%%Q4zgU`Z8LW4$<*Qky*NdFqjp+u%p2K^NaI5cp)C>^@czv4mVe zhiE*`Xz(Syxi@#)t;;yNEMb>!~`))t(3j`0rzP--3uZt%iiIfhu+1|umUyA zp$1Zg<@H0TX{Zi}AM(bAMi2Fzd6OfL=JrAh$C3PvqTsrF?7&xnA(jW76Y--RK z`5Os(&=~m>QF>>=j1d9BmM6V2pSm0p1JgE`$I|!Jy~sLHGqLxUT-hjqJ2{sZ$zVQ} zwf0L=U*cSp=C^-u~T@m?OMu1({=0 z*YNxQWPTAk#4YC{6#~OF0BwI7xqoFt*k2_MI^_;ME=W0llJWcXtzbh-i)`)1rqbd+ zP@7!nDlB{8P7lX!SD3+tW~K%9&Xeo?fnY;-x81Q?U>Wc|F?0%!ea#TGajy-u;5@^< z_&Af98My;EIyZpu4v{lYuQ9yA%ja*q^}CA6D#yNt+_XGn|^KrCYUif%NbdJZA6FdO^HVv zrcnia+iQnW?@;%;fS!Fc1w+CUXv!=qlg!~Ug&n8qK5l*T?Jv978@&rO$fT^k=bgLbeRh^Pq&W=lYTzt|{@in@h`DF# zb+#9?P}yQBXfZ42KX51S5h%R4A)bLh(kRD{H4g6IqWkB?yN?swJhU!eQ&dOGoQ zEj#xNsE&FQ!A3;6-}D^lcpEx;_l3bxrz7R%gubKBfDxN{NnpNluv&w|$mu?&n4_>S zEuGHP^>Y7#o46E64&iI?cqJOQrZ)5FU0RlUQYfjYRdJK?jJgC~mYMJZJb@al3HVYY zsc$i=NFOvhotQ@H^KA7u2<6o39JZt=TU_`l#&kA-$il zL`l=ps+J(F`U$pR!~67j7K7{+e*WvvxKM%*O4Z!E0 zoo5V;=*x9j-kNYD`?GS}v3TXC^VgtmFVq?Q)p2KAwm$Uz2YxQT2t`dw5_SjaEG5}3 z`(Gf36g&bsr7qFzeTX70WjWE@tWYS@cstuN?!M+7yWDT^0pl_EH1NVBjX9Rc+-!88 zx^QgTRVzZ3t4P&%q?Rq{B^gF(Pexl!s|6S*t<9kOztEj~k>yad;m!rED@G~ZRj6Y<2a=+_ks#Dkn ztZoMrVVI*kJ#m)y3P^jCB$r`NBo6E>;?mZEL_?Q1c9uVae#e{4WOp)iGHMSv>UL$% z8M4FrbdB$ZqD9x|DQhFR!{MC9jD4CA23P)Yib#5k6oQ+Hpj9xc_!`xhW z*-xslyrWRT;RMHkT*9DrI&$CmCycANfrLdqT&}*Ku{heO6ZP8Yr}Tdyw}h#Iy}<7P zdnwmiU)nkuVf>8x6b|F0`?%EWXk!zMI3$5BagO}rj$2;t)yZO@;}aR{GWCcYsx*lm z0zHZKoRi2Th0u^p`K~iK*TG5-ml~gny?m$oWIR4QgTtNVgb$LL@wa049m5yg={+fU z=i@ZCSIuarV-~W6^||11K8VP}vah_jAJEfPj(@hCvRs4zLS&yqfk->D&A!|PyI^qP zhyppwha050MZ-3bu-H1uWP#-Xg3S`iD=+Wp_*poaU+Y>xph?i&RZ zZvkY{$46DkP{>v8c6LETV9e|+_SEre=O&3Nm+oN)j{HwmmC zD<4e8I_>h@_rMLNS=3)aXAfO=o(RF|XO0?l(~yS$dRBtBPAtH~9p;@x{<~tx7hJ2} z60W|HG~kWf|L@+z%Qbuyed}B3&v;w2oZ$l2Lr^-o@oic4;V`SdJ=o)5%ihuN%Jh7N z3f|Yl`bgrV<-Ug;Hrf0B4KBySvp)=?&+)#NhC}O1$T@%?A(#6GWHcG-TYTM&`i>u4 zea}xFDn!b+ql~Npy+6_an4ULc`9d0P$G|M*28wR(TG>{tvUSO~MSFpCZf8_}`WpU!;*tL*8(`At78A*E-&f9iqFT`L z`VH=~E1eSCzn>*E+eZ^CmFyG@Li>4aIQtk%V*ma$-AxmZ4oh*)PKSup9sOByXVV4G z0%y-TAEpCo%91vhrekS3h^D*GfazeGP9p6Pny#nmP?{FgG>@j(OEBeO>f8s-U`tCT zlVOhtf1M9{zi=Qhj}6FSkcdu}v0@2Mmaz)a=2od+v^h7AcW)KoDms-STPW0WsHhYs z8PokmaCKfmHfZ*CM`|GN;$DtebF(*_HsZ&6mVaga#Hdo>du>4<&)XA@u(N&+KXGp# zesJPrKdO>EiMuWir>ESbA_nLMtv{fNLDP;1XkyT`BLkWkK;zv2tK!Q)+ha}VUt7f6 z05JyQM-Dt{rrZrEXCM7C%6kgtK_=(No&92#nE~cgrBHFB4w{d*BHCoIst>ueC~O}C zqhmE0v??6cOM5$^udU#`4Ac!aDsbpah(668VmlpUMzwp3fPvg9rR%;q(cbWiOxI&= zkX;j5-kVF<50lt29XqU{=!w`2k8X#iLMXQzqBO5L-CSqu@SzXVvC4%>IVjwaI%%&~ z91=r2^AOsctT>Rt1{j%jT`-z^Ii5*nF6B;j#eN3f6u?WYCbPw0V!6U-^FAxriUYO# zFyIA*nWrBVCbiOi%zce9ytk*$hgGI_#3MXY8_<|rgjOdfH85q)koY_F^nGa;AY;3@ zK@G!!vybzSe_=toE^`TP5RBuzfxha_#k@7A9{(a*?OGq|o zo5b2Yk!h0v)g@Xx#;YR2T_>HIgzD$0`xta9>k?Q;Ihnrh3+22Y!ky=>~;>`7(ZmXM1|%_^Rcus0>OdO0m4c>rbWa=6kxJ3Ubu z_YOh6a4}CE&n<^8K-iY1thG>&JIo(O68*>|dJsDN8mMCx?`DkM^qKRYDw&-I^XCd? zl>b*kfjJJSW43uAf*thb&tQJ#Y(qb(!t&;!)M~%JFISf+US>EwGjvL-u*B)a>A)sY zyxb5yD-WG_hs9M(%xjiu31#elh;3Ka7 zYG?nH;c|pmpTvVIkV&?vefYsuIOs^|N^=zMQsN-U_Kro7Xh?NiFwq_K{re zMBg`<5$AXwL_0VR4miZ=SaEF*fMD1$aI-(W3EYU;cY{?q*3_-KqDEkK)|2(Jj+HOO z96fU%YZ%sV+EF9V4zViG^IgZoSDCWHBb|7{n6(?8Af^1;999H0UER34hQp1`3GZ2? zoYW?b2|#ojQ$TxwaRSk4Od2A|Dv|vl;_wzBot}*{=inkeH2gkL=!jAmKE_!MF*0kk z%#eB`Hyr7ssE~aQt#6$53eq>h^i>Wx{!#C#h?1>hAoOi4#<=ig0J5eKToB~49sRz> zYp4*yb!7C;7U|R7(v&5$^$<`ez`O3G zRws`zbn9^XfFU$EJ#jpQr1DB;n?pywme!Eq$701O2+M~-V8+g5SB8B78Z9#v<3sld z1rgGr(4J-{vDHmxTksN*A5`?Pjk-K3dw!FW&P}KyY}JBax#b6D8?n_@JhLXwO=eF; z2h68$7wKsqKOVyu6jSQ)?`e^`Eb2KFFG0#G(|1Qm^!iZ&^s#e+RFot*0t6ZB)c2kuViJo@L+>YU z5K;GxsJb{*#8_{g&^WMj;77plPI86s9gC443L#5JMK1l8_h+?~p9xqIO7)lQDgINC zW1bli)5vkrJU}2Rc?Z3^Am0oJ4>OCCXIbpS$eaVC#6RSrQ_yTzNp^k@N-3j#%d@_?}AaDtF0?n(x z-^ar;n1#ICbPuj%B)R;5|5csQLjpdALFkJ9)V$f zzrZiFI-eBJK|;!9OS=C%7x?#h*P>GZSTUE8vKHZ62Ta5D5xMTm9|go>cS`3tM!oXV> zt*YZ4gR;{UU&<+#ZfupA)(i_~>8)hT40JxQE_AG8bHz5!e1d#vSw~r&cvI{uw%(YV zN`q3*x~v5?50z04IB`rm3%myMWc@PitNPvQ7sTg!4(bfP^zBdnSISVfmif5wBM;xN=K9j;=Sx zCvHwEZjzUcCGhktNQ*X%#i&p3I055qGX4pzn9)1ue^lvEW%D#9s>ZFsVt(acb$>YH z;iNel94Vk;&fy}oXp9q+dBtJCTpBKPlGVisgd`4(`fC6mm9GIf z7P~8rl76|5AcH!m@CjsaDjPBCYX{c}FPr(8>oPni4l0X;z*Yr~Gt zvv)c&R&ds>#O&&L$Kx}6h^@Z>fGvU}zq<05qF%f-Z0v?v>Jmh7m6y@?)(y~c(BMQy z?<8pOd%C(#ccg!yXYYm4(o?)RR0zZt5HT5&YEe8#cqvJ&zfiGC?_vxp7uGf5{e`@f zeSZivA)eY<)C0l#fwK0ISHFA=3=)5YHTGhv;fMJYa>sWsKh--<%Fj z&BWPem%8)R2;oIqFZ1{C*ya0~hr9)|^a2qy$2wI+4`nx_$gOYZ2#afm6gX} zVNi#~UkMnoz@9w@2{&rcy_j{Ej*~%bW}yC$NVQc;dh=KL7EMc|zWY|xq``i=9Fl2Q)Wa&KJ8zEy>O|{Hu z(C4t>B#O)+PNcMlk@1gDPv`zx!6@9!HV2Vhg2S6kQ*}9lJ;RTjn7N5DH8VM*siCo< zW5P^7MKlYf-Iy(QSe)5&e@?M)q|JFDBv}qT99d_ zGLhZq^hDl4o31w;Ya+~}5LQlSI5O=qae0t9#UitUBk6Ny68pQ3tEc5;@IB3$s>h3d z**X#xyrd3mN|~55YX#}vVy<55MclLJk4e};f_vDHl;J>R*E4&*o%8gYL2Vn$&oB(7 zcI8m&PuRvS!;dnGv`!Z5j<~)A)C-%Q0YRN4T=*AqFx|l4})KfbFZ8G@ABVX+MX2nHbmlX*BKElYcvYeD_Zs3p;;&`~Bqe$G5*iJAZup$F%duxBpB#e|-Ch z*UIf4?fmiilPm1gX+J*V-&|pTuflG<4*Bu;@%7EDupeJxUr0NDeEF9u?DiYw{IRt2 z$CvM|uur9(KR$o|3j0a4-y89lRM?Ymg3lk{y;pAU=dw&xpz9LZDHPz3I7no-r9CVF ziR?_;4-X1NBD)*yb0YZ?jCxXo86wfoHJ!)<%=AbNa8}?TDTO)9Vk{meB{OH4t$jN) zbC%iKw@VIYw)X9kW0{S8yW~)2Ti-4@lG)U^v#@1Z?0h>5Tb9Mnw@Zz2Q53#~FlJe7 zeg56FzYy`Igd2RRVOWj$@={~48u9I{EV8U7e7of6w<3L#gTIaJ%&gh;l2D$RG3!Nk z$#EM+cF9q@M0UwRheUQ}mh4%PU2@2x(B999x+uXKQj+-DFajw_{Omh{gOntG_T$jr z&-p5?;CeJhoJx>1*Q6UrLgz1g3LejGk$s@-HD)Bs$t(ue6mNtOM3@VG5%dx>;zWxi zd=YgMC1X;-8~SljL_+O5<~xlNYVA0s9Vdn4i=ca$5hq$C?Th&O`#JoQ8P(j)3_p5? z*)I{{++E1x+3uyjN!`NG)Mbwi2-U26L)O=UDR4FYtw3QZFa@kn144HBw=4WQG9YBH z4@`lpB>|zDm3$}gYt6vqyXwCLQSC&3^ze!w8OQf805^9lF+dMg+8JrAibCg7x4MlO z)az2rqE^k40?1mRiq+3?v)||89(?xuMx)>2FIltKhW;N`rqXH&_7&*&vADhGChXAB zq~H|}SKR|zs4FhI##HHAz#-%}SWm6cg>g4}H!_@U?r{|irY?@X$3j=`8|gbcg)h;k zwbiQ#6S~LWvU|J-c9wg$bNmh2pdU+j9d!4d@9rzQ^RTSW+dRJ?)E&>pZHaMNC*A$v zyZajM+&q6Jh&T?Q9z=AZhK2eov`4!+m{ep*zSUD zm5=lSfIX1G4xMlT;8LjcE~5KqA&apd=J6~%`12fXD8qAYZ0%*~?PG6Tho=!QTmw@M z>-w;E%!{eshnn8~XpYZ431IMH)jlpr`7V;)6?B18+Q&&r9^mq>0c9eSUu+9u>&Ecy zZiH3d;)Qu#Z2r;DGjePv$4Wq@UpOqu4+6Rv0iF1l@bF@6AahzQj2`objFBc;%k8Y^ z%5Cd#>w!r_WZqo%3tbM6A=f^>)!H9vupH7r--2Uf>`cVf^Y9|w%TbG~UVxcRNC&P^cTOx%F`3l!zU7h9?7Ae zD4e!`Ub5L*V-vkk&c^J>2U`T=*rc3gS)OI%C%#P!w@?VZi;F z1>_-wTrI*+LJEIWM)keYD^TArzP@j%Pm%_%f2HeZ0Hya7H_#*Qx@;#btl(MWm_P95 zihCqBm7L3~ihyA1qOH|b3b&jhHxjRBRQiUaQ z4I`YSw*_ZhN8nC-F&JUQs=L+2xc%nG?K{K`7ngl6QS@t&0Y>o=guQsR;o+V1@KI3v zNfnlN3DOyu{7wgtF|rFlC|+Z@x{I#%exdp-a`BY^G-vp8xXE;giF_A#cK@> zcT>aT)Ih4Riq{$Zd&qx+d{TwwT?XZtV++e?T`%Xy5>h_vab+FLC+ zX_ksvs%7sfguqs_gZv3Bhrm(paS+rK_=)nDYi)(_c*MxN1yP2xACL>@4nYl!Y1cHF zw`0KXU%GfJVp8v5H&Pb|<=%~OVZ(LJZMNfD)15w%gl@M~O4^x$(tj1r>97m%oxMcK4 zmZ!{PlRi?cS8%S82+D>PV#hiB!|`2N!uc};f-_eum1PF~VwrvvONU5S8?&A3So>oN zPxLXT$ObvJ26WUXz2~6?n}SknV>!v{=#;%b9XR#Xq66msRhDwl4q3T0| zaWEQV0N8;z{Ghsu4Oa1)KNOb;9eVXgKZ;z zJEG4`t;%vS_fWXN`H%_l*n0^ov^TDJrd|d?CKkO{ghfAG39Wh+Mm?9Q(@6KDSA)*Z z)&-j%m+o9~yX`J>XBs-yYj83nWJDU(>%s`uX(PPe03CR}Nv~voGYP~?C;?vBS{>qb za};MgPwCP;1Ceh{E_Iah26tI_tuA;SX5h61DzrDQc&3(upz(T3SoFh{(5km#tj22s z@tV=({xO6l(y88olL)U!qk2~u)p)%JI`H~8y%M}gAYMWV@LI#btMgqWe^I(rFc6t# za;c+KG`P#cYfZuHa09RRp~BA78&`hSe_+&jeIP9Q;Yw)Le_^b~>yN~1tENp33h|0` zst@5L!Yk6KJ`zSXULS)Fygs2!d7X5G~wCW2OtMTe3Ugtjg>53saSxAyuO7B zJ5O&g=)N?}bG_&}k>@2mDmywKnle-27=a#4FONeuR?US8c@tQ=u8rm0J655VJDDh{K@%M}*=a3!=V0b?~@>l3dZ zRkG>w6OsN|#PH5J~^rsNJZebc(@U7G6^XuTu@Y znxVqZ(;HWQ)kqjMUKwH04_88~T41ckYZKyi=MCE*65yp{(Yc&$LM1TPYZmrw${ z{$k)&mcJ-nI@3U8mWda2l+H4^%ff4O!Ru@TuW?Xe=jn|rzp4vHjaRp@=!YwzReyl7 z8m}#g*UR;j_YLuibSe){BD^AvYDHmGU?NB`?W1Ftoq!p_qhSANwWVbpl7B`o^kN@&%eV64UqXPz57r+2;aV2D?wQ}x10 zl)oa43TO1esK#p|=)h}jdL?*~K)i$!;DtNhL%cpV5utSHA_I{hO)hnm{%LTRg%@sd z2VNH&c&!5!cAnn2@~hT`QR6j9SoFh{(5m%dtj23P@mgVg|GObxkxn%kP9nS_jcR>i zRO7V)=)h}3dL?*~K)i$!;I+Mh*NFFx7Ld}VOAJJ&n_TKBEikyt!fS@$b*X{ZMo?kr z>5VJDYGW8RUQ>icKU@i|+62aGymla7yYBw)`$N1UooZ7!iSUXvsy_>(8n4Yj2VQ@n zSArJ_#7igvUNa56%JLVbOA8G|=9zd=N9i(yyDYr^DtKLP;I%na*m-*6%CFi2Mvd2& z!lECpgjQ_@V>MoXBVJp-_iHZ1E7GZ^!byZzq)|;1Mm1hrgATm5p;v+z3B*e%0bV;A zc%5J(Lg~^K1|rXzTgZ?*G_`hRR&($LWP~DH?I7u?O@b+O&1pZa3!>Adl;+n z+L?IW^4jiE{T1m{GvFk`E7GWT5Joj#GeHMlf2CJ~7YW2mC;?u(7S5nho- zwU;oe@tOlV@YA)E02^e-C`gz-NcJJO1B!^W#KhP@Vd>w>p-Zm^Yq4*Uo{s-jn_fK zq93k=RvipuHC}rYueDS6yd2^c=~RcnNrYFVQ5`CbYP{xw4!rX8O7J3qcnKxIYoA(p zQMz=yfyjX-Uer;#!{9CpuYCotI}N-Jg9fXGVqQB7t}bCBO^!FNJuO&F4_M^niiL8zx@VQF_qeE(y-#l#;uYys=fFvXSENy$D~xKq z&I28Iolma>FA|8CPy)OTsf8D%ON$Id(jOT83F;_4Y;c!_*P(*fBL-d>t6<53!%c!(;HWQ)nzbhye=0O{ct6; z>IxXE@hT9n^@<~tAzqPAbtRlcctsl3Rl=yo>uS(}*ERG?@FIbD2_?X*XyCP}i3p`j zj~j>_XmY8e^n}4(7G8%7UQZf$T?-XLy`S<8?FW!0Q%zC3ulQyo3_qb%cS}873l>E@VXT$>^!}30I#DBycU^=P`dQ2fyjF%mpV$%8Qf*zb&TM(*ud*v zsIc?&#+6@nAB-BW`-Me6@V+U2)dTpc#_L$(6<@w56XF%=R1d;Qgjb|dJtT~3ycU5D zydI`kf)@$IODF+e#~FB)wU3l8J#QdVRvxLN^n$@%7GB2-UN0JWJpvVWp5D0fs~&|> zjVQY`@cpDNa@l`1|m~TE_IY%Hn_{e>qNop6$7uQp~BA78&`hSGcanro)s4Ta3!?r zIT)+)DiN=@zd1I7CDN%D!%4)5G^*!?QH|FNpaZWL>6PF`0`U?`fY*Ejul-C!C|!Eh zK;&qXOC6=x4DPbObQf*+D97Ie}oaFeT3Hs zpaZY}(ksD>1mY!>0I#zQyvpigN|)X?5P8YOi#kg07~Eyyb++L3u7THwP+{lkjVr(E zBN#Pa9}A0qxDs0R35?Zv;aqfM=ha6X^IV8mq*HwgClOweM)jF6s`2_9bl~*`y%M}g zAYMWV@WQ$4aBTOri3p`j?-_`=9~w0^b(H>XaF>PGd4kvb23}u6g`KB2uKcR6VAObh zEiC$hbNl#J-{7YjFPsW*>>N_}k7(~qq*HwhClOweM)jRAs`2_Bbl~*^y%M}gAYMWV z@VdajtJ_3`(xv|xh)guO)KU7t;4TZV3k9$L8hHH(6?UH9xbmxhf>Go3v#{ugE1^}t zz*vpfMZ{~b(=Xp6%wLgC^(&l2ctsl3Z^Ed?YXEfM^*g;1yhtEkLJ9Etr-9dQCL)wB zeP|$Zpvk3<(nkh&S$JJ6cztZ(g=^-37rk-iR}BF{z@i_n{3-{8YP>EXUPB-4 zJ}Sg3(y8$Lk%3pFQQ@^CFao@S_qe<5X2-i?6*<@Sfq&t1AGf{7TGr-S0cT<(Efab9 znfEp1a_{4ZuY3H4117+E03VGr3B*|_f&OAA?p3q;e+MsnG?47K>R@_}rXeulzUg{v z8|dbr@4fhy9r_W@WbTFy<@C#jsGaQuX|d}-4y{{S;Y=1b(c>V9HhQ>vC0$w-O`uo6 z?)id4_0qN>Ok!c$^N`U3rb)B6FXZ)Is4FvPN>S37blj;?G`gs3OQZ0rtsb0Wu?je*n zT?W!pAbc;-#l%6jfwl(QdkA7FZwY?q1s($r9xAg&TD#1(oJK@U%!gP*aXab|!E)3yFk%Nb?xf}o zgT1^5D|>sH$hzFIaNebZu8yY}S0D1zqENchT^V(pww#mCoCn}W!m-NWM@xh3hGbkF z=#y1zWE=(KFdKpi3bw{^GO7)v2CcyM7QvIw50U$3suF@CH#%va_Xt=zC|b;08U;wT zLy0~c?F?kT#$h`(2N7DLovP7M0LDbdli)#9Z1M0Q=X&Ge#Jd933J$VgiBBK%XJf1> z?&g`8k`sB1+PezOri9K#I!KLikl$l~8si)DCO)R-IQ(uyijaSD%~2ZI4!6tL5St@{ox* zJ(&Bo6QOwwKJm^XN6tG^x~!UdFs!Eh*5du}6~4G`Pu>e9(26#pAfZ-iP4EzVjA1Ua z&!Bz-AZc9K%PX$rvM4kv`o1ev3s$11$UcU&C#W-;;X>Y=5Ciz2jSOgF(6mfI6NAvC zIcB}n67a;J(e`=fiD})uh<@WI_uXu%Lg0?~Z_!VfAnc z0Gn6+M9a?FBG0w#qRj_+pFs^ytS$!}rTgRU1CEn>17tYd$@@z9r9+X4gq3@p9JV>Q zLDzZ^IO`Whx-#CU6`~8e@JvrvYkWZw*)UAgw4~`UCLV9X@9HPh`!mmm$^l!~+XNxd z-bh}W;(bBIQuq2~Ycual@yrMkXrENkW_h3MK(VgC+qp>SKn3yoU*i;Ddv|dxl13h4 z%KnUku(&CxgXD>dToFOPp1!jN_)bIfdmI=?V5>{Cel;>q)ISww))T zeU|>taOUhPl6H^98z{dAGTkt5U=JJOoeM`H#j>V5azizr{b9=!h@u|T?+GnpJ@YLT zq-|c&%c?q&Jg>n`sh_Adygv71SA7S9l<8o;C01A-p+aT00z)n3ngo_tq2TQH+>uOX zyb{AQ!Mpu<&j|m-PqgU2L8HCy*E&smyu8dZO;-}p&kcSlgJ3?Enqv7UZ z;8pw$vBb&d?I$4Y%cK13JJ&VA8-Xx@IU{09fP^4S+^;bE`JL0bj}fe{To=T*a{S}bAO8k3W))_5T@|~ zO${0b|a zhreUg@BK-fS#umekMzRsDE_K6aAqEvV0&U`X<=B{9(Ur)zaZEioCC*+`1RJNG2^X+ zPk_a`G&4+MfIEZ}7kF!$7&L9YfF=e_n;g)@plRy|G%kUI3{r8wQ2DQ}6vWd%Mf^lRpAM+^dgeg>l7b`cx7q9K$ z*SikA1yDAl`FfbUHMh7q2tmhV3z!GoE%BS%isXJW)l~d+vkLIbV8TNCYCT4Orh%2l zMIT$^>*lt>Z(&>fu*19^zPai6@os=`j+-Z=I0HX{Z#%#|;LgNv?yn^Glj+?EY4tal z;Gr(_ak=~AU+_Ox(o@7?fjf~f!Q4y(TG0@Tm?Y6?9#;Zxa!{3IKe~D9S zc=UYjgQ&-Km>aELsNj|6!D{P+}Cs#=iAlAD> z4e$M!jjyXOCEEkW!k+k%AxNFuQrHWY-ZS7NHwPxfq_8)>$=p8pE$oXQy9H6%4~Cq= zk9P~AVrM`)fTS!*w~|DuT$xldPfZ!|!Pb`ifuM9v?1XpSnxcR4Iv1TDL~cWYLMh1v zZl1xzVPP~=Ejx78P23*$^)Rm-9EclY?B5ZC_VFXFBjMsnVsHoqai^@~-G)G_Lt*RP z1w2>HgRu^`@8>Xj{^m_XrBnHgC|DCk!h!6{Tj0>L1w~>k_JskgS+7JS%&FY z^xe|2%p`j5c8*4$KrKKAv)k=t{~#vUc&HN~gzGAf!aJaW`C61tTgog(ZqTi3F!sB( zgsn~jRbG(RMs+gm&E8Oyls=3E`CJuMh?wy7Ad-_4T7$+tyBJcM;OIva)G1IDU`1Re zK-+V~1s8yWg(~tieDL-(e&tm$SDis46FA_V2|_oNFi%eR9f~t`7C0?<$3ogFQK-%a z33iM>#onUM5d!DF#zW^~;&?7dOxyGD#l2GeXvYi-lKOmnau<+#7gLn}qAiR>pr3s* zrt$NPOa4O0I_e_)DhHV1Lic~dEK?G>i($fKY=wxcE&)BdN1Z71&-bQF$8P~-v=J|R z!`$7XF2%>kVkcYIMGHYHP`_?y+3sIPDYm6zfL_op4`^Z#n(kMG{e!}K(jnzoi+2;f zK&)2QW9h;;44T|{VFi42-wQX5vm%Y<4q)xcZddLK_<%tcehbS(t_x7E!eQ4{pm=vn z;m~z!%32-zUxa=~{TJObHY}Jm!0k*|LsZw;x=FzuEooFM*MN*RJrZ)?0n z9WelYEhPLm2k`J-o-t9^fmYwUG88A(^)NT2VyX=RRU4vX-Pa6yDi&88!Gfn}rdN4ds*I#mMoC>|=v$fa(4@Kn+M0Wugj$TyO9!Dm2%3?*5n`Bfy9pl@dRLs8 zjx>dl(sj{TYtr9zT@>s7ep8o=ss!8HI} z*@rK>z4DD0R+TT-09Mn6cwsGVa0_dQ!Mg>T3#*8EWMO4|-8^l2Rp36v7OMgzc(*}N z%K7!VH4&t)cws$|yPN{vMT>RC)HqDK7bd|x;QlcPGUg;!wvNDz*lWaJos4229TSYP zA`5RynoACsMjSErtp_)n+Q%x%RtK>JW$@EJne#xnYss6*Wb`>B$)1d#x?bM>XR9+| z29qC6_Z3r{Q>hIvL5Q|`pA%|p5N)Vfpa#rPTLYh)+Y&_H^D5aN4{D*3O~^u0b1!Ga z6Z$omEp*+kBCIGve%|q{-(con8L*fpuqy9AU5*Y7skX_T;!L1e2h2(bT@?;S zO=i9Arvx4N#&yfw<|>`F;_dcY&U_7%0u6!VFieIQhOBQ~8P2o|c%bWA-G_gjWpFJi zgLYvfWMtIk>+^%+vw?i$g$?mV2_ZT63#I|;9Nw<@C+7FtQ9jfiEF^|ruG78Ym(fk& zh-GwB`Nj%=mT$bUnS9~NU&P?u$r3q5%p(gMF?$(~^I5b;_z^^wM zhekF!jx-S}MsxQ5^U>)@YI0fMo;A8BxVhk>Wx5ACyK-3+t*#tvEX)n;!B4Z=OMiC9 zM=6@U2M`u@0PXiOBxX34gyFEwaJ<9LkA%Zk{~w1#uaPeGv7aVzI)$y}izA=%jTg3+ zFRmmN_I3-ZeFHJTAJC=*G%;w}CIL+hpmDD051d!o8|VU$aIR@K?2h8EIoBliV6G{c zD~Dg3271My{rYo26NAunzn9~^4PobBF!lE^vO!@pxUh5BR+TRNB{FU<#z!cy1&!VT zm~YHIO8b_u>%I%ZK-cVp(<#4n$1ff6E5i70MLYX*Vn7(CWWPrhl9|s55U-$dxZFbs z)0GVUPAKMGG3Dy`v#sn8CS+ayJ)~`MDzw|`F@!Gd^f8tgg06*+o4xUDtS;+mmMq?< z*Gt7hO~|T?CUSmD9x+VIpwz)raF6)?v(vI`eukBZo?s=y`<8jVfN<*N2zt%3S20Jw zHOZKx=eKN*o&?U(^DA@oG+DbRB&`d=BjBlv#sjN=1?g~W1^m?w5zry?^T*@ z+~|A)l&cqMQ?KDy#9g%uZo>2lSFd30pAdpPkLTAfX;~xv55nrGhmm_@#e-0YgIU43 zFb}v}u)Y5m@=aGincgEP?HJrXD&hxMiEl~q$3>h>?@@@?#oUBG6clkz)vpqc(w7tjOAs%{>W@_ZXZjEM%tjHR$M`c^`l&9c#BIrY6II!o)jnlw}_( zp^&H3^{%-nkkkXyj^WWFfO-nL{mW(bxUTGSS%y>EiOsA@#|LvkDcLInKd}FYsqU!1 zA_-!}L#iOyiV!?45RmEp3vponXMVcK&S8xobF<9SQthC?Z%0e4FrA-xVS9Xfrr3E> zRZB18_e`hMfk9pUU*N`Z(BV+dK32bcpcsf3q>)HEEMo{^R(%@`f-E>gB%jP z#Sjd_Mcva7#t(hn$BKKWaGZQ&g@4F5UN~O9-t+Y17%`759F4D=r%n6ug!Y33@5Mp< z_{jI;BysN)PL^-1aH@Rcg;V6~ZG;-9Fkj3g3nhHrJZ;*KC$%3WcvJM$ds&|T7=AeF zq&}ul9zsE$J{0Bwcbh0rlNsdclSTZnD)DWjJWXblr%$iS)1ScK|NrFaxYdU=|I|;9 zvk^D!juYI^67$HynfSVS+BEJ@Y1~OD!yWC{OH68fU*odJpMq!lDLOw5!))jP{Ve&>7p|r@K0N=j}`{Qls90)C`{- zsZei{%zWR3$Dq%{_j81$IoppmENn+51nCjhh#>z`ZRZ6n6s+rz7j}g2 z%o98D%k?(y6Z{goblvCg5WVP4$KMz0@A>IC&e$kR9mqgbk=qoF8)j)qipcd+hldkYXwNAXv2WxG$WcV8-0{t7i%?`D4#Tb69^a&VA& zIN!S*;=;e|W5_yMpPB(pwkOVMlL9-1cODC*Q#cnN?NU7eHyD@i#7b%j^0xOh$|%NRRFhDvf{X2r-Zcn!U7~ma z+y!;v`7jT-yVBiKxa;aCGj}e7{R~w2V_erd1!4FaVQ`8UR=J)P_}bM^ruQryrrnHs z3kr)DRf+$d;%|#*WXjBr_Z-|5FRqf^jk52EEScV7h`BAI*?T?+-)Y6aBbpOAck8`? zD0&azCwCQo`pFP3@jolXem9V@)A-*IDeO*j?p^p%*aJpM*M!9P8)7~iAt2_L!h2wT z2|uyI0)Fze=>2F>?#038kQ!xSEvtFCh42<>L*F==vIpb%9{gAT#!v2ev}E-y4I^yt z8+6@LYjgH5(*x_#Y~om^SZ86`$NCoG8(|HMJQK@a#o5q?sov9(aux*G)OXbOkk4R-{uX?@rS5K-zY$7zSH@Y8_5q0GVSY`p>Tx;)T5!)>vUr39_A+Z=OW&J~%4OA>AbFs(NpbC7f&Fsw9{l^%(=la_U-> z;Kff&w}TN#)+4tPZl`byK3ZIT4r!$QAtC;L-A%m-wHl%kmamvK4*GtEzGn23#53gJ zF9={73~U(x5@tMRj+eYOcHAfbAXvY^ja|H*5c%6$Z-aTj%?G}A^^+;>hA2trzJjVc zOibZZ-^*DqR!OilRo_Fn z8v;MzyUHs%H^m1sdxW_OJ_zqj&BH@g5ijAt5wwf7<{3Jl+1SZ8_eO1=fW4?p;_D=J zF;`UF2k^=M_}u-V_0uXerhVKRj`bG)75Al>1zShm{LN93c4e~RW2QOK(+h>#5#w-Agv}1|*^l@<4O>S?fKT|;Vx*y~MP3Wd@76#}^s!wj!krE$iEdE>`>#;%* z{6}T{Sc{X~Ho(vb8Y&4s>pG^zght%8h3@!9Zk4-q@mGeDZ_JJjC8?@Pvaa}BC>eM& zcv$MC!ZKntP#&v}hN6SiFUHEVK*!Kxg5+ccc~~(;a=L;%y!boR7Kc=j8;cgD?F#Y; z@vTa_Nqno2?q}{T@&{Sl@4vg&!Mf;K#RHhP_kgXfs_dDi$uE1Ag{)PVCMJePPnXnD zV$o083j9Lur(fcso-1H~fa4ba6|)QnmnBC9%^lw`m0BvQL{J4BNGcyKHmH7c$ES%S zYCkoK+9=))Rz1dX{43_@->;YtZl6Cz*E!`yT9@#nAf*fD7-Z3kQZ3f8LGFpfas|J- z`d?^dV;JQv$!fJVwpLU-gNBh$ycdrS!mX=>(3)d3{P2w$EIP%C7_(9^!Z%1AVQ6fV zr^x|`Q9lT<hmZGj$6yb>zCEb^hMEZe{8ghX*3w6X-^=@)tCRk(|MgPi`%K%W@Nu z9~bFSMV8WH1ErfOt;uatKNRaoW*B^Od+}3B*OS~7kw+Il8%)WV;uj(d(=*b|siwC`uc(qZ*pIG4`d{EJBX-B06^EXWEp3jR1va(qT)lQo%D5L+t%8i+tr4`5Iq$n=v z8YLCLf_}=2Be6m!kll++D&RZ)OQC_&9<<0q=GQ; zX>~IUEZo$?8hb&P?kFwj(p|y@U8AMXy`XE1)X59F#!A7IU0QtI&AfPVXr=$!?iH zRqA%{Io1j-^S9Nu*J$%TW!lUs&I|QCPd&5>jpNl_tZ>4G%XNmbQUZ+EOb ze_yEp?GDt1dP=M@js$9~Tj0Hfuq_mJi`~&#&aOAvt<(2eQx=_$wB6}oilNPqPo%0I`Mg43o!uJ9vAIET;U!F?8k1(wI_m9FLBK^0 zIVnn&~*&AJB9{q2|}VQ2#t2fh=47w z%R@NA?r17wm%DDVJ6Zy^9|JRf&2~p?8N1x|NV{WP5Pu(?ApYg4m$5s#g4FZlAcCe} z$muHvGJZs=WRTr^%48r&z)d~K^s5FkW}=ufZFa|MWio^4X}3FO)QDWF%)`-k$KL{F z5;@6BJM50xfv=)*5MMj(jy(fqCKT|sT-kDVC#U!W zSu=?sTb`E7+npyHvK6r@mtDc`JiSuZhq_AdIJ=We#6cJ;!dtGl%kI3WQnr@fZoBhc zL$)Gr<;nC1yYov!R^k@vEtmD|&fg7L32!ag746PVQ8TssHB(g>y4os0?y)dd`K^Nx##i)Ju=kq%=t1&ZIQOq+3@;iU;U0vIu`G+M`iyZQ4-4o7~(l2wS>% z7{bz&$TTH;MVM7ZXqnHeBf1vf1E$yFTIqhK7V{JK3G~y*J}M^q z3RNI@BR@%4ImtHCrkD$P_x3qZZ}-+6)T8dhLQUQdeP(a z-bMi7Y5Vv_>p1v?i#;0c!;WB>u**F?ac|K4-3`SXLABNIP_H&aSZwc2xK&mY7x7vl zmrz4su6IQ+843Cu+TM}<&t)6h{5=vmoK#E~=&A2DHnpQ<={3AHteze9Ubi=@HcRLr z`73#Pp9I6?DE@k{VJO4(O5!+MP{gWrFaYo8@|~QU2q_%iW5!Vr(EB+r(2)+U$G|G1 zh-eI>t>u#f{l`Mi&nwt78{RV#tOap-pJ^QJ-#ktnaLw>|J$CBEunJ?R4t(@l;%S=R zqv0cT zS&T_EE*Q6HToTu~B~JeDdrsZk-P1FE`F_v${O5V5>(r@Jr%s)!TkEN+2Jd2QfpxG% zZ67z@rAMZwZXuoNo*fM+v5{}L6K%f5r z*bSQ^B4umu!0lIU2pnpO0Jh%)A%}Y}aTb0ye!{p?UsMlCYc%xDKeIoq2P+Q)m*70q zBXiJF{O9-aukPc&ppSnI{W%xK99&ogFA|X3uEE8{2;*pSeo4_kp8m^<{sg&P-pBvl zKK`{uf0Fc<(w|Ea{EmQWf>#v5?+Hk2g5b&`cojj%8^jhV=GoFPIrt6hPRc^nv}}}F z?nZn_-0$8NpAfDapcD6@2R)F99csn-e;{fn#ESFm-#Wy~K@Vx~Wt2)`dwzyyzj`~s zz3wd}cpn69Jq+|-VF@n6y*=3U#x;Ftfly~qkLNgcUP>v?vWa$QfQ`v_+B9_xW%2o$Hce%Z5tFa9X)3?V zn0%y7(~bcB@LUzjzA6#M!^(oyB)42B#y=MiPa{-0m4*@%hOz}{npzr4Oc=^0p*)lr z2`yX=eLH@PwjGQ1Pw79Sp+tS@jxUBlPhZ2t*l|yP)S=41HFx?VI5nNSfm&>g_fEL z+>!?|q_`5^tE_~7!B1KaLQ0vGV$Ju_GSRc1VhN$#%-~|{TcW{5<6L}M|3o!~Wd|Dt zzlOz`1*KaQ&h9!S##yoq8aG#ptKTSYgC6w?MwSm|9`Po9bN~(P}~K zeF^3=gs4wsxB`be`4U#Eu9^d@Jc!YF#nkNg+P%mmnmqJv#Try%TEi5i2$L zpxn%^_XXrG>`+lqx3Qq=K+x91K<_mafM3rHr81+Lc*<{pFXcDlS1#?08BNS>u1{}d zg#d}1>;9ukn^I}bH51T!6JmB2dPDJG`E)u4;i2VIE&9wI7o)ucr_=Dy-K)3p&z$p* z_yzcX2>*>JC$1s5$0+4V|@PtUlDj31lnD{Ifeu^Ckg)pAKvR zg#(8h31cS3qC_PVGb!j_YPqp#`ddFXO|g#Ih|o}vPDzD|Da%e^QCMnvg`-~~PuwLL zemjQi+x>8jyWOsTI)({6p^eCTpW`TN)Gn$g8za0iS1C+;>>OxP$e(GG27ef~Nlr98 z8+UE9--b_ERlwWLwnn5xQZDAf0X?(^H7Wl9I2|0Md8+~Cs(537(L!V#{!FY)I_=M5 z7C+cDWU%ubkFaz`uyn`_5n{8wH&{So)+S~2eJA@A+Rhf7Q3P2N$ehizSKVyB91)Mn z9g6L-#n;O2EB73KwBE!{^$gZ*L6kAPvDr?9jCRUpD z@dh;Hc@6_n0XVIwbNyCoh>cD|eWI2XgROL3!pC$a-LWt5Xm@u9j3o(_omH3g_Mi`o z63POOG~+3;N-#nuy*)uR{y7yg+1rG>eN2l7S!f5^d30%eT{Hznldw4?8Y>0N&WY7; z5E?Yw8fA^%HLQEzw=(aA@KM&fl-@DgGO@`T1O3)ezZ>uo^DdTI_mhD6n$9F0w|SrR}tBJ#CHoAcSwQj!J}joou}bF$P1gPRDbA9DfwbCY`w* zuV8TEc0CuOnQg@b!eN6~2kEtMEF)3Bq=slahpgGciLI z!)Zyvzoi7jiAlo0HNqtP+X%-`rO(S_60T8qtAr>R-H8u zghM3XJk{p-VB;Rc&-;Xne8cpp9!h;ollJF4xXv*V%(@(P6|I}DhXK9*~ZLfu~ zz2W#p!`m`(u)zlF$<=d2534G%tlOZ-UZZI_}E+Ro5%_%ZVQ z>|5m7m+^J)j9rF$U-{9l5zdda8&~m=8PM9I2hCaI$N4pxqwv{1x5f{PLuSvK{HAt z`$G`skHk+X*&Y2!7Rt5o_)xCl)<=+_LF9T@f_pPBZyD{IM<-kTW9bgn?=&8XOw;d9udBn>tt zr#0$Q+RvnA{S}k6v`o@E8;de0{q2xJ1n4j$rJaa5t&uA2M8Xc{@_srP)5PA**-m8> z8_sjEdT4QFfn(y*`OF9K?}Gn{D3Mb9^T4gZ|6YCYOaax^T8e+d{0rO5CMjoaFZEm0 z_ENq@UDgS|s~b?qO?%1J9)pI%%I`mkpZ`^RSub;)W=a2CNEEgOivXLp!0@g%Nnx&I z5|$Jz<&g%%v;vd|Z{tExl(Bla=9Eg7N_s^<+Ap?Sxt{QwXpwsuhl#9g!5m-Phy6uX zwhw4qxpb8%YY~b*X_!PbGCfcaf$GLtXUqCIc zQsiASDQvR6X3GD8r5gLc|8es z1wmU61HHe3tj~1RtjX%T2qPc^*BCfl;4D_=VsR$thR3oDeYNa2j%2y{hoKl^^4rCB zT@g5(sW=bos%fkllS(E+I<%HGT074%brr5q@){8=bJb1~%%3FXOE9S%2l!udYwMhP9qnf=pEvh&<~@OJ|Tu4BN@4T#9!f_xnZ{Al4LK|JM{ z&P)IjQ+yt8tda~TM0{Bv*8Fl2T)8K7k;c*^j=$875y$w8^ID~oGtvI;6h-TLwYB5k z+SRp8+{ePEuQdj8NV|pd=gNS;2dE2JWav|;ZfBjEg|N0B26}I!PRWe1N>0~?Tf0eZ z&CG)8yYelR@94_Vxq$YvPXc!^fjuPw272!>0gk+DWa^rdk+*eQB+LT=YXhWiR0>MQ5T}^ zhp^81(GmyFCteu^>vvt~s?hkI)c9P)Z0lj5_cyjCQ!2>rBH8{z#=yeV3W~dn6bzKw z>%B{E(<*51A?<;}je&*f6%_Z9VxCYi(0dPz>TzB5`*f?C>Z>!HS=3h5R%;(xQ(Gg8 zc%8qN?}LpPF!JdSE?HI@tjkJ6bXjSLE-MXnYlnhZR+MUFDXMf?X-L_!(vZ?+rP_3u zMr}zNwf&`0=hoK!zok))AwhL*b#2u}$4swA;c8Q&ho%)S`M{C+L3?rCYMQnAVcN&o z*7eqs_#hdo+SaLqGWYBHifxS?bpNv<{C7Lk=E(5Zwz{3n`3i3cA15)^)y>ATSr4tO z(`gefL{0Z_FAQgwapJzY*&D(3oNwfCB+NhEhc>IUGo!UIvpBX-gn+$w8{$e=;hhU8 z_d1rQ_}<(#;1A1`Yw5&;>*(O>xa%cV@UXV9J!xpf z=VA4l9T)XFS%z9T2!Dwxj^)aP+t6n?%co-ii+jPi3u3$879>Fq7;los^Di~}o@vfG z^NI_PH-&)zJ;p%632uP?QNB0Qkpu9-Isv~2!r&%=cyFxQ@{!C~_L-}$}1>cqX8)V>?5DOo(*TiXYQdUlD?39V_R@{@kQM!S$dy?7r%YMza*7$|txNQr| zLmszazxf!ND%uO|i*^c2zx%*xsEQT3OWL&AfP3Ai*`~?DZVv-JxvcvEnIbU*m6!vbRAK)L;$Ihu87Nt; z$#AxqDjZNj{hJ~+13lhB(XkJR-8_p!uh3u~aX&f{DQ{aUCBp_ULQN{w6W2zN=B6XaJz*OPD3Ne0L6oY}9!vlnPUIp>*io^_5V(!+b3iB(7pDhwIP}LEZ>4y?& zK}DqB7b7uHQs{gf#6~N!={1^>QSv_ziG?>Op|aQ`yW_R7K3PAgBCY4b=xsd=EY14C z6~uoi5;L$g>xWbj|FKBSz_P5*NKkt3@A?UF)}B(S*|4VyhgLBA6Pf9CK@7C>Y+6;YlqMq!}dF!T?kSXfv=`vPef$f$*Z-iILbJ`(9plajEE_MP~%heDU6%~uR zhag%9zchF->Zi924}a8%eU_bP2O@N9AzKIA91wB5r$cyN&mudq;8euYeo#z$nrdp} z7=*=BVun~7hn&-MTmzNqf5HeUvoYB$X7~(*Ckjg{G`W$Q)GMSIsH5ahko&?>6|}Dw zX&LB!%HlG5tde__wq*bF5y)^yU9-0xGSucDio6$&u3+{TGCPcV>0zMv8JJb!1(bxh z6`}=s;Lr+uOsa*T^8QJgj;Ubt8VME&8wPrxgACmg>LSaRd(G=nzU|USH+$QIHH@A; zUgH&sScOM)DC5!!=C2oJWT41cQcJ3Hha(=$g=kESMYNl=9BDp+y#Q1V$x1z*AvuDm zL}m#H>Jr{!`jVJD9SNWoo6%D5zJTN%yMQ8vkR=H5kHQaDpo*Ag|7f5xBTG>AsWwc| zjsdzp4GZ~n^E`hkJV@nOIO8wTS$Lwi>7Hong{rnahEtv0oSE^X!LkzDE#Q{||4OQG zY=tJ?peBw3$F?2@dS9|Mj;kPhlVryW83VnqK<0f-+T$x|-y-b^Ld!t!Um#=2sg1=M z)~Wx}H38dNWc_6LAPS1|tdcjT#<8CHh?`c+oia;qJE;YYuQlZ_1JS^i+#K_T>uv2j zgk!0M*idYRG`Rl67Fq9CieU|gcUH-Aj@UMY>o6^Ni-9Y1<0Vcg6HhOMOAz|r9UgYw z0Sv@GA{ol{O67V)QLdCV!N|qC!iQpYX#(^936zHl+$1%cpN&GnDrhocF2y1&;1sS& zHMhI@2%{u|Z)#5zPN*oHzp`+&Szw@tWA%Ka^HZYnL8aw^$END08d-`&-9H^kYz+IY*-4{{WGc$LUy=8>Ly_8g80hs<0CSgKaE^xsYY7m|+H_ zDZBHI;J5I z-{Z>SbdXGBCdhc}ynTg$V-tQ^{L3DP>7yp;FF>d*>F70)+P~Mx!@pbOn<*hv@g#tDtcsRoKr6!|tO!`y znjd!6(bXZN{S8(yadp5^})8a3LDorV_8&GXmx9|jA&dCvUUp5*pMkW2RQY&dCja{$JwcYsu0 zW>AcG2>h6`VJ5DIh~9ZX=vo^3=+!`vwY`OmG=iBf<)$5he-8pf!b1UfJVin|f}?1d zekRmxavefK4pdcBT}-;|j`I-^P9tUT9urG|Tn@$6Uxcyf@xEs3&W=#Jz{2UsWq6FD z2XL?Z4GFGAm)O?BKpjAzhhr4pEc{?EHG3eeD{JFYJ}PR4+xTkODa(nXLJkvU0z9@v ze3n{5jS*e{E_lT1TQTwgIyEY$J*BEhq7xOJX1K_jv^}y>Oi~&ze-)y!%(acIv+e@C zEy`VM&e{eJ^qfSc>SCnDvh~=dqrC4+3tsd)+J0hHaC4!-^jQl)K(#ctZ3FiTl&XSDC zArdrmQ6D_1@O?IzdPg(jO)C7LkDv4DV)$y?JDjz#QPMbqU|PUM0NH?oGw$r*oz3l& zOOHFE(*4R_buXCdLCq${QUW=7sb?w~Zm9H3CHF%&1L^i`cbC&hp}5A&+osWnczdIi zi*LTy~Xy5fvmCPJQTw?nB%)mVucQ4g_9sg=W?Xn zQ0;_R+rOPy3TjZ*=73fW8eiPAQ<2txm=>oCZ9NQ>NrSfnOkjOqIIDsj&*6sT3@jnX z@{0C}%aJ%1Kf-)Yz1BY$s%{psBkYQ6{T~1e+q;B0GuAYPE337M?6Td>%ef4!bL0$J zXDh_|;I@Mr-`AS=czH^*7BoiDWdhV|>o1K*Qd}6fFm1y?4h-74(l6hh1R!PYXM3HQU8njh6_1NO_R#y9ST!~p*Y~r z^PN(_wO~cha+Yfh9#ccTWk6sP<5h@(3Xt$0MMDj17nXm=D?rKst|sCC6nGingN87= z{<}%@W6&i0$AA&DNhc}RfOj$(%v7L-(vg{YlCD&3>^xvT=^&cxKfq`l?x}PcDAQ`J zPdK%-(7%t#4I|Hl_c!e1)F+|Fq^L11R)3LJ)0t5qgEwwMIWcLdPuZ{w^vT5`msIqv z*H5&q`=ApfUF$S-+uqMXly+MWzwpg9JM9HpxAlm1)!XdEe#UKmV(6e%_nsccGsG71?xklWGqMI_=6&E;PLMpa0RCuIq)0*se!O#Ur$C z>)4N4Z|s^VLGdqJZ|&Mwz|ptt_-NOer1}Owc6Ki`Io?#XWSx%_`8Pq%iI8^_`3FHR zh>$lE$&~Eu!U*{tB5xPu(GimSk@6I?ojoo>o=7B3LhWoeLLN-yQbDeakoytILB5^s ziID6ZGcyEvR)pl{LuMR)9J!RKTL!m|T-MaBuPYYb#qCSoa=_6k{Ee_PyP?-HV}rDn zfYQ29elo%`BXyX{$rwTG-du1mF>;&2YKG77GVeqb_V~ZzX~t>NZ#H3RVGA?{>5L7E zhW6SR7JwzPti-~S_o8D=M}2flYdN{5HuZmVw)fX z>7f{AnYLm2k8eGDCp@5oFLmC@YmeWFJ3wyhqn94vvD1zMF8ZW?wE4<^IyckMH5UeL zZY!}}b!dMwHzKcY>kqbU%yeNN)Wq$=OVC#490_`V&GWk&2IJe0o6&{aB0xoK*W098 zC{!2xbI<`@HNtzJV|Sh3^=F8!B6S@{DlY4|t-tD7n=>t0VdYxiuzmlOwI_5P1S+?c z*sdcPAte!R4%VL7^@3z?htt+(yIvQNY`WeDn0ZrDTzKEDJ%-mIXRJM`i<3&XmDn!c z&Y5{cs6M^_*3-M1M6SCYy!EUuHotBw71%Y8R96Vq*?(wz(P%OD_n%&4w0Pu0x1MWK zc}L#vacf_?x$8wK9Adk& zq#7<%2b?$h_g!BJgJ1u)?fI_pq7;hMRYR)JMEV7b)_-EOc;mtAKQwFyAGH1-U0+HB zV!N6t*Ao(9yF=H1)OEfDJ$2ankGs|jNH$#~$@W^I8ohYbr-s+}?@N8!Wl0{1?dqnk zPZp}bAHM$6u6kkc;E|&~>slz%zH;=a&$|YZsvA_c+z+n(!C;CpMUb~f?d(@fbqhiM zI6}TfWSt;?9U)&P5=*$?`C5d0j!2kk0r^>k^Tu~8zQ&G zk0X~^c4xK_xC--*?o5lo)dF+i=E&Wx-5HvlIC4#FcjgP}AchFd`vMOY_%8y-1?Cvb zksF-5GfxT3izd4>4++d0YP&P{3e0P1yJZ8>k-KQSGgkFjbD;9*KEY3~6z_cpcK1f&MsmGKM&5zPNk} z05kcnxN_`QX-81l;JCo=h2A1UHEO^~Ke>Epq{5%_;+&LuEoY=jM zk0K{Y8DG=>?PMgo6J(b6V6gDd-KDhKc{tFvT-1e0B!~KH_}0PUV3`dZE1R)_6Bc;U zSXorq!M*q`c5pjb+Z?TXx5a&zAEM&Ge(yo8KUmKvg6sN%8J@cklD1ou5#RvtEToYg^%<_uCc(!VV%S%uz z)A2VKEPrQkVu>T;9cuCFzbE4R?)3xS^oufK(%#i<%(rxabpK5>#Fvb_mmy z>8hy@keB?zJjqhK$rG6gzC`x0{5A|Ac>f}{TbDW=oQ7*+CVi9g>~J%bbuHu{y7jnE zfgSm-#c!mkT??hLUB421Giv0^72TM=*TxFpXz*f}rT7l>jp>_gO1}uX6oJS`JVm_F zH>xp!N^A6f5G*62ee2S~@)wkbRF+CvL}MGxy&i(CNU zfqOc{381)NhByHf_p1;mfa0DBaRMmr*C9>-;3nJNpOH`8Dv9$D5N1wHZIBcj)7Ps( z$39>#T>jbKvxw5*)!>!v4&B`OEf8&;nD*dZ)^;_0i0(ubYHSN;Yi$en%MY_XQsW}T zY%>wwWjsJY9W1e%1Ilmb_DkR%Jc_8>`;cowt_th^J-?v*(93VadJ;$NGamV@i)($E+wh}r zpzBohOd>B8OnJK~EFc?gk53+1C%hsaq(auL|g-f*8|qGyQYBCwmC z7DaC(l8eA@Hhq0aSx+PzrRMfw?Zewg4s9RNKC-o4)x-@=);Eq#?*Ppn)TP)kWoLmc zZd_&=%%EO{WqS@iS33Rdf@o-lwRPO4haqoOW(KGd{<}cj!tm;(80y+<=t;`2_ZtMt z?t@#07xoxX++X);`C&{Cf4q?rCzg+gcr|5b`ows{72^QPj@=R4%2;z&%XPg`U$nIF zH`EpXE7YpxPhkX}`v_1Et5$o|?lxU`2+|K;CbD=>t9a2CQJJ>8q77r$;$j@AiMygwDmC1OELa#P&1;I z+)ewc=w(A;BV4<1Q3;z1g-tEltPwU0^e+U+{p!3#+(uddlcwtvtA_d~ku z^NvDWu_w##JCyMqpuIHok#yZ#Q zWyV;?vIijf;RD6iSeul^dY`eDAXal2i@6GUejuTL;3SIG6B4N;%>`yQq? z{}EImV<-W@2e^+zoB)dZB*X~-9PgYC=N&EKZO&c-x#Y;@LfqjjBNs;yo5&CVABOG4 ziO?a8$#XgyD~5A2yZHV&<2ycoy>6(v!$Z=_#PP{WjbUlnV@u+WPZ0!LoFmZ|b|F3! zFO4uXYW29^{0KzhAF2|N(wQ~E9wbDF5oas&wLp_=@McfxL&x-mZjq*4#)M*Mpq4Fd zN@=*uHWlY*1*S<4Z66P$Ao$C*AyS(y+4Y+hmh8NbPr`3e1XFGB_N6d1eXvChP3q+8 zfX-#{BPUuK{7pa{)~%t|RiK~EdQzgQ7LWG0(UeEkp&Ac^Iu|n?DLGA8tX)WdNV9bh!`m-Q&WFrvH5nEuBycR{!O__E5Zqu*Ag~lcr6m5r( zt|~JRBndH%myJW1g>pDolWM*0PuF$Ha4x08Ic}V%TGRW@9MiQyKoWGx7^?w1Qmx}U z9@|BRbE(!ptv+vh*J(mUY}fVxGyf3(O`lx-gqR4VTCLsQ`J0*ua4wrH8Bob^jvN2U z?Cs}-gS0%E+%8BD8PPGGPvqr-T!I!|k*uD27gRPAQL>)M#(z?tZdFR!OvuK6GMkN} z&mfXNt_;l=B66w4)ymYDTeg}R_ zJE9VD@;w~?+)?0$0_WNM3&)Fdr2J%f_^so`Mf^q~R@&=-yvXoqyhu-SjYKIQFB0{? z9xq-z(pvUEju&T*+^&aHjMfh^-0k^>O(NUN-mq;H0Qm;)KOs&4#aY#+trY+`+8llt z?tb!3Hlfm{ZEqNy4u09$nNuN?BK*_wefS0Zh-JL7xxht!Ag)dho4+dyYy|$s-jG z_Mi}p;_6Jz$oyAO8)?4-K|5B#@0QH{OxUFitCg@hsM*RQLnA^ryC6as;^ic!{GlFJ z0HKAu8ZwU+bB!?UyA0cfKN4nGTuc^RjV+ZWjB*(Q_0@rie~Rg=1KEX<5Fyp&<&2GW ztLg@GVd>xxbI_P0mB`-&gBkeQPx2GxUqAg@ad0q0yEdv-#|GNrX-mmU;rRRwyEgXy z?AMOqH`9s$B*uGmxG%7ORG-SaBq_0f!k#ni*T}Dof{3t3qhz?@$IQPNZ4vXsbO-9o zW|_%2>vuQ<$G4CS>%9Qr5w`E)`q%-K_y1;nEX}mAK1R1}eT)dMkI|L&F#=|NY%657 ze0?m$RIHCh0pa@Cf&3=gsVh0xftjXd&UI`}uBGc^_5wo62KBWWsdG*^%($Bvcers|=yKgr0IARUO+%ajirXy2 z2>=}LO^G*ohcGSfvCadIfBLPM9m=E8{ODGyxi{sl9j)@c)vF>3$UtE+1EP#}d~nQL z2R4t0_!>3G_Kcf z2AF@UvfOSou^t@v*Wz;+><&(NhnLaEt>vIS2y)b6f#_-^SZ6uG72?K%wc^Hu?}>~2 zepi8baHRrnaE$_q;3@&UU65XIxxkIVWpLd*eN}aXU`$+vMTk0U6%igxFXa3DZ{=Rx zkK6|vxo?#8oM4@}vEU|ghzGYS;0CuTkO<}Zz-4fpk?h!W@+$(N8xKCWjalZob;QIn-s&@(87~Bch&C{o;)@Z6k zm{e6)X{$v_I4tAl3+KeRd@?w`PD=PF(Uj6^`5tQIdrVStf}e;R3w|taJlG&EQ!!M1|jd>3gMP%3?tbRV8^s{n$- zjIMqyd31u`h>LZ4apS>L;zCzXD-aKUsel{&N`XZ1i~`BvcLJ!=ej#vU@N>9so<3FD z5LFrxq0&mrZlRIqHHq#7e-bwqyeMuwcwSt{^Rfc*;Li%U!7B?uc}wJ4tYPoYcK!w=;nhPx!;S%a zlm#FGG2RaoI0uR4W-$SrukVw<_$UGYMli-r%IqI-2yh*JIDnrg1&-7oGYO++xi%Vs zSSLu2e`(_oA=L{YT)hASS!Eu1Jb%wJp#o8pWCcCe-eqAXC&2GhhObF_4ajIH8C!@@ zvSgw*>0JR8`8Pq(P^8!U6DbwOKZ$zSmwKR;yJj(%2x()rsa|(BlfIeCZA6+L%!_eJ zUv0Xx4Y9mih<_N#MJIexdB!xyd|=g)UYV#>9Ik*Kif-6j+gG|X`;$)3>tI@BTQ67> z6M6>UwoV+h@< z@n8o9++d0Vi2ygZgGQUC?F4QNwuS5F>C^UOIHX4VK}6UzMQtU=Q%4y&_LTTeFjL%E zu&cQ7U^j6g$LsEELyUk5UK@6}T}t1g@K>PkC>uyovDc0AX>AsbijF4DaO<-wBq98w-4K2dAt`OI|ljU}qz>UGBaNRt8%DGiJ z6XD(6r`*^r9dCHwDDj=(I&pD7rMU6n262&}bpq%R^;&@&gKOZrdHOW5%{4J1!hvyE zUQFu*lhzLs{ZajEJT%`4)0`Xv@0Gj9v0yhTF!*%oYX`*8^Q6kF5`$Oa%fD@5s%>NHY8<=F? z2qAEDFgzV=0XoZ}99wUj=-{TInXO*5ISuX@M`*owF~h{p!c*wZI;MjxS$L-gi9HK{ zmE7lSdk3IPQELBUP~(VvJGvlQ>r8on1Yt>(goZVM(-6nIA7!E^fD`^dkz_miCmMYt zw6vVEMt5(C7M$R(;>Lov#f=B=hzs4lDS-EV)`>R+ZVX*ncGm9?qLi0~dL)`@AF z4-EAE?;sQHu)Sfv9RDKvuqq96hl_xSJN^$5Yb@r&KG36Z~CV zY}JYz4?YnWvVN#QJorceE$0seZVcXs>*ncGp~tGwM3j|tT=o!8GHHG#v7O*cabv;P z;>Lr2i>qq+Lg2>WbGUAvK1~4!cGy1=5vnCB0~)d~H)+ILw4b!ajRlUl7|V;RX$%s$ zF~Fn~Y0#%>;7(DdK}1;^OSqIFs}5$-0gCm`hq9q}-p{iQ(qDn}o$M%#7O*wEB^b#9 zt{lq46>I}WkMh88@MM#Rw8-ZKDRE;#mALVsT3nP(jRNstumIkVSfP>vHwFo~Zk|5P zRlDYj2=6CSp~AY$G*2;UZX&UrV7R!kphetxFhX3Uxv2v2U^4+U&0zvJ2F-BYJbjwx zI8Bp?e$qVEq}e91G0zb<7K|1*9*hweX>OrFJn#h2G)D>C7;FyL&C{o8Zl!4wQIh7R zPHqD-4Fk7%C@@(wYK!>~0hRj)*0^#Hi$++EaVf`@kWJUTosKckk0WhO2dtY6xcpSC z?4!NnPO5AB-)J?6%8`5idEvp>c!`@08e=P17Q4a3W_h$s+|1&En^lbKKZMn_4o+(n z^zCiApeAVF0bFI?D&7FP=G^Zv%B)WMF$Cm(3b@VR48LiA1b(Y3PZAV9L<@<1dv3t& zi!}5yfQOKgO^m^SBXcL&BXazu;3C^M9`-PJRk8YLUPDF_WF{4_L8B`Z-WG__)e&)i z9-PBT$hUF!u+5*&2@mXzW5wRKwzoE$qvK($`)-bvNm<_&0PKMq72*U?+~^P|0B{eM zE#&UR5&dE$g^R7diy<3sQM!~)+`AkOzUi@clau>y2sbx`FzX1%0D-B$lcXj(!M5VY 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 &075Qv=WA+vP92B5(O=;-8ki!KUdW03zHFK zom1${{}COzu&Fp7rE{h@AER?KaXwDxEIPGtpr-TZW>~lW-_nN8BL_qE8+N?VYn}rb zp=aV_UQqX0{bRnQp=T5G6?LCO_p9o@lx|sN*keOJ{c=&VojcJ+^Qoephfj4je2mD# zR8~XCKfnJ_NCOypZvUa{Y3Q#IYTm>TFfXjZl*0Z^Loe$;^zRyaQU9UuY3RlMhkl@; zm-HX{v4(Q4GoP#ba=O1z_Z4)1rS9eURMn5jD9tk(dL=RcrS1#pej7iWZ^7$eY)nE; zh^{M+zMKs)zv3REwh8$ep6)~Q+uj^|1hYdP`UhgHo)K}l@GPlWTI-V*$Fw*Q9rULM zFmWhsg8(KDg>4wX#Gx>bkG37$P;gI^cTi-|a6J9S?dG5nhDDva)Ntihf1BGa6db27 z4Mrpl=9OK6?gQO!I>iE6Bcjrie+a8N0*S}&X7T7O;n7ox$Bt+5=q%yU6X2m4`&O;7=7M4uwIZN@3zq*o*)s4ux?RYNH*5 zZ_vOpC!tWtBc~+Ew>T7+bCckHOEN)qYHl1is3K|2=Pl0B7@Pk8xt!l70B=(;Kn}PmQa3-U}W>PN-M~6?t zTZiv6_ce5W1fkL{!hE4%cl$U<$)71vDjxUMEIx7$deV!h~QaXL}nn* z{0Eh;JakTWd?g|#%6ZCenWa6skGm6X2i=iW{!fSpy=KuSD(@`JD=3rCIWvC;_7=G8 z%10GMsj5y)ipOND2D_S391l6Q?6(?Anjjkh4lef=Anw0$Oy)ItGlB-Ld&4AaI zF%{^HNC8uL3|t@YgPn{Om}6y6w}CURq=&Vxb6*Jh2PjiWp6bvyl5l`r+D)pSg@&5r zk!#$KDFtFv`T{_8vF5eMM-STk9ONn1yjg66EjA~=7;Bz--}2F$bFjpk(?7UsXxB|x zloi?qOaD$=toe{Gf5R?pJOF-eQTrxcZWDJo(kx`&k;ly77oPUEIJZwCi1^ zvG=8$9?-QyXgt~6eL&YjaWY3;FDs29y}x_JW+{H#0T)Sw-Tv`iT~owK5?u!>#?o)Mc-zu=;EHYD>N;0w5E@{N+F|Y|U8f89iVrgX z=t>HWx39nS>n<*mZBf|<>N`kyc9Z~Wt0|yM0qao)!AqN+nHwWO$5&-Y45ik@+Iy4Gz zsRKZ(9SRt(fH5VaLlm%a3ByystPKEku^9WP55LfzZZ$TCdvQBeqXcSHTpHrQS?F{kZFrY7$n3X z>&KX6mRva67inFvOePo9mQFg;}2=yB;G*GA7EddQdsbF$}b zi0b5@>u-v!gPba5@_)v})aI7wxZ?bZNqHXJz1~)wlwXxenSthHOv=R_YS88oZH=HY z(EO5UJJz86fM{z4je+JAqW!oAZEK?aMbH>%P9@q-HE7!q?XQByK(mZ!JJ+DiCE9C( z#z1o#(RQgp+m>jr3mOB>=|D4Q;K#~pV$LAkRG2hP%RrRtU=q3XmZh}hk>f0Js0dVo_7(whl<-? zPn62aW!vs2J?8?r`B&8!-`1ki0+rNbakrYJw`bDtNKqMR&O#}RyVs!2Bii2tje+KD zqU}+GHlJwk3K|2=Ijp#IS#jjkD0{pPc|=P?E+p+u@u2LZ(cE7K*)$c+-KP&ob3a(B z0@rxb+G?wxI?ZxFLAjcw5Jro?r`1p*Rot^C<2x|p?@PuRXwGBC$;y@MIO5y@`uwZN zYH_a`Qah5=2SSR0=6p~o?p=fSW1@X1Xbd#JBHBJRXgd+@?}Elaa{j%6Fx75!V_=^CDm#%T_&TAY~FD)E^?8IZa%U%sFX*4IGwS==NZGhRJ@oplSTPP+t zP|?RAy$bZgK>$;R$<#_KOV_3T2H^TC{l5T>c(1pR)$uW^!(4=l@IQgeYS>N)Bz{%G z)PP?b>%0>gBr^D$N6M2?Eg9h+AY&A!VR_5&9C?N1EhA*{Ufx6Qk#)R7rnohnt{H+` z{&IAZwYCDi@)=MDyD)Sg!94wtnPnl|^Qf1SAaB_*7}k}7Jf)mnAbcqj<5*fz+=j^& z4?)EQlKX?{|({Zf91 z=YAvK5xJ3<(LXYGx_p~*?&a|Ju*@pUJ&MvZ=1J#B=-cm(w!p$U6lIfp8*>@RJ+wHG z=3LjL5$Ku*c8l#~^G@6HH%X2-mx*+HI=t#E9$Hb_MN-vQk>^3gN_ENJD8QdhSt=!YBZB*ZJj2`{ zfXy(@^80}<-}+n(qOeda>0%Gyv}W53R8{a6Akr)zUX!1FnV&pVC|Y_LXf8#5a8$KF z!9PJgn-n|Q(QH_qSK|(kUtshi6>=MW^eZUl$so_Rg-iyu3xyi9k~W2ySv*p@bQBB( z)Wtf+!5i}@!$MPLCXXP1hFwY`W`>oTgxEL`7*rpZ%V~NIpfgrRM$v6F+c#;egtz8q zBR^SeG~Y#jf(I7fgBvFt((uycc3@6<3oHz#ouv@mU%&S&N_R8Wg-id$(UZkIt$6CM;Cf->Z9m#fM$W;VxNZ}U7d00Rue5yo-LyiUS* z3(|o>lu>}|(~$`%j7H3^@XI+#pA!Pz-wknaQ&;8-u!zuF9w}5e$J={w0HX1k4)11= zHr>28eIUO4Q^_y}#SR?K`7h8R%q&LEe@W*|aehVT=HmRC&Mn3HPdev_lY3w@q3moe zJ3HaD?E-nwj=>ld2hs=Z#{ot+*nt5|911%qfQdt3W*xQ|bgWwVos&BOmK9vn0Z(9{0%2QOoK5X_ zwtl-04(N#k^!Vn={#f_7lyNeeR8GbZ-g%=FqjXZ|4nT7aDHWGgOhrE2z1~5bb26Bk zTY4C1<|Br1Tb)f3jR~8}G5k~{buYa3fon6CK?mYTgP!XXWM|V2g$!~1Db*)as{t!5 zibAb&QN*1GGGr$Cd%9LiY;dWGwyz{>2x^B*y zSW5E|5q42Y$uqm-Qyo{p!34l&Yn9fA3@ug`=E$hq5kz8 zc-3Env+S~#9F>G2`$)(=Vb5%htm@4hu(XM&xdQcTevKc@*t|~#an@WOYBMyq=~m8? zQrD1q4)-p1MDC>8L*~bD${IBaN`E_5dUgmrBe{Ahf4(ce{Bw&()bxde*%xL@Utk~? z_(?)J3u~6S8uUZT=nNEeS+9QM7V|aOi^|vxRBWCb6qnZE9!gwYuNf%oH3|USqiS#u zFVkgUK(}eyIOimci#Z$@_PPnYPFtw>lNuU}Nkaz#17#4%iFKm=F2wQV#2QA;cwjBV zE2^i$mmpA`{u%7s6fjypk8`!d^Zu1e7b`n_1zKgVx8O7TP~$oAe?g6CmDKDh_>6!j zu3F<<;chy~%!GtwZDQHvBmo%f`aiJlkRQvA_7#d zQL@H&IZwxR?C_mR@F~s@*ha-;YO=7T+_VfdS0csYu{CH%5bZ*Yu9hAKnyWaP$Oq?X zN;MHGcGpD7G0L|nMN1C@&DDtFW2OQdbELH4#&AQUfkRDWXu5(sxpWH$V3!PIsRfzu7=PHA?Q;E}{V5Zne+f})#Z7Sd;B4D;meu5Cz@ z#LhrmGX~*s%h__Xy3ZnfJl(R8;e?m~T)3H}E1KuanX&*pIq)?`WAfnGw5glN#AdR( z<=A&Ub<2_HGgEBi*;w5?t~KrI=Ap2ep>7_+n(gQg`TvAF-y85% zo}6oo*hN=e1(^MTY>UCA(i6QFbB&aXi=gMrf$y~dz%CI;iWZG7M+z|4A&m1Zd3Nr{ ze*pJf)Mr?9fD-M^@Oz>I1WS2Q{~-{G`j5!(qxhA!u@}H!?q}(-;|F${I+Q1+T{bKw z&bAysW;8Nc91Q+I`W<#=Uc?*(Sjfxx|3T@+lv>g?zh{DB|9X4|>}$qp=nT>rBC@9+ zB8{?APeS>NyfX|F!65u3#nc_P2`)S^0dYF0!(NtuD!y^EFS4O-v4n z{N|5B>eP4aLDF8h@H2c|ubaMzcid0$F?%3U>>_`{YxuK#zrtyZ_<;c+XEW6|n ziUrIuwBvZH6v};dGOf|HV&a1>C_EdkyKHZ7*;rjlfXgGeixHJ{Ol_URCpI!F=R7WL)PVzph+qa{G#& z)G!H*T);ve7uo#LC^?=FHP|;!Djd%$X#xb+ZI5A2iFN5uI^!`@&= z_i@^w1DixQf*tKHYyE_8mCiwsr?&O$aNlGh>aN}`7LZIJpHYtbNG2AQZo+nxpL%ZK;gB8-N9UDYllXAGvs$vS%9EmcOdt&C!^rp!xO zW-8gX^e|8in$WgO(jiO4zX}aCD;vzTM*vqQgctTI)Y+`*_)MFG3iHam+-w=S&M|^6 z!WjxRTz?k%BlgL$jlC}VuJXH0c;}PnY(Hk*=1#yR1Fg(N&K-sg167`r!B~vujw7s6 z(>a>EAOMZv^>cKH+`Es3R8E>Z@>VF5^WrmwX={6|_%wGjqJ-7po(2xjt(pK{fU;HB zEJB-VB)MaSsNbE)J)kx~B9Z?(5lu=|tLr>j1@ch{P3mKDKaOr1_w%sgElWYwoQwV` z{91ZYLrcJ~4Rel0MeM{rC5CW=`aC2P)MqsJCf(Kb_;FuQ?mvXEPIBpNMG;5Cqu`B7 z;BrlU7ny5H_-KwYncGTWTMN9}Ru}TO#;3l09y$+9U~B^q%vwZq-;qEnTxy3h^|wlW zK&Z=wSkT)R^rl>+OI4^hb6Y4d;uLhYm@2yCB4fEZV4NGaxTQt#ktX5lnDW$^f~T@F zYX|aSXTOPRb)95n1;wf#h$XmuhlE zEcf{wj2re9`>r@eBdU@tmjYTw#`FxGD6UzbqelapVz~zcg39{XfKkY@9RF(+kvk+X zG1%bI2!6Q~QCLA6G6o^<2O(x&DXEIllLixbF*qq1KCsYBQ)C=L_>|kL+GM@u-S-}YKxo{@14-34uw+x`{ICWj(-cu}Yo>wr$~}m~kt=W&i`AGz_p!oJ-X?U&19kv!0msPF*CDFG-3D@M8qxC^-I_d+wMC+NJ|io*{Js=*&^i%y zA!yOoAES!=MxFi_m{$R2)Q(2rdCf0D(7sS161rA z-V5=B6-I zn+M1nRRaj_4|pE1io0Q|1O3x)}E42%cZ zvoFQj7dW8-aCXS!RlY<^#{YsJ93^tVgD&QbJX7MFAr6Hd8NkG$u%!V^911%sfQbXJ z<8kqtblgz|qPgYEHThY*6IL^H!$UqeEOv_aLmBcvmwxRQPC>u+y7{a6 z-NTT8^>b%b9*cN%K+(=eYBKmj*}byhaYG~b8VS3U(24j7vVhTX5=TdAo6}jQn2U{! zSWBa1jx+dA$IV1%U+@e2v;X-G5N_u0!w+VLCVY|*68#b1`Hgkqd78)dfgBZ6(11`9 zrq)Y4Mi355@*G3qdGc8RL-PzRE)E0_|z7L zn8;aUcDvZau!zcAk1V_8!i|N?l^nf0!!2U~mCHA?D*(KG(=n|AAVONPU^E6nX%9nK z(#HM#gIV%yI=Xgq;)lh)m0GJ6u1s(H0lo-j3|=4`AIMp&_~;M~_oetyhu*0=&bfKL zz*gr0&bvPfWO@;%N@oT)?)cm;-Vn+P(%42Zh0eoY=x% z<@1Oz-@|)B!y#{xJ7B??Aa#5x^FI)D%}v;VCZLh}Pf!3$C?Xhc65WVAd{XDeqW&6$ z6_=q~2D{o*;qLW*$-XrhOHfM>1I_PA%Hxb)*8MpOqz+>KPnA9%E}TwGCR%W?V-nN!Ppe7)lydqEl=PD^d53ROehBi#`o-L~x+zd}n1DXdz1ej6 z4$o~V-x0YTV2%Z~l5h6M7JujY0gzEC;O>4;;x|qc%2=^aof`a;D99ydT0V?p+*j zTezDrH*z&Pq$WSLiE}%QC)3zz22759kAKsq1!=*(v8F}4u&1GZ!S|iViVqde!H4r! zoyZp#F%Mz_3rikUN}<*+V_cV-nHc6yv})N2Y|Kzj5p(UE?HFNp-bv%k@;Q|DQ1+Dz zq_4Pz3y`GO&0ne4T>14Ag$k8QyJ7pI9hJAWcf`xj0NEI{8%=-i&_&fj3G9L5QbnnRe3Z|@IBWu+y$~5c zO#APz5nT>g67B5yo|s!sM#c8OT||U$(pimTEn2shUDk5EPuFt1jcqZIQl7WonXyeE zua3xh?BPh)z9Yg_sWT2*A$^x51f3a9n3%p~7&A3%Eyh}&Aqz|_gZ3ip-M`R{CJ=1; zbDX*z0-jrK%PwZ$zF7KpsBj^AHMlD>@V(w8tilxB1_B~zwa9^QH&KE|H#m>2Q2gS> zfCn@2MR50emot8Y%)|^dw{QvuPbX`EJGRBgE2N2GGg&Iv?1^*mHaznRo2&YCnQUO; zr+uD_fa0@#1PEqZA4aLIYPG_XjEeo8s`ua)Fco->CKLILA06&UU;P5Cs12=bXOrJ(1tC4G)50VnLvebGB=) z}HX?{qTGvBJ?n-*n9P|1EOXtP_ZX*GG)IeJq(mse)I+0@h{N@2=nrEXEb)< zPy&Qplb6>;hN1@BT>okC0IF78zEq=7F?r>M^umM)Nc`16772)2a|?;^M77^aI-C)t zsWwJ#MPEaFUO{mE7ZKH12$d#X|7Admmsa%HOQgpxXOGQFk7b~_O{wq>hU@>CaW1Qg zb7eUW1A{nn=R$M-1|lv4Gd8H4eb;{rK#?vWmvS9LaS85&3j}$@)c-5O%%U(C;k^#m|F8lg&$0R+RX})=!}UKY zL##sM6*aYU6>CQoBp6uybq&UEh%rLyoPovVH5k_rLzNyFh@*g;F&y$E5huqRBx32= z+<}U*UBl{VURguwT2fLSO$HXPs=>I97^5UN3@lz0uyp^9SZ;G;>2K zmgeTp%G~^e6?F@+O8MhSuer4Z;aJgq&PeAhtkckxP36acV#0$dps{d3ce5EEeWx>I zFlSB*bf?Dwp3L(XY{Phb>Sg7M`|H4+4)cwR32@THulQrDE1&tuW}yRkb6? zw~|C8s_C_W0G?XJG2&h9guUKGgys!?%nBBFT@tsCM&i)$*AUNt~fIMO@Z4p9;yP0EeI_k#U%U(bUyOq7D4d11tdyA!eLj_&fU<}5K z*Uf;O+#_QKN%vOL-GFrO>qB>h1E$P@N_nZhSbZV?P7dJPIe^(zUXGO#@>leMKK^q; z@96`5U7#!bKxYbt`>UbFYmwK0tI{><2Iloyu-h#?3^bQO>Sivb=*`~ss;G6=`C?a8 zm_v5DaJ8>VTeqDZS@sZDlAh&>rqLFJ{2{hsnp+OvhqcPg^%bw(Kv?_j(>x*>`W z!L&%AErp6@+O|7n+Ta2bu+w)oI1aVbcPIFiRiD>tAgY}{;t4YB^qm7Y<@Y_}*x9T= zwdX#&3-;^?DBI~1_`CP^4?llp|L~Rf_YXh#f&Ssw{;_}fv`71gpEv+~i*_%R@UzZC%p2Zt;jMNe+ys*^;;(4Z%J@v{T?Uvdt+7oGS{|#-$o{* z%lNBxLzmRAfU4?-kaGPp!?u3kK|p*Nf0YJZQojPKqCrS){cdN^Mw_7-<}4~)#0hq~ zdmO*!0tj_jo<%tCB%ozwh~P0w8NV$6jKj zoa0{1{PL_s9EhC2i4p&yzg!Z8i34Gb2l%A{TpWN0YZAgP3&O-v9rk;M6}7&s`0h2d zt;+L|1qt85HQ|k5C@!QX`u%@0)1YJFBL;vEgrE5z@du)l?Mnyy^Br-+$_z$knZN3C zRat=i1IvxNv0pQUQO0Bp81zY{@9DNiz8zdTy*ERF81lD5Z@v0GbUrzE-IQse&BMLJ z-UkftG7_o5@#g?dcfqCmU__9|iNoI2bpMP$+4A^gA6g&uehPZ_c<|eDgXA)!B1=H{ zelmO_JIU=B$E!jQz%NJG!iNTJ|8(&V6jS+;$%wcL6Kwx>gNY}|;79Ic)sP<%$IfOX z3X?kw(IuqJj|lwy0pP0!fG0-xPiNx+;JE?dw+{eMor?948Sj(NJum1V{^bC49$V2r z{<{Ofx4yf7{KE!-Uo`;yPXoZ6d-|upZ~*vK1HeBU06vd=Wk2_+issZ4C831mY`%icO0pOPm0Do}+xY&R0r@YPM`cHR< z0pRBj0Do}+cw&72^tT%T{=-%Mx6ASY;HL~gXYlI&>1;j#eE4&H@L@;_wuDZpKZ_RilX zGQ3dXMo754NZ}@ay7^1(q1!0|%hLKlGxP!~=ON0dPGkH#Z2aFdBCP8F!A89^i0X9{ zWLlL#Fpo7O{^^W=myLf5kZ7BG1>^DBfH|-kNfJM3k5*o;KMJKmM%qGQNXOOFG_ApT z9rVrsy|8~FE6IF{-wr5E;hLMt!43O*jrpk!IQ-AUTmVd}W&QP`*%qLG1{6^cr3U`=7*%HG1X6V;V=dY9G~T4bM#}sU zY06D+WO_dqn1}9xq#%Tye%SvNP*iUY8q$Lyi9A-?U_%tkneEisH{)Eq2C&Od9p)JD z@s59%x?!TiIps=cQ(V2lxoBTq-ACA^v8KcvX6j7TT_|FXld2L6bg1inQ7q_n&urlX7yelGx1lWykFrB>j@uq|_%{G4tBmsw zOSnI}?6LOAjn7H|OmLD|Sks6oX}cfnX1bhHB|UtJigaTR^M4NO*Bp=1K>geQA@V6Y zi;#EUg35*Fv;=wnj}XEF>-^CDIB8H$Ar8<1-6sf}3Hu+}{6BcGatfaEkKRyQmG!v2a)Fq1F9?4)XDvycg^c^fjP z!_$G0?nE-C6Yk21#5F$$WVr}SGA2j40RYCOcMlL$22kpT& z6WogCnqPok#Qzoo8EXwVe;(YJrT>E37X`%jNg9jg_zwKF0X}pP&jC>=nS6-Ti{bKX zob*tCa!@(x0q%bDM%J z@>G&VIQ8G=<9i<3a7f53L26-JW{PTCMgLcBv6*vF`b0>}iB*UtL6+V?;aW6*VacBy z_*JEUpvDP_R;R5?xeSR6l)|X5*(a@K_b1@sv-wxxd-rpX-Y3PuOHYm3%sc<}OdQpP4O{|1SIEw>YE?O00xU~qulX2Bl zF9KCuW5VrXjZS^lbQIB}HPJcWRjr@6!18Y-8O5U}AvB~`npA`+U7BwYD7Du)5m76G zO$6u~LZTXyQ7X1=^?va7&fqo;9OzboD4ed1Q8J5XG#m(PPN*X0-N_eFlw(^?!n>@0t7ejet9mR$Q}XNmme z59J7a0zdhq@#D{z;BRo~p5u=M=kw)|7+#9WjmM9M{3Hk|yo98xR^HvqtDY{DXO;k# z+gMowVAW^V-EjV75G*kJ6$`X$Rx%lK(tVd|8+3l3pxt5)R0+6^sEj~qcgVk>1jVyS zWCHTf;in)~C2PmVuDKtOke|jc<_Yj;lKyq-m3afV?VKywMoa4<1jjj=EM5M2M9Cmd zuty@^Rke8MR^vs4FTEOg4USuQ08Dg)6Dq8fZ@6%ud?SVXzDT)UEcQ8@DJdyh*m zY;}_5u!!qpt2Qtqbzw4XuGk6hD-1b}{#Dv@4_b z74;4c^T;a{{L9}CzP=^5d+4Pq`JCf-7vcc>oBwVVWC19XAkjr{hr4 z9XGV`1O-OtdZeHKlk7i3g*QPBxX7Zz;EL7*3 zSKzI8yICSJAD;^2x!G$`$hg5KdQ!(4l$;k>o???OW4D-xe3Sp_I0iw`n;Qc{O58sT zOqlS|FxS%s!U2b>!93|VPXR0)vXXbl1*DBIBWq|TKOGdIy&p?~`R#;zElql)V$zbS zm|jMz!i`6K2Xs5h8l~bfzXOrvUTMmP#QZMO*+s&Yjy21ubh4UeqLNOcl8z0rbmVeW zLg^$lKWhbM-%$;l>rsPi)=oou>7>d=Hmax(O%(N!N>;p=sut(K+>fn59k}W&+b4If zW*>*zLt-D|A2afe{o*6=;iz3s8(ZMZaQ+d0Zwo)Q4}4#QZ3@`s6>!F31?~>mE%?X6 zt;QyXJo+~9|CaspY1=>FlK#oxzWvhz%KcOOAS{~nde4+P*$i$mA45;8i*+&~y!^LW zRjrWpz#Xd1iE8)V9&w}o4p_yrvqqDS?}AKrRtj!y1?9-am`i1!sz*1P$v(qARV6jk zR<#nZ{##Y0U-o+F&gcrqhBIc1|0@3#`?gVRqbHrZmy~aJoKu{=-gBn0&ann_;OZtE z8KjJAJG8;?I7gY65j2`QNh=V9cWF4d>oWB*7;WIl)?(@|Y6=l#P>#rY`5hoM5mO1b zKt(wnS^F(QQ`K^$wMPJ^^%2uvzLcAdlC=}a2Jkv?w3zeIW(#JFBL#2imOv2`U2sD# zF{rUO)J_&5E0@SJnch%us&(xC=5b_CHeI4QB}6vsqN*)+rA~E1%sFrUC~fZH;3jQN zC%W_~n+~eQ$!ZEZ5;v@92rWBDqVC=*+YHy4BV0?YY;y4SI&@sfAm{Fg;`| zQ_t)T!pqFhjT|)f6=n?U-8hu00gcwR_{Ix+*DYuZu~A zbq_U)+S?H|C3TVPw%6hpGd4(+j6}-q;6sMm? zUK8yc@UDo4viWDQq(HVBMu1$~^~VCBvg~R(w1-Ho(s#g9=zW?=PG)v6c3fxvjt*r8 zyifBw2877g#DR##D#74ev&*lUnl)^YnXY-2E$}9OC^q5Uah@04#5&LY5Q~3qPyA&3 z^f*?(N*OqFc(Ca;9tH@spGWtyyvf3fNV#9*E{>l;MDjvmSn_h5-;=J$2hgD1aKsS>=1YH3q zN$3V4y>qz;0HjSjNNoRGk#7DX@c8IoODHuttQlP>pM-?2E~;bbPN3lu@LGl*0Ydp5 zu|U-OPr>gWgHqzM_Q61bIQ>}o{3(d)cM~3I-+=Ile;j>ag8U5-hR;U$$Zcx4yXf8< zWYfHE=>HhBTM-2c^@%ADSMdoEPaIHrB%`uE+VE5ibd8(-W8l%#-*jyp83vJr69en@+F$9sIV)K0t0z zJj<1M7es5RD=;W#{)A}7cLj@6Qfu*VaQAx86LD7}_ApR)4o@+ir}4x0FBdS=l1kiP z2saKfn54fO+?h>}!|WdQcZXZ*A;R3^O`SlrOt^fw&#&*UoWs>atHgjzc(TVHca+`n-?kf#rJ=+VZwW)+aNcvr$9L; zuA)sczvuf_p1&W7%41M4fB`qIulon!+iFc|>SIt_>qf%fp=;QgaQ%(0RfN4uSNM6j-h-?8Ff2paY$lUg zM~OAxZ@z(YVE$NGwCMQ&7$brU>LCcQ4@Kfc4>z2vZo*OnQ$`qO z1^$%Y#O?VAp+)}qyilwz-Xm5QuMg)p#Ko1KkAWg(MRRig$ zceB6g%7RwKHQ#5D2c4?9#4=aU6Cww{_kojZwFQZouaQmyhAuuqs=JKvA@3GmePjNXlQ<<2wGeP7qQ zPLlsG$&470zk|#TD;!+?x6Fd9J%0y{ds$D`uEIuGP5w)}3b(QX`?0Ga=Tk`^3@9eO z-GLO78|9eu5Ch8_e`m7YqVexGRw-vl?IQ)VkM*5fKtqD<<8qfs2dO%>hz@LU0Tg-e z;9_UE_>q9u8t2^kN??Bh?!N}M9+J)#z+WkSiDOgWjzh(>eL}{vFvEcZ6%AwyMs9fa zCFdg{|21jE=K#>A3`~3QFbBP>4Z$_NFIsUT*50Iu>jJUAZdh?8TliK7#aVWt;~j`1 zHcgx8Tnnz`KIB~w4c+43WtGvj@I$zJy;nK12V?4O>0zK)@9+gwjifQb|FU3I$a7yVsS!s}>xEQt`fLe^?D2fGf`YD6=7i z>DyC3GM(^sc5r*l@ERM6m^NHkraR(l9ha`N${G8y@-VKGb=RE*YiRg$oks^fdx5%} zAdB4uu=~-^Lt;`6gN<4tFzs(P$Anh5Rwfa-6&ST$LOj-TBaBt(-fOT^-xQ=9b;cQW z(~jDB(0>5*wO3-#(g=X(bI;PjDu*%au>V*1?7C379zTi_bH3x==Ky>kM*U`Cn z2laFGEU>8m;3r)8f}dXR4fed@P-FGF2{8#|P5BG*kGp`z!_2$d=8!ejK|vm4`zGXy zl$C~f*U9V2TIKJo^FjFbDf?%4Nj8K=VUeJa2;# zZ1H@9^kIwV-}tmSgny*)&=`S7@Zr7~bVv?=p^C+C(_sYkv!b=Z=muBEfvh*%zQy*O z3=)52y?klwC5j}`o-uxUy{}kaZAe12A=wv#H+LRoJC0~RA7o4tt-Ka%78IkhVBJwm zeYFWtFJw0Gmr6M!C^~CGUuF&Suf2u8uKxxZwb5|4hO!I5h{^?*-jUqiA`6d`erDQB z0WHr-rkpe?co6PkeFAp4bPwx5+`}S(_ptbtdsuX!{5*HzlJXexua-75?G&Quy(`+N zt1tE0?Oc1?i*c>q9posVk`i^4#DTbgeHOsP0a)q&ok@Wnj!n`+R^GY)THCswjZE9! zzXJZ_tk0$Iq8hOKIc_-i?u{_7R)E8QJL7m`M0TqHGd7DUshuo-1x9ujr(y-jU3dR; z#8X^6io&5Z0SM-5$Oy~^l_RO*vRi&JCjDb$<^*gpu#3&_h)t%BU)pNipH`|Ae#~Z7`J8B(<%OCn(xr<>Ym{_2G-gu7r@&t&dA9LHTnu zIy!_HY^;#~0hV`J65{?hsQ1!+yvIvp&J!8nQ%EcS7LcWUmqC0Mly5VpAt^#?EgDv_ zxTI*)gGjo8nX*ZWWmx}hP>N@Fh1?!@|DGw$wMhrcVb0S`ODf6rzec8(eGZPMI27G3 zIJg`Fcdr-P8iU;zeu051&3J)K!;6^8lX9jM^1}~dl=g?DoI9>!-aTY2k#D0lOfTw&is_21H%QhK%=%-( zS~1WpVXh}5LLmjO*UjIKW~9wMXq3n|U>jpz1RC_s#-dxu?lvX{G&Ozg6VEcQ3AbY& z;<{!Qx|rx8b>6aQ)3dBATCx-;%!LJ8lO%79MK> zw3DOLQpOxOsgn$XMpIS915qkUVfA7k&!2t|jMdC%$iJL?giOYwlDsjz!M1=J$ZmxO zGImf@&h`9nz+)$)*{&$aM2F9sA?YzFV>ZEWuv-WPN`$nvTj%28hf5F&DI$1w>G8qY zq>#;;xwUb*X%xOnqFTGjhdK4Il?p0YQyQHIFp)pUHqWxnp}^kE_6rrp^AncZD~y9X ztaU#ZHI$6zBS7|#K+okl+VG&_rONAK#SHMP+oql#Vcm4m43zfy6?u?^hSwxCq?`}~kxz5M-Z=~zJM5JF zYnryc^EK2Do@d_}g$$ARY(b8BN1>J9h<1*NbayjEnlmE(1ifN33n82vcW5w6Hxek- zm7XGs?#~!lY^uR%AcpSG7^wTRQ8j33qUrvOfx15%U4zz0G~J&uQ1)jw>ldO9>tt_L z-~KAP`BLlr|)-m>j?8@G81|IF3F{J}hy>!sW(0mFB-p5GAmdoJsx*4fs zehI;+&Q>~}&!fkup@1tHPPX5SqCrUjQ%oXVm#VXhuIc(zJXP-x#=(af%`q`JHCU(c z#l#;hEv_Vnv~$y+8lohI+MEy7oL3gZ8lok0no6YVQbY5NpzI%s{2=y5$iS4l)>7J= z8X_wefQtZJqkGSKh1DwzIc9{;%}aaJq=qLo9DCWQTrEhaQYo^f@@GoB{Zmec;2wI8dBT zK<7tr;671Rx*s9z6TtrhxGZGw6Mr23JptGU_*Vxv|6rq0x8f5}bWD;w`#^7TUXb-0 zBGnea6Kh#{IPj5%uGOq2p-LOG?6&wDGQ=6fGH{uV_&1@G=-%;EguTdla|{MDBn5?a zIf~sv8$aFR>84w`hrNNVP|=z6BuM5I-fI;(9rtFKQpa*$(9I!<=v|*lESHTh*{>4W zMU{!ry#&indBRIlc4Pk6k_UUq`g$!l_G11Yq}1Hsg~isv%Pe=Zl-n&#Vu3@2$^7(s zRu!7&BMvfRPm{?9D8-P=z_VgCQz+b?ou*CUFYdRAdAuj8PfZA~uuRq01i zmlY1C1EUUS5N_D+@jRy)fHlAw9LD&tSn~&m=UL2<8}Y9}RSQ0Kik1O?t+f48tONd0 z_({kcXrBKQxRbEceKfvxVc0Q;EFsacDI!F|8GTJ4CL$QMkUl>$PmX-*i#%tHYt}{E z1P7+afJQnj4-D9I)5~j*sUl%MFo3dPtn)tEq1KLG8u~96AlbhlpCPj;hmcpC0&$Y4 z(Ly)3e>K{U0}R(ar{DI&{gA+cl zbiWC&*AJYv z4ykEB<{e{{d+9FVyvF{iC$0OGa%!`FcnM&$E(L&G=?I)t8>p%xx4~YQ^YH7^*L|7c za;HHrXQYp&O>~gM^z{9{gg17o6xQDXb)lw*OEmJp<6mt#b0^;vT9UgJQVt)CeImDv z-W7p&sN?3gaG?ftBxF(@&7hFmb}`JP=5Bo!X!i$b0yH}zM{Rcv~2gar{hWPg4s`KRhH{@k z0AQRSBtZHJV3fC#mwJ=qb-b*{p~4gl>5|RtbvV>hm==I` zs3pBjv^ce5_E`_^Uayr|T8e~PdKhR0H6;XXNXL1bokub#Z>JB%xLKs(038&YQ)PS=MUfg%p^Y4OGl>i_o5pH4)6w}#d>s51j;D_O)mO{-Zn{Js&1(8OrjxTt>zI#9Bm8vv+7-U*NYJVp%McfpNwW{N(z zPD886o-?~Xo4-dl*@ zehA$0rKh9fcp%-e5MGNMG1~!I?lEvs@Krjq10bE0=82ED?y0x80(tCY-r?R9vL#iD z71jk1CWEaZKgiuG2zBtuMVG#g$mpITsd!?3> z%HNG5!QL~g*ngZA+WrjU#r-GXX02-Vvc||b#8Aw85TpHicn~Pn$qOJCF)`Ge)Ff1~ z#gHprUFh|`0ipc8h+|&HOokASY>3iq9doxvpEbT9Qo3-*I)KtDGwmoe#QggZy<-S5 zV@SlmpE)q_(!=s;cQ;p{i=^YRRJ?Dhc30iX)?hKJcGWSMTOg5gCSi>*XrD%BeQT@0 zb@q4O+IkS9WB!f{zwvjDgN^Mtz$2Y}pq3xWx8KB`e;v#>v|uc3!m;oGGVVXfy5KMm z<`x~pACXqOrPW1R(kO`9!KhE68qJduD;WDR=RD-)WA+PZXnVV%lja{H@%eD!0j(Ms z&I@MkfhZxjxG53{n9CV(_j)s!3Zu95Fi?oph27%J8q7`0m<$w5n2n5?A3+iC|4?=v z@NpGK|M|VUcc+tNTb5380c^l078$VVl4v#^(@iH}jOhe;<_;6$V-L}L?*>Bey_bX% zdhZZAgcdr49s=U~|7Z4H(Mg0)zn^aA&6b(%yR*BqU89DDihz;!Q4Jo~++Q($tGo2A zfpA!QuL)4@E!JSP4?=*_YIQ7ErCSlhVXlvk=2cWB%y%@4_3m_ z(Yy|C<`Nl+C>!5nzZtUq8?vb!rD=6>Ol2I8O2*K!w0fP7Y4q`!_@JY?6bvv{u@{s! z+9*ebQ_@DcS;RIvDu^COh#pb$q$Ail&+wS6&M!N#0^%~+?I=A|e~t$aEMenT=k42h zYh9@GcFwQYLb$J)*K0v4o|jz6|G5kn7KtA!T z#>R;E8|IVQmetFTen5?!I}Dv~V@glT+=e-kY#U=rhLqsV;85Po3e8ps;p1yd!a$pB z+H8R@Z3EB)74U3yMqI#@?Q4DT`&PlZhf~Xs%KTSoi0?&mSAG|_An05uaK$Txzl)D_#HoFYgU+`<5)~N={qsjXd z;`l-2{E31*3qfisfj$qPlGUt=B;}Kzu(CI&OKXH-4T<{DOGL{e4(v5Phgilp$Y$I4 z2{1$NdE*=Kb`WR%;UCOKVBg$O*j}X2zNC8n$d#Cmh#g(k#>sKdRhn2Jzvf9cGnuYQ zD+{!(@%n4|Uc^2!A;Dz2gsvhw2tR(*hu`)opLX#~2HU}pNPD~45n0Xi5CQ2(QVVeTv`y`%g_g|7Kn?7B`31xBq^EAG}%ac-8yi#Pz>*@!;qVVvF1C zKKe#rTl6i61D*=x*{;b1VsYBl8!g!)?LH32f?0LC{#a`^{LBW*3PzgYWWZRlF@84uy62Io$UH2)`7(l z7QJlwOs^V9>M*k)`(Q?oq#TehJ_0q5)&7}poUvcWETRRkvCti zUU-9!Nb)oAMvU^(`@=iV(tO9WG~=c$=q-e~pieofZTWnLRdHcwl;Avfdfd;&1oS{( zqQ%*75CJSXeoug-X#&hI2x7k_UgU4piI_E59lEPj1-{uGDRr0Peh&|Yi1yLgb8wj4Y z>jQ>Gere=yYUCuuMgrK!t#01pB;{Q6jOJ}%s)DHs!S=;|yJ#+erApZYxmZ+* zB`{%k6qvNL1uBo3f@kgafMJne%Hv(-K|&3WW&9$ma`ZVTo8UFwkMgK{?t&NXXo>x9 zR%hPdXMLxg={(e{&@*TqNIUs<72f6u)`49F&)S^;_j`vg!63kImb%6I5HwXU) z#N|Y~6sKqkzhYp)f(zSj8PkqaP>pg)xW*t_li|J7s&weBNwXK)T)OZY6sq%Z*GSff zEndqa+IN7JH+izRXs4^oEW32POMVbwAPXC8lI(v$WV0<`|02@|$oK9goTs-FEX zK15d3NqR?JZ18xxjemZTLdb4{Z;pUivJnnjQ$#!HL2-2yV{sz3hSPa1;uRi%o&U(sjU_)gL3 z7baY)qsKf4^RFtJ+DF|PB>DsNdzv2XUz(XNyv#jtZ-S8_ zJ-;$NJZmZl;sgy!6r~BGG$>P)CJ1PV$D9l{T;MbRIRoJz)+C2zksYVf4Z;&Y-TBjc zQRDZ$wu;~PiP!oa?rnxRn^2Dfc_T7o=A*{NuVd0w0iGj3Fx9>1Mnh#(`5NO_-Jtdh zCqjxubOwII=q&td-mtB34v4*8@bC*K)cK0*^GFX{7v6CA^XEQ=ghG$Vb%E{#oDH#J zUAi8K<1u9&;+1t^$hu3tSEqvBL&>O0Dwu`tYmiiERIpVfsZjY)Zb4i|fnO0Ux{ zJJ`{Ah$7e|l^+peJLf|i4^LD(-ORLcS9)~1#aQ0Z*B;2b|5QF zG>uKLn5c3yX9Zv4v$erlUiw zPT(wCU~Ywvt>)9Y99pa3x;Oe#!czLy*?<5S^s5ZE-cax2}6^W zk(nisFj4}k!lq~qC|F!T)k1Dg<@v~?6f(A=@_fBfKa#0DUoO;-FpXo@2%3h@8LcPU z40wYa7)W1|IzO)@}4QjV6A<_-zU#JRlyw}_YLB&nhkgzktvB{hb>b!I+&xaSy zN31v7k^xlQqIQ=AcO~#SMJ4N(_L7FV!N~fplU3qS&Q@ifXk5;u2`cavmI4XARk+U!b zOkEjvvZeCtm^E&S@s5Tht!Rrlki~9P$_}@_=7Nmp8`&hgHvpcJV&fyZEE!#tQp=a` zfmEQfEPW&S>AspvZ(zCEnwaqGui%huZw*V(6DC?y!_^A8fne6WO|qdC_@b?RW>8_1u^DPm*>d+64Z-3 zQ9eBj({rQw8fVnGl~VeU#&iFTFWH+>SyK)2kYY_ZiMP>=g6ZSdWP`j3IHY^nkT0^V zc&{bqTOA(p|K;^S-C6&8rco%ZCn6trNa zE!p0NW2Vl4DvKXDFUPbF*H(+FPGTgUyU zyHWo--()+FD@WC6=rr!n4PJ&^E93N`*Sw89Vn2U0Hl1$61r5)1q zx6oqzF02bDqE<&I;m3~(2=Agg83Y!AE92qbJHW><&2q2G&!5d+g1v5Is%Lk&6;R1C zO)2E{Um;-679gcd^B`4i_1lpw^~@O^<;+>BSm$jvy`2IXa(#3(pCbp&zT6`SO9$5Z z-if~7kPAw5G%rFiD7SvhIY?1T?%v5}fy|QRl#~vrdqL(@p!GsJqImtE@K{I3BE4t_ z`75i^%2tqyzF_;3IU(nrm`Cr44gzaqoloE8mrkSXuHl^yygxhxzlAd;Npv({GD%^AL*OJc}RNtr+dy zioS0F$}A2EcP1u~dWKH@G1la>kQX?n0$Dq@b{?DyhH-44ZH71sd!T^v^j@?-NVX3} zoG$V=IuXTNI;5&T91Ofa+?(}5Z->!Q4-6fJj;`5-I|1Qz9xw}{2Qn`z9ao2uqLVq0lI??nGn*s(oPJGpa|lhxVMJn5+SGrvQ&1(Ad}sxCyK5`vDE5axRX>v(r$of02V~0WR6iI}B*?^T;TqjW;p^VR;Nk zOMa-=o}9f`XU8qpa$r0*s8-&V-h}VO*TLf`_QD4S9cKY`HZ$1Xi%|xm+9UW5ktq0G{fj(SGfb`8ZlL|gV)qO5rni@!GrN&#*&H* zd^dM+OaYa#=K50BEj}zbRlHuxd^_3Xf!;up*}*)s1FO5o?}7wp?r7^ z<=Rw$m5VSJM9&ZuTDaUuNn)ae;u0<-o-Z5(rAHcQ^c;P6L@yDfqZa^5_hM$*oNMrl z%w~;3nHn^{Nzp|RD0-b9@8eoEfw^Q#zRUNndGF#thz7HfEiyMmpDi{8akR_FEVS@A z=1}Hg%$}lltkr{P6{ZLa8jQnBAY4kW`Za~s%c-<3q{fewg_JCyq9+iIYo1R^A=Ipr zbU)h1jAqrnHQ#BUix2Z8%p1rs&K*KIPr4}N3<__f%HWIy_VwR|>)={El&k3RQr?Jm zjT3#8KKApLL|Kk=BIDHOh!AIB-uZvTS&@^WDstvTPT8sZf6G~klc{5zBfOK5*PRll zs28Qb2%|ekVL=_jsc5;HjqGH#wWaXQ4b}4Sf*I5UHeIS%df2YOo#Q>^Va4%DK59gDEsKq z?W%u8mYDYu$)L^Am0QMZ?$G$C!(9Y%8cBZ&=f_$0=7XUkh|9gXEe@ZOdvA+N`0d&` zG3kgVx3YG9gh;~n<4-~3jDCU3U=qdgEtHGn3+1dv7vd{if{ceaFet{DWL@#w^)d9? z+*yrQ+`qq=nT0>iWj}v2))nn$7#o>nxG&nF3GmTpd{UYBIY0mRc>N(7E;QDUK`&ei zVazWaR-ROEFAene8NFRjZ~gT4w0iqsptmpS?FxGPmEIo5XS}u_h!i^O&1@dJKfXttg%a!B;Ey5%hkSX(Jh=Gq!=;Lbo7*_E? zX?>Z3!N*thaSeU=v5(3cwcUFXa!z77Er!g4{^IyXRLWfY_{ryuj;=*099V6?n3n;g zEIi;P@6l6vkPDCI2-Pe_)%qAXg+WF8W&)N(2Yv#{e#}4eyvSh!im)(!r}GOghFu{c zEUJW+%7g_;2=eQJ;L3WOQamcSsb`z}Iw~YmM@C{I#QcbKM&s$t&3Q+AuR+iym^No? zWGq}|7bTRa{fGpU#E%~>KsTxu!#w3sf(b#DY!4-Ie$>toWvU-ds03pIl~CMuDw4yP3)i!-enN8ms14t8^U04!0~Hf8a#bf3Loytjv6kl9&>Tcx z5^42iUVx~+V7cGGs1nF@XeyRK1m$2ZoZ1vwig~mQoN=+P1>@v^l2?l^i^E-tpR1X; z_@haTR0|nAKPS%{$ur47TL#mwsK+6r*0)G8``wGwrc zSvtSn7uXV!TGdNJpu2=pW!};%^9_Z(i9&`Ha%J_lEq9X!uURR2TUEVXc|`2Z#r7Rz zyM?i(>20cd!wSH~Hn_Jn)!Uu_BevDm+Y|r8+uG{wta1_tXS%L>+X2;3Q#!b}>GbB> z(2CxHn9@$EYC|i+)wL7my&sWa*6eWY#E*I^9#|GisP?cDJVdu5$J$rj1XanG*;MJw zs@d`xGFv{3v*iv6!+K^L8v24z93<*t{PbQ>%M< z_VM$eKd2k?*fSN5N9Zo^1V5sN&KLb27*$3RR$Lt|KJ@b^T^r0Xo1#jpf%n3uSo_Eo zsdn z%UeBMx)YTj?|ANsc=_i?!=-o_b?N-N+H)Lh&!=FJ>!YLAo+qh0y`awT@$~ywNSf=T zqZz>hPD0o?bD38>8@UnqF3a@l66kLT(pBIuW&>|_aI3csK4J@MFrale+@gE%qb_^(uH-ho?E=ESEhK23rG@dZ?(9Q|BXD9^baJ z)Tk@n8T92Kf1-3zUBo9=BBrB6T)Mc<+esB~bkuyMT3dvGt_Ck=wt%aQcg(ZZ!E<@= zEIf`3LO$Jx+=%n(@4%Zv;Fak~#@bu1H8nNl0MWXmLIf zYYWJ;0?V)m79EgFu^m8}g{iSR-L;@`!*w$-ZW!J=fizl+>@m3InL(XrQJkVXj+-2uHR9Ay@UE|EBnLV6we8DT~^*> zq82i5(U}Kwu%s{(dY*aW$!v*k2bCJSiG(imv>tDyjn6#FzQ`}!MKHsclQkN?&G8$vhC^@ z`qA~<#qp`?OB~=d>V~~7!2s#ZeK~emsDn-N9v(z+H+^c3n$k}oU7LGIp8Xx}0)Sk) ztg5bG3cNo&jdfjnIXYH)xj?$y(&cp_POpTZVx>F7I#zf^k093?yS1@A3cM*qBY^@jBU)&F;2AFc_>IQm zPTgV6CVIE&-m+HP4D!5-dCv2+f*=n;n-tRoQQ8tQO%SCm8Pfz&+EOu15T#9yX@V#X z)>@RWAWB<0rU?RCEA}Njh5^CfAp1vLqz1B7b#>r#`11{y%1{L3*O93v_YGIffvlUy z8YoIKJ@*Qk%i=&5$Bul3khww+WSu_sOd)Wc9>{9l`RfxhSI1B)`IqQN8N1Tpn#VU8RF`?5c4Y`=vWCMyM)~3;h4*MxD4yzM8qQX(1=ca z&~Tipb1~aVAWwlW&;ncppgC{oL$;f9756hZ!cgZ$#*8>V!V0A~YQ66)TbF*<`MnKWPCh`gcJ)L>@EW5%+R&+=58lE#8AJ z?z%c#x&hjLvRj?&NEnQcY^Vb6J z5C6iPe++|Iu8)qI^I425foNw+*VlzOzY>Cul@O+#!%?N5TbA`}GT2J}8tt6{=~k(( zU(Ck1jxaN0oC$FCMWnu=PKFCAGSIOigS@sBo7?4EOaFU6aX@{U?M*nOTwfkX##ok9 zRef1LEGiW2D)`874TMgEiE~c`Nid7 zpqpmXanq@1KAe0*6-*$C7}qDlE)2-j!~~*`apb*(O#MsgrmE7v5qN)iF-u?7Ep*h< zr@BSAyGl3Lg}9^=f{v9CrUSuxFx7w%qI;-P3C%LUI?BxuriWoX-SSn(INJ89R7vhRa2s&0m7}yt_)I%esz7dtl9d%)^ ztc0awC2SB$h&$^-TvZ7{$4Ut0b_>Q{--wdwuDY;SSHjY<5;lk=#9!+|TvG`_$4Ut0 zh8t<%Cc2ux)rGyb5|)mYut6jt?yd`QT_pq^DB66g!p@1h?^=Q=vWCM+~n{_JTF*|{r383 zdeU!i=$(wVxN3F3%~l`x)uIJ*V?G;qOlOOBeYmvZ`|BjSnG$I$rlU&4RvcXhTV`<| zemU^}-G{rz`#>F&TgXHkFCCQ$8?Q_(;xi$fSm>D$PAz`xOo+6>2kY3}TFG5HR&qD$ z$K5x){^!nw{K~K2cP7NO*@x)y z;P2oit?!XKUUyV@(ebx=HH5gl@jG})+k3Q**PRt!bo^~znGk3Ae+MsVd5_icx~syA zj=#;TF@y=^|H-R7W$}hV@#MqW|AZ5C&fOBtMJIJ4Sag2ZJPaF}*`69f_6-+EDPF)4hsq@b-Ji#durz&nX%^8Ycm;`;G<;29H zoR2t^(-4Pp_Tf-YIvmQmMm)b_A4FXek7%!kK8F|c#1;gVNb(dX6dXMBf#bRAHzm5a zsqiZ&qOdRcL{$+y4!l3Sn?>*(dXiip9nBaHj68Ddm!7Qid{4zQ9mTU8;N_us^1RF` zB%~w@Pa;1|9=)Akc#7zLL~+?3i9-L0x#8->xLy_nOqcP^40w{8h44Z@Ys7v8QQDd@ zO%SE671IP!+S)Nq5T&gX(*#l4x-m@ zZKIebh|)HWX@V$ilb9xm(l(80f+%f9OcO+Do5eIilqMyxRQ`NwnRven`k_W|Z@5k4 zSavT;nP*y`LJ_;8@%`a_Ec)^>UL3mSZIAfiJbjj)sj4nd1Md$XV3$7ImvJ z<@Oik)IZCZ^@h5hFM+QP{;=SRHDX~6gtJ>hxa#k%8sG{ z)!6U&Pnm#fwgtTx87PQuLQ4svXAnywIv5dVy1B6sMuz~$VK;J4$?fatS@`MY7Irl? z6-|eLevuNTqN5O5wZ95g{kemA7mD(==717(4x&doz}w@W+BYo}EU*Kf)`osDfUp!< zWHlBIgZVXXO3`~57e>}VUg;4%oX%^*t1c=GPv|`diA6b?iVlTjP2KD>g3-}JIOx;^ zcAlR!B}50ES9BvhG^gZRA)PYVAZw2sf0p6S#jj!l!9`K9CLZk`5{OWl-$T2!NE&#_R zUwe~!jZtBL`x<3qf2p2-Vp3ap5VeYrl>7EMR4TkSb}+@k#jDbbRWdDq17~5GL0;!qT;6gxQfNDotOVtI>1AO}wSH$wi{=S>cu6vkj%Gn*p46m9YriyZB3&8D%zgTl?0{th)>TF&dXwYH~l;D?!3o(8B;`Fn;#TS(=hycy8i z(bA!lA6Y$gw6=Gr5lowIYkNz(>_#H)2M~8lyF`w%IY}AbPh!#$rKEcHr%QWJBtV#r zGuW!j^lVRx7H70A7nW`bn3&VqMz}u_olqcA9FuuB z^GP&P+|7K0tv9f*18`3_C3f-GlJSSVxOC#!^fyIB=JsHR%Y0&O@pBleShNT#YT;Gz z$LVSz=;>-sHQniI9bVHg=*aO)jKQYc+Qf=`p^u{!mY-A66*>L(V}tHwLUtKX7xbph z)bX?xXZ!yQF8sqyM9v-9((E=ES>5+*;Agj$Z_XwBB5^$ZhU64pXBH?Py{x!Y>Q@wh z4Ip|Q;W7D^I(sw%**`EYn1%svj$)>OH}mx6-zh+#F$8ho{heX$<5914dgB{1lHx<; zm?x7ZZPYMXn9n77@77xf`JZUp)FlYJ&Tl|cX&ZRC`c;U&X=0N$%r&s};pSnZy&n*6 zU8zTmDvFmg(pv=Fi*Yx-VR7 zsPBNd!Zt!*mJ{@ruFe+j%-yDaGY~(f4i&58mro$C)p|Rz# zp)`v%&8qjB^f@LP%4kheg8?HfxQH*~vRc!uxv?W{TG~aYx3s&@OB>WTfy?@o;XPyt z&J$>xJQ!4MlZ7V`E$lA54`+IB(@k67_cJvk29i)TRT6 zub4y9b|5I*jp}(mLO>_-ozj+TMAlZ`yI*W-c)(2xD_9h~vZ@I?7DKs0yCZmOiKp@lGWK9V;Oe6Z7(Mf55%aUg|tgLR?5M&tDx4={97Y ziTC80Dz_&uDcPkge(A$H?*FWCr(=b?t9u;I;@dXAdppzh`CE7s=DIbMQ<^+onu+2O zV+{Fg(RV(J$*2#*G?+$w%Svg16-xN&qd)Q7HMX>a6z)QPpCDVB(~!XYdk8Kr5#|cq zvvl;<#RsRaH3Z!bL4RrpdeAj^{EH4j-)0~kb#oRIL_Lcj$YIcSjA?=>ZFWo(L}@$4 zG(kX{?sPn|N;DTq#prdk!0t@%-WLnFr-%F^3Ak{RKNNCZlp76uS7Q=#0{)`h*rZIU zF3REAQ2H|4V(Hhlzw2btT|o)83mo(v9Q64zPVs&<@4;@wwk>ng{3W-?&#So3v?^E+ z9uY*DzlcJsbWX51E2^_e;`R&NQeq6QDP1@O32H8UjJ^rC&-TVg>7%Mj{4d~mubGut z)=hnM)HTy52;!ECAJ>KWfFX3LNJm*JmOiQT_F*MrI#x$aQFL9F5KRU8Rj$im*94s^ z7Y3uk=t%exgF(1i$;pTeQCurW3FIt*j8*K8C4;Kw)1si}7=mbJ5V=4XcL`nGC3JAq zd5nxB-+Oz`()sDyzN`&p5~!LBoO6;XbRP?EppC8FNP~?n!m^Dm+Ce0NHns@G#ugED zz_{pdZa`vc?I6aKIy;DEhxnfM#_X%ELj-kp5N~FzWrJFpYsDGOvEbJge zPfb9^8dk{YZlL+i8r6Cp5{{jf!Erbsqk&+&!1c6Qv7L~TwhgDCIvLGC47cDY zu#J#H+7ql&U>$K+5YO=1#U0-ErZ;a@;}+o{>)tcAfc z2=g^M=S0WT*=16A=n&;l$aG_(bq^B`vG1>uW%l)Pv%=&y34eBf!~i@^d_R%O^R62z zOrqX9WMmXwXZXdrfNFBQ$e7H%sLY*yhb7$?5(L^c(AO6VBA{RTo^K@GtHQ=-=9V(g zfMA{`b!@4~=+VFR2|`=Ziy~c{q8#(dNw}(1qN>lCK*07dY?VM^W^gYwliDFZSE#m; zXH@!^$q1>_Zu1n_U~NTie)K7tyyL9%1nTEEMXdbu%`KsqNs_(%t;S(J{`_jSHRM9$ zJA5O^RgGz7E-3Q@nGFeR36i!XsZt)uLprkj=}IWh(-k^?U#*7j5^cx407*H;N1mvS zj@BtT54-5{C(9ui34ou3{OU{7nlVaOYZ0V$bg7>TC5s0%9xjK~qjn?lPJ_I9Ovgut za%5Z#>3DEeX38Py$dH3N#ys7`m!Sq*fF(>quw zavC(0aMBzZ7@sI$B~b%i$f=yB?T1WLtxI*9YF(<+G{8kUxtti_R+Xk&m+Catx(Fwi z;{#kY_v$N`7$xO=n(btLB%FUW-AAPjIPF2IPfAuJ)u+<6oJ)q(T4fDDjM zhw||tIuuP%Btp3%(ao>-_7-+~saz8;1Y;##E2JX!Nqz~oB2!_VLpiNF-DVa+KFKmKzY6Z$>3HI-%+I_omf2PNTgx)T@E$Jn zdie*+6x82CQIXLQ4kJj~BM4wF`EXWm$y?NQ`2eIN>puMwGH6%c&9NDC*^t^rUHW1o zXqeAq9GTSKZoZQgVeWx+l4QOofoDGBc2m=SCtn&*8DhDgsgm2YGlm}#1Dfy81PSvK z0UB^W<7M9f8IPolY^Q=i9?*`8X@V&2=$Iym(vFE~f++2Tm?ntQPK;@SDD9+}CJ1QL zp~M@88dEwBE+!>)hqQpS<0~}Ut6LZTH%Hr+Nec8izal}sm`i&J8cJIb_BneN!dnc2j8Z5aOM$-u56eik+^EkD#D4Y`ke$|sG8x#?YDcd( zOztl9F_fEBtUA^F3_1Il8;;aH)%=$vW`@?iEm2nryQAlXY4G7mwB6y7ZQjfp`tWdg zN?k4j-OPf2)Z|kr7i#jU0+aSMfuTKJVwZYAFRaJ(LP#%!;)S-Tr)W0uE>h{6sz&~` zH1h8&jhv3s$c=Ogtb1SkCJSlndJ@+LyFZR=H|pk@V2t{^0Q}oCC6ZG_46MBuq-6OzA2*@*782XSqQ^5FXlKY)n$6@Fy7{YUVu z{RuEE@=I;e=MV>M(UV|)#t(VwIIHg+Y~|wm1#Y9f!1fC?zXDi1Hv(GL4g(B}{L(l( zHBJ&tfS)+dakxuTK>S-H&O!nXI9Kv*T|WQ7d``kO*GEUQ7-EY;d^dJ4gutmN1)RfC z8Zd1ZL@pXbKMnYHqtk1Hj83Ryvi@Aoo3eAP>l@*qZ1xRPQAsPj! zvtnfbSV#17<#`vx_e$?W#q!!z=2?|h^WbAyRW>Gc+Jm@wNuy7#y zk?5vMU8a{VyqQ^KFgK*Kk7i`(tD3ysP{iA^Jc{%hwLuhhjc!CN(dJx)bx(q)Fxmn* z>>@n?y1e0)s)`l62n?T&&cH^pJ45l5REj~*P%PlBvQ1h4#{_h(Ug!callfyv0LyqU z<(JC96jgf|TRMlyKzjNFdCe)Ap1v+_{AMx6C);aTMCeCZq%YsR1lA*TiP|OS@qX{Q zh{#NGe0MG9`2`^3kyg;{#lnhtCs-#+)0Guza74%R-&0HnRyY4N;q~mM^J+(SGn4&9q*09T-0`QfpAnYst?p z=&aU3p7pWJIQKBiyK+N0B+Lr>LHbODX1qej;Xi&1<-b<jN9dj_J`9YQAO7xwqBCJe8s*11*2@O?* zRY_>9BCJM2a}{9?5=K-JrjpQ7MOdAL)+)j@60%i_4WRp*;aOtc4P6&hacp9(WYnV)6@hG(6d=?fm1B{&34 zC)@SuR;DjG1bwMmI@h&4k+416DQ@}pXa6`1wo1)rj0|+G-zl-xKa+k$C4)+}1w8N- zrLpKb^jc|HSe(=?O8CI$1SV{!z@%MFpz>H$@T^?~Ff8&*c`TzmNT}nn6Y^?j$77O&59|^G6Lv{~ zNxPIlOtdE}lC)C;NCxf34awWijixEy0R;QgbkS*Xza z6V~YcNub`JQ1t#poZkOWsQtBi|Cmyz_b)rddjDOJ2Zgzapib{UkFi$k{r6`hV;fuB zypJiDDd8KYEP$e-*U+4EIYxvvfza70 zvHgsdy(;KGq(h<}^zVkCuQX-w zcoG)ZtjIh zg$c+PvmClP@I_upkgeg)_KN)#`JOn@3-^@4S=zMFHgkTCaDFzzw?j7uYOZDE)QMEZ zySWQfBi{`AR{3K7TR4C5w*i036Mru{;CoBw{~{;AHjGkc{G1v4=0?cURprLGSP#|! z+@1A6lery&m5l>&PE$5TG2y%pgR`ly^3Di5^^K-X{?K9GoX{+R8lvOEZ7!ls%KV$) z-4U5Vu@LkCA(k@BBje2q%>7uOwujWqg%*_(x{sN-FiU7f_{o%R!6C{Nx z^lc>}L@nG=qHX%dRH)XEbneyYO@dkq|ssa;ssz3w7rWw16;90veU|8gr zj6dl=-f(%%FVta(vg8xba-Iz=T~#pz>W)@T^?}Ff8&*`SvJZ z5(e_!vCMa`!TIj(_--I^R%<-5M%SvwstEb>eF;%N`2mxO_QXP5b2F*x6S z9N*0)?!ay)Fk!b4sC;J#j<-Ak@s=lkmG4T*mxLO=^ZjS%mifL_=1axDU&yiKZe*8& zb5LqeC4vmNhgBpsh&oUWaK^F|{(ARBO=xRW+T1-5Cwf2D|8#T}d^L8n)nUJqLCn?o z5l8oZ4SncKwr%>7ZK{VGbg9AzaH(MzMvH-4CpxeoG%F)jW)=K6UGPfY!rOUi>VhkK z7*bVvrUH8yit8T6GZX|m-APq@7^*v*Bl(Q?OeHT9_74ILbon?#D|ps!4Hy>rrTM$6 z<}V4R3m%zT*5m)R?{J1+=t7&dHzPlNGZQjFLHZGZy@`~By+yJg`QPlFgE;qvY=NA+ zY!eI81+3G`Pxl^M-iv6%u3dRL27SRe4kcS7JkA9n@<0}fsg4e_5k@N%6g6%>F1ow` z#|(^E!}!0sly?~}10gmeED!w^DOYqVx?ojdG!&MDmwBOCjm03kO3z!_TYML9GS1_| zPH=;V!OhDU)A%O$=Q{hxHvLY?(<*t&tS*v6SngRvcC)h=Y=sIe_->jGzPCOS6{~=Bm?NNq zKnAl*4NwKY9L&89lMtR^l^vQC=KhOv22*5h<2kFvaY;B4T>((wW=3;2_OZNLZ*6Vr)Zoq! zB}6B&AM(eSIx_bzu)q^t97)6I98?&YX%!alVkO|MI2laUvx_5?H@fE{4jJok4(Wf4 zb)xX99_RKa|LFfe&S6b68wte}>Q-rRSj*|RHbx;**D(jF;a_0`^;V;qkNIO0-W23|3l~H1HFtHLYjOjkA=(FJAUpUCK{N`9HE-ZYCYTM? z`)YcN$4>{Soh|Ue2XEg5L|L*w52GbmmOPjR^IzPXz-vL?%3J6j9BnRr2g(7Ev8oFa z2{Q?)6L>`kNMQ@YOy%ZaN+rk4(KIhQ3b*O<+3CCz9=5-|KO-OV*B^<3UKUfWj4#UyTpXDK zse*C(%x!%3n0G?k(Z|d~n8HRV|4cYMw7a1?gh{(QL85RF3c~&o_`u~hE<}cHc5nJ> zvwIN?PZSn0sHCc*p9s|MNjkdEjaj)yp$D;hXn=j3!zOh27@Hq90wqXjT|$UWV~6Mw z)*@D@@IZ=YZp1Lr%R|huZ^`kdut-_tFin-m9*hZ|I%(FjUvb})QQ51ucf%ru66Lm{ z^W=R(sc)ZoBY)QVhITs#S%P+ZTikB-TidM=IZxdQe2U0d&QteCyqu@b6_~Jp5}33H z2t;H4vm!}5PY{E90~x!Y;8|M$42%5Ik$)XDPt52@Fw4NBt_8?*Tkt&4@jOyO2lgm| z344sdq&-^T@T5If5LMGzFvfj*Oy>Tt3y%FP6 zc^V*X(4K(6ev$biOVpm#5wPl8+e1~TLNlfg(NSn}a*Y%lWbY%tty{sUB z%d&t0mm@6er!C2ts|l5BOPxoSP#(eDbD>a1*U8C=S)TqRl2Cr56+q~LHp??BiW6f9 zCH4qIZbpuOOMmkGgcUx#a<>VKZ?VOt+g3#r!KQU)Ye%j`5%rSH^tIyt!hFDkAWK`m zbJ?mhOCkX_!fAnXK7>pi$6q;CxGyo(IY*L#x~-dMXZqXeB=a3^Y>B9NZgW)7pFIZ- z4af0U;~>^NDsZHlIOwEt7(~j+GV5_%788&9E>tp-*5)sttPrqfX)%w$~SbZpSr{CXN{2TZq4OnCJw_5#R zoV?C@Fyi#h_l&fyF_dxNoZ7@g0kX-BKJzVp>3bW94?4=zK+pAPF=ZyZ$ecR-fIh1V)@@uOAoTUZT0IX{{T5KU8jb>eH_2Y%Kh zz7~FBTbFAChdE@@N5M#wf^`9+_3+c_4qFUj7}k%b)7Qp0-_zVp4M{_^32>ML5(i8$ z)`z>h!u6i)%hz1tLb|Yq@VynhW%tm1dEYz4TkJ~3c~)lyM9M$64>rd5Js7>P4CUW8 zQ_W@2aGOLGZI7vD1MoBsY1356Yv$LH@OPxkuNx%gfxS^+!rmk>X>S#X z{JKq%q`g^@(B7#?%HE|&+TNi^gS|x%vkCL`dcm{yI>4~VFU`~SHBU(}Gs<~NxwuDo zl#}aW2_4u+1SagG0+aSJfm&)037)kN0)|C?DTfV|0|~Cw;=Un?0`R<}9gn9ZyvFsU z;92_wU|8gr#)YH5oDGps7Z=B)V-Q!NfF1ygrDx$Om$1(P^oRR#;NJ}C!?kM8?3-)L zfOF|%8FsFQeO?0XUlj<)H3x)YJwMLLzByuMJ>NoL!fq)rX}1!Ha@$&wq}@gkvn_!A zgWy?$ByO5Xs-xHXy9|}y`_XUFg z2ZER*ctGu+f@keJfMJne%64OAOM*EH9$kMB^E$!t`a*&S_Dg{Y`;EY){Z=4&eJ6+( z4mO*EGa+vWqJv;RBl% zsBwmZXKfNNEb>d^+)U#nVL+VC-ZhBx6vSCL9953Cf|?i@dD%>uw8IH}JAxoA5=5P- z2l@NaHOZ|r+Xz}Bx({naaMRPTJD}?Ha5pLu{T*}zgS)!^#OjGrq@%?U2QI4Ax=K`4 zOfYRef(>C7q6>=@$ZU?J$QV1%FFuBjPc(-97_-F$bf_dKxr^s`CuY1;*2tKeh6T+;}D2Y`II+^o&F{%`mHk%H+1M@#<*A0U`qfK z$&N<3fEb_i;{~=i+Ph>wQ1p_Rr?seV8yXC+V@xcs-rYKr@}30Qpp#mFsqYU@rldPV z(oB&QnI_^~Xr*60#xFjEsvt8oMWbEd-7j+Rjdlgv<-I$8WZzTxjHhZ(12+ZKB4;gy zbH@@r$d69vp1~*cwz7_TPAHfM2r}O3n({_dY%or13$U~xi$Bkv?xQl)Wc<9h?0Nv z;>4UVIvw&wd*Zt@o|~ja$2T8g6VV^xFyUmVgxqtw{}F{5%@K6gazOVb8b*15#;Abb zRJ0#{=Z?VStMUl;T#%xrWk)q08$z-FcP{w)(f)AeQxbmyRLv0Ym`tJ`QrAuaSCf2K zYy-&3Ki_PNHI3V4xF3S(Jkeb`>AW-6z+%R@D00QV!aAA%2p%!)q0{LwN3hF$m31K= zt2nNo2YEPuUKrJo#)q6B<^V`+CkURk;{n4WzqGI1TKh^8%shBR zq!Y&~&X(tN=uPKTo?b3p%GHH_FS=$8|7Wt)2w^61fIAv1I zlk>{+9nTdcbYNE$n6TXfleR~oCTn@YvvxVau*fgvu&r_+Ax@U03v1i$%Z>hPWOcrK zd5n^7ZL9N(3xt1QSChCCcB(+#<6KqntX%~#Eb>eF{XzMWP_xJRu-jvMonmBz-}TiVP^}~6XPC*&V!G+rTb&0EA+du%$akdqx^=oUv zvvw=Mu*fgX(%Fy&Ya0@3>KD%AaIa{vO7$jdQ#?Pv)RpgSi9fJ21t#nc0+V)@K$P!} zf|$E`G;w>uu>=9c-Y9-mn4MG@63o4FG%;>lm2(qsFzq}%QJeP5T-tY)s1tTKftvPR z1kc)?0mCA{l-#_uS&OzN=L!~9X$KbD;&?FgvL#CfeBj@n6y?Pcpjt( z^!|cq|M_RZv-SYMu*fgvxvTOdVL<;m13a&EJP(u5fjwLxZY~N0uR{gVL$8Mjp0x)9 zhDClUi`|q33Gt!VxQ}MKu5vt%mEeIrPGG_wFED9O5C~o;Dw4Fv2%=1n7CdW@0t}1% zQl`5rQxY6g*QP4xQ^u}ekr#-lp6^( z^@=*qjgdzF`6gd!UcX}ue}@$>Y5z&!7x|S)7WPCZVc(UySe=L0xICOM@&)!BfeCw_ zz@$A_AoB1piX`p%f|!HZ`JOF!)}93z7Wt*A+EY_Sf;m(=UzdkFmB+oUnTMC)r#26- zb$NKP#GJ5~2-G~hNbsz^5HKwAOS%0~xsmW2dAQ&ID-Yk(JbYjCkY8~geo&K#d!c-; zb9s2P$QRh#1>)$6z@)uHAoB1|MW8KHB(%2)qM3P<;8}YkU|8grCT}lI9toP6T7H4I z4tQS=-i7BeX7F6eeGD1czY!$t-2_Q{4*{6pOIV~^p#b$Z)&?(ds5uP9V>U+K#Pe^} z+uY#N^dS6me)o{Tgnd|G(mo;((LNxEIh@tue!;W$?|@;EUz&owH3cM?Bc(dTef7@e z`tSwogIIW|nFrnI>cbNfb;3R=Q0v3vf@kexfMJne%5EQJN5X*m(Cod1JmVkcyOm+z z@SrAI%)eOLNYlpxld|1EJc)Bp-lT4>(e>b?{YaekG{Eq%&iNB@PT5ZdrtN0}8|>!- zGxiICj!Vq6(S9l9X8Vo6;r8EzP?X8%2>Y$Tk@hQrP4;VnE%rNsqwMzrTkQ{kn7^@( zU=7F6+^=KG2$?cMCV`N$5>i$|%1TIC3CWd^G80lrLP~*g-S>dAz?Q`m9y<8#O0%gjMs zBfa^%E4+e+!c!Iv`lPmf7B#EXjh9_olX6a{HEcLxkB(LM zfZqmvUz;YF%+tt--fEtRp99_>zQM?jhtFIe9ZiI`Cw4k2 zb&wUAb%r&SkTsQ1YAWpvB-E+{M<|qSL&d(nTr+V~s})v^*D7w)V}mlmEoLj*BG6-u z9l_R9F4D>8Ma%M3$2gLcW-qde+sFc-iS9!X?Z<~_EUlA zlRj4@X}=W2oJ_7C3!b$f0ft3>Dc60KD+%UQ;mZDC89(UgfP#J>Nxi0%D<}awJqb>AU`A zwkC6>O!*_sl^V+*sa&b4{E^O;n#&&zxzdR82a-6v`~e|HmOq+uB^>#3e42BmmiQxo zp$Dz?DiE}Fc&^kQlUqmRN*(2ok-5_7@<&Ts>!_BNo(yuLrDfD8y^cJpwLd!(mp@Ba zKpo!NR%(sCk7_e#qIl$x@hDjJHcvyr*VdYa@#{n>3tv(+v~b z9QU~I0Phb!WO45Uw_G0`&EH^t3s$t#qOqWf{+6(%y(J5K?`d}9EsZcF-qbR@p{2QH zM9avQQH9Oe{IxwKyzyZ8H`Ed66$;Ni=*k`f7xZAa#QK-l4 z7h44;Y=^+4T|gk(&lp9LwoQ@HW(6^4AvT-~5^Fp@j2$(uzuBm|d)6=ZQBunm^Oq zhjrdQp*QRtfGHi#xdFWWtIpe}^!6CKkz5}g%{;`W>S3f$%e&cQ1>>Jz_=fq_5v`1g z0KoV9wTgbNs^B{i&A;)}ZobBEW$f}jKFq+s{MeD0x9^Ox*xZQh?qgq;UuaaKn#O)6 zzxE3?H1-nta}lg6EHgg8I>TO8j*mT8{@IBzXfv@p=D{v3|8gyjeNq0y`87P)tK>Tu ztO?7`C4Um4s)~<&O#Xse8oQeOZM8J^IQf@xXt^pZ`6dK6Xt(bR%M_;90vGU|8gr3Q!wI3DXs@W4(N zn6MiPOxpDYV(PVlAgUHzSMaP|2QV!1OL-lnyhx}%ql@%XiG_qt(1b2RkhBX|rI+*J z2c0aNO7wx9AuwS#6PUD{3q-oNP$X%$6hu`hn+Tq@8v}+#eyLD>DijG-`Y87O%Yx@a z;2D^M;RlnnA|)!1BV1R7(e~7rM0?}cJdK}dXMXGY0cT*TSVGAy$^<3sL`_gUzkk@J zXSPU&3lajM;ueUT5(1H)T@(rJu7YTKW(l6PI{=17erb9R(e#i|lb%NJb_o0kcotfw z%btPV1AhD{qS)tSq-hLOrR_nA=M;Yc-XAu@bKwFRi0EhzMf~PMeA_+onJUe!3o)D_ zE|L&*G>0Js=AxrWz8GY4I0zlbe+~UkuHBcsyT}~DXpdB%t@L>beI7;X(fHvh(70Z* zEg<=hX#5GcFK$x1S+gcOpF}RL)DKexvRrm<{C_ICJ*KK&uqBZ#@r)iz>QA=*ROJ#ZMcjfeYMPQ_V*S(Q%uWXexv%l$6a0?EV&Zcg9Ik*Ap(>3P=UzHK0!1KOM+*u z1q_S)(kwhyvyg<^HBaD8gHZgdIj~qU>FL()U!a4HHy+ihXHHs=ISw(~PHjT*`kYSG z7YG?}F@E!`f*{}pnu%$GfHvKa9-vY$dI-OT<2mK^p7x`YfT&7l!b&t$2^D)L+8jT! z*VByz9pnLgq2`e@0%z#bzoVUHA; zv_}ao5M_0? z;Mghv#8v@*mDP#LiiFzq`5srpPlK0lenGjE4h!Lh$D5$jdl7_Ct~4OuW7*heJmX}$ zSlGcnj=+SyRAACxBoMM)qDazSE{FGgc_cqmj_S&Ig_3oqx{lA$Q`{; zB{zr7KT*^3xD!7|T=IBZc8-CtnJ&N7H^A#lWv_Ip@|s@*p?vu@TQIEV^$fgNf*>P3 zvk^+eH{0Qa2>Tw6r-uPP$1)q=6}^eoRCX%W1wObQDqNve$n@?=QvQdd60u2jXj_ql zqgl2zMcD{Gay=q{(zU@HVLj3lD}ugI)DU}bvqU3aSS>~(3Nn#8f)80YUKf}Jp}y~B z2Wr*;bJKvKu)`TL^Ysp|nrH9o@{4TAZM37!v~d6Jd8vPwvfEf4pFumc)(}6_vmCy{ z=s2w8OMfosr&vfuyCt zdygPmckU28Yi|b(i~Q2MbBfj-5^CzsaPLnL`$h0fV6-XBfCYAQ8HOBZ@YY<9bjodm zOyP2n8w*$97q7uw36Nj;YN&Ir0v273pHy1zU+4SKk4D#kfGd~N0FtV1)`^oG^g0(l zl42sy&6(mc=_Qx;2PGAOyC-fpH>9t?*&orj|rZ&j{=58eku3UlsgH^JuYMF zOa9?_zbb(P`-Z@TeN$l4z9ta7Usoh)-%=#BZwsQlUlBZO{{a{l`K7#1SKcI4^KSCa z2JctEJCJiCIkDxEiFU+!wT{BK7#15-*wb7H+Yq8Uihcm8vzpR`dzv?*M)WL=h*D|s zfG4@6+vRqMOmp-~&ugqWjl7D_*AK77S64bTe+A5$N7&@iq&-vdWv-(a&@`VC-crC{ zrL5*Tc+Z5#q45ptkYDo@+gk=6f^dBQ4(54$2Id9)y8ei^L)L@yYY4Az|Kuk0;XAq( z^)VAlm(dYjhYxQ1WZ{BspG^>d#aFz~vCIcyM|3@WR%BBxu z3|VvwssoXS`MXf#wHqqHz8J3Mq)ATbM@ssoGyMGV&^p%iWF*9$mlPRE$9(01` zAT4Xm)t2OJ32-R~Cp(Cf(L!I^!DSp=mXPNmu0$OCb8}10@kK$*l|pl?m_qxwrA%Rcbkh{xAD*%zG(^+- zo%J37jzimntSvMdY#;eIP;Ycv9CXOq0++tHOYN2xTr2P%SQUw@4OqmfqU;5VE} z$(v?rrdVGz%xX!@>gfR^eTcEatsr_?I}`0Q2~%O{UCV7`i&#Eip{TP!e_<+Aa7UJ^S4-Cob{4W5dYhDx&+Qvu|TL~b@l+o_UB8XU~hiW#iQ z@Rt%ZSXBc4@S6gb`=ZdpR7w8aV&~g(sru$`h$GMg=h82Vb%>z16aMvWZ&{p3hG6q@ z80rsJ&(--5w$kI$*pnc-FoL7#8`Z1IC#;V2}_Gl~PZ~ zp^dGIINxDCjaQ=?b0a&>R2XeWZ7|PBH@!I)zG`kC#HaT6M47%J_tY0aioB8Ds73Ug zJ}Q%!o2)qZ#!f{H{PPRzvK*FYvr%irD+sy)sVfqdO<=UY<@iygM>W~^D6%qI58iOb ziEd(Oxb!&^73(%W0}lIQtTAKY56>hp#h8Ik@^k&E2N1q69dv`SXOcmYPqO4)-WQgS z@|y$nj?CZHFH))irjRL$m_m-$M=WqdpGHB@S0%kqHF;is)XFe1i@P0(`~+gKe~`#` z6T_o#(25gV3D{`&d{%yQP)el8NWdo*gu-x2>07zl`@A~uLaFk;3cTkpp|>)z4|W5g z?In#;NW6V`ttOEVR{@REDs3uhi`3Glkv5gIO4!vw(|mKb6InO|?I|j)%AVrZC>hr0 z_};+Nr8N+b+9li-44%C(J{>gBE+PGiXV@iNQ+UHJp&(nD$H7|d5|R*MlZR#F=wg>} z5W3hU9E5%e(>VzJqao;K)xrI@9Ge(4p4mhAAIF8;AbjT!;s4_rYD=ky``A2I~}rXlDb z4MCqUZE$(E9fE$!5cJ!Ipual=ebnlM^W9(wdT9u{*wY*&-^8BgAoTe|#B zgYXsmp@Yy*8zLUDD>{h(2Z!+g^APl<)*4)Xu}?Zk{9>PU5c=#P;yG*}9pi6lZS>=& zGb@7yZ0yVDJn`K>sJ_ zKQK--ZZxN@y%~^bBjg1b?MT6t>Ddgf^ZL+R36AfI|ELw-=VIKa(J!i|!svr`FC5>n zQXIkQIL3eoqNm{T(h->36^@dLgT^)5DwlHn4?*Z zuNg7M8b|hGB12{E(9-IOWh8PAp++)F~6(vOjsR1u5ovV zzxSE9CW~5<%Ws4;RMCgDx2M)m-76Hs;VpF5PUT6}On&M)@F{ihbx6>Zj^7J752C=@ z?P91Nh1(#r`3nfmsfKDBsSRSg?gQ52eD9YrM?XIrhcSe^W6M=)C?{9Du{QDA{*Suv zfR3xU{(gRM^|nZ|Ev>X`TL#(25`$bYrb)7~Ww4Dg)%0ExAi+s^&Mqe4_1h@4&_gE> zAOS)Rz4wxY5~@RQrV|1ILg=B0@AtcR-WIL=Pxznnedm08&c2zsGjr!oyEAiV=8h^# z9jboP(}tDaV9z~>UM=gZ{sm}ZxvBFb=Vo*sUve&_^TU#Jb2_)#Gzz>0oi~-7ThiIG zAoAUc&L5VXTf@mRw{4ldt-8D+k%-#!F$nj!QhQGMtL*}nf{rs6|6TCsVy6Lr!n(0n z14og{Thn2GXvg0Q>ThOzQ%H8&`Lj%ACALb%esacbYy3| zGjIQB4V`xOJ4Eg5e#c(rj6W78b&T!<^BtB!Hvjl^4qHJC!kGGa{%*KB|0}}5>YWB9 z>b+GHg+NNz2&bVfqATHaOi??$o!PUU@hkwI*;@?vyQE9cn`Pv@nVcyDiEp5~rhMTC zx1g%DtWT^i>*|svf%Qt@KrMlBTWUyHWDjX)jSb2)Em)v5abJhKP5ecog_=|1OVGs7 zV1Ftt4frtrpuLI{CVkm{m(t`uv{-4p#l42a$Ga3_;tsc%<~C9^LF}#Jc$j{HF2#L;;wHe{fq)Uu-IkJ8 z{N~yRK(-u>J@p7KQyH zf{6t%=qWi2Fck)qZ@6Y#P>L-{f>lq6+N2B*kK&6(;~z07KJ$zC1Z#Y?N&F+D_+n9- zj*4Jn0c^f&O6P`DOFQF6Z^&%o3Bbn%V!A|I!GJ`WqS>X2K^v~43BRvmQ z`!2Nq8g>&ZVAm3$p8$(*=MuidG#G{5<_NX#R*G_z0(i5?9If^}N}k6mU_SzmSNjgM zpQ!ddX+KHr+tYrE+7~mW(_r_4y#U76?-+6_P6k~2T2BGKQJY8m{AE;zV$pIvI)aHs zVaG%;u_)}=2qqSV9T&mG0vOuy;UvzSif_3cV-lflQ7jtwga{@Ug`F6|#GPH&&mnDlF=M`i!j>HW*5H(PK+2ZflL-Yl_0 z2L(*%pukttyGYX`U~qcn`_oS`Jzk=&PVXa|-aNq#l@elVdUM5&cMM?S9Rqwdy^A$H z0tTn|n^OK#ngLZ){tKGpgA z#HNS0LokNN!tKS>^zaM_V0gCxCf+T;SJS&x(<5MTdUuubcmL4&`_!hlo8XRxc;EwR zXnMPfy*1ngreB~-)4NR5BVcfPcbC%pVCeKdv*~?baL2-Z#nkk^C-&BGADDiDE=})p zO^<-V>D^OGZ|oLB*5Btgy&nngSomWxHNC}RZw(KC=@;nI^!}jf5m1#Lc=vB2z9AOy zh9T#3XCsum;@%Q%3#w?dlejxMWjc!?i@(O#TtOCAHJ|%JXm`Vr15N<-f&5-mbNvtc;S2|F}n(;|jIe3&taG z&rZOU6mi+=eVfy6+95h>#*242ogxPwaL{!o?4$d*S)GXGSFl8NEf9k>T0v<@=~co}FKby)`@$reB~- z>+CA6GXjRvv-7o0>nw?m^P6Jg{HB;VzbPitI#(>JXXn>qZw=3c=@;nIw64~)2&mMv z!@Xq6B)&mfZh_hMg8NYuDYw9)@a6h6?xOb2z$$emOWHnL;UQ4OJh>4@1V&ZJ5nC4z~!OO+o8eRs| zFVLk$e2o?{0p?HI1xvhy{ry{;#!V963$GUw`X|JMwh1v&pErv|&vsuYc4(V`32hVj zYGT)FVgyv4?S{OEdgbj&_;P-SD%T5804RqGqjLB@XvAbo*01+Sve4TrW-Po{%)0Q8 zVuHp$siiLbvshH`&0S(|4ex~M7wA%AuTx?P7^L@xZEu)O^AU;dg^!AflYL?$t;fZp z>R=ugdu#X*Ous;vCULzcLBLQtXk44dlM>wvpAs_`J}qWl_>7oH>shtbh0lpa6MaJL zt>Ir``USc)(Hk^T0xERSuuZWnJ)7oB659(`iWv)E6BDNg#YCE~sHHA^RV;dO=S8u% zhA+VM3v_9kH)@&$ROs{hNEdgjs`o#a`wAYoWxdDDO+XZ4f5Y-mJ%1UqG`}OMd*Qoc z;##|yxaTe=Xns#ExaY1GKm0%}su}k0Vs8!KhUpjRQsQq`;t5dAun}+N`O7++_Gc2= z3qKb#7JeZnZnKMtw7*nKUHFw+{P1hBm_M_7ek%6X@DrGRfi6w^7EPM~b3eN$)BbE# z<Fh|YO?z);I6=JYWE{A?N&S(>M?=9Mjg{C!Lf??-LT;aG&C z9KCh4g8~OVKl&h2w)^=XaFV@4dAP(-lnM%pv|n5Cfj(zra4k# zdtsB9v9MW8=<LX=c?@7pB$Xhb>~!#7Bs|H5?8T8g}v3#BbNc2`IPmh1fr(78K}| z7#<5AX7yt%Riz!$ROQsKXC8--D&Wn{L@Pa?0S6+7nw=x>Xy5DMpqky?0}cBadvGqs@)-Gg{3#LGW`9-4A< zk0F8PVLJP~7=H2K8J=wG4q;CO&vA1#_d3D^A7lTZG50}a#jUI!ZVKW46^9s|w2tcA zNbE#`1G8!R*9nre4<#))5&Z;+0%&~@G3Cvbqa=VHzpM)u1Nf4U%U%*HA%S`i!J%1^ z+9!J0NfI`c)vy13r}G#Gn^2gO;s*aI*XkUW>lG4Jk9>MEgHFZ7s8>09ycYsHywcz| zyTOAf2HF3~{{=Rj^iBj1!4l6ldvY*Sgj|WZ6nyY7d~7*`FG-=|EzLimaHWP_!4cA} zP=w`J#h%~5x+<_bsJE1<_Hct10v+=d8;tUwxYG?_BkAVh(dC>m<%34 zE(Vg(vC!3f}zLh1jEr6JABa@{iGaHLd0>Fg81)eTIrvKITaR*_p!n zJ|^oEKVf~YH`?5RE>IkY4iFtLY=?cof0!G>j{|o{KRwNz2xjiWC%lGfVWtNoI#Xy) z0ba_!&8f72PL>np``Gc3`O!q+;5@NIOvvKJ)RB7)vEyxMn0Ol+U+tWCYv&{&x^i3M z4di(vEbcCe?}d}ZguF`3x^S|XW(5mqEwQ(Tco`6B(4}eIqiGOeotho z=@Q=y*A+7st}kX?xSp8iNv5%m*jvMCF#Q5un#R4F1_9>jL1|1udQC_p|0K$rXNh|l z8F#Q@xCPd*htt7Nu`-@P)Zp1c@ma>rmd1?`hh;oV%vd;EO!FLRn<@6za0X1jK$p_? zN2QGb^Fjq~>ht!{tf2smW?w>-j&xl}zot6#Cy-ejFX4Cd8&q1ao)&0zWky0k6ar)`0Nns&-|ma({ZlK5V@qnNR9 z7ctH2D8_IHvA2fX!}JSuDen6fHv#5N1dRF_xDRlyCt zs$!}vX%DeOjVes2QN>r&dqC48phlL&yo|DW`GLgu!u`dJg+CP2;Dl2u+)wP#nFY4=Qc~%-a=t;rZ_$0XKh^Pyd~G?hxj@8pr#0=)P0jWY*Y4x^o;j@(H*h6AWN- zD_2-5fO1?}bo41Oyp3^V;q5fM@Gck;4=#uU`&g^w#iMPW50ZRge^*S%=EPK9oEJM} zb1)&B!&meC7tJ#PL-OJ=7WZDZ7kAo3#>5= z*gg)sKo|(w4gnsf7w@9}dNZtFFJ?Z#_}yUr62U*FAFTI>1u|Ig&kckP z()+_28Lan*H8WW656gbAzMNYC8~0;uqGz+2OeX&!Ua;!F6|ku8t9a=n4=4Ygc0Qd^Ke3OCSuimY=W)tTZLZSEfy0@QB37yw}>4#h+*Oe zF}|9Y6`B_UhU8;wSlstYeB9j?GZx+>W?lFvF_n+qE%w&%E|`9SE=}XFng#(w^0A3F zjfW+^7d|3pEPP1Jy6{mkwNLy-?6{Z<6Bl#w)ij>aGzh5b6A>TlKpJ>D3`JSR#|R)F z`xJFi;$z<^9}~Ys@EN?m8z1Ym`FT=k^}?scjD=5$X+CGYKOuHV1YtrVh_B}3NzDfV z=HCbywFCC;IcQgFTHG&4d@p=aOuYy8yx3dA=V1B;x)dkwa`9Xc0af?FB7R{k&etWr z7rrTGEPO*uE%(>Nj;ESn;$j`Xiu-BBO+Zz-N4VFrxc@2fz3>AuW8uHVG+(kT-WNNb zW`c>QnebKISZ0%M0?gMHWl`0yIX%j;b63xix+GmM{7i5|rKOnKuRj$#1bi?d;KNtb zdsfpUpsHV^58lT<$g@mhK|KI=e*_bY!XAiVViB0}Fz$6hZc#D83IBp{=;;qKxbpq!}rfYz9Goa=>4|DrI>mnfb$BVjWN=3yc$7 zhKr%*eT)%3+**vo(*r*1c@aE$r8)m7q>b_*GLKctp~`f8B?WKkgxLfYX)5=vY~&Ad zO;sKVCHX3KZL(Wf>Ew5cWgnTx8mtX;c&bQPF2Ba}No1wNruaTm3}OTzXoAJXysV@m zl|f52ya&xwW{^oOmg$#Doi2lx-&(1YtslH!31pq|t~tvQ6Ti#xr>yHv{5QZKSM5+O zW%$PyehCh{a}~?*x8uJbLW%GScEZoc|4IB08U&vX*gk-ria!_2{BfUe15_*a`Mim; z&sPVd%ee|in$f89b!-a{p}Z^COB~*Fiz~+XdWUkpwWXcg_d~h9$l6=UBI4Goz&+fK zt_UL69Hb?hm(fB+2T2Amm%*;jifUN5KpOHXR6#0R>0K2TsI?EJWZxc6tcQLILgdbg zWT=~`&h=!d&#XPntW^rIDzzTVA}M@?L3A0U8mhua0RdxRTI4CCKvl7krPr%Mf(FXO zYj7w)xQ)`lvjcxcv~>pKy0wh!7Sxs}N}2TGVsfm8gmk>dm4_f)I#%0*JX!zawR9{Y zrQ@+EwBvO^m)2zVVRq*Au#>qg?SCf(c=%kRC4~jn*Y=XQGDVQa^U>gl4xZ7RqkRJQ zWzL4$52iRL-C17Wkatc;IpjA(36ggXN736l0`FYNI!T~T%&VvkTZg!V%x2)>E$T6- zhI|tKK?)!Ex8ePHmhHO4Sq~T%Wh@1K|5@I3$1!+$)%^+tiwAGv9mU{p_%w8LJ(LLE zh8@yYGA|s!SR3M*UR;oda#XIDwa8wDt#sldAk)!84rW7DR`-SsU0|E@z)Z{f(5QmP z&(A}SI+7A8*qC;NnF}*$0I^p1{j*gFzkxvCt6SSS^^aPHl%$?mQ{@|EPE{RS5#-bCB&+WJ?2ni) zJcg2zIfPh%3b4l`m{=6HB7%tpFzVEorUtui10~!=QR2}8Zo64Z zOE=Hbsq&4qn3+d}_0Vk%z1DL1OkOEKR1r$wp+ae^*d%F%(%68D%VoFV9n9wJE?n<+ z&ZeREBlTz26OO z9j`9c27Rt#LhGrRbzz5?sN?Z!sS78F#WbPmgsX|YH5>~Q?>OSCmHoO_HUTCrEt7T4 z{XVkhZlo!<1F^3u_XT+iP~If3aLdg!xiLs}-ZB^98W`o~^dP<$v;c1ks5qRr!d~yj z&1@96uVwqLjCfTSa@aqVAD7S3@l6mElY_lQ%oEHUAjqb=Q-I)w5Hzt(h;hotvUcG8 zAa0z_VAx;^e1eP+(FC9$WML1+0~!-FmX5doCpsFOHLQ-2wrMTSJwP)Pxbr(;eBqn5 zYoh#I;Vk}g+RZ2=A&TF7c0gD+U?;|?yCcHqN7L^O&Vk;7jU^Y6ZTN>SxQ4livkgR=_{k2*QcF%XA)44>)Jl8qKS?fBrM`gIt ze=I{Od9BO;x(u_1DTFby-vqz-3-#X?kYkazXyBHf3qA7%=&2&7_T@vRve>wJfrq>P);jD%VD5 z8{nTkxLn5rSkX4MK*;wc_nG>YZ2h*aJ}Wd-^_eVQwADV-;H+DcbB}N~Myx)jr3WW` zI9-|h=eo(W`B)4t%$*Ly4^ENqY4SZ4Ur+9lx3uIBg2VRhXv~=doE&o|qq%d;nIb0i zR*6{`P8AdUXl=3RDUeBG$K?f>et|A!LvJY?BETL&vFn%Naz<%mq~&G#R4(teTYAuD zP6k;$<5(=N{A%S?}Y-qy^wj|b){kAbFd*-_F(l2GX;Qu<77fE_^pj}ReaE@QT(+IUi}31Q0HmaMNm;WjHl=2+Gsh1qAazkk)?NK$41Jj%r=OPS!V8V zsARdIW0Y$a31GA+AH^oErfe_6x@HIQZ4)h{*qy}{X(_8tyd7_LfgjR($O^_+L$3;7 z1D&OEe3On?_&N>GE^gm|L+B56$%#UlME(mBF9qhvOR)e2mTuXM-FofEWzrf+PZ=+f>Whj8k*4~ zH`TnQ<36G!Wp1@K@)S%!{_OH?&MKB~T0WDVzkI4Zc2J-G8^~avrdide31FY5DSet2 z+o$(N+SPqJg4Fcsl1J31H)Xx-2?RBL`uoIM*{Amcz|L7lI+vqP7qGu6OTt43$U9E; zPlVx_gYX^;gr{PL9rW>woiHL{%bKtc_y*tnS_Jpg)BKGSjkoa$yWm8WN%XVw8I3X- zko**a`{`-^&M5CN%2YU^PZ8f>ZG7sxIWj_)HBI28E?5U2K&Hc(@)llYrT;)?oqDHD zXURx$UHDa}xE>(+^$G5$r+Jqt{(~uQ04JhsNWUr~y8+4f5Zq5s^B$wT&nO7SC^P6+ z6=fzMHVQo}qHLs5X4OQQ%_w~Y_tVpSz*PRpROY}5F19g@6DA;c;U=(=e}QAW(1_Y` z7fzz%$9Ko_bKzGP%wq`h(bE9m4E37DR#0n025%K{BAU$Z+?X6U5(0HOd)CtB%egr> zj@j&dMD0!@khnI5tKJj!%kji4N=D;&N4!7^@(b_@Hp2%`1`}}N0kg=W%!O!J`E&7E zR`TfMYD`0Q#WOPxMsL#hy*Z}^mv9z>MrdN>h(@fN4jz861?=^)O?xOcTzNV^ zvFx^r%8UP=<>6&_hV+~su9q-dBKWqkO?y>j!Kgdn~~1%u2H^_ zo;Kdu41H{K1K=L~g+)q!O9jnMr0M$Q) zFjJmpiV(9E#z`=L<{R}gIQtsXe24~T!${c(AbSGW{~ek5dFEg6&+h^h!LCH#(7gt7 zq7f1jA=nLYH=%``FuTK*$nJtw=d|as_|l?h!N#Rhvho!5haX0B`c~&~S2KymOs(Ti^ z(5Lr@165Bt;zCF816WZS<)TgmKLkkWh-<4k8iaKs_5tvia;q{}Xfnt7*oeVaCb$@( za2`o`QnRz@`^uBrAQ$@)h}cT#V{&Nr96`L`NBHJ{j8E_re3Ux82Ts6&_@ufqKOpe| z93Woe+Mdi}h0hWgQ&yG;gW?lQSVYI#&XnDi4(_r16v45_lB3~;c^F*|?mTf0Zconc zh;>PH10Xzz0eam6O>6-ymGFbc_kUy5=J-ytG`{bTIXeLy-)YMDE+JL7SB?WO^443L zvnymPPp{$I`MLl(u1`YPt=YDhAfk@zl+W0?`ceQ(!}>q`;HQFJ`^7xxGvsa?#{DVf z1tW0dshr#IT^&s&y&y!aU;%We*LQCR-1z{(8xj~@Hv=r3ht-i6^fRRae+eZCzsK|B zW^2B+IT68CgnkkHBN2L{z&I*0%%2C3Wx%yLa83DrX{5f_$x?}j8GlKY0Px|TFVnZJOq9KV;wK%md`SHcl6U!VwsOojX{BNlHGfd)?~1pt`y~dK?rNm4#b^JDf0<} zrt7sBte@f-iCGw!IwIFwcuNZwrsaGjn(BihP(`xCvo%A5WQWDgYDhkqB>x;SJNoHq zuwI;0+`J~(&ls#IKX3%^!|W zdPGygGe^SKGy)34&LaFUme6o>IOC0M8rgFdNgP?+q9(hCmP>`6QYz`DrY7?RXz2MF zqcxddGnMA1W^*87;$E`qQ2ir)er^@`lCEx>oM%Lqn6}EF#a&AAxd8LF}zfAUF;m zgghR`lt-0`xC5E3N=!e(_!E$BF_zxEk{xF_m>DuiGwHX0MBMWI04up zJBBA2#g2?|B0v#~cw{IR5fOFl5JdT!c9-IVgI^NBW(Hu7?)0OZG^lMI- zQ{aSgDhzWhrAVg{5I3j8n6g6g#|JXomNSiRu$*m27;-CEo|(fYqV+!6+30|%-Y;}! zy%x#%i%$PQ1fMvpch6z6e!#gqj)k&rSON0*Q;@w8dthmJwAaB8D*=YvAeWXQHq8BH zh%Z-ba_^QQafNJfdNdM2Ec7jgOibY9%=9xHeZC~1t^)8C0fs|XESQH76g+)R7#=qN zDXJ|0Z&YjejX?ZxK+Jdu6>2eA??TQ*l$19S6HUgZ$v89_hbC#EDRDWMxd>&l9s7l@ zlf4iV9*S>V8n|jlqc{cMFh;A86V}n1KLd3b;)NN^aQW@wFyIy#F_?%=oK*KT05GfR zfCKy49IaZ+Hk31x=WC?6bP)9nTm(e;P8d@j(6)i;+00}#s>=LW=40DAo1CHboyHlZ z)lkdZBM)Azk4u(J7iOI7+L(&M!1)KtD&x{+Tf2Pdc8l*N7o4(PzO`k#l<=N8LK*Xb zX&tj^zC!E6#F8Lnv;=BQ2-c*%B~AErugz4|3~_;W?)^Tl%mOWjXL2~+^Fct$n}y7ii^NiqRGiNun+h@ zC$IQ5a@x^PPxCdZtGI1Vu>Nu|dYW$-Y`dCZzbFTzrvx(t2nB+zhN z+XXme0$A@B_o+y1Z`cR?lbG1qQpEH$jt{215E9D0A|Mt z_zCQ{fOR(cc-aKK!VP{0=kz=M0uzbip{1S=a8nF{&=qW;BNF)w0EM8mOhmXdqIE+( zsFOb)c}XYG?&DCu18Bv?uA`XE*(lhKbTXu0Q_^_IrC->sI^ZuW1JVxxV-j%p>VQXA z2Bcpp;2zZhkEskuKLo4;#^Qbwj?A^Qs8x3wgPx0G>*%MaG7%qP%`teNINS>`w+Tz2 z6BzD7l!S@XGl5S!#fdy;-+qB568v7QEYsj3Sc8l4N!NE{rQtN!n@i|w&@#ug`BFHW z5(Op@Tn4*LK@<^urM?j9a`;U7qjLKCfy`dzSpbs$13Wu1)wWo!P-k!%a+BF;9~^>I zd5Me%bKM2UL(2nxlo9Si3w}C@)Qt*do!W`LfK$xk{va-5CR$vlvly;KLLL3|G)a`h z;OO8PvC+cvGuc^2l59A`aV#BMPS7$LZ? zLjtIY>lchgKOa9nQ6>(|dJ<+X>}^GtEMK4`=Rb^>FC;S`b>& z`^NJ=+Y&K;N?eaT9k&#dz1;9cbxWe^# z2cLyD$GfsWL4(7yH#Ca*ik6v!eZW7TEmIe?^pvHWM$s4t)x@}fF?0b&PhEhO=AiX{ z<@|Fo0{F4w(i$WemOBJJr9&vEl!@$*7*E}lB7_IfUC=La%49YIFt7zkUI+ZMqfAM5 zL{8A)gVhp&Ns$qi=na1?sEM?AFg{Vg)zbMr5hqtz!FA{rSb7~m!s5a8?94)) zIgJ@SvY7f4^nulmKzguN8t~1SB zeuXFU8P}N~TmHwGmDP;J*-xAkCU3k6#F9524NCJzp@G#?fxyko0(qfy7yxdGN=KMn zu~?JoD;Ple13fj<U=Y4VGGkzx-AjJQhDu0}~kr&Rp{Gl?@AO|c6C=qpHd zcE4W^dFB5c@=pj!vTrYuy$EFU7`+*eMw0D22mz9kjbtzmCj)x9EUX<6%S6f3ivGfQ_JN4p(1!G4JpN{D9N5JNvBZQvoWK~ftBD` z)O@E+`7)pyZO^roP$h$QWGW~^wt&+uxf7p+QURqRE=MX+<~q-4lJ^dFL)*ff9e&Z z+<#HrXDaTqEbh2-HRA5hzV!qGaP4<4ywJtYgTW^BB%4sFbE)*{2J|%^sN%dj|1<(L za(^ZnFaCnqdn*~$RgA6@b$W_KUDk@>Zk&G>%6JS!J|^n9;E+Sgn>FsZnT8I18%=~SPeDT5D&%U9sTqSFJL&}e=fow zo)3r%$O{ofcuM{z=D7PS=Jyr&G$j1+QiM#zi-*Ig&hoX4suL}GDsz!p!$=SxUIJJ= zQJ@jr478nz0#Ox6vjua{#;pLGCPc)l?A0owY=DapucLPyNVAv}KH$F?h3;q!87L`P z(E%bRr%h1OoFm}jNUVl$AuW*@_^qMDz;TiM_kM|`7AB^ow|ImkURmMSm2*$eLDS6e zLcmzQFGa-ZB{h6Ii!*kNF?NQ&--iz(= zHV~I`i$~Su?1pmA=&3ntMh#=jfD^ce?(s+^Ayor8#BY#pV^TL=xOPaUV=Ko=02y=j zxN4QcDcM5mYJx0S*1|KKbq|d8(%YT2(O>pvne>c;&1S1ntXzkg#T?(h0sj2$k^$*Z z`7=OsJ&rry0a*8w@b!Z`NopeaHFI+p?7Y-@CLFTkXsyIp{AEoE+{idO?WCuc0H>YN z=Ee3%0ZmaiFFZ6(Ze9d;BNaYw>Ie70*1I+WQ5tkF9KxRIVAo0FmM7IRH2Fy(9NQ0= z!IJc-d0#kG^Y(*3B1mSV@i_hhd64u6$$eK7Bnw)W=Quy`w0-do6ORCqf76a7h4Au9 z`25_&+hR*Hq7ld3tbxyvp?JOUodSp=i0O zChq*)P_Rd8!ri3#9pn*>*e-@gjIX2YMmh>}er})!$Hj_ckViCAQXG0yit&559b9fU zpMyMx%IC2)ac$azJXV$V>oxJ`=Z1o1wi`^NO=pnDs?wQT6W^BeAdgj-GvBS+32k>D z@Pc9rA{#fjzZo3M>)8ZQ=yStST3ipk=FHds6#8Yz1LF_dajoiq@Pb0;)Q=T3N5oB*CXp(*E1 zXtC!?Z$}raK35t+YR;9GJfd@@-y_ey6$on1mEJ|Hr2{B9D}Eb$GV=y`Un5fI{@HMv zc$?0o!N|>@7@|VIVaQsh8V2(!fIL>|HsNqmX)es zf^jM-fi5t2O9Du5>Dw&W2Q)p(!G4ai@8rXRlK}{GhYV# zfd2+L^Pj*5JNoHqGN8r_B=8?<0>4=fOiu}HvWQWZYf-D1<9wgvjoc}Q7J|PZWxa`8 zb-a+mTjjBFry(7UR~mm`#<%*)%|qWUKDVLwllau)hV*fK&o_^(5=lfu)2N7sq|77s zCyr71Kcj3g%Zx)o85G0q4wMavY_N73#pdDy7v7zF6sQxK6ClBL11Nj&SCWFN@ww`% zQB@(Z3=ASV8E0azrte})yL;2fo^ZCCs@5MO&2winJ{AB)$5YfQ?x1k1hdlze6XWVvCIiBWByhYCnv`uSveg%|2M1F1l zyx)C~H$$BG{Db|G1J4EbLPC`B#{B4K)p zvpcU8Jj(`!CDn4nFCRl+=)(MEX`Sn|MQ1bH5wq*q2-b=`cVu0hQ0}f2R0;ovmjD7TJ`djacD=NlvJd}CxZ-x!C4jFJdk$+FI}===_2 zxUu5R6}|2z*a!Si*y~@>(Svdwh%Q4Ivh=xPdWN)tFSLZMOUOKPj+~*z6eO{L80iE@7 zrVpe#GLO?phO;-<2xFvGCvT;^H9E5x0Y43HLz0CSZ>u5pa}xWOWRIR^B1_Dcy_VI8 z(wLmP|9#HsNVuKeNtxYVgY=7X3DDDY0I5F3CbK|W?@hMeo2{9qxe)VB$loWKPR7yp z-pl>wYPW=mS$~!j#}NKXXS|4=R|>d8c0YJe$Yk8Q;++-L-U0i7{}rkIn-l>(b?Lq) zk`INVsLj+n9|M0-)n;S}y{iV}*Tkr@czS9JI2x4LLHF*O7~hm*(6b!F{0N;A_w_jH z7VoKvH^6vr3+?pOcm}k4fyB7CCdRkr81yX1Fl!-Ny-pNd!|6iQI^(u|rl&PIbETA7 z&J5uimVi&gktYhQm z4%og9eVlP^A7@OB-!M?XEyR3MKP@2d$HW4QMv96in2B;s@A zL3!@wZvF#!hArd{c6`bIlMrUdjpaXt-FzhBIc{_-Nho-(BV?6OyuT*VIwtxrNtB)j z3Nj|;KOqur^OK#E(B^-NHlP0pp>Q|7gw3uw*eFYD4(S)rSj!YYMtD2H9zv->?c$}c zgou~KS+C8p;1eL}VGD?j?w`wAo{}hd{sLAQ|ArBKi4S~bTJ*fGZK}0*yRw8TK2TGtK1=m0MDOUQr&$N3I#iko zq?yn(6aQtJI5?h{Mxpz;S!dG5QJ>q}+1Bc~aG@P;omh1Ce$!#=9HMsabJc@3%sc-| zqP=hh49y58jBH!Q>^&`F!!JW)J`JrdNWTmW! zDQl{mPuWMe!KLx>HuwOd_H@7}A#5#%hs?euh}&n$^4iJD*KMiaD)m^kN;jNymF4S= z&Z0dv|CGPH3}go}52fQmY^}uyYbq$t3UWrVg6OGZ%XDDYW?B48O^if220hC$%({r? z8ZMQK57k6VGTJanh@Kj4JxNGnJX{kaRgOW=atyORqNO+m6Fx@1`VaEeZ;&!z(``qm zHU`$wj=A%}Ap2WUiTRHaq(1{2Jidp;V6uxX4ItSk~M_pR3DSN0SjdZ?8I+HS0cPiv+3@UOpqKV{c)X^33c&}$I zP^CC^32YTPvejtnc1?T^k<0ln@~uJg%(2W1WnY37WW&-`l3%8PphliKf>dn7c& zfBtZ)a>&PwB;;5*>koY-;%e*TTG*4Mr6;W0j~XJ{22_lr`nEz`MJ5j*dh+d~MBPF+LMeCwzkTD%&{+cI zw=V@~88@#Zkk-+C98zmBSKz3?JT#6meH6*o;usv-o=v1~C>G_xXiqqciR&1nL7yDG zwClrWXI~WN&&cyy7P&n;uw>i2f|Z!?pzXCl=h6)*t!&?TVDWo-2GnbVhVP5u4|Ox+ zfGyvQ&!X-&#KdWB3B5}n7Z907HxmH@$WbvPZJ#2TTcwQSxu(lwOXMAa`go*iqkZbR z0amQIt7B!v76&YQ*2Bh7Fo`y#_s)KWE&&wG!KMcHBBUP zWp@^h9u4u_$C-)!kph`F;%n(s5RaQ}+CHuvemn?NE8vSCT6e zWkXYboIB}CZ%Im?p3w4B{ztyw1#kXblL4V#z;7SqV2)gR(mZ&9pyqWCVff@@To*)f zh3|GKhT}iRz!M111w`VBBM`0|@jy>rRx#~J3`lr(fd`oR!%*2Jgv^%jCh|qYNEsu6 znRbqA{^@YmMW$H?E*nD6px22Mpv6p-SR6m);JHMLpj%bIaMMhJ?nqcBe<9>^!(+qi zS|X%Te99kM%$kB#!ZMU#9qwB&bbLx$illBhZha~3g2T)}TwZV+hZfw$x5#M`oi}on z)RlG$2B`iR7bY>_EZ4AHf(t+xH3v*rxVTvli(SJKGvDG~Lv|x}3pd!9RAxEX^qqYH zITZazb$9hlgi{aUU5`M6wuW=pt%is5?&EuU$^zMYd&^O7{2!x4ZELiXL|0oD@yP+C z2yAk2%gfd3^MwoeGF@Sz`~!-1u-i-3neV4N#%j)Jjy?{3=1;0mXN&_6T~hU zF*^w~7@a1CYq1+kKBK-n8Euj@M(*FCZ&$j{x9+Xn0wYJVcCsMI+65*9vQ(+MrCz(F z;ptgQETx@L`aN4v4|ZFT4O(^OsH;A?Y@+K&saekNGUfI<%hkGKrPB*MiP`I#@8+g9 z;i4}`(_lV6AbxerzsPgOA+aLTZb`-TI*SMOer8hy6g`C6T%qYx$CHHUu>Cp0XHs=7 zi(=A%A$QptsRniETT5|-Y8bc%r)SwzUEG8EJszEa6ZtweTfOXI(9F7($ zZPecuY^C;+-xd{qTdI9J+Khx_W|3HLEWbB=(3JO;08>%dJHSTovfZn&$Dn5<3gZfv zayYlbFD8B)!2e?hde*X@q zvfqfMK2w4W?$!;76Au_Imq0UJiAG*sA_wl^(!&YR)uK#F_N2{k z;0FEZ;?M>N8qv@wLp&1rjsU)F$~{rb6S{0R1<*b5fb1;p-nf}l+o$z$#B+-;)|BNK zCi7ch?dYc`-%Ob#^NLKWn>rGFxA;;`q_%P-dP=0dLp(T;O3P`I6%a5Ff*xF&i%bg48(VX*`Ob~HO|m0%&0nYk@Z)fp3o`3XSKgqFs0fbd~` zm5`>~rX=(+CwCFVgR2M1nt_=sU|c3g%18icsKR*=ng$oDS{j<_6-`4Ej(1|C~0eMKz^Q?%@S~R9ygQR#SJ(P;6g5ZZ5UDQWy6SFhNY5D zp2}7*j&n2*8u970gL=s^ ztK{P?{RnKgK5nHVDzBc*1ZRnLd>Y9B3!9jsV_(+#9aiAt~y zWN5m|hyp@H0SN-9yfQ!_ASX*2#@E${_Wv=?Mrg}NGjEZ;1bx5~2cf0MwL*Uaa|vAx zF|n8}Q8gHN$ll`csC26;-c#N-Qg#QPyY39Du!JL5vhhDeZDR4Pm= zkwv)GpCCa<5y)1F6Q^8O_%ueaaF~zDWXtSdo7U*Q4G~IqG_|ZMXRR?Qy-G;18`Pkd z$SukJxDGrRc#c7NLVLEYaZFJty(-DXT)U`~CCsisloBOwQE=9s2GM>oi|ZGtCx!xC&V)u3AAKWB2)(EH--3 zmPHGmW@f6x5vm#2pxCffWezd*w2(02)~yYq#yi^}8!l2wn0~Md8uX3On=&#d>8EOv ztXe738>QB@VI8ej>={r7-G=fx2*F6!Y$7ad_7rup3v)aIhSMO?v;vx z=w;Xk{BazJ>XBqeKRwNcA|H61*~OL-Dd!Ys^0BA4%V1V4oh6>5c*o#oiw&ga0d{@&4FwHa`{KS^XCE^ zRBy_WVJ}$`iUPP^#!XmoZ~^MPQ?zgL0M@a<*D1O>71Q*4QL=!c6-RTEXszL`;B2>? zpI#0X-U{;73Sg@MImaynrG{KA!%AcKo(Od!%1*8jV$Op-2Ifi0ag}-KY-frR45_4W zS)o_u2%@>sZ=X~OAly{~*p*><&bc}G>#vX=rkd^BZp`CqZu^!qb`o=|8RR;E&zJ$T zdjf*G?K{q3mvq~6Gwu`fkX`0^uy77c+JA|E_ZRqh<|k4Y@6j$Krn+AhJE>7|oo52E zKM1(4ei!w}N;l$_vscOEe!Gf*WbRb~$>xT_NJd{3a7&V;Ltt(AXi(&lr+6?4aqUb+ z0VN?%B}OoqXg+@?qMd}Ybp?Ma(kR5pzF<>jn)RCS(Z7pT+|T2nrf4Cslu^vOM!B*u zx31CAYoZ2770;Lt%SGr5FBc|UL1T20gLFoEr$jGA4vC`ea&F5ub~0)AvMm|%ozuaW zeC&l(>4Z<0&MJW8osQU^D3kX@3%eJbDv&eq6%FoDY^IAfIF(C4Byb|U-Qj+?0hpgV zJRWW+Ofct~gW;iTQL&1+GXpWRjAvNaG6L7P5&>)3LlM5T&b=HCvz!o~TgYwxytgDj z2(}jaQY9=}2cIJSp>iD@0@141!KWhp|8^Y=+NKPqgF3|7$xahGyq=k`dqIP+eAY`m zXe7&baZIFxGaXvIuSlY;S1NTP=Y;$ zrGe#Y-$YultnTXL#5vZIZOLYYyR_w-kiILARzQe=z@uks18!+(p%f$|`b(jEtRVC3uCsybY+yg@ z%S6RiVG1RL0RRe}+b8Q@Ak>gzKX8;N1J86drEtoZ!aI?Ii~K<*3G~T`{H8&I&)|w9 z0(G;=H>I-T-`HKJRO9-Gc?@%!UT*n%Jw5m)WOpez2-*VV>_7tNYeu8gM8`N*@-4J; z!V7yV>^KC(vOr(6`+k-sBo*-p_$6^3NcJ4GY;ZW8%G+>qr(VYlEW#6|N{QwjE#8{}@6D}xB?9x#2%!_`4VYS;}pB)GxP@U48CWV&koGsgg~?KCcP`?JxuiZj7a z(ATk{HmQd)({y^@gSSuhn$?$@P)OMN3`r^%{~UlB5wr5o5`5$md=3$aUqv=W#`rI;FrGobc zT#^7sz&l>DMC={SgZgaDP1P!5Z~zS#cd;u}`Tk6_KDyWW*LBf-?v&Gq*!;PH5W22d zrC5wuNm8#&ucZKVKgPCpk}d0RNGr)FdH<2~X#U9Gs@}ip#~2`7PoZOl>*-TH{kUpe z!4Pdj?Z?gV3aGM48({{#_@w!GPyjQsROYvz9jH8~eFZd~!?qor;hzm}?4f}B@yN|a zG_YYh3+9MAeG=|iM8WMyhOkviX08Bp0a8w7>oJ&MGddMVSED45qZL3$K-kthR{}Gc zs@MdqA~sibHnw20lErWiqI5x#+5ecHhLZ6L=YPt{g0a9|&IH`sx<+c4)VhZCu5;5W zc!5Pyf=lC&@C6o8X)a5=oXavjnnEh}J&KeLn6rqQI?HkTaBtWxAU!QBy4ub~eXQdA z5k%8UBe}m$V|p;@fUHU9Jf79E$&HV_QgVrBuvcs^;1IeM2U~Ip2b*q$gBv6jI4Zqd z;vl2%<%u;sSi@?s&o)5P!fH=QwP$7I1c=oxxa_=v`+8#$?>y#n>6chRL&n4L&s+>N zc%5Ueo_1j0nXa&4UU^?hAm&^IMHw!(LWwdD#={kg0R1lJaK!&D(uw7Nj|eH(^EIaS zOZESq_>=us`?J0%<1y!JTIp$Hoi)I!&#wqy??G*tu>Igtgp#oxGvmY{5NEgS3ZnY1 zK^J*qug_ZUE(M_cSO;amr8O=mquXGvvw=d3x8AeVvW!Bcgdm12*|v`jTotexR*CdePrHeALk15KMfA>qUmuC1aI}46>2DdS?5@>E zsmVR@ex${pT^nV)Vwe-a8Lf$AAdJbkZ%2AasmA2nHxGcAUH!5&bO)%w#9~t5x>(l3 zd?HY_e%fcGZ0!jiZFge->IsFB`|TIcTnD;eJ@%#SWlINZgS{#$k;dX^DxrF#^! zaw}NHdsiY)sqS4%{w49A2&zi*JqG*E^1XYi2eT!Y@rC{NH$o2Shtw)nn5SsiSf&U-y-yHw$tMI|{`#QD&7ZF+eQA959ko?t>`~-DeB@SjEKG%vPp^j_l zh}OMh9nK9dLjJAn(o=p!ahEntk)X62Ss1nNrqW3ph&;**mI9xgXvLVqslEtYRrD!} zXP{-8EihM^ac$LBg_bXhx-^ET{AxTf4;@ePt*W<>o-jA1RTvlt>Grjh);+8wRgcw_ zWC^URi?zNrt1~1QoZ$9M6RXq!x#Fq`w@3f>mGCDs>Y_3hnNsUqU2&6Jt8^{kVF)>Q zVR0|2XbrYdEN-bS&qzIzKPpd2JEZ@T|9|M0rdOtalyfKOzx03C-d5czSi=8LTT@gY z#mPC+=$L?YkG!tR9=hG%2FAddKk8k@y zGgilV$q{TmrE{41xu)aFx|L9t=yOBEevWdD^eAaKs}E?9M^u1rB)XS1A{XD6qrDV2 z1ylULrX%Mp(7588(y;dlEa(JJ6x-O~asnK}=V_c7)oJ^tJR9;U`UZ0JgW5V{p# zd;38-Zl>f}DSNF9XxH1XE36V-Tkt*5)eCVcQXyuyP)4Voab6FP)>-Np<=EOqa-;ffkh}|&;m)KkD=A?oLzCo263YYYZF+Z&$>RmS)UuMB_e=Va z{FNoz+XwnqCp`t`Q1aSGX{nphETmN2kbe}#%U1w*L#UA1(m4{^#*^&jdT_-ib z6Cc5glBMUM}FQZeND?i)<|7@x`jQFwz*DfdM{c>18qe;pA49- z=Tl)t8MZ-}INS0yQcm>8#`QNdID_)DeO$FUE9r2}OUV99_IyFssT=DMjQFcL*J6cs zZAE;MLZglR5O6$#eHskZnxtJk2`R`4%ghQay>#P!#aQCUu%0qc^Mi3I)z5$uy6`tR z#a6__dNI+@m&sk2aF&$gWlv}ZvW`p!R{#+D0`olayg)pRBM}QP5)Ykj;WT`MKO&34 zO8^FM^YeFn!ZYdJ{*4gZeT@sc3Iql3z_X!vEUdkp%w=*d##z!*CSa5$pnbfhpd(wN z1!9wwsRMuPXM90Yz`dE2*j9jtYAF6wVmop|EgPLt`|=Cy z%T$f#%t7A+85xZ+S7hYse4y@)@+Vd6f9_tK`e&vFrx;Ssr&2 ztQ9=&CP|Wx#$tqeyNxOJGQuwcU90N*d<#FP? znvr`^TAADjYgKi&h{u%rRW|Bt+BDy1!)Np&KZOt2@M&`kH{&bp&Mn+R>p$UD!SwE6 zATT{r-%UlXZh;`n^sa;TJFyT(DDtB$M4Z}8Bovk)`Pj9<0irz0Y;x=AyxD_sc|hX>2vo& zi!pSupdF#lu@p;v?hUrBnm$J%1^T~QB zReeqpiTd2@Ad~YSwwaJ0*l(qsEeH17X=f3Y`rkW zN3D3Mk@97fxfw?e%a{_qFrFNi&K3Ja!$a{-;{A>l&NvcLE1XVGN6HCgs|O%yw{SP0 zcESy+E9X|%dBL6IxCVHy6huxSdcp03z`C}Wn*R{R(*Hg}m&yj~p+6yT$&LBiKjAO? z`kq|6Gh;zU*VO!{jQe3_+&#UZ8*^dvIJ$1mB7yrT>J{oNEtaX4)|Xo zV16`+?dYecY2%rd@8c7a3X1?87B4&_P-YgVAlOF~yadMw{4a=N4V1m+YP{uYr{ zXHZNPa{k{m_}SWx8GKCyPsedQ!oboy2jSdIM=E4lxlMQyu?gn#Z{TXi z0i#V2V4v5TJu2N0`~cH_c-8CKjwGI{#mY7%?_uSp&2@EYrGBlxD)5BMyb2{k}Zu@{{P%_pu!b}FPm95NIwa;)f z1wLKQob})Z?+5Vb9OO&+a~b*QQSUCNG0^(wr~(-asHOu|^Du?uu|`su29I^1Fj7*QAEgA)xd0);qJWi=}HPUYE)9|3#R zm+JJD9y}Qh950n}&KY}@b8r`_Sj~CdtUJl(yo`6slT{tG8Yyatr!6hI? zTXTYKR_a4PlOeBZ9*z z{oUOa3g@ZvhaO1!_!i?dD_a6c;TKaL=1|91niTS&qY%wz)z z_L0#^0P6!F)!H)>p>oeWFig8bej)O{tRs`#b7xzx=FT2=h;pnus@>L0np!qvT^j;@ z{#FDu$01E~7i_@>$Ob#ohA_(=iL|04rD!Zq;LDCQj~!_bA)g-TNHhjo(ddT>CuA(3 z>IN!yB*Is+J{wpof>Lk|s;;t!tuE0i%hLb!YiEq*E2N=R=FzywyCPU(JCC7!vHu*t zg#%g5ju-w-%vkugn04Xb#WYE1wF}=Adu#XxOus-Ea*snP^O3n`XE^}|r(P)flsUqV zZlNA&cgX&7C$0(Iv4)JnOnWngdEBP`p5XPu_r;8bABd@G{zL4o;kz*X0$rNsrkW-J zRcTJmr`V<%k#4C??QgC_!njj=0}OM?II&*_1BA9s8;=3laaKq>8-ZwOXP<*O$Z1X# zw$%h!upg>lb`RWwxY;;zHymokahBcECmZ1QfsY|wf|Xlo*bL7~a8?>`m?l^Qb6#Ps z--p$_2_iZwn)HrqTkft+I{#PX08bK1LX0bufVd3s!2%RdfJ<$uhGy5`0%S$|WF9eX zZ=tb#B)-kAYc^y1{07;E4lsZk+~qU|TJMV(J!1jY47N|48Weaz@>D2iVZi?}>7aH= zrE9?d2?11oUV% z!8uauSP8}VaXh`nha)PCag0B={k}R$-v;QQ}&6v zPHEnI5@@>>e@r|}Z3Ry0Qx^kvWp$ro@znIGJ-zG+lzT!Qb75@+xFDPV$vWWJ7*nP( ze6TuS9Qy^R#YrpS<7+t+J;!-k+Sxh?f_63+34xDa4gT&H)&)=`MV#+42ZHPiJbsNB0k(0kl zPBsv*(x*WLLpd#-v>XlMf{oBO$#!Q|4GJtUHyPsKKicbbe4D{`w_tlE19S`Y0NbT8 z(0YF~C@>aK&E}v0wo5p!Ugq!X-y4R2CcjdW!n?XH#M z-23PspWt6H&anxU`sCF7njEV(0<3a8Tb;8xjB6<{9h#KG&^!)3v(oelpI4+O?}&VE9K`1{+>RKQaDBm~ayR8AO`u$^P0qM=qPu?$&VESaEu@j3 z#AX9sDF5Q3Ar^iF!*1#O1;V6>=DH_am0qS7&6}^}O=b(*oF$y!0^e`}NX59_L+|CA z!C?-cj2YJ)xP=DHZ?Lzs5FVmaS}dReFb2lG%>pq5Te6k{XpBIlHk=(OAk5pOa|-Fi zZ5QB!u7ESpRQ1|Do^yj88o$kKc$wcL(N3%E`%S6MDrMjK{V6$-ha9&;N>EO+I1~H; zuuf|D&X4>*w4Dc-T~*chZ_b^$Qxh_Mk^l*1!eKBW6vLf_56I2&EL;vA;AI(lG!8){?Pcl@&Y+n*^%2ugW1BQ@H1xs z@4hyHCw<;UUU&{!&yS zFb#QYeC%2b?#5jV?&VqiE~?#Mpxtca(*2HQ(J{NYDx23`)Jc=*P0{yNj*|G5?r$jF z-21F_XLEVqQ!?DTl<$jUN+y4uoAMd#_+OO=q5L$LY$H1`D}c*z*y0om?}b0W%h07f zl0j@Gf26cGl86ti6EfJG=p!bUHO-@tw%Ei-3_Z{1n2Bc%e>%hUV+@7=B9CEtG~aIG z**?Fk!1;;iG~g;Gp3`}-bpG7U?#(B6hDB*ek8p23&GiDK8}R~yZ!4aSkwJImo%L8A zkw!CSzW-(M`&03&FhhE{X%V@cyu$0K$83UR=sTiI%e>ItozYC@aZ-M>*~njty3Ix^ zqy=}gk@oqVsJTTH57`7~%4R}xRZ~MY`EH9S6n;n#w}?WnoDBX))SV0t(jtnK_y@>9 zVTSZ@GLUPu3<52uunOEwo-RQAZYtjNrMFgJ;xle3>%!*e=IlFJoAkF-f77`+X?e}u zoT* zp>?JqrgaKFq=(ZwxxDaYeM_gk@TWLO&w;-pPt1Xf`|^cE?S%rxbKpP188ftFzb55B zfT7w+Yhy^khxBj^$<#i^n_)J2I%ceQd?dYN&sQ$d0Tz$x z4D;d2IcAvE=B3KEAC+xEe~t6#m#pb{Q~4NYmJwh5{Ib}}>#%(%YdYRa^&!<^Xj&zyS3%}$AAfgMAfzX$L-ug>kQe?DpQGkR z3I7_<;8eOau6oMmc&E%-yYP49^Dj%sJ9wi%eJzCPPd~|vF7vwzn;YSLk8cO{!p!Za zOCOm=dv7P|5q2P}q3Q36oPRT zy>f$>3N>o*a=a2kmxM{UJhLokaobC#aVT1$ti2O)LWOb8keZE*aFc z+qr6cd&V~X;`d=B<{LY-ulUwr3A29 zULS0R8D93pH~>wc=_d;yU(4NKPD#B(3~9w)`8qWmk~8;40P>FEw-{##E@EsY4Z02;o8oU zz~$Ez){N6FHj4iKhAuqh*%WqE0_at{QT>QRgOe_Z9WYznY$^@=Q&pOEMy$USmlX^( zD5)Ro@o#+!bHE8OkrS>V!I{J1MBn_I1K7;!AS8#Gx5?VGv{~;V+N8}oaUB)=HlF?q z6iI9AJ`S_}v|X1l+7vJgp)N~dcCqrptO6@5rkFSC%=v|04C#pF>l#EszB1dZA{Fss zs2^Csj((1=ScpubcD^0dJx5CPPfFDbiJ;0XOLO!!tAH#R^Ycy=b|P>-RA8t*@gMAE zrP2J)U%h`rng6}zKj!OT+);;dq-Ssnf(mYjYWLRanI|zC?m+wo2ap8K^Bu@Lo99!y zndj@$Yj7e@&GYRqHGH3DHY@A_G0&&hNY@XOAv1-7)IpHaJfGkU3Q9hW!NCAodXGCe zO+39X9a)QVH^5AJEnf#HJx`bIH^PnN}j(^TJW~%k`RnV z4(dJ9^`ja!DEWs&ti9ym8hw-=Fy?p!;M#$H2jzXMPEw$(w z8AoyVzd{lmZb)ZCCfz+*hmlf*c&dwtvTs;O{{XnR^$I=J$B|TAgV@3W(|J?&(F+GM zOgz4*mLL*Pz40CXhcY(5Hs8rl?XF(|`vd=6{h@M{)*ne5hB5yh!AFnzcg&iY?MKO` z3wrSzR7sQ_#NmbZtwP=%+(9XiD|Lg)>?Sh{RBYI^A#ZRJ;p>8pv4HW(Xs}=C@sC78 z)46jxoNV}`_{2C)nKipGk8pKssQ)kp6t-{ziSnvvK|GoUgtPSRxfahz@p64c3U=(d z@;n;42=Oz+E3h@A+%hjmt}%v7qq!Mb^9~YH^;P(;egfv0*HN^NzQi%G3F)8=Wl#yl zkr^o+OI+2X3FjY3{`&{>i;38>wuK5Su9x~3)> z@ZS$bUJg@ABd6X5%Snz!9y2;pIvXadE1>6Zi7E6ChA}m4E}FAFEI9Jr$-1Y6eB~kr zg|dkN6QIgRJQ2`SeTV|l(XetUD%!^6fI!7@vo!acv4c~~tcgI#v&^Cz^O~p6& zbnt8(Gk4=3BK~6LyCdn6BVRoR*+$ZxSfa`;O3?+xmoRXy7&sPx3E(^djswsZiztv~ zcKe^#XO^>Us#!gTJde4w+8}l-#nnxu8S7ZIdXAa%ATAS+8Db4lvsli}d8A;Q^Jr`K zFHoakG2C>tw72-j<8X#hD*&bW98MfD)ruiplV>|;j~$9IR@xSYP>2rdL%m13UQ7(3 z!a?jri0O1C!Gp_)r}9shL+2&mz77j6SzR*9cnwKH&v1uk->{InnPDTBH$$ysJfAMl z;~9wU8y3=Mp#q+*Jh+UKgn|Ji7DJU?L*vKL85W(BV7YHtNdGXBDNWp{`iRpjS+ovY zx?^%Nd9=0q++&n29L!-F{3T%e^K&u{*pvzUbGA`JE&3J#$<5_QF2g12V}B--SXpV^olx0eKkW`hZO7R*E(b5# z0yS$i_f>Ylhb`sotL&0Fb!Pa z#gl`<7hwT|Y3$sOJNgF$;*Fu6*6N<|OsR3CG)u$dU!pt)6@)y#scVya8k(+f`(LNb z>a+RfE=$)2kdMb!n@P(E|1Vh+Z9@>j)#S^7I!oM%pznQD4*x;498O^X70lpG0Kr>4 zRz9YnF^X%NN)dX9|5h_pcEvwewup**j+E|^;m%spuc#%Rizjw|UcN3M z10$AdCB(mn@%5i#(0X7n<<`Z^A_%+%w-IVJ%iJUan!g{>YtZMZ=I`Mu@2dA%U;v2X zu2-%@t5Bntxjl~bw6_fi zh(ZY?V0aEAC!|YN^-+AFBN8G?VXuG=NGTu>=?Sbl&1&tfKFzJ9;lD}qw^x-K#*9{% z8cX+&$S!JiF-&z}8T7?fGu!)t%(|6S|9J+<2GM({tL?S{O|@Pt)3b?!w%d*5Wc3o_)O!Ciut+@NX1j1*B*q-rRuELa z@ls-9ZY5S$0D!N|yn^Eae7dO3L)`M7R#-~INa+el$CE2U`nynVuExTJAt(=lneN@+R8(jugP zw34wC`RL-oe2Fb5&43yIQZ!fJjlm}g6K#nblci2$`8{zoD2RV5I7ao{frv(cLZWb4 zi5@jFr*?~ST`N|$(tB_Vp6v|O90Nlu*RV1zghuA{&KWX=Yp)%di{*X|{MztlE+1@x z_{zOtR!dn@na3NURlagiM(>)Dg)J&At3_6e)2NK$R)R_rf0{D?4fa^AoCJ{dVUms$ z%t-go)Tmkv$yZ`Xmms-;K@)-{e)Pa#hKUCTwH{o*XA#Z~yy-R+lrD{f;Vj$(d@cUp zXTaNwWFad@Kun?^#Uig zdXwWwt_9QG=$2*eqyW|HIe|3VB@25iJ}}otM>T$vTkzGiiUnUgXQ3`9RJ=Lp8ItE! zucfd=A2Sc`X$0lXj^(eus0-vJFIHplbW4vqNJp?7r7)Ebr5)QMFx#vn0ZQd})q8Lr zPpSN|Qu%KnOcpc%lFI8n(#6%lZnKWUq4I+eYmebWn*&Qm6P$g!T4noA?)mK-7SeBm zt`~~)kd`8{?u{Hj|20Djq;`QPzN>&N(Qbo`oB z6CRx+tCW~bomwpyW45BLsl6$;qHR*jSW~ncL1IBRdHx9!YcNsrdCaE zj-x4tyS!u~K3o^sX|N1*?ojP#4R0 zM#pkV7E9_Y7t5qo#&R(9tdC^tNMcb47%=e2LXa$yJ`>oBkJK$OXpZvaTFOJ@7`D`e zdLWcyfxGbY>3f=oT=*%Y!qYZQ!Y>>hemXZ>4#`6<{F=4lshceB!wEaI2l%1O37#+KD|)Sfs0Lha7WOV(PWlI1varXY=J4wJ$0Al{M- z#+-kXCcq8lwN%Pi#(o+qzqvSe+F3ZQObD9FuU#9f-K*gCD~H18=j-h|S#z>MT7J{{ zdX@5;`Fblu$$2>BgTCH)+#%kN+tI)H-(Uq|LHSx0W<_skJmr+Vs}S zZKb$ZQ?m7IX!Xuzr3|^y-iRWf#R==+NSfscT9b!J0Wi)O+x4&q| z*W27K^S9U2`SgG8naZU)r@cGs_05oeWnQ?D;3L=*FFYFJ=A5?%zDjl@oO?1U9k1P5 zo(niSm^NZ0yeTd+u&}X}t79xVVrq(0JJpUFhddU?vTP<~0JnMIBNo5o>bR7v zPJ`M#b)iA!G}gLJ_h(lBM>1MWfK*1zUEhoHxM)#`?egCyEy`rJhTW#Tvotgowj$0= zOkPiuOIGa`1380V=zAJycLG8WydOahyW8KqFn|l6lS|_Bvt@&3R(~M=mgM<6kd0G^ zkYrv5^WwPjrM5{@XN+msRlj0z{5qPdKhd5i+q23u_G;saXTCSSZH+@FP2SN++KJJQTQ9O63NyyN2iJ-){qHX zK3G0Ofozd80~cj1qYdbAt=Syho&M9(0cmPeW&QP&BOM0TRmevFDP$Tg70Di>$dWK? z48;+|w6!E`#%DZVDWV~+_|o-YFDjL+2h$p8vZe5iZ@6V(%aarsd+~Iuak?04((O?3 z=IapqPU?^?Qu(BHh;pE&4lxNOb;yrlM0Lm~@fAsEX&v$km{f;PDf-_c(Q{xI(Vd3B zqW=s4sjn)yL|^B4;c_99F|kZ1Lcp<3L^xxePz_!-o`pA^m;i_c1nRA`O@BPECAI0mjzpfv@2ZB*{Q~Bp z&q^edQgec0u+yyh&bjiqwdM#(~%c=?JrHU=v< zrYN&-HEet*WurG?L+d;fHqc;~eiMr=cYN6$r~@}31y0rb*UBj!sFPpss3jr#D_iuF zu3Ki9q+?w->Zz_vDtTSE??l(;DT^()H8v6)FEyU4Io!ZjJ7l z;9^}p3n%Gt{j8P(({+U{EnfMdpVdf0RFfbO>uY_ExB6S-@Tfl5G5oEMWi(AWhCYqL zv6yG1yG3(Q%_xm+6^%ZnXnP|0v<{OC@=^5ZgM;*r4zl@&2AMQE$YviJWb)`Bn|^4J zDWikT{Lmn4jt;W%hXz@zHb}m`Es5s!$G)XMYU6H|=3}Zq%IzBcu`EdR=QY!x_&hcL zZ?!sewldGcM&~M4SLeyTZEf)g{~`A6O(?F?zO@&Yphnx|=%n`T%S13mI@Yps4_*{0 zW5f&3Bfgw{yFlz=QG_kOO&~86D7J60Jv(2XLOm;wtHt6j(t6H(3p#W%+vH3a*}gei zedE(t)o#MB$h-f7Ov*VT+G7!8*=`p);e&DxALf-&bOs9B>MGAz#MrdvIUffN%Pg4Wjs=rEQVaGEm4vT z`?UgGtcw@njCFB+(y+b7Dg5f%U+*j7t2j%vy}+4Lg^%UJC+#mfK2pDTd7*w5w-;Va z6gl;~R2-4*!j^B9&rl#!KWlxpHdg+9c3rG;b2N3(XuIQL9b78F5VrEe)K5CoIJES` zJ+3Pz8vP$hXD$?XJ;q#ab-$&=v1!V(i>>aLd{7o~pDN3HlG&K^<0c|6{qy>g(Tw%Q zq>$>17|H94eJA>|V=DVpUzEEw`eO7GeR&tQq%W7?D^iA}`cmAwMqe%i7wgLvIMEkZ z-!&Ap{AwqQSALkA)VLz8GmV*8wPr!XD?tVtX|kgnY~lLW+ep9;bAX!H1Zf_CG#j&~ zkK>y)IVIJ5C3lkW<>H|y5Lr(} z@D+*mR4y&Ga`_CnST3K%nJY)TSe(MIt{hE-)T+u+YoQyR*fkeEDMw$BOk3Qd&92De z_QGq3B3F*C6GxOIVcn>{7Yb$tDY{nJp?a30M4y+lJGx%}4z-*xNGO2aXUFOTdAGvX z=xn#$`;IXc+<#dG<7!-FiejT+*brSDIo?F7XQ4&mKDx4@te&N@u$k4#!rh+7X+I}-4>R!29Y@iZ zK(TD<%kp9OUON&Jt$|Tf6c;@=pi}n-ZC-CNq+zam^bkW^PLO+v=Uw6ARfLYkX^hU&|%oEN2BISPC+%BA@sTCdI5|D-vSyPedr(J*yda1xhz^#s>**Y@;8 z7LM?wc`6VI45#C@Wcj{l%4>11MlfQbkS*e$#BM@jZFRNhZ))i-Igo_r`I`wOdUJ}C z=GFr$vf553XCCSpvzv)LyNo7MDj0Vx6=dx9vUmlufs-WK$u3$K*peK4N$THep;uZ) zl*~`s_H8e$Kf54A;dfprT`C4#Psa5>&GWhedVQhv5!;hf%YL9zPoR3CbjmwA$c6dF zmo#x;szO^XvsO!xfWxj4ZfB&A^3H}fB{xrYAH~9+g%|+;BZ!3qOS16(bRaG~BN!>1 zY+K5BahhQ+Z#bVx=?KnqMZ1uES2-D9|iSN$^J`nfBe{ZFfCO{fdLMd6Gd zhL}E{(Z?h4$FutQ3LhZYE$oH{b$$XBPfn*g3!2H< zTQUr`&>iw#@KIiF#%nUDq5}kNLMGQao6jPbibgLQ2!<_jvm=QBrl-YV63%{Zlsg|G6oV=Hiur4_Jz)!szs-O*cel9v_8Ni<@Z8#1 z3|_}*+R%C_VEsq)roVAvS?i_v;a}l`T}XSs&XmB`NL1K!6PDf*YJcqCc`pXL%0KHw zM58mJx-)DN^{3F17)t}+PKN4qBUP?AhrL+{S z5Eg@1C&delY+E2RIsy#6my7BoMRf^@8y3`1{t}oQ8p(MI(VooV49EN=*A! zUtZEyyMhn3)vn|nUBv_UXXL(G?quBNvwToztx@;_75TnaH;78yXi$Zxf#Qqe_g{GL z$#+>jf4a98VJPC2?!PnrJ7sO8OjiOxa1D>>b3AA%$zPc^l?EpS3a;g`;#HRDMAykj zD4ym#pj3ncEPD-?TTu)`Ws~lh-G6vgCE*h%oib$b_s6cV{E_rYNV=v(kGz2Ncc!ZiKv3$h(B3Bf@LNYlzIL&E10b zS`hT!S0G0q4r*%ou;<@{zf@=L<-_SrGPicyZ7xsH6H(0RNwLre(F1bnLraXxn7 z%<@rRX?)L-?MRR>u*6_R{RG+oA_yf+W>(1t~LVMId_u*3~~ z+)2U!e~^5pfJ)>echtiJ`6!^t$0)ETlkXr6;l69+qj00;W3WsjRHvxgO3;mikI%c}pzIK7f%PsVuLT zEY;3nuTbF#lcho;%cO>R8!@V)()-)|%m3g(IkX7aEr;)5Ne`XO7gcL-$R}bHvizVt zvJFQ*tV*9Qq!;QHtt)r0dBkkqC9f`6>|xnYfkYd~SE%Q}F^-zt!B84szdTHG@onmS z2;}1Ph4ezbTzr$0__UHH4#tir1u!%Hr03s;Kw_S)!k=qHfwuxpjrUD_j2Q#uc*TPL zx%ys7Om#x_{l3JyC?{j=IF={18<^IZhXl>DAId@EvXJ!s%tPa{R8GEXprciz4w6dFV9~S;N3$bEHh; zZuR^JNLi{|_bM+nzMQlR?z}V2KM~C7lWP}zBA3S1ve>aOxq^Lz*8a=->ta)F zx>!8MaGc*J2AAKmM+*_o(ePl3#8I{cjP22;I5T^suhbqr21jZOYz{Ee1NUsXKkD3N zxwnuz=?^_f2=cY7@FhygF_vag%-Jb6#23B*GkSONFS7$&`D+n#>M*{YiP{nuSu1#X zuKakRK+a?g2x)u;dyEH%PzA$16>>=ZR_Cp#C{KDbeKg+S$otb9uaaJE=_kKs%|VC3aH0 z{U>(P_{r>~DUh?1(r(J8%7sBK`Ej{0Nw{*MzL3L9%rV|1UR!RC%gW6>JjUf_8=P6W z(N|h-zC{XDZngy&>4AHDxwmuf9pwI)+)254f)G}}YtPGKlc^{+m%8U_M6)z$?%lk~ zRJJNB78#qp@ZnT#7hEJp@bV4v!$ue(tvcUPNXJaD0M!p3zz9dX3fk*}9B4rX1@a#h zawiwWLWW|LgKCAelC!gY>p8HFi%afkYk?;jLF}m2+>SgOuR!?^L3qYzJ^xvf8nb5# zxtG=4&j3xW>o@tpx+b>u-~Uc+t0JkXBa z#7a*8%7yx^T>Kt8Zf5LK%S)zzmMKpEq$RQb8PqZ()-TH!r(Z|MS>^;Z()}0ueeRY; z*I$v%J|WY+-Ju%m-flQE-P2d9doRGfbZ-xUksi4Bl6z0*-dpbZawod?Z9<@XI~Ar; zr=MuD6vbR!tj$Kx(8(+R6$kof5n2qvL9zAy7KU|>R5iB5W#vG(rMa^4{;Ym?hR6%W zhEv)DaUlT_E1x04UMP@L+66+g8-bUZ+h`vFLOlm`EEJX28S zqqJ_0k|w>n;ytcn)#y1ob&k&dqT^T>KOOb$^@ix)(u7xUG-Kh}1d&U#5n^)%p%*${ z91UYdn*haROr!L{G{nKe$oOys6EQTy{51Z;jSe2?fV2cX$;$qvSot%n9K5U3N57A0=tgR41$h4RGK_7t>)5)n8T#ki36gluM!WX! zNjbZ+n)a_ETV?x~@g1X?*;+F|&eqB*#4E!jxes&hMRFe@cVbU}KnUzVZroF2HWM14idQ6;-)@tQ|vJ(zcOuBng?FQS$R9tEeXhC%(p&Wyg@&V zM8}HiB$uK&jwykHe`{3YXd&6R!%H^(J;98P$q5_hD~JouW*Y^Rw$Av9=l=|;mv_oI z)7VmF_l0TsOgW2>VzGkhq?aoU`NCt8w!(0>wV8iP3~3YPB|g*v%&$rR0W#7*R~E#5 zt}M7V^T|1#SbdvWnf`%oW~Df3vxv7`n`O0Q3&u}Yj!fZPIZ|ne%aK7%(c*Gsl5pin zZDxm;^kckbL~S{%WaaF5JjUg0G0v=<=_@T~za|ALXD0xR^uT?R+$TEs61fAplXCV7 zAt-0vg@=)O)nqD)xi<6D=s;P==}@*TGA|T0SGJbnA~AxO7s!tn3gpVx$wGReUao9a z1PJvUu#TW(G~m>{Qt(Grl&8o;uOqiyYUm9LJ(HHS+|h9Y zPcl-JJUW|&m@vDr{tFS$ImH^+zeQ5zKy(~|qeS51q-@x+Cb{$s zt;z68GM>ZfZShpIpoa1p(cZ{s?aW60N37P8lvY!QOrlnXuCp@q8#qbJ(69IyU54%= zC-l#iA&H}=45`h(G?(A2Zu2W+Kd{X&sU+n`eC5i|YTNwAOIChNsa*MymdEACpr%Z5 z`7kNC@}V}r;VqL!x_25^+veAvsAZ(ByE=O}D-h9$?z%{H{&(lQxgqGR1#XOy_< z=*U^rzQv=R+jj32Q8FQJ(y$&p9vb_bXZ!kH_#Sp%ZSCN;N~D`bTAE|uF7`W@=6|8f zwr8=HYmTyq6$>ur*9cXYoUpGNbrgOWXxP^s`ka>DWtb9c$og-3$H;ocSnp3 zZWkG$EC%025gC$P1ON4%^PPA93EDdS^$uxGpcwwuHIs2ojZ=0|7whbcPZCeP&5c`6 zq25l;thc#wN4_q21SVP4f;cUcpprl?n4RGeJ}wp zN5F|XPQZjZ|C=Pilyw3N&i1k~XG=-v{AAs>{}uAa=2kS&dn$Qp1&=*~sCHbxoQ5;2 zU-XsMFYi-wRKJ7(BRz0Oau3R#l&^oOd}*FVxpEqDus;!eu1>j5nV_7YPO%8QP!wF9 zGK`A?E_hjsKCt-$xjJP?NcIW}V3ROs2+-?%+d@vZ38ubJ7t**5Mvx5~&4l{9#I%m1 zEI@RsyiGD1*{F;0#!KL7Rn|IAZ3K?a z6t33-qBd73s3QXf^YCeTh~4KKAw36d`A;Zk@H-69zy^+{0c^rn;mQ>UmD|na8?{i+ zfenQ-r=vdCMmb_Y8X|ZL>N6j89tUIqYr;H{i@>JBEdu9dnN9m#KwNCjQj9j!Zq9fD zNI7F7u{DeC1b^q(riGkvCMCl8q~eNYyooWAhEGJQ*j=c}NvLhJP>4|;+)Sw?RlZwo zA(vBD796?W~-c^0{)R(h!$3gIZGJa%B4F%286!v-Uo^&;`*PAftE9{kyWV6Hr|+b22UH zUvuz5w!U*a4iES$#-5E|cu{M{)_7bZ66><^+#C*eX#z6`u)7@nV&#oGj%c2;cA->P znZ{YGZf#xBZzsWA2iM6fox5kPYMa2}Wl-~}mvg*u{>ZKr9+q{1b7ze716wf~wy0S7 zz8chuk)_3t!hZCaDlB+n{g#~8Rn31t9^-Yk#BBTxS-zQ9=3JYbIf3B`-YfpjxNuBD za7}i7UGO)6SD(ZMQMGfDcotg<^sSab^NN$>dBaV;lmAAL6?!;6YQK}bQM*h72B#Z8 zSCQ~ya1cx8X?{%t%tAdtK~Dn^)Qy8+tGOgOBeF+eY*}Ijor`l>i;8) z6?w;J=(&MU0c-0wB1TfqY{e{KFWnZpm=1|PD->rDFzWf6-lr%4)m%O!KNc?;Xy2W7IDJnWQLtVqFxfbDDDjz){9rH;;G*stn9uzri zPD#Hlw=AqR9qBN(cpGF$M|iv5%5MS4Z~I1X^ciQbKuK;gPgh)ae@%Ys%V)2m%TI+P z8xb8M~%&iXhJvlQ|6!Mee-eL zog;r*uctoKIilx(3zTwVH*z9b!|m|r*5G%~;hPI{34fmPqbP!BVj`62<`r+7ZA#&? zP2U#$9oeS;k;`!s+b8p=I?vgk(7qEpxCe&wIf4{}@9^|rX1z&Tw=~G!AJ_Up zK1&U|%&;#Cn~UQ#!-e)-Za!YB4L?Lj!?0)CbJ(6|n9rY#qIIs}K5ftQ%*V@)mhK)~ zg3f#zof-NWDm>>Cd{vI1V0QbjpjyqGiKA-PZhNXMJTNY>=EIfn{lG5ox$ra;og21} zrAQ6G1fgDov^Mrk`h25<{zS#-IPg8giYcx)nhGn3@qFU1Y;_RS524gFS9Uu%cD9tf zi;na60od9--WDL1D&FCBOL{MyK!iFupu6EFIk~}soBRsy-o+Rae2R?~I={2i8O#oD zVd(C5X9RN}gKTX_Lt|S*Q}7z&*G)YO$2Sq%rq?ybyp1ntj{ky$w;m^v$|nw~=a6Md zr^V#p+^sJ#1mdFaG3LfMb(f5hrhrZXryMj^Du=S;&v6b{3Fq}=IIr)0L20t#6_=L! zNo{qp%cZL1&t&9;RJ_6H-tfx^w6StdM!un~AtB!|hJ3>q@(n5Z21mZpk#BV5f12f2 zsi2MpqX%vNG34ctV(y%r)MK%5OFiC=rCyBSq#@}-Yz!GOEnw+nUsM;0&V?x*Me#v& zq&;KlB;|$n)GK(fDn|Ayfm6m-0?)T4(lRCL*vYp0uoFu;DDz|7%bze!-`Fj(9-CH^_@Z@6Y~!TXrVj3>hV!Emj{!+z=jB{Zo|UNzhV;W~rO zL^LNK_T@1IP96AHQtQ@w*}l9VtQu}NzZdNe?l_39YM%tuUl zl=aod&_9~nAtk@yHJS|0f5q>&#Nl(1PmE#Ysx*qAYk%<;Coyw^BI#KcwXtmC#~F zdlE+1hJ;Ib3YwRKTrkNv4wedJaq0(w-IRW$_^+oR@c&9b96@E)XoBrWBzzFy==38c zcx?&--?%98>+2aj`q$ z=OQosv+P=3@H#IWZyRFubhAGsk3$dE!+U1K*09uJ2h6dySG`)kcweRWWl15A+VMwk zw?nT9b!UUcZT3VE*%JftRsek~gy`K8)4fS=wf)srxYTTB4_^9g&ThK9LzGi~ z1#@i$)+Ou1Zoa6n-k*b)^=&t2^moesI$D<*t+iJ{OR$udKJv8mozlw2F{c8K&*51I zC&Jd9*&D(0rW#AAJv*|+mb71yqm!Pkb}KZIkot%trJzeMX7Hf;oispi5@1d)0Q1f+GK<`X zu`4#GB;kM*I33OYwgjfBrJD6Jt@MNRPsX#gUfT&pf#dOHBfr>MK7;r zZ|cUu-mLA&+LCI&kpcEA20HbctD9gZ2&Te8Kkd!|4aB;0vSZXqpyGy4+J4eF^yVoY zckK2I(J9WN)-AKvBW{L$)S@s{y->StiL~52Z7mIVB={<6^sb%;GMYbEi5L|_Vh2lQ=X=M4KY~^#E(ucW~UP8P5(mejJ2RFxy$zJ3~GGX zku!p=%Fo)xU@2nl-;uM>In+BiX(j=V=0Skbc}G8HOuU4@nK*P`v2rer3%IgN^~5{s zCtT+ETPv6fgJ$tq4gkZ`-haQq-EGXWyEZptw41tc{w8|QtiH7k>EAA&eyihRS*Trp zc%1(XrQxm8Nqy??L1Nrk2hG5J8<(a@G52=c`$GT8exOWX^}r9k2K+MF_@!M-FeTA-_p>2&J0(Z zT)`~Qz#E*m@@|#+a$qrdjFzM|-h-dP*|Lvu1s~@V6@xp0yYjo+b~*$%DZd$fNkxYY z8k{`uCXUMXr!&`Ny|F?8*cPK<@S=0@ng89n@I}UUt}>goA0ps9ykU$X=iyyNJDxgV zA;9p@su@0u1cFcShz<$!F5T)A%OGAG^xoLj|MuaO%Y}^DmoM* zWyU&dQIc3r6tVMfNToV6Lz#jo);v`we8*cR8h>NFS!ApMHolbyvB+}?F~QHruWjgX zbi1S1iP)>YMf#(|EU-ch?Z_j!&_e}zr1ai4bdk@bB=rpo>3IPa3v zXpwN|(xho@x+pcZ)icPs5dn3_k;unhl}N5TS_{l!F2$h|3+P`YJjM*Dl8ghGthT2Z z`SpZZJy+$8R~4qT+A#A-2@7}j}{ z=Nca2V6s9l+PNX7LRpgh)P`M*JwKVh`ahamob4o7^jW~APGHjJP#bBTIH28m#xiuc z45pLkK{pR~=SlEdy9Kkcp((SrFMN-k>6z7u0>TQ_!=tN)` znhfQviG53FV^nHQqT>{x`aYaSCyfrPYzqR&2AqG`94!G)4R8};D1Qr~c-1xL5#nl$P@}&OX&PUhVg@WPc~@B-Z(`81x-Tt4SvBRw=qQ`o038EDm%+(5 zVmOvpu%EJkWnAvcw;~Q#Xm#hKw83jDMJMY$_z9lvCC^{3Ld#vTlprB=<(u9kUF#-= zR^iw>Ru3^<`6hT@P*NNXL4#soYDMQ_DXos(xE2VdES)alZ5U87?AQ%d#dI{bH3r|M zBKJdCrlz*0`0gL+#z<)*%3@dk*i}Cr&7B=BZO!gBj8JOJh283V@KQg6z;=(%?YQws zY^e_WR!6OtNmJuc%(pePwO|3Blnfh57~NCL#?HvHr88DtQ(CjQ{qJFM)q}+1s+4=7 zaB+{+yqV>Njy~9^z_X*(*_QFT+s(4XvZ8Ig-$azDZOLI;{bn$kZ8@8men)vao^r0p zlb>wMRlu&}pR+B>=%U|ZBvRY5s2H4SO{fZ3bGqRw%n#!nmBP15oOfk5q>2Y@$Zim& z&3PGiiwzLKoF%amrxFvi62TnT+B0-kiW`t;R*FZ3-IuUdibuh7rC4>Gi___qKZv@t ztZawHn3XK9@x+zXu4DMj(KO4DOUHSHtBxlLl+%n8L4yfEySLUE1l0~yk|s#42lqWv zI#MLQ&t;hYTyAdkucc?ADdJ!a!uM?5hhQr|p|_I{ZqkzkT))R=vnkqsL;@q_OjD@m zK$XCaO(UgF6^V(^3)*;0ci1>1Gp0l}Mx9L_$Ii2GR)=|X)Hl)^nuh`6#+o#aKwkYj zl#|ObJZ0mz^QaH!cXWEHXF>xjVzh&$&}u6+R+o!#J4|Ep6scLaXb_iI?IV9NsBbg8RDRuhhx31C2o(_CNNkgasG|rlHg4) zDeV|v*1q?)@xfdAn$SMMe;Y^eBp&?J;nB_qkD(oFloz0YZLJnQ@%nYVwyzQN5c3?@ z(6tz|qS%QVCPk4z|AKy8EpJ11m4`)g4T~|Lo^9@!Sl&gTiNPd&F)NW|7)1DoxgBi$RK{Yf5(#kgJGqq@^%Vg;+M z$6uG=D?15Qi)rxJ13{&>?NOjWpFn&3VBXK>+U0`3Op(+X`F|M&_o-xd1JBt+g)ZL7 zPj&4@z^>$Aw_ej@9NkdlQIF8{X5YtW0f~$coF;gQt8>+R~j*t{)Qy@AT+8c70 z39FpDlr=;bDY#2ngXf>6l-*!)3PJTXsCVu%VM|$qyG*ztxlH(c<3XGZ?aHH8d&WsO zW*~{aVIlpqi6b~z!|6`h$Dg=Hn5O2`Dr zTd@a+lGY9u6NwIWm4;}8S)$R3UQ42FE&kg?YPzrb*2z@5L}$Ep=;JtQ`S!%O&MfX5 z7Se9iu2nci+(w)^#XCgtZbH29DMK?yH$v3dnx?$1X>0V)kvKLLr#dxZGlkifbb7jq z+GW!C=wc#@Giki%pQ}vzw;5w0I>vj0dXIE{q(=S5`N_`GiaUu(Rs&;9_7ze@rS|1ULucbKddY+a`DLS`CIYi?{2mr?JSsM^Fhw6fSX z(Vva1(>_`bQ}VX}Lm%xZcqZsI{LkP&1-YqO(ogW8^7tC>t0~P%eImH(1#Nla_JuLy zMR!p}FlnE!XJqq=e%Yc-`*e4>IS}sN0oxr6fVFFF2V`E|%!o|^Xx1k`#b-#|0WTjbw8p@$7|Lf7CIn&DIqv3>#kX7+%U_beR0NP5+&=nTD)1Ei!duo1wJ% zNq5n!Tfg!DhwhYF+ZE;`*K4dEz5-V1&wdH!>CgTU;bV%fGX6iq)8C%@ExKOcRVo0^ z);84DJYA31jod3(SPP6t?j6BeR6c^>`*gITYeaSMvpjv4KqGGf26QR}+{kT1AwkG= zSac(wjHBsB$?_@k5egCvf@A(6N24~<1{)q}u+fp=2SnQ1IA>SOGV_;J^6xWFKFJo* z_I0~JbjV$}8~b`=c0NM?A>VxeGn8DppSqA&7bZw*fkZ)JJHo)=)|ypm^oz2|>gMtPxSmJrnTgL;p2ZJHQmh2w0cts&N4@8IgZiWP?bw*k!PjbqHD zE-O1{x%vw=<37U0<38riS}}yw2QTGl`F06^Oj6moBR^x3+q2r_s-f@!$z>x2D)RrH z+)-N6OdXnZ=QtG{o^!204tBRhD4$#kz3>a>PO0Qm=%2230ZqqRjkR_Dqk&ee=CHUk zE^r#!%YxYm%8BaR9t_FR7#F918h8yh4p;}lLNnCY7@nkVz1W|r{BwN;>1fg4&JoG%aZ5Vp@22t1 z{<1j=-)^~qM*eM;h2nN=Ig|CWU4`ER`y6(z+DC2ITq@cIw9c};(l22gy3fvJnBHlp zkNH}EkV2?p3ceO2M7I-JDb#Zg>EYs`V0+l?nP(qfcuR`5WeaZI$3ZmXN+YG|s%4uY z>+eSko=aC2LV(Rt1ZnUOBac{Xv_lHYbv4H2^vlI<#PdqV-Y&>Ax)!6HZase@yGpVi zM2tJmvPB<6nfTk4yRGoyg?I4u4=3Jyeb`W7ZlG{2GAssF)2Vy#mb;36BcLyWc|!kQYF@y)Y<%zat)#JlDSPebKYeTb*`4QBtR2T2-h0N(P|1A#6sEg5bs%dp^iB zx;qDWNC0u0_;^@=J9B_X1h`88uA@oeBc*JM33dcWBV9~Upv7UB#T+WmU=h-{j*<>}+JX*KHaD*XGpqhJy#^oP z+3tD%3K{$DmH1GIR&P+RTrWwXAXSD`yO;Emj2pA+I_sd;stXreb@R3u+!mVoF!z_^w6>P|}gy}0N=N;3Y=02soZA<^xE+rJQY{nr^UUPT&Z;#Z%> z!zAPXJdMt>u^Mp1(KLZE%DfqJV1o*@YB2uhz@=x&T;>;Xt1Uhs(T<*PSQ{Pexb6R`09yIjx!YA+&97PFG+c%(eZCX zpgId7wxBjo0gFTo_c!Ghg$7673ZT~Yg1sh@Zx*7k-PU86i3W=Cei4$-^6D zPvbRcM`1WR{q@KSHEGk@W$w;=xteCq!KNnP6{mr`+jMt(A^12_x4jhA-Eh%t9_6JJ z><*k>hEG)&sJ^p4j3ZLrO$KETJWT(vdQt1=YU{-Y*sc8XPpsOSPrG{ng8{F`+GH?O zC(d0rgY+tC?Usb+vn1@Ph^b1AtJ;NsR)gkJ>Q-FN=`)h6@SpP<)Df8%e$V1DuX`yT zFMI(=aEc1n=onS-g8BH0uVVau7B*vRZ{ybLw3*edl+QoFTbi$XvwD32UU==ptGWxK zmCcx0ohQFP%KP2X=~nRgG)97@V0fHa-Cq7)%=<$&r>Nj*T^{S-RQYP{o~?}Jt@VCs z{MxUqy7UWiU0QN=>8|Q8aW;k3rFFbZp}t&Qda)y7N5r?03Y?9!8$Q5ybEGVqPQ<2KDbUiBnVCQ1pQ~T!&iC4-bL}}>u0}^$ek4DcoxQMU`}5DW%T#QN{xOdDlv(|ST?qF@vw436Mce8{0%AS>#Fta% zv(S&|&wR3@h^MnqjQ}$XWuCtVDBn@@k0qAqFAA6=7rkzj^b9nL=8i=Qb5IqYII2C> zH^0!n<)OL<&R|bHj)S&V>n`StC@4!B*H?l-F36FoHXbBc&`jfk2JHbpQr-m*6Z|oLmXSe@K>d zdp%nC%tU6PPbBn;l~h1ZZvMXbvjxoYg|JTQsdRJ1TK@}T@Sc*0osSVbc0QgtpTHS# z6=13QFs|{r)}{&8BuZ;Dp!lLkq?Rs2hH3JbfH0kv!ts+1wxM6W{Bt^}j4ApjJI_;Q zO)P9c=Gv?1Ov!Gv z@&^`P2r^x-k*;l$Ojjr}{bWcf(*-Bf6<1?W0XV%eH~0>WXi0ni^?w$U z_Po-alW9+(h|D>bh!qrXfyoK#2j%f!0E3ok7L^!p zP#Axy{Jew93*GZ=1^?H#8Y@@d7?PUlXvwRe2b^@Dn&I z6NQ4*sgP2h2tMF!dKPQU`m2gt%>)e#WX%L+kj32?y$7*4ZiWS}R@{GA+Cw227SJo# zOt82;*GynoK+S~lEALc*PM2~giPMzK-;Sw_C8d$>Z3z_D;O2XzOAs%Vw0th(YPNP& z{QV4NePDeeaMt0qnwEz`IGa%24}WZa6r_4PJ(A@Nm7_40N)s(5ayeY3B{IV_`5}mG z&EQ7Fwkh&w=hB$E;yGZNdB~Mh@>8vy|9@ms=rVRf4rDsJERb0og?B^y3VR&U0*AJ?h9$Bj|&iG zv#sfi8s)pkJhPQ@p1j9lDT*Q$FeTXB*BU8w!Ik`MveDfhM>Hf$=los0`DLX** zUP$hl)SgJ=)M&8vW$n+k;_6dm>x# z6-hYhp2#D?qhk|Y$!rz*KfZ83^1CA!KFR+Rfg|#(lviDm#qEWiL=ny|!G}46=h5JxtTw`}l+370 z9Fno}UEW+FLxF-}v|4K&6udF`F@d6XdF?TuXv8e}jRVGEtdsDB=ZkYRKy*?KI9{ni z1Cso|;|{0K%75e3qK)(4I7stf@#gd2zLWg_QYwcu|0U^~{5Sf^p4i1OqWo9FB0bXl zKN$>r2$%92?pB=17~rlPaih}F*FAOP>wGphR~Qn#wQA`FPZ_Q@YY&%CD>u1|n$)GD1iubHcv&3IZL{*pa_Uv>L{Z7*+d z21+x~14S-Z#813M_>&<6W;B z*Q+VlE>-*a4o*q_X52kHkn9G-`l;1zRKYV!3bk_HWK8{ zYm%-1GCG>uhRJYX7d3@-pG(eU*kKg5XEd`Nn~~($c6?N+FBp?LGvIn?-f4O-Kk?c; z+D-+H$?U=ZH2xgG*cBO#a5lawSHD=d#0}34%mwXL&S7n8w7DYbF@JX5s0FB8ouYbc zQDuf=)f6j>;vM+%`!sZ7R3_DWWvzyr;|qn&mtXr%&LF-kO-EXOmD-x}YfL5f`x?|i z<#!8wMM@d&0lCFfT9$9XS6Y54YiFXF{%2`WDW8*a4g6DrUlYSMZvOysyxVv>3sgLw zwl#s0KkKs;Mq5(NT2*d(xNb@HC_a~a(;mT0MCX7aS{83!4m3_K7_Ra zREswS@2Fonhalj+`G97x!Ve0*>!1QlB2vL3~ z@Snzi1%F0>3m>QM;+4WL#w}%Hn5G8)nHc^t;4&wg0dK`|6#volXDsk3oXMXCW3m@e z8c_54GcMU+Lsqf9Tws7yf4f*8zU3WoX z?_7=8T8E*qasp1z|MEPV6rTSjz53n#@|zdx2VA?tV`KF?^O81mN8w0PdT%bi^xTrO zp*6yvmddvH!Dt%8+~5aa!PTTD_$-g;<4Q)TXMEs`W@7he;IgsYOmP1iJXIg!42k3l zwgFnY1r%L3mud8N&PIGw)Qv9ea0{VG_E>xr@27-zu_r5T6kG5)#lD-o(%KSl<&)(z z6ezeBB#VRSfX|;sla2;lbF{npeb@Q5NJD{w*&tCWHqu$dy-CEoiriG^-*aBACHJfo zF_z0Hx(E$(rjt4tF_E_MZdXo>I@4R53-ni}>ZkUe3E0TRN^eP7414{Bp(K@#lh}jt zZyspli2HnfV&6%7@V+#U)A~eOS5u!@{7HRs8%(P{*%e=r@+PfA?g5kQ%k2R!)?@Da z%j)A&VL9o#-{O@Y#x0rv&>y25xoX&gdf|Mq(E=Xj?Z9yPAI?CUFtCokbAV`1P^g9i zTD=@oun?O`cjR&bW;~<=PU5;&;qtPv??g5aq&%jwQA%oLV-Y2?xi6N@-uQ~deJYzr zYGtzzxL7v(;e?;0pZlQ2Dg5gCIgdkXRsEcX!nH`{pEDfk>cZhq~1FJSpZJ=vG1fUqUCHZ(fmAb&K8jYx`78v<9M=G>PBAG zFY@rOQ`5=mo?FYM{=rRPf{ANbJutKS9Bd39<9Kj>)@7{ZDKSTU>lS;ePp9tg)Sd7( zn|-&0Wz@#eTb%#4CUntXG!0r5f|=DHDy|dqaqU=n;4bFAbwF;XowP`#32kI@qVpS` zq_@uAHwS!y3lf+)4CeTsSzW1kmgM81FYuC*!TaU-U8QfK@6qrlrvcvs88D{RJoGeO z^SG;(!q~(*2KEfFc&jV@4biX#amC=J0Y zYJ}9K-PHj55ybGKs9c{ zBEHYaYmfQNjfWZ8(85?-iSaOTO7$UFT79&M``mcgHP>YIM@Ay9KaAR9Yad~k6mP!% zuhn0!6X~AfJpg$; zZt==b*8W*f^42hfz-4ttY2#dvJGzbsq7&SjH%8mx{2!?eD_`z=#&c%d;`WUK=F@K9 zN!q`b@{p!ooYth>VoK6JDNg$h__8R|v`H5upWBJ`Gidh@!3FBM%)#-Xae5|UCv__$aw)NL@;S*hdUb1gl`HqC% z6%%Pvv|1+pU0TD^nd;~U+%VFQ=M zhk?yGhInG&Mk#Rb47lG96Q5fFY+`y>Q3nr@*TG~SQxA?${R?h^K7$Ikh>0HKfx=~r zjbfHq{;Z%XTyxwlUy=^xOW?G8`M^f^MJ6CC{qubhlZ;V}%bF2i3~`OshGHYHZ}y$& z+mor>Q+-p;*65qjPxNgAm{M809bc9OslLqw!EEXkh!Cd!i=186PmeW**HSNLRM z&b`i^l+T&f%@OtRmz1vwY_=(NU(s|AI;;i#8;q#!b~8;`y>D@E2hYm%y^x8^^!=ik z*u-v=>o-k50SWW2-%Qf}aFhS9=%;RASy?h~z^NFcL`hBH)fGu%`jV7S$dupdis z&+%M&9;hRb`SR0ZWy7a%mtrsskMw*ht>^wx5a>syM!d|9yXq%1t0 zrZp`Kl1NQiuoNX_fq^wE3lHKe5^HH$mq!9mhmGvk#W*S_?E>f{OauH4v<=v z-Q2iPAupcEg-`6Jwzp$Mv_%2CX>o^NA%@^3Bs6p=(24B*&e8n}F*58;B&h6Yqq&i`R@NZCs3#j=B#?C-sTPis8j86>!wS zYF^XOUBpnEpe>Z3yP?-NEM#?4zWpyKu6qDRj{#HVAjy>LFs5u9z!}^NvZJy3sQ7GR zz1)3#X9ldJxva(jJE>*DI1Qx9PNn1ycq{5H&Vt=f7`zz6KXMGaSrUMYv1+e4VLG|A zH4fcP#I?+q#ry-1=^GZ(zfpXD1)hRenQ`f8war7E2l7XKjfacJ!Eus>I?uS5 zgssj~I`ie+zE#c{FD^WrmYKAit5nvMbEBWkw{AzORp+S~M0()0NTl~UP=0}Yg_lz1d?io8~yr=TO^Y_r}L&l7|3VR^e|H;Ld*odDZ z2Xb{2(AWs$l|3M^6#QfzjKn>-YSnUp=wc%wWM(9yZ}Ley>>CO!^ALSY0LquBmTw{= zu!5Pnh@KXfEh7TRZbZ)rz}T&M-&5WP1u`?jRuTDgro`AwO$nWtb>*3zNFvZ<;Zf>C z1x(1PjS!PF`*M@#OMDJA(j_BZp&yv2^!Id_|HV z?x*Bdex^_dez<&om z-Bh$SkU8l?cylk@W=lLcWF8)DKB9!q>x#Z5nC$)+K&e=buRNqv8fF-h{j&noX z>x1vF!)Vp|=2iUKS$?ho={ThlReds{u8Bl9wKU{oI1C-l$wBuG&4<$Sq|^T-i3lDd zq!)f4y78sBFW^LJY~raq(pP#Z?$Ko7|6%P-;Or=>Ki-?Zvu7sBoqaM134sYs0wFBI z%p^bvn;<)~?_m!}hq)|5PmfVt5CIVol|Lc|P;p^VKx9({K|mA`L@*;D`@RXtHoWie zRBv}OiTK{TpURXyTDrPgM)Eg+kpkj>Rs2E4rN?-!>L3Ui<2Mza1@;%S zMKOxUhI+hp3;#>__ufE&Yq5O1P>lSwXVZ&az$7htgj<@dkiB}+k3$eB_>Pbd3F(Ep z1T22n_KcuG@0$J%|9ivAp=W7nH%QH50q}7q{4rkiFAI$eRqJFuVDu zki1$Wfb1rE+yQiPOEOOg5Gov6(vYlAyS*f#QEk5tE%GizfsX+?#ph8a>$F_4o<9j4 z%W#q68ACf+<4m~;|8O=7#a{}bIVMCWlIz$+T4->2#-AeSrY_A##n5H5b-=lxiFuoV zU8#UwiGZ4GGyhPK3M_APTeN=Y{0kV*;2RzaT)UaNw3b<>br??ly}Nbm;h%nw8mtOY2XO zsXQfKot87Ue1&+#a>RaE4E>KVv*<;Uh6)GnvTWsB`<K#zGj9RqVCjCKQ9{^J?=Uh#p$23Q)fWJAGu7oxVou*iK(1n%SxDQae2j@nom}1{f(I{)XbOE1t8{ zK?rRfZZ^94yOmvxa(%RpZhwZF@1>e$qm~cfor5Z8qi=JIZ3x+Ws|fkVnJCOg-xSgd zb#XTOmH?r`fe-j_le5v-e;KrQoz#CV5jUe9VmP%DhxnO{=!BIL#RjGf{OK~VS0qNK z37vKHRz4@Z8Q2Ea(SI2|r3UszRXzr`#guGZKLa)CpR+Hqudpxm`H$vwypld&dF5=Y zqR%&;nQfV{IopzTINQpK5!;qkN2P5&Hp;gCL+aSJ-X)sZmhMv9Iur3^TOR<76cB%3 z@%I$Z+16Je{KLvEM%b1{I_k?c{pvlK0K2k0Lh%UprgCibKYblEy4mI; zaF~6-xUGoFhoMUk_Z-hr<9h^UPqq3EEHFj!vvoy9+_!A#n}kg3M8YPpOs6JDPN&j} z<|=OdvwW1!M!?28`-o_!Gu@>+J3rQ0u^l}rKxe!nSQUK?RbFRjNoN`}>lkAeW_UM9 zjQEJR+ZNVkFI@G$piq!YON>t3*FTv?zNlso}Mb+dka^CR7?7vKC?HyhebOYLw6*{mq%mdLccWMfFDyQzh9ui|ANe%*5? z7KXS!p*jk-g0R63Gp-4?CQh-o;bNZ(z5ieqV*J$^UidRKz+IPAwZXQa+k;ON>TR>_ zMBD%t4eY0ZNF7A=E$(f;__506D?V0fomHZG;(Dzu9DZ#2Eagpfi!ZCP@0>n~j9I06 zVLc`s`+z#4nGeui>H~g=(a3K$0E`sm+x>+g;a^?Y7eW7JcB;fVU#US;?`8z}O5OJs{}aivWEX|4_Vw z)6j>ja_XIKBLxmk#n}a&eBici-U6ZAM)4O}W^ix(j<4k;nUs84tsUf0zCo*w$%1^+dDP!jMQkoxMYmW01`Lepv`E`z&-`B>D{A}m*4K=eivk%Zcl5*EHBNp z!t#uBXC>!YzzbFBC*rd0)ug`ujPuJWXKZZXlHTei71m`uyT;^s@h9Mk9Sxi#o_Py0 zo|DJqc~K=#?T3ilUM8n{sP=*D#g$iO$K$bG!!x$sW3s&jwtZGHuD+xW|IhS>ebX(6 z7f?cu$&^Yaa)ZepVr4Sgz73Yyw;4;$zGcDL+L^|bV)wDeq#j=!RgbGd7}w)e zqFFuaF0IGA<9eJ1FjA1O$KShpbmN4VtR9tkCF2C+8;VCT6|r0WNwkmt)m4dJ29Bmc zK#hsoYD|&bI6>&qIyG3PQ_Co)Q7tn?b%pKwfQtUH&h``g zer;5}&W0$i*I7ifdet5Es;;@c{A66O>jI3{A;{P3W2#r3nYoZI_I0aQCC>S&E)XBW zsJE2}e#&_B5nQPJW0247>6^qEUZ~5^$t)MS&t|SCXy`(5 z3!Y_u6i;%U`%0lUF5pni6fkh;aB+#f3fsS$a?4@P;yc(5qes9z0o3b{%B9ve0!CSe z4jjBfwtcHRnT3qjmSCM$mX5~cd6X5~m5ENUp}*TE%l|@s?Tzs)^>J>N*WttR>r{Pc zP5<0nS^QJGPM%G@k<10Z;rCF z4M`o_*#<;2JJVfiXD=e2=3Acv7;Qw5x3i}}+I;Rf6#bT!Rf%){V-pa)wtn3f8DB3H z6SInFV`9k$U&W*K39vU2n(K&D2eDbuR%KZyPMrow3U4PNjFc zIRFv=JxVt>(gnx5xru0|8{MV4xtSa3<`%Betpvn>tN8C+{5HjZuQ(zl&vS3*p0?Rg zI2rqS+jJ(T=uDQaF~s&De#mu8uDyD|LHo3PJEA+FWR>X;x(wb)IMTLA8!86x%HkZA z%1!V`g6M7n6spv{zkwxfrtFM)`XUR2Yd*;1y z@jO!%CVR+i(<(P-o3cLFt_@>FkK47?o@>|1xei7D9h#w6<^M;m(QJPk8Mod`vIu$6 z9=1(j7BQH6X07)&v|jQqWh({`kazb2tMl+d;>F;vguz1u@fn{FXVAY9o#_oe!o~kP zLBHuc6h(7kr0sA@S`!!%a^?Hbmbq<($I0_Qrq4&AF9wehHgwP*{DVkPWS+;Vsq*65 zWD!I8@^MX{_5{}UX)g)wEhYBXg0`1BWRn#Km5fO;Gb4#pR*#uY&Dhz9*zd`BjFF9z zteKIpN|!G;^{}Jrf$L65eOLr}f$f~YEaG6hBruCO*scl8A_jAH*`MxX*i9q_#?^`h zM>OHYrUuK=>@4iiV21&d9VYkButO(*uQa+p(9y>RXi1L}Dj91n;@w2@;I&c2Dm*4~%}R zBUZ)pL!-H2Oq$69YaUs>#Kx6xN!&RD|KQ#UmgMn~rGN9NRpl{aG>ea370t&+vwW2_ zg-O!UR*T2tGkM@L;F+w6?wL;=guaXZH#Aa}4LLgiesCWCPX{mB(X9h}98;Z~J;5pY z3u#70e{C-QOc$S}xKAKEJp?}HODtlx7A{I)7ICmc6PQJSUF;1V2CSb+ZJ~9fW;XoF z!x-RFK@H{qgw<~P!pYXn|+ zDsQ@jmflO%^}*%9qk2xdEmeoQ#A~^F<`qMN7Hqa}4ggl=jm99slW_0X+Ia=I{4c=4 zQv?;@;AtRQXDkwkfr>L&@96ES5Pqjrv%!4ZBxZ0(+%+0YJ=y9a+wUSF8GfMdL1iLGwTEE?@IJ{w#r^_??cqPy~Xa8jsE34xi{pfw>B8B zZiL%Jx*q=T@m~#XYI38W;EK-yHZKQX%?mts{^Z;C;nY@-eOFxUmsaW&|7YlJ323~%Fl&Ng^ ztz(4wrDLDUvoE}k1;IM4E@vMdJZPRx^DXO2u(PbQ<%L~{;<4UwM6MWVPk$l0xTj?J4_7$np13~GjgseIArlz*4$vV+4^))C67Hdd-N zw+n4A%$8JRJ46~0dj10LmY1q$>5N+aw24QY@8z$t(@9i6mlWQ^o#qvct7R7U`#muI zb7M>iR58X}8_KGj&E%f1s<|qUWKW|6%#AZE_IMfJYaTjox%cWbMyT!Uh@v01vJRlyYA(60&x zsH+oh9#FJo}{@CTr^K_ zI0+TJ0#*4|ysEyTFa%_v`~{ig7ZjcUv#KS*Z6IrFg5Ptg9Ixq{^m-&vW3qpd1hMRj zt|)s=KJPCNseL_U ztO-RibS4DJxZ)7XSzZd&Qb7&nw?m(7xi#Hy%m)jx+0GHqbv#KKdA~oT()sKjX>;%Q zAD5Kzo4|wfaCfeZN}Z(rWm492pmD{Fx05^d2pnsM6YO$*ofm!!R54f^_nFhH^nI?f zo5LEo!NXUvT(b|0xO#gnfmy`CUQb{a$>o*E$~@o=XvpDBF8ZK7FUsk^kVya`5Xqc?Mn6cE2f@pqug_m%H*&v?G8 z@FbqC-rATL<<=?h1aT?<-rET1FD;+e=s(KNWe{El=b`riBo^^L@$v@*O9eHQ|8ot< zenAj@2rNnZ1Co|M0uYS|I`}c+NZaMr_KHejvJo16s0?kG7$+U683(%T5jbIt!oO14 zT%~M^N*yXZ;q%&Q@iJMt`|<6Ky=S7^+PX_`A*ZJLpbDy>nxJnV;Os;qvoj@yd)+3K z+G462V=IE7mT3EY+slwcM#6b9pbnBq<*+x{yijGyp@Mo)GwV9`Zmg?4`{>P&(YlJ! z_*YYeK6}O(nr2L-paF9C5e+9}G#&uUL8=s6ud~_-m0{2*lramMh_-KfG2LqZd7;v# zeV>ei@tj~*aJ84L|1!3j8Ah_g?0JCN%^%RNKS3O`c1Hj76Pi=_W$SEyOw_-IVx@Mk zH68iT2D7`LT9Z6#E!>eiGOx8^7^HmYBwcyUvbj))5spjRjlL1{b4xXmjLVah`vBkC zQl3{Ra2fxCsLL20`LtJp@WQKv-jTslDwp_PZMbWM+dhxG0NgJP_bUspweUI%uP5{m z5OtQ1#=KgB9WP(Y;Kh7xqkOFw{u=tI$@$$0T>PeLTIWd3Lb=^udgXxQJvjL=-*X~7mB`E>+g?$kw7k^CgM-`V2W1IhpG}n#n^)&pF)sZsD&E@WbxK}Hg^i7sGFK$5L=5E8pm|zHQ zpHHLj5Gb&@+dqVCjh5@;MC7F^-Gssf(w-rQwcI&#=s2FRIg}lu;f^Zm*F1h?vNVSH5d9kf5|HQOy)c`V?lShIgc_<{l4s9bDrj$ZdWnqkt8dd^BCW(&#MHu+3iyRBL&2tQT%DebN2Zw2(izO!ZLK+X0nQr?6V8$Yr6KajVX5% zUjl{J4|m>3D45cnIWpJm+}m5@_6SqUN9@h>}jSb7yZj85zPVlh>o5Ewf>3(!OZnSd| zI!u4Bkhd>W=G8BvZjXRmD1%pn_}^FS<^wpwu0t=xpe0RIQyaX7@vpepnx~05zXhQt zeew%l;Zuc<9nw9}wugkV>Fb#=uO-5yV?gcWJ|Dq4P3|{D8jJR3lB_29H<^sDb*JYv z4j&0QZ}@<&4PNIY;9A?sQX8;Wy`g>a0$CV)8n)mvRtZwJOcW)}OYmm0m}VA(UAgMw zGl5ns^zK<%u0_1Cl%!#{sOXny>^A>Rd}0@6$_#?9oIn-(#CM5iK2dk6PrMIPluvvQ zV5ETf2a3P1II2$i>^mXECzc8qqUp(|KQYS9DFYC@@$X$sfGf0o9Ow4nK`bEpkOWL# z2>Z4Xjf)4Hu;a(pj%@Ew(5+F+-%)i`-vK=@%aEY=V+@J}0tJr>`H`6MQUsTQ&gQBm zt^OtJkjF?4%!922{BaIk;0(%`n(h55lbtR~!Q*n^8!t@3UJg7xV^S2jHLB7jRQ_(& zY)45PN{{h-%aXyNwl||PnFf{2z5Sq$-|hPzp>o{;;kNr$gv)CAwx}v7ypowgvJLkr z{PU^gU2S=*G%kZ9ECVaupm2VOZu-Z{z&3h|a9LeN2Jwz>mx0UpDx2|{f4SC#y~zfx@8+)emZ;pPSXhiQA)E;~@p-3t$Q#6waSPd&?Ez z!&F1Ib6kYW>MIHmZvf60;Oblfl)@FDJ*p$lB7(B90%*6XyY~i#bD8(1)$>uBxh<7D zQuYQ{Ks~{{<*$TS>*ac_r8C3u1&9G4~DYKjT0;0{WG3f|77xJ?kJKs za{W_QlDL1dCcU^~oT&BTEm60AyfcDsSk{wUm_-ney7l~xr44Yc{tVn|e=~q$FdGU7 z4rT%6D;KPef#md`A`UZA3C~-~grI_wG9*RmDZHB9@;pMOAUHY?eX-6xk4p<@)13~RsVaj%LtHgoV7D*PoD9a*zm6F>A2^rp-N?A0r!sj>FjIgYD+zDLIhn_joHbtQct)%vOi=4ilEz+i(*5E@apkoni7(C_ z{@Pr$<`m9SL~HgIfgafG1ZEKjYoWr7&LR%hn!qe#u*x-et?SEP^eYDIz_zb5jRrTd zXM&IBz$9K*&aJ08J{No8x+DrC+2B&MFx|6DlhFU7pug6#M>1!J{w#obdfoFi1VTr@ z9w}{)-_{7S8ugep7IuSzjQj5OiDrGb?$W;dT$rivK96gpAm3kaApYGvBhpT{X=dw6 zoSS3+f{c5+!BT0)*Ku;8i9S-ZAxX*`5d=3-zUWiL*g3YbE*<$q;UL-fjE^<}!^b>D z;Qxe1L^q%`!SlqUUM1_aWbTVse7Z!)7eHSAshUD})F%qQAi^c;SG>!p*;x8TgpJe2 zscbfIv**tfoj>2DRAwhU%JZ&ZQ)1C(f`$tF)Z}3;HJPw+bclOL5&R}Cp{3|=VUCk- zS6u)P+pb!%Uv)m((?921C3eoYx@Y6lbNbKC!%69$J+iHmWUHKGsF=wdLxpvIn1xoa z&|9e9)QRA+^1AMJE>8QCEN4owYJZt;w#v@TEt0i4-)xvrwHNzl)u;bc>StEr)~v== zYbVN8Mq3c`LWOYxMa_MtZGk;AJv6PNzb-=o1fg9ogvi>kMUV~HmI=%v26Jt1U$02w zW0^JZ5bhz_k+0^iG!{wPoYr^O27@ahtX$vSo;}Y1?R&&Ue-A5<`T@1Q)kn3x^&pJf z-W;M?+tXdz_UgBE<7O5Ksv-sXw&#JQ?KKw;riN!&-79f!FY1OMZX)w`uD#kVLK`&x zw0$UTHZ^e06gFpP3j-U0pbkXV`wVHZW`W#1dVL`=BV8ofsaEcmJ@V{u&PlS^b7YHiJ(d1<@GwE$enTGE!kK+QVV zT?y;i8SA)=Hs%>`iw+GaH=X_0a>MLDKlj&Lm-^@0qy*2kNw+q(My_7QYLoGNUfJcw zJQe!^jBnPitO9fGN|xl>m0_&5;$~tMx7h43wO=-1lb{%EMN5b~q6CNiZUW9`O2yzu zw#P5Q?TJ6MbVt&@?8{#6S!^;dTyvCt&x1I&Z=Ss6>|1x(xAfLt?xfi2TQ&t4%_ktf zx#F8CE`Jh_sY(!H>-B|YzAZH&HV`Af(*@qQ8XCJBwXCH>KLMLt%lyjr_Ip+rt z#BTz{Qxf{Gpag$YdDI+!E5mO+HeS#3b`a{{Bt-`r6QQNTS}LPu;T6kUV|&q#VpARM zq>KL{hjX&)*ng3e;U;?iYh3n@b_SK$0R4;-1l!1g!%y!Bsk8M>cVg0SBi>3*<0?4G zmSnHfNVhiWDrou4tMc==C4N|b4%Pgp^Pk6GPWE{IddTfg0&k0q_rQ1muWCuO zkFx17h&?oRs8iUJ>QJ4dQ9s>1L3NEr-TT#q;pEY%0nQG047)2)8Zdfr4J74b%ycNZ#!0cm{5 zlD)VRgMXHS^`**8H{`SUljNiFYHyQ%Y-uHl{{|{!_bO4oHDjTH-!kxV0NVXsto7&X zqLNRL@mXRVjRJ?>>oA9Qa0BjvG>FU0_bf`(vU1j%%Y5C0N4|18HYO#}Qi!8c~XE?)=0 zzp@hguZ{&*-}5ygM(smB5WCVAljG^gyus5&p$*BYO)1^i$4JLFOwIsYbT2?=@8ZoF z<&)2XDE7%`63u+F?$WXRz?N>Tb2h+8L4JI53`qR5d#8Cl^Uq3*f7TNY`RDokle15Z zZzvwYCFrv9?2{B6eFFk~wW!TkE0Xh{LXY;J2Fv`XW#p&bPo$F0(mN&JBogdmj_({r zcO@!+D!7$HdvkDiC66z_oMI(6hf)*lCQAP_nydLD?JZD0x4zK^kFc255ho}H4|5mn zPJ;d}NNDc@6ob!NzRIc?>>+j_)s{3SkU!l(yl7={zH3<8=5fKE(Dt=CPJ0ob>Ex{q z_SQ|ZXQ8^F%r>m#QRh-g7JH>Q&ac|?+eoyb^ibri++E{sCfOHZ@ zQxojR9knl+Px8w9a~G*JUikn;MHG+5vbJ+Jd+Eb-!NeN_zD3l(26gy*;{(bEk`TSu z7uG>g{ffasfciAlanaytS-)eU%^Ln?cs{qyGVh19H|0Ed7lVUI9~?qZUL@u)O8mq? z(*D0Kd3bW|wtx6~K`T$E_i!Z7r_fJ%yB*B@qc*o6r=5!zUH}6+&F#UJHrnG(A8dz%RQ)4QcMuKVv zba7N~t=S^4ZIg?E_BNTG!p8S{i47CT>ue|2Z}D9r%ZxR@>^%qip9_JyPR|K#`%Jy? z|3-l;`ybV%X2~0)JICHuCEQ1;Szfb4QNLefU#9lLi{KUPr&>)rLnCh&HSP6 z(lP3h+-M|q6xZlz0^*AmKgPw6Rs3^`%ckP89LGKLz=?(Jn0xnHyA#vgJa9UQr$~^j zISSqTF#x7_;>>quZ`2Z8Kpc=V;4KouU)tem+IR3f>UG#TR zS%8kn#Edsl2oy{f@&re%OLU^*okrNvG^~AmaWY;ZQ39KbvWem(mnJUjO)9J6uJrz! zG_d|Rw41XjUV1ng($^WZSsQrx==kWny7Jrv|*h?xHyrOy73Ea`&-^wTw~^YsZzd zPhIpKMz3frPD+KGbj{X?*34ZOzF+PO;*v49ybH%g@7xVAEgn;!Vf1Pp$ zTeQ+L?*v9$6onh1@%aYg!9pSbAf!vz&X!WaaVDHHAb6)GVrfaH6tjpETXVk7^M8Z1 zae9}gD@mh~Oe|Qs_H;ee6>Cp--<_^KDaRGs@BCc(SJHkZ!OGgN@yyzt8 z8phNTw_nqcYrn}F;-;hO`fgIkb^S-8SzYTct?SzmPkqO|0Hb>dh~KC9pA?s0i`&(W z5Hdz`-|pSa$}L8@G4}2d_a5lfYI(Fpe})3}i!VEBEfgsJh=D_gg0ye0Aupu5iS80* zMV{p->bs}Mz^9Xk4DTpoEGu;}CoNlF^tPS47_FVu&_Y)+qQrtm- z%SX~CmkMeqHxBVTJ2^s!0&>! z4ON^!LoQ?SaE`@2DGNb|6jaECKTzgH-=~TBEk*5P=iOFK)sZ#1`b9Z^&6}q>k@3Y# z4BF~R-p{(XeG=6RMG}0E zXLqxB$NEBD)4qmr(XQmrhGA&z^tlYjy2@ifbI+ojtkms4fR|4|O4Y6Gerv9qkFxuJ zLKNHmFwxBJb(h-xaal9|nn!Ybd-0{{?32N}L-f-2>w1{CodEfEzL1 z?30D6%r2;g=t*Lju)*7-e{t7)nn>AoNZdY~96cqPP~lL>W0>xM?f(sJn`yYr16)gE zhu=DqQ%93@I1ohASt(uiVloCJ>|W!r*d?QzX8X=xJ2i#n+No;O`9Q;%?6D6tMJ|>Zr*?Hg zF&Jck5Krl~UxfD{0XVQbDM0>%Hj)FMW$ih!r-h^J={Y2b?de&fnLX()wWsGPqU`Af zfYI{=d3$;iByFw3&LrK^^rghuQ#B&$ui4el_`k}3R|bx~yAxnnmIqsJp$=N<+Wk|I zHF}X-EXgho$Aqw^%*z@z(KEtzSVHri#=%H~AJRbE7W^MsxU|7C1Dqj14B@zJTBR5V zmw;?9@59nhod2jiI!EQP5)SR^u*uJdrYJJEJrxoB~AD$}a6m|U4fI!+9w zr_`2JS&Y#Qmf51IE@z9fgv{3PQ!fD+>->qA$AmKmdY{P*o(ms%{BM} zfpAH_nt#6wk^gfiX!qP{h3~Mox0SV}|A65ex>^WhFDEyx{^xKD<|YQ#C>D|fKU$-4 zoz`$ZfF}6mx{N@Vl2POVWn8}p>3el@0gXYR;EO_j46Qf#A)&uSU6#v%J*|R=!ur?2 zDdPLj+>3KqHC|Bxf-k{hJiO0`_fReH;4~mJ9bujo&mY8#?}h#fZvB6=vRK|$BDN-4 ze##Y6%cjP%kz~P*D$AGQ+|*q3zrwYDb1`V|;SEittB;BjZ}2^Cnv1+Dz`S;N9~Cte zqRAW7Wu)zU>|OKP;#9Eu8myX&4$c?QGzF^*t{}6HdF^T3^z6R{wEQ%|sVf%Fm8~wg z7PL#>znLZf2>?5nWrgH9C3Xa-BZgNOh}x=fSZh1OTGXm(pFf_9Q|;Fwo4K5Ac~%## zuIgM>YCh1vrp^oD6&4u)?#>_86Waw-3PQT0dsC9-~k%jLfeY(s7&RPxiB!GXXI=dVFAgMx7zyQ(Yt zXBz9YUGPp?6+-gL6!72{+`vq|r&c{0#@ML_n12Tv9Nn&JL6mQGh&ko)TNP@S)&WZW z$s)dSUh@|eQ~o}kgIgjyoy8dOnNyx1`i4CHtfC#FYa1Nql+Q-6rQej)Nn2*@caEg} z7D1;w?ldh=_uQFqWvzx_}+5?)CS)p-dIbIS8T{-wD2N zA@?zn-EUBd&!PN%=EU1wS*wJ9u`alX3r|vHcLoW*4`60|md&9I#8!Mcp~jVk2c4i@ z!72)_F`8G{rSRi--Ma{L$``|P=?@4}eQS(Aom}-RUjn9w=bk_08Z3)%F4fIt1QqMD z1Ew85=qs$WLt5t66}E}G=S0UY@HHzABtb+^^|K1<5rc(Z-izr>TL} zGa%uw2#UeAgt6ION7Od;#OiYVXdSU!N!vM)?&o?-{A)s**ujx*nXB{^*24NfZS6+5 z&ZIHiz)kQQf(HBMM@?`eao+8@iA!y8v#_dMI-XmI+t*oCqnPaRO`qkXm1-FX& zw+gCB((3OrpEmLBQ7Bce|I&JV^E_rS89@C(eRRormMvAI`RO>TU<YyaJ%HYgTQ^Ip}!$%SrwnucHiDnt?GGI z0o`@+SL@U-(Kq9uKftLN+)2ndX8U*(PWdkG{XY_DJQ&`si1tNW#L|cNBruCO*u4qN zBDs9jMpx%vKOT9|!h(wxNX^A8>VKA!tL^32I=kmix3g}yQ{8hiFuvFeXAl;HpTffn z*C6gM#2p2HBL8A=AEABD0%QBLByynOFT{ffskq>5?I-H}D;d=U-z3hl4aFw=NM&8U zF|7?Aht4k`-of#>VBm}FV&M(=7uy#pk$AJ8ozT4l!%G5~%X-}RX_KmpIs&$b0=A|dd^itbSQDZTycfOJ2COMHG?rgP z#-4Hybx7m!u&kiN#fyq}DLysm<`O&sWmDW)iZIXI`bidZ$lY=@cX1f^b%FTA$9klw z;M)Kl%A)?RBGOAw9NZzJibQJPzGhn~(fZ(CRN=H>%B>GfxziYoB%S9h)nzL@7}%); zsyGi0mnoLzhpGFB-a#{GT_PSZqs{fxFx*Px^OLb(m% z<|_XYBK=!Pa*eZ9sa~lnny2gFx`fRkRyvqH3bW=I z&R{c?aI}xd04MVH0%B}xVz;`Ign|W!(L@8&yUycct7!!=I8hp2P>FG6D}TVSuZ7}D z30d(=K#a$lnj79q5rTgp$4sxhoJ+Kc z#NPZ_OKtB5$jMQI@@Oq}Y^WyBJ`D%>`1&(g-t><@E)?ed%?h(IIKVQKLFq0y!K&UxB5 zQL69a1U3^=(wG_d0Gw9?u*(j8og6$N3C5S`)=foEavMiyP=%%O!M}h|bejgfr)kh+ z`~|ILf;iVY&sys|t1E~9J5~Er;@jBV=s!)w*?LE6>y6QTmBQJ2qvziyTc2cpNQjn3 zZ&25fw(Bcwz0rRLX3dQb-ooj3(i@+Ye-`jeuDrkB&@!QUf#%d|7ibV9Ugmgxk~ z|GhGGOIkv-Oz;ME9ckO3BGU=}W-x1>;NUG2OA}Y3s)?qmSO>Sm+P3ugK7OplRXwT^jRp5jJ8C44Gac+3yg4k3qL~`93(})i2BDjkMm#R znk~18mscC@sLLQ<9II*`=lOTYNY638La1+JgRr(qSR~IOe0|`*six^m5zlZ)&9fNuQCf77;)ig~GQ+}z| ze*>;IHmJnZ`)`7fE$F=z{bZ4UKv!A;%(XSuVbOof5qC2}W0O^3N3)xqK{1Y#Bi@_; zCM0}{|F39L-=IM+q%nKpt`M==-NM~1{H%q05C#WJ{ILy5%rT^WbQ#=*P=<7;ba}7w z6#_%jC1*%VHQJEs%)Qq&4G&pDiDJ#uc6zin4Ii0;JA&1)tpvVv3O8~$ju@nXV--rp zbR&q9C= zWIge}%Br4c<+3jLA7y2RYv(Xyx#o;DzjI<|_rVStuUXe$-86g;#^flPhHtg_iU#$+ zl&5QzqUola|;2D0Z$o&&%%L3rVQ7Vb|NY)ga2ssF#l%_(o8_3Q&+mCor|9@z%2Hj!V= z#aVBgS=fQLzmxR?2SXmu=dVRGM`3J-sUel;Yd(L3N$0{v#3LuP0~{)VlivXj6F@~_ zTOgs8Fz0^HGR>zLc8jK@BQ=|9`t%>P!qTrCrOV(Ugz8uBQok}}jb8}* z6k57#@dtYU zLyv%WrcE{%yr_#k^1Ksy3rho0r^VZ%4sD+gb;rmZb|F;m@+=5|G_uM&! z`?2m_tbHF#w#*M6g*o$sZK?V4HdLLC)IJVWbToliejfvmmM9tqguzKHGc@+fRhGAf zx~Dce1~5x)CxeI<6CbJhv@GBQ66y?iAoanA+yoyH)VVFmx98Wj>g*#3?&uRerW_zi z?{c$5rS|WkIKc>sZGGxvB5rW5cCSKcq*T^*@Nr;hp2InzcgLCD_C}tesnOrr7wHblUhPIF$sNuRyYoZgNjx%LO@F!(@f@La%W(2zC z+>K>gLEFbel{v%y6Ul~8OgPKbGCsrovF$g0RI!!h%5gbYot)l3V`(Hc z={!L;UeU9xQf*7!@Q*8b@FfIDel^RPUNto5ZZdByEhvR!qCFDgSNxf0=)>7D3eJLE2k!G5=iKlf|TMZ%%m! zEO+UNz|wwFed7l7zFargb+e)^kGJwXcvlvnXU!?^sx+tM(=-%$eljXu_uTHnPcV*M zt?fmS)Al4NZSOWN99|DBIA2{H%V;(S--K&62Ui-pxG(84I7Fx}?miiv?Wziepu5mz zq>VQP#@Ut!rJ{@Lg_Y*u2Dhxjy=|k#Qt-BJ!aI73p5KQ6Rn&?;7hcWP2_UmK0qUaD zAd3a?I0s4sXwx+B>w#c(OqY?i+me=M0(gE6teU+T9$Kzi+Z{R;>ZEUx5=UxwllQ2F zyl1J9{$I$UsXn%KnRU5DLKa?A(Ss@h>Vq0x>^QfejwqWP)`Xnz%H-U(6~feq+pZ&* zbb%{}sq*W=*x3#+Fxv^JyOa3mY)5ra^czGe-`Z|xKkR1p`(;pB{r(9aUiejDtEyk6 zp?=TMW$-J6s^9yiUUngoUI^+}mt6fS)e7~itm69p8c|yv<26V{-Q4VSMZc}8ZbW=l zbraXDs!f)<=o^s5vQX6zNS2eVEJ9G#x{S2_KB;Px#q%e?N>wd5J%EI%=VeMR!MT`s zMRiz4R$8iKA_>##$YD6P4UBuXnbe2AfTC}5e-TN6^%{_sg(0{89a+nmGYnz@iVIwA*d8xa+RV~D^!ZIiYw)tMB_@a zsH>E71ie6&(o7jtDJ@*HN-!dHYEXgUZ@%1Pg`Wdj^El3)2}W*(0JhQjg)5G?H7nhQ4kZ{=^!p+`}X> z;yp}?#*6r^Cbw(ORq6;VcdHo2ZyVy|Z2wdx#@`6It%=2bgr13O+l;Dk^d6-@1ACZ% z&aPDLMW1ImuAMg(HUQ6CLD`^{C$jrJ^z`i^Dk*43hgwb7S$Tiv+Od$P*PkCuW6JgOYkXm1%j-r&<|I zcp7m-xk(>zM#&WUv1$7Z`_obEPt~fOd3daos5AOv3US^cnAV9J^mi? z;3C#{Ri|^x2dn@6KVWH}9rwQniiCdnv%3DMIP?Q(lCDPdBQ=MstLZ|CJxhi3|AyXS zx|mmpNmFH^f3}W))X<`8miqPhEnK~4wLZW-%nmLkwHN--!e!!g6^@QiA2Jw!iw&W* z^OU}Q7PnvKiL2z!?{!GtiQN-k<;BRvmh0%pu;KeDKQf+|>1L$n7|FYw5xxJAUVzB+ zKu1auUT*YCRrIe!2<4T?to^1x0pnlm5Jx13uS8cUmEVDhM?WJk_bFoUqqeE|zVmxXIDCp(7I@`l%>7EIDr< z>?2IfEs@UBNjl~+j-T=W6wPeib3I?W0%LMmd4eg>wr~0=9`&cMDg;uf&I;1Jv5HLe zTNh>NuL{ub)I@VufY?)7UW>`g_U}mAbT2sjdF)>2z3f2xebRbi>%ZG|q?$JySfG01 zT@9<5hF)#&Y%d>&RC*Oa8jchY|ApeJkmS2W{)tef?GT&17G!ud^}%bHM2oq0dn%+$ zuLGFU(^d~tTb)ymg!^?Kr#7A0>u*5C_Ii`3Ff0CgSLp(T-Pg({nz+wb>(eHk6I7B} zXwK5}{-M}}w-Wky(Ilc9b>CrFd&{7LOvL6(hEAhA<=v39Je6Rmxl3Gr2ioq-5kSYP zvyv5Ct>ShcDyMwi*uCmTi<|qU@~3s9QdZQBiIyCh^@X@@ZhLRz+Xt{Bo=!Yr1QqYeCU!sCl!bb#O{z}!7p)Vy(W9i3&y<7=nS7J=}<&Q;#Z3@d|s1xi28bz*5^(ic)Ye-<-l{y)p!f8GB zwvcO7jVZjV0^XFusq7s6gcM#`cN0^1rO{6k{HTX3$@N7@AfyJDm>$u5BJ$xi@WOP=u%k(%>KF;T>n`G_lVv92HtXcGTu2;H?!P0c7sCo zCaz2N3Y!hj}%45u9a+vt>m}eWtJYHxe(MY}T9hJli5`K-^ zk5PMpVi)&SqIjs1LPNak1ixAE`d~5x`DmuZo%W$C?pK|11Pi)_T~km_FU~RE@^*N}fvVB`*u(CVap8ff&8A}y zwWNx%hw)Fwg{LF4#)Y!LNCEL_imPVR@y0n&&C1&8THxY#x;9bTaW`W(tt}m7`3p}w zeSe>=H6hgl7`4%)y`n`sKT=oh(cr?1PJi0%94?;!ZRxWxyt>_7;BK@i?D^kuH`1!- ze^)p1CAL1Wuu#2i(x!JVRDU*!?_a1sB#9qgs6NicyXUS|_&4=)a87R9J0sRd^4A8q z-|^o8qVxDu*>`fGnpDWvM)q8DjZJq<}HYtTzPi_cW;B*ma0}gA4x?m zRW(-!zlNqy`^!9@mDmK!$@!s^bG9&{DWbv@~3+6PXiZA>PC?2K17%$8Utv0`h= zKUxD$+^$62CKK1OV&|*4n7F@i28FnSwes|0w0O<+!3~n^7LzRYReUBB$=-L8#lA`~ z=d0Egmebz_FkfY=o1Dn^|{z zhoo&b^>y+_6~4~oOMKnAlv%z`wihWNzK-HDpw!o01Qm1GDRekXrxK*|+YO3m-pxu~ z@H;F4cayHmEV7NQyuWq;i$m<-hFSVP+MM!buv@wju$0e?m{dF_7pK+hT-w!&<=6G7 zmpSwo#9s4d(XA!{pwnDW*F}`Me5;kC80{#r2Nn9$pksyUw_wWCiTjVZwJ=*_Dh8`t zIf}uI__~I!8x&3O^51y$E8dxZUjWP4zb};J^6yoJy`VYNbS}IbhsjoGRrVK;~z`~K9+}n_>Fs#17^RGqR<(~g5U3iC%^%9TMN~%Ad_~|)?)pt((bW7n%m>!nP ze`}t@8@3U|j{Gt1Gr8cKtpMSc(#%sVwe=k-u)BYq`$ih3kRATtQIr1?plBQJZ3Nd; zU*|?|JbCiMtpSJUFrcYxt_ohGifj{HJ^NHUwM z-j1`jVv}o6d#QR^w5>=(<#u-8kSJXq4ivnrf!~_cPPCoq@U6yjV-bb|1wA0m7qiWl zd;b9mzlDf9jFt}MYod;m#;MHD-QqNkD-fsN87_BmaSS>W2Igkt;y`U8y8^vSf%>P} z2uQW&iaFADWSxDXSEWil%r&aj(d%HBQxW>x}D zi#1rnKd9V!|6uote>gm~mDE4Tnk)Q+8A{?Gu0~?{2i19`fcRF5%T`kVa066nTjsol z?LiiUHJwvP)@rxs%m5(l7X}+6Qye?3f%-G4$zpjeHO0)Ac;U|A!eusu-pN>>7yFw< zG46*sT(XXy;4THnGO-c{7mP|b7EWgpst?gGW-l_wp=d1Z)KIrR@~1_@Yp43?U@AE2H9catB=4TfHwZ}-M-bV z(XXwjV6!rq@z~cC>loTvUEFi544(Pu<56?$qs!8`TSL`%TePSD2(x|B@O(?#L~y^_ zDrC#2mNYy8g_+< z5epCvh@vA>X>7UI6)Ts*9z^5{;2EQ2fIc5qZhYR<+fKaRhU4=2Vu6FV9%AEMG=cxkuFV$G2fcS}utEyA~^(s_p``iheJX{@F7((Mes!>DY zr}SK_2gMwP9OZu-?_v}|#+lM$I8yVr>O6a(1#SMB)`79pP!eb1&bSl)4(~8tI0Ti$ zuMD1~({r#dh`t;Wk5DK8dt@{$;h$X3 z7`obA;cVJ$xDG)vSeKAF5n=EmO(gT%ryA|VaU<>bCqqPL7#ZE1G8>2*mab2b%GfXR zzb53f3fuEWFeR$8Flb;4jQouI5<`}I0i44w03>1xSzkVi z{b4+?H@FJKlWGeuvQW|ohe>*E@3O6on@IdE$isFOn)qyGyjbK;e*E)4B>%yp35C#B zMqP88O{@S_ZiiP4&pn$ruEuTjv-u2@Je$|^M*TFlIuw;H<}c7pnkCQXrPB1-eD~be z!YQJ$I{g7C>hySssnhql@H8Bl7cR5#QVTD$@Nx@(WZ{o3{0U)jf>bkvMzlxb3)0Ny zVxx)`cYOX8x(r@Is67(Pr5Kl!5bTlAWu)ziq+*o{R_qe9JraVm*y1FS4E~u`v6+}G z1UT7Do>P;m(&0l^Qznl#!kIkv(NAF#%fsPA!;)vQm0So8AJQdPPbN>%)f0ygxt(|> z$XR5S+tXs~t(>Fxv}Bm1$|~OTT1b894CGv$Nn5!(Yqn$9j}`!I6oP8eC08w$ePy-e>cV(hSw`1Ih8eRi^d_1XT zzF83cEnuF36UMGwkN@2lZXd#cJI+FL<9gbiwoK+_K1Xgfl=pOnmCl^VK?~hda4KHJ ze0C_$w1v!Bp1@?rXWuSzCp13$NeO)-IdH@;2>EPXN7^1we3MCC)YMVYZHt>D!t-S!OjmS!{Nq()qiqpUMh%%_k|`?BqilRd^S$6`Ri=Q)s@8+?_)6ZRDO5+H`Eza_&u`vvW}8cH$aU z1+VXirmsJ7&)LNm{@FrNc%ky=h#m{YpRcbPd{CP<6v_0qcA5;?c0BJhSg}Uc%R@kkkP&m%) z^RLk0|FsUfi2p16AHaAoqJy{)pjV!ue&8{vIBwX&hY2G|~xyIH^O)+rPK&9qHpDcd+5(~kw zLYG{>WC|&|X$7M_O)GRpLHruS(a2q@kWnt(IrAOCaptnJ%gW$?M=j=3n`n@uM#f0i&WzNJlg57zu6@^=bkgOCGw@Q@ zHC{(v!W^hgnD*XKSsI;=Om9on>~nrTIOz z7PGsgo&0Zxq8HFtZYbwlmY>{E>PmJ0W!RF6TRCj8_GT@Aq-Kp32NK!nfnIc#BZrmn zm)3G^lv9E<=?(r*cIlexQ$}Ih<#XelFl67}EU$?$Ee)lH=6auRGB!kVcCJB}8b)e* zRuom|{`B>kVTmP&{m513n}LBFLdR(gvnQ*0WVP;T`918-^&{;MYsJLbxA{b8$c?4O zzMLU5Rdhx91ct1)^jBfXj_dzm$n0Y_LvC`0tizlf#K{!!qL%g-JNZP}GQka%CaSYD zNvtWACRPrWa+nEmcbA^$I+0ee7608a)z!#QKca=}@Ns$^G#}^RCSd&28bKHR(scg0 zv6VWEw11paUYm+t`aH07-H0_NRivI%$}RH@G4s(Z#c6Ha;CimGMx0knat!o#vQ>O9 zmuV*}Cf`YcS4S_fTc&H}%XsNN%`3^fh3|Z*PuI_7R}93g7%U=W7vULtv(K$XCOmJ? zcvi@t@Z_50Wq6(+oQL0Q9dXYJ6z7@3i6^o$Z7}fP0%l*s8=c3DqU$D9h8e}+P>B|o zM`EfxcI}>fMu8Q^LSSP9Z*n>|(1X^?ai94=uCNLU*A175aiFe+HH51g5Qr}Wg4J{x zED~x!@Pa(_CgjaFEkFzibjb|}lxmCt!34laqRY?=*hTW_KsGW`GfRHpdb;qQr9%2I zl1tR$lGJkP*H&~dtRre;2G(MGvDrd<)FdJsQ@^CmO>ClTSlykPq z`95?#ONI3JV3OxJyRw<*tnOWm%+1y2FzEuF&~A|+*bI^Twg=DgkJc{SW*aM)BLSgu zd7;ES;qN>a1tLdn`}r%mZ4Ti~ax#B;*6t#45-16l|fgI|~?kd4<&LW)&c$Re-Y|?PZ8Mkjo3j&9)G6$e!MSW?-=~ zqaq!$CjySRQ!%D~y+-4Og0|ETKO~o0 z!?z~zxP>|R-beVaNh>}F7aOa)5l#2&c;>hXy|O~d=BQ*^KImC0r2jH{u{P47v;3^i z#>1qP+%$31UTz?q=^TRJ$$Cf2cyuTCFfil&swKf)vd>8-ynINze;KZ;GhRa}jbGN& z;e|pk3*OsEYv2vU%Qvd6(T(b6=+zYjT~C7k5pDM@71HOS+>22G zFCfWf_#08);O7n?EAVM)vxA>FfZCIz_^AV^VOM1UGqMNE%g4(8i+tPA8cv$LM1D;+EBxpsu#LG z(0af9G-}vRC1`7I^S4h+pi#XmL0dEf4si+EJpT=qU=u5W5G`%qpspirPgazm&Hs!_ z(B|MR?WJ~m2hM4_)E?{rmUd^N+Qe*Yp7&P12rFDXF*7nc1)G@ZhC!eTh9*>V(PYJ4 z8#hORkI}@~fvp=2RgM0yu`EZNAgx=@dGtF{3UlFhx>w3cKH9&%lEV2T$IibVoO7QQ z;Ohj==jh*(GD*PJxPG64Lp2IJb*D1n%?U;MREBCTxS!OsB^f&(?h2;9CD;*NC)t<@ z@1}$MfU(_DF{ok-EMR7n3#>bo2&uTD3PYWIIc@^Q6{x;fmI~u9>$qHT3!lg0G+TY(l!B zh2yD0R}A_BDPU&`+0xbAy6>y1YSMN80{X zVfJ1At}tuva`2YPRG#!unw;ogy@(fDsY40pFF};Zb&m>z-6V`{;iH`VGKTC212R#z9Tk+R~eO|0gPOm68ebq74b}akBUi0T>L>VL zxsSN+!c8*o68u001nLn_=c@6Beu6)WFWQuAJMKRZuVPg3C*b#UP3Cpn7VaYbk-+9K zh9@cCnK4|0mXm=E@s}n9=UK7l3F$mg&x&t_`x2X{tO<%W z_Cq0N1@9d$n8!?jxv|FD+&Z8au0?!R^Aqu7elkm!!5%`*PyQ`Kl}_2L3yArNF1h)M zQjMCQ485^JYuZ4q$xTRvv z5!HsGRsMJ7xT@_SD_>I=;%2pWrkzP?isMYXyA&dj>u8g8@9o(oEx66MY|O@9l`&vB`gKRsC%A>|p0PaMr5wp#9Cpf#h9&gd`^Rhg z%-QEq1gB~GobFHId#Y|}cztm3_f*-Bxq)~E#~LZ?@9lPe>hWf~8-l`iFL1WIgW1=o zfVn3&w^^-j%snS96WS8&_>Qxd*qo?#8J1(jS~%s5|1*rd7s7gssn3%26Xq67bw*XT zV5+?kl0XMuZK~_wMua->>OV3ncW%BAdg7>SZoyRPU}g)ZF}$U^<1DR5*tX)vx|W*Z zdyuHiNnm;tUj?ucwboT7q!Zo?UHRr2L_(Lq&$l}hKHbOBQrBGPHgasCf@&$<311g& zCUPfyo#($N;SV!q2*LbnP}kh!ClkKTJsqrbaHKU8wu7RKukqucsEVh9IZREmV2b*4CdFE_7 z$yaMtz3|kJ?aGos3|#ImODZA7xn(Scb|9&8sQfN@+8!2)m1_^(bIXO<`0oL;@hw5{ zj0w34*Ps|3U9lZK#6b;YQr;6!6YU6v7rJn3;*oT~{&T_=AC!vBH0MwYnNKD=3bsuX zCH#^UqMX7gXIIUC3vM2ichJtrfiFM)H#TnU@sOe;>Px#DVSMg#HKI zJ0UHL@oev9+e0e9t+~a^@+hk?aMn`_;3 z!?bNXNou1#MD8T1_52Y@@*pqUa&|HZe954$xsKQ*sdY~dYaQI)%8GWje4UjkKJ@Pu zYV=cC_bjAaFH}>Ct1XL;)a)hNOPDzNA0v}}^U9g*!0z>hCA^>YJ0|@z{rRkZ8!spCsnm)YeBlpjn8)F*4b#ge<_9Rir0d2*|8|3 zl_91?B6cf7wl+%NlC}<8kZfs87GE+YWGgVG#vop25uaq8#Tsn3&cYekAS>2d%pK&{ zTC4`*wHB2;UTaYk%dNFEH8?RQk}P4^T8j`YS`F&z)>P~f zr5SdUTWe`@@E*6gV(+T{1-0cgQL(o))sKHG7Q@#cLJ8bIk!mY(qt@VS3M`Bj%clEl z#AsD$wh){0TISynCrLr^@JIYg6Z~sIr|dN~7yGOgj@0ZU>(knzZA~igk1KWGoJbkE z$}UgVX143FqgXuIeYWbGQd{=RVkOA-hu&VHiEpet-BP)bHlKA;l_n0ovjX`hB6kCJ zAZE@?V zCEhZSKqEE#rtIXOTG~p@fj!G;XWYrNjCS`dgXJ1|%u+}8I2V*Xd{|!bu(mz_@L{C} zL`f$2$D^K>&Md#qTz*}p&Qg~dn_IC@GJAaGnU&fS1!{{)`2ZKrHRYb-E>M5EH?1mn~J}=vf?Y6y(vY@5|dbu%=T}@RnUKu z)&@1VPrh(dSqm%%nk#ROcdz!z43_E$%>OCRkNW1N#;d)bS7t?hh;Ykm#qF7D+8-W&y=#CYp` z;Fobt_;6b|lHvCej2i9(PBdMsN#OSb&#%=a@F#(5F(SWK^CrXi_xR`LI~p~vFyFa{ z!|zo~;XZ`nzFBm?EV@6@;9x2xSggnF>Abpo?wW<~BWT&?69+)WocTlqVZG-yje5$5 z(mJ9=#>s^T5szFh4sfslmRD75Bq_Y|B1I2V)C&*cVo#5v zqlhu&I)gO5_NED^stc)yNzvh=E*~OM4plUCq4JMZYB&iJ9c@U3rV_?RN*Q>VZEz^r zY7gCET(cJzRAiQ<7V9#2Afc9|iZb)BV=rDP1WQu7jI_OwSh-Tc>IhirP)EU8F;Kpo z#Yjj4ccMrt6QyrU3g)C7saYhC^DG|60bIqURphe}IGZPHrXniuu)9qC0?YgTow;S6ZNPs&``o#A&dixJXU?3NInywiZLuQCS_6YC0Db%|!S4Qwb{E0gv@41hIQYyeGoK+2TA$nj#b|RsaX`w>%u0`)=ti#_rF3eN%*x2XTexcxGvl zj1D+l&cMMur2}du$!`8c0Ca#HZ#1k}!_3kydqcJRseW7UdmDNbP4zY|ZR=5?)Q94u z>sdY^Q8kWORvj;Fj2k^bJlC^KZ5xY7%>}^XAQo^4yFD#)-vX2E$dYBd*pa1rYMEo*R=Dxn12{{{8d5$M%A{jY5M$W*IJSAuKDhgHSAQk{Q zljF&mVvUe9(aj;VpD1VZDC5w{S)SFj5{u?BgzacRj$;U$_`sH3ds+_15jM+VLvpMF zyBs2H4TZ1?fUwE&gw1j|j<6Y4LpNO5JV+{RqyG*! znK}+ox@YSe5J`D$hQM(!I)G?fDw&B%_zT{$A>WCBoH_A+q6G0t3M$#P5mWV)f`zTJ zAqufD)S?B11;Vh*vjaZ00`L|mY9m-)$&+L(M-ksPUwLD(cxe`T^E0BgGjZm)Nukcl@H<${v1EJE7?hTtsTr0a= zF+DN{u9l{3WMzABEFVUHOLMhy_aNEGs=B^?hzymh`xBB9&_6tiGKE2pz8Z zi`vfaZo2=w+UvUKiCxaTx-B>SCO;q`r{?o?YqBPhEnCN=4!td7$~VObCrb7_IRoG0 zDJ45uW#BG+5dbAC2Njg{N)-!Kwup%iz5KN)<{Z2c`m=G&$dH`RoBB0$#Y#PwiON`B zW~-sguJD{Fb}ml15zJ0+ZO+#$sS*}zmef2Oxzy+jh?3Gvjb2*xGGqn40IJb)y){cl zaE)8Dl=|BS4^1Ppvz@@~K->=AQPYe<8zXErV$G6Z=@`L+Wn%<=I6R1k7L0bcWinz{ zTpHcRU+%m)%x)2aLT+=>R@zY@jkBsr-T=>#W{Qk<1J^Mr) z*+IBC_x-ll+q@+A^?L!rU^^5h*7Y7s+H9=HhdEav31UTJmp#vD0NJp^1#~n*v147c z#-bm|`>~vXi+E~pZHwfg8^#2&0PL-m<86&rEKv1mmnugiXvccbgV-!wwiYezrmxzvg*LtSzgPs@Ts%R;A|&5(Gh7Yo#g#iM8#ah&#|Mb zDB6^3o%2v#6w>Eo({+JK%%`Rcc1Z-HZ;7AR@y0aFPIjajYj#S#h-bvF=Y2$;pb8Lu zTS2ejjdKv}srTqed^!3+ueK#&{`ZX6;&PTX1D*l1gV;*S7rSFeF9X^8#?I?c8Mb^D zu{(&Yq%>+PMf&O}GI7|>d?kfun{-toJWsU6AJs-2KtmXDU%eFiyq%6*i?2S*o<<;P8Y z^Us#JZGEx1_izy5Hg)aHS+O*C@5v+7f>R_yU3pS~eVn}s8crs2)P z2O|iWuW-{F#-+f9DxZM@SBImO)qed1262CRhXW#Bmu1 zKGAm(spOh0>ennUPVHI*U&|h^ESc4`fsKu3HQmVa)R^U|r6KvLl5wi6f!m{`$~HB{ zDF6eN0lA*p)Kpo6nN7Rd8EN*^B|C$b8K$r1)I?t+?R3F}hz%b_74*7YD5iS_cR31T z1xVZRIP8>YO&pXa0DXKBf%bB`kY!Y>jU>Yh)FEZ#p7p;A$R7T-Trc zL%ww%XGGsj49WiJACxrL^AbO=->Uu`Vr*C%v65Hq(*W#$bb)*1ZB{)|8!#CY3V9!eHt1Cv8cDBCey6; zlXWlG;VpdY&}#9l1=kVo(ALjD*i*yw$Q^56Ys;UPrgLtVwXnm1z8FJL4I+zCR(^#-7T-Zqv@Tqe!=c=l*ZE|JXl$GtLVjqi4 zjD5JDntd9ljKjYBjpon5zR9i*hMh3qnBjk`Z_klTa5Uuup(Q)EYO1mX=)@&`q=@d< z3qd(jOJFJXj_@ijEvVF>4U?baDOKruE&59Fz6M@XebI>shC6$40$ZJ{ z&t5ccF@$?o5gofG1mSXG*O4M17JyOSfLzZ)V*HxmEF=@0fAtVcl%#b?uc=09GkVF3 z%F2m#(r{qzv z&*g{1#_$f`QN1~toVRL4>?yoB%Zbc7E-TH+^NR0k3oBw*P*!xyNe~O56|r)M+W(rh z_?DIC@H-%z!ire=Yl*4qb@-uH@yZqdl$pubc2#fyag+md*^{RK~0;GkMBnuPdWXk1k`AlNXer%h==~oUV*j z&{sh$K(QOJl08%_)Vp#N@oq z$!=R>rPgLlv8!NA6NHbGtzt&UbfkKD)r&>^#>8tza^pg4wPuv*$+2Wq&A5j#|1)St zwbicwU|5a8^dG8jj4dE;(6wLyz7tRDQcl7BDj(U{O8!^!cntU8VSR^>_lm_H+*|J${vB!tJ`0a<~GY5~a?0g+l3O2Si#rJ75|60YuvDfM#iIav<+Miy3r$|0HMN4W61Xo1vPr z0sIMK0cfk`cvAz41uB~wV8TpO1G=5-k9doxiKou zs6b<_n>2En%lNZ`J9?SRm??TsH^vH}xeU2p=fVhf>n6Lig_G41FEUbzH@dTiN*~-5 zV=8@uhJ;3fCCz%8vruaJkg$N%h2H}(YXMzP#ICd_tkl}V$$a=SjS9B=6W7Jx)5R)= za`=5=kezEvDJFhmvilAoWOpYb!smB!g7{thg7~lY{F^=h&NG^lu~ACH?5KI63k&M; zJvjq^;VJdFl9IE35P&YU9B%=VVu8xeS!D^O9Vi`JcK-#Wt8XT8>3SC28mc|o`q9`myIN}!g~Af5 zQ=v?Q!-cO~a+W3QtS&#Ivnp7s11wmk1N1=?Q0M^5lVxVO4k#eQbO0vx(tG?)q%4N( zfa~zPnV+Wv#3)Y(EMK}F_)#V^n3g0n6Y&V*fRB-dU(S7fb9N2BX?I1@qSGnSzCPoM zB6No9XIOdx130W7fqEMPirX_sGQcKt#Vq+QN7^PQ;qznwp*XZ2r&v8 zsvCd;kQP2M{PmPaz_CGop59PCymD?Xy+fSER?c;RDCYq@D(6xM5%&hXYGJntk~c&vTZu;eAKsK5I9AO>_W00h7cWH#9Ml5 z84A)KIsXDc>K3r8{e@Ivx++NI!Gh}G$LfO*Fjp=d+rt+ME*>9GL*jZl%kl9b} zQ2TqCAw%)7AzlJ94oEcr!J!d-)eS}Ob|0?Y4bgS=|Zo6%rtGks$M(M~2L zDjuXZl?duh)^{S%ru?qr*TygVQ`5E$<+cMqPj4!3BjiSBtZLk4xKO2spD5undvMoN1yFjlbIPHL1r|hqfhpV5LHft zCCRD)I6TLmEAdQ(o;#L#M&qo(6h3-qv*ZlS?*}XUH?!6%Is~QK(X~mrGKtYpDL0C0?J*R$!Mnx?qkHpKFN=fG&_T z)c)^GPAu^t{E_4Y9V`FlhQW_VNXmRk$`yp_S}b5V7sQy>#UjW1$u?PN9MUPXehwPK z)d?M)O9*D}6lJLPbm6njr|oFNK@{t-gH{(Pz*Ux@fP-_ECRJ80&K_K9MA(fK_G$|& zV7LasLNqKV40%nzF~~SW(_baV9;C*Jv9J4O3i&fXPgYg7qsXe$ZO5g$ZHDQykRyv5 zlNIf~J(-SM8|%yIwl$#?-6oHazG<%8GJQF1USH`O*oTfcd7VeH$gAsfeveEiYvaRl zKs;Sn;`Q3%zz?>kjMBkiYf9?-=7Lgo&PnAOn3t?8WR+|6+N;-C*gLuU#~p?GdA@$6 zkMPF|vO1qszbhqNmFwiQ4nB4^&qRBoKSh{rb8POJOYL&%QVP^KLo(~>f= zvZanWhM(qX=%zIt0)GQOV7?m6b1t%VFf_50g)Uahh{;dtnKyb%F zb2=7gdF3X&V=FawNUP5Ko7b(RY_NHMXU~U7$Q7 zu27vg1(tJAzraiQ0==!GC#|9`mJZt2u-U5U9*jqDL&-;Xh}`C$jZ}oq;hp-L#H_&I znnAoJ!p>O~P5sZXdL0IzFO+oL!H_0gVt{ zSqqJOBt%HUoe2Jnm~5@Lsc@4ElLt)M_W;QETY8X*;k`iAx616<_wYWS-$(qi_8T!O zsV{sGuExUZ2{78txEo<}cXy`JIa2is-Rc-igM)8;@(9(pvmH-72{)hxa*R zrp@avy$kXaO#iJ92>o{kk!i0rCgZ>={G9HadrNU{)BN1mA6YNReLdZ07=m~sVA0yD zUH6l;%o`8j7KUl1P20Y)oPiB^s%>9KbxY?Ta)~ZpwC!?++J~~LrC6YP5L7a6$e(SI z(uGdiu0`64IT7Wh5QeIWkpa4orIVqHKZTNbD>Ux z<)R_kM1fr{7}EDB7pG7rTnh|WXlS`8JEI{(dRtFiWA|HkPp#aBb)0q38KRb~&>MYjh}Uv+gjav~Vwo>wwta5o5k_oqqH^luKR@JxFa)59vqi zubz@Q6;~4=lW?>B&FV+rg6!SR&#OO@&Ak51t(=E;UA!rN`TYhQgP=PM=0M^s>|AT% zmZQ(}w3{;Q-r(_InzPg0!^h!fcL(U5^(vD~Z$s0GR#&$sN$Tp6cyQzn9~@<2&u#6w z9nZndcx1=xi^QSY^VJrd3TwI+3&@dNJc|iYRZCmil^aBr4ApXbIRo49RJFuzl-;N& z0M$~CHyf&0pt6NsR7)-FYAUP*hSQKWgdb|=L)uTnodQrFegvQx|K4~pB3e>Hl+Z`YJKq_9&vEU#X+8aYM>His&n z7m$XO=h^sj8F2rG-~0TA+vPO**TK)L7s{kpE~|3yY^ijzmCK%h(*3V8HD*8Tta}47 zekb4LjiWhuI?a7`mFC_@;fmSpXak@`|As}{H;>v;IaVtN_rs%9Ya3zqBE97y^j3aB zEWqH(P2f61YipcGve4oy2}e-)F4&`o5fRmgM{tW~N?D;k93W?4FP^Fo>#AHgwPs2H z>Vur2cJ63)cNHlXsD2JAHB<7Z_pWs7qq9Uwn@2>kWR(G+C~kmEoWRfNTl|+swXo>|Y6ErG`1Tef;>jPF` z;NCQNbpstPM}ey$wwBwiQx#dx^ZObKXrX6wbTn~WYizJUN8%WLv8+2fo;nilf@8q!)Qih)w`(E5rrSM+&KN%Y<*OS|(iM7O-Wj0E|6GM}31WG_CvwA=1V#5!m0c z+=N5y1P%>b#eJmp7x30j^~-!1$UcQ18o}|}O5pP%_n;96@d%cRn*}q4PfUF{dG;tGsO-nSGO7{gc!2NG4tlIhL*A#I=XFf&XKt&5+iqx8b1te{EQHxp~ zmT;8Zfg^b8u!IdI-rNNtl$f$ix{+&Y4unkP>Rb9^2*dD1*VMGpT;NPqt8tJvBVs5Nj~UQU zr*_%tp$LUlsg;DGA!JzyYyF9|oZ>cyPuHYdCpw#!oI?;C4HQXB(mCflRTZPoRgAiP zd5m$mEpa7fC1*1I`4KWLSSr&NER$({bTm0>o+yQC#bE4jnOHs9R6y$8Y6HgHsVr-5 z`Y5RW1@3TJ))_Jrkjde)thA<@-s!CK@_x5yD2Kli6drMy8Kpk!oU&7x zI%|>ROccaCLX<`0wD3HI(cFN}a?(1X(~L(a5e^<5tVorg$c#w)15e;UYHK*MtFW_@ znbh}(ua$F)>I)E(oNm!vWCe58Uuh?z1tlSrPJ^7u8ycjaM0@_Cl!I7aPJ^7G(6LYi zvEHLCIr_QpUdxq(ohsjP+C1fn^7^iv0gBYh>kBHcZmXyQP+oGpHczpJwRs%8foFUo zkFLHIp`hFOxaUyqPc?-5rObIvi2~J!%P_0d>(+BPhFA_>ghAaj29A8os#Up~5tI9Z zrB%*?WmQfeK`fMmTkxnJNnR{6ULuXI#R6JO9d4XSd9$miHuf;i7)uy?REb>yX>ekf z_j?A&cN2U&iQn-0ILds1_?^quY@kx!Eo5LdCjHUz<-AE?L5Hn?aP+E3;2lsgR%;L;ozk8oykovAG>cer9byd8zPqsshduoX*NY@^_!wvF^ZwIQYd`{%PLA=5gXJi_E!&dnffUHRJy7kAu z<#kIL%gbciye8`heb0D#3m|x@1qwlY8y{>rZ4;x(r=I~9ZP@)>22{NGe8!Q zhVYL7@_x{n=jOUrgq6cT395e0w9;F&QQx;b-vx*~)7PY5^CwcR{Fz|My?C>il4o?d za=#sqz9!3(Koi}t!4FZaJiMO(^jd7?VS{xfT6dzX@Lq51KoE4X~I`jN0=KML1BZ5TdD>V!$#R{mdhYEMp&2T?Gd90 z!O|{`1*4U=EA#Z>XiFC@+Qfme!}!3&!CxUN7l;B<7yb=^+TUy}@d5AK^ZN=c^T!gJ z&t4TjnNWuJU9?v57JlAXLhLVxe|N~I&0D&(5Ad^1o*o2*JZ%X=CZ(*?8b53~c!cM8 zRY@_aN_1i<;D>msDs8C(c0wcoRY{Ill@x1ORT{h&ziRdKfYi_bgWj&i0)|_GmKx^> z`#C-c;G;M}Y*d~kkzYu&tzOjuxURx$w#=w(Na!DBR3e63i^{#pGP~~pAck^=+F#_P zJ-b!Gs4RyEfl9Yy$-k;QWmqlKbf?}^X|@X+{RIzF6aG^i_t&A!hVN|t_sd3{D~JDb zWs~)t`q)qW1c0nz(K>XUF?NoX*^@vhGmUL1vz71xC&TrDzM6 zF{z2N>}dUl%M#^{B9!k??b9l3<}Mq-_S&7_XjdW%>dj%2XS0C{v9;f|#LCT99vfr)9jH`})g^6*k(P zdNBB7-l}WUgGFO5VE9#d5^l=_1J-sp)=+d-zDMk)B^{8&tu3z-FsK{6ZUnN2gzVo| zH3ba61_IX*{RhwF6~&81WAq^>tffo+^j$fd@8~1Dx;KY+M5mjFk-rB7s0JT`CJkbT zNdSj0JXN*)eG)N)*?)N{*3F20GMLdRqwrtkDTCSe(6(eJvcQp7-qbUzJQr;98NR9ZFhP`%^ZkyV?}NzBM%;$a7o^`Ob50JK*Fa=kvT zkyY!)Doi{q%b9rC60A&OQ8suzkLp5xSmp8o`RH0KV7McR?Cy%uBBo81UxN4TT4($xMiO|M!-=^S+=c&h3C6|Nex35dYbp@9+%I zO!=3EIh@T&ifbbgHsJ6c)4!;R-gW`AaMR{ZFU_FL&NF_~Nf5tP5JBZNWGeX=K0)lB zG|4rIkYM!rkDQJKRiNJEp~g$pV@xZU z(;|oVh+0qK{e!34qn%ZFhg!83fc8j^S9pp=dt`-2d!)kC0TUv1iPq+6yPU|k6)!1K zdqPR?YDF&e&GAplx1SB+sH`EXKcX~)SZI2$vS`X7$IQvcu0E;Sj%c~vOKE61MdcMZ zt3{M9fkcJVQe$c~QK6+YNuO&?`m3tj8jU&Cy$kub6H`|&$QA0gmYSlN9U&O>4**g* zj7KzN6e|Z+Yg&xQ#$0N9m5h3j+FV98cdtL4ocB|HS}!7|YV8D$>487;_BVc>9#G-s za;*N$?+AOo$sDR&GN%q%+r*xeaMf<(R}qRm?Qeco9o4!zXCGew3O27k@H4LMB6aqm@&wZrwlTb1%q6k-Mj~P#7-bj z`5m!yEB^%3;#%c1)7>3qE*>G>>|4BD)n%Ez7ArCfXNH=P@lm*Ce@5T-&5Jc07PotvSsLo|`MovrhJsXV#E$-RK@4)rEGraefqXR(J zJ5%N4@GbyvI@>VOXwL7xVjoBr@7grp^26du7qJD);+=%XBT8v*rMxM;YsNE=zi%2} zRuV5=V+i&;B{^%nFyq0p_!+I^=dpFnJR$>aMMjEp&hLfo02+M_QVU*ZY3feb`Q%N1 zX;x)I2;!+lawwHXQyXXt-=7NF=y(!-Rb}kUWh?f~Jq;@>V4YwsBPzr&$2JqJ-VF&e z9$e+do-QLs(!^dbp)tYQ93u|}LCHBCROG3#^rh;wrxm%8xLOmQCOaH7nV4QlyK8Gl zNn=uJzv?uW8!dl2k|RxMRQO83xxU@Yp9Qo0Z9)DzhH7#Os`=ZT@0T+C*&VGgk z+l#%6L<};#UMgeNc$-RFruhou=>^+pNg8`Ga)D^LuPYx(GbKd5jcE=ZjGcw`P@EhOp=uW z(S~;6m~M>aLA{U7=cYVdw+pw5oC!%NXHreFs<{)AS-6rv;whlwE2LZnm8+m~EohPc zY`jT-H9;-DJQU7+G``WFcEfjd;i+{Vi}aEU_hkR zNZluwVj)dlJl54LXyy0L`SxSbaUY>-)wqi4gx&SykV-G6#g( z9Or~Kk(LegyH3K;ICT=k3ywzh(~ z%nRr;PiK7?kJeEzmw5q)XP$Pj$ox;qXL|Z?r|rTFbt7`eP}%ATSUy|0#AcO8{qd~wF!k$Il10RCnb;Y<0E(=Ryzq&2BtC*@(bDvMI@jBcO?zqL-qe+s8rk-gcy(yZkFD4(PJvpIQEkLw1e%X1vuUFAr zeIBoyN#xu>D(ho>mJ6_Hp@r=di9NSUbKm! zY)k0mp^i>0%ubW_AHrrg?iAik+KIc3r68g&YKQ8So+l-ztAn<+wWW2+l<-SG&Qtzv_x08~i-KivrIyxMVOlX;6;k18>iL9mLeHJaE-W_?t07C9;^_qvM_s$S z&ixQ%qqWWL>~#wIjxj4mcn^jdE`e_GZkHwpUnU2AtIB|)nX-%_Q-(Tj_4dfBpjtcJ zx~@(cqD>`PBgQq()+JHwVUP~jCt2`15TN4SF8s76t_@dz^52*5t#CCr>d&PffeAtl z#Z9<=_{&jx>bty*>xbVBMLZY(Q*nL&yZP4G|8KaWQh!Q!5v`Z(5Rc;e_^0#zI|ZUo zkcCe_OB|tF;#@7WlXTMsTHCecdH+gl9Zw)17g=9obJ#{7a*)`XJHiPDw!kl)NZFEC zt_SBCxduueabE8_HgLAv+&A1uT7YqJG0mK&%9#U!p^-EtDCrSmvd+(9)_;wkBAb7VBH~Ra&Q8 zS^{9kljC({EUoJ6gt)z#Fh!2tck~!ZulJm`6&u`RQDNjW8Z4v3jC=(kGx9A+jan3& z&DZZp`N&w9mYL{}A;<4DkBIaXEE&$GdQnA9$_m1RmT1p zd)ho&N#x@)E8BfZma_eim^biEK3t<>l`}brZ)Rn=KAe|mC8VwB3nd5O#|A{6@wcdv z>9ukea%F`yqwhLaD0v{R-nLkIs1!l2wW1%OA13yEQZ%b4BjTQ?xIRfUNWCt)5bs{K zKYx-YF^v@=2iU41+#g644Vvvm$8RcgiKpyZT)I zw@e;B7iYkr;pC_AE(0lHS59<|rkOoms>*`%-;2IH_HvBLHky9{#KH&OZDe zYJ91a0ozbZ_y386T1SGD6vq|UZq_-CRIi@1Nrj06SGRYQ_k8V|2dN8J0WvGQx1CK} zrBnIchTQmV*aZBvr|Gx&?+EeA2%n|QpZgW<-u50`{i$!H%gVT0rX+Qjwt14xau_N_ z*~ykE%>>`&rhg(p=pPxP>DyW@9XJ#}Ukx3>Hx*D5>x9`H0@w|SUt74fCX!s6Q^AAH zruJN9&%=2-Lz3(N2yJmXmrSmgSM`5H3eDNKrn>7};d@bky^q zE}ZSC=w`nqXl%-9XxSwMB}d9))q(^g$vDh{gjv3XdXU<1wv!k2v-CHc-xU0{m?4HN+ z?&`>;YG>{y=t$5N0_p6pk)X@-Se1EV;_<@mg4x+z$#?Lej$Q>tJ@50VY)?LP@S`%3 z4DhH61{BB+?@D6cf>>DUV!cOefVJ;FBGwJn?w~5PCLZCzlnIrm2v;9r8ENc^3CJEo zq5B-)(dT(+#o^kx#UNKaro}r|&cM+;)rX8!BaRcpVgcww${A{(p0%5b1*&yGr8UFy zS07S|F;wZn8|l|q)XO`2H1%p@(1q$bx=iXEvA*{;IAN{2%p^7_G+5PfV$P>s2aE-k zKJXyl9K z^TtCfNQ>*>>jAQBt*s^9Op0@X0!qagKUq_Gx^Oy%vNHD&F?1~&0`~s_(8^r7-l>Yl z(FyKUMONnOW^0-2MX)8>`$Z$gvMdXo0}a*gEIC*og2F>d!%izs8B>HZ1TD3d>4c3o zAS_*Xj}taXd||{OQCvzp6vnWuL!l2V25q}n&2b0M)%RQjg_T`MLk4FKXuCq?p}Ng+ zat*On@`AGiIcGDNxXK1;7qWNPBD-dS#bmu}byoWnB8!|`B(3VF9B>gWWiFlB5c~xT zpwHy*%frod^;NRxFePV@3wE;dG%EwbUC5p^f~&%@G*I+64ssk#IyCJtj&!u6qdt># z(7P8wEfW(ne6TZBM7BBY*5))@n^Qt$@c*do{mx|M{gpG^eZ>DPqyYN z<omana1?s+Ht+3`x~PPf-JORt>#0M8)oq|B1@g-!j~ za%pAqdZo47f;ZMq|A=#Z)JvVORZ68L`alyx!|S#WEGTuBCM;(xC;oCJ?H9Sqi*Kif zpvm7N9*2NW0vxovwBrj%A({b4`{!R!!|8R=w+RhaiQ`y{Ub}Kr(Yz;@`_#o*xb+`&L|xc_9%_l@o23X?BSeOovdcm()Qg= zs^PH|Fgm`Wf9jMPFSr3YZEaf!PtH?9ot6$qesfMKrhb=fnxfS5oTDAbsMewaHaIRG zEH2i~vi0bQw*Ae7LHa3#{*KJKrKqVUx4cM`b8cD?i-uXtR{qZB{3G-4)_Fl`KJl;4 z%VPS_d{wF-R%u(g&!4344p$nDJ?esXR1SiuMr1~Xk?4`Hr8*4AthtpVa28*rG_!tU z>%7L2>>K(kN9#Ayk|IsK(ra!Ecail{X6179kP7o&9{t~Jm~e=zU2a8@^*`xP<{@0< z3LWCcSqIxbdJout?MkIX++x7Fl>yOnsZY<^<-xnCN6SVGTs1!1$p233B=zrKFwFM5 z&aE6H99Q}{s^KRHe$6m=qp)kg?A*#Jf?wyuOyMW6I8OM z%9Gsg+MFtY9G__g!^gS_Et96+s(!b%PUvzqT#;qPa3V-?aZMm6-yToB?(XWJo8b?i zRJgjX{uQcy>RrVbmX-Gb48YF7( zl!`-th;wN=kFVnn;AZ}ru6+bQ{d`?(!f(=;>RJ;_6{+c36_T%O?K{)8KhEn{u4`3; zt8}g5&vfng$%S;S#3Ruo*R>Y_LD&8eXYtRJW~OV`EcZ8MVoLT)v&vQ?rqw(_@g6>Zd0}S)reV2W1QRf1x!UXy zMKZg&TB(Quqbe2cD{QE2Af@s+VNfc{2ukHY5R>JLowazH2J6$+HbJPuL8(~gW92cu zB|B&I=xiL8d1K??eMGO${G~g z#g9ShbOnQftnPmWS<=t1`zBb%mbC60hw{2FUix+4zO%ajlU#Q5y06+%Rrd{lR`;)k zD%E{)H_;=n`!@o~#|7o&8DMFB`z5blefz1UDfn3R?KWk7l={|DI*4j@gBL&Rr>~4Z zoZAM6s%B}&&ym8gn$|@1%Dwx4fc)2cHLG-f_U%=w=ammCm2mbvB1w$pmG#N<=2$Zv z-igj%WAjFKG$n5dSpY&t&js z$dU|-kBJ_+4BiDKZ*%ryA^fj^_McW_+|&j4r$tj;*Sb`+3oj5NmD87a_2l#>qf79y zi5itmx06lK0y1Z;*EoRnN zd9M$jB@fI-ysnSvHHiU6RA$9~%m(k|MSLl8A@D|amQZY{ROEB64nomRdCM;^@B5S= zD{q|{C!KTMb*`8-o-2@~Wo-EJGFHld8QZtYSo2l4Ey-yw%w}&0L=4 z{T%`@zjhuKr}8MI%Jlv&e#sj=c>k;3fA8M^ruRSSJ(iC1s>6Jv zJyAR^K=ch>EBo_ks!M(L;3N615T_n{$>TTjsIQ9!uzBC~bs^b_7AnN=#tU(vLcBR% zhyxVjt?@$W9Lwa_3K5H%{$0q{=mfb-AJ)U%R~H+~q1wk_XJrqDHg&O>7H-EGdUoWq zW*j)T#G7$YDKO*kcR2%p;i(yi!(<@egHq<=M!cADkmH^9saT-mJQSAIG3B5$pAmkn ziPYAi#E=>`&LFC?CKiy?nz%r*_>=C`{Mk$VJX@bA%58mfE6V4D@+;x_r;q2Da{noN%a$R9*e4rVxqor);r4^gt-Pdo|Muf;Uyk18 z?B2oK85B-4LxnR;jO9;X{O^SK`UU?3U8-+C6y~BIe!JBZ!N=0KzlNUC^zG!*AIQNS zUi?hoUV=aB?QtA5n57+mL<;dcWWcggVHs;9o7Vf!yuN%$XY?b?YlHtGMAi*d_J?X; zCglo+N9R_+a1kOCE!Bw@8RAnaTey3Al};?LC;+H~4D!?(!hfhu{y^b7Ex4zk9c32L z6!cv|sLZ0E<(Dhy*Tn!S=uAH^SyJfd0!ON!4c$VrXJbGm?CWRy&f4HRbKd3pS*25@ zpACPepI?I#>1Smx(IeNxZvgSi`(t2fc`vnmRe9fKX$n48dB3gfj?%tPE`122@Al$n z<$aqg@87ucwzT7bWQ+1Cp{!a8UV)xe3Iz0|U;;QL z1xhiM0(s?9kkgP#K}q2|Ex0EIj$VtHNr6~^6v!`^g15w$I^&Dl==a_$WT3#1%77uu zWkAXLGGO15fi1xEvz(u~45-AaWWexeGVmsJNCuR-L=WC;^eztP^6(C!unGJXXK^in zT$e3ml{ISLF10ET-jQn*Z45?lY^cWclU(lB*b~!~DItyjS15-q;GDIoi*M!`x09S| zklHpgJ-Ja3w?a=jY$8T#Uu7R~;YU5o!fx(1hz4;7F`Nn4>qB;+icH zZ48LPhdcZ&y^ri#I|Zcb3P)6;qC}eze6WSLtcpoChHC$dm{gW9i`TVSz{+`KEPSer zwq>nJW3mE%s%Oe|&;F{ivAm+C05T=qLPyF7s*5%i{wct!@&8ZwFET5IfX1otB&-x} zSUwd$>ZQ4Kc zs3MiL?pn_Bx+~KBx@+HA-MufDlf3Rq607R2;m_)B30hTmRoIChyie1+WFxP;b%e_6 ztNInwfoA=RtZrP(X}yR}=V=pZm%2IzPlme}MJM9LRg(&DJD!%tYNR=bNuXRj6Ym`F zk>xDv^wlqlZYJLJ)eTAXRVHF)m6#XtltZc1S1yOo5Gvi43eHyH?Dlb@H?)Bpz0)CB zI1F|VlR45Y=1MOdTps+N?A$iHb`yPoaFLcuYC>(+vV&_YI_tn|>>L(~vDClm^EHPo zR^95~S8sFmcGv9EA&Xbnn=A{9=jrX|@>@f1Po99^ntHo-+X;s(UQ2J^K7hB+>8)Q_ zKF{0qhSOMzB0dT`_JTp-zN~BNpfF$7walRKUe>kjps-%nHEmEhFY8)vP#7=kT7FRY zF6&xhP}nY8>}c2cWo~5!@nRjkbKWmIIF)&?V$9OYz1=4$gZy#t8bO`ib}N=sEOQzH z3t{~ovOi$eWP|!cFu8IQT!PSY^ax*EC5g?Vr=4fN&iGCP3X}$%T8qgq9gKAr<0?gycC!jvgABkV5+jEnA%s zeFP^Lu^8#=Mf=YBwGZUwnCnHAc9mYV63q1CDu|Qxq6#$8BiDsQg`r;2UgY)a zyS%^2Zw~cJ4Wky>tVgBOA$KqFn@VJ>Z-zhK8Vu2MnUwX8Ro|Xnxs=tnEk##nqUcuN z^wM*-C@7Fcx2W;ihYP9l>iaNUaOaDKklUCTnyJIksQ=`l2~Q+apI2cY;5*X+Ubs}e z4dpV`DAQ2CB8TbXYO~&sqEcQQmc?a9Mf5U=dbQXgMXc|^FT;Xr;p~&}xH%jFLsojv z?i(xRM+;?US(r9z%74fUX)HW6+*O6N+-P{>)zMJ4zBXJR;dYloId(kjaku}Ho?OEk zQOl2tZ0@H+cIGf-D~yVajTePIrHX4E{5B=qG^ko9M z36TQ97D7P06M%2Yy9;D9BSoOCTp+FO=_aiH2)?y~7jbOPq=>VPizD7i56*Vc6z?mF zH!QNvz7&zaYLQ*sbxQwC<5nJ|7`w{Rwn8urix}W?EfpKxIcmu52-G`W)DOoYh()(u zEN#)@AcVCqKPlS+ zn6~g(vX<`4X|shRp{BpeonaY3@&Pvw=%3Y!W9wn+$mT6(pG8MZ>v32%zRQqxT8Fu75?YGRP1UJsvq zw7A+up+=d5d!c!EUnU zpX*`WGkXr0A3?^ReF^cUOeuzhNB>K--_hmBN9AK4jkVF4I1|G~!0NKzO4;>Mn#_(< zYv%(oB}9nV>npz@cyomvqm7mKSa;Rc5&cU}Tl7z@!m8;iGbtIe5uh<*Eqr5l>x7ZU zpq2@$3p8v2ySHa9aAuN#8|v0L!M)F|?5;d5^z*c@>l^pF^90pRj`mSER&56`^=ng@ zb#Zf;`p;{od@e^Ud2ehB|G?NPRkQMl-l^wX3$9#%=svzP9pc*7{LILMp{X3UNt0uc*AKjgIZbJg?cG)k;3e1yb$53_XS5w)@xyZ`u3ELwt3$x}p_$uB$4-*FuaeNC!|B?vNL@CD@$uPd3gzfj zXzhRhJTjo}nU#fW6B}%e?PHdPNVF4=%aKtlOUC%vGg|L*yc|4?7+F;pvxjOARL!%k z_lpHgH{S0?8-*P7HCk3Qa8Vo5Y3I0p1HZ=$dNe(q(lh51>G7!VufMg12{bL%?VFP0 z=%1PUI!-%%oXJAwp5;<>jf)ps=Av|!g2b8iZpJCIcj=q>Ha`4GIXWE4Gyj6^spJ}D z55P`xiF25w6xZpWT<^4$B!c?mew~Ns!!OtIJPJMp@nW9%KZJh}A1&VQ4w}mG6k=EE zA1xB%Na%NZE7o_3llOCRY{ob*ld6^%^-HC+3 zKx2fkv5fSr3{X6b2Tu}t7^DU5xs3?ex8-Nn^yEZAV?C{;dpA%3H)?qj7Gzqsf4y(y zqn6_orPJ_Xfj&bKGW6T%!>|qRU)XE<3Y?F5I9XXBG!IO$Y#j|GZwq(xmg2K{OejrPWn-2;ZWzR~oUIa?TRDm7 zi@$?kj)OXMon$w6)4C+v`02u!&bb%%kTdX2<@dY7B>A0C`X+pO%JL~V^83L~o7Cf{ zIu0xCr{Q|`lb>L-O10BJTvsh0qAt7Sk{LWo+-e}f`mV|+2+x$0?Rhy%AG4MUfn)QZ zu!Sq8fR=E^w%PkGr3rgvb)wDopFi!zfsX zWg@L_#z|M-1~VqR{HFno6&67Cb-ei0S7pJkulAkIJw21lN?u#_*Z&>BeK-)#j`xeBvkj$kUBpZKcJmxOSJsHad z@m@SRca`U?(fqRTnp4rjS^Wj^b>z7JIp)Vg%_(I|WQ&1)IXaOJMlq*!vigj)+^|=# z;qPhu(>bPt@OArbWD)2;b9(6z7ijRx5j_8~c>cQ0D+?I@0yKkHjR06n%j%QnF9&8x*Y<*2SpbK9>r-d)_Sge2+I(~^ydkf;5@hnGI>W*wWHMGHg z-%D-REg~TXj*w^cZSLk!gZNf}cCUfvZ|_$fxecE@ADfP{)`B_P*XW{TO(}MRTHEXT zEV`mRN|%4|WkRQ8nsob{(U`^ibH)8<`Tnn9EKoaw2bL3%o; zr&U6;n6BKefsMM292m^#DyZB;29t*f6~yj&FYk$X;5>{SZt>+t|EtOFFplpdfb*?g zDO;!+#P{dE{vA>Z8{MV*yLT$n;Ud{mI3MO$CTcTe5I-RJy?8hT(FOJ@-ynWau>3ke zR;&)YSWNlj@RXB_6^<(^d$9lM(YTLJk(Wjnun5}Tr|_5A9W;T zU(I|aPk^dJSF0D)o&&WF;qp{eLRNcY&X9ldu*etzoZx_uj0&uEz@Lu_taHG}Mgwv^ z9o{a$FZ8y8qf9i|dq$1Ybsi}NO3QhqsS3aINK?x3CH6KQ%zf59i4B>3uV}?>xa?b+ zIEW3EzRir+*;?Na9*S_7f@1H2y-y`1n;7j~9afH*(YuS$(b~|_+Ss?LVaroDOJNY} zJDRTsjnjx+1j4s8^c`Tqt@tKWd?6=<&Tef?XU(h2D%zQ5xRqlHR0F4mYf`G&`i0RX zwSU_Nw&p$neXO)-ndro|rM~ZA5#G?ZpPa_NJ#gIU?Te#JzOA0_r*p=(a48R`I&B^G z#%$jVzlQQ1Ry!nbZj|O$DYVqv{no?9%jJg^T>qE+G`alrXi(eI(7Qg4L(~zjG2jLM?r7tk_Vka5^&XX9B>)mf;y0qCY#p<5YoC4DF97a*}UFG&kB`|sbHd+B3 z!IWo)F-70wGG$L=iex8zgx5Y+S&;!#BC5u{m*?MB@oa$BI;ls))Rsv$v}&E)GC6#l zxV_t2SkZ;gvq&_jGfy^4lulPR#;fgWX^AdVa9Oviy)VVv_gOl+MByj7k=Yqlhec1a zc@JOQDav;CmC?wK3TF z{#Wq5J^mkf{(PF9=1=s!|ISY|bu)hBoN@Kd^LzS6jJci#nvS`q&D*8zlPsGD0Kh#BE$ui$0)4iB|>ufs6C(OnFy!zi1j*h4D+kp*B`CCfNxr=3XqOQ zW@>@9r&+*h~zw{cFXwsPVH!n=MrK?Lgeoiih8kvG2X4u*OK?Okm;K44jhR5EWzaPMgR) z*nl*2BcnPQKC|E1T0tN?zv;4>^@zYB=?>JYqc~jfOOYQeM+xj_pF$Xv1Di zOT0l6oHA+3@Ge4DYlw#n4I!Y@5cxSKR}y!O%%~Hj^W1X2dqTba;BPo)grn zF90?{YpG9uN61vt>Vxn|N!s4lSrdSf-hf`n<63D?`Xu&g zC7c2iT~D@YN|V=Lh@{P%PK$$r^K-(w=Nn3h+r8 zullRCvV=#i{yLcUGgSYOG0SY5=&lkIlbB)sgCD|(o~}aLoE>Gfkgg|TWntpIpGDHQ z*Bfi@Sxr*k>^i|ZO2#_Doz)58{R|z{NL^dPnnnM~s=i~h>&P}|9a$@JOZKK5)%f~U zE#%k?O{nspfuW9#L$yyraOHaTDux_=I=gjp6?>&BTJSQNv~rKF8#~jwu_mIH12=i2 z?<&UGP%&lP(kbF$3e;3(%hIlI%jh9j0gN85@OXL0xV%)X`1xV_Ro@Qb82a@a;AgtU z)MA@chHb7Iw*%w5 z75^bz-@g;d-IK6%V)OG831n%BnXS_U%zv*!(y8Lv z$^trHsJs#Vtv40!K*AM!VK(Q=bERd`!@s~8*}tEsm0SEa{<-~|`4T0Ne=`^ICyQs- zg@)&$7jhW$R__>WRSc>ar{`8)6~4a=!^$!XYpHg`!M0u5N0Tn( zmN~3%@M;U!^$jih&ePkAt>EAe;(yxnUp&JahbHF%H2oX@a#-&G#oYCO6v?f<_%E*{ zke#*oE9;92KGwd*Ka#J}_U=xyW&SUC@$>oRZ;^pp_%ZqUAP&=#MnDk%Pjti|^0Wm# zHhz4M++FdB|{3xl(ORWpoGzg@`^SBsIy;$sG~kw1-2&dD{Q9)WuEve z3Y^AFBbgGvHwcvz^n~GdC|umBS={!5*nHmh56XVANB6bY+;pwxS}jvXSE@*i#Dywq zt&I>}{#>hlXLGGD=6uWNT2(StbFGFyn`?awIyBe%0ihCQpZAaSE^g*?t$!huzh@g* zTJB4&VC62k(_GBqWc4MZ;bX#Zo&{4UT8gj@j&0A7H+ifj8vG9A#5MMmSLcq=zu36t zz_ENfHdNIns6G-HjD4=h2$n)y?;*-v&$&=ydy;U(V^w zd8OP`@yZg&c=gwmS7kyaA{|~$(7Q5_^Xi}CRjxmRxDFs!KJyGeG96LWp9XSFEZ7XOadsWSKuDaSqHJaqL%@(mLlw+W+jaZ)M;V7J+YQI7uqVyBS9#VsVKsb z*Mt8m8$~_1{PLn66d2NaU`a2`>VcBY%b0p$-`N_W*Yf=5Wh)L>m962=%JzNeP(5fS zR3e__<@qrX_AmrrL=R6^npxR0|Gk_v;;ZHCc3m1Oq+Fj&ZZ!7lvwc>!V8}i@^%esZXuO)Q~KOWgVLQ+oo=@G-K<7hYqY-v%inL6`Qa`UEOY-)ZAFS> zW_kH+`BL6C{FyDh&^Fl4BOC3B*rD1c=&tlLuIySYAa@S)EQf2rv2d=~mg8U^d}Uzq zObfOW&uFsR%dz}3e(Bz&FT-N}aiy@u?&dkT43BDJmM(}9kPC!)I%0x>koRzP(PhnP zb^FiV!WLl`&qzauf^ORX*#=up2xe8~>F{*z$Zo734&pfqumJ(GEy=X&T`JeP0CIWi zC~G~YzXx@m&^!IGra~6_;dPU5!N<}Mv%o)^erPPc0l$`b@iYB!0shp>N5szd#L^Dp zwMfF&e{Bm|yK>f0YMquCN@WdsW@)?hULn2qwT=lwWfZLGiL63zAf`?)kR8^;un~4gZ2DxF3{MgA)DLjg=RRRgS)?KJHP=I#?D* z>sB@)P`IsQgwFAvivjF6R)(7bp?@L;5>#$c=kQDTaQi3EXb~}@{Xwer6AN*RJ1G_o;SYkY{PrOaG9f$|2n=6}h(YP}(w484WE$tMuJo@DOUqHwWvdn+%3(SfKhAD5nI@KOM@V*oGWZ!-j?PEJjqu z&T2?Hl(n_u;=#pN<~$G?`M7i1yq40M$lC9W2VVgM4>p4b^o8###-GkI* zlPp?`FaG`)$xCHSJQ%^>H;lhK02q ze|N?^T*mmTbjRhdCESqgO1Ly(_?MzbvnngSSzTp?1AgxS&5d+vS8*w<;WT6o)fKh?q| zyAPno+HM9zwr-w%b$G<@(RqokK;63j(0=$4V!lzYkLVEeUvwxB<-Q-cXf=e9+<#Nf zz&Cg*_s1&t8m$Gf0OVfIP&>QNrv>e(ImH6iVW3j0A^)(0FpQE?xldgmL8lt_mQtv=QG`L$&nAM>U8hu6>x^EHb<+`tc)Q5)yU~MS9 zwVJ= z9D_w=M|c&zyGjkiq~stVu-V$_SRX-rAWwH>?ulq=Q)A+Ip~loi2tOP@SkAygp5pj% z;`oWiGy&ka9Iu)w7N~mAjN-V|*b-$j=d~zEd9BynzN-%t%qFqZ61Pjtsd3JN&p{M_Q(&}sqy1K* zQ+4wH)Ak;4a#mH`|M2W)c4o5+*=$Pz63T{$03lR&6G)*-?;SyU=fNF7<(Y|5q!+2u zq!$6DN>Q59QMw9(uz(_hsGy)ABFq2#JNGGfW;c2DeLw&G?A-f2=iGAcIp?ldaK7Q)wnjZ8A~=Vzx$+>Cc7iJ++^`lj%w}7m_yC&4n!ebS|_L)M_qN zAyiEo#PczF7ALcL(Vm3D-m*8|!mokI<`w%kO-POqn=6l_wR&Iy>a*GMzE5=l2jXJVd^&HigNPz%4VRw@lg8Yj?JWbc|I<=+o3 zC$=>}Y=Ts3XY!s+?Tz6xDu;{yZ~FA>95L+iuR~L`3`&65*dH-EjQNiemE9)wyOQ_R zUp0A8{l;((p6%r<6}C#`-V}vIWz&vGKM-mL&@!s$*}S_NsliuMG@Vdx^-gDochID) zwo}+CSjx8>n=N%XC@zON~oV9;}w<(7>1wQqMuWubU<9xtb{!}F5F5wHF$e|a{ilCq{X*n-<^uN{wt1(UeL0NkG7+}EH1 z6+8*udpf&qmZ+RT+ctf{r)*7GWXK*bWD+?E>tdUYx+yF>nw>8_K<5lLqjIjwd#zty zpXNn9&!}9ecsKa*ly3T)QMpw3n|wSxTPxvRoj;>;mBQZE5Y~9^^WM?@-|_vwmCq>Q z{;y>{qjI0h@e98kbDE-W(`sj1rVee2PT)P;2esgUCDGksz8rbBzNSc(p6wB&l-Xt< zQrge>X+yihW%~TxAbuWvQ@9XlveflC0iw6n4Cr=u+Y3*-2O~YDixp zr0Z(JY`=Qa^cO1cl4Q~eHAR+?^+AI(`Z?eJcxs(>Kz&>wrGDrQLff9Qd|dGxvP#Ak zFDuu4T=6Gs>xEy`xZ+u89c^6EYRk~x_2Q@F3N6{ACp#UFp2+g%2z#=K&ZK(P7fAg-vP;Yi&uFrMaI_Ul|solT6;;cdM!2YV}vE{qN_(;?T#Q6y>zEaGV4yC zRvB#nvgQlwH?O$Db(iWZ^F)%q(#XmBO6BbLmG++Ym4D94pY@gEa9v+%>8E>^pMn(i zl`3XU4$rUXSq#eh%3l!*U5kGQaQgB=Q_UWr8`$qtm^}bHC#b##EU3P2?i+H4i?5PI zBV?V+hL9>lM*+*Y4 zOkUBQ(Xn|@VKGl@%`$Xb;%qLUwUd7(kFM4r{0&ruTyEVua1ciHMI-oYU zui_$9@jYHDRRme7X(W@|ebI%#yEOi(G(M zDnQEj9m@Ayzb5O;q4#6;mGv|*@ptK0TKLR*IxhoEne?6G44(N$awcB?ZN?Xa=qBFQ zt66-<+*on#I@Q+bVR##z#k*!uXXEFbQr_%tKSy5mTwL8;aEdr3{xcv0gjq@6aQC{Y zg+Z}M>U{GYTo=_u-vwZ!AD%DZ`C1hR`>`#9u$y38dv`Ue-AfRQ>@yFUyPkH(cUiH< zb5*dSc}8a=(x_ubbs;e_qqBz@6J7+y-5&|*)&TdwV~z>-*ZHW7P2t5BRP9~!l%x%` z2jT%6lfvx5!HOx&9vrMCh1r9Hl~R~JI9NG_*@J_%rZ9VOu(2u39vrMKh1r9HwWlz9 zaIhs)m_0byQYp+H9Bf<)vj+$3NMZKiVB=GmJvdlr3bO}b>3!>eLyvVI%DQ&e|zrS{BG6x?%2<-1xH@e7@y0IewPD$q_ zh&HuD>vbI2EG=-PB0F#+Z{EYY? zSa~OdqP%W1)H1wq5;<-!XJp&URNUw=6<41)RNQ)dsp3=I<>XbD##M1o&x7G%c|Pdufhnu$P{Cc2-u=;Mq%^Y(KQJE)Ob`?TWncIUZNCon~(<;YLR+ zy0A)s!iMsyE8wcIXXJ$yg2Kx43aeO)F07?#i8T~9N921tQv0wcTan?Fcd0@o-E6F&eOtT|~{vT4@i1fewt-`Q%&8uV2aRzM{V= zyu<~VQCVd(#vN+{%f?ZYDi6Yn>4iy^hZFzt#Q%BXKWctHH|ZyOGB=r}l(5P2>!gj@ z2f6&yM3)UiZx=cjG4Hs91P8&k?EfmrQQZi)^kViPg<@jESjTa1BUk&E_;;;4RY= zovA;CG(RTI=4dd))_fm}5~zGyT*91aHYrEzVz_Z zf|@FCxO;Y5Qx%JvdRPEQY+1cXR$(EK7@Jyb^GsPOoGf6%uRbCRe9DQ<)TY9L>W-Q`! z%ks8N8@ZKZQf{UFz~j$kyb`q@uf`QR(XW3SuQmq-udI*ns%^tt5N~PjR_1PvyEv{W zK3v&WUUdsxapfFw#f{m8fGhGmt|-<>uB;1JO3^zoWk6p)++%d^im46PJ{5Y<%7Nma zqgk%;PY)ybXH*(_jr_|ojr@D%_Zk0GE{}gs*FH7=Z3imXwViomeFE-CUE49k1@TS_ zu^3%bs&I7|dDZQ4#np3jT@wPX%JaCYSR=Wb=-TVzm7`Ct@k$RPcx8E8rj5MHF^#

Kr!IOq6Q7?TzDzy)-fY;^MGt18Mq3kykJ!_S6 zOgX#-1Z{OPX1L(8Oix5$Q+TVPv$dh@mDo%0ApR0w_$%a)^$nXhg)i_P#CwCvY_glv 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 z%0AEJm~z+)!rF#${zP&*0rEvM+|o5n`yW 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� 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

atVm+*N zL)`pT<~NZMz7WD#Tbm@|vmt((&b*$6b7)W%BT9QE`hGws*UVU z0r1|@sWY1biroPO^ZNU^#hy5IltbVp*}VaM-~l0P?n%J)cG1PRe+2{Or6=A|`$eZ< z;jmB>cSV`6YC_FQ|8MpuL;%vP-@pOZV9QaEU*&!%OwwvLP(~0eQ>CE^kOAfpLW1>F z?lk@h#%=L(h$AOY!&{3t+wlVekNcHuH*iAyWllv+SvmrM7GQVr1@svCt}!;-5tu3@ zvB416apWP~V#YIwacBewu+#ljw5Lp{2WpFWU66-zJXW(>lGk7iSbkQ#D6d^qa6rn@ zNRS0~NpD_DLrjx!*O@~;Ix1Tnl<*>#Bs7v4jArb4^h(mg4RM`Gb|)5#?`Y zy@MputJzQo4`r(hvsG-j-XfA*45yPoq3zu+Y#^_Uh(HHJF8fJAE*H_r(XClvoiog^ z-s+PL2Zg!Nd4^xePAIhIYFEFK8<&q>EsMvSzpaW)9#JrpOOhS zJ`6YV+081V(&95^?BnWj1VXH;6kMgjaf)q^!x*P4D~FY(9v1~N%8~O_gu9|(u4c_n zWOcYq5vYjKBFB$PsOYOmt!)oMl8Uv?5UM2soV>CDSXHty@a5Y6K}|}&R}+zzJ{80y zBf)xA2_{enVM9Pmv5Ku=QgednThQCtW0x~io!w#XRcnX)dn;f6>Q`^-`PR3;xwyOg zrf)6gudgreTG_L5arf6(-ccad z?~M$sKbou2xxTMDNPKjz-z$rw271#szk2;`ug~rEl|1KGxtdLXecxV}QdZj>Nix?$ zx-OGT$Kmx8=(=`sU6;D9IYJb3T~D6hr1;OHT`X7<4R|?&K>7xAd_@74eZ!LE*>Sf{ zxXgVuTp1zSH!LZ7Yr<8@S)&WrpSZMt^NQ@oCGyDNf{7@t{w7!F4jC>jpto;WVwRt3 zJA0c2{|Z;;IPM#kl-(p;og5=v2xwY)52GuxMXhiBCx7xM(Z#`q{FN~23zsC*wz8gUASYCjP*< z3IbPmHT$wh`&zwAWbdCjM2W!?I_}^2wP_K^D|GqzJF_qBEPzHo3uttFEg_pY`ROHp zFTI1kLV+0?DDnBH%mW<@o_Qgo6DfY1XLXRq7T)>~ygN3T#$p57j;Ld0eDbtdZK~Oq zE>eh*+F_tE4{Y~HV5fajIfXH)O{pUZRisk?w^BlHE(plV}`!qG_nsJx^3?FwEIH_OqP!GC1E zRNX^htwQ~Y9S+hYoR}kV-N>8f?-E8aEHU8C-bm~6f&S_Q=(Oj-Tn zHfRBte8=VEPhQ^tMrbsxtFWjnMifX5Tlmo(kx^K6H~nODW^K0|Shhh_aeTQ^K{e6X^_iz}N#rxsjiC9J`X-6M7; zxd!2@tpSPrj<0%0&&(V-aPi3V(|b-`JpD8Ukf5+2Y<=;KH@qxq+6?d#X_bG|cY@zK zeQ2Mc%pbl!wd1)^8_Vva2&wN5=M2()l)(zS!|{6}%Nwju^bHgBe}T>qHw4WeqW}cR zX=DxC21@Nf<&*E8YPR%2$IvNnMPb-mNkDG1nSVh19kDti=Rkt~J3VF~&3PafaBsl&c?fNj}VC#Iu8Sc`m;3h2w0{>1FGN{RB%mFi_06a-b zcDo=bae*QN1V9SA{nSTKsLrQje@>&oY^PoT6rsX*BPjH}DhYrd;#=U{k`IpT_5#W( zaKJBC2UOs0%Za3U^0f7}2r&K(j*33YbLnk~CG?98L$$49;VSYU#fSvzn?S+<;a0=w z^fy^*aE%V6x&WOe%lD&4USW&?su4pg2UKHVjbPz~B9#`YczXO8>xgN{K3@j)gi{16 z1m~;BKGzb8O@tH@GN1JygOVPZCiKhaVx-jQBFKI%ij&VeqJ=YZnZ|^j3g4VCD%l^# zI1{RhZM($XP}H%i*@6^qG)Rb7QDG_MnpQPaPPS^Dr>2}up|C-}-f~8g*;Xo5(m8VX z#r#n8A|f3lale-U5k8V(v2)Zh=7qP^E|BF?T*iw#pyljff&f4fu+kZYB5I>pNba9J z9fMM6TzB5Os@~miY<*#JO?y#|Z7?x}hXqWo0P#nH^>81n2XsCB@hD1KKk8t}{Jur3 zB1ayZ`Ss>>N_~8C83uIb3yBJ$?c6@SX@@Ix>0&L6CDX6{D!(wa_oA;L zTH@B}Uu^yO)YhW5=Z)#^m6nVHt=y`~0}vhvXYO59vBbE<;5AwUn;gX?C@2Ir4rEo+ zSz-lBUnTu*^f&vH(nv;JV^xFgk?yZ%t1XR(Itr&(I+2|6(|)eH6N4lm_ZoYwje0?M zWcTVPkmB_|%2};#jtETGRR^F|$o7vTD?qslND?~NX*$xZ0~ZAb4K|x;p$cc39`H0w zk|D2hwKL|nrC>a>X9%n?97SrlYWA}SRV;fY?;xP{4IEMIIW4)^prfdAJrwm8b!&(k z+fb_M;OwE?tSt#PD%x{e5b#1T14DornnDd{0}8HLr?n8)dh%BvllfTlvT z;uu$qK0iG-wN=$7XGqv6Dqx>f#UUd%2}~xBkz8lRC0f#q-~b3V6S)~$k*!rD%j9Mk ze|2i+wQ`=`&(dzUZ4T`CB>8x|z(IkXDrY4{9U1uYw= zyX~;f=UoRW-n4vpq_(z4yj$e-f&G9!Q$q}y397LQSXXse;ZEuzv{bRG9Mg1!JyMAg0QZY%6!Q*@6dCLN_?Q)~x_B9Ir?S28koMV3DE>g1H9k6e|0SU}+5tLMRedXORMG2&3Yd zS+B8tJY!>-P2d82=cHl~N~;o-x{q`z@tbOcG5$J-qYFUCGh02}i^A!L=*%m>o_h57 z^m8Yt4{S~4A7CJL8Cv?az)}h*cKZ4K)6ZI};!WOCYQw8RxW(*Wx>N=6itZS0dN7{U zsZK)$3@f?2NY{`K1c1FZCRqTwm26U3+pT4{v9X1D%J0l-3AVb-84?)PSby8_sJDnS z`={nmG22_(5AaQ;M{1h*Y`ODBl;SOfY#ou|j`j6cvsHz26iXsjIDpfhb&mR=F*YPB z?*Wf63HDI#Df_HV4_Qr27z)Z2I~CwY;{TBC>3%@Ptr=zgLntcKi}t!*zqyG zT9W+aVS+!Yj!(WGR+jV@eV^D>c>1Fs!CW)yPh9Voz1#jgBRN`B9vrd?L5wJ6Utp zY#V7Om?5u4a|1Z4@|ID8Iu!c$l~wcsMztUrix~bZJRAN59uDBgYP8jKNtEh_j!6boTLgE^pg;WWUQRmbw-2RaA6jW2 zZ3ZgXVx7ySS6KsJt)$Ezi+$q-cD6K6G+09HPOIs4P zd`zMz@S~r-8hzqn(k$oN^xdFQ_n-uq^$Fe1cfnp40kM&*qa?Om36LE{go5-cI3ks# z?cqu7uvf?%h@I9l5f}1km4_(3TG`V#k_OT}(im;59IM>YWM9RuK&p)la*slFHO0E@ zav>i>TD28rBS*QYuP8Bk>+b2p2dBL7;IT*BbsF-UgJqd2<)*F6{f_%CHtOW z*Lx8QI%r}~ZFknGQqNrwg>FB##Rn|4rQUGAJ+i&eM#tiRgJL*=w4T2B$=>rJy)|WpD_;i zq8>@U6ou2f5Y95e$w5v*bOXDsT3rY2JbhwCLnOq+~KYlrxzr=3%Q=RkZn zEnjn=mx9j00+9vT&d z`(HEi+~s4615!2#Zq%$|uIA&OYzZ<4Xc!027@?!ms}q3Sa*Y(+? z^3v@WvI_>r(PSo)b)L-n%o?A z%qyLIP2t`!0xkNJVmX}*Dm+tE^RUv`+r_7^?7zdJiQ2$3j#};P?a@Rk<<-y8TFaZ1 zcmP^nvTZe(gZqHJ8a-<1S1Crth;{5#w*527QnN(-cKUm0fJ4bulJAqLCNBQ;m$CWm zc;l02Uo|@ZXEqUqn%)26Cy$>n(qQwl=@(yW9j&~WdRS;wya1s)F*rT@;=0JoU+7fo zbIbZE5D;A6uxs}Dx7&*av|=m%?<^1oOdyVG=Qa?yj8*yAz_~ps{S1RVRf|mqC37Te zGC(p)UO;gN6q>-VtvccG3HZ4}gpA9@7obw%$j1~19prQq#0c?xDGF3R)tv{)z0FlZ`0 zAT&migQ0DWUCKo9pBw@q*J>cvSnMJRQw4?dE6iokQ-^5|3`x2#d|Mfp_B23VYxF7D zmF6raXy(!;{zM$M=y4#Ed}%+2=UA;DH-kbu{^Zytbf1>qJAJJv?(nFaD#rO`m*L(N zUZmW7_Gb?*iFW1pAXbf#!l=obspUUt6LgQ60cKy!VXb}70DAH{0#W?>LraSGQ^zN` z-J{zg0<-$i61P`6UvD;tT_1p$JSA!Pc8ja!twW<*3V_V7KD1<+%d}39S!_D4Zpo3E zU-Pjt<+0N}K3Mkt5u^D>S2MhJM;I}Bm2{dbJR=}BulW(YMmiHaKEmlC_3jbM$t{I5 z0WMvw6v*s44RK)gWm1xew(BEdOUr`QV;aM3nsceU z|K9Y_lDG>yzM$Ls;5aSErLf-HEt`9r=K zjt5m$?Me6X-6(DKSOap9_#W>S3Q)DXhd%!BML<{=-``C!`9v0J%}qbHZtCbsFW+YE zrBw2qLeA=+>T36Kcw^xiQ>UJmZ$bFybZ0g+wdpNA@12)kL3~NRzD=UQ$@5I9Fk$2ym0tRyv9V6-L8x|_lzBWitqp{ z@2V(N><5aXjTX!^%WDH`G%ogRZ9@+uF&y}WAnp+tT5&gqvP?#$CO34b_eulm>y4=y z*`U_6+T*z`oAOW~ON_yKYP1%~L<2t><-{~3Daqb9Uok|bhqhUMiQ;TiG6DVZ7dDW- z57LfGDVUF^Rj#biKHO&}DPo^ut+A)FXmS9fGp16#Yk}ZeZ2-nM^uYdgvwL64A+mO* zj20{3Dl)UcVwM{%-nHKzJ9v5D8<$^xqrF{YIJ0&O3};Ve)PL<86)SD0gn#2@K>`Sk zR~7+n6lg&DkQ9ZhP8P0Ckc=pk(L#k(HD2x zb*bbR3e+lHtKZoaT4NYFvNT#+_P8I+Ft)5BYtoOEm&vO2a&@yz=v(p1EoM& zUo{}Z89lkcTZUGTdhD1$gAEp#%gR?egIKXi*UiiQ@Yq?0zL?(aSmsk7J&JaIB5unmbY&+qP%ohsGa7?Id!>|5K|n(&L{3SfMwL5Ijdh#GGpe$A4RbBtbYs3v z5%8gHB(pUIZh{chA1+Tvv)~U1FCDpRM8bo!*dpWA?BD1sKNP3;JO&Vfv#)gc%FF(T zx+ooH>AKRPsq4PG)JaM81}==6H`*CDboPbS=g#P;a{R&vI`t+$C>&VXw_yY=95y%l zX(R`){H%1Kc$fs-0q+5i=hyBIYqW4Oh(JHcYjI$x|7bBD$0f|Qz(ywu?y_Ka$N<61 z!U*C`?uNZv&1m?blC-OEV&16lg5blmwdXMlrN7oeOlp7@>FOwgcD-@=*fW=35`~j! zl{N3PdGOB1(iQ_lDQQ3*dqI!p+4XB{=S~lyO5LalAgrf3Fg9u%jkqh-2exDyj1^p6 z*hVdz2slw7^}yORfUS($<3bmyEORK;OneWluA}#5m%a~pcZCyAj%C#6?iWP%w zZJyl+HBOO$50D&+3Z7DDGSclDbRBS-OGV^RM?H%R+8DhnziD~veOW9P)=T&YmKH~ zXe;d^GU%qC-ZH)JmDp}i!~+#HMu?7rwudH3oQHy@XkRTFq|{0uix4yVAVhKs4Z(!k zKRz~4&HkoLek7uzppmhK+H^#bA(;Xg$KO>!u_Z0{(XZh;*Ou*YbBn?V<8?LbzMaQ8 zIM@`B7M09(HSwtj4UUPI8Og*4${<_dxXTMF-{f&u)xy>{!Xo#*E9*yUUr^$R+9&Oc zF4sYrP9DOr8M8vf@`j_Xwb1JdwK*;zW>Dy=*-w4B8oc?2BqTUF?bJi`qM^Iq)fkp} zidr5!&qIaPgdN>fG}bZQ3oAaEM2@VqucM?BeRpiOnyrirOlu^{ zKn@2DpIH_@OPZvh%%l}&KNrNPT5vU|j>T3|ovPWdBjyJ(Itjy)7>q&`1tn=a5&C1k zLP2y?33LZkrpH{CDoqN<+)2Ipyz*IoMg#Z+Q07~57#bkR#B@m-uc8Jh`Ua+cH{}yF z21U^#mfzq6WN#Q57#J1QU^*g_(#@{mGyeh^*+_EkJHty!|UU8eVpd!)VeO8u8u|T z`Z!%5r{Wp>FP2EdKp%izB2jVtEBn%IExwO2uYX>+HWG|{c1do|ycNcNsGP@t&hxHG&!7h-p4S_O?_FTU zh*6yHg@Ng1%M%{4^5e;OdsPgK00<6NoJSAB+&X!h&_XZZiZb&2J~JSPA_CoDhz=aI zg6xd+g881DS766LW;RnOL&j^3SRrm!%NkVhMHZ!*&W57E?gpGPc^K{trq*f}V~JuN z&G)?Q&+lEi(gPg$1xEY2M00_SOk2I=D`KVcV2hjUtGI4PDQTye1-`t|d7Gz3^D?3o z2Iixo&}nY8N@j*7Qx*oc74S1#juh;jP)OrSOY*__-inCB>(R;$crgQVkAQJh}y|GR5&5)ov&R3M(tHXVMyGM(0dAI{`T#41P4BOHe|W@RibKt{3T=Tu=qkA zcgXq)57gJn^C3twBU^;p@YW49}D z1OMd@%tvo#a9l<5uL}JDo=;ru9nk+)G+8Fgr~Ce&j_2MvT7u_%`jCBmTELZgJJmPe zuTC$_7$iW7@dAX517GyugjtB=WG+HBGEirC43dHXC}oh;nSN@xr(%isS)o5RUhqh2 z>LB*k9<5fELhwBd^mJ^k)p!Bn`CW<@s6Z^jccJ{0lya$2d3KRGIVr_Ef`jb7OY1ho zI_=NT9C&s5=_ep6K?qL&0(Z{yAD=k*@rl={xbfY;UAJfE=*t)1fAnwHy|^$H;#dT; zI=@^zvhL!(!~7!Yc{{ZhX>w-EaZjQYZS|LZgZ#Ajmgsgi=1P{f<#=l!x7K-~G zsw||sZz#bLN{{jOR27{(FxD@m>`#BF6z){J_ilL}hD2v#DSjk@Ba7G8`-n}G?ajD- zSH;16OczuZE-zHsScDg@r?S9?bwM?IXgQYy*MO9OMbCe_4Z@=B5QH6xXG=9>gu_6$ z;qe#Ml}{Oz5csPW7K>=~G#25dyk(@mhU}i69;+y>m8qhx}ZuG!25GzAhr~_h?(F)=Gtl#*-cNw<8y`OW>t|FD&NscWoVXeZBl`>n(grv@Dm7H zBw{h^54QGlgvKjbD85q=w=wY$X{t7Y?0y^R4Wh7AR_UW1<-H|YEOFjaE@P4WnQ52x z+J>#v#|I$#2~_Se{G%Mf%F=Q1w~xWEf*X24 zhNznTtD?feQ{mOnQ~xW^+N%KDzWnm0Le%e{-BX)bOR=&@%)=}292$)zJJsy>&hPTF zC#@-UB;Efryt~e6Lq!p&uP7h8tTsAIHP9ZNNL~22RoBz<;eqB7uZQ<8)+;R(6Xlxt zQcGq&+^dsj>oIbyF0K3J%!gZMPQCu|`yX6-ZTrm8^{(QR`(8@oJo@Ox{pGR- zmvJOk_7QL8SQ!c{A!U=sL>@+IfDqdEZxzhBh7tCr62R>c1g&GVRYJK62F*}Qy^0G+ zY!FJe=;j=O8Rh}2(P*4>D1xM`5YA%a_^c+;S#tHJ6mcaL++n9a%Md9~Dt}3h?v(F#Eg4`K10>*!k+lq9W}~q+o~YwBczNXXk@Gg<|~!SX!NMeQN>yAK+Y3;g9wXC z+)czaGV1iAS@aRf~A(x%4fr@+hi?f_!&ZiLbm4z3sc50jZrN~ z5KEN`+D?#M`fFR}E5RGnb}e(8;`GtD_t1|p;wq_Otr&xceQ!u_E3Z+`nf+)Z3>A0v z4G#2?V9g7*R%U-9+eSim1Slp0;`Z!JJJ}S|DRt5DQr|z1NwB~ z=%TdmKbulFU4}Y6tS0oc#T;$+zZdY)FTZyy=P1;#l$Y-TL<|b1W`%hA&mGPFwIpk( zti$r=v(kInEG$QotM6|sg5!eLM6Bf~a4CtuTndX4fVaG&jr1#JKFFEj8z-dE@{W0F z8e*<)Uh=M%J;k>BQcKj zg`aH*Vm}KkvR?fiNgcBc2cvYU!UGVvTUi?|GJYP)sU-NZufg47t~8KL58Q6mqm|gm zv&4OZJhal)VansUQJ9*W#`wxEG3*D7>Fa|!=`sJI3bL0fb+Nlk%;xM}S%KOs?Ic>L1o!UXBu>3(=4o^REMC;`)0VAAV z)Q$jeLr2PUC#O#D;G~#(cAuVLNL?D6er6Bf7uTPJ)nSt}jR)ijI=59yS`LxWSpm<~ zxdV+gwQl1gojlc3Ta8O7kSlV5s`qkwFfVbv0$Z}OXA_eL`fC#yz2^?}t;DJ3rxw(kQ45Wp(OKHW=qmJmad_=4`2vs9Gjw;T3tz4O)F;#za*xdE)j=S;1)- z)I6)x_N@F=R26iFR$(mIwIox@HzhEJE-jvpP%I_Ck_fB@2W!2xi3!lK@?~+Wz5fL7 zFO^Y@k$&FU>NX}+1P8Dxd6m`||d6SW|7hlT_{9`P;V+ zRdhfSl(c6h-LlSZgN<0)`~R?8-A?-r&veB_^|kzBe{N#jDCI`;v2)b z#?!YT7OC_d*lBCVVL3W6&Q|l5R-sd!+W@`LQiy0doC`}D6Dh7}^zbM@ZB@nfOdj$e zyO&)NHihxonl8k0)|59O+gr&CXbDOcsAlIdr_669?^PgCsYG0;fE~E7IOl~gl>-W| zmdqy0pR&9xkxhnlDlbkO{%8^8GOk*f3?+wXe=e|0CZ%dfFfZ%Yg0YVqB9Vb58 z6x0T7t$f5ngyJSMpY5cV6q9%XTlc6NO#lQ@mPvfNZt~S7i5HV~_VPxK%wb$sISV|A;=Jzm<)_(@?1a+Lz%--d{ zVcpz$t=?V7hM*?$^7Yk7Pn~(rLZu^mRMhK zU&u3JzNUvoTnF@VPVdE*EP4@>&jNJ=pKpMk?&$2%;2IHcqj~(jc{J|(J9{)DYgqT$ zHgbc8@ZEXGn@o#l&`Fy!ZcO?>z&mxVC`p8L&kxSYj8C*wLV|M#Taa zf`Xz3Ym7mP(nM*B9Yj%z9V^07Fk(RoHtZs3R5THcq7hpZ8`dcH@;!UzoC6r|ec$)T z_wSlK>}hA}E^Dv7*4m1GP}L`j&L(A4bpNQ;cmax!qR^rkt|U~;YC$Bw(7iIerZ%_c z5T1liOt`K$Q>5$@;UjN}Y0(de7{H>Jvqij;b_JW4KbkYUAUcI6^-)-gGJsPw?6P9l z!@2Vd(v}P7xgtN879+)boGe6mT@*Tzi6{C7&}~2wq27;(Q-?>6;R%a0b~;^(dKklW zmU6Fi>(Bj)*gPAtIpsfO;}j7UQRmjexCxm)g;gO*Bd#+QxZJ{l_aM~0Lfq92Ta?tJ z(UTWawWa)vY>TOdA5|i5(o9Ps+!>S{$BG-{^16`S8lu&e2ueo>#!pyM>jKV+(FyQ$@=JUu}Nw~H#HW4{vPPz>TW_T>F(kV z9@?=nU_C*ZT^A%B3PR?{NDBydeHntf0ycI zL`I60@vG)eRYlL;n}=I+`wf={c(IzRC+_n05QS9gyK z!tv+k?(S}l)coKpLRx-ydrE`_aeI5^p}_$`z9LMTP@fU&O#cXc8eHHR(bj8{+`KF` zYS=fS?I*M9W4!rK3_QL*AagAtYK8j4%@YNbMv2gm5>uI{L3d^)A9yiiwEq2nF%JY8 zZYV(9y2n)1UBbbx#O+zir_{Wv1V0l)9lHBg!#WuJzsa}QCa4NpozpjdctS_6i^s-5 z$~bRD*EK;iP@OVBjEX8}bX229h%TOorg^U+j)MpGYo!@#qo#e8bw>CD{l~dc6^_@% zUPlQa{Ko%%Px?U_AKf?6jo)Awu}@d-gl~;L;FvDIE7c>LCj=7mof4?1jzwIrk}+^- z8Qh58AvDi?Z92<79{ybZ0K@+Otu%^Ki1ES~?uuA9 z=w2D_(Z~m6+lFnPU177vh}>lZ+k3-n{;elR1

E-dX(#V9smO0CBt?_5MK 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

gt|@rrlN16 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

9zD!W!R&0Y)Zpyd+2o5G-fcVmRv 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

x4)BdgdV_?M1t7FU}ls>IKNlf6BeA(7z)_? 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�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�H?s7>kZa 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

j5h_yN4(`Z(zwoll0)mCy`^ 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 z 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

P!%aBS7G9X@ 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 z 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

`$7q0mOk)0&n7L;_vmCI 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

S!LAcQfFx?Hrq0eITQ>_vDsF1 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 z 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 + /// Only SqlServer + /// + /// + /// + public override void BeginTran(IsolationLevel iso, string transactionName) + { + + } + public override IDataAdapter GetAdapter() + { + return new SqlSugar.TDengineCore.TDengineDataAdapter(); + } + public override DbCommand GetCommand(string sql, SugarParameter[] parameters) + { + TDengineCommand sqlCommand = new TDengineCommand(sql, (TDengineConnection)this.Connection); + sqlCommand.CommandType = this.CommandType; + sqlCommand.CommandTimeout = this.CommandTimeOut; + //if (this.Transaction != null) + //{ + // sqlCommand.Transaction = (TDengineTransaction)this.Transaction; + //} + if (parameters.HasValue()) + { + IDataParameter[] ipars = ToIDbDataParameter(parameters); + sqlCommand.Parameters.AddRange((TDengineParameter[])ipars); + } + CheckConnection(); + return sqlCommand; + } + public override void SetCommandToAdapter(IDataAdapter dataAdapter, DbCommand command) + { + ((SqlSugar.TDengineCore.TDengineDataAdapter)dataAdapter).SelectCommand = (TDengineCommand)command; + } + public static bool _IsIsNanosecond { get; set; } + public static bool _IsMicrosecond { get; set; } + /// + /// if mysql return MySqlParameter[] pars + /// if sqlerver return SqlParameter[] pars ... + /// + /// + /// + public override IDataParameter[] ToIDbDataParameter(params SugarParameter[] parameters) + { + if (parameters == null || parameters.Length == 0) return null; + TDengineParameter[] result = new TDengineParameter[parameters.Length]; + int i = 0; + foreach (var parameter in parameters) + { + if (parameter.Value == null) parameter.Value = DBNull.Value; + if (parameter.Value is bool) + { + parameter.Value = parameter.Value?.ToString()?.ToLower(); + } + var sqlParameter = new TDengineParameter(parameter.ParameterName, parameter.Value, parameter.DbType, 0); + if (parameter.CustomDbType?.Equals(System.Data.DbType.DateTime2) == true || (parameter.Value is DateTime && _IsMicrosecond)) + { + sqlParameter.IsMicrosecond = true; + } + else if (parameter.CustomDbType?.Equals(typeof(Date19)) == true || (parameter.Value is DateTime && _IsIsNanosecond)) + { + sqlParameter.IsNanosecond = true; + } + else if (parameter.Value is DateTime && this.Context.CurrentConnectionConfig.ConnectionString.Contains("config_")) + { + _IsIsNanosecond = sqlParameter.IsNanosecond = this.Context.CurrentConnectionConfig.ConnectionString.Contains("config_ns"); + _IsMicrosecond = sqlParameter.IsMicrosecond = this.Context.CurrentConnectionConfig.ConnectionString.Contains("config_us"); + } + result[i] = sqlParameter; + i++; + } + return result; + } + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/TDengine/TDengineDataAdapter.cs b/src/Admin/ThingsGateway.SqlSugar/TDengine/TDengineDataAdapter.cs new file mode 100644 index 000000000..ac68a5c7a --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/TDengine/TDengineDataAdapter.cs @@ -0,0 +1,173 @@ +using SqlSugar.TDengineAdo; + +using System.Data; +using System.Data.Common; +using System.Text; + +namespace SqlSugar.TDengineCore +{ + /// + /// 数据填充器 + /// + public class TDengineDataAdapter : IDataAdapter + { + private TDengineCommand command; + private string sql; + private TDengineConnection _TDengineConnection; + + /// + /// SqlDataAdapter + /// + /// + public TDengineDataAdapter(TDengineCommand command) + { + this.command = command; + } + + public TDengineDataAdapter() + { + + } + + /// + /// SqlDataAdapter + /// + /// + /// + public TDengineDataAdapter(string sql, TDengineConnection _TDengineConnection) + { + this.sql = sql; + this._TDengineConnection = _TDengineConnection; + } + + /// + /// SelectCommand + /// + public TDengineCommand SelectCommand + { + get + { + if (this.command == null) + { + this.command = new TDengineCommand(this.sql, this._TDengineConnection); + } + return this.command; + } + set + { + this.command = value; + } + } + + public MissingMappingAction MissingMappingAction { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public MissingSchemaAction MissingSchemaAction { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + + public ITableMappingCollection TableMappings => throw new NotImplementedException(); + + /// + /// Fill + /// + /// + public void Fill(DataTable dt) + { + if (dt == null) + { + dt = new DataTable(); + } + var columns = dt.Columns; + var rows = dt.Rows; + using (DbDataReader dr = command.ExecuteReader()) + { + for (int i = 0; i < dr.FieldCount; i++) + { + string name = dr.GetName(i).Trim(); + if (!columns.Contains(name)) + columns.Add(new DataColumn(name, dr.GetFieldType(i))); + else + { + columns.Add(new DataColumn(name + i, dr.GetFieldType(i))); + } + } + + while (dr.Read()) + { + DataRow daRow = dt.NewRow(); + for (int i = 0; i < columns.Count; i++) + { + daRow[columns[i].ColumnName] = dr.GetValue(i); + } + dt.Rows.Add(daRow); + } + } + + dt.AcceptChanges(); + } + + /// + /// Fill + /// + /// + public void Fill(DataSet ds) + { + if (ds == null) + { + ds = new DataSet(); + } + using (DbDataReader dr = command.ExecuteReader()) + { + do + { + var dt = new DataTable(); + var columns = dt.Columns; + var rows = dt.Rows; + for (int i = 0; i < dr.FieldCount; i++) + { + string name = dr.GetName(i).Trim(); + var type = dr.GetFieldType(i); + if (type == UtilConstants.ByteArrayType) + { + type = UtilConstants.StringType; + } + if (!columns.Contains(name)) + columns.Add(new DataColumn(name, type)); + else + { + columns.Add(new DataColumn(name + i, type)); + } + + } + + while (dr.Read()) + { + DataRow daRow = dt.NewRow(); + for (int i = 0; i < columns.Count; i++) + { + var value = dr.GetValue(i); + if (value is byte[]) + { + daRow[columns[i].ColumnName] = Encoding.UTF8.GetString((byte[])value); + } + else + { + if (value == null) + { + value = DBNull.Value; + } + daRow[columns[i].ColumnName] = value; + } + } + dt.Rows.Add(daRow); + } + dt.AcceptChanges(); + ds.Tables.Add(dt); + } while (dr.NextResult()); + } + } + + public DataTable[] FillSchema(DataSet dataSet, SchemaType schemaType) + { + throw new NotImplementedException(); + } + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/TDengine/Tools/DateTime16.cs b/src/Admin/ThingsGateway.SqlSugar/TDengine/Tools/DateTime16.cs new file mode 100644 index 000000000..f0ac157a9 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/TDengine/Tools/DateTime16.cs @@ -0,0 +1,21 @@ +using System.Data; + +namespace SqlSugar +{ + public class DateTime16 : ISugarDataConverter + { + public SugarParameter ParameterConverter(object columnValue, int columnIndex) + { + var name = "@Common" + columnIndex; + Type undertype = SqlSugar.UtilMethods.GetUnderType(typeof(T));//获取没有nullable的枚举类型 + return new SugarParameter(name, columnValue, undertype) { CustomDbType = System.Data.DbType.DateTime2 }; + } + + public T QueryConverter(IDataRecord dr, int i) + { + + var value = dr.GetValue(i); + return (T)UtilMethods.ChangeType2(value, typeof(T)); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/TDengine/Tools/DateTime19.cs b/src/Admin/ThingsGateway.SqlSugar/TDengine/Tools/DateTime19.cs new file mode 100644 index 000000000..e24e7c0a0 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/TDengine/Tools/DateTime19.cs @@ -0,0 +1,25 @@ +using System.Data; + +namespace SqlSugar +{ + internal enum Date19 + { + time = 19 + } + public class DateTime19 : ISugarDataConverter + { + public SugarParameter ParameterConverter(object columnValue, int columnIndex) + { + var name = "@Common" + columnIndex; + Type undertype = SqlSugar.UtilMethods.GetUnderType(typeof(T));//获取没有nullable的枚举类型 + return new SugarParameter(name, columnValue, undertype) { CustomDbType = typeof(Date19) }; + } + + public T QueryConverter(IDataRecord dr, int i) + { + + var value = dr.GetValue(i); + return (T)UtilMethods.ChangeType2(value, typeof(T)); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/TDengine/Tools/FileHelper.cs b/src/Admin/ThingsGateway.SqlSugar/TDengine/Tools/FileHelper.cs new file mode 100644 index 000000000..5ed3a34e3 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/TDengine/Tools/FileHelper.cs @@ -0,0 +1,66 @@ +using System.Text; + +namespace SqlSugar.TDengine +{ + internal static class FileHelper + { + public static void CreateFile(string filePath, string text, Encoding encoding) + { + try + { + if (IsExistFile(filePath)) + { + DeleteFile(filePath); + } + if (!IsExistFile(filePath)) + { + string directoryPath = GetDirectoryFromFilePath(filePath); + CreateDirectory(directoryPath); + + //Create File + FileInfo file = new FileInfo(filePath); + using (FileStream stream = file.Create()) + { + using (StreamWriter writer = new StreamWriter(stream, encoding)) + { + writer.Write(text); + writer.Flush(); + } + } + } + } + catch + { + throw; + } + } + public static bool IsExistDirectory(string directoryPath) + { + return Directory.Exists(directoryPath); + } + public static void CreateDirectory(string directoryPath) + { + if (!IsExistDirectory(directoryPath)) + { + Directory.CreateDirectory(directoryPath); + } + } + public static void DeleteFile(string filePath) + { + if (IsExistFile(filePath)) + { + File.Delete(filePath); + } + } + public static string GetDirectoryFromFilePath(string filePath) + { + FileInfo file = new FileInfo(filePath); + DirectoryInfo directory = file.Directory; + return directory.FullName; + } + public static bool IsExistFile(string filePath) + { + return File.Exists(filePath); + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/TDengine/Tools/SqlSugarExtensions.cs b/src/Admin/ThingsGateway.SqlSugar/TDengine/Tools/SqlSugarExtensions.cs new file mode 100644 index 000000000..e3a810c24 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/TDengine/Tools/SqlSugarExtensions.cs @@ -0,0 +1,36 @@ +using SqlSugar.TDengine; + +using System.Reflection; + +namespace SqlSugar +{ + public static class SqlSugarExtensions + { + public static ISugarQueryable AsTDengineSTable(this ISugarQueryable queryable) where T : class, new() + { + var attr = SqlSugar.TDengine.UtilMethods.GetCommonSTableAttribute(queryable.Context, typeof(T).GetCustomAttribute()); + queryable.AS(attr.STableName); + return queryable; + } + public static IDeleteable AsTDengineSTable(this IDeleteable queryable) where T : class, new() + { + var attr = SqlSugar.TDengine.UtilMethods.GetCommonSTableAttribute(((DeleteableProvider)queryable).Context, typeof(T).GetCustomAttribute()); + queryable.AS(attr.STableName); + return queryable; + } + public static void MappingSTableName(this ISqlSugarClient db, string newSTableName) + { + STableAttribute sTableAttribute = typeof(T).GetCustomAttribute(); + if (db.TempItems == null) + { + db.TempItems = new Dictionary(); + } + if (sTableAttribute != null) + { + var key = "GetCommonSTableAttribute_" + sTableAttribute.STableName; + db.TempItems.Remove(key); + db.TempItems.Add(key, newSTableName); + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/TDengine/Tools/UtilConstants.cs b/src/Admin/ThingsGateway.SqlSugar/TDengine/Tools/UtilConstants.cs new file mode 100644 index 000000000..9ff77f845 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/TDengine/Tools/UtilConstants.cs @@ -0,0 +1,69 @@ +using System.Dynamic; +namespace SqlSugar.TDengine +{ + internal static class UtilConstants + { + public const string Dot = "."; + public const char DotChar = '.'; + internal const string Space = " "; + internal const char SpaceChar = ' '; + internal const string AssemblyName = "SqlSugar"; + internal const string ReplaceKey = "{662E689B-17A1-4D06-9D27-F29EAB8BC3D6}"; + internal const string ReplaceCommaKey = "{112A689B-17A1-4A06-9D27-A39EAB8BC3D5}"; + + internal static Type IntType = typeof(int); + internal static Type LongType = typeof(long); + internal static Type GuidType = typeof(Guid); + internal static Type BoolType = typeof(bool); + internal static Type BoolTypeNull = typeof(bool?); + internal static Type ByteType = typeof(Byte); + internal static Type ObjType = typeof(object); + internal static Type DobType = typeof(double); + internal static Type FloatType = typeof(float); + internal static Type ShortType = typeof(short); + internal static Type DecType = typeof(decimal); + internal static Type StringType = typeof(string); + internal static Type DateType = typeof(DateTime); + internal static Type DateTimeOffsetType = typeof(DateTimeOffset); + internal static Type TimeSpanType = typeof(TimeSpan); + internal static Type ByteArrayType = typeof(byte[]); + internal static Type ModelType = typeof(ModelContext); + internal static Type DynamicType = typeof(ExpandoObject); + internal static Type Dicii = typeof(KeyValuePair); + internal static Type DicIS = typeof(KeyValuePair); + internal static Type DicSi = typeof(KeyValuePair); + internal static Type DicSS = typeof(KeyValuePair); + internal static Type DicOO = typeof(KeyValuePair); + internal static Type DicSo = typeof(KeyValuePair); + internal static Type DicArraySS = typeof(Dictionary); + internal static Type DicArraySO = typeof(Dictionary); + + public static Type SugarType = typeof(SqlSugarProvider); + + + internal static Type[] NumericalTypes = new Type[] + { + typeof(int), + typeof(uint), + typeof(byte), + typeof(sbyte), + typeof(long), + typeof(ulong), + typeof(short), + typeof(ushort), + }; + + + internal static string[] DateTypeStringList = new string[] + { + "Year", + "Month", + "Day", + "Hour", + "Second" , + "Minute", + "Millisecond", + "Date" + }; + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/TDengine/Tools/UtilExtensions.cs b/src/Admin/ThingsGateway.SqlSugar/TDengine/Tools/UtilExtensions.cs new file mode 100644 index 000000000..e831c9a30 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/TDengine/Tools/UtilExtensions.cs @@ -0,0 +1,140 @@ +namespace SqlSugar.TDengine +{ + /// + ///Common Extensions for external users + /// + public static class UtilExtensions + { + public static TagInserttable SetTDengineChildTableName(this IInsertable thisValue, Func getChildTableNamefunc) where T : class, new() + { + TagInserttable result = new TagInserttable(); + result.thisValue = thisValue; + result.Context = ((InsertableProvider)thisValue).Context; + result.getChildTableNamefunc = getChildTableNamefunc; + return result; + } + public static string ObjToStringNoTrim(this object thisValue) + { + if (thisValue != null) return thisValue.ToString(); + return ""; + } + public static string ToLower(this string value, bool isLower) + { + if (isLower) + { + return value.ObjToString().ToLower(); + } + return value.ObjToString(); + } + public static int ObjToInt(this object thisValue) + { + int reval = 0; + if (thisValue == null) return 0; + if (thisValue is Enum) + { + return (int)thisValue; + } + if (thisValue != null && thisValue != DBNull.Value && int.TryParse(thisValue.ToString(), out reval)) + { + return reval; + } + return reval; + } + + public static int ObjToInt(this object thisValue, int errorValue) + { + int reval = 0; + if (thisValue is Enum) + { + return (int)thisValue; + } + if (thisValue != null && thisValue != DBNull.Value && int.TryParse(thisValue.ToString(), out reval)) + { + return reval; + } + return errorValue; + } + + public static double ObjToMoney(this object thisValue) + { + double reval = 0; + if (thisValue != null && thisValue != DBNull.Value && double.TryParse(thisValue.ToString(), out reval)) + { + return reval; + } + return 0; + } + + public static double ObjToMoney(this object thisValue, double errorValue) + { + double reval = 0; + if (thisValue != null && thisValue != DBNull.Value && double.TryParse(thisValue.ToString(), out reval)) + { + return reval; + } + return errorValue; + } + + public static string ObjToString(this object thisValue) + { + if (thisValue != null) return thisValue.ToString().Trim(); + return ""; + } + + public static string ObjToString(this object thisValue, string errorValue) + { + if (thisValue != null) return thisValue.ToString().Trim(); + return errorValue; + } + + public static Decimal ObjToDecimal(this object thisValue) + { + Decimal reval = 0; + if (thisValue != null && thisValue != DBNull.Value && decimal.TryParse(thisValue.ToString(), out reval)) + { + return reval; + } + return 0; + } + + public static Decimal ObjToDecimal(this object thisValue, decimal errorValue) + { + Decimal reval = 0; + if (thisValue != null && thisValue != DBNull.Value && decimal.TryParse(thisValue.ToString(), out reval)) + { + return reval; + } + return errorValue; + } + + public static DateTime ObjToDate(this object thisValue) + { + DateTime reval = DateTime.MinValue; + if (thisValue != null && thisValue != DBNull.Value && DateTime.TryParse(thisValue.ToString(), out reval)) + { + reval = Convert.ToDateTime(thisValue); + } + return reval; + } + + public static DateTime ObjToDate(this object thisValue, DateTime errorValue) + { + DateTime reval = DateTime.MinValue; + if (thisValue != null && thisValue != DBNull.Value && DateTime.TryParse(thisValue.ToString(), out reval)) + { + return reval; + } + return errorValue; + } + + public static bool ObjToBool(this object thisValue) + { + bool reval = false; + if (thisValue != null && thisValue != DBNull.Value && bool.TryParse(thisValue.ToString(), out reval)) + { + return reval; + } + return reval; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/TDengine/Tools/UtilMethods.cs b/src/Admin/ThingsGateway.SqlSugar/TDengine/Tools/UtilMethods.cs new file mode 100644 index 000000000..ae5286bba --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/TDengine/Tools/UtilMethods.cs @@ -0,0 +1,513 @@ +using System.ComponentModel; +using System.Data; +using System.Diagnostics; +using System.Globalization; +using System.Linq.Expressions; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Text; +using System.Text.RegularExpressions; + +namespace SqlSugar.TDengine +{ + public static class UtilMethods + { + public static STableAttribute GetCommonSTableAttribute(ISqlSugarClient db, STableAttribute sTableAttribute) + { + var key = "GetCommonSTableAttribute_" + sTableAttribute?.STableName; + if (db.TempItems?.ContainsKey(key) == true) + { + sTableAttribute.STableName = db.TempItems[key] + ""; + } + return sTableAttribute!; + } + public static long ToUnixTimestamp(DateTime dateTime) + { + // If the DateTime is Utc, use ToUnixTimeMilliseconds directly + if (dateTime.Kind == DateTimeKind.Utc) + { + return new DateTimeOffset(dateTime).ToUnixTimeMilliseconds(); + } + else + { + // Convert local DateTime to Utc before converting to Unix timestamp + return new DateTimeOffset(dateTime.ToUniversalTime()).ToUnixTimeMilliseconds(); + } + } + internal static DateTime GetMinDate(ConnectionConfig currentConnectionConfig) + { + if (currentConnectionConfig.MoreSettings == null) + { + return Convert.ToDateTime("1900-01-01"); + } + else if (currentConnectionConfig.MoreSettings.DbMinDate == null) + { + return Convert.ToDateTime("1900-01-01"); + } + else + { + return currentConnectionConfig.MoreSettings.DbMinDate.Value; + } + } + internal static DateTime ConvertFromDateTimeOffset(DateTimeOffset dateTime) + { + if (dateTime.Offset.Equals(TimeSpan.Zero)) + return dateTime.UtcDateTime; + else if (dateTime.Offset.Equals(TimeZoneInfo.Local.GetUtcOffset(dateTime.DateTime))) + return DateTime.SpecifyKind(dateTime.DateTime, DateTimeKind.Local); + else + return dateTime.DateTime; + } + + internal static object To(object value, Type destinationType) + { + return To(value, destinationType, CultureInfo.InvariantCulture); + } + + internal static object To(object value, Type destinationType, CultureInfo culture) + { + if (value != null) + { + destinationType = UtilMethods.GetUnderType(destinationType); + var sourceType = value.GetType(); + + var destinationConverter = TypeDescriptor.GetConverter(destinationType); + if (destinationConverter?.CanConvertFrom(value.GetType()) == true) + return destinationConverter.ConvertFrom(null, culture, value); + + var sourceConverter = TypeDescriptor.GetConverter(sourceType); + if (sourceConverter?.CanConvertTo(destinationType) == true) + return sourceConverter.ConvertTo(null, culture, value, destinationType); + + if (destinationType.IsEnum && value is int) + return Enum.ToObject(destinationType, (int)value); + + if (!destinationType.IsInstanceOfType(value)) + return Convert.ChangeType(value, destinationType, culture); + } + return value; + } + public static bool IsAnyAsyncMethod(StackFrame[] methods) + { + bool isAsync = false; + foreach (var item in methods) + { + if (UtilMethods.IsAsyncMethod(item.GetMethod())) + { + isAsync = true; + break; + } + } + return isAsync; + } + + public static bool IsAsyncMethod(MethodBase method) + { + if (method == null) + { + return false; + } + if (method.DeclaringType != null) + { + if (method.DeclaringType.GetInterfaces().Contains(typeof(IAsyncStateMachine))) + { + return true; + } + } + var name = method.Name; + if (name.Contains("OutputAsyncCausalityEvents")) + { + return true; + } + if (name.Contains("OutputWaitEtwEvents")) + { + return true; + } + if (name.Contains("ExecuteAsync")) + { + return true; + } + Type attType = typeof(AsyncStateMachineAttribute); + var attrib = (AsyncStateMachineAttribute)method.GetCustomAttribute(attType); + return (attrib != null); + } + + public static StackTraceInfo GetStackTrace() + { + + StackTrace st = new StackTrace(true); + StackTraceInfo info = new StackTraceInfo(); + info.MyStackTraceList = new List(); + info.SugarStackTraceList = new List(); + for (int i = 0; i < st.FrameCount; i++) + { + var frame = st.GetFrame(i); + if (!string.Equals(frame.GetMethod().Module.Name, "sqlsugar.dll", StringComparison.OrdinalIgnoreCase) && frame.GetMethod().Name.First() != '<') + { + info.MyStackTraceList.Add(new StackTraceInfoItem() + { + FileName = frame.GetFileName(), + MethodName = frame.GetMethod().Name, + Line = frame.GetFileLineNumber() + }); + } + else + { + info.SugarStackTraceList.Add(new StackTraceInfoItem() + { + FileName = frame.GetFileName(), + MethodName = frame.GetMethod().Name, + Line = frame.GetFileLineNumber() + }); + } + } + return info; + } + + internal static T To(object value) + { + return (T)To(value, typeof(T)); + } + internal static Type GetUnderType(Type oldType) + { + Type type = Nullable.GetUnderlyingType(oldType); + return type == null ? oldType : type; + } + public static string ReplaceSqlParameter(string itemSql, SugarParameter itemParameter, string newName) + { + itemSql = Regex.Replace(itemSql, string.Format(@"{0} ", "\\" + itemParameter.ParameterName), newName + " ", RegexOptions.IgnoreCase); + itemSql = Regex.Replace(itemSql, string.Format(@"{0}\)", "\\" + itemParameter.ParameterName), newName + ")", RegexOptions.IgnoreCase); + itemSql = Regex.Replace(itemSql, string.Format(@"{0}\,", "\\" + itemParameter.ParameterName), newName + ",", RegexOptions.IgnoreCase); + itemSql = Regex.Replace(itemSql, string.Format(@"{0}$", "\\" + itemParameter.ParameterName), newName, RegexOptions.IgnoreCase); + itemSql = Regex.Replace(itemSql, string.Format(@"\+{0}\+", "\\" + itemParameter.ParameterName), "+" + newName + "+", RegexOptions.IgnoreCase); + itemSql = Regex.Replace(itemSql, string.Format(@"\+{0} ", "\\" + itemParameter.ParameterName), "+" + newName + " ", RegexOptions.IgnoreCase); + itemSql = Regex.Replace(itemSql, string.Format(@" {0}\+", "\\" + itemParameter.ParameterName), " " + newName + "+", RegexOptions.IgnoreCase); + itemSql = Regex.Replace(itemSql, string.Format(@"\|\|{0}\|\|", "\\" + itemParameter.ParameterName), "||" + newName + "||", RegexOptions.IgnoreCase); + itemSql = Regex.Replace(itemSql, string.Format(@"\={0}\+", "\\" + itemParameter.ParameterName), "=" + newName + "+", RegexOptions.IgnoreCase); + itemSql = Regex.Replace(itemSql, string.Format(@"{0}\|\|", "\\" + itemParameter.ParameterName), newName + "||", RegexOptions.IgnoreCase); + return itemSql; + } + internal static Type GetRootBaseType(Type entityType) + { + var baseType = entityType.BaseType; + while (baseType != null && baseType.BaseType != UtilConstants.ObjType) + { + baseType = baseType.BaseType; + } + return baseType; + } + + + internal static Type GetUnderType(PropertyInfo propertyInfo, ref bool isNullable) + { + Type unType = Nullable.GetUnderlyingType(propertyInfo.PropertyType); + isNullable = unType != null; + unType = unType ?? propertyInfo.PropertyType; + return unType; + } + + internal static Type GetUnderType(PropertyInfo propertyInfo) + { + Type unType = Nullable.GetUnderlyingType(propertyInfo.PropertyType); + unType = unType ?? propertyInfo.PropertyType; + return unType; + } + + internal static bool IsNullable(PropertyInfo propertyInfo) + { + Type unType = Nullable.GetUnderlyingType(propertyInfo.PropertyType); + return unType != null; + } + + internal static bool IsNullable(Type type) + { + Type unType = Nullable.GetUnderlyingType(type); + return unType != null; + } + //internal static T IsNullReturnNew(T returnObj) where T : new() + //{ + // if (returnObj.IsNullOrEmpty()) + // { + // returnObj = new T(); + // } + // return returnObj; + //} + public static object ChangeType2(object value, Type type) + { + if (value == null && type.IsGenericType) return Activator.CreateInstance(type); + if (value == null) return null; + if (type == value.GetType()) return value; + if (type.IsEnum) + { + if (value is string) + return Enum.Parse(type, value as string); + else + return Enum.ToObject(type, value); + } + if (!type.IsInterface && type.IsGenericType) + { + Type innerType = type.GetGenericArguments()[0]; + object innerValue = ChangeType(value, innerType); + return Activator.CreateInstance(type, new object[] { innerValue }); + } + if (value is string && type == typeof(Guid)) return new Guid(value as string); + if (value is string && type == typeof(Version)) return new Version(value as string); + if (!(value is IConvertible)) return value; + return Convert.ChangeType(value, type); + } + + internal static T ChangeType(T obj, Type type) + { + return (T)Convert.ChangeType(obj, type); + } + + internal static T ChangeType(T obj) + { + return (T)Convert.ChangeType(obj, typeof(T)); + } + + internal static DateTimeOffset GetDateTimeOffsetByDateTime(DateTime date) + { + date = DateTime.SpecifyKind(date, DateTimeKind.Utc); + DateTimeOffset utcTime2 = date; + return utcTime2; + } + + //internal static void RepairReplicationParameters(ref string appendSql, SugarParameter[] parameters, int addIndex, string append = null) + //{ + // if (appendSql.HasValue() && parameters.HasValue()) + // { + // foreach (var parameter in parameters.OrderByDescending(it => it.ParameterName.Length)) + // { + // //Compatible with.NET CORE parameters case + // var name = parameter.ParameterName; + // string newName = name + append + addIndex; + // appendSql = ReplaceSqlParameter(appendSql, parameter, newName); + // parameter.ParameterName = newName; + // } + // } + //} + + internal static string GetPackTable(string sql, string shortName) + { + return string.Format(" ({0}) {1} ", sql, shortName); + } + + public static Func GetTypeConvert(object value) + { + if (value is int || value is uint || value is int? || value is uint?) + { + return x => Convert.ToInt32(x); + } + else if (value is short || value is ushort || value is short? || value is ushort?) + { + return x => Convert.ToInt16(x); + } + else if (value is long || value is long? || value is ulong? || value is long?) + { + return x => Convert.ToInt64(x); + } + else if (value is DateTime || value is DateTime?) + { + return x => Convert.ToDateTime(x); + } + else if (value is bool || value is bool?) + { + return x => Convert.ToBoolean(x); + } + return null; + } + + internal static string GetTypeName(object value) + { + if (value == null) + { + return null; + } + else + { + return value.GetType().Name; + } + } + + internal static string GetParenthesesValue(string dbTypeName) + { + if (Regex.IsMatch(dbTypeName, @"\(.+\)")) + { + dbTypeName = Regex.Replace(dbTypeName, @"\(.+\)", ""); + } + dbTypeName = dbTypeName.Trim(); + return dbTypeName; + } + + internal static T GetOldValue(T value, Action action) + { + action(); + return value; + } + + internal static object DefaultForType(Type targetType) + { + return targetType.IsValueType ? Activator.CreateInstance(targetType) : null; + } + + internal static Int64 GetLong(byte[] bytes) + { + return Convert.ToInt64(string.Join("", bytes).PadRight(20, '0')); + } + public static object GetPropertyValue(T t, string PropertyName) + { + return t.GetType().GetProperty(PropertyName).GetValue(t, null); + } + + private static readonly char[] separator = new char[] { '9' }; + + //public static string EncodeBase64(string code) + //{ + // if (code.IsNullOrEmpty()) return code; + // string encode = ""; + // byte[] bytes = Encoding.GetEncoding("utf-8").GetBytes(code); + // try + // { + // encode = Convert.ToBase64String(bytes); + // } + // catch + // { + // encode = code; + // } + // return encode; + //} + public static string ConvertNumbersToString(string value) + { + string[] splitInt = value.Split(separator, StringSplitOptions.RemoveEmptyEntries); + + var splitChars = splitInt.Select(s => Convert.ToChar( + Convert.ToInt32(s, 8) + ).ToString()); + + return string.Join("", splitChars); + } + public static string ConvertStringToNumbers(string value) + { + StringBuilder sb = new StringBuilder(); + + foreach (char c in value) + { + int cAscil = (int)c; + sb.Append(Convert.ToString(c, 8) + "9"); + } + + return sb.ToString(); + } + + //public static string DecodeBase64(string code) + //{ + // try + // { + // if (code.IsNullOrEmpty()) return code; + // string decode = ""; + // byte[] bytes = Convert.FromBase64String(code); + // try + // { + // decode = Encoding.GetEncoding("utf-8").GetString(bytes); + // } + // catch + // { + // decode = code; + // } + // return decode; + // } + // catch + // { + // return code; + // } + //} + + public static void DataInoveByExpresson(Type[] datas, MethodCallExpression callExpresion) + { + var methodInfo = callExpresion.Method; + foreach (var item in datas) + { + if (callExpresion.Arguments.Count == 0) + { + methodInfo.Invoke(item, null); + } + else + { + List methodParameters = new List(); + foreach (var callItem in callExpresion.Arguments) + { + var parameter = callItem.GetType().GetProperties().FirstOrDefault(it => it.Name == "Value"); + if (parameter == null) + { + var value = LambdaExpression.Lambda(callItem).Compile().DynamicInvoke(); + methodParameters.Add(value); + } + else + { + var value = parameter.GetValue(callItem, null); + methodParameters.Add(value); + } + } + methodInfo.Invoke(item, methodParameters.ToArray()); + } + } + } + + public static Dictionary EnumToDictionary() + { + Dictionary dic = new Dictionary(); + if (!typeof(T).IsEnum) + { + return dic; + } + string desc = string.Empty; + foreach (var item in Enum.GetValues(typeof(T))) + { + var key = item.ToString().ToLower(); + if (!dic.ContainsKey(key)) + dic.Add(key, (T)item); + } + return dic; + } + //public static object ConvertDataByTypeName(string ctypename,string value) + //{ + // var item = new ConditionalModel() { + // CSharpTypeName = ctypename, + // FieldValue = value + // }; + // if (item.CSharpTypeName.EqualCase(UtilConstants.DecType.Name)) + // { + // return Convert.ToDecimal(item.FieldValue); + // } + // else if (item.CSharpTypeName.EqualCase(UtilConstants.DobType.Name)) + // { + // return Convert.ToDouble(item.FieldValue); + // } + // else if (item.CSharpTypeName.EqualCase(UtilConstants.DateType.Name)) + // { + // return Convert.ToDateTime(item.FieldValue); + // } + // else if (item.CSharpTypeName.EqualCase(UtilConstants.IntType.Name)) + // { + // return Convert.ToInt32(item.FieldValue); + // } + // else if (item.CSharpTypeName.EqualCase(UtilConstants.LongType.Name)) + // { + // return Convert.ToInt64(item.FieldValue); + // } + // else if (item.CSharpTypeName.EqualCase(UtilConstants.ShortType.Name)) + // { + // return Convert.ToInt16(item.FieldValue); + // } + // else if (item.CSharpTypeName.EqualCase(UtilConstants.DateTimeOffsetType.Name)) + // { + // return UtilMethods.GetDateTimeOffsetByDateTime(Convert.ToDateTime(item.FieldValue)); + // } + // else + // { + // return item.FieldValue; + // } + //} + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/TDengine/Tools/ValidateExtensions.cs b/src/Admin/ThingsGateway.SqlSugar/TDengine/Tools/ValidateExtensions.cs new file mode 100644 index 000000000..ffe9325bf --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/TDengine/Tools/ValidateExtensions.cs @@ -0,0 +1,166 @@ +using System.Text.RegularExpressions; +namespace SqlSugar.TDengine +{ + internal static class ValidateExtensions + { + public static bool IsInRange(this int thisValue, int begin, int end) + { + return thisValue >= begin && thisValue <= end; + } + + public static bool IsInRange(this DateTime thisValue, DateTime begin, DateTime end) + { + return thisValue >= begin && thisValue <= end; + } + + public static bool IsIn(this T thisValue, params T[] values) + { + return values.Contains(thisValue); + } + + public static bool IsContainsIn(this string thisValue, params string[] inValues) + { + return inValues.Any(it => thisValue.Contains(it)); + } + + public static bool IsNullOrEmpty(this object thisValue) + { + if (thisValue == null || thisValue == DBNull.Value) return true; + return string.IsNullOrEmpty(thisValue.ToString()); + } + + public static bool IsNullOrEmpty(this Guid? thisValue) + { + if (thisValue == null) return true; + return thisValue == Guid.Empty; + } + + public static bool IsNullOrEmpty(this Guid thisValue) + { + return thisValue == Guid.Empty; + } + + public static bool IsNullOrEmpty(this IEnumerable thisValue) + { + if (thisValue?.Any() != true) return true; + return false; + } + + public static bool HasValue(this object thisValue) + { + if (thisValue == null || thisValue == DBNull.Value) return false; + return !string.IsNullOrEmpty(thisValue.ToString()); + } + + public static bool HasValue(this IEnumerable thisValue) + { + if (thisValue?.Any() != true) return false; + return true; + } + + public static bool IsValuable(this IEnumerable> thisValue) + { + if (thisValue?.Any() != true) return false; + return true; + } + + public static bool IsZero(this object thisValue) + { + return (thisValue == null || thisValue.ToString() == "0"); + } + + public static bool IsInt(this object thisValue) + { + if (thisValue == null) return false; + return Regex.IsMatch(thisValue.ToString(), @"^\d+$"); + } + + public static bool IsNoInt(this object thisValue) + { + if (thisValue == null) return true; + return !Regex.IsMatch(thisValue.ToString(), @"^\d+$"); + } + + public static bool IsMoney(this object thisValue) + { + if (thisValue == null) return false; + double outValue = 0; + return double.TryParse(thisValue.ToString(), out outValue); + } + public static bool IsGuid(this object thisValue) + { + if (thisValue == null) return false; + Guid outValue = Guid.Empty; + return Guid.TryParse(thisValue.ToString(), out outValue); + } + + public static bool IsDate(this object thisValue) + { + if (thisValue == null) return false; + DateTime outValue = DateTime.MinValue; + return DateTime.TryParse(thisValue.ToString(), out outValue); + } + + public static bool IsEamil(this object thisValue) + { + if (thisValue == null) return false; + return Regex.IsMatch(thisValue.ToString(), @"^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$"); + } + + public static bool IsMobile(this object thisValue) + { + if (thisValue == null) return false; + return Regex.IsMatch(thisValue.ToString(), @"^\d{11}$"); + } + + public static bool IsTelephone(this object thisValue) + { + if (thisValue == null) return false; + return System.Text.RegularExpressions.Regex.IsMatch(thisValue.ToString(), @"^(\(\d{3,4}\)|\d{3,4}-|\s)?\d{8}$"); + + } + + public static bool IsIDcard(this object thisValue) + { + if (thisValue == null) return false; + return System.Text.RegularExpressions.Regex.IsMatch(thisValue.ToString(), @"^(\d{15}$|^\d{18}$|^\d{17}(\d|X|x))$"); + } + + public static bool IsFax(this object thisValue) + { + if (thisValue == null) return false; + return System.Text.RegularExpressions.Regex.IsMatch(thisValue.ToString(), @"^[+]{0,1}(\d){1,3}[ ]?([-]?((\d)|[ ]){1,12})+$"); + } + + public static bool IsMatch(this object thisValue, string pattern) + { + if (thisValue == null) return false; + Regex reg = new Regex(pattern); + return reg.IsMatch(thisValue.ToString()); + } + public static bool IsAnonymousType(this Type type) + { + string typeName = type.Name; + return typeName.Contains("<>") && typeName.Contains("__") && typeName.Contains("AnonymousType"); + } + public static bool IsCollectionsList(this string thisValue) + { + return (thisValue + "").StartsWith("System.Collections.Generic.List") || (thisValue + "").StartsWith("System.Collections.Generic.IEnumerable"); + } + public static bool IsStringArray(this string thisValue) + { + return (thisValue + "").IsMatch(@"System\.[a-z,A-Z,0-9]+?\[\]"); + } + public static bool IsEnumerable(this string thisValue) + { + return (thisValue + "").StartsWith("System.Linq.Enumerable"); + } + + public static Type StringType = typeof(string); + + public static bool IsClass(this Type thisValue) + { + return thisValue != StringType && thisValue.IsEntity() && thisValue != UtilConstants.ByteArrayType; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/ThingsGateway.SqlSugar.csproj b/src/Admin/ThingsGateway.SqlSugar/ThingsGateway.SqlSugar.csproj index 6383e3c34..5a4fc76cd 100644 --- a/src/Admin/ThingsGateway.SqlSugar/ThingsGateway.SqlSugar.csproj +++ b/src/Admin/ThingsGateway.SqlSugar/ThingsGateway.SqlSugar.csproj @@ -9,11 +9,6 @@ net8.0;net9.0; - - - - - @@ -23,7 +18,23 @@ - + + + + + + + + + + + + + + + + + diff --git a/src/Directory.Build.props b/src/Directory.Build.props index ac0cc0b4b..b80a19e2e 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -1,9 +1,9 @@ - 10.7.21 - 10.7.21 - 2.4.0 + 10.7.22 + 10.7.22 + 2.5.0 @@ -20,8 +20,10 @@ All None None - - CS8603;CS8618;CS1591;CS8625;CS8602;CS8604;CS8600;CS8601;CS8714;CS8619;CS8629;CS8765;CS8634;CS8621;CS8767;CS8633;CS8620;CS8610;CS8631;CS8605;CS8622;CS8613;NU5100;NU5104;NU1903;NU1902;CA1813; + + + CS8603;CS8618;CS1591;CS8625;CS8602;CS8604;CS8600;CS8601;CS8714;CS8619;CS8629;CS8765;CS8634;CS8621;CS8767;CS8633;CS8620;CS8610;CS8631;CS8605;CS8622;CS8613;NU5100;NU5104;NU1903;NU1902;CA1813;CA1852;CA1822;CA2100;CA2008;CA1812;CA1508;CA1512;CA1513;CA1810;CA1814;CA1815;CA1835;CA1819;CA1823;CA2002;CA5350;CA5351;CA5358;CA5384;CA5392;CA1805;CA1851;CA1510;CA5401;CA2022;CA1848;CA2000;CA5394;CA3003;CA1515;CA1849 + net8.0; 13.0 enable @@ -44,5 +46,10 @@ Embedded True - + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + diff --git a/src/Foundation/ThingsGateway.Foundation/Attributes/UriValidationAttribute.cs b/src/Foundation/ThingsGateway.Foundation/Attributes/UriValidationAttribute.cs index a2ae3f1e5..0888db7eb 100644 --- a/src/Foundation/ThingsGateway.Foundation/Attributes/UriValidationAttribute.cs +++ b/src/Foundation/ThingsGateway.Foundation/Attributes/UriValidationAttribute.cs @@ -15,6 +15,7 @@ using System.Text.RegularExpressions; using ThingsGateway.Foundation; namespace ThingsGateway; +[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] public class UriValidationAttribute : ValidationAttribute { protected override ValidationResult IsValid(object value, ValidationContext validationContext) diff --git a/src/Foundation/ThingsGateway.Foundation/Channel/DDP/DDPTcpSessionClientChannel.cs b/src/Foundation/ThingsGateway.Foundation/Channel/DDP/DDPTcpSessionClientChannel.cs index f402378ab..b28adaa43 100644 --- a/src/Foundation/ThingsGateway.Foundation/Channel/DDP/DDPTcpSessionClientChannel.cs +++ b/src/Foundation/ThingsGateway.Foundation/Channel/DDP/DDPTcpSessionClientChannel.cs @@ -99,7 +99,29 @@ public class DDPTcpSessionClientChannel : TcpSessionClientChannel { bool log = false; if (id != Id) log = true; + //注册ID + if (Service is ITcpService tcpService && tcpService.TryGetClient(id, out var oldClient) && oldClient != this) + { + try + { + await oldClient.ShutdownAsync(System.Net.Sockets.SocketShutdown.Both).ConfigureAwait(false); + await oldClient.CloseAsync().ConfigureAwait(false); + oldClient.Dispose(); + } + catch + { + } + try + { + oldClient.Dispose(); + } + catch + { + + } + } + await ResetIdAsync(id).ConfigureAwait(false); //发送成功 @@ -132,7 +154,7 @@ public class DDPTcpSessionClientChannel : TcpSessionClientChannel [MethodImpl(MethodImplOptions.AggressiveInlining)] private void ThrowIfCannotSendRequestInfo() { - if (DataHandlingAdapter == null || !DataHandlingAdapter.CanSendRequestInfo) + if (DataHandlingAdapter?.CanSendRequestInfo != true) { throw new NotSupportedException(TouchSocketResource.CannotSendRequestInfo); } diff --git a/src/Foundation/ThingsGateway.Foundation/Channel/DDP/DDPUdpSessionChannel.cs b/src/Foundation/ThingsGateway.Foundation/Channel/DDP/DDPUdpSessionChannel.cs index cc521b6b9..f8780ec93 100644 --- a/src/Foundation/ThingsGateway.Foundation/Channel/DDP/DDPUdpSessionChannel.cs +++ b/src/Foundation/ThingsGateway.Foundation/Channel/DDP/DDPUdpSessionChannel.cs @@ -160,7 +160,7 @@ public class DDPUdpSessionChannel : UdpSessionChannel, IClientChannel, IDtuUdpSe //发送成功 await DDPAdapter.SendInputAsync(endPoint, new DDPSend(ReadOnlyMemory.Empty, id, false, 0x81)).ConfigureAwait(false); - if(log) + if (log) Logger?.Info(DefaultResource.Localizer["DtuConnected", id]); } else if (message.Type == 0x02) @@ -185,7 +185,7 @@ public class DDPUdpSessionChannel : UdpSessionChannel, IClientChannel, IDtuUdpSe [MethodImpl(MethodImplOptions.AggressiveInlining)] private void ThrowIfCannotSendRequestInfo() { - if (DataHandlingAdapter == null || !DataHandlingAdapter.CanSendRequestInfo) + if (DataHandlingAdapter?.CanSendRequestInfo != true) { throw new NotSupportedException(TouchSocketResource.CannotSendRequestInfo); } diff --git a/src/Foundation/ThingsGateway.Foundation/Channel/OtherChannel.cs b/src/Foundation/ThingsGateway.Foundation/Channel/OtherChannel.cs index b7a17031e..4a77572db 100644 --- a/src/Foundation/ThingsGateway.Foundation/Channel/OtherChannel.cs +++ b/src/Foundation/ThingsGateway.Foundation/Channel/OtherChannel.cs @@ -149,7 +149,7 @@ public class OtherChannel : SetupConfigObject, IClientChannel public async Task SendAsync(IList> transferBytes) { // 检查数据处理适配器是否存在且支持拼接发送 - if (m_dataHandlingAdapter == null || !m_dataHandlingAdapter.CanSplicingSend) + if (m_dataHandlingAdapter?.CanSplicingSend != true) { // 如果不支持拼接发送,则计算所有字节片段的总长度 var length = 0; @@ -208,7 +208,7 @@ public class OtherChannel : SetupConfigObject, IClientChannel } private void ThrowIfCannotSendRequestInfo() { - if (m_dataHandlingAdapter == null || !m_dataHandlingAdapter.CanSendRequestInfo) + if (m_dataHandlingAdapter?.CanSendRequestInfo != true) { throw new NotSupportedException($"当前适配器为空或者不支持对象发送。"); } diff --git a/src/Foundation/ThingsGateway.Foundation/Device/DeviceBase.cs b/src/Foundation/ThingsGateway.Foundation/Device/DeviceBase.cs index 75d3ecffe..bff7430b4 100644 --- a/src/Foundation/ThingsGateway.Foundation/Device/DeviceBase.cs +++ b/src/Foundation/ThingsGateway.Foundation/Device/DeviceBase.cs @@ -375,10 +375,10 @@ public abstract class DeviceBase : DisposableObject, IDevice if (AutoConnect && !Channel.Online) await Channel.ConnectAsync(Channel.ChannelOptions.ConnectTimeout, token).ConfigureAwait(false); } - catch (Exception ex) + catch (Exception) { await Task.Delay(1000, token).ConfigureAwait(false); - throw ex; + throw; } if (token.IsCancellationRequested) diff --git a/src/Foundation/ThingsGateway.Foundation/Extensions/StringExtensions.cs b/src/Foundation/ThingsGateway.Foundation/Extensions/StringExtensions.cs index f806332e6..fae72e15b 100644 --- a/src/Foundation/ThingsGateway.Foundation/Extensions/StringExtensions.cs +++ b/src/Foundation/ThingsGateway.Foundation/Extensions/StringExtensions.cs @@ -180,7 +180,11 @@ public static class StringExtensions return true; } - /// + /// + /// + /// + /// + /// public static byte[] HexStringToBytes(this string str) => DataTransUtil.HexStringToBytes(str); private static readonly char[] DotSeparator = new char[] { '.' }; private static readonly char[] SlashSeparator = new char[] { '/' }; diff --git a/src/Foundation/ThingsGateway.Foundation/Extensions/TypeExtensions.cs b/src/Foundation/ThingsGateway.Foundation/Extensions/TypeExtensions.cs index 70d199a0f..d5c52e7a0 100644 --- a/src/Foundation/ThingsGateway.Foundation/Extensions/TypeExtensions.cs +++ b/src/Foundation/ThingsGateway.Foundation/Extensions/TypeExtensions.cs @@ -62,7 +62,7 @@ public static class TypeExtensions var nullable = customAttributes .FirstOrDefault(x => x.AttributeType.FullName == "System.Runtime.CompilerServices.NullableAttribute"); - if (nullable != null && nullable.ConstructorArguments.Count == 1) + if (nullable?.ConstructorArguments.Count == 1) { var attributeArgument = nullable.ConstructorArguments[0]; if (attributeArgument.ArgumentType == typeof(byte[])) @@ -83,8 +83,7 @@ public static class TypeExtensions { var context = type.CustomAttributes .FirstOrDefault(x => x.AttributeType.FullName == "System.Runtime.CompilerServices.NullableContextAttribute"); - if (context != null && - context.ConstructorArguments.Count == 1 && + if (context?.ConstructorArguments.Count == 1 && context.ConstructorArguments[0].ArgumentType == typeof(byte)) { return (byte)context.ConstructorArguments[0].Value! == 2; diff --git a/src/Foundation/ThingsGateway.Foundation/Logger/TextFileReader.cs b/src/Foundation/ThingsGateway.Foundation/Logger/TextFileReader.cs index 3548c7521..3ad25e668 100644 --- a/src/Foundation/ThingsGateway.Foundation/Logger/TextFileReader.cs +++ b/src/Foundation/ThingsGateway.Foundation/Logger/TextFileReader.cs @@ -51,7 +51,7 @@ public class LogDataCache } /// 高性能日志文件读取器(支持倒序读取) -public class TextFileReader +public static class TextFileReader { private static readonly MemoryCache _cache = new() { Expire = 30 }; private static readonly MemoryCache _fileLocks = new(); diff --git a/src/Foundation/ThingsGateway.Foundation/Utils/CRC16Utils.cs b/src/Foundation/ThingsGateway.Foundation/Utils/CRC16Utils.cs index fa018d6e7..48d32be61 100644 --- a/src/Foundation/ThingsGateway.Foundation/Utils/CRC16Utils.cs +++ b/src/Foundation/ThingsGateway.Foundation/Utils/CRC16Utils.cs @@ -13,7 +13,7 @@ namespace ThingsGateway.Foundation; /// /// CRC16验证 /// -public class CRC16Utils +public static class CRC16Utils { /// /// 通过指定多项式码来获取对应的数据的CRC校验码 diff --git a/src/Foundation/ThingsGateway.Foundation/Utils/DataTransUtil.cs b/src/Foundation/ThingsGateway.Foundation/Utils/DataTransUtil.cs index 274b1b355..4313944c6 100644 --- a/src/Foundation/ThingsGateway.Foundation/Utils/DataTransUtil.cs +++ b/src/Foundation/ThingsGateway.Foundation/Utils/DataTransUtil.cs @@ -15,7 +15,7 @@ namespace ThingsGateway.Foundation; /// /// 常用转换 /// -public class DataTransUtil +public static class DataTransUtil { /// /// 字节数据转化成16进制表示的字符串 diff --git a/src/Gateway/ThingsGateway.Blazor.Diagrams/Components/BlazorDiagramsException.cs b/src/Gateway/ThingsGateway.Blazor.Diagrams/Components/BlazorDiagramsException.cs index 657a83c69..ce68438e0 100644 --- a/src/Gateway/ThingsGateway.Blazor.Diagrams/Components/BlazorDiagramsException.cs +++ b/src/Gateway/ThingsGateway.Blazor.Diagrams/Components/BlazorDiagramsException.cs @@ -5,4 +5,12 @@ public class BlazorDiagramsException : Exception public BlazorDiagramsException(string? message) : base(message) { } + + public BlazorDiagramsException() : base() + { + } + + public BlazorDiagramsException(string? message, Exception? innerException) : base(message, innerException) + { + } } \ No newline at end of file diff --git a/src/Gateway/ThingsGateway.Blazor.Diagrams/Components/Renderers/GroupRenderer.cs b/src/Gateway/ThingsGateway.Blazor.Diagrams/Components/Renderers/GroupRenderer.cs index 014b5f673..5de2d99d1 100644 --- a/src/Gateway/ThingsGateway.Blazor.Diagrams/Components/Renderers/GroupRenderer.cs +++ b/src/Gateway/ThingsGateway.Blazor.Diagrams/Components/Renderers/GroupRenderer.cs @@ -59,7 +59,7 @@ public class GroupRenderer : ComponentBase, IDisposable // Update the port positions (and links) when the size of the group changes // This will save us some JS trips as well as useless rerenders - if (_lastSize == null || !_lastSize.Equals(Group.Size)) + if (_lastSize?.Equals(Group.Size) != true) { Group.ReinitializePorts(); Group.RefreshLinks(); diff --git a/src/Gateway/ThingsGateway.Blazor.Diagrams/Components/Renderers/NodeRenderer.cs b/src/Gateway/ThingsGateway.Blazor.Diagrams/Components/Renderers/NodeRenderer.cs index dddb97f68..9a69aaef3 100644 --- a/src/Gateway/ThingsGateway.Blazor.Diagrams/Components/Renderers/NodeRenderer.cs +++ b/src/Gateway/ThingsGateway.Blazor.Diagrams/Components/Renderers/NodeRenderer.cs @@ -48,7 +48,7 @@ public class NodeRenderer : ComponentBase, IDisposable return; size = new Size(size.Width / BlazorDiagram.Zoom, size.Height / BlazorDiagram.Zoom); - if (Node.Size != null && Node.Size.Width.AlmostEqualTo(size.Width) && + if (Node.Size?.Width.AlmostEqualTo(size.Width) == true && Node.Size.Height.AlmostEqualTo(size.Height)) { return; diff --git a/src/Gateway/ThingsGateway.Blazor.Diagrams/Core/Behaviors/DragMovablesBehavior.cs b/src/Gateway/ThingsGateway.Blazor.Diagrams/Core/Behaviors/DragMovablesBehavior.cs index 3f43161a6..4c70ea72f 100644 --- a/src/Gateway/ThingsGateway.Blazor.Diagrams/Core/Behaviors/DragMovablesBehavior.cs +++ b/src/Gateway/ThingsGateway.Blazor.Diagrams/Core/Behaviors/DragMovablesBehavior.cs @@ -32,7 +32,7 @@ public class DragMovablesBehavior : Behavior continue; // Special case: groups without auto size on - if (sm is NodeModel node && node.Group != null && !node.Group.AutoSize) + if (sm is NodeModel node && node.Group?.AutoSize == false) continue; var position = movable.Position; diff --git a/src/Gateway/ThingsGateway.Blazor.Diagrams/Core/Behaviors/DragNewLinkBehavior.cs b/src/Gateway/ThingsGateway.Blazor.Diagrams/Core/Behaviors/DragNewLinkBehavior.cs index d04c35e4f..60ea252fd 100644 --- a/src/Gateway/ThingsGateway.Blazor.Diagrams/Core/Behaviors/DragNewLinkBehavior.cs +++ b/src/Gateway/ThingsGateway.Blazor.Diagrams/Core/Behaviors/DragNewLinkBehavior.cs @@ -99,7 +99,7 @@ public class DragNewLinkBehavior : Behavior return; } - if (model is ILinkable linkable && (OngoingLink.Source.Model == null || OngoingLink.Source.Model.CanAttachTo(linkable))) + if (model is ILinkable linkable && (OngoingLink.Source.Model?.CanAttachTo(linkable) != false)) { var targetAnchor = Diagram.Options.Links.TargetAnchorFactory(Diagram, OngoingLink, linkable); if (targetAnchor is SinglePortAnchor singlePortAnchor && singlePortAnchor.Model is PortModel portModel && portModel.Alignment == PortAlignment.Top) diff --git a/src/Gateway/ThingsGateway.Blazor.Diagrams/SvgPathProperties/Parser.cs b/src/Gateway/ThingsGateway.Blazor.Diagrams/SvgPathProperties/Parser.cs index 94b80ea5a..4c7e11abb 100644 --- a/src/Gateway/ThingsGateway.Blazor.Diagrams/SvgPathProperties/Parser.cs +++ b/src/Gateway/ThingsGateway.Blazor.Diagrams/SvgPathProperties/Parser.cs @@ -41,7 +41,7 @@ namespace SvgPathProperties command = command == 'm' ? 'l' : 'L'; } - while (args.Count >= 0) + while (true) { if (args.Count == _length[type]) { diff --git a/src/Gateway/ThingsGateway.Blazor.Diagrams/SvgPathProperties/SvgPath.cs b/src/Gateway/ThingsGateway.Blazor.Diagrams/SvgPathProperties/SvgPath.cs index 1200dca93..8e3b828a5 100644 --- a/src/Gateway/ThingsGateway.Blazor.Diagrams/SvgPathProperties/SvgPath.cs +++ b/src/Gateway/ThingsGateway.Blazor.Diagrams/SvgPathProperties/SvgPath.cs @@ -336,7 +336,7 @@ namespace SvgPathProperties if (abs) { - if (_lastCurve != null && _lastCurve.IsQuadratic == false) + if (_lastCurve?.IsQuadratic == false) { var c = _lastCurve.Cp2OrEnd; _lastCurve = new BezierCommand( @@ -374,7 +374,7 @@ namespace SvgPathProperties } else { - if (_lastCurve != null && _lastCurve.IsQuadratic == false) + if (_lastCurve?.IsQuadratic == false) { var c = _lastCurve.Cp2OrEnd; var d = _lastCurve.End; diff --git a/src/Gateway/ThingsGateway.Gateway.Application/Const/ThingsGatewayCacheConst.cs b/src/Gateway/ThingsGateway.Gateway.Application/Const/ThingsGatewayCacheConst.cs index bc72cde41..c25975778 100644 --- a/src/Gateway/ThingsGateway.Gateway.Application/Const/ThingsGatewayCacheConst.cs +++ b/src/Gateway/ThingsGateway.Gateway.Application/Const/ThingsGatewayCacheConst.cs @@ -14,7 +14,7 @@ namespace ThingsGateway.Gateway.Application; /// Cache常量 /// [ThingsGateway.DependencyInjection.SuppressSniffer] -public class ThingsGatewayCacheConst +public static class ThingsGatewayCacheConst { /// /// 通道 diff --git a/src/Gateway/ThingsGateway.Gateway.Application/Driver/CacheDB/CacheDBUtil.cs b/src/Gateway/ThingsGateway.Gateway.Application/Driver/CacheDB/CacheDBUtil.cs index 70d682677..bca1a0981 100644 --- a/src/Gateway/ThingsGateway.Gateway.Application/Driver/CacheDB/CacheDBUtil.cs +++ b/src/Gateway/ThingsGateway.Gateway.Application/Driver/CacheDB/CacheDBUtil.cs @@ -14,7 +14,7 @@ using ThingsGateway.NewLife; namespace ThingsGateway.Gateway.Application; -public class CacheDBUtil +public static class CacheDBUtil { public const string EX = ".db"; diff --git a/src/Gateway/ThingsGateway.Gateway.Application/GlobalData/GlobalData.cs b/src/Gateway/ThingsGateway.Gateway.Application/GlobalData/GlobalData.cs index 62388487a..9d6423f29 100644 --- a/src/Gateway/ThingsGateway.Gateway.Application/GlobalData/GlobalData.cs +++ b/src/Gateway/ThingsGateway.Gateway.Application/GlobalData/GlobalData.cs @@ -480,11 +480,11 @@ public static class GlobalData /// 报警变量 internal static void AlarmChange(AlarmVariable alarmVariable) { - if (AlarmChangedEvent != null) - { - // 触发设备状态变化事件,并将设备运行时对象转换为设备数据对象进行传递 - AlarmChangedEvent.Invoke(alarmVariable); - } + + + // 触发设备状态变化事件,并将设备运行时对象转换为设备数据对象进行传递 + AlarmChangedEvent?.Invoke(alarmVariable); + } /// @@ -495,11 +495,11 @@ public static class GlobalData { deviceRuntime.Driver?.LogMessage?.LogInformation($"Status changed: {deviceRuntime.DeviceStatus}"); - if (DeviceStatusChangeEvent != null) - { - // 触发设备状态变化事件,并将设备运行时对象转换为设备数据对象进行传递 - DeviceStatusChangeEvent.Invoke(deviceRuntime, deviceRuntime.Adapt()); - } + + + // 触发设备状态变化事件,并将设备运行时对象转换为设备数据对象进行传递 + DeviceStatusChangeEvent?.Invoke(deviceRuntime, deviceRuntime.Adapt()); + } /// @@ -508,11 +508,11 @@ public static class GlobalData /// 变量运行时对象 internal static void VariableValueChange(VariableRuntime variableRuntime) { - if (VariableValueChangeEvent != null) - { - // 触发变量值变化事件,并将变量运行时对象转换为变量数据对象进行传递 - VariableValueChangeEvent.Invoke(variableRuntime, variableRuntime.Adapt()); - } + + + // 触发变量值变化事件,并将变量运行时对象转换为变量数据对象进行传递 + VariableValueChangeEvent?.Invoke(variableRuntime, variableRuntime.Adapt()); + } /// /// 变量采集处理方法,用于处理变量进行采集时的逻辑 @@ -520,11 +520,11 @@ public static class GlobalData /// 变量运行时对象 internal static void VariableCollectChange(VariableRuntime variableRuntime) { - if (VariableCollectChangeEvent != null) - { - // 触发变量采集事件,并将变量运行时对象转换为变量数据对象进行传递 - VariableCollectChangeEvent.Invoke(variableRuntime); - } + + + // 触发变量采集事件,并将变量运行时对象转换为变量数据对象进行传递 + VariableCollectChangeEvent?.Invoke(variableRuntime); + } #endregion diff --git a/src/Gateway/ThingsGateway.Gateway.Application/Model/DeviceRunTime.cs b/src/Gateway/ThingsGateway.Gateway.Application/Model/DeviceRunTime.cs index 8dd57620f..514949cd7 100644 --- a/src/Gateway/ThingsGateway.Gateway.Application/Model/DeviceRunTime.cs +++ b/src/Gateway/ThingsGateway.Gateway.Application/Model/DeviceRunTime.cs @@ -81,7 +81,7 @@ public class DeviceRuntime : Device, IDisposable } set { - lock (this) + lock (_lockObject) { if (_deviceStatus != value) { @@ -191,6 +191,7 @@ public class DeviceRuntime : Device, IDisposable public volatile bool CheckEnable; + private readonly object _lockObject = new object(); #endregion 采集 diff --git a/src/Gateway/ThingsGateway.Gateway.Application/Services/Device/DeviceServiceHelpers.cs b/src/Gateway/ThingsGateway.Gateway.Application/Services/Device/DeviceServiceHelpers.cs index d50ae62fe..e6adaeb37 100644 --- a/src/Gateway/ThingsGateway.Gateway.Application/Services/Device/DeviceServiceHelpers.cs +++ b/src/Gateway/ThingsGateway.Gateway.Application/Services/Device/DeviceServiceHelpers.cs @@ -30,7 +30,7 @@ public static class DeviceServiceHelpers public static async Task> ExportCoreAsync(IEnumerable? data, string channelName = null, string plugin = null) { - if (data == null || !data.Any()) + if (data?.Any() != true) { data = new List(); } @@ -112,7 +112,7 @@ public static class DeviceServiceHelpers propertysDict.TryAdd(channel?.PluginName ?? plugin, propertys); } - catch (Exception) + catch { } diff --git a/src/Gateway/ThingsGateway.Gateway.Application/Services/Plugin/PluginServiceUtil.cs b/src/Gateway/ThingsGateway.Gateway.Application/Services/Plugin/PluginServiceUtil.cs index 63c1a4ace..e32637097 100644 --- a/src/Gateway/ThingsGateway.Gateway.Application/Services/Plugin/PluginServiceUtil.cs +++ b/src/Gateway/ThingsGateway.Gateway.Application/Services/Plugin/PluginServiceUtil.cs @@ -166,7 +166,7 @@ public static class PluginServiceUtil if (classAttribute == null) continue; string propertyName = property.Name; // 如果属性存在且可写 - if (property != null && property.CanWrite) + if (property?.CanWrite == true) { // 进行类型转换 var value = ThingsGatewayStringConverter.Default.Serialize(null, property.GetValue(model)); @@ -194,7 +194,7 @@ public static class PluginServiceUtil string propertyName = property.Name; // 如果属性存在且可写 - if (property != null && property.CanWrite) + if (property?.CanWrite == true) { if (dict.TryGetValue(propertyName, out var dictValue)) { diff --git a/src/Gateway/ThingsGateway.Gateway.Application/Services/Variable/VariableService.cs b/src/Gateway/ThingsGateway.Gateway.Application/Services/Variable/VariableService.cs index 15910a6ed..a688fab99 100644 --- a/src/Gateway/ThingsGateway.Gateway.Application/Services/Variable/VariableService.cs +++ b/src/Gateway/ThingsGateway.Gateway.Application/Services/Variable/VariableService.cs @@ -715,7 +715,7 @@ internal sealed class VariableService : BaseService, IVariableService propertys.Item3 = properties; propertysDict.TryAdd(driverPluginType.FullName, propertys); } - catch (Exception) + catch { } diff --git a/src/Gateway/ThingsGateway.Gateway.Application/Services/Variable/VariableServiceHelpers.cs b/src/Gateway/ThingsGateway.Gateway.Application/Services/Variable/VariableServiceHelpers.cs index 65e62d347..ee3b37b9e 100644 --- a/src/Gateway/ThingsGateway.Gateway.Application/Services/Variable/VariableServiceHelpers.cs +++ b/src/Gateway/ThingsGateway.Gateway.Application/Services/Variable/VariableServiceHelpers.cs @@ -30,7 +30,7 @@ public static class VariableServiceHelpers public static async Task> ExportCoreAsync(IEnumerable data, string deviceName = null) { - if (data == null || !data.Any()) + if (data?.Any() != true) { data = new List(); } diff --git a/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/Channel/ChannelRuntimeInfo1.razor.cs b/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/Channel/ChannelRuntimeInfo1.razor.cs index d386be46a..4393ec61a 100644 --- a/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/Channel/ChannelRuntimeInfo1.razor.cs +++ b/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/Channel/ChannelRuntimeInfo1.razor.cs @@ -24,7 +24,7 @@ public partial class ChannelRuntimeInfo1 : IDisposable private async Task RestartChannelAsync() { if (ChannelRuntime.DeviceThreadManage?.ChannelThreadManage != null) - await Task.Run(() => ChannelRuntime.DeviceThreadManage?.ChannelThreadManage.RestartChannelAsync(ChannelRuntime)); + await Task.Run(() => ChannelRuntime.DeviceThreadManage.ChannelThreadManage.RestartChannelAsync(ChannelRuntime)); else await Task.Run(() => GlobalData.ChannelThreadManage.RestartChannelAsync(ChannelRuntime)); } diff --git a/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/Device/DeviceRuntimeInfo1.razor.cs b/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/Device/DeviceRuntimeInfo1.razor.cs index eee9f1ddc..5f76d5769 100644 --- a/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/Device/DeviceRuntimeInfo1.razor.cs +++ b/src/Gateway/ThingsGateway.Gateway.Razor/Pages/GatewayMonitorPage/Device/DeviceRuntimeInfo1.razor.cs @@ -71,8 +71,7 @@ public partial class DeviceRuntimeInfo1 : IDisposable } private void PauseThread() { - if (DeviceRuntime.Driver != null) - DeviceRuntime.Driver.PauseThread(!DeviceRuntime.Pause); + DeviceRuntime.Driver?.PauseThread(!DeviceRuntime.Pause); } protected override void OnInitialized() diff --git a/src/Gateway/ThingsGateway.Gateway.Razor/Pages/PluginPage/PluginDebugPage.razor.cs b/src/Gateway/ThingsGateway.Gateway.Razor/Pages/PluginPage/PluginDebugPage.razor.cs index c09e6f858..36a38f1fe 100644 --- a/src/Gateway/ThingsGateway.Gateway.Razor/Pages/PluginPage/PluginDebugPage.razor.cs +++ b/src/Gateway/ThingsGateway.Gateway.Razor/Pages/PluginPage/PluginDebugPage.razor.cs @@ -10,8 +10,6 @@ using Mapster; -using Newtonsoft.Json.Linq; - using ThingsGateway.Gateway.Application; using ThingsGateway.NewLife.Extension; diff --git a/src/Plugin/ThingsGateway.Foundation.Modbus/Helper/ModbusHelper.cs b/src/Plugin/ThingsGateway.Foundation.Modbus/Helper/ModbusHelper.cs index 19ab0afff..b9e581de9 100644 --- a/src/Plugin/ThingsGateway.Foundation.Modbus/Helper/ModbusHelper.cs +++ b/src/Plugin/ThingsGateway.Foundation.Modbus/Helper/ModbusHelper.cs @@ -10,7 +10,7 @@ namespace ThingsGateway.Foundation.Modbus; -public class ModbusHelper +public static class ModbusHelper { #region 解析 diff --git a/src/Plugin/ThingsGateway.Foundation.Modbus/Helper/PackHelper.cs b/src/Plugin/ThingsGateway.Foundation.Modbus/Helper/PackHelper.cs index 5e4652a55..5fa815a13 100644 --- a/src/Plugin/ThingsGateway.Foundation.Modbus/Helper/PackHelper.cs +++ b/src/Plugin/ThingsGateway.Foundation.Modbus/Helper/PackHelper.cs @@ -16,7 +16,7 @@ namespace ThingsGateway.Foundation.Modbus; /// /// PackHelper /// -public class PackHelper +public static class PackHelper { /// /// 打包变量,添加到 diff --git a/src/Plugin/ThingsGateway.Foundation.OpcDa/COM/Comn/ComInterop.cs b/src/Plugin/ThingsGateway.Foundation.OpcDa/COM/Comn/ComInterop.cs index e6cb595e4..af6af78a4 100644 --- a/src/Plugin/ThingsGateway.Foundation.OpcDa/COM/Comn/ComInterop.cs +++ b/src/Plugin/ThingsGateway.Foundation.OpcDa/COM/Comn/ComInterop.cs @@ -176,7 +176,7 @@ internal sealed class ComInterop // 检查是否在本地或远程连接。 uint clsctx = 0x01 | 0x04; - if (hostName != null && hostName.Length > 0 && !hostName.Equals("localhost", StringComparison.OrdinalIgnoreCase) && !hostName.Equals("127.0.0.1", StringComparison.OrdinalIgnoreCase)) + if (hostName?.Length > 0 && !hostName.Equals("localhost", StringComparison.OrdinalIgnoreCase) && !hostName.Equals("127.0.0.1", StringComparison.OrdinalIgnoreCase)) { clsctx = 0x04 | 0x10; } @@ -365,7 +365,7 @@ internal sealed class ComInterop /// internal static void RealseComServer(object m_server) { - if (m_server != null && m_server.GetType().IsCOMObject) + if (m_server?.GetType().IsCOMObject == true) { Marshal.ReleaseComObject(m_server); } diff --git a/src/Plugin/ThingsGateway.Foundation.OpcDa/COM/Da/OpcServer.cs b/src/Plugin/ThingsGateway.Foundation.OpcDa/COM/Da/OpcServer.cs index 36d3d5542..0d2bb35e4 100644 --- a/src/Plugin/ThingsGateway.Foundation.OpcDa/COM/Da/OpcServer.cs +++ b/src/Plugin/ThingsGateway.Foundation.OpcDa/COM/Da/OpcServer.cs @@ -51,7 +51,6 @@ internal sealed class OpcServer : IDisposable return AddGroup(groupName, true, 1000, 0); } - /// internal OpcGroup AddGroup(string groupName, bool active, int reqUpdateRate, float deadBand) { if (null == m_OpcServer || IsConnected == false) diff --git a/src/Plugin/ThingsGateway.Foundation.OpcDa/COM/Rcw/Interop.cs b/src/Plugin/ThingsGateway.Foundation.OpcDa/COM/Rcw/Interop.cs index e3fde0a20..36531a7e2 100644 --- a/src/Plugin/ThingsGateway.Foundation.OpcDa/COM/Rcw/Interop.cs +++ b/src/Plugin/ThingsGateway.Foundation.OpcDa/COM/Rcw/Interop.cs @@ -17,7 +17,7 @@ namespace ThingsGateway.Foundation.OpcDa.Rcw; #pragma warning disable CS1591 // 缺少对公共可见类型或成员的 XML 注释 #pragma warning disable CS8605 // 取消装箱可能为 null 的值。 -public class Interop +public static class Interop { public static PropertyID GetPropertyID(int input) { @@ -523,7 +523,7 @@ public class Interop internal static int GetResultID(ResultID input) { - if (input.Name != null && input.Name.Namespace == "http://opcfoundation.org/DataAccess/") + if (input.Name?.Namespace == "http://opcfoundation.org/DataAccess/") { if (input == ResultID.S_OK) return 0; @@ -568,7 +568,7 @@ public class Interop if (input == ResultID.Da.S_DATAQUEUEOVERFLOW) return 263172; } - else if (input.Name != null && input.Name.Namespace == "http://opcfoundation.org/ComplexData/") + else if (input.Name?.Namespace == "http://opcfoundation.org/ComplexData/") { if (input == ResultID.Cpx.E_TYPE_CHANGED) return -1073478649; @@ -581,7 +581,7 @@ public class Interop if (input == ResultID.Cpx.S_FILTER_NO_DATA) return 263179; } - else if (input.Name != null && input.Name.Namespace == "http://opcfoundation.org/HistoricalDataAccess/") + else if (input.Name?.Namespace == "http://opcfoundation.org/HistoricalDataAccess/") { if (input == ResultID.Hda.E_MAXEXCEEDED) return -1073475583; @@ -612,7 +612,7 @@ public class Interop if (input == ResultID.Hda.S_REPLACED) return 1074008079; } - if (input.Name != null && input.Name.Namespace == "http://opcfoundation.org/DataExchange/") + if (input.Name?.Namespace == "http://opcfoundation.org/DataExchange/") { if (input == ResultID.Dx.E_PERSISTING) return -1073477888; diff --git a/src/Plugin/ThingsGateway.Foundation.OpcDa/COM/Rcw/ItemProperty.cs b/src/Plugin/ThingsGateway.Foundation.OpcDa/COM/Rcw/ItemProperty.cs index 3218945bd..a229a3a26 100644 --- a/src/Plugin/ThingsGateway.Foundation.OpcDa/COM/Rcw/ItemProperty.cs +++ b/src/Plugin/ThingsGateway.Foundation.OpcDa/COM/Rcw/ItemProperty.cs @@ -499,7 +499,7 @@ public struct ResultID : ISerializable return string.Format("0x{0,0:X}", Code); } - public class Ae + public static class Ae { public static readonly ResultID E_BUSY = new ResultID("E_BUSY", "http://opcfoundation.org/AlarmAndEvents/"); public static readonly ResultID E_INVALIDBRANCHNAME = new ResultID("E_INVALIDBRANCHNAME", "http://opcfoundation.org/AlarmAndEvents/"); @@ -513,7 +513,7 @@ public struct ResultID : ISerializable public static readonly ResultID S_INVALIDMAXSIZE = new ResultID("S_INVALIDMAXSIZE", "http://opcfoundation.org/AlarmAndEvents/"); } - public class Cpx + public static class Cpx { public static readonly ResultID E_FILTER_DUPLICATE = new ResultID("E_FILTER_DUPLICATE", "http://opcfoundation.org/ComplexData/"); public static readonly ResultID E_FILTER_ERROR = new ResultID("E_FILTER_ERROR", "http://opcfoundation.org/ComplexData/"); @@ -522,7 +522,7 @@ public struct ResultID : ISerializable public static readonly ResultID S_FILTER_NO_DATA = new ResultID("S_FILTER_NO_DATA", "http://opcfoundation.org/ComplexData/"); } - public class Da + public static class Da { public static readonly ResultID E_BADTYPE = new ResultID("E_BADTYPE", "http://opcfoundation.org/DataAccess/"); public static readonly ResultID E_INVALID_FILTER = new ResultID("E_INVALID_FILTER", "http://opcfoundation.org/DataAccess/"); @@ -546,7 +546,7 @@ public struct ResultID : ISerializable public static readonly ResultID S_UNSUPPORTEDRATE = new ResultID("S_UNSUPPORTEDRATE", "http://opcfoundation.org/DataAccess/"); } - public class Dx + public static class Dx { public static readonly ResultID E_CONNECTIONS_EXIST = new ResultID("E_CONNECTIONS_EXIST", "http://opcfoundation.org/DataExchange/"); public static readonly ResultID E_DUPLICATE_NAME = new ResultID("E_DUPLICATE_NAME", "http://opcfoundation.org/DataExchange/"); @@ -599,7 +599,7 @@ public struct ResultID : ISerializable public static readonly ResultID S_TARGET_SUBSTITUTED = new ResultID("S_TARGET_SUBSTITUTED", "http://opcfoundation.org/DataExchange/"); } - public class Hda + public static class Hda { public static readonly ResultID E_DATAEXISTS = new ResultID("E_DATAEXISTS", "http://opcfoundation.org/HistoricalDataAccess/"); public static readonly ResultID E_INVALIDAGGREGATE = new ResultID("E_INVALIDAGGREGATE", "http://opcfoundation.org/HistoricalDataAccess/"); @@ -745,7 +745,7 @@ public class ItemProperty : ICloneable, IResult } } -public class Property +public static class Property { public static readonly PropertyID ACCESSRIGHTS = new PropertyID("accessRights", 5, "http://opcfoundation.org/DataAccess/"); public static readonly PropertyID ALARM_AREA_LIST = new PropertyID("alarmAreaList", 302, "http://opcfoundation.org/DataAccess/"); diff --git a/src/Plugin/ThingsGateway.Foundation.OpcUa/Utils/OpcUaUtils.cs b/src/Plugin/ThingsGateway.Foundation.OpcUa/Utils/OpcUaUtils.cs index 3de03384a..aaee07030 100644 --- a/src/Plugin/ThingsGateway.Foundation.OpcUa/Utils/OpcUaUtils.cs +++ b/src/Plugin/ThingsGateway.Foundation.OpcUa/Utils/OpcUaUtils.cs @@ -15,7 +15,7 @@ namespace ThingsGateway.Foundation.OpcUa; /// /// 辅助类 /// -public class OpcUaUtils +public static class OpcUaUtils { /// @@ -407,7 +407,7 @@ public class OpcUaUtils ReferenceDescriptionCollection references = await BrowseAsync(session, nodeToBrowse, throwOnError).ConfigureAwait(false); - while (references != null && references.Count > 0) + while (references?.Count > 0) { // should never be more than one supertype. supertypes.Add(references[0]); diff --git a/src/Plugin/ThingsGateway.Plugin.DB/QuestDB/QuestDBProducer.cs b/src/Plugin/ThingsGateway.Plugin.DB/QuestDB/QuestDBProducer.cs index 4744239b3..334536a08 100644 --- a/src/Plugin/ThingsGateway.Plugin.DB/QuestDB/QuestDBProducer.cs +++ b/src/Plugin/ThingsGateway.Plugin.DB/QuestDB/QuestDBProducer.cs @@ -56,8 +56,9 @@ public partial class QuestDBProducer : BusinessBaseWithCacheIntervalVariableMode public async Task> GetDBHistoryValuesAsync(DBHistoryValuePageInput input) { var data = await Query(input).ToListAsync().ConfigureAwait(false); - return data.Cast().ToList(); ; + return data.Cast().ToList(); } + protected override async Task InitChannelAsync(IChannel? channel, CancellationToken cancellationToken) { diff --git a/src/Plugin/ThingsGateway.Plugin.DB/ThingsGateway.Plugin.DB.csproj b/src/Plugin/ThingsGateway.Plugin.DB/ThingsGateway.Plugin.DB.csproj index 38c16b30e..5a9e3741f 100644 --- a/src/Plugin/ThingsGateway.Plugin.DB/ThingsGateway.Plugin.DB.csproj +++ b/src/Plugin/ThingsGateway.Plugin.DB/ThingsGateway.Plugin.DB.csproj @@ -19,7 +19,7 @@ - + diff --git a/src/Plugin/ThingsGateway.Plugin.Dlt645/Pages/Dlt645_2007AddressComponent.razor.cs b/src/Plugin/ThingsGateway.Plugin.Dlt645/Pages/Dlt645_2007AddressComponent.razor.cs index 6355fb9cb..9367cef54 100644 --- a/src/Plugin/ThingsGateway.Plugin.Dlt645/Pages/Dlt645_2007AddressComponent.razor.cs +++ b/src/Plugin/ThingsGateway.Plugin.Dlt645/Pages/Dlt645_2007AddressComponent.razor.cs @@ -52,8 +52,7 @@ public partial class Dlt645_2007AddressComponent : ComponentBase, IAddressUIBase Model = $"{ConverterConfig.ToString()}{Value?.ToString()}"; - if (ModelChanged != null) - ModelChanged.Invoke(Model); + ModelChanged?.Invoke(Model); if (OnCloseAsync != null) await OnCloseAsync(); } diff --git a/src/Plugin/ThingsGateway.Plugin.Dlt645/Pages/Dlt645_2007Master.razor.cs b/src/Plugin/ThingsGateway.Plugin.Dlt645/Pages/Dlt645_2007Master.razor.cs index c7ea6df94..926ea4b8e 100644 --- a/src/Plugin/ThingsGateway.Plugin.Dlt645/Pages/Dlt645_2007Master.razor.cs +++ b/src/Plugin/ThingsGateway.Plugin.Dlt645/Pages/Dlt645_2007Master.razor.cs @@ -61,10 +61,7 @@ public partial class Dlt645_2007Master : ComponentBase, IDisposable { {nameof(Dlt645_2007AddressComponent.ModelChanged), (string a) => { - if(DeviceComponent!=null) - { - DeviceComponent.SetRegisterAddress(a); - } + DeviceComponent?.SetRegisterAddress(a); }}, {nameof(Dlt645_2007AddressComponent.Model),address }, }); diff --git a/src/Plugin/ThingsGateway.Plugin.Modbus/Pages/ModbusAddressComponent.razor.cs b/src/Plugin/ThingsGateway.Plugin.Modbus/Pages/ModbusAddressComponent.razor.cs index 551ca4681..ceabbf2ab 100644 --- a/src/Plugin/ThingsGateway.Plugin.Modbus/Pages/ModbusAddressComponent.razor.cs +++ b/src/Plugin/ThingsGateway.Plugin.Modbus/Pages/ModbusAddressComponent.razor.cs @@ -52,8 +52,7 @@ public partial class ModbusAddressComponent : ComponentBase, IAddressUIBase Model = $"{ConverterConfig.ToString()}{Value?.ToString()}"; - if (ModelChanged != null) - ModelChanged.Invoke(Model); + ModelChanged?.Invoke(Model); if (OnCloseAsync != null) await OnCloseAsync(); } diff --git a/src/Plugin/ThingsGateway.Plugin.Modbus/Pages/ModbusMaster.razor.cs b/src/Plugin/ThingsGateway.Plugin.Modbus/Pages/ModbusMaster.razor.cs index e7e2dfae5..497ec9c3b 100644 --- a/src/Plugin/ThingsGateway.Plugin.Modbus/Pages/ModbusMaster.razor.cs +++ b/src/Plugin/ThingsGateway.Plugin.Modbus/Pages/ModbusMaster.razor.cs @@ -61,10 +61,7 @@ public partial class ModbusMaster : ComponentBase, IDisposable { {nameof(ModbusAddressComponent.ModelChanged), (string a) => { - if(DeviceComponent!=null) - { - DeviceComponent.SetRegisterAddress(a); - } + DeviceComponent?.SetRegisterAddress(a); }}, {nameof(ModbusAddressComponent.Model),address }, }); diff --git a/src/Plugin/ThingsGateway.Plugin.Modbus/Pages/ModbusSlave.razor.cs b/src/Plugin/ThingsGateway.Plugin.Modbus/Pages/ModbusSlave.razor.cs index a5cf6fd99..5de8a4d24 100644 --- a/src/Plugin/ThingsGateway.Plugin.Modbus/Pages/ModbusSlave.razor.cs +++ b/src/Plugin/ThingsGateway.Plugin.Modbus/Pages/ModbusSlave.razor.cs @@ -54,10 +54,7 @@ public partial class ModbusSlave : ComponentBase, IDisposable { {nameof(ModbusAddressComponent.ModelChanged), (string a) => { - if(DeviceComponent!=null) - { - DeviceComponent.SetRegisterAddress(a); - } + DeviceComponent?.SetRegisterAddress(a); }}, {nameof(ModbusAddressComponent.Model),address }, }); diff --git a/src/Plugin/ThingsGateway.Plugin.Mqtt/MqttServer/MqttServer.cs b/src/Plugin/ThingsGateway.Plugin.Mqtt/MqttServer/MqttServer.cs index 93d8e3cc7..e7ea0b75b 100644 --- a/src/Plugin/ThingsGateway.Plugin.Mqtt/MqttServer/MqttServer.cs +++ b/src/Plugin/ThingsGateway.Plugin.Mqtt/MqttServer/MqttServer.cs @@ -79,10 +79,7 @@ public partial class MqttServer : BusinessBaseWithCacheIntervalScript 0) + if (data?.Count > 0) { if (isAll) child.Children = await PopulateBranchAsync((NodeId)target.NodeId); diff --git a/src/Plugin/ThingsGateway.Plugin.SiemensS7/Pages/SiemensS7AddressComponent.razor.cs b/src/Plugin/ThingsGateway.Plugin.SiemensS7/Pages/SiemensS7AddressComponent.razor.cs index 0c5c541db..66f3c1473 100644 --- a/src/Plugin/ThingsGateway.Plugin.SiemensS7/Pages/SiemensS7AddressComponent.razor.cs +++ b/src/Plugin/ThingsGateway.Plugin.SiemensS7/Pages/SiemensS7AddressComponent.razor.cs @@ -52,8 +52,7 @@ public partial class SiemensS7AddressComponent : ComponentBase, IAddressUIBase Model = $"{ConverterConfig.ToString()}{Value?.ToString()}"; - if (ModelChanged != null) - ModelChanged.Invoke(Model); + ModelChanged?.Invoke(Model); if (OnCloseAsync != null) await OnCloseAsync(); } diff --git a/src/Plugin/ThingsGateway.Plugin.SiemensS7/Pages/SiemensS7Master.razor.cs b/src/Plugin/ThingsGateway.Plugin.SiemensS7/Pages/SiemensS7Master.razor.cs index aa31b8980..291e0b144 100644 --- a/src/Plugin/ThingsGateway.Plugin.SiemensS7/Pages/SiemensS7Master.razor.cs +++ b/src/Plugin/ThingsGateway.Plugin.SiemensS7/Pages/SiemensS7Master.razor.cs @@ -61,10 +61,7 @@ public partial class SiemensS7Master : ComponentBase, IDisposable { {nameof(SiemensS7AddressComponent.ModelChanged), (string a) => { - if(DeviceComponent!=null) - { - DeviceComponent.SetRegisterAddress(a); - } + DeviceComponent?.SetRegisterAddress(a); }}, {nameof(SiemensS7AddressComponent.Model),address }, }); diff --git a/src/Upgrade/ThingsGateway.Upgrade/Services/FileConst.cs b/src/Upgrade/ThingsGateway.Upgrade/Services/FileConst.cs index ae69cf94c..4ba15a29a 100644 --- a/src/Upgrade/ThingsGateway.Upgrade/Services/FileConst.cs +++ b/src/Upgrade/ThingsGateway.Upgrade/Services/FileConst.cs @@ -1,6 +1,6 @@ namespace ThingsGateway.Upgrade; -public class FileConst +public static class FileConst { public const string FilePathKey = "Path"; public static string UpgradePath = Path.Combine(AppContext.BaseDirectory, "Upgrade.zip"); diff --git a/src/Upgrade/ThingsGateway.UpgradeServer/GlobalUsings.cs b/src/Upgrade/ThingsGateway.UpgradeServer/GlobalUsings.cs index 4df1dc079..57835fe8b 100644 --- a/src/Upgrade/ThingsGateway.UpgradeServer/GlobalUsings.cs +++ b/src/Upgrade/ThingsGateway.UpgradeServer/GlobalUsings.cs @@ -1,15 +1,4 @@ -// ------------------------------------------------------------------------ -// 版权信息 -// 版权归百小僧及百签科技(广东)有限公司所有。 -// 所有权利保留。 -// 官方网站:https://baiqian.com -// -// 许可证信息 -// 项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。 -// 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。 -// ------------------------------------------------------------------------ - -global using Mapster; +global using Mapster; global using ThingsGateway.Admin.Application; global using ThingsGateway.Admin.Razor; diff --git a/src/Version.props b/src/Version.props index 15b7aa980..6bc06afe3 100644 --- a/src/Version.props +++ b/src/Version.props @@ -1,6 +1,6 @@ - 10.7.21 + 10.7.22

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�jINv&l}ByRP&6OyM%U3tB!3bR`<%{-%Ar3M;28XGEn@ zGt>`FM;p*t^b&=Y*OfD)a;Oa&h8CiI=q7Sj(Djm{0;m=G1?@tQk^DhL?4B%+I-=?5 zAbO3yuB6A7Lp{(0v>fe6f1wX3va+t18I?v&(9h^sv>F{ow~(ibuAByyMIF#Yv=LoF zj;gv!3RDWUMPt!AbOF6aQPp(4+^8n%fu^D@=nDFTl2q6A3ZaImADV-9qdO>G4P7M@ zs*Jj!sc1X8fdXslD(O&p)Co;Oo6$Aos->%>Lf@lyXgpevE}_rpo7%cw3Dg=5L(9-{ z^a4fJ(N(@hbx?0K8|_7RQM|gkN=8%>wL!ztVsrrAMS=Bny_BdJYJ&Qs*=Q%ajy|K9 z`nqynR2%(-rlQU05_*qf8c+|_Mn9pcXfwKkTn%-VG^jl4f~KOK=nhKINLR^*YNFm~ z9y){`qR_^=N={T8^+EH{LG%PgG|^SQMfFgBv=ALZ4^V=px=JQg5p_Zn(Ry?Ny+?_f z>3RiGL)0J5L%Yxo|Q=Dx+>_D%y^2pupC;N;*^?bwZQSW^@g?+UP3DP!ZG=4MvO5 zA@m3Zx7GEspvtHVnuIo@OXwr|<_BG`6l#YiqRr?}6xdFW&44PS?q~+uiyokO?RAw* zs50t?rlRfW1`6z;tE5BaQ71GBZARBnXh&V81nPy>pnE7uCq1Gn8jg0NHz-4AJ)$w1 zjEW>zoW9Su% z`B7K-4mC$3(MogndeXCo~1^M0Zhe4?Q*~s*47oC1@AAg8oL~Kk0fIQ7O~} z^+r?BI&>U8Kmk2<$sEE2lxl zQDf8x{f0K7bLb@s?WZeeMBk$pXds%04xmRUw7;&G1Jy*m&@8kU-9rfm=qg!IRn#3# zKx@!R^bCaz)b%oRn=z3|<_oxjTiI$^d=qU;vsq1A$l~HFj5v@h%&^wf9l&)6@HAaKb zB6Ju%L1CkHl^m!B`U(Al)}hnr1qvIZ>t#U|P&+gVEklRV1N7BcT`vtPh1#OAXdSwM zKB8pfbiHDzB^rrVp)=?$`qE$SCK|6R7ew_@Pc#zEM(fdG^e1|a;!V)YNQ!cx(x^V_ zjE17=Xa(AXE}+NAHBm1&8f8F*QFYV?^+ms+C1?jaj&7pAQT$)@8YD&8QAtz>wMPTc zuV^XSfli~>DB&c%+`^~@8im%Pi^%v@k4=fnppNKQv<=-vf1{Aex?XBj2-QIC(GWBf zZ9vD-U1Utr%ZNsqQ7P06^+z+&R&)`)K@n5+GP0m@s5Kgb=Am8aJbI4ePt%oCqC%(^ z>WoIAg=iPLj9#OV-}ExlqT;9-8ieMfedsm{n6B%kM8#1vGziT{`_OIl9!1R1l{28? zs6P4;jYW&mZgd&FLP0b2GE$*Js4n^$%|JWQpU62&*ZT$)MNQElv;gf#_t00fb-gsG z3~GnQp>^mydXEy%(e=JVP0(Pp7#%@Rk!P;1k{IPe6;Mmm4^2gD(GheTeMZsq^wP4R z(x?gQhi0N}=rVeTqUY<%IZ+kV5sg91(P4BSeL|57bmfewII4%bqETo*+J?@e$0%T- zUPfY+6IDR1&;T?YZ9pf{Llm$`FC!_+gQ}n(&=52WZ9=EeL*!YkmysCdLKRR;)DKNX zYta$(2qjpemyrRLMQzbYvM&1KmdvD|D3ts0A8_wxHW6WThUP2Q@*X&<6Au@~qNh)1%6$2bzWU zqbDeGwXTvMH9^DCYIFe^YxLM8C=aTMengYd@8}GAiNe?F%2`oG)E$5`E2lsOP<7NE4MnrjW^@`oLIIofGQL3tP+im$O+}l~ z1@t$H+N>+*L{(8|G#;%+C(sKNe~Ye{4CO(UQ5!T6O-Jj|adZzkx9VlYpzNqDYKD5E z`DiOTk6xglZF(7LP*GGL^*|HRN^}U_Mvm>eazd09l|ju=A2bE6MMu$HFAMHe!(P!k}qnA+#wL%lnHgp$-?bT!R zp=M|d+JtVRpnZC5Zqx|%MRU;~=srsDhpv(tRYqOWWV8icL(cuWN(xjGwMOI6Msyto z9?(@XqN=DTnu89aXDIrhu2K*+L*vmN^a`asq{miAL(oQaAH^KjBg&v&XgRuuLXPMW z`A{1)4IM@wQ2L{KY#lTLZ9xxF!ee?w8PpRkMMu#!^b*BAuB#+Q*-%MT7j;B~(KNIQ z?L(K)Q}hAFKcUwkAxe+(p)#lrYJ+;B5oj7(ingG`=q55wvMwk$s)E|15ojUWgRY|w zDEgFMMs`#I)kE!3KQsZ&M;p*VbQwKH#%aCWFq9nSL?uyu)B{aKE72iz8#&JCr6okU zP$kqB4MB6zHgpL+L-Ee)Wu!m_QBBkdjYJF3PIL*qLc!3P;WFB?L`k!*aclB7pjapq48)9I)z@K@Qb=$HdGmP zLgUdIbPByd@h<6lsZbGAAN_&oAtf~Yp?hDM-8Xg9io-Xq@?y^LI_ zD(ZyBq7~=}dVu0y)%8-LVyH3df(E0>XbIYij-fx%3*@<`mm7uBpuDIos*5_HfoL*X zhIXPe=pOorLa*y}NrApaWl=-)BN~q8q21_D^cf}kQ!gzqs)@RxiD(Tvfu5iQf9ZPZ zP*Kzn{fwrf4d@Jdj)HIK%IQ%l)C~1UGtpLb9=$=4H+AJ4s0!+Y#-UZ{IC_GDZs~d% zP#M%3^+z+%W^@j{K*6_l<#eb7YK(fLsc1bqg`S|uJG%0Bs12HgcA-Zo`mP>Z1pR;} zqdn*e^54^Ai=j?v9y*8O-q$1YqPA!TI)Pjd^oSg&Ihu?Pp^qrlLp`=C>W@~TYbe1Z zJt8k^i6)`F=mkplSdT4_dZESW5{myskI0XHKr_&BN&gXBMK68pD7`B4MZAI(Px&_fjb zN>|B>s-g~PELw$5pl2xbwXT;1RYV=oShNbAK+jO<8(l9gDuJ4yzGynyjLxH1DC}=t zITI>_TB1Q{4%&wvp@_G-US8A)4Mi)_IrIsoc&DqBL7mVPv=iM$;qUd>0;naLfVQLi z$ooN$EreR5U(inU5Ji2|W4}f9(Ll5Woj`9;qEEU?5!4cmM(fZeqL(5;t|92G;&&|tI>9YFU{Jo%5wTK~RddQ=wu zfJUR0=mffo-lDJoT{#mfgIc0NXb#$euAp})Do|I>jw+%b&~UU6?L~i~&nRIWUHMy7 z4Ru0e&@yxo-9hfSy585Q5UPiIqA6$tI*VSSu&;FG%%~h{gNC75XbU=ro}&cubmi2j z2&#v=qX}pQI*4u|BfhSj7!^W|&;T?a?ML@f`~je4NP=pyvK>V_7hD=5UP#}+|d(IRvi1xM-;g;5u@ z5M4q+QF=sf)C^5PyUVX!bv&ik!BeJ3fXcStCj-sb1%&)6tN7c}e zXbRebt|B8wSNR$hMa|Gyv;#dsi4*E7Wl>MG1YJaN6X_ATQFHVQ+KXPIw2AfDCTKc3 zkHV7Z`O2U{Xa{zC0?kER&}sA#xl`z6Bt?Z#1Jno2L_5%RWTd1XDuf!K zK4>P|fo`JssdT;Us16!{mZH<>14^D+S1E%!qiJXldWa&^=&>nLanuTpLaWhP^bRFX ztLqg;&CoEk9Gyh3kw2ZTk`FaPL(p<`8ofhF)9Wh5Q5!T4Z9rF%JA)pZ7JY~6pl)a! zT88$c8|V}AWz@^aj>@A}Xds%2en-dAedNxhD7pHQN#x^g~L8}&ew(FSx5y++<_x?Xlv8FfUH z&<=DDg=N=O@}eeaB>Ej)LveEGu~|?p)E_NHd(d_C0Y&H3^|GT1s0|u|=As?wGWr`u zsp z2R%m7dG*-Bs12HkcA+OIVLm;!6zYoRpyTKxN}FGgt&RquRp=@TD4<7VMfK1y^gH?s z1r^j|^PpyE9NLENqsT&fY+=+2{epI($H@Pk9$OrBMAOkh^csC#SdT4-dZ77eH@b;D zMfBK|s03<(hN8vj5PFCb6xH=IqVlLc8j9wl-RL^{i2TKLVQU}C1^jog`CB8 zy+kMU6^%iQ&^~kn8D(|lL?{ocjyj{UXgNBB?jlb)UHNNN7&Sw~&~kJVy+;1>x?Ta) z2n|Au(NXjqMOM&NGNRI`8S00oqfO{6dX9oB>dI+RanuO?jHaL!Xb-x89wS#Jy^Ls- z0To8oQ5)13{eqUD9q0mjj^b6;%T12*qiU!f8iHn_P3RPQh&)yFGJGftDuo)N?r1Dp zgm$6}=oyMzRWB_W`W970%}{UjD_VsPq8sP~@>bJJ%ZNB!qbY`A}ul4D~`2&=Rx@T|iG!U>&`*#3%o>g4becf1f4;jQJVU?N@dgwEkGyGdz7Mq9$OxD zM|02-bQc9S)MHbkVyFq~k7lEt=sNn05;xM7^P@WGCo~Q1M7L4A#=1&oR2B6=GteG% z4+S;RRkEQPs27@r4xneq*Hl;e4z)yM(MI$qirY+&&4Oy8zGxBp1KmUMn(HbVPWM61y`^Z_O7sH+r5tWgNh-RLIrbkS8(q7tYD8j2R9L+Bw&(3N_q2x@?Op($uRI*p#8pl-Ty zI#d!hMg7nWv<>}<+&}7isZeRu7L7q`&{^~rCG4*26-15DKr|C=M_18Dl(2`cmlxGS z-O;aTJvxhCp@^S!y=>@v)ExCkGtg#q4!uCZJ$2=Ds03<^dZVdmJvxWpqa?j_niC`8PpbyLMza5^bCdd(e<*SDyR#Ziguzq zC_!JUpqi*RnuiXdXDF(lu96=$Mn9uD=rDSP68G0tN}%>=GTMb6pojr_Y<|=XjYb>L zEfhIWk1c_IM2pZx6mO6okq@;+)6p?x4AvvEphjo}+Jx?+$RT=cQPdI5L`TsFly;~d zTLTS3YtdimJ&GQt$L2tlP&+gNEkgUy4P*@0^%9{xs5+W`++*yX?lKIcv-B06p|o83 z4w!wtq_3>m*Yl*usAy`G^wl;sRn*wj0#Qp-LkGExAI-5_q|ee$Q5CcAOaHRl8JTMR zhUFbfJVN)CMxD_#v===_VI%d}Y^WCMhnAr8=p#xwN>{0fI-^DCB8op+kI0MKpo!=p z`hZf5(POKlVQ4M7fx^e?5&2OIG#TwjFHrJvdTd2B1g$_P&@+*BEIs1`jrQh|?wr(! z?Hi9mWNKG)Y6?+LQ`t}fQ9rYa4vIU*ynx66?nWj35tUcLVWG#1y$XdTCXgTw35Y0Eo9uX}ybx~xEy)Ux1_cd}&)KkMy zQj`l7K&4O>kyWoTeVs+C&E@{*qxH0qVQetxv+I3nd%tu%`pPn_{W2WQLaRj9TJ7gt z+b4b25j-Q>W-j-p$l5PYMOMAIzi1^B9X997DmrN@FRFkViq4x;XI?jqtERe2-wjj! zMONieXrAbvIdz5Tk*PhR7pBgN-kQ26vexgN=(E}9niOcnnW?V3YH6d)^E83<)i4z$ zvd)d@n(o-KyN`H`Aag__nQE;|9%Qfm==6pWZq9c;iDCFmm8YIP)t0>mc3(Y4v|t%s zMM=zheMKou4LIS6l|453dLy<^Rkqe(C{?QcWW;Wpx~ilzt4x%s)*iCAcaHRBF{dsU zuYT2QW!LjbpH(Fl$|EXo zjwr&2vZAVH-@4YGSk;ie`evWnFV^-pW<-0DbspII->KYFDmOKkYwN@teVw#*b)a6W z+TUo6VEz8{RCUa)Et1ZDMzn#zClmFX$W38-9Yjbbdd-s25+w66Iz0PBFFqdJU z2lmn5A#0G;yb9P;?ISa$sbO^c$5~>JRa1NY+o=bna({EGy)O2-R-=o@82XQ~_I!;w zM)uOw>*Z*3gnbs<^}aOLKAWyl@14kc&IC=?`qHbxtKs?_+I&Fk07@idYnWHZOjOB> zz86h2*R-ipAnKwYRtJOZ&>cPUe()2h8P`6dg15 zsiemWW=M+9P^v>X_)Wsp}%o zEcN>KpReOzdcCx-==N3qOV@V$)%30`%{s%rv_KPxvdM||2Y&r4J8 z9a?|M2y3}tTECRj0*&VXSlXAWEE9+6wr%dFQ@)Zf%(rk+FbXXp|37TJ4J?R#qvDOuZ5@CzgC*Z6-o z*4mT*@5>l!u4!pm#yoS&XNks`eeYOqpP6#CHT%Ykth?*UqFH8N{^~|--z=uiL-v|3 zmcB*ih?SyMrtI~z=QCgZ%&GRaZIBT)%=NSP#yfE)~RLytD?0Y9uPnfdT*)vNY)i0g9_A{XO*ZOS#(krog z-8yGh4wHJ8V$j#fe#KVXaorrD-Y;0Mbm?V2>lo!hB}EU+`Kq8gsENp0S{F1(WK|iD zW{O^#RpyJVXUYzdb$vNb-z~ID_WdVw#53j#m>n3~7cH`?WEEL!pI2l(&kKpHZR?Oq z@3m6WXRX1BDz4ZO6&YI#HAUS-)-r~PtTp}8mD)Z=zsh`ZXRFs$`?bd&tB#RXPwo3) zbH4v?d)%s`_PC`l?eW#JF4iYDYTsMecC}Bv=5p1Zw50ZKQnODT4@>qjviI|sj?w?z zQYf_bL>V@ z4fE==AF2CJ>p62;`Z}2NT}4kt-ORo?bG4G8T&SF=uQ^}KWiDg5shX(7O5OJ@y4X?o z)t9kj%@IF{V$UJbWV3HPnkTZ>Pu=fY>#XjEXPEP;d-=tt*2sL;R_+s7^*XoG_w85d zdyX9Q0*!U%Sg&ZCsk9=iN=7MgK5Oh? zG+A`soNpQ0E3)d@%RM7~SIznEiEf(uD6+OZ+5AA`zS);nWUamZZX&PrSzD>zby(NQ zQqpIg&ox9(&9U}tlzo;old0D6=qj?7_A?rc#-Oa}^gW^d&c;6f)E)P0b7|^5lVgs$ za;bZC>-efW^Y~_;y5qKvbj4LJBg*WXBg?R~T9nl6`_lPhpC$I0vr|T-HAj5u&fI>- zV!xBKucu#nFJ!-mu-{?)_wN_%cLetPfd8F)e*4{oefMv_udv?-sB4(@-OfeXLzeD{ zvYY$LzDoVy-rw7IDE6KHm+mqg3-lGvzN5A8mh5|A`_A`E_q%o#_4;O=ssyVg1$U5(Ph-#UA)6tjK%3iM88->hk@vz;l*!9~Z zV;h)N4vDO@_@u~s#$Ob*G^g74Yjzd&+G8D=zhu77=6tV2JxqN>@fYeoKT0kdV9xg~ zDvjzgpS_F;vQMo$)y^_?q&c>S$m$z#Sx+5E-#F1Yb3S#|`_+`XE0}94S5Ez1ku6na zu{m|BjJ2*v_LeV{zUAiBbt3C1*kgA}-&%9(A*P;1_I$SNDt2Xi4eaIG>tes^K9tJV z`n6nb7@N%HK9jyZrrxn!yRY4DSM1c$@_p!0bG{z*#a*Pg$gUhIeV5Jo)E)9)=&mVy z8Mf59VXc+j_a&u}%GP{pU95LDYF+-h$1^kEx1z`9a_#-{-%tJ0k+IJgTVMK4xRk7c z^_{T&ue&#=uMe7nHlu^+I(m-cF4mP3qfDp_YKXd`F=z?efi9w_$hkx>Edwfos-sTm z7qku?Lw8ZwQe8O(%7ZGRrl=bliI$>$=qCD%{LA#xvZ6|;H5!C|L5tB|bW`-wJj3pb z-dX2bd3}vgS9H%@b$`&%XBb1wt7z{V`fRT(&q3?l{dKNk#53nh&RDN}H)c(JCY3EY zmurQJLd_9T$UeuDNT1J~np*UYsjQ+jrt*j~nJOj9VX6jdAu43{*=uUAeHWQp+??81 zRL0aOQ6*EM-DFEmO_aVirh?`hMo&{Sq|dtkEk^b_Z<4;j=6riZV@;`VX(P;U6%PJv z7}LzYM5pwAIVtnaHv4XftUdHpwAAc#tk75aXwho3FC%>gL|e_i*79xNee=jvps%55 zpE@s4U zS+5*tqJ8bOkDGms{m)bX|Ga_~m36W9%kT1wo}=bkg~D6|TlM2}G1)w*(eR17sh zKOuD=mfl?FnbKz+!E&wiakI~%Hk@G_WIpRy?nWojB~ezhp8bj39j3k#S@(6GHM+{C zf$rFP@ukmteuRsxbxDZQFd{eC7kjPj=hm0DLv3YlbFGvLn948pikh;ww=zpp^-7y( zvH9t~Df?G6?Pb1l)T?3YKhM`)Mp)mz+Es>1pS34vAiJ_%Z-GpWeQqK9tlLDNeLPeZ zYd_ojbiee~GuPBUJMH_#pAZ1hDR!JJyWq0882 zs*{Y^W2(F8pegmf>^M4a${t}?`O;Oue*X5Ade$>}3R;g&qjxA`ogR@3)k5t>*UfeL z(s$8aWvX=^*uRjpkAnI&!R>!LcC1_pt+A6)e);vpLvw_E%zLv8r`-DlnXl|4>086t z|2)F3Y=3I{rS&@_-?LlyNLSEJ(Q9+9#>yI4dPrZyL4D^od8L+prP(R-SzGxl>)b%9 zSbcApYVU&#GWCPGw0SZjV7_|&-7X3>HAI#XV@kanOJ*uqK6$e0g|auAHqw0^P&<~Z z_M|o9$ttg8#mkE*>cN7-_F?Y~snKEu>eu$HEdf+e-@ zEvbEPNgWwW|NT+0_xP8N$1PdsoaTN}`@VoFb!1AK`pDFX-{n5m?E6||jhH;oFzT3n zxuwr~22>VVXHG{n0sStr&bl+`g_2ptw_cwIrA5}ck(#|>AFV{v*UTKD&TQ*y`&3?C z+M9j$vqv5Cu4bS8tWaaEZ{qCv?5Xy1+Lo%^*Qz2{*1@K}^tz?y8*5IrUtRw5>&KT~ zHSE`lF0v1-V?GF}WlS=cF>CI*pdtZgieAYUveY)40Dt(r6G4*?q^%_+bwM1P+))sw`^FH>| zD5g$BOVL+yZ8~l)ZH@F@GW9>7dH=jV|M%CwFRj&o|7`rybI`t83}B1?^H$o=Y5R(4 zUr+6QU|(5Vjn?0t^p;;c+%or&eKk{e@DI$sPI9+t9T|J-ZMoV$GpGLN`RuX#WINvc zV;S~T`xEOg9V7c}Izg2i$X+YEp1nnHWWJC8cDZY0+m@SWyIO;_rVfd$`-pqUeN^`) zM7dE#)ExCk)6i;k0zEyEB*`?Aq%x8uNk)<+Ns^2tNxI&9E&KPo&;I*z`QPVx zU;pEF9A3w#?|NQ)t>0dcdqnCjQn^SIM0!D_cSYJJ(m|2ZI3J9b?HrLV6{)vKWg^Af z)971QU!2&JtjY1ox!S=H z8R4)$kg~!QKYEB#{8^@G?&X@4=O6LbJ0~0>Sy$Pg>?%JknSN)wQ7a#RTBJ!hKJyJr zX&xroUoC3U=awT3SM3l}DskKNQM7omn*AcR) z-6_(8BFSmUy<1L0PWK6hC=KWDzduAtHo0`X>HF)P5hI zID6VIYVob4Je-%mAMG~q;(tpMejEbcKZU3! z&jr?6Pn{clF{yNh0^cP*BlKa9&YSEgMt%^MMk;^8J!;&3UC0j!~81s+eT>V6f zj*OBWU&%2mZ&}K3;Shg6U3o+%=U&cpe7f!n@nhDj*?*&DtIe3BB#$Ehex1gbC^a0* zsSF!^3av=dw|)HBCq3+1NG&5w3;DJ``b?xJU8AG*AtF5~(gKk_5NW4K+Oov3DI#Ty zlrK^jkxE1wA<{IFJ`kx!q!ZsxOryO>r6P?NX}(AsMEYJNYk6WU@(#?&qSjQTVv%kX zX|PD6MS4r5k45@gq(4Qfy&^H)Gev49lDzZNS=4S2sZ6ACA}tZ=Gm(B0>DZNtshuHG zOOd*Zbh}97MVc?tMB*-#H?+73n6ChKn>)q<2L6O{9!fiSb=3(hVY&i8M*13XwL5v{R%% zL^|oc#F#G-sYs-|M0!r76(W5tlD0ZAmU<$!6{%FD$3&99)c=yGRf)7wq@P6c-%m^< zQ>3OM6^nF(NTni;6G`4TmG>Z@7hT^LX`@Jgh?Ko1F$eN)t~@W3Ph*@*e^J{hk8_Hs zMQ3u!uBdmX>seu&SVlTGOb13Ae?3*#f%hugh1Y4eJfEmtBWedlZGfny&QA1qzo_Mk z+LNLtr!glsK59&6b&0cbB$^z zi}B6)E>XK|a$*`eVqThw{_;glPFD_lzF1eCM1L2rPfWKpDVnaVrM{M!S`$9YqnGG< zx<<7-|45AGWVVf{7QgoyEm3^$iE8b{8oxw*GV%(>W9${@hkJ$Gqb5;n9oA-%3XbY3 z`;$j_^XV_Tz9CcmOgK7LS;zIiXs>;X{-PwG1CZx4@*N3zH96Ul%)5*wI+EEglJY^K zmLXDsNc}|`E7Hp%eJ+wb-jz>X{Yls8*)4CarF0J0#tEceVag&64AWVp@-Vd&=~B|j zuonMh#)at$YE#0ri2FU!^I6VA-qv6J1^CCah+K=(BLfAEaZnZW{FEVU(m{yRsh3PYq_L6po zHRD6B*3O9^`JOPUFKyW06u`28uA78YU$gcGnVr|$phqNI~at`FO#h;ao_9L0%`^Tnm2-)8SjAd(B>q6QQ zCi&b~A8Nb9+C8M2Fij#I2-6~w+R}uIpE21XPM7|WYeIliN zl9<}*B3&fXH6jfb>F>{79vA)15NU--pNn)vr1Xu6IcOtNFOlvQ=^2sU7U^e^Qa(+L zO+JSMunDAH| zNU33xPXX8ZEYVVDkfLkS=a8bO2|AL}!y$T*qHS013%61`dS9sEI#RRn*)2Kjee{rRpOk(z{E4~bg*8fY|Er))}$IZLFbr0D8p{Qgmt zHgTsvYAr6NYm}}fMSI@uV%T9K)$luQ_lM_T6GeYBNG-#8iC<654^#YY^1?9vYVihb zm^@oit_{-yhUgcjcSzB(=oV76Hg=Ip!>+%Rq87>eJkhl_DeAg@2ES|+zFjE$lb=Vj zwn?rlxrQ1uzOry?9mO=_%W;2Lliw9x=o&pMeuGE@Nm0A@04W;FMADdW*yluAM2eQ} zUDA_b*Uw1Nnv~C2@1z#3jXz1xg#F2OH`vVmqwx1tHoc?#^%QFfM^(>-{mJ%T9v_^< z5K-%RmPi+fbeTwU8ghB#-&@hTznT7`xtCqzYv{#ry2;Mt|MfY%=)KmzYL<8KA7I(u4wplIOOB%!eTz>QOD;bTc{ltu z(N*3TPxj6E4*f;j#%7TYh;;lHiLQ-BDiEocNOy}gK_vNB)ND~pw*9>$x^5BaXOXNe zi8+wlX{M;jwobm~62JBzy#sR|{Y6`>d>i2sQIqei{Oc1W(UG7$avNHaXcOb>IeG>+ z*_QaPckH5l=6~ur+W0-vcf&0weh2HLFy-((+~;AEcRb^FP`?Rl@jIS-!xX>Mx<5?a z#k%@G@7>Y2hJ4Ca-W~aW_f%|j9Q%KFoyvQQ$)3oQ>o3{bm1{EIHbt%X|9LC7a-mOW0S#iZ!d-%5(MwUfTI{@T_$lAqdOw$Y_f)z@^Yr~t^4*at(KXrq)&CoJReuq4aKcxKrOXvc-uvt*YW+mIN2I4kS}f9+ zBK;xKiQ5v>$P-C^CtodUr6NrbsY0Z6BGrf_?{CSwTD87TOfA`6D0#;ze$^$~n&gvH z@>;=9T&?I3ZY9Z{mv6*0io>prNxi}(j~V3AS97}F5_T;lMeWNCBFUCZ-s#E^&+8c? z$up&LhP^!;-y~9WMdZc!_`;feX0b|4<2zE+avc&$PFG%Qk^5zx?TH~8inMEH;#yE^ zQCmSRdOKLoy_`QezDD9nN;yOkV~LK;<(T`4F_(!X$1IPf9;0jY-8fOCr%BPfxpTy@ zi$q!`lANpes72pGva9^o{e-U3r?rK2Z@6|3k{$|E{O#|@!zAY_{=QoDE}lGll2_2= zxrX*lV$8Kk(I*rC=8WcEj_*X#ReoB@uJp#|>gjM^cOyQGd6Knq1Grs6|VZY+mF$UGaA$-Uz1_KemlhvUif?8%zI> zC&f52kEZdj$5+u<;>)%y9CI#jC9e+CSmtU&m?n|F3X^<~KK{8y*KXu?Ah(Sd#1h5F z9BnVjzAF~dU-aGhp-4ML(sm@eW{Y&ONLPwfB2s+1(RF3HL=Vz6x?AulDcU;a_8h;T z_kFl*Tzx5iB={Y*X#VzzBwJhgbf7#M*1t{6RV|VkjwODN$`6yA7dd}& zp5=1Hufa!iFRzEqq!o?kC5*cyWJjLxm(o)Ujn^0;sv$fq^snoRaKrF?Ev zzPlu!`IP&Se6LczLn+UcWD6qaU>5V79?r``Qbw5M_7@)_dIDRXvB-C!bR~ zeD6~}11XnR?&orO<#~WSJC@H`%6CiUJEQ+}$&>BfE5zrvMWjDP%HEmyRId=}E|JEF zR4LL|BBgwn7&c#|8$=o@(hDNJE7CV2ow_SAmjBGwB)1p2Cgs*7x3GV@PDia!{(7rN zKJn4^o&S2(N-P8R7bk=lxMjYwr8 zJuXtRw&YpS^;MB%`?5~d)*FdA_);X<`ur^VGxj9%EBK`ee-}w8#(vj@DtSMtI2=|_1()D3F`vIU+&E&4wHcfQ$WTf9KDD*u^QS$@YaW=1*IT{^X#GfXO()l7Cp}**C=kxm#XUsFJ65rxMO=4c+*FdAK zq>iY0P54EnaIQ|H7IkeRy0#IiNF@2zrF_>i{{CgOlyZ6HR$RjU)wba@ZeyOKB#!`Y zWX#dFQA)Zr>~9e%`fckiq-d^WE&b<2S6P$q=*YcxC`Ve+u<`3E(Ve(t<4acS%rv4; zZ~!S9%Lr2R`$&^XmxXis3hBx)C7b(yT1z$u|8&@Y+Mj&hcDY!hS9td0nsDydh}yRz z9VSKFTKsQ2MSo8&*)Pp|`x5Kw1X8rFlJ(bJbR9^F#(b|B_7PI_JNZ*d*N1bpWJcmQ z;^$MlDXhi+PDu1uI#y7No*|XnL9*H}^cVeY4%y$oQ)~STS7yU``Ol2+pUz9NH;dy> z2S$HY$k0sh` zza~Z7!9J0+{fSx%>5*_O@oSM$I!)B#S0JNW{Q6{+&J$hZS0$sG{0-e?znm+(%3s*c zXDrb*Q2A@R$!dl4_xMq3BYxdBYGbSD`efKO*7s*Cm#RbGnxPR)tCaO2H;-(O$Nl z6#YWB{Qah6zwRV|)9K&&)uw-E*c#?^eYhOIld8j1|2KZoHB6U~qF*wRpH{NBNaS1b za$SYncsQ1kj75Dpeq1+(6zvP~Rw`Q0Pf$C0jm!7I*U&Y3cRks>B>PmqUp2wdPl3!to{B+LO&evN3OGx>1WH*G97b>i?ejjStyh{I5he3FqKa z-inHr{6_kV(g0F)7ex-Sl-fl{t%GF!C99osh~GYro;?@)WwQR{yBYG?GI_mDc9m!J zvTHm=cNLysYL|p_8n1NdC_o&@+RIT1&j(Co$CHvfx)sju4KjXVA9AB~_{?oN&TV1j>lx(^s%xU>Q zSaNx$Deuh7d*J`+Yc2not3oSTw&CHjRg)e+YAM_OnfP`|*0mE|pALt(mK0qZiMPcw zj;i&e>kCJXd7T)uJd2iXj=Vo8&y)Y1A--U0FCR4ra%yWIOPskTYi;BE;A`Qq^^RD| z8(~Ve<(x&=s6W~Ilv8`>sOcu#!v5=VyRA%0Rh46vCs9>mil*4g=o54*k6^rJP`NqH zq|zT%h2I{ha!Z;`WdN%D%i;f9(_AV8F+M(z%AaXI=?JPyT_s@bry!5mpLh)Q7^+G= zr55%2Xe(c(A4`23s!E1ZoBF9}E3ei+j`~7Wl?F-*_0!N+eye{x^@FG?nMx}4)6rH= z&NzX3T~w7UC5?JRw3SmbPNZHBRi%+qhx!?4D@`*_qTUQuC0j|SekR&VZsy6<&p}nm zVF_Z&S!gRAGf$yjh^o?0Xt zz`WZ^|I9O~+=8moin->kY-C-XeirqEs48uk>zHy8+RE>zpH2M`s!Bd{9aGw(t<=jp zhkAWfm3GW^Ot~0sB_r!x>ZhWrT)|x1N_N(HRL(?I>7q2H&nwYZa7`GQ5lV@@)S!GQ>LJ;Jkqc&^)aX_PqQ>JWh&arH;pc)z5`X|8Kph- zX=p3oHoAoRPE?g=l>+M1(N?}|bSd>+s48zN9jGrwTlv0GN9wy#Ro+qxsbrneiAqCM zm1Rm1m7Fs!qjDCi%G*jYmBwdWPUUP=mE}rjDn(~pLFF=3l@+W@TPZ%{N-CG5s_am@ zQaL5}Dk}9*Rdy@gn1>(GR!+^mntB6Nl|4##>OZ2boR)hH^-NTi8l?x7({rz-l7*`B zv(l4Fv)t>bG)Gn0uk>QrU(r@tp^~3_BbBzO zDq5^Bed=f{?Q(CTelgOH#QIS;(N@~$-c0=xq&(&N3UWTGO50c^xgDy?#j(ZY z_NXeC#Fmf?P*pCCEhTqARp}U8MlM8E=@eT|E<#ngEVhzdjH+^Ztcu(jX`^DR$X!rX zu8gh5Yh!D$XKXF?>rhpC$JUW=Kvn4zTTi|bRi$ri1NkOYm0M#Q$pewrELM%T$2MVU zY%}#ckX9?Ug?uN{QpL8C??P3%C$fj-wu5{hs>;%sMScru-(n8F z9rLIxN7}AfKwgQe@=mN4xe8U~-B@k%DpZyCVkzX+s4DNrQpsyjRX&KNk=LTCd>E^P z>tg9tK0;MlAFE6L7*%CMtRDFjRF#df4DzR_DnG;;koTaf{20q5??qMlDV9a9LE61o zBl12}m0x1nYvlV{ZBM=k`Hof# z$Zb(oI;b7U9Z^*Z)k1P7RFxvNhHzX2RFx;yf#k`^ceh$Xo`QT|tEJ?r$al0lh&&DXURDQ_r=zO8 zs+QqGbqJN$knLG5Coe*_XLTqpQHN1^6Zwu-hvPPN1b(fKq`n0>P-AuorU|<+4ze(2luOU z@mF;o9#H4wZ|VX(s4m3c)e1bMR^lJ(Vmz!a!9Ufdctl->inbhM+DcTlD%7-9sB5dy z(AJ=-twl>)hqksJ9c=@;+D7!WYV@^D7-*aE7;OvI(zfEU+BU4MZO7xZ9hjo+#N)MH zn5yl@6SO^;rtQTOwHmCW?ZcC_{g|#Dz>~FuSXVoQr)Y<fza12IguF@Ek1@ zn`l{huGR?iv}`<2%fY5vV{E47Vsottp0DL$3#}<$pf$&qS_`~TYl*G2R@ho=gKe~Y zyhv+@`C5Bys}*27tpi@H6=Hj>2rto!u|VsLmug+GgVq&0YTd9<>yDkY9$2LH#LKi^ zSgiHN%e6k(S?h~eX#KE@)*r9b24GihAYP@FU^lH4uhs@(cWp3Uqm^L~Z3tegm19qB zC|;)x!(Q5Oyj~lDy|s~egEk8LXru8)Z4CC+#^O!dIP9m5$D6f@*k7B3w`h}bfHnnh z)u!S=Z5rODO~(>#Cf=^i!cuKE-l5IGLE2orQ=5l_wfT6LwgAhtg?P7CfkU)PyhmG% z<=PUwS6hlhwPkppwj76PEAf7<3WsZ}@BwW#j?mWNgW6ggsjb6@wDmYj+kg*i8*#K& zjgM%XaE!JYAJw+tSZymlrftJ<+IDi_=L6xCuw`}Nv#GaYy0pi zZ9h)Y4&c+;L7b``!e_L@I88f(&uTG^Z*7eoK$)Q#I8(FmInBXYnupJ80nXNH;R{-A zoTH`Si&`qq)za`Ktq#u9((z@jF3#8L;VW7OF3=j_t6C;5)Uxn3tr1pe*|xOH!?)ahB1J`Li@guDluGf0w$66oUp!LO1w0^iz z>yMvm1F%{fh@WXCxJfI;&$U6gSsRRBXl1xX8-ibI<+xQFieG8NaGN$9zt%?Jc5Nhn zqm9BH+GzY%8-qKwvG|=f4tHte@q29|?$##Z587nhqfNmdwW+vQn}$DW)3HXIi9c(z za34Q3Py5dg7}5S~bMaSg9v;x<<8RsmJg6+3O{_FvcVRNcS^x`n6d4rb~eo~{R&rPsoSdTnf^r{EcSDrW0xc&1(lbM$mPORtNK z^?G==o`Jb~13X91#3p(co~t*)JUtuF({r$?-WZ$dx!7E9g6Hdb*g|iL7wFBgrQQNB z)LUXJy%o0B+h7|#A1~6|VZPoT+v)|_PVayh>xI}}FTzXoVl2=*gCu|ABxxM!?2e=9Iw|$U~hdS-k^`dKKf|9Q6GbS^|5%9J`VfoC>@9pNY5Yv#?a3jd$pCaF9M1@6_kvV0}K`r7yrT zeIeehSKtu667SI$W4XQr@70&$P<iL(l_A4`bHeBSK}l4CLE)0#z*xnI9A_^kLlZRoW30&*LUD}eJ4)Pci}{RH$I{7 z!AbgFd{VE$$@)HgO5cxD^aJ>`eh{bXhwvHwFiz8t;In$np#9f5^igK$2F}zid`@?8 zmhR#6dVsU_TKIxq8|UaL_@bVQbM-WQNw0(R^mKe#uZ#2bdiaW-feZ8o_^O_X3-v5~ zO>cx1dNwZ7bFflxjIZmtxL9w3Z|HfrL~n|3>dkSf-U8p!TjDak6~3*v!R2~BuF%`z zO1(Y4qZeS6-T~j$3vrcRgzxFaxLWUw@9SM~jouYM(7WMUy*qxW_rP^}Py9&lh3oa+ z__5vxH|Txw6TKg9)cfP7`T(rf2jXXX32xF$@pF9;Zq^6m7kU|P(TCuddO2>@hvHZI zFx;jO$FKDfxLqHK-{_-ohdvs=)yLpYeJp;bkHcO1c>G?Uh`aSk_=7$f_vlmbM|~>p z)u-W4`gE+(XX4NLEZnEh#$WU~xL==(zv}bwfIc68(-+`DeIfp?SKuMN693Q_<6(UX z{;4m;BlO$ zAl5Yw;VH&ptY;j-`bNy8{Wml`)iAJuVc}_pgPDehryBug8MUyXQ5zc>DR_pFirGdQ zo@vy<93vgiGU{SuqaL1ZWMHn*0M9Wpv5Ap|=NgSL&&bB}j2vugG{$B|E;cus;Q2-# zwlJFF1x9mhX|%u#jh5KTXoanfHrU3<$BT@1m~XVlwnhQAGdkeKMj^I0itrMn7z>Qf zc&X6^I~ZNDqtOiujqcdV=z&E>PrS_Ng~dj1yxi!6osGVDh0zbY82#}|V*qwF2I5sl z33f9|@oHlbb~gs&HAWfsFoxi@MmhF0hT?U`FzjUv$Loy|*xMM1HyER^k1-l=G{#_G zV=UfejKhA$c)Zz|i2aR8c#AO^2N+ZER%0p-G^XKg#&j$(X5#I}EG#u<;~mBv9AwPJ zJB@ib*qD!Z84Iw?ScrEU6*$DG#CwdzSZ*xAdySf(H(9=>8^ z-~yuozG`IRLL&=bGa6xqk&TOt9IP}N3F`D9=Msr+hw7|EFmblDl zg>M^eaJi9>D~xuy(rAzG7zJ2mbij9wLR@7O;d@3gt~NU3`$iXBV|2w2jBdEr=#C#6 zJ#d}T6F)L~;d-Mter)u?4Mt!5#OQ|`jsEzlF#xNLf%utGf}4y|{M;CXn~lNvg;9oE zj3M}?QI1=Uq4@oQrQZZ}5aH^wO3VT{IajWM{>7>nN-<8YTT9=|sx;%;LS z{$NbTJ;oIL(U^*RjcNFkF&%4+nfS9Y3-=kb@fTwb?lt57pnp>D25!(4-=xfU&R9opu4bj%It znj6tGtI;<%VPI~?W6UjB%iM~`n%l6pxgC!)cVLRS6OT7{VXCU47M^B0m}z==x*1@WSqmGQ zwXu*3jE2IiU#@EkJ}o0wU6uGt9l%xpZ*%)zE+ zV{B&TVsoBPRR@mBXgKf-wyvS^a`DS};YZhQTvjbji z7Gis|2rn^nH+*IbH2&1HC>xg3X? zEAf7_3WuAk@Bwo*jxg8YgXUTsX|BVE%=I|R+<*_88*#K*jgOd{aE!SbA2qk&SaT~r zW^TiA=5~DC+=1iGojAeVg%i!)_=LF!Cz*TkNwWqgoBQx7b3aZo58%`0L7ZwH!e`9G zIL$nQ&zdoZ_TS`kr!vDdaHeVDbEbo{Ob?$o1DtKv!WYciILA!E7tK_hYo_5#W*wYo zrsKb9A zY>rFK7WkIg5|^2+@NKgVE;sXWh1m{Qn(gr&vjD5i4*0HFh^x#Ze9tV#)n;dW-|T{G z%&z!>*$vm4-SI=S2d*=F;zwpLTyOTqkIg=~!R(8lnEi00*&jbO2Vk{15I-|ZaFbbz zpPPelvpE>QFw1a@IRw8n%W=}THER{>)@n4YHE3FE z(X!T|ZLLSg+JLUL5k0FKeQOg2)@D4$+Jd#Lt$3`p4QpH5@i=P-rdT`icxxA?TD$QC zYY(Pbd+|i82J2Y+@FZ(LrdtQ_Wa}W-wGQDa)?ut?9l`om%%%OeG(6QZuz_XaX_kYT zmWQWX0cKgXu%T5O8(ArMhLwuhRvMmZ)xjJq9nZ4rVq>cwo^54duGIj~u`;oVm4)Y8 zjWEy3#`CNkY-%;eW>zjXx0>MjRvxynn&Jgkb8Km~zzeOG*ve{!t*tiL#>&Twtag}h zwa2zr0k*R`;Kf!UwzrD#5~~;stj>6;)df3PU9qFp4GXRA*vaaFMOII|%<6^3R&TuA z>VuuFzIcVz54%|X@k(m|cC`lLRaOahvr6%5YY=v~2IDnW8TPP-;I&pc_OyoLb=ENK zWevyctr6JU8i_Ypqp*)P8gI15U|(x2-eir#e%5%r*_w#`tx0%`H5mt3Q}9-6Dh{-! z;ceD*EU{+d?ba+TwPxcT)*KvU&BZ&dc{tdbk9S!Mu*_PBcUu)W#Hz%5ti@PvEx~)O zr8v}DhWA;^ahSCd@3*ROxU~u&uvX&;YYjeVt;LbnI(*1lkE5&&_^`DRM_bkSh_wmF zSex-tYYUFGw&G*fHXLVd$H%Q5INsWc6Rcf0(b|nqSbK1iwHKeXYH+f(51+F3;}q)v zK5ZSusn#KU#yX7CtRwiW74vBSEpBuvGb{sVS{6QMIXKJm@Odl1*;Xxl!K#gOtQ36F zO2xTW8op%J!Fg6XzHHUS`BpuA#mc}1Rs($1%EX0M7QSXR!U`)J7g;%2X*I^ztz2Ae zHNiKmJX~Tm#W$_yxYTNaZ&@vInbiv4w%XuwD<4-_?Qo^l9^bJFu*&Ly?^=bp$|}P5 ztYTbkb;kFtF1W_(iXT|raIMuHKeT$_I;$ssWc9-JR&V^+>Vq4szW9mN4>wx<@l$I6 zR$Bw{GphtQS*7^7H3&CbgYgTi47XTA@Jp*4w^~E-D{B~Tvxei>)(G5gjl^%PQMkhz zjo(^haHlmEzq7{SE^9n~Z%xGA)+GGFnv8p_Dfpu`757@x@F#0J)>t#~XKNPjvu5Kj z)*Re#&Bb4>d3eB@kH1+9@SwF2f43^|kX4C)Sc~znwFLjPmf{g>87lU2jM*zuwX0CG zSD|jNM#El%ro9#|dmY;LdUWg!=-M06v#Zg!H(_9J#$)U)Sj*mu$J*Plw!IyXvv**M zy%UeOcVVi%8&9zJV4A%bPqb^Wj=c|0viD=UeE?6k4`N;W5T0Tm#(MS{i&? zZi8*?e7wkRhxvAUY-<-_JG%p3Y!_mCy9h6_i?P7&jF;M7u!G$dJKEi_(C&_%>>gNT z_r%NWURZ4R#>?$K*xBxjSJ?fqi`^fuv3cwdnVp)&%#oBHr`>+!9n(1ywjeCgYEfvm%RYX?1gx@U4cXF zO1#HjjOF$cyw_feL+xdFpS>K1*(>pWy9$TftMCDPHIA^?;Dh#B9BHq^hwSw@%HDtv z+Z%DTU5$^}n{bT186UN`;8=SrK4x#jarSn6+}?rX?VUKm-h~tG-S~vP2PfHk@kzS| zC)@k*DSJOou@B(W_CcI#AHrws!#K@8g3sEqfcD?!5eH?4ZQxAX!sl!UXW1S;ZwENr zu7xkywQ-J}f-l;sIM+_Ym+U$?&rZje?YcPMu7|JK8Mwf1fUnw_xX{kR*X%}EVQ1qa zI|nQ6#`wCOi;L|h_=cT_OYEljrrjKu+AZ)cyCp8OTjASw8(ePZ;|jYSuC&|ZJ9Yt9 z*&Xm*yAW5|MfjdwjH~U=_`cl**VtY01G^ipwY%eob`M-<_r#CvUbx=wjUU^6aD&|! zKe7AaM!P?LY7f9_dmw&hm*6J56hF5I;TQH`++vpnG3867<+q36S9UpWvxnl>_AuOT z565rp5xB!1iQn3zaHl;Qzq7~SE_*C~Z;!*>_IUiko``$wN%*5Z8TZ;#@F#mJ*4WeV zXL~yCvuENj_AK0Q&&FTvIe5UHi@(|P@Sr^(f43LlA$uYIVOQW`yAuDj7vm9o2`bJ~ zj5*6tb(W*%tVG?ZLc>{wrn4F?XARoUT6COs=sN4sb2gywY{bB+#$%jKSj*Xr$2wcE zwzCzFbGBiMvmK9jc3`Tr6Hjncozr<##kZ%`q_3vG8=q!7RtahE9NuoLYE>Qya6L6g<;O#T+LM&vNQu zV<#QYcIslTQxDH^GO&r$0MB(YG0(}u^PEQ5)XBzXP7XGA8sqs+F1B!*-~~<|wse}} zg-&y9<+Q-oPD^a#w8D#=Hkj|^V_T;kwsYF!#ZCdXcRJuDP9YXJMR=)Gj2)cL*wN{N zg-%!OigkXDr_A zjKluUc)Z1#hy$ESc&jrR2Rc*mHfJi9IMeWUXF8TTGw}{*77lV|todr0=S%~*I6 zKIE*$QO-Ji*jbOGoelVivk}KQ)%d8h3CB8{@iAu$j&ru+nPA$-<3jMJSXIKzq6qUCpZ#7%k5 zF>sb+;q#7zvmFm#Z~~m;)WR2?+Bnxq!Izv=oadzB%T67f@1)}^PF-B!)WcVu3|#0m zz}K8itZ=e$k<$n(oosyF$-%`=V|>HO#U)M?eACIprA||P%W00woEG@D(-N0Et#F0Y z23I=y_>R*KtDN@uu2X=koDTS&Q;4gbB7EN|#x+i7{J`mgYn`t6q0{X=GZepehT(Q+IDX@dz#Yy={MH$TJDt(^oihe^Ib-pAXB_T!#^VppMBL*{!XKT< zxYwD2KRHve#+imcJJWHWGZTMtX5oHkHva0&!2`}*{LPt%2c7x&yR!ffIScU*rveW< zmH4N#7>_thP;r-H%w2}6yBsxlCF*V!8ty7I-PLHhYtVMrqT{YZ*IkdEy8(T7BL;3Y z9^-DpTJB~%*4=`&-K}_>yA4y^?RdPq15@3dc!IkN)7;&7qPqv{xO?#=w+7SQeR#6F zAM3gY@D%qT)^iVGefKbCxJU3*H+C%TzpLSCu7R1Zg{Qj?X1N|VbOUVU*1|K~+L-O8 z;F)eJ=D2BimRkoKyXkngTNiWPdU%eTflb^7c&?j?d2SY-=QhHoZZcBw;`YTW-G12B?T=Tv z1F)Mr5U+Miu)AA|*SLeQhdUUrb<41)I|Q$D%dwX`6t8!OVQ+Uh-r$bFKJG}o(H(_- z-O+fHI|lo?WASEp9QJp|<1Ow)9N-umH2>L zg(KWm_@KKQN4jh9A$Ki~a@XO*?s^>UZoo&}jX1`w#z);vIM&^akGWfLoVyhtcemkq zcRNmSci=>KCqCis!b$FKeA3;6lij`elv{&S+*4~p9=_^k;6k?nzUF3Pg`0(o+(uaGX5;H_4lZ^Z;~Q=+E^(XSn{FO1b(`W_ZgX7b zw!pXDmblz)g)7`PxYEtXcieVZ<+jIn-2zvqKt z-EO$f?T#P0J#f9-6F+u);Rd%ie&Y7Qjc#B3)a{4WZh!pD9e|tMf%v&wf}7n^{K6fC zTin6;rCWwu-68muTaMe@q4>2s47a<(@f&vp?r=xqx9%w1>5j(l+%dSz9gE+)<8Ze- z9)EBr;vRPr{^(A|z3vqJ$(@Qd?lk<_osRq5nfQx43-`OT@mF^a9&qR4Z|*!i=+4LA z-355aU5J0U6?oXK#6R7|c*I?TinkPF-ZE6Z<*0cpQTM9Q@K&MetwzgRgSNL89d8}F z-g@-B4d{CtG4QJK7;h8S@;2kK-WIIwZN=lfZJ6S1$K$;nnCk7s6TDrR=IzE4y**gR z+lwc8HJI-0!;`)JSl2s%r+5dko_7fAdxtT@JA$WrvEyj}Jq=Iu49xT_Jl%6J%k!|I z7hof=7M|hN#%wPI&-79;$4kSrygJy}OUJXlx|r+L!*je0Y~nS*bG=N=^RnkA$Xlvj=j90c)d3adwaw2 z25$uR@kZi}-YD$rjmDe2G1$)=i#L1Yu)jARZ}BGL0B;iB>P^Oh-W0sen~EjgG`!uL zj-}pAyu+J?gS^>zr#A-&dvozFZyuI;^YLzP0S@sN;yqpkmV1?WueTV7dQ0#=Zz&G* zmf`*0avbih#0R`89O13P2ffue(p!TMd24Z$w+3?|a3# z#_Nn9cwKO<*A+kXy5TynJAUN#!1Z2F{MhS-8@%55iPr}=dVTRzuOC)>{qZwz0B-UI z;^$roZuUy?3vUo^@do3UUKwunhTvCTId1cY;@93V-0ls>Z@dw>!yAd;dZTcsHyXe5 z#^5e*EPn5e!` z8ZCbf+WuN}{B`L1>(TQ!pzm+Qz^}$*{7qQP-;BrlTd=mj6_4|`VT!*UkN0tJI)9nbdbVy<5g&+#*`iQfRv z^)oTg&%*QkM%dKP#%6vFHuoFj`F<|8@SETTejc{;o8pCjb8O|ez}9|CY~#1Wi~Kg2 z@8@G%za6&o+vCN40k-!$;3a+`7WhSYsb7p8{La|X?}CMXSM20>!y>;sUgr0}V!tO| z?)SpZes8?O?}J_ZzIdhI54-yP@hX1+cJl|~)qV+f_e=2_e-QTY2jjJV8TRyt;B|gE z_VS0~_5LvI?GMKr{1Mp4ABi{mqp+_(8gKH)U_XB>-t3RV{{DEp#h-`+{7HDLKN$!5 zQ}8x_Dwg=u@OFPXmijaC4u2L7@@M0n{u~_a&&9j^d06Jp$GiOnIK*Fw_xKf9?pNZy z{$d>JFTwl#r8vxAhWGo+ak#${AMmSigueG+CY7Z>>T@Krwp7y1qGH9r$8{48ALH^NFk8(;Tx zaIxPQ-|%yBiQfd@^z(43-xS~So8vOS1-|XK#N~b~T;aFDm3}_H8sQkHKC3Sp41}hr9jp_=7(Y_xO|WM}IQz^{3!Z{#2~-r{T~3blm69 z#9#bbxZj_Rzxs3VfIk<1^XK6~e?I>1FTg|oLj1$8z{7qe{^>8qBmNRpf~6P>mZ2If zM=e;1dQgQ%unNs!HCn+Mw1c(i1nbZZ)}t3}KtI@sK~Rmy1e>r{uo;gHwqWgGD;^hY z!<1k<9v|$$)L4Ae;frkx)02>9h@Qk1~W(O&FW{`?GK^mSF)WOC=q2ftAi5k9+cuW!657r z4907NGVB=)!Rvx@>=g{f>w{s~I~a~P1S7CdFcNPJMq%GzG~N`9!G6J5yg3+${e$s% zOE3`!1e5UAU@{I2rr>SCR4fUm;qAe6EDdJj9l^RO(Kk9P+P za7eHa?+GffJgCHbgT**BSc3NjOL17R4DSz?qd?q-E(}F|zY;YK-2S;#55KE)|4|tVbc`h(;R$$@t zfrGOH4_^oZoDfLrq~Xg!9h@Je<10a3ToBa5SAz^(7&O4wf=sLk zvT#w*2rGkZd_Bm)#X)0yBgn-iK@)s4$it;UQ+z9Ej?01;_;%0|mj|tIMbHLU2Ko3- z&=9PCr%;z`9e>{sOB_loU!N|BF0C<<^uQHVb(cHp35 zC;nGaghPs5_&>#NJgwM+KPmR&uwozntk{od6vg<9q6E(>4&bkfgE*on#orWVII1Ye z-xU=&rl`a}6jeB`sK!4PH8`O-gnuaxbzR-#Dgf6^5=thOmgM!eDqR@wu z(2q*t6ov}}7$FRzN*F@5a2g|pVT=;a;C$gMYJ?Gt7DiDkjA4v0jV!#@g(-{^ zrcp1-Mffhl;BteN*Ap(;HHQIzIOc69_7qsXQ zV$mtcm@4SeB^c2yn9(C7V47e-uV6)=U_-xP$AI9(py0vJ^lRk%&4#(RYt%o7gbeZpbfF4W@vLLKG{_4t6$ zfCWM$J}4Z)LZJyC5{}{yp&1_*T5zY(ijN3wSR@?7M}_0KOE`g#3GKLB=)lK?PTV7O z;eUi~+$;3p6GAWU6Z-H;p&$1Pr|>CZ0E>k|d|DX765%vHBMjpK;S4@2oW+B}2tFr_ zVyQ5O&kN&NCQRT9!X%apQ~07VjTOQSz9fWbnEwU-BPO$67DBN~n2WCnVOTB9!&e0b z)(9fLCMfZc5P`1?YCJ4N;TwVmYXvR7Da2x(Amdwt9_s}ozAc!sK}f)N1Pe9_R(w~m z;Ss@(?+H$95?uJc;K8GU7e5gE*enF`Lt!Dd2#fF|VKKG}OYmc1DYgmA@DpJ<9urpJ zr@~4+F08`Ogw=RLNXO5G3~U!N@e5%Mb_i?nOJN;$3R(D-upYaF4fwUN5xa#=_>Hg` zdxS0ct*{k)g>3vz$iY4#7f%Y?uwTf-?}hDnO323_gaRB83h_r_2M!85@xMY54hg&P zf5L7&E$qRcguOT{?8Bdh{dh(w#$SXIJS!Z)UxkA>B9!89LK%(<<@mc$fn!1?{vlN1 zxKNFM3N<((9KyeZ!#F9_;@?6YP6_q+kI;bALL>ew9Kji(31^8%F+^;}*_??|3d6+#j1UJ= zB@UrlJdKg!Fh+@IaK3mJHR1?Hi=(I&$1p}5$5?R!b>bw-;uOY-)2J6`&>)6HGyjWo z&?JVUS)7aUVi+cf^Dt3VphXlhNmQa$jKE}3jW#g~Q$!8gMJ+nSSagaqriyxWiAHpb zX7q>&m?m1#D_YSf+R!iBF(5iID7tWg=)r}e7cUh3xJV4*MdCtSEH1)}#l^TpT!NQ~ zOL3{V3@;Uz<1%ptUM8-@<>D&5TwIMS#B{tu%)pgmCSED7!Byf~yh>b$tHmt5T3nCm z;s(4%+=vbx&@m{e8^Tb1VpLiIzi?w*aScmyyJw6~dV1d|(4~j>yP;A17 z#G|-FY{rMh7ThVe;v-@k7Kz93QSmtL5>Mb`Vms~@JMeL_6ZeQ+_#d$w_liCEgxHJw z#6Emd?8p7$DSS#Cz+!O_pB9I(L_Ceph{Je5JcG}QXYrsog3pPgSSpU;^Wr#`i4*vO zIEm%r6uu}n(@KsTPHKK^GiAp>qM&RqB z8V`$6_=c##T2YH{im_NH%J`P3$9mC-Z;NJZ5EJkn(SnVl72g$Ycto`0d!iGYL>Im< zdhn>|#ScV3Hj6?0P+W*D;v)P=T#T*a68uvT6aW$S0 z)A4gL1KY(+{6buV9pYO2Qe20fVitZSuE#EM1AZ-T#BOmDej{$i9&rnPD{jSJF&n=V zbFfd$#gpPT>=*O!dvQCS67%r~u>c3eLi|zOfrH{s{I6JqL*g#{pST-Oi+k`VaW4*w z`|xLRKb{eb@fWcK&x!}|SMeZ@h^6?OScaowIsPtI;Fws6e~48$E>`28Vhv7+hwv}) zFiwiK__tVxQ(`^-BR1f)*ogm%M{q`L!dcQ$43U~~w$y@iq*gpfYQs?J7@jK~$GOr8 zJWp!JFsTF2mpXBt)P)yF-KdayP>_02l=@JT`cWyJ!fU9HG)bXomgZu-6ov`X zJWP}nXpuxrl9XtbA~0D}qfLs!6iI`2NsA6C7M+resgfRDk`djK89h=0rb!m`N>=nq zHuOt&3`kB4N-kU=d2pfR#S0}rE|P+Hk+cvOON;PgX)!L5mf$7QQd}x6!%L;*xJ+7s zmq{yexwHx|msaBnDIKqnGH|7oiC0Q%aFw(cuaef`YAFk^meymsv;nV?He!af39pql zW2UqPuamao8YvsEmvV5el#4e=+i;zfhc`;wF-ywFo1_93ZIe& zuvi+zr==k*kxt_?(l8#7&fv4sSv)9>;B(R_mP%tWA+w%G=6`9NegT>Pr3v~)Wd4^X z>6efhRhq(fX&O&TGhFXSW-W>T$my(8$V?^8p$CzfN(!Zikh@hm7uCuzj8x9!dK7Y3 zDix?viWsd_qE;D!F-kSYDx*-R)S#@?Vw^G-^-38HNnSXjUd*ywZXRN-HKR zZD>*2F-hq}tI~zZN)OtUUQAK?(XI@lL%9&0%0-x}T#PQ|5_Bt@fBq;Rx3;JRpkMEU3m}>D@*YWWf|5g z%dt^efgdX?u}xWppD3&Gn6d^xRUX3Q%ES1XvKGHl)?u%*o`1*hkTp@+fG3rW*snZ- z-z%H&l=3M4pls%q1IX&5Y{7q&tvIc0!+({>Ff{x)o*RAw=Z3dqcz6eD!aGqP-i3zn zZZwAXpeei;6T}0|<^tZ^T7OthgLq4_eSo$Qg?nlTN5~1hHY-HV! zFw*BBpGbrmFNjD$WrPL8Bdiz^VdFI_WR{4q(~-#h5aFaX$jlJoqGOPmA;LrJkU1g3 zi~0yZR}9D}8xf>U$P5s%khUN*K*S=nMl8nUh$U!?Sc)kT%Xp0)nE@h}(@tath*&|p zkQpFiCGA1({fJex54ravR?`9G-j7J97a(_BL8PDHH3$%rgo z^EWb{Bi7UZAmce=1N|?upHXe3)yT}L+C)bo<5sm9&8jUJuiAqw5jsYuG)?cRX#dZ1(>QTM7L@OdQ>|xO;yC7^&C}~ zRk&4Ejd!YQFk5v9?@}GcyH&NAtE$6$RQ0$`)qwY^8Zl3G1n*Nd@waS8?l{#^d_dLA zl>+3BQ?=kjs#e^gYUBFD$c(BwMn8hA%c|qJOLYPtQ?=u6RR=z<>cl;&F5Ii?#wS!g zxL?(a#i~AhQPqzxsZQa`ssX-d6*7ye2C-T-gf*(uTz?I@6IH|by6OzRp*oATsu8~W zCUOs|MzLNs#+A2`laXp1-%(9qqiPc0RZU@&Y8u~H&G5>j$Z1FwB6Av2@gEwV)uIZ; zR@GcNWY)*XXjFyKpCI$IY99S5a>`IC@V_b%hg3?g{|`B3s3Pc}kW+?AP5+FXGE`CY zFUToFrJ;XCP8ljK{Tp)1P{q=}Bc}|NO#gx01u8xL7c%BmM*3f523DK#8g&9!GLW65 z+Cpa{yGON^UW<%CwT)hf>;~0#dJ{6P)J}RcGOpAvdJ8hT)E;^(GP=}WIvW{XYCoNW zj4^eP&PB$UdLg|H8Dr{2bRIIs)Qjou$l9%5LgynRO}&&ZKxTOLGP)31wbje%oye-K zUO^WjBTc=M-i6Hk>Q(e}$QrF)O+Sy!|LS!51!VqLXV5Pq^S?TiehHcX)obXNk@;V} zmVN~pZ|ZgQtH`KRXVI@AqfWh^ejSi25KsjqE_wrSuH44n~&I zA;|eRvYeiSoPQ%L=ul*r5?M*lMb5vGRdg6~{*A1r=OJrlWDTuA&cBg|Xc5^_M;@k? z$Ql}1OGhBuSI6a$P@TPWIOJQ?7$}@JNfE<6gkRQhmo-nIYysB#zy2gjzvyzhJG$59E`7G=RtqOAC7lnswZ*|9sy$@l*T*`YqtAV9<>&fO$m|^z#NVS9a%BwJheR#H>8Qo1n7;&-^Oy3~aAa>be;KVp zPGR$xqhbCEOqjnCm(O3tS1(7N49s6mUxkdB`RVjE$ev+-27Mj!1Ymw9eLb>^n7@X; z0eOe#ucg-`XZ-o==#9wUV15>TJMwPNUyl#W-+%@4H*)28dJ?(! z=kKJako$dp5j}&mXU*2^qURtxSj}#X(d@xk&0f@L_MxoVkC$tTdF2Y^e4#0!S0d*N z%>jB9GWIkF>D9k$X^6MrR=Rpr)M8MD9UN1-%Bj2Q`)ST4a6HRMG2@rz@Ij zItzKXH8u2lWS^!vL~lUedCg&ZBeI@qYUxeLda9|THzVt*rk>t{tf!g=dMmP?Y8vTm zvS+RMSM~BI~K#61#U5M=8 zG{@;3$l9toLGMJ?R!uuygsiQa4tf`|i_>({yOGhN>7w@_CofGmy%(9OG(EUa)6117 zk$s(}kA4a{BWwEc4b3U8)FP)O%>Z48+~b--x*nOYG(+@z$bGFjO}~#kqt*;#hvp1d zzC@lLYtGVNAy1DrBlOqE9jzIqzd`P3%^3YHa^}*E)88RyF3kje5;-YrCh6~y`A{=O z|A0I_)=bksBKtbc4E6EZVsLg}B8eVt}5{R^_M(}dB#BKtbc zJo-0eU#C&fzaz7#Mx_5ho*rwI^qV?*=?Uil8P9z}1Y z-$ll7^d|Z}WDG}drr$@#aP$`X17w|w-b#Ontjp2a^he0gO`>z?&ym?RIv2l)-o}*< zQd2}c8bCc-pbQkh-ljwZ98yWM_1@yPbn2#=`zeC1+^bYzYGUlUq(%&OvKDvnh z0U7hryXYU0)jWDPJ%P;O(R=7gWDbwsOHU#Dx9ENJG%}w@@26*w{hPL!4ndwNX-nvH zktZ111N3<~dzM~%kTxJEacwCbkF1Q^GCC1CiEGR0B;?71wgQv2m0YnQqfuK$+mTbb zwwiV#r*dr#?LyAx+C%8k9_C6KGH$iCv=2FC2GST-!`vjy(U@w$N7~&;PZp^p(geuWh5RLUxJTWAxR? zimyFRUxPgV*PfuSMOJ@pJAECp`fEGr>yfod+exoO)+TKiorSDT+HQJ1vNmaZ=ncr) zr0u0QB5RYjkKTl=P1=5XGqN^mPtjYDwMjcbZ$;K7?I4|vJhjsf(RU+H611o3dyppy z+F|-$@c-scv?Hom7kDVR69ZcjLf3iN%|LL z7S&GCzaq1!cAEYTnMJiT^zXA#R!R6Cdc8<|D5Ve~)9 z{!KfN{ulWfoK`{e%guC1j7ZN$p2fu|>2r`#9TP#Hi;U_RHGLj38^=V^=OeRmjE24d znT=z#w1CXUF|o9S%*HV?9gfV#F?w2s%*HWBIuaT4F=n*IBw$L61syR~zUo9qV2lkf zi?QSK7$?^+M@C?b3$KXr;K~>;UKQiV)iFVQDrO-bj9G-w#Vp3km?c;hvy|`f3Nl;9 zEW?_Z<@kEc3OpRMlCQpj?9XFX(QhJWgqYR%YfL(h#$@2{F`0aI44FM**5G8!TKqR= z9oJ`&U0-Y#&WT-*p|KlqZtO-pFLo1##csw6Vz=OBv0M35%aI*mY&N|D`L~PBp;sbz zR%|YP74q*GyN$jY`6*az9^M?g9dC)v$Gc(+_-YQazltrSbCLa3><)SxvcHPmN#`N^ ztJorXJF>ru-9_gk`>WX9bOExzirqsOBKxb@z4Q*`NoMRm9E#n~mH#2{Pi!&%9$SKA zu?H|#cMy%bQZ(tx(5x#*yRHHqx=M8FsxVbojV@gcx^;)pqdSZXbhWrhSBHyr^?0$a z0WZ}x;^n#{xI)*2SLlx7N?kKvscXSix>j7RYs0H`$M7cIasFx7BTpc8C-4?sJ8so= z;GMco%+__`UAk_}*Y)57x?X%(*N0E(`tb$bDXh^A;On|Ud_y;cZ|P3sJGx-3T7ljpC=eF+8pt$Io;V*shzzFLYDbp_|4pbu-wh3o)`9>gM3rx=`%a&BY#F z7!K&>;ZHgR4(miCa|<%J=#==2E&|W$)cC6|3P*Gr{7t9DQC%$lu9I<0r^i2ZMjY3f z@lRa>PUtN7m(GflIvf72v*VP`iT~(aIIZ*GzdA3@==?ZK4q}MB5NFGaaE`nf&ykm4 zsJs-Ux5=e=r(A~Fayi~5 zS744@iFeCYm@8M~J#r0hlMms&@?p%AYwbqf~C7A3#nzawA=YoOI+P zxJz!r$K<12-;JDf#vd|N(?4e|&!$)otbJcdW*ar{7@z-D<8Ka!`gRi4I=G+34BG9TaD$&q3~> zI46BBatFn^=<|>}D9%HlkK93VUit#$4vO>B0&)k%1!)Nxw{Z*c(zr#K6So-ej$4Ac zaZB-@xMi3Zw;b<_Tfv{&j;s-JD=|NA6+RHRn(GC~8WERH7a}L+xD0$IE)x&Lt>OB! z$k>Qmi_gWa!_v4cd_HbHmc?zr7veTzdE6#^F>W(f#BITs;5q^R5?4fjjEs=DUGyi& zIEmX$e~Qf2aeL^`kW)1*%;{UKE752L8BMM+l-jm--vqs z5j5zV(5OF(c6~EC^eyPrw_>Wkjlb80oDTHIFrYt&PwHoo zUA~zUAF?yo&!LBqow+^~f6~vzVSO0>te=Nx^a?zy7x7oU5=ZnA_?uphf9RudT(9B# z{E2*OdM*B?kHty7jDPF(IHfn@KYBAx>l5%_y#;6VRy@aG<2!^R>!86-&qdZjgOd(J z#(=>^&qF?ag9j50UanY>pH>_Ev=!Mu8-lbA**_Z=(stx?H!Px^$mecYOuLX1qG1X8 z3`@~(ScU<^a$ICs!D}u;c3Fm%^u@^8(69FROXngZ*|3kk7n%JG`|10TdCyQx=Ogo;p@c3#<~_p!x)9kL8xGPtke?bGO6f>=@`T+6- z$#9rHh|I2rTDlaOT@7_~88W*X>gjT1b~QB66*zm=ONK_e5}92MN9ZbKb~QB7)yP@N zaFnh=o-7%f=|jlA*U&;AMs~l3R=O5hyA5r09kO;Cj?wkV+HE*aHy}G?!wI?(nYRt? z^buswZ0MkykeR{INgqX4c0(678@lmBLl3qXdhsJeAGR9$@ngd&Y%>htCx$`%%P_=u zm_+VA!)bb!@iZM`9HwU@bFA?Utw45(# zqs=&jDaMd^W?kbPbQnYNBI8_KWemfsjPr1tQ4!DRLPnQSr0+xaaz-V6KeDzOBj^W^ zpJf=;^n=J*!x%-shWxa`sG(m+-eIE_-!;bK5u=Rn8THs?G~&lbGaff4V879VXN*?- z#c0E`Mmzp$bmEB7g})g+_=nMpe;NJ!EtAO1W(?9($joM3NKYd(n{g36gY5QAi!s); z1a+pRT$hm*%CwBuBk!SUIhsr>&}>?X@upRnU|NldrgXHJGSF_yzZjYE zOk3$okf&j$Z2D4U#xv#6mm$w0Ou6*s$Ufh+jlKdodz$j-E0NFLw4J^RnH5d>c(thj z(@lk3zXq8VO*`moky+8SlfDj_6-`C-^~kJf+C|@h%!;Pnc%x|#SF(_EglR8koAzO` zX+J(~D(0&t$Sh$h!BW!!eBN}B>t)DnU@E1{arUeVQyJEn%JDT*1s*b0VuPs)-!WC= zC#D+w!gL5bOo#DHQ!RFz>hK#=J@%Lyu-DXxKbVg2{RfcUj;V1i6)luX$YHGtV(=q(RbR5S`C-6^GJ5HE7@Gny*PMW&#Z&No;nR@UaQ!h@N`tV;< zKhBs=;Vkn2hL{I&ws{EWm`~$5=3xvqpTTp@XK}811kW>%Vwiah&o__bJo5xzV4g&k zd5V8pHFCx@Pt#Gz^9u6}vdShfijYxco`aW|L%FgPXV1FSJeOXEoDXec?C|HS91Ly{BHUOzhX(HhuMkkb_ zHld8`G02LRP>%M53a&Vixi_H_?@Xw|`xB}$KcNOI5)Sd2myl7HaF~7>+4(2b(yt&p z|AadFRpk9isHa~;-lc>F`gP<)kkClKfxK@CN9Z?^_bs7`ehYcu5{}YuBhSGSnz1{f z1;0sX#h!#V{5Ihj_9h(XmER#}frJzEN#s3DXs5qN-ou0r`UhmxC3NDS30+*7KxUYP zZk$Z$!M_uFxju!ww~2jtPGUcXCZ57`69@3T#6dJB4)ID8vaThbrsI)yEpZqV6VGtP zg1p0tXK5?4druspZOHy1ag??rqc?Gkb|ULk;y7NEIDv~3C-J((DO{5{jq4I;c;$`A zT#*=($nRJpd*H-5^zFzVI5CvI1K9&7&ZX}}R*l3k`YvP-oH&opMLy+31wNE0a%Bh3 zp7n5|lHQ3tUrLOii;$;7iE8>OWc5jmqMt^d3@2*nXOJfViCX$ulOxR4&e*|UC3Ttp8d z^Ht(vdI*`X5|_}YkuzrEQhFFUVv$B-YWysFTQcjm6=PXMFU4fjlER}R6a?Y|; z(N)MAZKyF zWYxAbVx#2Hd|WpLrWXBSdQUGmgCrJIe{Nr z+Of^jfoCn9e1~6=^~lmi|AwqbmTvlYWJhP|q5nX}u%(y&6B)ynKKd_Y3|so?zmZkm za*F;3S$&cQFg0lqT}eaePCAXAq+wo@hU}e^&R}}dS*~1zjLD=C`nIG|dh`E#d(s%a zC21U=PnzJXWyt51G)b2upI6cpRwPYhRniQ;mK0*)Q$Rk2q&f6q)#=( zSW+x~5}9+7WO@*pbCUG*5HjZ^8S!+I8GlMj;QBB!=OkJ1Op=u=zaXDil8ycq`Mi?s z^l!-LmE@#q>mlx(X|-tMMgkI#yaU@MUWzR$15JE7rAGZC!`2 zTC=dmx*lJ%ZooseAiltN31*WJ?lR?!jj3Ui{Fy4_mDJ@gr+7wpvT@W9tEI zvmV4xtfhF&T85um%kj9i0zb1>;t6XNer~PCc54lOVLgN$*2DOvwH7*_?ll2r1TLmZ)74&g7>(|Fc8jK5mX;E44s{$?G) zQR^uFZXLrh>p1>loxpMHB>rig!U^j%{$-uPNoztpg#yf=9n9!*}3A0)59*5sA=aq=oWp1c}AOHRj*Y1@I}ww)MdE5iAEdG+QYKZDqK?R*s8p6?n0&5|`Pk@G@I9uC&$Qm9|5eZaa+E*lKZ&tq!lZ)nk^e z0dKN3;zrvMyw%o(TWm-14qG$k*jn&zTPx<-+VDQxF)Xkh#|LdEaHp*uAG3AfZd)fl zVe7(uwr+gd)`KOsUVP5hho!cDe9?9aD{KS!s%;Q!Y(se1b{cDK!}yl%3?8wa#b(5E#@}sGIA+t}xJ`@y*kWnY{kly zY2b)uJ@xzpD*piZmAEj)^)|7nwIHdsFQVQ{tlpXk4%1%6yQiPwU?85ex-S|by z9_&cji(jSe!>*M5*qu_0-=vh_w1V~=a~*Y z9fi#54kN8W?n;N5)*{d99SL*-@;j>z3;G>a3^;6fvBQp6IGlL3!-eS%4_@Q&Vur(y z*E)iHhfHJ)I2O`tkU8A3h+d1#w2sB}lgQrEv4nmKdBWgWN%Og%aM)sjvRd3k&6wEZTOBO4;vlZ@m)tg9&r@ldyYbEa_qqO9Xs); zqX<86?80WpZv4=(2U{F_@gv7RY<29%j~&I><|x5W90%~2;~;+OD8=KBGW^U@jwc)y z__?DJ+Z|Q-g`*lf95wi*;}CW_4&zsjTI_Pv;n$9O>~=KZH;zW^aU8*K9ZlHlIEvpn znz7H(f+roV_=BSj2OP)nN5^rTaGbz@9PNAx)5wm((Lv90cF-ZtPI@*n8l7GAIml>q zcGKq~qtV$zpNH%^oV|E~vkw){eiWRiP;?HU(mBX0!;xpd&LLWbjA7?#jB^g7&3T6F zDahW#d6sq{XG!M>?Ly`*=P11Z`PqJH_dxUq$W|r=NZexl^1$`gP<^aW16aK<*Uh zBKl3_PH`@#-$L#b=MwzXxs)r%krC@$MxQ`-6wc+SPhEkA)RmZ!x(X9hSED609c`%@ zXiv>VXX+YsrLIL^>N;GMnuQmouE)ix8}QQ9jkqjz6JC+J8CRxm!7Ed@;;Phayec&Z zGg5Q$+SF~BnVN^!rEbTYQ}c0iY60G!T8LXxcVJHHPRvg&!iQ6L;iIX$aaZad{7>p$ z+?%=&pGw`2#i_;kY-$M}Og(@vq#nfb)KYvowG69L%kj0;3OtlriEpM>VO?r9Hm26# zcc9+FCtmC7!c12;Ugzq;HLhN~-qnX|UHy22>lChY4d9Kg zLCkUu;Z3g7xZX94H@nW@2G?1<#WjK(U88ubYm9%wO~^ax8mAva)^XPa{W$V8&oznv zaZTY~*EH^P&ES)+kQ7!5*BpGx6^g~Kx%jjz3`<<|@EMl^54c2p)}_RQt_Xb2rN&ZM z6h80LV3|vcFSuf{+$H0SEym!JL^neAOc`V(ZfcP*qpMdowYBDw>a&s~e@PGmlJ zEup)R`P{XX?ndTw*D|^Xna^Fz>0V?$cdelNkY@s}m3Ydv3I|-P@kduWUmZlA1GqBi zA>=uLE0aEroGe^x=wakJfNL#%202-{*3oB?lZ7jb9zjkPuJ!aNaocW2Xa$oO~X&<14uyK`w1^0OHCHaZ@e zRor=WA~LJEx6?_;PR5;2CnNKWyMRtXW*c`Q?Lg)n_YOK0nR(nhX*Y6`br;cT$ZsIH zchNp%U*z6R2ar|9y@y_atTOJs^o7W*<=#hMgv?s*{q)7i&yL;2^d-n|Ah=8DOOe^k zeE=_WAH?PEQoP(|eq?5I*Wq4wJy)JU?lN}+{Umaixf|)Hkh{!%gnk-X9o$X$n)@gob~khV4dgCx zw_uaIl`HQfJ0Eu&e&9Za&F(@e_9s9&`8Nr|v#H z?(WCW+^6t_djLOo4`RD}2)}Tj#t!!|e(64go$j;vm3suc+@tuldknvEk7JK}0#CXp zvEMy~gYIcO<+Ors@-#N#2sqq*(oxf-E-+NWDL8*=m}&m9^$bT-1>mkT9D^CPH2RTi9)F^tQQ1WOn+M`9SCzjX5AmiU7qt2s8*<-{wj~Vrz z1T=UoX!Kao_bfq&XDK>8 z%P`fm99^Cj==Q8ck7pI8c~+y>la4-52Kqgj81SsYpl2;E@T|jyo-Dl3vmO_DHsD2` zjkwse2`~0+#wDIDc!_5#F7;&NrJfvI=E=p&Jlk-&Cl4?8Y{wOze7wR_fLD16akXa$ zUhUb5n>DQDJ>~eErvg9lRARHI3P1EzW4osYzwjL5dv+ir&~q3&J+=6irw;o(^?datvdc|t zKq;+}D@x=EN!k%Q0(t(B)`WMZ9mSlqX1qJC1#{C{@&2?nd^qhG?o2z5kEETzqO^8= zG_3=7rFG(CXkv$58Z+ zqslwMbv3f9_fFDL$jQ_@MQf0|!#hoDk>^F;89ElZJG>zdo);l^hj$LGNA3=9C~ZXU z0`FYfg4_k(FxraT1>SjR^D4NKf{b>rNV}2I?p4xh$Y}RQ&^~0ed)0IR8SUOEdI2)p zy&C#LWVCy=^hL;M_r}r}Bct6b)0ZHl-K(cBMMk^VNMDAGcCVSf92wQ#1Uenrp?NKs z;k9z*T4aajwb9ohJ2bDIz8=}3d7bnP$PUfxqHjcYXkHI}6S70|dg+^y9h%oq--7JW zyg~X_li(h;9VYhcbe&a31 z@4O}0=RJTYy$7-1TgspP9$AmPW%yrjIsWRcz!7gH{^qU1QExR)cx!n5U&s#1dx-uU z`5B7$F#Qj5+Va-Y|03&=w~n6WtD{4F_4I7ygzjsg&p}QczD7LPcLdM#HKFJ`ijuDx zmA)1X_qAe_uMG{pW0>GO&UZ*e?kV31^!wT|;OoGkuM-#ex^SVd8!z*rIujYgz7f2|H;VWA#_%EE zIPUOG;KRO2-07RbM|{&*_eJ4TJ`EQ8v{>Sc#bXwQmu=>RXJpz9smkZz5zARNW0$`JzxH=xx4#R&@pog7zX!kd_hPTV55M#GW1s&Np7al3zkd+F z_YdJI|7rZeKa2zZGx($bEDrid@W1|19P*Fh|NP^4+CPCm`6qGMKZQU0r}2z`27mE~ zq_T7N&%s~)p*Z57i@*89aMV8!fA=eJ%rD{}ekG3kBk)hZ8Ylcw_?KUUlYTA!?T^JN zzl{I*^*HS};=g_~&iE5>R=|QG0V~c9*l^aoa@vIjusx4>$80W!Y@(&-D4`7Mw^ zUxdtKflRzKum+a})^hzaK~}V zuD~JuI&c`f1GV@~pbozc)bq+-;7@@@t_&mNJa7aj0!>``3mL1yqx3n*SPeGQ z=OSY@*g~I&oCJfdv zwu7B?3UcBLcG0QG9yr)dyOBL`u!l}V_NBpIyeim-tAqV`b?_8lO-IIOZ~!xcgLrLl z2(J&G#;o8l<^<2+1HrRc5FEh=gQHj&9K(l#(}FBmNdN<7hC!#cxR=v&sSs&RJl^c?)c)T3|=@0w+c;aAE8M4<;?}qIH2E zlNSWhwqPMHSg;5eE?A8J$JbpzMYZ`}eADL=BfShmb zPo`N9CRbSxC0APyC%0OUB)3}!kQb~2$&1!w$(z>W$lKQA$%odV---Z$ffS>G#uHk^Fo^ ze~{Gc50kd~qokewIBC%TMLOwEka=88)IYNJ(9IwAgPSD>b!}WK`Nc}%#oc;khQ~!vZqklrC=%0~k z`WNIv{VQ^r{tdZO|Bl?K|3Gfhe2i7Tx#2%TxM%UF1PJOuCTQsSK8W=t8DGbJ+=!b>E|A=4HnQ2Rw>p#5Sp$bJbq!G0+@*?u`W&3+{rWxtw?xBruzZNHXGwqH-qv)@QAw%<%< z+ixXT+HWV<*zY9Q+wUef+3zKH+V3a#*dHX1+aD%R+8-s)+8-w`*#AY|v_D1OvHzQV zXn&S`Vt<}|X`f5JvCktv*yr zr{OkTki6bA+$BpG{vk^m9+0IBkH~U{CuDiUGqQr=1^I{J6AgdZa zk<|=e$m)h~WDUc2vZmo*vVlR}j^9L(JQo|Z^u{F5#RiGql;lyxAd~h69qDB#$^I@R z*O8$#y(`HzWhhJUM)G`WC{OQ6@?DSN54tbOZ;cI==z%1+Lqiq%SdwegP>nv0r=(7%#g zQw~m~;^0D7bZ{f9ICzlt96FN?9J-LL9J-R`4&BJs4n4>=4!uZg2S0L*LmzUSLqBq% z!vHeV!JpjbFqquwFqGWyFq}N(Fp|9L5I|mc2qf=2j3pmAj3XO5j^|P}B6*H*45c?A z`JU8qBE1>OPh}k^lOc{_WRzn#8RIyOOmv)1COJlv3ms$0MUFGb?T$0aosP4~XO4;F z3&$k#u2Twm&uJd{kJEhevC{%R$`g|Bt(+FppOJiT<+OX(>Am6z>A`4xfkUw0Wku6+bkgZ%_k$$dk z$Ud&`$P2C?$cwI@$b8o?e0?sH+)7=)(XWy`in@NM7m&PKaQm08CV9=^rncmFF(jXb zo0cw-d@gPhT_(9Ty2*4M$>WBbj$V@Fal@@7y)?W4K!-dKHq#6Spe#Y9x;*Zq?{DNFGnzYS3$uJkGn-qSqmLRpwTQv~;US z&Ub4-_Hl1S_H%DS4s~zF%g749wQBS>za?ycz2B)3v`GkPq^weN00X1cc__q(@a z{{fPFhkJYaVUpW`yA}N?$t}UX6a6^JbEUft{V$Sxjk_)VG0CmM-Jbr8p7e(?>Ule?HNF}^9-~c$G^h9 zS1*QtGn=oP!M`xgRmZDpl1ZuxP4ZRws-RZKn&tCX#3#s*RwtWXQ)QUus;}|ao9jzn zQ)QdJvc9I;XL^kN2TV_phfIw#XH3_Yzs6r+zOgP)S(?2gJD7bSJDN={S-{^<9w+r? zf0K4*p`^j=Ea_<0fOIy?C0)%vG4F2nh4eK0MtYfjCwtn_DWqc|Fq6yaDNK-iUNHZ$i48>q_0?FA1B`z06yX-sY{y-saaz z-ct27uf(bS&CS>|(AND4|&O=A9>lrUwN;xY<;ZRd;UV{1lh6m$z~nZSsjj%*&R-h%Q~EFW>9yu z%2gZG)>h-n7}R>J@nsEaJF7&}V3kBVTBVTAR^vI<)oL2u-D&{oX*Hkpvg*TrZ>!%f2}>#pE;U``m5FAG9Kzes}|)v)IY3-(p4Q7keZG)*kjUhC@DJDBTIBNBTIGc z!>MID4yTvv_^)-4da8AL@j8(|>RN9c!7HhDd6e>F+UoSd(c zq^!vSWcK9FT1i?qIm|+mR!q)QOVX;z9_(2&nXiMijmgr|4kk-WdnN~x`Eh&X?775bJ?Zmgf17&J*U5uz z>Pv<6`qB@2LrF~WCzUCKZJJ0`=}n~S^p=vI-cquon@X;9Q^}p~Ds7sQuX2^PObH;j zO$j7-ObN4am3B?ZQ@cugri^6IzA3qCcj*w5?$Qw^-K7&ux=SaSbeB#u=`NjN(p@^o zWLN11$L}iL;`lz&9lDQnkKRKv3G*jK*kGGpQU!W1sUqD^s!8{gYSa5lZRvd_OL~9F zn%-a1(+5hfVfm_ol6zPH=^55pJ5cfp3$qv~d57hx2TDF+9_;BJma85ktz&YGw1LSn z(iSGiNZXhkBkf>vjI@i%G149;$4Muc94DP*a-4LA$#K#-CdWw^m>efvWOAHziOFE; zDwDxd0h7Vfjj%xS7L&o!9VUaNdtoElbDzoa(qksaOHY{`FTLcL_EnGBJ>G8rNjG8rPNrgqkbNSdi(79oN7b(YRKdSsWFoiq^3+xkeV|YDMd}q zX%H#JOkLL?S-LZIs7PVXu=_8XV z(q|@9q_0y0*i*=4iu8lY6iF4Hr=BZ`@bx9gojzomCA+Z zv!?=+^Q6j5&XcM#IZvw2XWcP7)NzMPsa^$-74GF=+T zsp--nPED7FaOy&76vtU8jpjHDr7=t{l!7?ULMfQZg;EHU3#AE6W=IiCW=N4tW=K&? zW=JtiW=L^NW=Qc&W=OM`TqMn9a*>qE zVlq=&!(^tE!(^tkj>$}E1CyE3CMFk4yO>-o?O}4Uw2#Tf(g7wHONW?TEFEETv2={d zEa@CG+0tv~v!%B``+MfIrH{;KOP`s~mcBBdEfq4qR5FQJUvjA=B68_UM1X3kR3c(C zdrC!wSuB;xMC7xlTtuFFjb!&T9huxH4T%V}*(eQ*7;E!Lnma9D^+-yc7C@#?8^@lE zX<-(Rq|9k~>PJ%6wDIi8o|dbABUO*|w|OJgj2vw9PO49TCpDyhkebs!NG<6fB~$uG z$(;U0($l|4c662KOIL~hbd4B7*N9>C5+apeLZs14igbEOkwGsdGU=s67QM8{rk57W z=w-#8$b3~xiOoE}J8PY<)GCG^wtIMr_YNcI?}=c?-p z&*}a)b%odT!8Y}T59g{Ux^u32qBrNNC;D=(dZIt)swW0=u6kk+=c+G;F&qFp~|$1ST7ZNlZ2nQ<-caBA9F-BAIL;qL^$TVwmhG>PO|P zI*Nu-0c6vtK(cvMm_>P63}9&Pl(mr1?o&7@xRjq1jp{!H472~66HNle;{DNNdnsZ837 z2qx`CB$M_cib;crW6~hvnKX!msO^;uVh)oAk<6q)%#GU0o>V3sL^_iWB7;c>ksXy^ z$w4e*(m||X(m||>%45$OCSApWC=acxIP|k0Vcu07i|Wjt6U@7clN`rYoaS8a;u4eY z;xd!&;wqEwqJT+vaf3;Baf?ZJafe9{@sLRm@t8>u@svpq@tjEy@sddo@tR2w@s>$X z@tH|a@s&wWQOKmH_`#&7P(_DXcnVE)K6^}}^VFS1iD(aPXHhCTmtH11K-F24i|)*x z3QTqu6`AZTDl_RNsx#>&YBK31YBT92>N4pi>NDvj8Zzl68Z+rF+D5z8^A?s&dW#NB zdW(+H9_+DZ(p%`6^cHqZb`_4%p*CHGGm~9~E0bM?d-O#1crw{lcrn>kcr)oEx-;n` zdNS!Fe3|qSy_xh8eVOzT{h9O;1DWhDMlji3jAF987|mpNF^0+RB8bWEBAChUB7{j_ z5yR#56>&`Zig+e{#VjtTuSj6hSIlA3S0pp(CsLX86KPEPiF79YLF)4;; zd@&`4=Xxkukgm5K%FC>Ui-orbmr<@tVna@s`PW@jhmVjtwNoFqtWWn9LNxOfD4(vE59TiaAU!70FC46?0>IuqTztr6P^Vr6Qfl6=DOK zBQD1_sFfqGGM^&~n9mV6V#925#4YA?#2w~y#62cA36r=sCYywa%cU!E0jf=+L|i-e zl!^%H8Nj%SnvWOpSQ%4>`uOknm*O!Cn_2poC1384=K%PKvEXUIu%d_asR)#dkfjIlZskn%+-tOYbLJ(g(=a^Z~M- zK2Wx!50nk`!LlcPu%JKAJ@+|r=Ie|V}UPB)(=g@=X1N0#I5PiISl0IHO zO%IhX&_m^m^ojCS`b4>aK1seopCsR+PnPe{C(HNfQ{?;fDe^;lnEaR?CO@Tz%dhF- z@>_a@{GJ{mf22>7KhvklU+Ix@Aw5$5L64G6X8V(3_F$W6S)oVECFn77DSC`thCV~C zOrIfFrO%XW(`U+c=?QXEdV<`Xo+!7ZC(5SuB-xRkBs`qUSJ?Sa37k#em zO`j|K(C5kB>GR~C^iayg5>T+XJikeAU{$Sdfp z=ZM89Sa>%E3%NltU7u*)xI3$8rRdkL5@vAImX`xy>KTaZEmz! zHqYgZ#KAUi;tt$c~exAJ8s-^y2+{3vVY?t=V zSFKjOnN%x2OsbXca{|epOsW-MCe=#sIV0KAmr1QMiAk+8g-NY4l}W7@i8oRhL(4CgrQjE47mX$i_*5 zWYeTDi}FfyCd(@=nJllEGFeYaNaCkV%A6z~|CLmF10{{#N;$(3S}Er^LM!DWM`)#7 z;s~vj%N(JVa+M>rQVKYNsd9@+Q{@hmrpmpf*ruk+eI`wnhfJC(kCUR=^OQ+*xu z%6lfwmCs3CnwTqJnKV}lnKV~^Bz0zwDmhnet#~KrtE?5D z)V|3h+0&m%CuL5uzm1cUoIKdZS(!_BR#NFMN*dioNvFFi8FW`AlkTQu(cP46y1TNB z?yjt$dnl{u9?BZJr?QTZ?x}3xqkAe_lC`Zpm2G@tY*(1KKcM9BZb$2 zN@fbL50&f`ULPvUQh0r+tVrQCq_Qf7*O1DZlw9>F<#UR^%_!w-%3zxSrH~$={GbOa zj&uD<=edJz#wf1zF^W5Vtl~)@t9a3a6mNQv;zJKshRo&XRLZcq{G3V|F*lYyqvrB+ zDrNLseom!~nLC3$L38=(lQMyGg(#CaSEw?D9;!^GPf#N06O>5$L?wzoQHh~XQsU^7 zlz93yWgUH*vVlHb*+rkO?4idhXXvrYIr50lq zdZO~0K1X>=pQF5|Co5m+$x0zTMfpKbQB?DI^j0+Uc=T3G=<^gopQkAFG^GMPO{qxF zR7~laiaC9WVohJ7=;_&t9X(qy(3dKX^reb3eVO7)U#7Uzmn)w1<%$=5mC~EOO6f~q zt@Nj_RtD17D1+#0lp*vSWehz>38Jr6g6V6O5c)c00)3q_iN0Q$LSL^;rEgFo=o^$s z`X(iwzDb!y->f9iH!E}K+mvkjHf0%ohq8vgL&>4_v>$|3pzBp6i^b^Ww z`U&ML{iLEw<(Wm%r1H$7n9xrtf__?2=%ECFqNiV0?C6&j1O1BPNWY>u)2}M7^s9q>X}b)_f$ zhT==Vq4cKTQU=m*DTC;DmC^LO${6~6WlL(l>b|lqHGtfa8q1ztsbLoPl|89>>if#R z)EVqKkeaK0qFm)%Pm}`A^;Ee*f2!P~KU40|pDFj~&z1Z1=gLF+3*|BWh4PgCQh82) zsl24WQeM+vDR1eomG|`5%18Pen+OVP~dr&~$|`bVWA z{j*Y?{#mI>|Dx2Ue^Khvzbf_VUzLXRLdA4Gul5!5`Ft0kw4ML0NugpnpYH;c4)gg= zLFqXE3wx~R=c>Oedd~G-vEy7n6a)Q-;z(ENyyz;OH(jmsp{sS>=^9;6x<=vR+7I^86CX@8tx7NAR+v>dNZFSys zOI=U8rOub$Ue}x6Ue}l2NjD~qpPuQ0()j6_Zc>_L+DSJhjh~+Brl#@JGhIZQmOYVa zd|#tW<6O47bk60VTSa%!t)V;Xw$Yt+JLs;uGaSuTcaEdE>Mo@%GxL4!Yq2};uqv`>Z}Ew*^{t3{<=F0 zEZB38$)UQc>G`Umy6WixWbO1ovTk~q#ZX=S^gK>&m_Cv{jni}00Xp||o(*-L>3m0{ z^P-Q|dDDY+gE()nZbdq6?;n=tAh>x-@#Y zE}b5s%b-W-GU?NFS@dbTY!xJyy4a9;@3$kJIg;$LaRbrv;d(qQKvmsY;+ce($SVtj97FkJ+fl{#IxQd$^!8 zdSVDBAqvUJz-ny59vsGBn8R_-v0lb4yuoMui&9*e>fnEWqTn!^f?=!X6nipiLX6l7yHcH$t;;WC~fg|Eds)~`@Z_}>_!B|5?jy)XU^xf^O)8(U^jG@Ov?SSES;%B`VdQ*n&eii%YnN*Z7VS zg4+lhq6J*g8GR9m5QHHP3y_VC*opl(iZdv{6MRJpS*7{|4bTE@;Q(*=VI(FZ67fhv zCYB=yyKxMca0l;UqNr4*Q4>vIgDx0=0K{V{w&E}@;x^twt>f~5$7NM(*un+fF$|%I z#C)v60bIlr6rvP&q^hWoX6OVr^ub6>$9(*W-8hSzc!h5$U6QvKf)%==H%4L#W??Qe zu@1X%7-w)7&+#6bQd|zyLJPEoHN4@6K^TW9%tki;#4a4dDdgfAw53(5lBkaQXaOsD zpeI5Rg9Kz^1=e93_Td=L;1X`&DLzA8My1lB0;;1G^l(8B48?d%MJ$rB3^_Q4v$%-| zc#R*ZP?rDE7@EQwF7U-LjKfsKBLz#5gM&DMpN~nbS8y8-@e&_lJk6+FIj&n6dn?wq z=!DMbf&LhVF$hH}bK$J9n1@WP#0G4`J{-dtq7vP>hE0u{@MD9MPDKO!m!XU5IR~#TM+t-zdO6JcFtNk8`MuhA@L2ywDdx zn1lo@z!Gf2ZXCgBT*Y0yLLo~0!Tk$0(Fm>Z`|DeLZCkY8x@dJ|^?(n2;g3-WMi^p{ zh%_w08f?ZM9K{*j!ApFCsv@^PR6q?h#Lw%MwH@?uMi=zKAcSBlVv&gX$iiA|!(m*& zH9W#=l&Qr15RG68;yh1J-AT{wa>xQyF) zj5qiUbrr5}{DGQih*oF^TX>-l#v&ZCn1f7g!X6yKO+3dZXn0{#8dXsj&0vlWu!AeQ zqaTJN2vZP^WGu!?Y{V`c!QZ%!`*?v5&{pGqhl;2P21off$KkL}EVv z!~vYaCEUORyudf;s`FfmYN!VbbVY9rLLeq05;Kv4g;<6hY{7mU!x`Mc2Pidu-Nrmw zdtv~FA_!rKK_b>+I}YLu@^KHZQ3$D~ivQ=aN>v5*(HxerhbQ`AFpRgsK-N%16m9>$ zT`pvMH8x=<4#4+g?cT-&m* zBkW=PIBu-VS-Z0K#So0gbR^;TWl5*6#6}#%DO|u++{Z_l)aLp^E3D?2omh?McVP8G zPxQwyjKKs%AP#eohAgbY2JFBAoWM;yz(WDf;}2*5aS%?_QbH>Y!QCOlwvf zIKl%x;Sb~d1lFjcK8rOC%dr{8&to_1Vf;R}aXnRR`%Y0eJ`O#le}<+m_iy}xrZ9&k zY~TPl7?0DnX!R>v{fpL-MeDet_4oB*TqmPvoIGKSdO*Wi=#M;Yq*Oic#E&l)aP*u zwb2so-~bQ!!5v>$qeY}D3bNRxmZoqW_oR-q#d@IfC8#%N4M3=*&#r|Zbi;6jA_9p>!zygT9-Ku1UO?5D$2e3#eYA!xJTVM`n1EO$Vm_8* zBlhAL@^AxB@D`#8w_(&obF_yYywC@OF#*v?Ll#zHBX;5-{=x-3#3%fV8cn%x!UA@1 zg*WFiztt z?&3ARKs4vF!Wxd~jPB@%p%{a3q+$s+;Uq5L79K&}g2!nzg&Eqz1-&p3V=w`;Fb|nn zgIzd{Ts()mCEvTC5^A9Z+Q1%O=!yOqjVXx6Y@}itcHjt3<01-hA8+vuN-Lh%P!&zl z0d{bM5Bw2^c%)$kcHuB`aUG9fy#Kvo{RXWm*A1$`ct5ODv^FVP+puyV;8 znl%(LMcWCisaSzMID(TnhXOpoTYQIX#&Z&?qdD|&K`;1YBtj8^8A!%LY{EVq!x`k` z7M|b(zC)OEe?UvLgB!Y{H$pHKF_?!;tiU>K$9|l^d0fXE2n${dqc$wz2v2lJUkt%$ zOhGhKk&T@=h?BU0+jxXm_zZPxUQ3}os-Yg5!wU9rM>h<@D1;&&saT9P*oqUli0gQa zcc|Hh`x$y;5XNIM4&gQ);S)-<<>fS*z#aXOg7tWfitTue!#tcoF7D$s)Rvr%CTIgk z^ui#7VmjtxCD!8xDz)da4(H+Bf!8v~#zR!H;_($uh{YT%#wr}cIb6YQyunu}9l1ZC z4w}FW?O}&5@P$7{BLv}CgcUH}-q#ha+gK0dG_K(ve1@tM$44Wyf+cL>4j&9g2-0v6 zXK^3KV?SqoU(^d(O{{r*Ks7Xk8H{sQtoHDRAN(;2!I*+5%tk6!VhfJKc-{-F*KiNd z@E(OIWyAG~+GqkZbbvSb9fc|waY)4qtiv`OMFF1UBUE~Bqo{ydXo}Y81SbqdC?b%6 zR4m749K<k@$we1-orr%Y)>{yKko*6L^k zOL)V0+ySfsn1C5b!7{AFUYy2t+{Z`Aoq2467bapEwqP%g;VkapIsQcjFRov-gaH!~ zg;_|)Y8=B^T*f09FPG7GdZ{kluFxEoaEBiTVH85}dwwcC7K^bK#;?Ox*1b4~eB8uC zyuxRwz4;iZg=T1tPH;qL^gw?M#~fr~6*giQj34_D>q)%EH<)zgdkR!XBluwif-o6T zNW*d*#3ek$SCsPMc?6xY0lRSt_wg?(cjJ2lw1Yj|;DbIGfW$(FpR}?Bq9sjaR7Jm4*#M=Pac!d1}^A_*+|D) z?8INVi2Epnt{3+g*dP!S5sgGF!b%*&W$1jlUBD7U5sIZ)kF&UqSNH+hkJ}NN!wR10 zg^>uxTo|v*Ka1AQtjCatm(cX)xdpY*2p!=LUkt!_L}4K|!+5OStY`27<@#`4VF8w4 zH8x=nj^H|;p;TXfHjAOi#73OMExd!$kNXr12uD0pum;=k7cSsE8usUYhqkbW1ANd2 z!!Zuwh({8Pmw5r}I_$+=d_&CvyhcEG48ugkVL1-sJZ|GV>J8+61mm%OvtV0~9`Hv9 zrXe1gSb_D}f#W!bn^606yM+^s$Lh>#yiXf{#@3@)A5g51EY`;t>*2-vjAA{dSYK4E zuPoL#7VEo<^`pi5*<$@lv3|E$e^#u2DAxZi)^&sa?{oa4Sg%#AH!0RFiuF##x>K>< zrC9eX)(02sfyMg7VtsnCKD$_-U(}Zr+qb${-(0NkE!K}0>*wj$a2L;@8qDn<6=4o5 zxS}h3F$Bize>CePL?QvH$b|8<6|C!u`fk?aFy3~~vgYG1p5O!2LwF29dDMn+t}$yz zxS$IH5s5W0p1z57AM%Q}ud_bE8+<{Tp}&6D(Tuetyf6}>m=5DHX0m2sZPE5t*26Gf z{!^^ExP^!K4&5*=BdVhX+QJpy@WTYeAPMPMha3iFVO_1J;KIEAaY12K}vNYq9P zw1oj4=nmu8tuN~kj6paOFdr+h9=mZEr*Hwc@d95^dK8xrRxrRDz8HwHn22deLGh{rly#vQ!I z56FRB-)ILn@XsPuSy+utFn&FZcGC|R+s>t5!(BYZI}}11!)+6LI8RO1MraFbc%ml; z;P-Jy(t{C(7%WB(w%|B!<2l|#9?R<;R6%2y!UkQ@8)1k?GXBIC{Dlj+jUSMMc%6b4 zXbU&^z#kJZ3-hoH>u?x<<0|grJv8HZu0k`kMkhF86oL_k7$hPM%drmIaR4u%3g+vC z%4mqza6*4fMigdY9u?wk@DiFyoDcP2fIni8j$D{b z=6fED!!}%n{S=<3a0vEcyavKn{DlHMgglk|54Fr?$H~3%>Mqxa{Fat?gh-KJ=bGV8-c!D?h z0!=KpGgOE1V>V=M1xwhX3wmKZjHeq-E9xml>vGo3IELH!jEZsGXVD5aaDo^5U@)d& zHWpwRHex3Z;VcSp4jVkVI8(%A1>e~ zUPC*Z=VzFs3v#d(hjA6p@eMT+_`LA@y7;{wezgDs04im?UyPLQ`0vKSpCR zval9gaTuqOho|@kc@AGo)PWUT(F=iyK@zgD5&Llw*YOnJQ6h=^BkI8#JunL4NWf~G zz;!&q8`MbVX8>poJ9wcdMqx7IFb8SK!YXXX0i4BU{DbHC0(AgQn;JZwx{>QjmqUIE-_+j%Rp}9}x4o z{h=D_qXn$sh@KdRsYt+jY{zL_#C1HydlbSXjr##Aqc)ns3SQ`s;TVfaSc*;9jnlY< zdw7O&3%ISL4Xn`_eJ}(wu^6kd38#^V`*?vLP|~^1(HL!D4L9_{5CkI(^RXQJaRTRY z4fpU2#{1X@*6%2@kk1SC&<3604j=S^@%kOi8er^5LN?apAkHEmxA6*vD4D_MiSCHT zava6qFn;Vj*6VnLcQ9GR?GCnZg*W^Vj}&C!3@+gY9^eH&K+fcLh~_X})&SP=n2sdu zf$?WIhgprM8@qA6&h`Vm#1BY|`C8!jOjUY4G=&B9`2BbLE_CBu57vGdU$kvJb~t@T z(e|H3>*k_$chP#dXgyW5<`%8jSnn3~7px!f9a0vLcc_KNFhd8p!W;dNicG9PK5pR= zN-p7VF<=U7IH3#tFa}c*hr>9JZ>X8g_iAVhCwO5rLXn0gSdINSjk|b-+Dmy}LuVMj zKHZDfzN|xv`e@cLEJaSyzAdbKa0I!yhI@Dc)iPdlp$^)^1AQ?HQ;>iYFy5}tvl>6{ z71rBD{SoUcd@9;jFX!|Ia71VL!+0IWvL;~>w&EI&vA^t!U&pV;S`QsyoVR0j zFY4V{`(t#`_5{{Q7%x{G>l~zE3AW-O{)X}NJl1DL{aw-egH^2Lwu#DU1>A1hkVuwKGFyeiuFiPdBkKd(V;w1gg>2u38X;T~$O=6MXA z(FY^20LyS1Pf=nGw>y}_7A`Pe?k=o;7>DqpeX&JrBJ2F3Zu%#m4?-{vvoH^tSdA^% zkCV8FJ9vV(Xqdxo93eP|s%v@thZPKXfJ*E5JTMSrunuofWj)s`4CsOp$ii-%!8JU^ zXEfZvb2>U<7-nNG-lFtIo+mIIL5M*LGO-*d(Qgy4(Xbh3aTyQr3FS8P+=Y5*hSspd zB+SPp{Db%S4zY!=H#)!t{uqTxh=cKVn#!65g#9$?^ zK);LoI|8u)+1QM|xC!-cu1i?L7K1S#${udZFoho?@F%w56dt0=Ud{(Sd@&j`k%0~9 zxsS`TpXWMU#Cudbz{khmc#9wCcaZZU6bZP87x;)Fhj?v(IXI63R6NYr1}~r-;c}uH z>Z3Jm(F;Qmjulvk-8hbNM|tfITZH4nrE;n=>N{kj^pH%FH2m|2WXY0Dkw%eor4VvM zi7DjXl9$QU(k79-N>}lhv8ra0TE$m{BN^6e=Kc_87m;dWAjpy@P_@5u|_b~(h$7#mP^4I_Pc*fIKW&Gzcjh}<@<0WPO z=V``|XFT7P|8Y#?`3@}k@8>g~RyF%SPy7Axwx|B*X}>?-g8BdbwCf!6!MguE&3H`X zX&?W~<^28e4sQ6*^BF&$@p7Ks_@Af!K41GS|NS)M*Jb_xIL&xW&B6bDzAD8|GhPPc zZO*HiF>jaowKetUR`wI^&(l`^+!|I^`MGuB=T_%xzqZOZH=a+a{cEf5&#iXter?74 z+)8Z!Yb*QbR-Wgtt;0XJ{_XN>>(0+DH{V}dN((Ea@U2L9X{ z_Q$WSsGnN{>iya}{&UNv!>=vl|D&GAF6&=g75RT)^Qy}E*Ou}Bx6b#GgMV!e`?*zP z)UT}>Kev9ENUDEL1TQXyN)3%9tF$nY%Bdu&vPwu*RkBn~rAReZI+Q?3ltO8gL0ObT zc~rn3sEA6aj4G&#YN#&NR@FdFsgA0aR9{tFYM`nkHB{9_J=8}7sj;e|)I`-tYN~3C zCQ@@%Q>g`yPc8U&mn~H-q*khyl9{TNWUexWnPj0dm)fW-q_(QoXoI$B2TQa^2Uwva zI>8z?;D2ADvV|S&VSoc1;RI*6z!h$AhX*{-8D8iDZ*-N~seI5)vQ%|P5A;MY_)48r ze&~%p=!<^nj{%Y`?{aIa@|WyXgCu*^U<|=f43ivH!!ZIQF$w{a3;)*CRTU_?@t(IH zs;@`CHP|d_F%$AO*5|D^FNRo{27<+~1 zm@7QPT;u&)ukp^T*HrVR0^WDEK(#=+#XGFt;@`vG<6Tkz;r&kU^G>D@c#qPDsx0ZT zYKioW_aA-3zmpodKFenC3wHl3hFZmthqhzk$ zBw468V+;SgjIENhdKaP9^ zCvi&Zr9LhBs{fXHtIyyp&PjdM=WzkKxQIMlLcZj$zKko<5cO4Qr1}~Pa2+?K0QF7W z!fo7Pw*7aqzv_QyueFok@}UCseUaj z;T=_%sNdop-s1y4;uAjO3%=qT3h^C3@Gn$iiCPT}v|_2+1d>>$7LdhqwIWuibz-Hu z1WKY5N~4Te#rveLQkN5ds>`E-$Wi|x)~hRuP3lTwv$`^>psLuSu7>KUA-1Y(qL$dM zt}S+|>!7aKrLHITsOzHv8j5}DM&f|FF`A$$nxVNkqHZDnR<{%v)vaI(Gnm5ytc?wJ}*S5ZpiBT8$!i87k*qO7KeD5vQu z{?PP-uc)N)6O}c+MKw(yQA5*L)YkMve+q>_#tDNaSU70L3wKS3@X&-}0w#){nn|L!W-_KA zO!U`GMK~fb4Uw2G256!XEdn($Vyq@s1Zme>QZN_ukc#<8 z!vZm0la7VRz#?Q~u?W#*VF|LaR7}(?!*UU(Ss`LIE3rz<(5%K9F-!9&aVCz19JaaDp>j;40f`-QW%nc%n1B&_#CCdZVlCr1e2JbVm=lhqkBe ztL-KC)%wc)w0?4bZEtyuwh#KEANpee2EreMFc?EH6vHqaBQO%95P;DL#2Ac45XK=G z;}L>TOu$4;!emTA7^Wf|5txQZOh*)=5rbI7VFuzc6SL%T+Sy1zBId{wwMj@u3g%)S zQZXNCSb%gaM20+By9k+BEYH?vVF|La6w9z2E3gu)uo`RdCvvbB>*P7w_1J)oa*}qF zoTA-~E!Zm0)o#Of?7&X!!fx!rUhKnu9Kb;w!eM!y_6UyR7>?rv{=!L|!fE`CGdPQL zIFAd+#YN=d67q3bPSsw)Ra}!Zv<31K?RDIcS7>j_E48=e9PMp+t@e(*L3>x;qP-_? z)&3(N(B8)bJj5gUp!PAI$cMB~@eI%L0x$7OKCFE$AJOt|hT6AyCm++k#|M1GC;7Pc zGrr)f{QuZ{52&V^weNc;*@1+jNVOpdBB%rsS_nnK0-{nB1q+B6iYQHrEg}M9!`KzE zV!_^f$KJ3jc2w+L>|MYA49V7W&V9~vo^P$Q&U)AT=3dv&f37KeX7=pavu8HBPYAy0 zo)mo7Jtg>|dr9z9_p0C*j1gTG@I==Ie9;Yo0LF@L3WP8n(Jg_l=(a!v6N~N$^kDih z1JPZ9q3EHYj_8@7F3bp4PxM@13^NhE7MQ}!L~jM=Fbi0HSOd{}K|_&9JNKBi0($6lMdng*6iyvv#oNB6HRr<^XFUYQVOHwSqaqBrqqKGfXOK%*tRcB1_g4 z<_2?zdBEf_PnZJcC2GQYi>z25n6Jp1Z4GM!^Mm=r+QQnw+QT}CY}f!;Agm)S2-XSK z8P-MQ$OgkgV4)%j8zypMyTZD`!eQM-&TJ2nl#LL{*hrBJ+f(Gq_7b_Vy+!V9ACa7m zf<=ow*}kwCSU-`1jTL#Z{Y9-=rKk%VCkkfcVF|EA*Z@&CHVHNmHVBq1>dmHz`m?F9 zG*~)puqc7WuK}`|qJeCdD4ER`rLaRpsq9cu8aqrhm>n+4U`N1Iu#uunHU~CJl*Q)4 zM#J)8`LF`f2zHEUBwGj@E6QQV!N$WTz$U_qV3S~K*ksrg*i_gw*mT$o*i6_g*lbua zY!0jhHdmC(&J*Rc^F;;h0@y;>B3LPGv1kmtL{!Kwg)M`X!Iq20vMWU6*p;I3>?+X& zb~S7bY%OdZY`thAyFpaMZiL~N_1MiKHM<426}Anw9kxR>ncXRx!tR3Y7ENXM!1luS z!S=(-VFzFbVTWLcVMj#M*rTH9>@m>{_PA&!djfV6c1kphtq{#-Ps7f@&ce>Y&WnoK z3$Tl#IqW6aWzjsg5_Sc46?P4F9d-kD6Lt$$1-lKqBU-@Tg;m4ui59Z=MWyTm*hA4$ z_K~QJeJonfK7l=jJ%c?LZDL=DHnT59Ti92kt?X;jHnv8zoqZ$P!M+vkWZ#K)vF}B@ z*$=RfuurhhurH!L>{r+~*mu|u(O&i^?3ZXC%ZT@}JQyD)fU)BJtPrLH(-oJqBA6Ja zCqBgL!wg`CusX21Fe6w!m@&)*W(qTdnZxjFZft$=VYY$z2-{G6lx+lS46}qafmy+< zVNGE+Fk4tNm>sM+%pT?dYau?ywiF*{TfrP*5||Ur8776vU@kCMm>bL;<^hw7Pq3ci zQ>+5!1@ngah)=V=u-33P;tQ-F%pcYk)(+NQe39)SzQhK=0>#%@{H7WkB)-LVf^~*< zfdz|ivmxR;Y$z-Y))m%GT+N2Vy2E}{Jn;qHeDPD=0&%ivjJQBlD4r=AD?U$`@K}bd(vsCAmN5g#M~7(%5}yv^ zMI88un9d;aiI@%bz_BECBkY;?MF{Io(lXw>^Pq(ry6TQvG>OLb_=H= z1u5Td;gZ@+n7u-5Pf*`pVYSA$U-(Vq+b^^x(eXJTYz;-e91toszC&1HsP!EZuF&|7 zaK|_zJgD&<<9x@2eHJd)#QQ2Fzl%qX@l|+6 z!q>6U_*fkejgQr7ukq>VL~DFH zI!PLzlTNlqxp2yjQy!f1UURxQu+UqStS9@(`amUEg)m}$KINuOm?R7MaJMJ*u zLz?nE)77@_GhJ=#zR=aS&g#Jmt*IiF zvetj1No<Y&c(-{xeOxgz10L#7oxKjIaNmEkv1sfd@DHL3eLBR%Wn-A zx`qo~$NAQAz73pj1Lxbq<+6=aJ2kN=8K-&=c! z(D&BP!kxXfZ6M>6E2rE!CFhiaQ!6;Nic@Pi#q-ggh1WjX5%>+K_K`m_qMvL(Z3%{Q zYB;AA&V_2rcR{1@YinV(K0Ie?6y9X(ri~ZGDFykHaQaD$(5k_l%H&iwr-pK> zh*PULCF!Zn#hFuvy|g|Vr(8MZ&M7&k6rA$rlrN_SajJ8a_MQ^VsZdUJe*!v( zAuM6;fX9L#QV+=|jr_46Gn8Ks&w|7G&{1r?pSje9U7V)Qo zYNm?%A5qT>Fo%{W7w^cBIQTy)<0$!HC7;wz&Fq4{$v;6!T2jq;;0h_i9y-yJK^5L{ zA>}75!k#kGmq9u9Pe~jB7P>0=jX{}!l)qy)U+7$hP=@_Kj7Hd{Bc&Y-s%Se@P(BHn z4Ud`$5tH2dfoeQQk(i`?Va7OK6_CG>)1H zgujCN3t3WP{o9TYNjtJ6O!ZRFe%U_l)l0e z=nAR}=~0xzE$}FXJ1O^rYUT=u+hEo(%m2m0AI1r2P7-_O>bXxl(gCQySF%qMVbtE2VERrU^4GhaYw+W(bI z6Jz3$gB46iP{~A4Pa`s9U!)+OYB6hqH@PWQ)NiRrS}CC=DOF#} zR7y)d;<3~tS9+vgB~pnLPryjMR8!KrUJ{a< zoZlXLyGBPtZ;d)69xLQKu@0QF4*HX`B3CTH=9;<>OdOeL_EhhQr z>Yqc*9O^Hpan{n%hO|7(C3VfowP^uH>UF2=4JsK@1~rocU9R8J0yH2wS1@tV55;A~ zqhA5~cwPXPdtU_$*;>yd=xbSV0-# zNy?*QMl>KE!U)d^lv6-G#RAaBb15k2Zv zNbtR^1h%4*`#xDlZ>Wi!f_ zlu}AJN-{n-@gS|y#KV(%d?-nqH1Y7Gx;b6jIW zHR#Nn0j~5WXStgvVRuSn1<|II?mTjIB}3Y?f>O!cu_T(%-{?80WEwOfN3^0m3%c{J zf_jQqV1yURttVv^WpB!PENO?WRPUuM6Q@{_^G<8QtbrcNzXC?k_Ag-?Ta&ujfM$lI z#mdCwjO;O915ddD$*q{4>(#VI&OCBlHFE)>?)2X0%p-A>G-oA^Szqx9p@l5TGnZkT zl6*+2axuw2jjw`dukR>$)XY|BHDhK&>fRj0odT>Dhl4dfdEhIbT1|4uWg6R(n1m`i zu9nd8wuCtjPYH7y3}st2BYAcP0|nWjoL>ssnXU&b`By<_(^p_2YhXv>NI-8>LKPDX zVoU}rm^3ihbQ~z>M>Qw@Q6PFFxW)7g7!t+U7T9J|(Id+Fh0u1UlR-7J0+jR1sfU!X!0ZEbG5d|O zPAmKshnWqiX8b6_z+7ekDCZY|cBbSEE;8FlLvMj9<};W}S-~VY61@>DCqG$$y6=Oo zpm#tu(^NuomVp(t%`5q7&?Qu#jh+g9HhLkAvw_BWU`TQ!?NiA=1&_BWxwjV6K2XS# z&Ik>KU6d8pk;!sU>cv?-z)jH&cyEy zrt!&r(^nV=n9&DA*+TC6g&o3nUfjXb&IbwCL<$P5T!mTc`id@su>IT<$MWP!L+8# z0@L`lEP__mSqm0w=2zFD-RqMa+`xbxmW<4DKI=+6hM=5p0;b_DJaQC!=sm`M zpqdE+E9iVr%?zae0_vYc{bgVpe+~6)qIx&gCn?W^h3s`u&aVM;(3m98eIBHh&Vp*@ zJ*B&x=*g77D0_O6<4yq8Ofl7@oN8txw2C=M{pYCvF4Z5YUsplmSb`Od6V*OclUl2p z9?&W#f%>zkpOj6_%!Zcpw}EN={a`Nby-g|A%tLsN1wRK1Su*plVEA4nRXwnfC9|&* zCWv|pK@~HJ>gk|)Akldigep2u%cX47ptm=E!Jt73sH&3><}wRGIe!^gLA8of&1|Qh z!%C0%0Lb;+qE&s%U7r0nrMJAjDTODWIG`oaQhMbmx&!d{QH#2Q}Ib%K0-9nk%f0c?%kIX>cA( z?zyvQ%wkH@KoZANuSGlDyStECO||$1bUCG(`3haa2-=e)HV5(CL3JC@okyMo)94f8 z_AVjRp8#&}Qrp7gp-bpfRUu1as+k?|xbu#JZRxpCGcTd#d_pzj(1C<{foDTW4uJyF zK93vqfk({@rQ8atn4?r*0{0jX3Lv3bluJMrv!Ciyl;pgtnd{J%eA0jLtk;pS1E^y9 zQ$2)o9_4=OzYbRNnIIC=1gxNAh&xXT9S}lVtP|xDjedkSUlu~xDTK7Yaadv}l&vxG zD49a&(KJ-fFM(ERMx;&9YUUdCe4@HhXEI)Sfoi5BWelid@~EByD(Ol>3C+RGlH_k@ zNsbs!&!(D@bRltCgYG=Sq5Sq>FB((LBtWChDb0dO&aR-EX+s%BJ%hkX{#fdn0h$Na zjy!9i3t6Huo`jIK2r{k~Q{s6Oo-}^#NZSp1i=`6WW=T91%yj5bb_b|tZiCLIWgXhwx>|BQ21qxYm2PvUf z(AiL;&xR8J*-&z)*ke32jMQ{Ah&cfIKrZFkP;%XM>_$B024qwT=tkP}d?=xsxrF#? z<`(6BP$qarwNqE(mxJbkwRf)8)DuWOwRfd3=v;=3>S`vIhFW$bM{f=8F-`#0%rNk} z#dJ_b{c2`Dv{O$q5<2xH`Bc!_IrXd^A8P%iMmY?jnwb-hzv#AFM_CD~n6IFk5p_o& zup+5EyAu|&t>E$OPIB<ho_Kz37UpkN078F zDO-caVd0cX)RRd$5_IMjQtcQ?>PVKV)l3waOUD)&OGb%3#)N9-1q}`8Nn(xx)yyyB4zecn753{zT9~ZA$pobJ()ffbhO8+W^de{6n2udB z^tfuK0P)i!NK2a2yI}^^6DfyKs%Se8L~)-H6T}3AFJlkq->EOW?x_*tGl3z5%wh>6HrB;Z7D3eO&6GHS{Bc1n`UAM6rE&U<&t-jr%) zQXHPgsV-qoLPtc2;t89A&Zf~+&jI_$cVHTy zJTvCl=_isn36t_5_CT^q2bFL{=!>0A3OGZdbS!*1K+xyf!ObeY`>u!z5o@+hdH zPl$nnZ_w=;)EPk5Ha$SBi-Ix%d44Q6pcE4pvV?N}I~vL)k#T@Lrt&MspSt1`0@x9ycPC^Lrvx#mocM%ptI;K8dr( zxJ3%-?a^Q{lLo38{Lkw_k_vxSi8v*cMs)$ zU2Z@^W%eB?JA>i&N~)76(UclhIrEWWVBH;Gic~e8hV+A5`P+>lnrb3)Gr%G zN)is@IhxXMIMK`q!Wznvxcn=ab)cHrPx%p4G4)3hj{>YsFoY$vJymqiNX?Kv9XbCJ{7e#QVJ3;> zz$B4W7>H@@zSDdNnIy8mgH(vR*^hYA_zuu2hOmONgo%MhyMd`R&-A1TR8Ir@C6PU* zeo5qraz0tl$GQakG65M&F}li#M>m(0ytZW;K|2jZ6#PyxVWeINJZdHztYVgdiS|b+ zD?mqj&F0V@vKUKX458RN0GCL}`rZ;rBs6A6;A$swU85b)hVFFTcuWdu z!zFZArQCq5zLqexYp`n(f0g+bFi=oIHCg$rd0<=pyIf@76r&i>0lP+5Xwls=kO>T1>?wh7gN>+2iyCCSeFLV_{m@uL(*c^6gtpx7Uew3#gr>R zXVYC^6>|U#cf3tC8K2e6Yv>9(VplPrq16m|l~F}k3yUeE9h<R~338K4e`K1pc|j`Sn#ztpigG_OhRbF>fiJI7>D z;g~`7Ffh<@6!^ta4gPX01#_9&)+Xbmit1v@K*!|>&9Pq#20CsAm5w_>h2wrO(f$bK zDKOCSJSddhr}`#}_dyl&0h9>{?Mw-=Up$HQ zB68ky?CV13*qeb^Z`_QJy+(iaSLi~|*pv%sa2dEhF^V(^V*D_F$eO+5#|Pm*(B zpkpPdX1;?}jBqMpT}m^sglPs=P-15a8Y>~xp9#K^Yyjc&wX%@afjI?&Ui45Kbvwj7|I?70|j5eRp!EC5~mR*@ucx3 z&@wgv3>0L5m~+z5nP9Lf(b%(~JOYNYRg|AV?1Ri9?M%uLD3}1f%AAxcP_T(=;`cWq z?Qn6JsDzZ6j1qx@me4s=m+KRcifKe>sFXTWwgJtW zb)*ad-O{6}Ur9Nbas=fh5G!CTNjn$1{|NFiI-DYXN?>QZ{fQ zdL7*tPUDY+elE)g#V)(Sc`TXXV0X`fJZX}7MGKc2_;YCw#A?YRvbq}wM!L)duUjmo zdK0K-c7yh2H>jr?#QR#H%0*mC%0^Z#iug{@N~sU1W+JGL28-DQaED6)Z=`_OMOs2yI1Y3+EdXQK zWgwomKxqLv@6rNt-lYZPyh{tnd6yQD^B&4JUP?-y1?EsDH6rg$sF`wjDwuPint29d z#=eZS67iIa$$p2W9$A0$HzHc%=7i8dHbONaBv4L_YcySoH?kqemL0fp-0+rHtl;1(O^u{ZR#|BKXCaVG1O@Pj$Ya{+f z#G{lhpmEkvZlyd%d5*FgjQ4m(`I%C074bBtlv4(Qcz+Ttmdiv^Pc-E(5To=E(lg0= zLLp18uXvAot4T~UGxSK8K*xKyQu>2xrWe)y!4)2PlryNm4D>f5`NVsyhmQBy3*zlf zdR)R{s^dMb($I$>W*s#CC+g8#L)xS>SmDtXJV)OMs_=+`j`tWunL{~+axvu=%7c`b zKs^6~ZnWf&J-$Fc_Rw8RTCzE%Go>%6qOF0QacEt6H?Wv`$_+>>m5T}Iv4l$L5SnTX zrJAyYax3K_&{keSd7XM5QvHQee;s<7ycsyy-UU2oPkM#D8KF$x3!ZlJTAfW}7JzDI z7PwbQ&O&v|71Xm4EaD#nt6a!zJW;N*o}8%$phtRpFj3x#vM2almPVOFSxUKVhijQCf{+NGp+0r8ES7r8JguAY~qC=P(5f^jrdldv2vX21a{Uf#>W= z&sRztZzgOB7P9_eo+nvDSYRJY{XIYxor&4XS3=v$cT*mtJV#kg`3$`3sk4Q|F{QMo zbfT0~cAyLaAA81vN@)tXL_$WePo7huJ<^HRS1gBap*RZ~N*;l7#S73@{*zK~E9qAb zpfj%x*jW)nnL??eoB(>HuL651Hh_Z_X4^BYiU3Q2{Mi_<;)?L%}JE zzLbfS+2A(CEXt*no2Y*`&DT*-4UX+QTp?fyvC{QV# zM7a=LV80fuVm5;#{Rp=yPJnLdSHKotk3qSD*+FXY3L|_AFC*v{UQNKhE%!N+TKhnE zRkOTPSV;&luf|S3Mm-x6$Gl8 zIIx1rrhXOW1khJVQWY~tz<94y;2z`WpqhD0X|Ri=vIMieY{AZoK*}DV+AEzhA5_sC zawr#jEriF;l;l~V@oa=AQGST>Jmqb0jn@mxuV5uh;(Mew+)a*R4SJ-@srIGpOxYiF z)A-Y=9!LFZ%6XJ~K#%l`R9~a|HPs)$9QuUS!b`A+JR#Nt<%(utyjK8-wF|0;Qaz2b z40Pv_7Ra&R4{eiCJIWn_PLy8+mC~D(4=7(!ex&?GDcVcom{VF&I#7CmofT~;JA%62 zO3K01lS`?loJYBeatGy6%1e~hl&>j&Q0ng^IX3{^H0^0mJ?@n4D7#X|P^M500WG|z zQ7!@ly|#lrypDi)^B=VLegr1Ue^Bc0Cq1wkh&>e0R^Em(7?dkwz{j40L4|iNSS=>& zc#l2R(9S%f72by{H*g3;csDE+}i?@$o?Wz;hY9O+l9=R@atuLe6S zc2XV#CwN~5!bqw10HeLzQ$~ObyoZ8nrVzwiM$}(SSq5(L-U4=3 z90cWx#~@a`!C-IxL6WB>80>8eMtjRB+kr>DyHfTBmC|I&Y%tn;66Gw)rQlWXO<-q5 zIprDfsP|3qvG+%?gsvbdyn_yr84+o>)lOuUpo$?Hd-U-C@~&;qanN|*49ul_nkxF- zT5dp|UgiAD@R!i1&T=tXQ8aX~g2&MPIjEF=0k2E>hsnIL0VtE(gZ5^0g-sd+9q8E; zjP*$XH_5ZWY_Eyn6vceX)s$PnP4a`3=fFhyHPC>G6BxqkGP4*9M#9d4o(rqQ!^l;< zUSEyBc76bR2z$zy)_Vr~hVbvq0Q}r=oPZBwVM3TLObpY98NdvA0|a&OZ#|d=Zx&M@ z)&SNJCgVw17nm!|4b~36_OK4HAv{Jjl*fpN!G^;|z*Ml2uu%xhg^h;g!SK5rq5{|$ zSRrgIY#eMhucKiJY%XjI&$QlF*f!X9*ghO%KMZeMD15-$PhaSfe|*>a$-iNoKc-z; zMu%bX!Lj(C@1xW!rOU8wardSj`L59PtA%NnwBvrN-2%=jPLK(zrP|@ zmJlm*i3;ix9G{UGACesypA|*vlpfa~iT~+`&ln-h8m~gt$G>6t7ytb6;W6aco`2yd zkwz2z&8Ig0fPV-gx!|w7F^8Y(=fh}!?}PXz3f#a5v_TBmf$0Vn#DwA(aANRp2onHJ zKKt0$Kgr*evy1@0W{iw9pEjC*iKq4`+7SF-DX1@kiDfd7PZE>N#3RpCCV@$#k(wbl zyeWXtSa>rL9?N7QUK+mDem2s*avsW*g;+`OC;mlF$8ZvPjFkJ&jp+}KUzTP};BSZg zQt)3qQfJ{Pc(P=&nLvEcLd$DINeSZcy$d}`JUx6D#+*x^PRlqPX=7<#BtkIaDp9U9 zUV8IXj!za&>lz6q-m27_jjy+zib|iKFX%eF9P{xAjjWr=gK%S zZW@y@u8a$r_TuBl=ph|B3t2QxDoUCBN3DLhjT6F$!Egt`U-I$LGVy4A5}UMHIy0P} zkwj(ya{Ax2g%euYAGsvsUvf@sbI7FMNUM;V3<2X%di-4nEpMnMCg}mB1loF&Z{*DW z*>1nf?a1Jl!EsfQGZcq&u0%=yJb%CIigz!Wy8jUO?_2eEn@W&p7LF`ItNw1=*7ThC zqYk7NDd>gCXeDc0SATnUe&^%#NBWlSeVAuFR7LZ7y{Fu#&9IbY>ml(OnMrA>zBV!^ zsf~4fsxmDuDK*j8rbqVxiKmTqW>#!!Tx@b$YP_$_@c2xdHmwcy4E20sGc)5;`X>*! zMi!}=zBbtzsa~1N0r4rZnUa(wWky-eo0;M?L}p{15}TTo5TBV9@#pc7 znYA^SSx_8Czoe|;e^VwWDJfTKYzhhxJlsD$Jvm7kn}rfP#ipm*IM?Qom64s96_lEg z_HUNQ#fGNCu`}b9*%>&d<~uwY@q@Eb&iJ^njHDq+$?=Ktng3?4uG(C!@%fz(ZXe2Q zQsOT0L*kRIlL>uoVl#tMholXP&#Vp{`-i}oKUr^WoSH*v5fotSy=kpn9e(R$;;+A7O zj{RiF)8&`ux84rdW7 zEmdYDtwWr;Cc53?<5JR6<77>wjfqFk#4K2Qx3R`dJv}Xh-Wz01rB)Gy*L&Lhs z?4@?KUz?=1OG+OQpJClTyn}Uz@DQ)IvbJ(bfQLLl640TYi_A`HTU(>Xe_5mO_>3V* z%6Mr$&*sk-#!t%$@_BX9A$WTHd>)Tk9+27e6tixW!{LV}^IRt!_Y_UHYrd}D_eG{= z`I-Hdc5h#GQN@(3^jZ7JdUWKYamLwoZ;#Mjx6CJSL zH=DP0rR9-9V@CGO+UOBCFQZKi)kOSv}Nl{%}1Nsx_!RhEg)U*rQf$9-?p^c zc-VbQU1@ic-ii&Dc9sT}29|ah+ipNsR=St7voa&uiJJ#GVIJh1J}8NJozpYY;j?hOY}-7M3MH?5#g#73Uzrrwu`irw4?UBlt0!-bAX{khyK+8;xqn5&RJ3u zQeHdOK&q$BQXu-XyWoOEci}M;Ml{wj9ecR^Rrtudzv?eFXOg=gzF3m*Zs5@^;a0ho zc`;qzegEpTa>E66J*yi#+AmG#8wGD1^KqejtJ(-i=EqkQk_N}hX)}@xHn(kiQ((!}x2+!^6 z!%~Bw%X(F*JpE11NrS`ROsRMH;Y~yR9LJesgA*-=Uvn7ou=G{;z4DAVbBity_3RhE zp&)L)OW*yGP7BH&sop#t;Wl(yldLYQk9U52Yhd;8-Y0uoUvQoG@YDUJyGFdqp3`kg z<@kGRcC=hOYGPiCMincH~9OT(;BD^P)xY{2RUM@IB7I(W!^^|zjBN0s_B zf~0wkRCjVmhHec^i5b>-!}d?JO}f3_bs<>sF!OfD5FgXk#fFO0MO~|-E_l~WPJAZq zw<@^zf_D3E+U`D3_iAUO1@nVzx-J>B`oa7azE_sM)~nxf<=0IiWpnCuF0V0t$DG*M z@k*bco>wQ-lL&XWTXv+dU9xTlU8u*SN+r^C<2ON=&^X$Pi$XsM{bx=;mwj8+qV z>dBrpKl3Q%__S%eW=>jCKUVH0?Mg20dMw6^WdYLmvcEe&xUrG0Bs6L1Dsxx3x01R^ z4%_p9zD{@~v%vkYn1A-Yk}{kx&&HKV4~jIVbpf8DZg%U8vGDDHPc zb)_`3K*enLMm>{|{FU8($4KzB0SpYxjK(CYSonSgcnjF`s`ZAWQW8=KE`9 zK_1-7+9QJH@W{P}Ib_X8cG?zJ%&dgAHLC(L;37{_a1F2K!A)_}|#|8R=Ad!I}qZB;vjHTyk;bxTXjvTlcaRZ;?Z zn2)#i&(0cRRKTWMRXY`6{AdM@xoZHXZ zx^2mj@Z{EwuB4r=c=K$~&th|-M3xA-0tn%uV-5y$Pq*TL-DmC!;#t+OW>|uET)hhAYZ4 zG9%tt%zW^}eP%}Kl_4=rhIS|@^fW)8*+;lLv75SVbCUCo2KqmzXSKLD#JPKwskGPU zi|YPADo(}($ijBnn>_H7p37)qVs)pt0q5jCo(s1QjViNPsII6tCBo<6T5*ivl6C~^ zixI51v@RJLOnAIstWYYzKYxr~e>swnS+QkZmc@g^SgEm&So2(B#$$yvV@!WJAD_(F zeq4}+TxvdXR`r~I-YaEk%YF7}Jpa8>tac@hTrlK%L9YKee(`zOrEi_vJYd7~G`~$Q+Xg5o?$E)kSP+}$~`bL87uZRfvv_xjcSu~u$AJ0s@SgxeOj zT$bN>#=V)kmT&Kce4e!U)Dx4kX(1;XU7V6JtL5O7c?~}|t_i=Ac-rPyl;xQvllIwf z9_n&BGR40CN$;}T- zz8W_d$9v9vGs3FVo(t6xPtFgUT|eriyLn9249lR&lKt!4+Bbe>WY&<0u5$0w^z5AD z;#Y-rCWWTdF$wV<*`nk8j0^9QPaS-bzBF=rWX?==X`_yU-k;7bP1MUO^LQz7wm9)9 zLvH*&ZIe%8{+Di>)vo68P3lanGP)i2KJ9G4f(*)wDiV>x@>TXWS%^MYo(#-AIw zGEn9`_tyQvzE_!1{R1zY9e;9X{jYU0)CZUPZs7Y3{FO9s_C2GOMqA}!qAQ1erTIF# z7{6a@$8YliWZwnC_$`%VZh-;Z-Bn7Ss$FFCtE-fJ{qH^Q|4*a$qQ%LZ?%eD+ zz2(S3P7SK}-MfFZq^oV%y0caFLz>lnbz#MYF6*+S*7ct2R(79d7BsU_+v)4)L`j?9 zU!bnn~huW_I+Yw$M34g<1L>(4q3YRpl$dm_16yP#OM2N zIKQziyX4FAY|QJr@(}I_AL@t7C*S8lh|LW%H&=^VFkF|7!SMtXvlR zAD>?28x3D)zPM9q*}5*7J>Tn^IK}@TGwT0akM3w|qvDB&qXg|es-A9LH}vM&;a!7y zo1C%+_en7@S#x&3YRXQh%f?G4rS#t!$*%~pHVG@K8sT>@a?ggIa~oG%^2V;+Gwf~A z`4`^2SNHZ$(G#9fcf3~VcJ`4?ddtR>!$e@N`B{y#r#Cc(XMr#^^a;}KBvgK%{|?QE?-W|It`Kew#v{y@hsi< zSD~KCokM!DlWVT-w0Is;lzY^@Ro|uM&-do&w^dyZ&uIEedTP(G_^3WS3q8|17jKx( z`QWoFq30Hf^W!gtV^4RDc(NdUX7XA^m&+fAm#?lrqJN9mOXjt3(;3>Z|4H8_DOUM4 z`o|sjoNKq`;g=UV+wL!0ndQDSE10{HU=l2Sk(5{=;2LA zFEo=TJZWp(x8aF}HcijBd)n&hp7$M3J6^u#GP+CimK~eL^nMocdim{=1*g2y_T|}U z>C}5Qq-puQ{Db!0w{IHgJ7MvV*sZCHO_rCh4y-Xw`!UfadGpUZT~AE1J(;j?f#o>k zIKHoBL$4`2A2fZqZR08B)?wX+m;If>*3R5mHf+t7(%IP!uTLLml5OMcvQm^<+Gmno zdFkshrKyG8buGBj;%i@>7q)Tg zVLs@j$y_ei`|qtw(AD36ST^~+_*|N|pG?;@i_~I)%;1luXBO+b zzn7-)P45jx!KI>zKq8wY2QDxFpz~wLrUF7 zyZq(8dKh+kmCPV85xc3@h(mVlYrkbI#_FGDvPRZk$kGelYW(xwigi{tAAdlqdNh5s ze}!3({+@y2*E{p_%=ZtlDpK6}85OYpfL%@EwIex|yAxiSR`NPmiKld$-&f~Do#^l1UZglZAIqOMf9ur` zLF43Edw0LB9CKovLGQ~y9aeQK?%i#N=aF4aD~{T|+qlkp@~Fz9%B80#?47vb)XBLE z-@IJCuluwyi}RZYOY`~7exDs3Sw7zku6ndTPX4dm|NnX~;*Z`KEv^5%)B>44_eGb7 zzR2}9p{y=lUvrl!WS&x2>>K<;U(A@$PyfrNrDk5czkIeRY-txTwY%=0eFclGq_0dg zy+5bR7{fkyO1!5XaH=shDl5uut1I*}|Mp_U!s}bYpA}#Ali%tcHBnIc-KzQXooU}A zJNmr5@8URnP~S!gi5o5}1{>}#XBupHerEk-=o>$8SZW_bqxCH}_@q3L~?c{Hmg~hux>|ZTPf)%DxHH6ym-X zi@SQo*IO59>Fj^}!iLN@=ik5T=YIC<0{fTCH@}_!x!v3>|L<$hoEv}a$i*h->hFHH zWKZ+%LGkCOezW0?k~$vkUSNJVs^^szr-DTfibIs++M8Gu{HmuP`s&cMgD=L$=AQYs z+(YMibG!SI7VlmNrTNX8{=Pt2o-DsvJ@|fa50QN<+n>^W+pp+5vbv-R`;G0$ix;qb zTyD~+--&r_DOn`?r+Zg)8RL(%taZ?plRB1dqKmrZAM(ANKl0eVPwCR{OWj;k#=J2; z7yi|!G~aMKCe>I77JUA_2+YIB_o*lIdOhJ#Cy9Sq$LK2Kg|F8nIJi~jwCthk58(We$eTNRv+G;??H}e?5>fT)gHu)OwqJT%N!RmCCvME&*0uCrO8n1H_4U-! zlwhwFN8fo!Ud1?;PG>eX?sBi<)30WBxmTwgs47l9yV+v<@y6{}G&?ma@6jXKF7xZr ztuG9Cwf{`V^J5*?->TE};DL#UUp1Q1%jDxR@6j3c$A^DgQ!Z^?I&@#R@#Q0~J=r*8 z-Qq@*E{|Gn6lS!)`b6Wnuj8D)9SKY@KQKn+u_0!6Vf$o$UCG^zbs8;cZKs`y=VB(V zs$I6QBYRNV#RGwW>-k+oyg$}edPWRpYxeDN{ZKIA^yAS1pCUJ0wE17G3VXP@v~p9# zdn#QMTqQ29?h1*UJi$W}>)JnF;-U0Zy2;&TO1J(Vf3uR(F*WXCnDA2mY74pCW?Rb2 zQ`!7k|9o5QFL$ld(lhDK7+$yt;@VkOMXjIYUD#~f0>3N!64v&K zbie-oiPwP+B}TzFw@!UfF{EprxMs_Y;P)SlAMERBZ@M~7a&=I_8Gi;!Qo49NAL8pd)jYvO^;o-+&c*+&r=hXd)@c% zZT96r)#D?t1vX)(!fmG)Z9VD0``BlDxya3J#X4`BZ$mA1v>elUkazctvL~^rZKqC| zR6cxfkn(C-qKI3 z?SOKfCLvNiU;f--n=Ex!g!nFQVd2r}LeaD8<#=1elyH-cmdG)a(@4>w}>`}ve#=lNXnQPXE{l28*!Y^++yPvGgdoXCS zpJ9O>b8^(k9UWJvJU+hqguH!RskG5PDHD~rIINXuah2zL!|+@A8DXy#R~hwfBM+zwZVAd^P>|T|tIL8Qc}z6}3w; z9`vgWyM&m({I~B~`TT!;Wr0s#S>WT91zdA$YQAO|HFjEeEp>IiQLx+Yx7&L*UDUQw z%Rx_jhppbJ7Ju5E*S0jAtYj2Q+K<=iqyE=@ZIz4u4`aBrsDl> z!C3XDn40t5$|ubnxA#EPEW4Qc4%<)LJ9yg7QFQV+H*(YT^^I*;tV(zhYc;UiA!tF& z_$TrOcH5W$oajvwzpB`PeYrxPgpPA~tYx(&xHxHO?+1KcDkIs)3 zi|anM4cc3OXZw*I9vnQJG2(8<<7Rit14@p*KGZn!W`X)eaFBGxn#p%w^jWmwd)3B- zdk2g2R4=c*?EE;W#R?OL z7i=fENqdtBRxIvB($Lc2(#~Up{{0ODT)wz&v8Io$FYaa+BXx~&aiKT6zS^4`Zg$MD!)A!ql<1Ya+@wPL0o1MJX zT>BoqleOjF4XE_)oA`EY*_^xGhc|S(bS*2Pgmla3HkC~cN6cEfa=IX|G~p@~L&maP&R z%zHWD>j0-&r7hdE9Mm&NX(did?LBAqgM#-5ro0Pib^E*b`F-xMQ|;D2++hFmeAT-; z8%i9C=LFaB)qg9RaK-AdOZ|H_Mw=GX*RxVhn}7;gY6sU zZhCM3`L?5%b<&*8y(SDuO#(0tXF0E>L+kuDCcz2%2et%%sGgGBIL$1l!-^pf z{aVH^JsjPw|JcKpN{`~PcW%7<{MLN&T>I)XWyR-TMJxU9_tBj{uC>lkoeMgfvaL+_ z$Hs1}xpk}&yZ?^=@j4E#ZpS;nDE_!OYW6ke%Hn{1z1|gKX!4+l0_p%*nI6c%WBQHK-+0o(UCTbht+dwI^wmz-ljI+R-RrK zeQ@#kd69!7LOOOh(DvlKA${~lcO3L%_@aaRQc?z-c_y2$0%lY?81S_*)tsjq0z7f{9M;q%G zN!8yHPX>gXZ!g|{WUfh8vmJ+0tG^}u7uLW!I{C#6=sP{e{pju?oi6$J9;Ti)qOrg4 zWBbG=;m@5?glVrXt(@w1FgJXCRPU_)3z~ntzj_qY{is^n=Ius7_0;y4IS5KZ)4x6I>7?~Wo)Wd66ys*=` z^o%<{&wsD!Q|PTf!qO|dW=z_hv6U7JBhQYBin+N3KRS8r(-4a$nXTvB)XX(s$0)z| zJo5ilbLHVst$%zL+YGXWtj)MIB%HxmvRqjbB84VJ8Ef{RC1jsbVyS_dMr)<~j4e^I5*1?-=76-RzgrkJ_=* zjaKaQqw>z=vmJJqpi^DWk09!Fjuf{R9Iw){e=YucbRbIRe_7>Hfh@+|__2--i>7pg ziSteTR`^Gu#qC|4e02G-k|wq)3n3{aHZ6d}rm-Q?;Be5}V8el9*o|**)!Seb0EhTt zQ1~ys4H9%W4gKdu!wlLRB&Z1J83}<1hz8fsr>V~vBjHdmG4r)CLWOEA>b;rk#H9H# zJA>e4;(Df=MR%bWNOQ=T6;v^081jSB;L(Q`$Z^})K;)4DK3Q2(fViX_?kGSN2!qa1 zh!4_<1^;*k7Aq~MCZ;B>Di0=uWMsZG14|)oX#%|)7cj(U{F$dCh&$JKI_w)*`SD5!f_is@YI{?9TMZxz)QGA`0UR4pG@cDya@pV4N zVow(GZq(!lwoCJi0k$#r?uN{58Au4}?9yxBlRCRjR9C$qRC?S~wiZ1$!)}0~!fxNg z!+nwO34$%$mjD>&0qRV%W+7(`)fhT-@5|BMQSFV+Zg&IlJ4k67jLj4Cn#9I3K{LrC zW_#Zp(F~S6!|zV8i9A6x%xj9?A64=2s%Z;>%}MwX_d;-BQ9t3s-UENmJbKpA==Xvv-_KIp-W`5?VJy|e{A7z#OvJH{Ckfjg z{@IA$28&#^V*oKg2DFCO80udT(ff_LL3T=6W2dX>A>u`V7zGe7d__=+fYJuPgmPei zlc4JBXsxqTHGKmKODk)_DF^rOpr`s^OWzlIy4oK4UG$XpWynFsp+^I8KN4NwO37&{ zXy-ext~ShLOPi>Nu76y_J8}VcDyyyQ?E2?NXS_;ku3Z)yUMPNTZ-}Q8JxEe#U3y>; z)-Y$rO7>__QQ4E&W5T&&vT&Re7bkdF$9-Crxi+^f;8DOES-+z{^1g{btsQv?k}Jo= zIWKXVX!ZPhzWqqelXvyr5N91gT~I)==`}nf_JXlUaltax;Qy{jYGam-awR?%ya<;n zGCz^fjaM0;eG+dS5w>Dy9?)B@O&_^qNPdz_+r=W;2klsul$NTv7~mIc7vXi)v2`$M zf$xJ{XUyEA3}kF@ddX$(n^dYn_wxf21>{iqLN6>Q-_Mo#TNn#5d5g>AJyKn6(2$uz zISL7xERPd9w`e8=RyQ|hHc$Hn%?v8IAo@++;{@mkUAOf4ol9m1A^oFR+Vtq%@2xbw z#(uuGFYA4ut3Scs+4DN_aV8>LzG(8%<4)3D*G9g%`)8s2flEB0I}PP;AM2`RG>Z2= z=cYjSGEQvb{D71=tH^x0gdqm~zBXI0OZa}?_3qf)nX0LEOGmACB`z-1LMj3Tyhe{ zO-HE3rx_{wxUOjghPUmGKaQWSuuG=<>4^({?jc&NAmDU?1%t{>1%ptnd4`eMpJKp& zUIAaB-=fby`;)%814df%Q@LQh17`K~zlr{T2vnv1d-C7U*5J?zdU!p4Ih4u#jUFS+ zmb~q%vYFm~!HFOA3J{J;qrxaZZZ2+~$I<3Ilsxer8gSptEPl=<{d0lrZ=u6>(6Pfg zPM!V{Hp=tlp2DuQ@E~mXbJd)rAr_35GV71V4zW3C|9DK)gS=sPYiU6gWr<7IwHbQ5 zVDLVz-GLlJJ>Nhdkf5w^&cuEIirZguo4{Dwth9w!QU8!jv-9I2mWh=V-PzO2TEjDHpMi=8d{Ug7st0Z5i+i@3Uh2?0jeMSX zF1-x@(csosR{yW!)}IW3gG>W#eOV2FucHfS_GAAlboq_BzjgWlC|I**{ewL*X$HH- zJ}{piZuy`f{PrnlX(1Ql;ZhrYLc7HiYgQ}en7AzuodQ*+CONJ2doMF^(lRI2K(^l4 zUVCH_(x05OZ6=rC* z;BTSc-*jmFvK3Iz&|s|UJOvfX^2T_0Pp!h{-g7tZEU>AV= zGy!PK0|@X91{H#V*>2j^Ll{rk`t|X0pVjL!o9>2T!7BDfhG$KTPh^6jaKl z<8MCkT7O1RyQsWT;n~wOc}Csj9Iv9O(%y8(2TQEaBQN@enyNK;E&T8%A^`aeO=&Vq z=KFa>R^Hwx2kl-rNIRYr(^1k{yBDpCNU@=Bk8Zssa#inUjPSzLi)e9zvP|V$_Pb+Y zhEsvs+x1)(@%+8}8C-#%j7Nf=YKbarU9xx(@gm@D>pfiD7M!hMbFMJz zWcRW5T+@~N?W|M6_wEkQz2LS}IAxbJ2^$_9@nT=*MPlG8a1VA4yUAcxf4V9J(wtg0 zI2%>$B+F_>px-9zfLTsRfAvJ!JoyZw3n6;dX%GF2=9?4B%TI>tHuaX1z#A zO4t#_@Tl-B=h0<32`W{TS7%BzQ4^u&Fn~C;X#yAnme>jqn*d@1K+L1s`fIxv%$x$m zz;yqA Tags = null; + } + public class ColumnTagInfo + { + public string Name { get; set; } + public string Value { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/TDengine/STableAttribute.cs b/src/Admin/ThingsGateway.SqlSugar/TDengine/STableAttribute.cs new file mode 100644 index 000000000..2df09baea --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/TDengine/STableAttribute.cs @@ -0,0 +1,13 @@ +namespace SqlSugar.TDengine +{ + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] + public class STableAttribute : Attribute + { + public string Tags { get; set; } + public string Tag1 { get; set; } + public string Tag2 { get; set; } + public string Tag3 { get; set; } + public string Tag4 { get; set; } + public string STableName { get; set; } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/CodeFirst/TDengineCodeFirst.cs b/src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/CodeFirst/TDengineCodeFirst.cs new file mode 100644 index 000000000..5f5d31732 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/CodeFirst/TDengineCodeFirst.cs @@ -0,0 +1,309 @@ +using System.Data; +using System.Reflection; +using System.Text.RegularExpressions; + +namespace SqlSugar.TDengine +{ + public class TDengineCodeFirst : CodeFirstProvider + { + protected override void Execute(Type entityType, EntityInfo entityInfo) + { + var attr = GetCommonSTableAttribute(entityInfo.Type.GetCustomAttribute()); + if (attr?.STableName != null && attr?.Tag1 != null) + { + entityInfo.DbTableName = attr.STableName; + Context.MappingTables.Add(entityInfo.EntityName, entityInfo.DbTableName); + } + //var entityInfo = this.Context.EntityMaintenance.GetEntityInfoNoCache(entityType); + if (entityInfo.Discrimator.HasValue()) + { + Check.ExceptionEasy(!Regex.IsMatch(entityInfo.Discrimator, @"^(?:\w+:\w+)(?:,\w+:\w+)*$"), "The format should be type:cat for this type, and if there are multiple, it can be FieldName:cat,FieldName2:dog ", "格式错误应该是type:cat这种格式,如果是多个可以FieldName:cat,FieldName2:dog,不要有空格"); + var array = entityInfo.Discrimator.Split(','); + foreach (var disItem in array) + { + var name = disItem.Split(':').First(); + var value = disItem.Split(':').Last(); + entityInfo.Columns.Add(new EntityColumnInfo() { PropertyInfo = typeof(DiscriminatorObject).GetProperty(nameof(DiscriminatorObject.FieldName)), IsOnlyIgnoreUpdate = true, DbColumnName = name, UnderType = typeof(string), PropertyName = name, Length = 50 }); + } + } + if (this.MappingTables.TryGetValue(entityType, out string? v)) + { + entityInfo.DbTableName = v; + this.Context.MappingTables.Add(entityInfo.EntityName, entityInfo.DbTableName); + } + if (this.DefultLength > 0) + { + foreach (var item in entityInfo.Columns) + { + if (item.PropertyInfo.PropertyType == UtilConstants.StringType && item.DataType.IsNullOrEmpty() && item.Length == 0) + { + item.Length = DefultLength; + } + if (item.DataType?.Contains(',') == true && !Regex.IsMatch(item.DataType, @"\d\,\d")) + { + var types = item.DataType.Split(',').Select(it => it.ToLower()).ToList(); + var mapingTypes = this.Context.Ado.DbBind.MappingTypes.Select(it => it.Key.ToLower()).ToList(); + var mappingType = types.FirstOrDefault(it => mapingTypes.Contains(it)); + if (mappingType != null) + { + item.DataType = mappingType; + } + if (item.DataType == "varcharmax") + { + item.DataType = "nvarchar(max)"; + } + } + } + } + var tableName = GetTableName(entityInfo); + this.Context.MappingTables.Add(entityInfo.EntityName, tableName); + entityInfo.DbTableName = tableName; + entityInfo.Columns.ForEach(it => + { + it.DbTableName = tableName; + if (it.UnderType?.Name == "DateOnly" && it.DataType == null) + { + it.DataType = "Date"; + } + if (it.UnderType?.Name == "TimeOnly" && it.DataType == null) + { + it.DataType = "Time"; + } + }); + var isAny = this.Context.DbMaintenance.IsAnyTable(tableName, false); + if (isAny && entityInfo.IsDisabledUpdateAll) + { + return; + } + if (isAny) + ExistLogic(entityInfo); + else + NoExistLogic(entityInfo); + + this.Context.DbMaintenance.AddRemark(entityInfo); + //this.Context.DbMaintenance.AddIndex(entityInfo); + //base.CreateIndex(entityInfo); + this.Context.DbMaintenance.AddDefaultValue(entityInfo); + } + + public override void ExistLogic(EntityInfo entityInfo) + { + if (entityInfo.Columns.HasValue() && entityInfo.IsDisabledUpdateAll == false) + { + //Check.Exception(entityInfo.Columns.Where(it => it.IsPrimarykey).Count() > 1, "Multiple primary keys do not support modifications"); + + var tableName = GetTableName(entityInfo); + var dbColumns = this.Context.DbMaintenance.GetColumnInfosByTableName(tableName, false); + ConvertColumns(dbColumns); + var attr = GetCommonSTableAttribute(entityInfo.Type.GetCustomAttribute()); + var entityColumns = entityInfo.Columns.Where(it => it.IsIgnore == false).ToList(); + if (attr?.Tag1 != null) + { + entityColumns = entityInfo.Columns.Where(it => it.IsIgnore == false + || it.DbColumnName?.ToLower() == attr.Tag1?.ToLower() + || it.DbColumnName?.ToLower() == attr.Tag2?.ToLower() + || it.DbColumnName?.ToLower() == attr.Tag3?.ToLower() + || it.DbColumnName?.ToLower() == attr.Tag4?.ToLower() + ).ToList(); + foreach (var item in entityColumns) + { + if (item.DbColumnName == null) + { + item.DbColumnName = item.PropertyName; + } + } + } + var dropColumns = dbColumns + .Where(dc => !entityColumns.Any(ec => dc.DbColumnName.Equals(ec.OldDbColumnName, StringComparison.CurrentCultureIgnoreCase))) + .Where(dc => !entityColumns.Any(ec => dc.DbColumnName.Equals(ec.DbColumnName, StringComparison.CurrentCultureIgnoreCase))) + .ToList(); + var addColumns = entityColumns + .Where(ec => ec.OldDbColumnName.IsNullOrEmpty() || !dbColumns.Any(dc => dc.DbColumnName.Equals(ec.OldDbColumnName, StringComparison.CurrentCultureIgnoreCase))) + .Where(ec => !dbColumns.Any(dc => ec.DbColumnName.Equals(dc.DbColumnName, StringComparison.CurrentCultureIgnoreCase))).ToList(); + + var renameColumns = entityColumns + .Where(it => !string.IsNullOrEmpty(it.OldDbColumnName)) + .Where(entityColumn => dbColumns.Any(dbColumn => entityColumn.OldDbColumnName.Equals(dbColumn.DbColumnName, StringComparison.CurrentCultureIgnoreCase))) + .ToList(); + + + var isMultiplePrimaryKey = dbColumns.Where(it => it.IsPrimarykey).Count() > 1 || entityColumns.Where(it => it.IsPrimarykey).Count() > 1; + + + var isChange = false; + foreach (var item in addColumns) + { + this.Context.DbMaintenance.AddColumn(tableName, EntityColumnToDbColumn(entityInfo, tableName, item)); + isChange = true; + } + if (entityInfo.IsDisabledDelete == false) + { + foreach (var item in dropColumns) + { + this.Context.DbMaintenance.DropColumn(tableName, item.DbColumnName); + isChange = true; + } + } + //foreach (var item in alterColumns) + //{ + + // if (this.Context.CurrentConnectionConfig.DbType == DbType.Oracle) + // { + // var entityColumnItem = entityColumns.FirstOrDefault(y => y.DbColumnName == item.DbColumnName); + // if (entityColumnItem != null && !string.IsNullOrEmpty(entityColumnItem.DataType)) + // { + // continue; + // } + // } + + // this.Context.DbMaintenance.UpdateColumn(tableName, EntityColumnToDbColumn(entityInfo, tableName, item)); + // isChange = true; + //} + foreach (var item in renameColumns) + { + this.Context.DbMaintenance.RenameColumn(tableName, item.OldDbColumnName, item.DbColumnName); + isChange = true; + } + //var isAddPrimaryKey = false; + //foreach (var item in entityColumns) + //{ + // var dbColumn = dbColumns.FirstOrDefault(dc => dc.DbColumnName.Equals(item.DbColumnName, StringComparison.CurrentCultureIgnoreCase)); + // if (dbColumn == null) continue; + // bool pkDiff, idEntityDiff; + // KeyAction(item, dbColumn, out pkDiff, out idEntityDiff); + // if (dbColumn != null && pkDiff && !idEntityDiff && isMultiplePrimaryKey == false) + // { + // var isAdd = item.IsPrimarykey; + // if (isAdd) + // { + // isAddPrimaryKey = true; + // this.Context.DbMaintenance.AddPrimaryKey(tableName, item.DbColumnName); + // } + // else + // { + // this.Context.DbMaintenance.DropConstraint(tableName, string.Format("PK_{0}_{1}", tableName, item.DbColumnName)); + // } + // } + // else if ((pkDiff || idEntityDiff) && isMultiplePrimaryKey == false) + // { + // ChangeKey(entityInfo, tableName, item); + // } + //} + //if (isAddPrimaryKey == false && entityColumns.Count(it => it.IsPrimarykey) == 1 && dbColumns.Count(it => it.IsPrimarykey) == 0) + //{ + // //var addPk = entityColumns.First(it => it.IsPrimarykey); + // //this.Context.DbMaintenance.AddPrimaryKey(tableName, addPk.DbColumnName); + //} + //if (isMultiplePrimaryKey) + //{ + // var oldPkNames = dbColumns.Where(it => it.IsPrimarykey).Select(it => it.DbColumnName.ToLower()).OrderBy(it => it).ToList(); + // var newPkNames = entityColumns.Where(it => it.IsPrimarykey).Select(it => it.DbColumnName.ToLower()).OrderBy(it => it).ToList(); + // if (!Enumerable.SequenceEqual(oldPkNames, newPkNames)) + // { + // Check.Exception(true, ErrorMessage.GetThrowMessage("Modification of multiple primary key tables is not supported. Delete tables while creating", "不支持修改多主键表,请删除表在创建")); + // } + + //} + if (isChange && IsBackupTable) + { + this.Context.DbMaintenance.BackupTable(tableName, tableName + DateTime.Now.ToString("yyyyMMddHHmmss"), MaxBackupDataRows); + } + ExistLogicEnd(entityColumns); + } + } + public override void NoExistLogic(EntityInfo entityInfo) + { + List dbColumns = new List(); + foreach (var item in entityInfo.Columns.Where(it => it.IsIgnore != true).Where(it => it.PropertyName != "TagsTypeId").OrderBy(it => it.UnderType == typeof(DateTime) ? 0 : 1)) + { + var addItem = EntityColumnToDbColumn(entityInfo, entityInfo.DbTableName, item); + dbColumns.Add(addItem); + } + var attr = GetCommonSTableAttribute(entityInfo.Type.GetCustomAttribute()); + var oldTableName = entityInfo.DbTableName; + if (attr != null) + { + entityInfo.DbTableName += ("{stable}" + this.Context.Utilities.SerializeObject(attr)); + } + if (attr?.Tag1 != null) + { + dbColumns = dbColumns.Where(it => + it.DbColumnName?.ToLower() != attr.Tag1?.ToLower() + && it.DbColumnName?.ToLower() != attr.Tag2?.ToLower() + && it.DbColumnName?.ToLower() != attr.Tag3?.ToLower() + && it.DbColumnName?.ToLower() != attr.Tag4?.ToLower() + ).ToList(); + } + var dbMain = (TDengineDbMaintenance)this.Context.DbMaintenance; + dbMain.EntityInfo = entityInfo; + dbMain.CreateTable(entityInfo.DbTableName, dbColumns); + entityInfo.DbTableName = oldTableName; + } + protected override DbColumnInfo EntityColumnToDbColumn(EntityInfo entityInfo, string tableName, EntityColumnInfo item) + { + DbColumnInfo result = new DbColumnInfo() { Length = item.Length, DecimalDigits = item.DecimalDigits, Scale = item.DecimalDigits, TableName = tableName, DbColumnName = item.DbColumnName, DataType = item.DataType }; + if (result.DataType.IsNullOrEmpty()) + { + result.DataType = GetDatabaseTypeName(item.UnderType.Name); + } + return result; + } + + private STableAttribute GetCommonSTableAttribute(STableAttribute sTableAttribute) + { + return SqlSugar.TDengine.UtilMethods.GetCommonSTableAttribute(this.Context, sTableAttribute); + } + public string GetDatabaseTypeName(string typeName) + { + switch (typeName.ToLower()) + { + case "bool": + return "BOOL"; + case "datetime": + return "TIMESTAMP"; + case "boolean": + return "BOOL"; + case "byte": + return "TINYINT UNSIGNED"; + case "sbyte": + return "TINYINT"; + case "char": + return "NCHAR"; + case "decimal": + return "FLOAT"; + case "double": + return "DOUBLE"; + case "float": + case "single": + return "FLOAT"; + case "int": + return "INT"; + case "int32": + return "INT"; + case "int16": + return "INT"; + case "int64": + return "BIGINT"; + case "uint": + case "uint32": + return "INT UNSIGNED"; + case "long": + return "BIGINT"; + case "ulong": + case "uint64": + return "BIGINT UNSIGNED"; + case "short": + return "SMALLINT"; + case "ushort": + case "uint16": + return "SMALLINT UNSIGNED"; + case "string": + return "VARCHAR"; + // 添加其他类型的映射关系 + + default: + return "VARCHAR"; // 如果未识别到类型,则返回原始类型名称 + } + } + } +} \ No newline at end of file diff --git a/src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/DbBind/TDengineDbBind.cs b/src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/DbBind/TDengineDbBind.cs new file mode 100644 index 000000000..b6fd0a75b --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/DbBind/TDengineDbBind.cs @@ -0,0 +1,146 @@ +namespace SqlSugar.TDengine +{ + public class TDengineDbBind : DbBindProvider + { + public override string GetDbTypeName(string csharpTypeName) + { + if (csharpTypeName == UtilConstants.ByteArrayType.Name) + return "bytea"; + if (string.Equals(csharpTypeName, "int32", StringComparison.OrdinalIgnoreCase)) + csharpTypeName = "int"; + if (string.Equals(csharpTypeName, "int16", StringComparison.OrdinalIgnoreCase)) + csharpTypeName = "short"; + if (string.Equals(csharpTypeName, "int64", StringComparison.OrdinalIgnoreCase)) + csharpTypeName = "long"; + if (csharpTypeName.ToLower().IsIn("boolean", "bool")) + csharpTypeName = "bool"; + if (csharpTypeName == "DateTimeOffset") + csharpTypeName = "DateTime"; + var mappings = this.MappingTypes.Where(it => it.Value.ToString().Equals(csharpTypeName, StringComparison.CurrentCultureIgnoreCase)).ToList(); + if (mappings?.Count > 0) + return mappings.First().Key; + else + return "varchar"; + } + public override string GetPropertyTypeName(string dbTypeName) + { + dbTypeName = dbTypeName.ToLower(); + if (dbTypeName == "int32") + { + dbTypeName = "int"; + } + else if (dbTypeName == "int16") + { + dbTypeName = "short"; + } + else if (dbTypeName == "int64") + { + dbTypeName = "long"; + } + else if (dbTypeName == "string") + { + dbTypeName = "string"; + } + else if (dbTypeName == "boolean") + { + dbTypeName = "bool"; + } + else if (dbTypeName == "bool") + { + dbTypeName = "bool"; + } + else if (dbTypeName == "sbyte") + { + dbTypeName = "sbyte"; + } + else if (dbTypeName == "double") + { + dbTypeName = "double"; + } + else if (dbTypeName == "binary") + { + dbTypeName = "string"; + } + else if (dbTypeName == "timestamp") + { + dbTypeName = "DateTime"; + } + else if (dbTypeName == "bigint") + { + dbTypeName = "long"; + } + else if (dbTypeName == "char") + { + dbTypeName = "string"; + } + else if (dbTypeName == "smallint") + { + dbTypeName = "short"; + } + else if (dbTypeName == "int unsigned") + { + dbTypeName = "int"; + } + else if (dbTypeName == "bigint unsigned") + { + dbTypeName = "long"; + } + else if (dbTypeName == "tinyint unsigned") + { + dbTypeName = "byte"; + } + else if (TDengineDbBind.MappingTypesConst.FirstOrDefault(it => (it.Key).Equals(dbTypeName, StringComparison.CurrentCultureIgnoreCase)) is { } data) + { + dbTypeName = data.Value.ToString(); + } + else + { + + } + return dbTypeName; + } + public override List> MappingTypes + { + get + { + var extService = this.Context.CurrentConnectionConfig.ConfigureExternalServices; + if (extService?.AppendDataReaderTypeMappings.HasValue() == true) + { + return extService.AppendDataReaderTypeMappings.Union(MappingTypesConst).ToList(); + } + else + { + return MappingTypesConst; + } + } + } + public static List> MappingTypesConst = new List>(){ + + new KeyValuePair("BOOL",CSharpDataType.@bool), + new KeyValuePair("TINYINT",CSharpDataType.@byte), + new KeyValuePair("TINYINT",CSharpDataType.@int), + new KeyValuePair("SMALLINT",CSharpDataType.@short), + new KeyValuePair("INT",CSharpDataType.@int), + new KeyValuePair("BIGINT",CSharpDataType.@long), + new KeyValuePair("TINYINT UNSIGNED",CSharpDataType.@byte), + new KeyValuePair("TINYINT UNSIGNED",CSharpDataType.@int), + new KeyValuePair("SMALLINT UNSIGNED",CSharpDataType.@short), + new KeyValuePair("INT UNSIGNED",CSharpDataType.@int), + new KeyValuePair("BIGINT UNSIGNED",CSharpDataType.@long), + new KeyValuePair("FLOAT",CSharpDataType.Single), + new KeyValuePair("DOUBLE",CSharpDataType.@double), + new KeyValuePair("float8",CSharpDataType.@double), + new KeyValuePair("BINARY",CSharpDataType.@string), + new KeyValuePair("TIMESTAMP",CSharpDataType.DateTime), + new KeyValuePair("NCHAR",CSharpDataType.@string), + new KeyValuePair("JSON",CSharpDataType.@string) + }; + public override List StringThrow + { + get + { + return new List() { "int32", "datetime", "decimal", "double", "byte" }; + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/DbFirst/TDengineDbFirst.cs b/src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/DbFirst/TDengineDbFirst.cs new file mode 100644 index 000000000..336286069 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/DbFirst/TDengineDbFirst.cs @@ -0,0 +1,6 @@ +namespace SqlSugar.TDengine +{ + public class TDengineDbFirst : DbFirstProvider + { + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/DbMaintenance/TDengineDbMaintenance.cs b/src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/DbMaintenance/TDengineDbMaintenance.cs new file mode 100644 index 000000000..bae870b59 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/DbMaintenance/TDengineDbMaintenance.cs @@ -0,0 +1,588 @@ +using System.Data; +using System.Reflection; + +namespace SqlSugar.TDengine +{ + public class TDengineDbMaintenance : DbMaintenanceProvider + { + public EntityInfo EntityInfo { get; set; } + + #region DML + + protected override string GetViewInfoListSql => throw new NotImplementedException(); + protected override string GetDataBaseSql + { + get + { + return "show databases"; + } + } + protected override string GetColumnInfosByTableNameSql + { + get + { + throw new NotSupportedException("TDengineCode暂时不支持DbFirst等方法,还在开发"); + } + } + + protected override string GetTableInfoListSql + { + get + { + return ""; + } + } + + #endregion + + #region DDL + protected override string CreateDataBaseSql + { + get + { + return "CREATE DATABASE IF NOT EXISTS {0} WAL_RETENTION_PERIOD 3600"; + } + } + protected override string AddPrimaryKeySql + { + get + { + return "ALTER TABLE {0} ADD PRIMARY KEY({2}) /*{1}*/"; + } + } + protected override string AddColumnToTableSql + { + get + { + return "ALTER TABLE {0} ADD COLUMN {1} {2}{3} {4} {5} {6}"; + } + } + protected override string AlterColumnToTableSql + { + get + { + return "alter table {0} MODIFY COLUMN {1} {2}{3} {4} {5} {6}"; + } + } + protected override string BackupDataBaseSql + { + get + { + return "mysqldump.exe {0} -uroot -p > {1} "; + } + } + protected override string CreateTableSql + { + get + { + return "CREATE STABLE IF NOT EXISTS {0}(\r\n{1} ) TAGS(" + SqlBuilder.GetTranslationColumnName("TagsTypeId") + " VARCHAR(100))"; + } + } + protected override string CreateTableColumn + { + get + { + return "{0} {1}{2} {3} {4} {5}"; + } + } + protected override string TruncateTableSql + { + get + { + return "TRUNCATE TABLE {0}"; + } + } + protected override string BackupTableSql + { + get + { + return "create table {0} as (select * from {1} limit {2} offset 0)"; + } + } + protected override string DropTableSql + { + get + { + return "DROP TABLE {0}"; + } + } + protected override string DropColumnToTableSql + { + get + { + return "ALTER TABLE {0} DROP COLUMN {1}"; + } + } + protected override string DropConstraintSql + { + get + { + return "ALTER TABLE {0} DROP CONSTRAINT {1}"; + } + } + protected override string RenameColumnSql + { + get + { + return "ALTER TABLE {0} RENAME {1} TO {2}"; + } + } + protected override string AddColumnRemarkSql => "comment on column {1}.{0} is '{2}'"; + + protected override string DeleteColumnRemarkSql => "comment on column {1}.{0} is ''"; + + protected override string IsAnyColumnRemarkSql { get { throw new NotSupportedException(); } } + + protected override string AddTableRemarkSql => "comment on table {0} is '{1}'"; + + protected override string DeleteTableRemarkSql => "comment on table {0} is ''"; + + protected override string IsAnyTableRemarkSql { get { throw new NotSupportedException(); } } + + protected override string RenameTableSql => "alter table {0} to {1}"; + + protected override string CreateIndexSql + { + get + { + return "CREATE {3} INDEX Index_{0}_{2} ON {0} ({1})"; + } + } + protected override string AddDefaultValueSql + { + get + { + return "ALTER TABLE {0} ALTER COLUMN {1} SET DEFAULT {2}"; + } + } + protected override string IsAnyIndexSql + { + get + { + return " SELECT count(1) WHERE upper('{0}') IN ( SELECT upper(indexname) FROM pg_indexes )"; + } + } + protected override string IsAnyProcedureSql => throw new NotImplementedException(); + #endregion + + #region Check + protected override string CheckSystemTablePermissionsSql + { + get + { + return "SHOW DATABASES"; + } + } + #endregion + + #region Scattered + protected override string CreateTableNull + { + get + { + return " "; + } + } + protected override string CreateTableNotNull + { + get + { + return " "; + } + } + protected override string CreateTablePirmaryKey + { + get + { + return "PRIMARY KEY"; + } + } + protected override string CreateTableIdentity + { + get + { + return "serial"; + } + } + #endregion + + #region Methods + public override List GetTableInfoList(bool isCache = true) + { + var sb = new List(); + + // 第一个循环:获取超级表名称 + var dt = GetSTables(); + foreach (DataRow item in dt.Rows) + { + sb.Add(item["stable_name"].ObjToString().ToSqlFilter()); + } + + // 第二个循环:获取子表名称 + var dt2 = GetTables(); + foreach (DataRow item in dt2.Rows) + { + sb.Add(item["table_name"].ObjToString().ToSqlFilter()); + } + var result = sb.Select(it => new DbTableInfo() { Name = it, DbObjectType = DbObjectType.Table }).ToList(); + return result; + } + public override bool AddColumn(string tableName, DbColumnInfo columnInfo) + { + if (columnInfo.DbColumnName == "TagsTypeId") + { + return true; + } + tableName = this.SqlBuilder.GetTranslationTableName(tableName); + var isAddNotNUll = columnInfo.IsNullable == false && columnInfo.DefaultValue.HasValue(); + if (isAddNotNUll) + { + columnInfo = this.Context.Utilities.TranslateCopy(columnInfo); + columnInfo.IsNullable = true; + } + string sql = GetAddColumnSql(tableName, columnInfo); + this.Context.Ado.ExecuteCommand(sql); + return true; + } + public override List GetViewInfoList(bool isCache = true) + { + return new List(); + } + public override bool CreateDatabase(string databaseName, string databaseDirectory = null) + { + var db = this.Context.CopyNew(); + db.Ado.Connection.ChangeDatabase(""); + var sql = CreateDataBaseSql; + if (this.Context.CurrentConnectionConfig.ConnectionString.Contains("config_us", StringComparison.OrdinalIgnoreCase)) + { + sql += " PRECISION 'us'"; + } + else if (this.Context.CurrentConnectionConfig.ConnectionString.Contains("config_ns", StringComparison.OrdinalIgnoreCase)) + { + sql += " PRECISION 'ns'"; + } + db.Ado.ExecuteCommand(string.Format(sql, databaseName)); + return true; + } + public override List GetIndexList(string tableName) + { + var sql = $"SELECT indexname, indexdef FROM pg_indexes WHERE upper(tablename) = upper('{tableName}')"; + return this.Context.Ado.SqlQuery(sql); + } + public override List GetProcList(string dbName) + { + var sql = $"SELECT proname FROM pg_proc p JOIN pg_namespace n ON p.pronamespace = n.oid WHERE n.nspname = '{dbName}'"; + return this.Context.Ado.SqlQuery(sql); + } + public override bool AddDefaultValue(string tableName, string columnName, string defaultValue) + { + return base.AddDefaultValue(this.SqlBuilder.GetTranslationTableName(tableName), this.SqlBuilder.GetTranslationTableName(columnName), defaultValue); + } + public override bool AddColumnRemark(string columnName, string tableName, string description) + { + tableName = this.SqlBuilder.GetTranslationTableName(tableName); + string sql = string.Format(this.AddColumnRemarkSql, this.SqlBuilder.GetTranslationColumnName(columnName.ToLower(isAutoToLowerCodeFirst)), tableName, description); + this.Context.Ado.ExecuteCommand(sql); + return true; + } + public override bool AddTableRemark(string tableName, string description) + { + tableName = this.SqlBuilder.GetTranslationTableName(tableName); + return base.AddTableRemark(tableName, description); + } + public override bool UpdateColumn(string tableName, DbColumnInfo columnInfo) + { + tableName = this.SqlBuilder.GetTranslationTableName(tableName); + var columnName = this.SqlBuilder.GetTranslationColumnName(columnInfo.DbColumnName); + string sql = GetUpdateColumnSql(tableName, columnInfo); + this.Context.Ado.ExecuteCommand(sql); + var isnull = columnInfo.IsNullable ? " DROP NOT NULL " : " SET NOT NULL "; + this.Context.Ado.ExecuteCommand(string.Format("alter table {0} alter {1} {2}", tableName, columnName, isnull)); + return true; + } + + protected override string GetUpdateColumnSql(string tableName, DbColumnInfo columnInfo) + { + string columnName = this.SqlBuilder.GetTranslationColumnName(columnInfo.DbColumnName); + tableName = this.SqlBuilder.GetTranslationTableName(tableName); + string dataSize = GetSize(columnInfo); + string dataType = columnInfo.DataType; + //if (!string.IsNullOrEmpty(dataType)) + //{ + // dataType = " type " + dataType; + //} + string nullType = ""; + string primaryKey = null; + string identity = null; + string result = string.Format(this.AlterColumnToTableSql, tableName, columnName, dataType, dataSize, nullType, primaryKey, identity); + return result; + } + + public override bool AddRemark(EntityInfo entity) + { + return true; + } + public override bool CreateTable(string tableName, List columns, bool isCreatePrimaryKey = true) + { + if (columns.HasValue()) + { + foreach (var item in columns) + { + if (item.DbColumnName.Equals("GUID", StringComparison.CurrentCultureIgnoreCase) && item.Length == 0) + { + item.Length = 10; + } + } + } + string sql = GetCreateTableSql(tableName, columns); + string primaryKeyInfo = null; + if (columns.Any(it => it.IsPrimarykey) && isCreatePrimaryKey) + { + primaryKeyInfo = string.Format(", Primary key({0})", string.Join(",", columns.Where(it => it.IsPrimarykey).Select(it => this.SqlBuilder.GetTranslationColumnName(it.DbColumnName.ToLower(isAutoToLowerCodeFirst))))); + + } + sql = sql.Replace("$PrimaryKey", primaryKeyInfo); + this.Context.Ado.ExecuteCommand(sql); + return true; + } + protected override string GetCreateTableSql(string tableName, List columns) + { + List columnArray = new List(); + Check.Exception(columns.IsNullOrEmpty(), "No columns found "); + foreach (var item in columns) + { + string columnName = item.DbColumnName; + string dataType = item.DataType; + if (dataType == "varchar" && item.Length == 0) + { + item.Length = 1; + } + //if (dataType == "uuid") + //{ + // item.Length = 50; + // dataType = "varchar"; + //} + string dataSize = item.Length > 0 ? string.Format("({0})", item.Length) : null; + //if (item.DecimalDigits > 0&&item.Length>0 && dataType?.ToLower()== "float") + //{ + // item.Length = 0; + // dataSize = $"({item.Length},{item.DecimalDigits})"; + //} + //if (item.DecimalDigits > 0 && item.Length > 0 && dataType?.ToLower() == "double") + //{ + + // dataSize = $"({item.Length},{item.DecimalDigits})"; + //} + //if (item.DecimalDigits > 0 && item.Length > 0 && dataType?.ToLower() == "decimal") + //{ + // dataSize = $"({item.Length},{item.DecimalDigits})"; + //} + //if (item.DecimalDigits == 0 && item.Length == 0 && dataType?.ToLower() == "float") + //{ + // dataType = $"FLOAT(18,4)"; + //} + //if (item.DecimalDigits == 0 && item.Length == 0 && dataType?.ToLower() == "double") + //{ + // dataType = $"DOUBLE(18,4)"; + //} + if (item.Length == 0 && dataType?.ToLower()?.IsIn("nchar", "varchar") == true) + { + dataType = "VARCHAR(200)"; + } + if (dataType?.ToLower()?.IsIn("float", "double") == true) + { + dataSize = null; + } + string primaryKey = null; + string addItem = string.Format(this.CreateTableColumn, this.SqlBuilder.GetTranslationColumnName(columnName.ToLower(isAutoToLowerCodeFirst)), dataType, dataSize, null, primaryKey, ""); + columnArray.Add(addItem); + } + string tableString = string.Format(this.CreateTableSql, this.SqlBuilder.GetTranslationTableName("STable_" + tableName.ToLower(isAutoToLowerCodeFirst)), string.Join(",\r\n", columnArray)); + var childTableName = this.SqlBuilder.GetTranslationTableName(tableName.ToLower(isAutoToLowerCodeFirst)); + var stableName = this.SqlBuilder.GetTranslationTableName("STable_" + tableName.ToLower(isAutoToLowerCodeFirst)); + var isAttr = tableName.Contains("{stable}"); + var isTag1 = false; + if (isAttr) + { + var attr = this.Context.Utilities.DeserializeObject(tableName.Split("{stable}").Last()); + stableName = this.SqlBuilder.GetTranslationTableName(attr.STableName.ToLower(isAutoToLowerCodeFirst)); + tableString = string.Format(this.CreateTableSql, stableName, string.Join(",\r\n", columnArray)); + tableName = childTableName = this.SqlBuilder.GetTranslationTableName(tableName.Split("{stable}").First().ToLower(isAutoToLowerCodeFirst)); + if (attr.Tags == null && attr.Tag1 != null) + { + isTag1 = true; + STable.Tags = new List() { + new ColumnTagInfo(){ Name=attr.Tag1 }, + new ColumnTagInfo(){ Name=attr.Tag2 }, + new ColumnTagInfo(){ Name=attr.Tag3 }, + new ColumnTagInfo(){ Name=attr.Tag4 } + }.Where(it => it.Name.HasValue()).ToList(); + } + else + { + STable.Tags = this.Context.Utilities.DeserializeObject>(attr.Tags); + } + } + if (STable.Tags?.Count > 0) + { + var colums = STable.Tags.Select(it => this.SqlBuilder.GetTranslationTableName(it.Name) + " VARCHAR(100) "); + tableString = tableString.Replace(SqlBuilder.GetTranslationColumnName("TagsTypeId"), string.Join(",", colums)); + tableString = tableString.Replace(" VARCHAR(100) VARCHAR(100)", " VARCHAR(100)"); + if (this.EntityInfo != null) + { + foreach (var item in STable.Tags) + { + var tagColumn = this.EntityInfo.Columns.FirstOrDefault(it => it.DbColumnName == item.Name || it.PropertyName == item.Name); + if (tagColumn != null && tagColumn.UnderType != UtilConstants.StringType) + { + var tagType = new TDengineDbBind() { Context = this.Context }.GetDbTypeName(tagColumn.UnderType.Name); + tableString = tableString.Replace($"{SqlBuilder.GetTranslationColumnName(tagColumn.DbColumnName)} VARCHAR(100)", $"{SqlBuilder.GetTranslationColumnName(tagColumn.DbColumnName)} {tagType} "); + } + else if (tagColumn != null && tagColumn.UnderType == UtilConstants.StringType && tagColumn.Length < 100 && tagColumn.Length > 0) + { + tableString = tableString.Replace($"{SqlBuilder.GetTranslationColumnName(tagColumn.DbColumnName)} VARCHAR(100)", $"{SqlBuilder.GetTranslationColumnName(tagColumn.DbColumnName)} VARCHAR({tagColumn.Length}) "); + } + } + } + } + this.Context.Ado.ExecuteCommand(tableString); + var createChildSql = $"CREATE TABLE IF NOT EXISTS {childTableName} USING {stableName} TAGS('default')"; + if (STable.Tags?.Count > 0) + { + var colums = STable.Tags.Select(it => it.Value.ToSqlValue()); + createChildSql = createChildSql.Replace("TAGS('default')", $"TAGS({string.Join(",", colums)})"); + } + if (isTag1) + { + //No create child table + } + else + { + this.Context.Ado.ExecuteCommand(createChildSql); + } + return tableString; + } + public override bool IsAnyConstraint(string constraintName) + { + throw new NotSupportedException("PgSql IsAnyConstraint NotSupportedException"); + } + public override bool BackupDataBase(string databaseName, string fullFileName) + { + Check.ThrowNotSupportedException("PgSql BackupDataBase NotSupported"); + return false; + } + public override void AddDefaultValue(EntityInfo entityInfo) + { + var talbeName = entityInfo.DbTableName; + var attr = GetCommonSTableAttribute(entityInfo.Type.GetCustomAttribute()); + if (attr?.Tag1 != null) + { + talbeName = attr.STableName; + } + var dbColumns = this.GetColumnInfosByTableName(talbeName, false); + var db = this.Context; + var columns = entityInfo.Columns.Where(it => it.IsIgnore == false).ToList(); + foreach (var item in columns) + { + if (item.DefaultValue.HasValue()) + { + if (!IsAnyDefaultValue(entityInfo.DbTableName, item.DbColumnName, dbColumns)) + { + this.AddDefaultValue(entityInfo.DbTableName, item.DbColumnName, item.DefaultValue); + } + } + } + } + + private STableAttribute GetCommonSTableAttribute(STableAttribute sTableAttribute) + { + return SqlSugar.TDengine.UtilMethods.GetCommonSTableAttribute(this.Context, sTableAttribute); + } + + public override List GetColumnInfosByTableName(string tableName, bool isCache = true) + { + + var sql = $"select * from {this.SqlBuilder.GetTranslationColumnName(tableName)} where 1=2 "; + List result = new List(); + DataTable dt = null; + try + { + dt = this.Context.Ado.GetDataTable(sql); + } + catch (Exception) + { + sql = $"select * from `{tableName}` where 1=2 "; + dt = this.Context.Ado.GetDataTable(sql); + } + foreach (DataColumn item in dt.Columns) + { + var addItem = new DbColumnInfo() + { + DbColumnName = item.ColumnName, + DataType = item.DataType.Name + }; + result.Add(addItem); + } + if (result.Count(it => it.DataType == "DateTime") == 1) + { + result.First(it => it.DataType == "DateTime").IsPrimarykey = true; + } + return result; + } + #endregion + + #region Helper + private bool isAutoToLowerCodeFirst + { + get + { + if (this.Context.CurrentConnectionConfig.MoreSettings == null) return true; + else if ( + this.Context.CurrentConnectionConfig.MoreSettings.PgSqlIsAutoToLower == false && + this.Context.CurrentConnectionConfig.MoreSettings?.PgSqlIsAutoToLowerCodeFirst == false) + { + return false; + } + else + { + return true; + } + } + } + + private string GetSchema() + { + var schema = "public"; + if (System.Text.RegularExpressions.Regex.IsMatch(this.Context.CurrentConnectionConfig.ConnectionString.ToLower(), "searchpath=")) + { + var regValue = System.Text.RegularExpressions.Regex.Match(this.Context.CurrentConnectionConfig.ConnectionString.ToLower(), @"searchpath\=(\w+)").Groups[1].Value; + if (regValue.HasValue()) + { + schema = regValue; + } + } + else if (System.Text.RegularExpressions.Regex.IsMatch(this.Context.CurrentConnectionConfig.ConnectionString.ToLower(), "search path=")) + { + var regValue = System.Text.RegularExpressions.Regex.Match(this.Context.CurrentConnectionConfig.ConnectionString.ToLower(), @"search path\=(\w+)").Groups[1].Value; + if (regValue.HasValue()) + { + schema = regValue; + } + } + + return schema; + } + + private DataTable GetTables() + { + return this.Context.Ado.GetDataTable("SHOW TABLES"); + } + + private DataTable GetSTables() + { + return this.Context.Ado.GetDataTable("SHOW STABLES"); + } + + #endregion + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/Insertable/TDengineInserttable.cs b/src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/Insertable/TDengineInserttable.cs new file mode 100644 index 000000000..8aa27c6d0 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/Insertable/TDengineInserttable.cs @@ -0,0 +1,90 @@ +namespace SqlSugar.TDengine +{ + public class TDengineInsertable : InsertableProvider where T : class, new() + { + public override int ExecuteReturnIdentity() + { + InsertBuilder.IsReturnIdentity = true; + PreToSql(); + string identityColumn = GetIdentityColumn(); + string sql = InsertBuilder.ToSqlString().Replace("$PrimaryKey", this.SqlBuilder.GetTranslationColumnName(identityColumn)); + RestoreMapping(); + var result = Ado.GetScalar(sql, InsertBuilder.Parameters == null ? null : InsertBuilder.Parameters.ToArray()).ObjToInt(); + After(sql, result); + return result; + } + public override async Task ExecuteReturnIdentityAsync() + { + InsertBuilder.IsReturnIdentity = true; + PreToSql(); + string identityColumn = GetIdentityColumn(); + string sql = InsertBuilder.ToSqlString().Replace("$PrimaryKey", this.SqlBuilder.GetTranslationColumnName(identityColumn)); + RestoreMapping(); + var obj = await Ado.GetScalarAsync(sql, InsertBuilder.Parameters == null ? null : InsertBuilder.Parameters.ToArray()).ConfigureAwait(false); + var result = obj.ObjToInt(); + After(sql, result); + return result; + } + public override KeyValuePair> ToSql() + { + var result = base.ToSql(); + var primaryKey = GetPrimaryKeys().FirstOrDefault(); + if (primaryKey != null) + { + primaryKey = this.SqlBuilder.GetTranslationColumnName(primaryKey); + } + return new KeyValuePair>(result.Key.Replace("$PrimaryKey", primaryKey), result.Value); + } + + public override long ExecuteReturnBigIdentity() + { + InsertBuilder.IsReturnIdentity = true; + PreToSql(); + string sql = InsertBuilder.ToSqlString().Replace("$PrimaryKey", this.SqlBuilder.GetTranslationColumnName(GetIdentityKeys().FirstOrDefault())); + RestoreMapping(); + var result = Convert.ToInt64(Ado.GetScalar(sql, InsertBuilder.Parameters == null ? null : InsertBuilder.Parameters.ToArray()) ?? "0"); + After(sql, result); + return result; + } + public override async Task ExecuteReturnBigIdentityAsync() + { + InsertBuilder.IsReturnIdentity = true; + PreToSql(); + string sql = InsertBuilder.ToSqlString().Replace("$PrimaryKey", this.SqlBuilder.GetTranslationColumnName(GetIdentityKeys().FirstOrDefault())); + RestoreMapping(); + var result = Convert.ToInt64(await Ado.GetScalarAsync(sql, InsertBuilder.Parameters == null ? null : InsertBuilder.Parameters.ToArray()).ConfigureAwait(false) ?? "0"); + After(sql, result); + return result; + } + + public override bool ExecuteCommandIdentityIntoEntity() + { + var result = InsertObjs.First(); + var identityKeys = GetIdentityKeys(); + if (identityKeys.Count == 0) { return this.ExecuteCommand() > 0; } + var idValue = ExecuteReturnBigIdentity(); + Check.Exception(identityKeys.Count > 1, "ExecuteCommandIdentityIntoEntity does not support multiple identity keys"); + var identityKey = identityKeys.First(); + object setValue = 0; + if (idValue > int.MaxValue) + setValue = idValue; + else + setValue = Convert.ToInt32(idValue); + var propertyName = this.Context.EntityMaintenance.GetPropertyName(identityKey); + typeof(T).GetProperties().First(t => string.Equals(t.Name, propertyName, StringComparison.OrdinalIgnoreCase)).SetValue(result, setValue, null); + return idValue > 0; + } + + private string GetIdentityColumn() + { + var identityColumn = GetIdentityKeys().FirstOrDefault(); + if (identityColumn == null) + { + var columns = this.Context.DbMaintenance.GetColumnInfosByTableName(InsertBuilder.GetTableNameString); + identityColumn = columns.First(it => it.IsIdentity || it.IsPrimarykey).DbColumnName; + } + return identityColumn; + } + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/Insertable/TagInserttable.cs b/src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/Insertable/TagInserttable.cs new file mode 100644 index 000000000..ce5dd2ce0 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/Insertable/TagInserttable.cs @@ -0,0 +1,129 @@ +using SqlSugar.TDengine; + +using System.Reflection; + +namespace SqlSugar +{ + public class TagInserttable where T : class, new() + { + internal IInsertable thisValue; + internal Func getChildTableNamefunc; + + internal SqlSugarProvider Context; + + public int ExecuteCommand() + { + var provider = (InsertableProvider)thisValue; + var inserObjects = provider.InsertObjs; + var attr = GetCommonSTableAttribute(typeof(T).GetCustomAttribute()); + Check.ExceptionEasy(attr == null || attr?.Tag1 == null, $"", $"{nameof(T)}缺少特性STableAttribute和Tag1"); + // 根据所有非空的 Tag 进行分组 + var groups = GetGroupInfos(inserObjects, attr); + foreach (var item in groups) + { + var childTableName = getChildTableNamefunc(attr.STableName, item.First()); + this.Context.Utilities.PageEach(item, 500, pageItems => + { + var sTableName = provider.SqlBuilder.GetTranslationColumnName(attr.STableName); + var tags = new List(); + List tagValues = GetTagValues(pageItems, attr); + var tagString = string.Join(",", tagValues.Where(v => !string.IsNullOrEmpty(v)).Select(v => $"'{v.ToSqlFilter()}'")); + tags.Add(tagString); + this.Context.Ado.ExecuteCommand($"CREATE TABLE IF NOT EXISTS {childTableName} USING {sTableName} TAGS ({tagString})"); + this.Context.Insertable(pageItems).IgnoreColumns(GetTagNames(pageItems.First(), attr).ToArray()).AS(childTableName).ExecuteCommand(); + }); + } + return inserObjects.Length; + } + + public async Task ExecuteCommandAsync() + { + var provider = (InsertableProvider)thisValue; + var inserObjects = provider.InsertObjs; + var attr = GetCommonSTableAttribute(typeof(T).GetCustomAttribute()); + Check.ExceptionEasy(attr == null || attr?.Tag1 == null, $"", $"{nameof(T)}缺少特性STableAttribute和Tag1"); + // 根据所有非空的 Tag 进行分组 + var groups = GetGroupInfos(inserObjects, attr); + foreach (var item in groups) + { + var childTableName = getChildTableNamefunc(attr.STableName, item.First()); + await this.Context.Utilities.PageEachAsync(item, 500, async pageItems => + { + var sTableName = provider.SqlBuilder.GetTranslationColumnName(attr.STableName); + var tags = new List(); + List tagValues = GetTagValues(pageItems, attr); + var tagString = string.Join(",", tagValues.Where(v => !string.IsNullOrEmpty(v)).Select(v => $"'{v.ToSqlFilter()}'")); + tags.Add(tagString); + await Context.Ado.ExecuteCommandAsync($"CREATE TABLE IF NOT EXISTS {childTableName} USING {sTableName} TAGS ({tagString})").ConfigureAwait(false); + await Context.Insertable(pageItems).IgnoreColumns(GetTagNames(pageItems.First(), attr).ToArray()).AS(childTableName).ExecuteCommandAsync().ConfigureAwait(false); + }).ConfigureAwait(false); + } + return inserObjects.Length; + } + + private static List GetTagValues(List pageItems, STableAttribute attr) + { + var tagValues = new List(); + var obj = pageItems.First(); + if (attr.Tag1 != null) + tagValues.Add(obj.GetType().GetProperty(attr.Tag1)?.GetValue(obj)?.ToString()); + + if (attr.Tag2 != null) + tagValues.Add(obj.GetType().GetProperty(attr.Tag2)?.GetValue(obj)?.ToString()); + + if (attr.Tag3 != null) + tagValues.Add(obj.GetType().GetProperty(attr.Tag3)?.GetValue(obj)?.ToString()); + + if (attr.Tag4 != null) + tagValues.Add(obj.GetType().GetProperty(attr.Tag4)?.GetValue(obj)?.ToString()); + return tagValues; + } + + private static List GetTagNames(T obj, STableAttribute attr) + { + var tagValues = new List(); + if (attr.Tag1 != null) + tagValues.Add(attr.Tag1); + + if (attr.Tag2 != null) + tagValues.Add(attr.Tag2); + + if (attr.Tag3 != null) + tagValues.Add(attr.Tag3); + + if (attr.Tag4 != null) + tagValues.Add(attr.Tag4); + return tagValues; + } + + private static IEnumerable> GetGroupInfos(T[] inserObjects, STableAttribute? attr) + { + var groups = inserObjects.GroupBy(it => + { + // 动态生成分组键 + var groupKey = new List(); + + if (attr.Tag1 != null) + groupKey.Add(it.GetType().GetProperty(attr.Tag1)?.GetValue(it)?.ToString()); + + if (attr.Tag2 != null) + groupKey.Add(it.GetType().GetProperty(attr.Tag2)?.GetValue(it)?.ToString()); + + if (attr.Tag3 != null) + groupKey.Add(it.GetType().GetProperty(attr.Tag3)?.GetValue(it)?.ToString()); + + if (attr.Tag4 != null) + groupKey.Add(it.GetType().GetProperty(attr.Tag4)?.GetValue(it)?.ToString()); + + // 将非空的 Tag 值用下划线连接作为分组键 + return string.Join("_", groupKey.Where(k => !string.IsNullOrEmpty(k))); + }); + return groups; + } + private STableAttribute GetCommonSTableAttribute(STableAttribute sTableAttribute) + { + return SqlSugar.TDengine.UtilMethods.GetCommonSTableAttribute(this.Context, sTableAttribute); + } + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/Queryable/TDengineSqlQueryable.cs b/src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/Queryable/TDengineSqlQueryable.cs new file mode 100644 index 000000000..5d1129b5d --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/Queryable/TDengineSqlQueryable.cs @@ -0,0 +1,63 @@ +namespace SqlSugar.TDengine +{ + public class TDengineQueryable : QueryableProvider + { + public override ISugarQueryable With(string withString) + { + return this; + } + + public override ISugarQueryable PartitionBy(string groupFileds) + { + this.GroupBy(groupFileds); + return this; + } + } + public class TDengineQueryable : QueryableProvider + { + public new ISugarQueryable With(string withString) + { + return this; + } + } + public class TDengineQueryable : QueryableProvider + { + + } + public class TDengineQueryable : QueryableProvider + { + + } + public class TDengineQueryable : QueryableProvider + { + + } + public class TDengineQueryable : QueryableProvider + { + + } + public class TDengineQueryable : QueryableProvider + { + + } + public class TDengineQueryable : QueryableProvider + { + + } + public class TDengineQueryable : QueryableProvider + { + + } + public class TDengineQueryable : QueryableProvider + { + + } + public class TDengineQueryable : QueryableProvider + { + + } + public class TDengineQueryable : QueryableProvider + { + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/SqlBuilder/TDengineBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/SqlBuilder/TDengineBuilder.cs new file mode 100644 index 000000000..dfe78013a --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/SqlBuilder/TDengineBuilder.cs @@ -0,0 +1,133 @@ +namespace SqlSugar.TDengine +{ + public class TDengineBuilder : SqlBuilderProvider + { + public override string SqlTranslationLeft + { + get + { + return "`"; + } + } + public override string SqlTranslationRight + { + get + { + return "`"; + } + } + public override string SqlDateNow + { + get + { + return "current_date"; + } + } + public override string FullSqlDateNow + { + get + { + return " now() "; + } + } + + public bool isAutoToLower + { + get + { + if (this.Context.CurrentConnectionConfig.MoreSettings == null) return true; + else if ( + this.Context.CurrentConnectionConfig.MoreSettings.PgSqlIsAutoToLower == false && + this.Context.CurrentConnectionConfig.MoreSettings?.PgSqlIsAutoToLowerCodeFirst == false) + { + return false; + } + else + { + return true; + } + } + } + public override string GetTranslationColumnName(string propertyName) + { + if (propertyName.Contains('.') && !propertyName.Contains(SqlTranslationLeft)) + { + return string.Join(".", propertyName.Split('.').Select(it => $"{SqlTranslationLeft}{it.ToLower(isAutoToLower)}{SqlTranslationRight}")); + } + + if (propertyName.Contains(SqlTranslationLeft)) return propertyName; + else + return SqlTranslationLeft + propertyName.ToLower(isAutoToLower) + SqlTranslationRight; + } + + //public override string GetNoTranslationColumnName(string name) + //{ + // return name.TrimEnd(Convert.ToChar(SqlTranslationRight)).TrimStart(Convert.ToChar(SqlTranslationLeft)).ToLower(); + //} + public override string GetTranslationColumnName(string entityName, string propertyName) + { + Check.ArgumentNullException(entityName, string.Format(null, ErrorMessage.ObjNotExistCompositeFormat, "Table Name")); + Check.ArgumentNullException(propertyName, string.Format(null, ErrorMessage.ObjNotExistCompositeFormat, "Column Name")); + var context = this.Context; + var mappingInfo = context + .MappingColumns + .FirstOrDefault(it => + it.EntityName.Equals(entityName, StringComparison.CurrentCultureIgnoreCase) && + it.PropertyName.Equals(propertyName, StringComparison.CurrentCultureIgnoreCase)); + return (mappingInfo == null ? SqlTranslationLeft + propertyName.ToLower(isAutoToLower) + SqlTranslationRight : SqlTranslationLeft + mappingInfo.DbColumnName.ToLower(isAutoToLower) + SqlTranslationRight); + } + + public override string GetTranslationTableName(string name) + { + Check.ArgumentNullException(name, string.Format(null, ErrorMessage.ObjNotExistCompositeFormat, "Table Name")); + var context = this.Context; + + var mappingInfo = context + .MappingTables + .FirstOrDefault(it => it.EntityName.Equals(name, StringComparison.CurrentCultureIgnoreCase)); + if (mappingInfo == null && name.Contains('.') && name.Contains('`')) + { + return name; + } + name = (mappingInfo == null ? name : mappingInfo.DbTableName); + if (name.Contains('.') && !name.Contains('(') && !name.Contains("\".\"")) + { + return string.Join(".", name.ToLower(isAutoToLower).Split('.').Select(it => SqlTranslationLeft + it + SqlTranslationRight)); + } + else if (name.Contains('(')) + { + return name; + } + else if (name.Contains(SqlTranslationLeft) && name.Contains(SqlTranslationRight)) + { + return name; + } + else + { + return SqlTranslationLeft + name.ToLower(isAutoToLower).TrimEnd('"').TrimStart('"') + SqlTranslationRight; + } + } + public override string GetUnionFomatSql(string sql) + { + return " ( " + sql + " ) "; + } + + public override Type GetNullType(string tableName, string columnName) + { + if (tableName != null) + tableName = tableName.Trim(); + var columnInfo = this.Context.DbMaintenance.GetColumnInfosByTableName(tableName).FirstOrDefault(z => z.DbColumnName?.ToLower() == columnName?.ToLower()); + if (columnInfo != null) + { + var cTypeName = this.Context.Ado.DbBind.GetCsharpTypeNameByDbTypeName(columnInfo.DataType); + var value = SqlSugar.UtilMethods.GetTypeByTypeName(cTypeName); + if (value != null) + { + var key = "GetNullType_" + tableName + columnName; + return new ReflectionInoCacheService().GetOrCreate(key, () => value); + } + } + return null; + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/SqlBuilder/TDengineDeleteBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/SqlBuilder/TDengineDeleteBuilder.cs new file mode 100644 index 000000000..b8eed82e4 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/SqlBuilder/TDengineDeleteBuilder.cs @@ -0,0 +1,7 @@ +namespace SqlSugar.TDengine +{ + public class TDengineDeleteBuilder : DeleteBuilder + { + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/SqlBuilder/TDengineExpressionContext.cs b/src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/SqlBuilder/TDengineExpressionContext.cs new file mode 100644 index 000000000..51cd249e2 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/SqlBuilder/TDengineExpressionContext.cs @@ -0,0 +1,444 @@ +using Dm.util; +namespace SqlSugar.TDengine +{ + public class TDengineExpressionContext : ExpressionContext, ILambdaExpressions + { + public SqlSugarProvider Context { get; set; } + public TDengineExpressionContext() + { + base.DbMehtods = new TDengineExpressionContextMethod(); + } + public override string SqlTranslationLeft + { + get + { + return "`"; + } + } + public override string SqlTranslationRight + { + get + { + return "`"; + } + } + public override string GetTranslationText(string name) + { + return SqlTranslationLeft + name.ToLower(isAutoToLower) + SqlTranslationRight; + } + public bool isAutoToLower + { + get + { + return base.PgSqlIsAutoToLower; + } + } + public override string GetTranslationTableName(string entityName, bool isMapping = true) + { + Check.ArgumentNullException(entityName, string.Format(null, ErrorMessage.ObjNotExistCompositeFormat, "Table Name")); + if (IsTranslationText(entityName)) return entityName; + isMapping = isMapping && this.MappingTables.HasValue(); + var isComplex = entityName.Contains(UtilConstants.Dot); + if (isMapping && isComplex) + { + var columnInfo = entityName.Split(UtilConstants.DotChar); + var mappingInfo = this.MappingTables.FirstOrDefault(it => it.EntityName.Equals(columnInfo.Last(), StringComparison.CurrentCultureIgnoreCase)); + if (mappingInfo != null) + { + columnInfo[columnInfo.Length - 1] = mappingInfo.EntityName; + } + return string.Join(UtilConstants.Dot, columnInfo.Select(it => GetTranslationText(it))); + } + else if (isMapping) + { + var mappingInfo = this.MappingTables.FirstOrDefault(it => it.EntityName.Equals(entityName, StringComparison.CurrentCultureIgnoreCase)); + + var tableName = mappingInfo?.DbTableName + ""; + if (tableName.Contains('.')) + { + tableName = string.Join(UtilConstants.Dot, tableName.Split(UtilConstants.DotChar).Select(it => GetTranslationText(it))); + return tableName; + } + + return SqlTranslationLeft + (mappingInfo == null ? entityName : mappingInfo.DbTableName).ToLower(isAutoToLower) + SqlTranslationRight; + } + else if (isComplex) + { + return string.Join(UtilConstants.Dot, entityName.Split(UtilConstants.DotChar).Select(it => GetTranslationText(it))); + } + else + { + return GetTranslationText(entityName); + } + } + public override string GetTranslationColumnName(string columnName) + { + Check.ArgumentNullException(columnName, string.Format(null, ErrorMessage.ObjNotExistCompositeFormat, "Column Name")); + if (columnName.Substring(0, 1) == this.SqlParameterKeyWord) + { + return columnName; + } + if (IsTranslationText(columnName)) return columnName; + if (columnName.Contains(UtilConstants.Dot)) + { + return string.Join(UtilConstants.Dot, columnName.Split(UtilConstants.DotChar).Select(it => GetTranslationText(it))); + } + else + { + return GetTranslationText(columnName); + } + } + public override string GetDbColumnName(string entityName, string propertyName) + { + if (this.MappingColumns.HasValue()) + { + var mappingInfo = this.MappingColumns.SingleOrDefault(it => it.EntityName == entityName && it.PropertyName == propertyName); + return (mappingInfo == null ? propertyName : mappingInfo.DbColumnName).ToLower(isAutoToLower); + } + else + { + return propertyName.ToLower(isAutoToLower); + } + } + + public string GetValue(object entityValue) + { + if (entityValue == null) + return null; + var type = UtilMethods.GetUnderType(entityValue.GetType()); + if (UtilConstants.NumericalTypes.Contains(type)) + { + return entityValue.ToString(); + } + else if (type == UtilConstants.DateType) + { + return this.DbMehtods.ToDate(new MethodCallExpressionModel() + { + Args = new System.Collections.Generic.List() { + new MethodCallExpressionArgs(){ MemberName=$"'{entityValue}'" } + } + }); + } + else + { + return this.DbMehtods.ToString(new MethodCallExpressionModel() + { + Args = new System.Collections.Generic.List() { + new MethodCallExpressionArgs(){ MemberName=$"'{entityValue}'" } + } + }); + } + } + } + public class TDengineExpressionContextMethod : DefaultDbMethod, IDbMethods + { + public override string CharIndex(MethodCallExpressionModel model) + { + return string.Format(" (strpos ({1},{0})-1)", model.Args[0].MemberName, model.Args[1].MemberName); + } + public override string TrueValue() + { + return "true"; + } + public override string FalseValue() + { + return "false"; + } + + public override string Substring(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + var parameter3 = model.Args[2]; + if (parameter2.MemberValue is int && parameter3.MemberValue is int) + { + model.Parameters.RemoveAll(it => it.ParameterName.Equals(parameter2.MemberName) || it.ParameterName.Equals(parameter3.MemberName)); + return string.Format("SUBSTR({0},{1},{2})", parameter.MemberName, Convert.ToInt32(parameter2.MemberValue) + 1, parameter3.MemberValue); + } + else + { + return string.Format("SUBSTR({0},{1},{2})", parameter.MemberName, parameter2.MemberName, parameter3.MemberName); + } + } + public override string DateDiff(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + var parameter3 = model.Args[2]; + return string.Format(" TIMEDIFF({1},{2},1{0}) ", parameter.MemberValue.ObjToString().ToLower().First(), parameter2.MemberName, parameter3.MemberName); + } + public override string IIF(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + var parameter3 = model.Args[2]; + if (parameter.Type == UtilConstants.BoolType) + { + parameter.MemberName = parameter.MemberName.ToString().Replace("=1", "=true"); + parameter2.MemberName = false; + parameter3.MemberName = true; + } + return string.Format("( CASE WHEN {0} THEN {1} ELSE {2} END )", parameter.MemberName, parameter2.MemberName, parameter3.MemberName); + } + public override string DateValue(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + var format = parameter2.MemberValue.ObjToString(); + return string.Format(" {0}({1}) ", format, parameter.MemberName); + } + + public override string Contains(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + return string.Format(" ({0} like {1} ) ", parameter.MemberName, ("%" + parameter2.MemberValue + "%").ToSqlValue()); + } + + public override string StartsWith(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + return string.Format(" ({0} like {1} ) ", parameter.MemberName, ("%" + parameter2.MemberValue).ToSqlValue()); + } + + public override string EndsWith(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + return string.Format("({0} like {1} ) ", parameter.MemberName, (parameter2.MemberValue + "%").ToSqlValue()); + } + + public override string DateIsSameDay(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + return string.Format(" ( to_char({0},'yyyy-MM-dd')=to_char({1},'yyyy-MM-dd') ) ", parameter.MemberName, parameter2.MemberName); ; + } + + public override string HasValue(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format("( {0} IS NOT NULL )", parameter.MemberName); + } + + public override string DateIsSameByType(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + var parameter3 = model.Args[2]; + DateType dateType = (DateType)parameter3.MemberValue; + var format = "yyyy-MM-dd"; + if (dateType == DateType.Quarter) + { + return string.Format(" (date_trunc('quarter',{0})=date_trunc('quarter',{1}) ) ", parameter.MemberName, parameter2.MemberName, format); + } + switch (dateType) + { + case DateType.Year: + format = "yyyy"; + break; + case DateType.Month: + format = "yyyy-MM"; + break; + case DateType.Day: + break; + case DateType.Hour: + format = "yyyy-MM-dd HH"; + break; + case DateType.Second: + format = "yyyy-MM-dd HH:mm:ss"; + break; + case DateType.Minute: + format = "yyyy-MM-dd HH:mm"; + break; + case DateType.Millisecond: + format = "yyyy-MM-dd HH:mm.ms"; + break; + default: + break; + } + return string.Format(" ( to_char({0},'{2}')=to_char({1},'{2}') ) ", parameter.MemberName, parameter2.MemberName, format); + } + + public override string ToDate(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS timestamp)", parameter.MemberName); + } + public override string ToDateShort(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST( SUBSTR(TO_ISO8601({0}),1,10) AS timestamp)", parameter.MemberName); + } + public override string DateAddByType(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + var parameter3 = model.Args[2]; + var result = string.Format(" {1}+{2}{0} ", parameter3.MemberValue.ObjToString().ToLower().First(), parameter.MemberName, parameter2.MemberValue); + return result.replace("+-", "-"); + } + + + public override string DateAddDay(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter2 = model.Args[1]; + return string.Format(" ({0} + ({1}||'day')::INTERVAL) ", parameter.MemberName, parameter2.MemberName); + } + + public override string ToInt32(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS INT4)", parameter.MemberName); + } + + public override string ToInt64(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS INT8)", parameter.MemberName); + } + + public override string ToString(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS VARCHAR)", parameter.MemberName); + } + + public override string ToGuid(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS UUID)", parameter.MemberName); + } + + public override string ToDouble(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS DOUBLE)", parameter.MemberName); + } + + public override string ToBool(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS boolean)", parameter.MemberName); + } + + public override string ToDecimal(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" CAST({0} AS DOUBLE)", parameter.MemberName); + } + + public override string Length(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + return string.Format(" LENGTH({0})", parameter.MemberName); + } + public override string MergeString(params string[] strings) + { + return " concat(" + string.Join(",", strings).Replace("+", "") + ") "; + } + public override string IsNull(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter1 = model.Args[1]; + return string.Format("(CASE WHEN {0} IS NULL THEN {1} ELSE {0} END)", parameter.MemberName, parameter1.MemberName); + } + public override string GetDate() + { + return "NOW()"; + } + public override string GetRandom() + { + return "RANDOM()"; + } + + public override string EqualTrue(string fieldName) + { + return "( " + fieldName + "=true )"; + } + + public override string JsonField(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter1 = model.Args[1]; + //var parameter2 = model.Args[2]; + //var parameter3= model.Args[3]; + var result = GetJson(parameter.MemberName, parameter1.MemberName, model.Args.Count == 2); + if (model.Args.Count > 2) + { + result = GetJson(result, model.Args[2].MemberName, model.Args.Count == 3); + } + if (model.Args.Count > 3) + { + result = GetJson(result, model.Args[3].MemberName, model.Args.Count == 4); + } + if (model.Args.Count > 4) + { + result = GetJson(result, model.Args[4].MemberName, model.Args.Count == 5); + } + if (model.Args.Count > 5) + { + result = GetJson(result, model.Args[5].MemberName, model.Args.Count == 6); + } + return result; + } + + public override string JsonContainsFieldName(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + var parameter1 = model.Args[1]; + return $"({parameter.MemberName}::jsonb ?{parameter1.MemberName})"; + } + + private string GetJson(object memberName1, object memberName2, bool isLast) + { + if (isLast) + { + return $"({memberName1}::json->>{memberName2})"; + } + else + { + return $"({memberName1}->{memberName2})"; + } + } + + public override string JsonArrayLength(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + //var parameter1 = model.Args[1]; + return $" json_array_length({parameter.MemberName}::json) "; + } + + public override string JsonParse(MethodCallExpressionModel model) + { + var parameter = model.Args[0]; + //var parameter1 = model.Args[1]; + return $" ({parameter.MemberName}::json) "; + } + + public override string JsonArrayAny(MethodCallExpressionModel model) + { + if (SqlSugar.UtilMethods.IsNumber(model.Args[1].MemberValue.GetType().Name)) + { + return $" {model.Args[0].MemberName}::jsonb @> '[{model.Args[1].MemberValue.ObjToStringNoTrim().ToSqlFilter()}]'::jsonb"; + } + else + { + return $" {model.Args[0].MemberName}::jsonb @> '[\"{model.Args[1].MemberValue}\"]'::jsonb"; + } + } + public override string JsonListObjectAny(MethodCallExpressionModel model) + { + if (SqlSugar.UtilMethods.IsNumber(model.Args[2].MemberValue.GetType().Name)) + { + return $" {model.Args[0].MemberName}::jsonb @> '[{{\"{model.Args[1].MemberValue}\":{model.Args[2].MemberValue}}}]'::jsonb"; + } + else + { + return $" {model.Args[0].MemberName}::jsonb @> '[{{\"{model.Args[1].MemberValue}\":\"{model.Args[2].MemberValue.ObjToStringNoTrim().ToSqlFilter()}\"}}]'::jsonb"; + } + } + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/SqlBuilder/TDengineFastBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/SqlBuilder/TDengineFastBuilder.cs new file mode 100644 index 000000000..9a22d14fd --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/SqlBuilder/TDengineFastBuilder.cs @@ -0,0 +1,218 @@ + +using SqlSugar.TDengineAdo; + +using System.Data; +using System.Globalization; +using System.Text; + +namespace SqlSugar.TDengine +{ + public class TDengineFastBuilder : FastBuilder, IFastBuilder + { + public const string TagKey = "TDengineFastBuilderTagNames"; + public override DbFastestProperties DbFastestProperties { get; set; } = new DbFastestProperties() + { + NoPage = true + }; + + public async Task ExecuteBulkCopyAsync(DataTable dt) + { + // 移除自增列 + var identities = this.FastEntityInfo.Columns.Where(it => it.IsIdentity).Select(it => it.DbColumnName).ToList(); + foreach (var identity in identities) + { + if (dt.Columns.Contains(identity)) + { + dt.Columns.Remove(identity); + } + } + var db = this.Context; + string[] tagNames = null; + if (db.TempItems?.ContainsKey(TagKey) == true) + { + tagNames = db.TempItems[TagKey] as string[]; + } + await BulkInsertToTDengine((TDengineConnection)Context.Ado.Connection, dt.TableName, dt, Context.Ado.IsNoTran(), tagNames).ConfigureAwait(false); + return dt.Rows.Count; + } + + public async Task BulkInsertToTDengine(TDengineConnection conn, string tableName, DataTable table, bool isTran, string[] tagColumns) + { + + string insertSql = string.Empty; + try + { + + if (tagColumns?.Length > 0) + { + StringBuilder sb = new StringBuilder(); + StringBuilder sbTables = new StringBuilder(); + // 创建一个列名映射(忽略大小写) + var columnMap = table.Columns.Cast() + .ToDictionary(c => c.ColumnName, c => c, StringComparer.OrdinalIgnoreCase); + + // 检查所有 tagColumns 是否在 DataTable 中存在 + foreach (var col in tagColumns) + { + if (!columnMap.ContainsKey(col)) + throw new Exception($"Column '{col}' not found in DataTable."); + } + + // 用 LINQ 分组 + var groups = table.AsEnumerable() + .GroupBy(row => string.Join("||", tagColumns.Select(tc => row[columnMap[tc]].ToString()))); + + foreach (var group in groups) + { + // 构建一个新的子表(结构与原表一致) + DataTable childTable = table.Clone(); + + // 将分组行复制到子表中 + foreach (var row in group) + { + childTable.ImportRow(row); + } + + // 调用 InsertChildTable + InsertChildTable(tableName, childTable, tagColumns, sb, sbTables); + + var sql = sb.ToString(); + var result = await Context.Ado.ExecuteCommandAsync(sql).ConfigureAwait(false); + sb.Clear(); + } + } + else + { + // Build the column names and value placeholders + var valuePlaceholdersList = table.Rows.Cast().Select(row => + { + var values = row.ItemArray.Select(item => FormatValue(item)).ToList(); + return $"({string.Join(", ", values)})"; + }).ToList(); + + var valuePlaceholders = string.Join(", ", valuePlaceholdersList); + // Construct SQL without tags + var columnNames = string.Join(", ", table.Columns.Cast().Select(c => c.ColumnName)); + insertSql = $"INSERT INTO {tableName} ({columnNames}) VALUES {valuePlaceholders}"; + + // Execute the command asynchronously + await Context.Ado.ExecuteCommandAsync(insertSql).ConfigureAwait(false); + } + } + catch (Exception ex) + { + throw new Exception(ex.Message + "\r\n" + insertSql); + } + finally + { + this.Context.TempItems.Remove(TagKey); + } + } + + private StringBuilder InsertChildTable(string tableName, DataTable table, string[] tagColumns, StringBuilder sb, StringBuilder sbtables) + { + var builder = InstanceFactory.GetSqlBuilderWithContext(this.Context); + var columnMap = table.Columns.Cast() + .ToDictionary(c => c.ColumnName, c => c.ColumnName, StringComparer.OrdinalIgnoreCase); + + var firstRow = table.Rows[0]; + string tags = string.Join(", ", tagColumns.Select(tag => FormatValue(firstRow[columnMap[tag]]))); + string tagsValues = string.Join("_", tagColumns.Select(tag => firstRow[columnMap[tag]].ToString())); + + // 移除标签列,只留下数据列 + foreach (var item in tagColumns) + { + table.Columns.Remove(item); + } + + var columnNames = string.Join(", ", table.Columns.Cast() + .Select(c => builder.GetTranslationColumnName(c.ColumnName))); + + var action = this.Context.TempItems[TagKey + "action"] as Func; + var subTableName = builder.GetTranslationColumnName(action(tagsValues, tableName.Replace("`", ""))); + + // sbtables.AppendLine($"CREATE TABLE {subTableName} USING {tableName} TAGS({tags});"); + + var sqlBuilder = sb; + var valuesList = new List(); + + foreach (DataRow row in table.Rows) + { + var values = row.ItemArray.Select(item => FormatValue(item)).ToList(); + var valuePart = $"({string.Join(", ", values)})"; + valuesList.Add(valuePart); + } + + if (valuesList.Count > 0) + { + string insertSql = $"INSERT INTO {subTableName} USING {tableName} TAGS({tags}) ({columnNames}) VALUES {string.Join(", ", valuesList)};"; + sqlBuilder.AppendLine(insertSql); + } + + return sqlBuilder; + } + + + public static void SetTags(ISqlSugarClient db, Func action, params string[] tagNames) + { + if (db.TempItems == null) + { + db.TempItems = new Dictionary(); + } + // 删除旧的值(如果存在) + db.TempItems.Remove(TagKey); + db.TempItems.Remove(TagKey + "action"); + db.TempItems.Add(TagKey, tagNames); + db.TempItems.Add(TagKey + "action", action); + } + + public object FormatValue(object value) + { + if (value == null || value == DBNull.Value) + { + return "NULL"; + } + else + { + var type = value.GetType(); + if (type == UtilConstants.DateType) + { + return Convert.ToDateTime(value).ToString("yyyy-MM-dd HH:mm:ss.fffffff").ToSqlValue(); + } + else if (type == UtilConstants.ByteArrayType) + { + string bytesString = "0x" + BitConverter.ToString((byte[])value); + return bytesString; + } + else if (type.IsEnum()) + { + if (this.Context.CurrentConnectionConfig.MoreSettings?.TableEnumIsString == true) + { + return value.ToSqlValue(); + } + else + { + return Convert.ToInt64(value); + } + } + else if (type == UtilConstants.BoolType) + { + return value.ObjToBool() ? "1" : "0"; + } + else if (type == UtilConstants.StringType || type == UtilConstants.ObjType) + { + return "'" + value.ToString().ToSqlFilter() + "'"; + } + else if (value is decimal v) + { + return v.ToString(CultureInfo.InvariantCulture); + } + else + { + return "'" + value.ToString() + "'"; + } + } + } + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/SqlBuilder/TDengineInsertBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/SqlBuilder/TDengineInsertBuilder.cs new file mode 100644 index 000000000..1bae0573a --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/SqlBuilder/TDengineInsertBuilder.cs @@ -0,0 +1,184 @@ +using System.Text; + +namespace SqlSugar.TDengine +{ + public class TDengineInsertBuilder : InsertBuilder + { + public override string SqlTemplate + { + get + { + if (IsReturnIdentity) + { + return @"INSERT INTO {0} + ({1}) + VALUES + ({2}) "; + } + else + { + return @"INSERT INTO {0} + ({1}) + VALUES + ({2})"; + + } + } + } + public override string SqlTemplateBatch => "INSERT INTO {0} ({1})"; + public override string SqlTemplateBatchUnion => " VALUES "; + + public override string SqlTemplateBatchSelect => " {0} "; + + public override Func ConvertInsertReturnIdFunc { get; set; } = (name, sql) => + { + return sql.Trim().TrimEnd(';') + $"returning {name} "; + }; + public override string ToSqlString() + { + if (IsNoInsertNull) + { + DbColumnInfoList = DbColumnInfoList.Where(it => it.Value != null).ToList(); + } + var groupList = DbColumnInfoList.GroupBy(it => it.TableId).ToList(); + var isSingle = groupList.Count == 1; + string columnsString = string.Join(",", groupList.First().Select(it => Builder.GetTranslationColumnName(it.DbColumnName))); + if (isSingle) + { + string columnParametersString = string.Join(",", this.DbColumnInfoList.Select(it => base.GetDbColumn(it, Builder.SqlParameterKeyWord + it.DbColumnName))); + ActionMinDate(); + return string.Format(SqlTemplate, GetTableNameString, columnsString, columnParametersString); + } + else + { + StringBuilder batchInsetrSql = new StringBuilder(); + int pageSize = 10000000; + int pageIndex = 1; + if (IsNoPage && IsReturnPkList) + { + pageSize = groupList.Count; + } + int totalRecord = groupList.Count; + int pageCount = (totalRecord + pageSize - 1) / pageSize; + while (pageCount >= pageIndex) + { + batchInsetrSql.AppendFormat(SqlTemplateBatch, GetTableNameString, columnsString); + int i = 0; + foreach (var columns in groupList.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList()) + { + var isFirst = i == 0; + if (isFirst) + { + batchInsetrSql.Append(SqlTemplateBatchUnion); + } + batchInsetrSql.Append("\r\n ( " + string.Join(",", columns.Select(it => + { + if (it.InsertServerTime || it.InsertSql.HasValue() || it.SqlParameterDbType is Type || it?.PropertyType?.Name == "DateOnly" || it?.PropertyType?.Name == "TimeOnly") + { + if (it.InsertServerTime) + { + return DateTime.Now.AddMilliseconds(i).ToString("yyyy-MM-dd HH:mm:ss.ffffff").ToSqlValue(); + } + return GetDbColumn(it, null); + } + object value = null; + if (it?.Value is DateTime) + { + value = it.Value.ObjToDate().ToString("yyyy-MM-dd HH:mm:ss.fff"); + } + else if (it?.Value is bool) + { + value = it.Value?.ToString()?.ToLower(); + } + else if (it.Value is DateTimeOffset) + { + return FormatDateTimeOffset(it.Value); + } + else if (it.IsArray && it.Value != null) + { + return FormatValue(it.Value, it.PropertyName, i, it); + } + else + { + value = it.Value; + } + if (value == null || value == DBNull.Value) + { + return string.Format(SqlTemplateBatchSelect, "NULL"); + } + return string.Format(SqlTemplateBatchSelect, "'" + value.ObjToStringNoTrim().ToSqlFilter() + "'"); + })) + "),"); + ++i; + } + pageIndex++; + batchInsetrSql.Remove(batchInsetrSql.Length - 1, 1).Append("\r\n;\r\n"); + } + return batchInsetrSql.ToString(); + } + } + + public object FormatValue(object value, string name, int i, DbColumnInfo columnInfo) + { + if (value == null) + { + return "NULL"; + } + else + { + var type = value.GetType(); + if (type == UtilConstants.DateType || columnInfo.IsArray || columnInfo.IsJson) + { + var parameterName = this.Builder.SqlParameterKeyWord + name + i; + var paramter = new SugarParameter(parameterName, value); + if (columnInfo.IsJson) + { + paramter.IsJson = true; + } + if (columnInfo.IsArray) + { + paramter.IsArray = true; + } + this.Parameters.Add(paramter); + return parameterName; + } + else if (type == UtilConstants.ByteArrayType) + { + string bytesString = "0x" + BitConverter.ToString((byte[])value); + return bytesString; + } + else if (type.IsEnum()) + { + if (this.Context.CurrentConnectionConfig.MoreSettings?.TableEnumIsString == true) + { + return value.ToSqlValue(); + } + else + { + return Convert.ToInt64(value); + } + } + else if (type == UtilConstants.DateTimeOffsetType) + { + return FormatDateTimeOffset(value); + } + else if (type == UtilConstants.BoolType) + { + return value.ObjToBool() ? "1" : "0"; + } + else if (type == UtilConstants.StringType || type == UtilConstants.ObjType) + { + return "'" + value.ToString().ToSqlFilter() + "'"; + } + else + { + return "'" + value.ToString() + "'"; + } + } + } + public override string FormatDateTimeOffset(object value) + { + return "'" + ((DateTimeOffset)value).ToString("o") + "'"; + } + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/SqlBuilder/TDengineQueryBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/SqlBuilder/TDengineQueryBuilder.cs new file mode 100644 index 000000000..fca351ab3 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/SqlBuilder/TDengineQueryBuilder.cs @@ -0,0 +1,120 @@ +using System.Text; +using System.Text.RegularExpressions; + +namespace SqlSugar.TDengine +{ + public partial class TDengineQueryBuilder : QueryBuilder + { + #region Sql Template + public override string PageTempalte + { + get + { + /* + SELECT * FROM TABLE WHERE CONDITION ORDER BY ID DESC LIMIT 10 offset 0 + */ + var template = "SELECT {0} FROM {1} {2} {3} {4} LIMIT {6} offset {5}"; + return template; + } + } + public override string DefaultOrderByTemplate + { + get + { + return "ORDER BY NOW() "; + } + } + + #endregion + + #region Common Methods + public override string GetTableNameString + { + get + { + if (this.TableShortName != null && this.Context.CurrentConnectionConfig?.MoreSettings?.PgSqlIsAutoToLower == false) + { + this.TableShortName = Builder.GetTranslationColumnName(this.TableShortName); + } + return base.GetTableNameString; + } + } + public override bool IsComplexModel(string sql) + { + return Regex.IsMatch(sql, @"AS ""\w+\.\w+""") || Regex.IsMatch(sql, @"AS ""\w+\.\w+\.\w+"""); + } + public override string ToSqlString() + { + base.AppendFilter(); + string oldOrderValue = this.OrderByValue; + string result = null; + sql = new StringBuilder(); + sql.AppendFormat(SqlTemplate, GetSelectValue, GetTableNameString, GetWhereValueString, GetGroupByString + HavingInfos, (Skip != null || Take != null) ? null : GetOrderByString); + if (IsCount) { return sql.ToString(); } + if (Skip != null && Take == null) + { + if (this.OrderByValue == "ORDER BY ") this.OrderByValue += GetSelectValue.Split(',')[0]; + result = string.Format(PageTempalte, GetSelectValue, GetTableNameString, GetWhereValueString, GetGroupByString + HavingInfos, (Skip != null || Take != null) ? null : GetOrderByString, Skip.ObjToInt(), long.MaxValue); + } + else if (Skip == null && Take != null) + { + if (this.OrderByValue == "ORDER BY ") this.OrderByValue += GetSelectValue.Split(',')[0]; + result = string.Format(PageTempalte, GetSelectValue, GetTableNameString, GetWhereValueString, GetGroupByString + HavingInfos, GetOrderByString, 0, Take.ObjToInt()); + } + else if (Skip != null && Take != null) + { + if (this.OrderByValue == "ORDER BY ") this.OrderByValue += GetSelectValue.Split(',')[0]; + result = string.Format(PageTempalte, GetSelectValue, GetTableNameString, GetWhereValueString, GetGroupByString + HavingInfos, GetOrderByString, Skip.ObjToInt() > 0 ? Skip.ObjToInt() : 0, Take); + } + else + { + result = sql.ToString(); + } + this.OrderByValue = oldOrderValue; + result = GetSqlQuerySql(result); + if (result.IndexOf("-- No table") > 0) + { + return "-- No table"; + } + if (TranLock != null) + { + result = result + TranLock; + } + return result; + } + + #endregion + + #region Get SQL Partial + public override string GetSelectValue + { + get + { + string result = string.Empty; + if (this.SelectValue == null || this.SelectValue is string) + { + result = GetSelectValueByString(); + } + else + { + result = GetSelectValueByExpression(); + } + if (this.SelectType == ResolveExpressType.SelectMultiple) + { + this.SelectCacheKey = this.SelectCacheKey + string.Join("-", this.JoinQueryInfos.Select(it => it.TableName)); + } + if (IsDistinct) + { + result = "distinct " + result; + } + if (this.SubToListParameters?.Count > 0) + { + result = SubToListMethod(result); + } + return result; + } + } + + #endregion + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/SqlBuilder/TDengineUpdateBuilder.cs b/src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/SqlBuilder/TDengineUpdateBuilder.cs new file mode 100644 index 000000000..50ec566cb --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/SqlBuilder/TDengineUpdateBuilder.cs @@ -0,0 +1,16 @@ +namespace SqlSugar.TDengine +{ + public class TDengineUpdateBuilder : UpdateBuilder + { + public override string ToSqlString() + { + throw new NotSupportedException("TDengine库不支持更新操作"); + } + protected override string TomultipleSqlString(List> groupList) + { + throw new NotSupportedException("TDengine库不支持更新操作"); + } + + + } +} diff --git a/src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/TDengineProvider.cs b/src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/TDengineProvider.cs new file mode 100644 index 000000000..8d2db64b4 --- /dev/null +++ b/src/Admin/ThingsGateway.SqlSugar/TDengine/TDengine/TDengineProvider.cs @@ -0,0 +1,124 @@ +using SqlSugar.TDengineAdo; + +using System.Data; +using System.Data.Common; +using System.Text.RegularExpressions; +namespace SqlSugar.TDengine +{ + public partial class TDengineProvider : AdoProvider + { + public TDengineProvider() + { + + + } + public override IDbConnection Connection + { + get + { + if (base._DbConnection == null) + { + try + { + var TDengineConnectionString = base.Context.CurrentConnectionConfig.ConnectionString; + TDengineConnectionString = Regex.Replace(TDengineConnectionString, @"\;db\=", ";Database=", RegexOptions.IgnoreCase); + base._DbConnection = new TDengineConnection(TDengineConnectionString); + } + catch (Exception) + { + throw; + + } + } + return base._DbConnection; + } + set + { + base._DbConnection = value; + } + } + public override void BeginTran() + { + + } + + public override void BeginTran(string transactionName) + { + + } + ///