From d26faa599e8e696b73747f861a084b576b87fe57 Mon Sep 17 00:00:00 2001 From: cKey <35512826+colinin@users.noreply.github.com> Date: Fri, 11 Nov 2022 16:52:19 +0800 Subject: [PATCH] add null/not null queryable --- .../Table/src/components/AdvancedSearch.vue | 8 + .../Table/src/types/advancedSearch.ts | 6 +- .../src/components/Table/src/types/table.ts | 3 +- apps/vue/src/locales/lang/en/component.ts | 2 + apps/vue/src/locales/lang/zh-CN/component.ts | 2 + aspnet-core/LINGYUN.MicroService.Common.sln | 22 +- .../Queryable/Dto/DynamicParamterDto.cs | 9 +- .../Queryable/DynamicQueryableAppService.cs | 108 +++++- .../LINGYUN.Linq.Dynamic.Queryable.csproj | 5 + .../Dynamic/Queryable/DynamicComparison.cs | 10 +- .../Linq/Dynamic/Queryable/DynamicParamter.cs | 8 +- .../Expressions/ObjectQueryableExtensions.cs | 201 +++++++++-- .../BackgroundJobInfoAppService.cs | 6 +- ...skManagementHttpApiHostModule.Configure.cs | 2 +- .../appsettings.Development.json | 3 +- ...Queryable.EntityFrameworkCore.Tests.csproj | 19 + ...micQueryableEntityFrameworkCoreTestBase.cs | 4 + ...cQueryableEntityFrameworkCoreTestModule.cs | 9 + ...ynamicQueryableEntityFrameworkCoreTests.cs | 302 ++++++++++++++++ .../Usings.cs | 5 + ...NGYUN.Abp.EntityFrameworkCore.Tests.csproj | 2 +- .../AbpEntityFrameworkCoreTestModule.cs | 23 +- .../EfCoreTestDbContext.cs | 24 ++ .../EntityFrameworkCore/EfCoreTestEntity.cs | 25 ++ .../EfCoreTestEntityDataSeeder.cs | 33 ++ ...INGYUN.Linq.Dynamic.Queryable.Tests.csproj | 18 + .../Queryable/DynamicQueryableTestBase.cs | 5 + .../Queryable/DynamicQueryableTestModule.cs | 8 + .../Queryable/DynamicQueryableTests.cs | 337 ++++++++++++++++++ .../Usings.cs | 4 + 30 files changed, 1164 insertions(+), 49 deletions(-) create mode 100644 aspnet-core/tests/LINGYUN.Abp.DynamicQueryable.EntityFrameworkCore.Tests/LINGYUN.Abp.DynamicQueryable.EntityFrameworkCore.Tests.csproj create mode 100644 aspnet-core/tests/LINGYUN.Abp.DynamicQueryable.EntityFrameworkCore.Tests/LINGYUN/Abp/DynamicQueryable/EntityFrameworkCore/AbpDynamicQueryableEntityFrameworkCoreTestBase.cs create mode 100644 aspnet-core/tests/LINGYUN.Abp.DynamicQueryable.EntityFrameworkCore.Tests/LINGYUN/Abp/DynamicQueryable/EntityFrameworkCore/AbpDynamicQueryableEntityFrameworkCoreTestModule.cs create mode 100644 aspnet-core/tests/LINGYUN.Abp.DynamicQueryable.EntityFrameworkCore.Tests/LINGYUN/Abp/DynamicQueryable/EntityFrameworkCore/DynamicQueryableEntityFrameworkCoreTests.cs create mode 100644 aspnet-core/tests/LINGYUN.Abp.DynamicQueryable.EntityFrameworkCore.Tests/Usings.cs create mode 100644 aspnet-core/tests/LINGYUN.Abp.EntityFrameworkCore.Tests/LINGYUN/Abp/EntityFrameworkCore/EfCoreTestDbContext.cs create mode 100644 aspnet-core/tests/LINGYUN.Abp.EntityFrameworkCore.Tests/LINGYUN/Abp/EntityFrameworkCore/EfCoreTestEntity.cs create mode 100644 aspnet-core/tests/LINGYUN.Abp.EntityFrameworkCore.Tests/LINGYUN/Abp/EntityFrameworkCore/EfCoreTestEntityDataSeeder.cs create mode 100644 aspnet-core/tests/LINGYUN.Linq.Dynamic.Queryable.Tests/LINGYUN.Linq.Dynamic.Queryable.Tests.csproj create mode 100644 aspnet-core/tests/LINGYUN.Linq.Dynamic.Queryable.Tests/LINGYUN/Linq/Dynamic/Queryable/DynamicQueryableTestBase.cs create mode 100644 aspnet-core/tests/LINGYUN.Linq.Dynamic.Queryable.Tests/LINGYUN/Linq/Dynamic/Queryable/DynamicQueryableTestModule.cs create mode 100644 aspnet-core/tests/LINGYUN.Linq.Dynamic.Queryable.Tests/LINGYUN/Linq/Dynamic/Queryable/DynamicQueryableTests.cs create mode 100644 aspnet-core/tests/LINGYUN.Linq.Dynamic.Queryable.Tests/Usings.cs diff --git a/apps/vue/src/components/Table/src/components/AdvancedSearch.vue b/apps/vue/src/components/Table/src/components/AdvancedSearch.vue index a94768937..ee8ab6938 100644 --- a/apps/vue/src/components/Table/src/components/AdvancedSearch.vue +++ b/apps/vue/src/components/Table/src/components/AdvancedSearch.vue @@ -209,6 +209,14 @@ label: t('component.table.advancedSearch.notContains'), value: DynamicComparison.NotContains, }, + { + label: t('component.table.advancedSearch.null'), + value: DynamicComparison.Null, + }, + { + label: t('component.table.advancedSearch.notNull'), + value: DynamicComparison.NotNull, + }, ]); const getAvailableParams = computed(() => { diff --git a/apps/vue/src/components/Table/src/types/advancedSearch.ts b/apps/vue/src/components/Table/src/types/advancedSearch.ts index 3a63abf5c..bd6a1b01d 100644 --- a/apps/vue/src/components/Table/src/types/advancedSearch.ts +++ b/apps/vue/src/components/Table/src/types/advancedSearch.ts @@ -65,7 +65,11 @@ export enum DynamicComparison { /** 包含 */ Contains = 10, /** 不包含 */ - NotContains = 11 + NotContains = 11, + /** 空 */ + Null = 12, + /** 非空 */ + NotNull = 12, } /** 动态查询字段 */ diff --git a/apps/vue/src/components/Table/src/types/table.ts b/apps/vue/src/components/Table/src/types/table.ts index 4baf8fc29..443f71f06 100644 --- a/apps/vue/src/components/Table/src/types/table.ts +++ b/apps/vue/src/components/Table/src/types/table.ts @@ -1,8 +1,7 @@ import type { VNodeChild } from 'vue'; import type { PaginationProps } from './pagination'; import type { FormProps } from '/@/components/Form'; -import type { ColumnProps } from 'ant-design-vue/lib/table'; -import type { TableRowSelection as ITableRowSelection } from 'ant-design-vue/lib/table/interface'; +import type { TableRowSelection as ITableRowSelection, ColumnProps } from 'ant-design-vue/lib/table/interface'; import type { AdvanceSearchProps } from './advancedSearch'; import { ComponentType } from './componentType'; diff --git a/apps/vue/src/locales/lang/en/component.ts b/apps/vue/src/locales/lang/en/component.ts index aaee986ab..bac3fd3e1 100644 --- a/apps/vue/src/locales/lang/en/component.ts +++ b/apps/vue/src/locales/lang/en/component.ts @@ -94,6 +94,8 @@ export default { notEndsWith: 'Not Ends With', contains: 'Contains', notContains: 'Not Contains', + null: 'Null', + notNull: 'Not Null', } }, time: { diff --git a/apps/vue/src/locales/lang/zh-CN/component.ts b/apps/vue/src/locales/lang/zh-CN/component.ts index 207d457a5..483aaf82c 100644 --- a/apps/vue/src/locales/lang/zh-CN/component.ts +++ b/apps/vue/src/locales/lang/zh-CN/component.ts @@ -96,6 +96,8 @@ export default { notEndsWith: '右不包含', contains: '包含', notContains: '不包含', + null: '空', + notNull: '非空', } }, time: { diff --git a/aspnet-core/LINGYUN.MicroService.Common.sln b/aspnet-core/LINGYUN.MicroService.Common.sln index b2d38253a..5e7eb96e1 100644 --- a/aspnet-core/LINGYUN.MicroService.Common.sln +++ b/aspnet-core/LINGYUN.MicroService.Common.sln @@ -286,13 +286,17 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.TuiJuhe.Setting EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "dynamic-queryable", "dynamic-queryable", "{3975D028-3672-4D23-BF77-B7F4A445D44E}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Linq.Dynamic.Queryable", "modules\dynamic-queryable\LINGYUN.Linq.Dynamic.Queryable\LINGYUN.Linq.Dynamic.Queryable.csproj", "{5512A359-80E8-440C-B652-7C96F614DD9E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Linq.Dynamic.Queryable", "modules\dynamic-queryable\LINGYUN.Linq.Dynamic.Queryable\LINGYUN.Linq.Dynamic.Queryable.csproj", "{5512A359-80E8-440C-B652-7C96F614DD9E}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.Dynamic.Queryable.Application.Contracts", "modules\dynamic-queryable\LINGYUN.Abp.Dynamic.Queryable.Application.Contracts\LINGYUN.Abp.Dynamic.Queryable.Application.Contracts.csproj", "{6A23EE81-0CA7-4CA7-859D-6ADB669DF0E1}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.Dynamic.Queryable.Application.Contracts", "modules\dynamic-queryable\LINGYUN.Abp.Dynamic.Queryable.Application.Contracts\LINGYUN.Abp.Dynamic.Queryable.Application.Contracts.csproj", "{6A23EE81-0CA7-4CA7-859D-6ADB669DF0E1}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.Dynamic.Queryable.Application", "modules\dynamic-queryable\LINGYUN.Abp.Dynamic.Queryable.Application\LINGYUN.Abp.Dynamic.Queryable.Application.csproj", "{EC7970DC-A656-46A7-9873-A730FE72B213}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.Dynamic.Queryable.Application", "modules\dynamic-queryable\LINGYUN.Abp.Dynamic.Queryable.Application\LINGYUN.Abp.Dynamic.Queryable.Application.csproj", "{EC7970DC-A656-46A7-9873-A730FE72B213}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.Dynamic.Queryable.HttpApi", "modules\dynamic-queryable\LINGYUN.Abp.Dynamic.Queryable.HttpApi\LINGYUN.Abp.Dynamic.Queryable.HttpApi.csproj", "{F515E8FA-449F-4D54-98A7-0F7DF1AA3C94}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.Dynamic.Queryable.HttpApi", "modules\dynamic-queryable\LINGYUN.Abp.Dynamic.Queryable.HttpApi\LINGYUN.Abp.Dynamic.Queryable.HttpApi.csproj", "{F515E8FA-449F-4D54-98A7-0F7DF1AA3C94}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Linq.Dynamic.Queryable.Tests", "tests\LINGYUN.Linq.Dynamic.Queryable.Tests\LINGYUN.Linq.Dynamic.Queryable.Tests.csproj", "{E9AD81CA-D992-4F74-BD23-680CF98BE262}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.DynamicQueryable.EntityFrameworkCore.Tests", "tests\LINGYUN.Abp.DynamicQueryable.EntityFrameworkCore.Tests\LINGYUN.Abp.DynamicQueryable.EntityFrameworkCore.Tests.csproj", "{2E29FBF7-CECB-4DF9-9E02-5AFB704DDD10}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -760,6 +764,14 @@ Global {F515E8FA-449F-4D54-98A7-0F7DF1AA3C94}.Debug|Any CPU.Build.0 = Debug|Any CPU {F515E8FA-449F-4D54-98A7-0F7DF1AA3C94}.Release|Any CPU.ActiveCfg = Release|Any CPU {F515E8FA-449F-4D54-98A7-0F7DF1AA3C94}.Release|Any CPU.Build.0 = Release|Any CPU + {E9AD81CA-D992-4F74-BD23-680CF98BE262}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E9AD81CA-D992-4F74-BD23-680CF98BE262}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E9AD81CA-D992-4F74-BD23-680CF98BE262}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E9AD81CA-D992-4F74-BD23-680CF98BE262}.Release|Any CPU.Build.0 = Release|Any CPU + {2E29FBF7-CECB-4DF9-9E02-5AFB704DDD10}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2E29FBF7-CECB-4DF9-9E02-5AFB704DDD10}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2E29FBF7-CECB-4DF9-9E02-5AFB704DDD10}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2E29FBF7-CECB-4DF9-9E02-5AFB704DDD10}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -906,6 +918,8 @@ Global {6A23EE81-0CA7-4CA7-859D-6ADB669DF0E1} = {3975D028-3672-4D23-BF77-B7F4A445D44E} {EC7970DC-A656-46A7-9873-A730FE72B213} = {3975D028-3672-4D23-BF77-B7F4A445D44E} {F515E8FA-449F-4D54-98A7-0F7DF1AA3C94} = {3975D028-3672-4D23-BF77-B7F4A445D44E} + {E9AD81CA-D992-4F74-BD23-680CF98BE262} = {B86C21A4-73B7-471E-B73A-B4B905EC9435} + {2E29FBF7-CECB-4DF9-9E02-5AFB704DDD10} = {B86C21A4-73B7-471E-B73A-B4B905EC9435} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {06C707C6-02C0-411A-AD3B-2D0E13787CB8} diff --git a/aspnet-core/modules/dynamic-queryable/LINGYUN.Abp.Dynamic.Queryable.Application.Contracts/LINGYUN/Abp/Dynamic/Queryable/Dto/DynamicParamterDto.cs b/aspnet-core/modules/dynamic-queryable/LINGYUN.Abp.Dynamic.Queryable.Application.Contracts/LINGYUN/Abp/Dynamic/Queryable/Dto/DynamicParamterDto.cs index 0f63b83c8..6e47b7edf 100644 --- a/aspnet-core/modules/dynamic-queryable/LINGYUN.Abp.Dynamic.Queryable.Application.Contracts/LINGYUN/Abp/Dynamic/Queryable/Dto/DynamicParamterDto.cs +++ b/aspnet-core/modules/dynamic-queryable/LINGYUN.Abp.Dynamic.Queryable.Application.Contracts/LINGYUN/Abp/Dynamic/Queryable/Dto/DynamicParamterDto.cs @@ -1,4 +1,6 @@ -namespace LINGYUN.Abp.Dynamic.Queryable; +using LINGYUN.Linq.Dynamic.Queryable; + +namespace LINGYUN.Abp.Dynamic.Queryable; public class DynamicParamterDto { @@ -6,4 +8,9 @@ public class DynamicParamterDto public string Description { get; set; } public string Type { get; set; } public string JavaScriptType { get; set; } + public DynamicComparison[] AvailableComparator { get; set; } + public DynamicParamterDto() + { + AvailableComparator = new DynamicComparison[0]; + } } diff --git a/aspnet-core/modules/dynamic-queryable/LINGYUN.Abp.Dynamic.Queryable.Application/LINGYUN/Abp/Dynamic/Queryable/DynamicQueryableAppService.cs b/aspnet-core/modules/dynamic-queryable/LINGYUN.Abp.Dynamic.Queryable.Application/LINGYUN/Abp/Dynamic/Queryable/DynamicQueryableAppService.cs index 263e52643..93a885c39 100644 --- a/aspnet-core/modules/dynamic-queryable/LINGYUN.Abp.Dynamic.Queryable.Application/LINGYUN/Abp/Dynamic/Queryable/DynamicQueryableAppService.cs +++ b/aspnet-core/modules/dynamic-queryable/LINGYUN.Abp.Dynamic.Queryable.Application/LINGYUN/Abp/Dynamic/Queryable/DynamicQueryableAppService.cs @@ -1,4 +1,5 @@ -using Microsoft.Extensions.Options; +using LINGYUN.Linq.Dynamic.Queryable; +using Microsoft.Extensions.Options; using System; using System.Collections.Generic; using System.Linq; @@ -32,13 +33,15 @@ public abstract class DynamicQueryableAppService : Applicat // 字段本地化描述规则 // 在本地化文件中定义 DisplayName:PropertyName var localizedProp = L[$"DisplayName:{propertyInfo.Name}"]; + var propertyTypeMap = GetPropertyTypeMap(propertyInfo.PropertyType); dynamicParamters.Add( new DynamicParamterDto { Name = propertyInfo.Name, Type = propertyInfo.PropertyType.FullName, Description = localizedProp.Value ?? propertyInfo.Name, - JavaScriptType = ConvertToJavaScriptType(propertyInfo.PropertyType) + JavaScriptType = propertyTypeMap.JavaScriptType, + AvailableComparator = propertyTypeMap.AvailableComparator }); } @@ -74,10 +77,13 @@ public abstract class DynamicQueryableAppService : Applicat return ObjectMapper.Map, List>(entities); } - protected virtual string ConvertToJavaScriptType(Type propertyType) + protected virtual (string JavaScriptType, DynamicComparison[] AvailableComparator) GetPropertyTypeMap(Type propertyType) { + var isNullableType = false; + var availableComparator = new List(); if (propertyType.IsNullableType()) { + isNullableType = true; propertyType = propertyType.GetGenericArguments().FirstOrDefault(); } var typeCode = Type.GetTypeCode(propertyType); @@ -94,27 +100,103 @@ public abstract class DynamicQueryableAppService : Applicat case TypeCode.Double: case TypeCode.SByte: case TypeCode.Decimal: - return "number"; + // 数值类型只支持如下操作符 + // 小于、小于等于、大于、大于等于、等于、不等于、空、非空 + availableComparator.AddRange(new[] + { + DynamicComparison.GreaterThan, + DynamicComparison.GreaterThanOrEqual, + DynamicComparison.LessThan, + DynamicComparison.LessThanOrEqual, + DynamicComparison.Equal, + DynamicComparison.NotEqual, + }); + if (isNullableType) + { + availableComparator.AddRange(new [] + { + DynamicComparison.Null, + DynamicComparison.NotNull + }); + } + return ("number", availableComparator.ToArray()); case TypeCode.Boolean: - return "boolean"; + // 布尔类型只支持如下操作符 + // 等于、不等于、空、非空 + availableComparator.AddRange(new[] + { + DynamicComparison.Equal, + DynamicComparison.NotEqual, + }); + if (isNullableType) + { + availableComparator.AddRange(new[] + { + DynamicComparison.Null, + DynamicComparison.NotNull + }); + } + return ("boolean", availableComparator.ToArray()); case TypeCode.Char: case TypeCode.String: - return "string"; + // 字符类型支持所有操作符 + return ("string", availableComparator.ToArray()); case TypeCode.DateTime: - return "Date"; + // 时间类型只支持如下操作符 + // 小于、小于等于、大于、大于等于、等于、不等于、空、非空 + availableComparator.AddRange(new[] + { + DynamicComparison.GreaterThan, + DynamicComparison.GreaterThanOrEqual, + DynamicComparison.LessThan, + DynamicComparison.LessThanOrEqual, + DynamicComparison.Equal, + DynamicComparison.NotEqual, + }); + if (isNullableType) + { + availableComparator.AddRange(new[] + { + DynamicComparison.Null, + DynamicComparison.NotNull + }); + } + return ("Date", availableComparator.ToArray()); + default: case TypeCode.Object: + case TypeCode.Empty: + case TypeCode.DBNull: + if (isNullableType) + { + availableComparator.AddRange(new[] + { + DynamicComparison.Null, + DynamicComparison.NotNull + }); + } if (propertyType.IsArray) { - return "array"; + // 数组类型只支持如下操作符 + // 包含、不包含、空、非空 + availableComparator.AddRange(new[] + { + DynamicComparison.Contains, + DynamicComparison.NotContains, + }); + + return ("array", availableComparator.ToArray()); } else { - return "object"; + // 未知对象类型只支持如下操作符 + // 等于、不等于、空、非空 + availableComparator.AddRange(new[] + { + DynamicComparison.Equal, + DynamicComparison.NotEqual, + }); + return ("object", availableComparator.ToArray()); } - default: - case TypeCode.Empty: - case TypeCode.DBNull: - return "object"; } } } diff --git a/aspnet-core/modules/dynamic-queryable/LINGYUN.Linq.Dynamic.Queryable/LINGYUN.Linq.Dynamic.Queryable.csproj b/aspnet-core/modules/dynamic-queryable/LINGYUN.Linq.Dynamic.Queryable/LINGYUN.Linq.Dynamic.Queryable.csproj index 7128b5214..6a04a7f20 100644 --- a/aspnet-core/modules/dynamic-queryable/LINGYUN.Linq.Dynamic.Queryable/LINGYUN.Linq.Dynamic.Queryable.csproj +++ b/aspnet-core/modules/dynamic-queryable/LINGYUN.Linq.Dynamic.Queryable/LINGYUN.Linq.Dynamic.Queryable.csproj @@ -8,4 +8,9 @@ + + + + + diff --git a/aspnet-core/modules/dynamic-queryable/LINGYUN.Linq.Dynamic.Queryable/LINGYUN/Linq/Dynamic/Queryable/DynamicComparison.cs b/aspnet-core/modules/dynamic-queryable/LINGYUN.Linq.Dynamic.Queryable/LINGYUN/Linq/Dynamic/Queryable/DynamicComparison.cs index 84f343b42..99e7f80f6 100644 --- a/aspnet-core/modules/dynamic-queryable/LINGYUN.Linq.Dynamic.Queryable/LINGYUN/Linq/Dynamic/Queryable/DynamicComparison.cs +++ b/aspnet-core/modules/dynamic-queryable/LINGYUN.Linq.Dynamic.Queryable/LINGYUN/Linq/Dynamic/Queryable/DynamicComparison.cs @@ -48,6 +48,14 @@ public enum DynamicComparison /// /// 不包含 /// - NotContains = 11 + NotContains = 11, + /// + /// 空值 + /// + Null = 12, + /// + /// 非空 + /// + NotNull = 13 } diff --git a/aspnet-core/modules/dynamic-queryable/LINGYUN.Linq.Dynamic.Queryable/LINGYUN/Linq/Dynamic/Queryable/DynamicParamter.cs b/aspnet-core/modules/dynamic-queryable/LINGYUN.Linq.Dynamic.Queryable/LINGYUN/Linq/Dynamic/Queryable/DynamicParamter.cs index 1ac103d17..14d4c6454 100644 --- a/aspnet-core/modules/dynamic-queryable/LINGYUN.Linq.Dynamic.Queryable/LINGYUN/Linq/Dynamic/Queryable/DynamicParamter.cs +++ b/aspnet-core/modules/dynamic-queryable/LINGYUN.Linq.Dynamic.Queryable/LINGYUN/Linq/Dynamic/Queryable/DynamicParamter.cs @@ -1,7 +1,12 @@ -namespace LINGYUN.Linq.Dynamic.Queryable; +using JetBrains.Annotations; +using System.ComponentModel.DataAnnotations; + +namespace LINGYUN.Linq.Dynamic.Queryable; public class DynamicParamter { + [NotNull] + [Required] public string Field { get; set; } public DynamicLogic Logic { get; set; } = DynamicLogic.And; @@ -9,5 +14,6 @@ public class DynamicParamter public DynamicComparison Comparison { get; set; } = DynamicComparison.Equal; public object Value { get; set; } + public string Type { get; set; } } diff --git a/aspnet-core/modules/dynamic-queryable/LINGYUN.Linq.Dynamic.Queryable/System/Linq/Expressions/ObjectQueryableExtensions.cs b/aspnet-core/modules/dynamic-queryable/LINGYUN.Linq.Dynamic.Queryable/System/Linq/Expressions/ObjectQueryableExtensions.cs index d23ae7a0e..9334c1123 100644 --- a/aspnet-core/modules/dynamic-queryable/LINGYUN.Linq.Dynamic.Queryable/System/Linq/Expressions/ObjectQueryableExtensions.cs +++ b/aspnet-core/modules/dynamic-queryable/LINGYUN.Linq.Dynamic.Queryable/System/Linq/Expressions/ObjectQueryableExtensions.cs @@ -34,65 +34,213 @@ public static class ObjectQueryableExtensions { propertyType = (leftParamter.Member as PropertyInfo)?.PropertyType ?? paramter.Value.GetType(); } - var rightParamter = Expression.Convert(Expression.Constant(paramter.Value), propertyType); switch (paramter.Comparison) { - case DynamicComparison.Equal: - exp = Expression.Equal(leftParamter, rightParamter); - break; case DynamicComparison.NotEqual: - exp = Expression.NotEqual(leftParamter, rightParamter); + // For example(MySql): + // ...Other (Field <> Value) + exp = Expression.NotEqual( + leftParamter, + Expression.Convert(Expression.Constant(paramter.Value), propertyType)); break; case DynamicComparison.LessThan: - exp = Expression.LessThan(leftParamter, rightParamter); + // For example(MySql): + // ...Other (Field < Value) + exp = Expression.LessThan( + leftParamter, + Expression.Convert(Expression.Constant(paramter.Value), propertyType)); break; case DynamicComparison.LessThanOrEqual: - exp = Expression.LessThanOrEqual(leftParamter, rightParamter); + // For example(MySql): + // ...Other (Field <= Value) + + exp = Expression.LessThanOrEqual( + leftParamter, + Expression.Convert(Expression.Constant(paramter.Value), propertyType)); break; case DynamicComparison.GreaterThan: - exp = Expression.GreaterThan(leftParamter, rightParamter); + // For example(MySql): + // ...Other (Field > Value) + exp = Expression.GreaterThan( + leftParamter, + Expression.Convert(Expression.Constant(paramter.Value), propertyType)); break; case DynamicComparison.GreaterThanOrEqual: - exp = Expression.GreaterThanOrEqual(leftParamter, rightParamter); + // For example(MySql): + // ...Other (Field >= Value) + + exp = Expression.GreaterThanOrEqual( + leftParamter, + Expression.Convert(Expression.Constant(paramter.Value), propertyType)); break; case DynamicComparison.StartsWith: + // For example(MySql): + // ...Other And Field LIKE 'Value%' exp = Expression.Call( leftParamter, typeof(string).GetMethod(nameof(String.StartsWith), new[] { typeof(string) }), - rightParamter); + Expression.Convert(Expression.Constant(paramter.Value), propertyType)); + + // TODO: 单元测试通过 + // For example(MySql): + // ...Other ((Field IS NOT NULL) AND (Field LIKE 'Value%')) + //var startsWithNotNullExp = Expression.Not( + // Expression.Equal(leftParamter, + // Expression.Convert( + // Expression.Constant(null), propertyType))); + + //var startsWithExp = Expression.Call( + // leftParamter, + // typeof(string).GetMethod(nameof(String.StartsWith), new[] { typeof(string) }), + // rightParamter); + + //exp = Expression.AndAlso(startsWithNotNullExp, startsWithExp); + break; case DynamicComparison.NotStartsWith: + // For example(MySql): + // ...Other NOT Field LIKE 'Value%' + exp = Expression.Not( Expression.Call( leftParamter, typeof(string).GetMethod(nameof(String.StartsWith), new[] { typeof(string) }), - rightParamter)); + Expression.Convert(Expression.Constant(paramter.Value), propertyType))); + + // TODO: 单元测试通过 + // For example(MySql): + // ...Other ((Field IS NULL) OR NOT (Field LIKE 'Value%')) + //var notStartsWithNullExp = Expression.Equal(leftParamter, + // Expression.Convert( + // Expression.Constant(null), propertyType)); + + //var notStartsWithExp = Expression.Not( + // Expression.Call( + // leftParamter, + // typeof(string).GetMethod(nameof(String.StartsWith), new[] { typeof(string) }), + // rightParamter)); + + //exp = Expression.OrElse(notStartsWithNullExp, notStartsWithExp); break; case DynamicComparison.EndsWith: + // For example(MySql): + // ...Other AND Field LIKE '%Value' exp = Expression.Call( leftParamter, typeof(string).GetMethod(nameof(String.EndsWith), new[] { typeof(string) }), - rightParamter); + Expression.Convert(Expression.Constant(paramter.Value), propertyType)); + + // TODO: 单元测试通过 + // For example(MySql): + // ...Other ((Field IS NOT NULL) AND (Field LIKE '%Value')) + //var endsWithNotNullExp = Expression.Not( + // Expression.Equal(leftParamter, + // Expression.Convert( + // Expression.Constant(null), propertyType))); + + //var endsWithExp = Expression.Call( + // leftParamter, + // typeof(string).GetMethod(nameof(String.EndsWith), new[] { typeof(string) }), + // rightParamter); + + //exp = Expression.AndAlso(endsWithNotNullExp, endsWithExp); break; case DynamicComparison.NotEndsWith: + // For example(MySql): + // ...Other NOT (Field LIKE '%Value') exp = Expression.Not( Expression.Call( leftParamter, typeof(string).GetMethod(nameof(String.EndsWith), new[] { typeof(string) }), - rightParamter)); + Expression.Convert(Expression.Constant(paramter.Value), propertyType))); + + // TODO: 单元测试通过 + // For example(MySql): + // ...Other ((Field IS NULL) OR NOT (Field LIKE '%Value')) + //var notEndsWithNullExp = Expression.Equal(leftParamter, + // Expression.Convert( + // Expression.Constant(null), propertyType)); + + //var notEndsWithExp = Expression.Not( + // Expression.Call( + // leftParamter, + // typeof(string).GetMethod(nameof(String.EndsWith), new[] { typeof(string) }), + // rightParamter)); + + //exp = Expression.OrElse(notEndsWithNullExp, notEndsWithExp); break; case DynamicComparison.Contains: + // For example(MySql): + // ...Other AND (Field LIKE '%Value%') exp = Expression.Call( leftParamter, typeof(string).GetMethod(nameof(String.Contains), new[] { typeof(string) }), - rightParamter); + Expression.Convert(Expression.Constant(paramter.Value), propertyType)); + + // TODO: 单元测试通过 + // For example(MySql): + // ...Other ((Field IS NOT NULL) AND (Field LIKE '%Value%')) + //var containsNotNullExp = Expression.Not( + // Expression.Equal(leftParamter, + // Expression.Convert( + // Expression.Constant(null), propertyType))); + + //var containsExp = Expression.Call( + // leftParamter, + // typeof(string).GetMethod(nameof(String.Contains), new[] { typeof(string) }), + // rightParamter); + + //exp = Expression.AndAlso(containsNotNullExp, containsExp); break; case DynamicComparison.NotContains: + // For example(MySql): + // ...Other AND (Field NOT LIKE '%Value%') exp = Expression.Not( - Expression.Call( - leftParamter, - typeof(string).GetMethod(nameof(String.Contains), new[] { typeof(string) }), - rightParamter)); + Expression.Call( + leftParamter, + typeof(string).GetMethod(nameof(String.Contains), new[] { typeof(string) }), + Expression.Convert(Expression.Constant(paramter.Value), propertyType))); + // TODO: 单元测试通过 + // For example(MySql): + // ...Other ((Field IS NULL) OR (Field NOT LIKE '%Value%')) + + //var notContainsNullExp = Expression.Equal(leftParamter, + // Expression.Convert( + // Expression.Constant(null), propertyType)); + + //var notContainsExp = Expression.Not( + // Expression.Call( + // leftParamter, + // typeof(string).GetMethod(nameof(String.Contains), new[] { typeof(string) }), + // rightParamter)); + + //exp = Expression.OrElse(notContainsNullExp, notContainsExp); + break; + case DynamicComparison.Null: + // For example(MySql): + // ...Other (Field IS NULL) + + // 非空字段设定为比对默认值 + exp = Expression.Equal(leftParamter, + Expression.Convert( + Expression.Constant(GetDefaultValue(propertyType)), propertyType)); + break; + case DynamicComparison.NotNull: + // For example(MySql): + // ...Other (Field IS NOT NULL) + + exp = Expression.NotEqual(leftParamter, + Expression.Convert( + Expression.Constant(GetDefaultValue(propertyType)), propertyType)); + break; + default: + case DynamicComparison.Equal: + // For example(MySql): + // ...Other (Field = Value) + + exp = Expression.Equal( + leftParamter, + Expression.Convert(Expression.Constant(paramter.Value), propertyType)); break; } expressions.Push(exp); @@ -104,16 +252,27 @@ public static class ObjectQueryableExtensions switch (paramter.Logic) { - case DynamicLogic.And: - expressions.Push(Expression.AndAlso(exp1, exp2)); - break; case DynamicLogic.Or: expressions.Push(Expression.Or(exp1, exp2)); break; + default: + case DynamicLogic.And: + expressions.Push(Expression.AndAlso(exp1, exp2)); + break; } } } return Expression.Lambda(expressions.Pop(), condition.Parameters.ToArray()); } + + private static object GetDefaultValue(Type type) + { + // TODO: 非空字段此处返回默认值 + if (type.IsNullableType()) + { + return null; + } + return type.IsValueType ? Activator.CreateInstance(type) : null; + } } diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/LINGYUN/Abp/TaskManagement/BackgroundJobInfoAppService.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/LINGYUN/Abp/TaskManagement/BackgroundJobInfoAppService.cs index bc27d1714..ed377a1fe 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/LINGYUN/Abp/TaskManagement/BackgroundJobInfoAppService.cs +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/LINGYUN/Abp/TaskManagement/BackgroundJobInfoAppService.cs @@ -374,9 +374,13 @@ public class BackgroundJobInfoAppService : DynamicQueryableAppService + + + net6.0 + + false + + + + + + + + + + + + + diff --git a/aspnet-core/tests/LINGYUN.Abp.DynamicQueryable.EntityFrameworkCore.Tests/LINGYUN/Abp/DynamicQueryable/EntityFrameworkCore/AbpDynamicQueryableEntityFrameworkCoreTestBase.cs b/aspnet-core/tests/LINGYUN.Abp.DynamicQueryable.EntityFrameworkCore.Tests/LINGYUN/Abp/DynamicQueryable/EntityFrameworkCore/AbpDynamicQueryableEntityFrameworkCoreTestBase.cs new file mode 100644 index 000000000..c68022c75 --- /dev/null +++ b/aspnet-core/tests/LINGYUN.Abp.DynamicQueryable.EntityFrameworkCore.Tests/LINGYUN/Abp/DynamicQueryable/EntityFrameworkCore/AbpDynamicQueryableEntityFrameworkCoreTestBase.cs @@ -0,0 +1,4 @@ +namespace LINGYUN.Abp.DynamicQueryable.EntityFrameworkCore; +public abstract class AbpDynamicQueryableEntityFrameworkCoreTestBase : AbpTestsBase +{ +} diff --git a/aspnet-core/tests/LINGYUN.Abp.DynamicQueryable.EntityFrameworkCore.Tests/LINGYUN/Abp/DynamicQueryable/EntityFrameworkCore/AbpDynamicQueryableEntityFrameworkCoreTestModule.cs b/aspnet-core/tests/LINGYUN.Abp.DynamicQueryable.EntityFrameworkCore.Tests/LINGYUN/Abp/DynamicQueryable/EntityFrameworkCore/AbpDynamicQueryableEntityFrameworkCoreTestModule.cs new file mode 100644 index 000000000..7e9fa10a5 --- /dev/null +++ b/aspnet-core/tests/LINGYUN.Abp.DynamicQueryable.EntityFrameworkCore.Tests/LINGYUN/Abp/DynamicQueryable/EntityFrameworkCore/AbpDynamicQueryableEntityFrameworkCoreTestModule.cs @@ -0,0 +1,9 @@ +using Volo.Abp.Modularity; + +namespace LINGYUN.Abp.DynamicQueryable.EntityFrameworkCore; + +[DependsOn(typeof(AbpEntityFrameworkCoreTestModule))] +public class AbpDynamicQueryableEntityFrameworkCoreTestModule : AbpModule +{ + +} \ No newline at end of file diff --git a/aspnet-core/tests/LINGYUN.Abp.DynamicQueryable.EntityFrameworkCore.Tests/LINGYUN/Abp/DynamicQueryable/EntityFrameworkCore/DynamicQueryableEntityFrameworkCoreTests.cs b/aspnet-core/tests/LINGYUN.Abp.DynamicQueryable.EntityFrameworkCore.Tests/LINGYUN/Abp/DynamicQueryable/EntityFrameworkCore/DynamicQueryableEntityFrameworkCoreTests.cs new file mode 100644 index 000000000..f0a53cb7a --- /dev/null +++ b/aspnet-core/tests/LINGYUN.Abp.DynamicQueryable.EntityFrameworkCore.Tests/LINGYUN/Abp/DynamicQueryable/EntityFrameworkCore/DynamicQueryableEntityFrameworkCoreTests.cs @@ -0,0 +1,302 @@ +using LINGYUN.Abp.EntityFrameworkCore; +using System; +using System.Linq; +using System.Linq.Expressions; +using ExecDynamicQueryable = LINGYUN.Linq.Dynamic.Queryable.DynamicQueryable; + +namespace LINGYUN.Abp.DynamicQueryable.EntityFrameworkCore; + +public class DynamicQueryableEntityFrameworkCoreTests : AbpDynamicQueryableEntityFrameworkCoreTestBase +{ + [Fact] + public void Should_Null() + { + using var dbContext = GetRequiredService(); + Expression> exp = (_) => true; + + var dynamicQueryable = new ExecDynamicQueryable(); + dynamicQueryable.Paramters.Add(new DynamicParamter + { + Comparison = DynamicComparison.Null, + Field = nameof(EfCoreTestEntity.PropString), + Logic = DynamicLogic.And + }); + + exp = exp.DynamicQuery(dynamicQueryable); + + var result = dbContext.TestEntities.Local.Where(exp.Compile()).ToList(); + result.Count.ShouldBe(2); + } + + [Fact] + public void Should_Not_Null() + { + using var dbContext = GetRequiredService(); + Expression> exp = (_) => true; + + var dynamicQueryable = new ExecDynamicQueryable(); + dynamicQueryable.Paramters.Add(new DynamicParamter + { + Comparison = DynamicComparison.NotNull, + Field = nameof(EfCoreTestEntity.PropString), + Logic = DynamicLogic.And + }); + + exp = exp.DynamicQuery(dynamicQueryable); + + var result = dbContext.TestEntities.Local.Where(exp.Compile()).ToList(); + result.Count.ShouldBe(2); + } + + [Fact] + public void Should_Equal() + { + using var dbContext = GetRequiredService(); + Expression> exp = (_) => true; + + var dynamicQueryable = new ExecDynamicQueryable(); + dynamicQueryable.Paramters.Add(new DynamicParamter + { + Comparison = DynamicComparison.Equal, + Field = nameof(EfCoreTestEntity.PropInt32), + Logic = DynamicLogic.And, + Value = 2048 + }); + + exp = exp.DynamicQuery(dynamicQueryable); + + var result = dbContext.TestEntities.Local.Where(exp.Compile()).ToList(); + result.Count.ShouldBe(1); + } + + [Fact] + public void Should_Not_Equal() + { + using var dbContext = GetRequiredService(); + Expression> exp = (_) => true; + + var dynamicQueryable = new ExecDynamicQueryable(); + dynamicQueryable.Paramters.Add(new DynamicParamter + { + Comparison = DynamicComparison.NotEqual, + Field = nameof(EfCoreTestEntity.PropInt32), + Logic = DynamicLogic.And, + Value = null + }); + + exp = exp.DynamicQuery(dynamicQueryable); + + var result = dbContext.TestEntities.Local.Where(exp.Compile()).ToList(); + result.Count.ShouldBe(2); + } + + [Fact] + public void Should_Less_Than() + { + using var dbContext = GetRequiredService(); + Expression> exp = (_) => true; + + var dynamicQueryable = new ExecDynamicQueryable(); + dynamicQueryable.Paramters.Add(new DynamicParamter + { + Comparison = DynamicComparison.LessThan, + Field = nameof(EfCoreTestEntity.PropInt32), + Logic = DynamicLogic.And, + Value = 2048 + }); + + exp = exp.DynamicQuery(dynamicQueryable); + + var result = dbContext.TestEntities.Local.Where(exp.Compile()).ToList(); + result.Count.ShouldBe(1); + } + + [Fact] + public void Should_Less_Than_Or_Equal() + { + using var dbContext = GetRequiredService(); + Expression> exp = (_) => true; + + var dynamicQueryable = new ExecDynamicQueryable(); + dynamicQueryable.Paramters.Add(new DynamicParamter + { + Comparison = DynamicComparison.LessThanOrEqual, + Field = nameof(EfCoreTestEntity.PropInt32), + Logic = DynamicLogic.And, + Value = 2048 + }); + + exp = exp.DynamicQuery(dynamicQueryable); + + var result = dbContext.TestEntities.Local.Where(exp.Compile()).ToList(); + result.Count.ShouldBe(2); + } + + [Fact] + public void Should_Greater_Than() + { + using var dbContext = GetRequiredService(); + Expression> exp = (_) => true; + + var dynamicQueryable = new ExecDynamicQueryable(); + dynamicQueryable.Paramters.Add(new DynamicParamter + { + Comparison = DynamicComparison.GreaterThan, + Field = nameof(EfCoreTestEntity.PropInt64), + Logic = DynamicLogic.And, + Value = 1024L + }); + + exp = exp.DynamicQuery(dynamicQueryable); + + var result = dbContext.TestEntities.Local.Where(exp.Compile()).ToList(); + result.Count.ShouldBe(2); + } + + [Fact] + public void Should_Greater_Than_Or_Equal() + { + using var dbContext = GetRequiredService(); + Expression> exp = (_) => true; + + var dynamicQueryable = new ExecDynamicQueryable(); + dynamicQueryable.Paramters.Add(new DynamicParamter + { + Comparison = DynamicComparison.GreaterThanOrEqual, + Field = nameof(EfCoreTestEntity.PropInt64), + Logic = DynamicLogic.And, + Value = 1024L + }); + + exp = exp.DynamicQuery(dynamicQueryable); + + var result = dbContext.TestEntities.Local.Where(exp.Compile()).ToList(); + result.Count.ShouldBe(3); + } + + [Fact] + public void Should_Starts_With() + { + using var dbContext = GetRequiredService(); + Expression> exp = (_) => true; + + var dynamicQueryable = new ExecDynamicQueryable(); + dynamicQueryable.Paramters.Add(new DynamicParamter + { + Comparison = DynamicComparison.StartsWith, + Field = nameof(EfCoreTestEntity.PropString), + Logic = DynamicLogic.And, + Value = "1" + }); + + exp = exp.DynamicQuery(dynamicQueryable); + + var result = dbContext.TestEntities.Local.Where(exp.Compile()).ToList(); + result.Count.ShouldBe(1); + } + + [Fact] + public void Should_Not_Starts_With() + { + using var dbContext = GetRequiredService(); + Expression> exp = (_) => true; + + var dynamicQueryable = new ExecDynamicQueryable(); + dynamicQueryable.Paramters.Add(new DynamicParamter + { + Comparison = DynamicComparison.NotStartsWith, + Field = nameof(EfCoreTestEntity.PropString), + Logic = DynamicLogic.And, + Value = "1" + }); + + exp = exp.DynamicQuery(dynamicQueryable); + + var result = dbContext.TestEntities.Local.Where(exp.Compile()).ToList(); + result.Count.ShouldBe(3); + } + + [Fact] + public void Should_Ends_With() + { + using var dbContext = GetRequiredService(); + Expression> exp = (_) => true; + + var dynamicQueryable = new ExecDynamicQueryable(); + dynamicQueryable.Paramters.Add(new DynamicParamter + { + Comparison = DynamicComparison.EndsWith, + Field = nameof(EfCoreTestEntity.PropString), + Logic = DynamicLogic.And, + Value = "1" + }); + + exp = exp.DynamicQuery(dynamicQueryable); + + var result = dbContext.TestEntities.Local.Where(exp.Compile()).ToList(); + result.Count.ShouldBe(1); + } + + [Fact] + public void Should_Not_Ends_With() + { + using var dbContext = GetRequiredService(); + Expression> exp = (_) => true; + + var dynamicQueryable = new ExecDynamicQueryable(); + dynamicQueryable.Paramters.Add(new DynamicParamter + { + Comparison = DynamicComparison.NotEndsWith, + Field = nameof(EfCoreTestEntity.PropString), + Logic = DynamicLogic.And, + Value = "1" + }); + + exp = exp.DynamicQuery(dynamicQueryable); + + var result = dbContext.TestEntities.Local.Where(exp.Compile()).ToList(); + result.Count.ShouldBe(3); + } + + [Fact] + public void Should_Contains() + { + using var dbContext = GetRequiredService(); + Expression> exp = (_) => true; + + var dynamicQueryable = new ExecDynamicQueryable(); + dynamicQueryable.Paramters.Add(new DynamicParamter + { + Comparison = DynamicComparison.Contains, + Field = nameof(EfCoreTestEntity.PropString), + Logic = DynamicLogic.And, + Value = "22" + }); + + exp = exp.DynamicQuery(dynamicQueryable); + + var result = dbContext.TestEntities.Local.Where(exp.Compile()).ToList(); + result.Count.ShouldBe(2); + } + + [Fact] + public void Should_Not_Contains() + { + using var dbContext = GetRequiredService(); + Expression> exp = (_) => true; + + var dynamicQueryable = new ExecDynamicQueryable(); + dynamicQueryable.Paramters.Add(new DynamicParamter + { + Comparison = DynamicComparison.NotContains, + Field = nameof(EfCoreTestEntity.PropString), + Logic = DynamicLogic.And, + Value = "23" + }); + + exp = exp.DynamicQuery(dynamicQueryable); + + var result = dbContext.TestEntities.Local.Where(exp.Compile()).ToList(); + result.Count.ShouldBe(3); + } +} diff --git a/aspnet-core/tests/LINGYUN.Abp.DynamicQueryable.EntityFrameworkCore.Tests/Usings.cs b/aspnet-core/tests/LINGYUN.Abp.DynamicQueryable.EntityFrameworkCore.Tests/Usings.cs new file mode 100644 index 000000000..c649add73 --- /dev/null +++ b/aspnet-core/tests/LINGYUN.Abp.DynamicQueryable.EntityFrameworkCore.Tests/Usings.cs @@ -0,0 +1,5 @@ +global using Xunit; +global using Shouldly; +global using LINGYUN.Abp.EntityFrameworkCore.Tests; +global using LINGYUN.Abp.Tests; +global using LINGYUN.Linq.Dynamic.Queryable; diff --git a/aspnet-core/tests/LINGYUN.Abp.EntityFrameworkCore.Tests/LINGYUN.Abp.EntityFrameworkCore.Tests.csproj b/aspnet-core/tests/LINGYUN.Abp.EntityFrameworkCore.Tests/LINGYUN.Abp.EntityFrameworkCore.Tests.csproj index a4a511315..d7bd553f1 100644 --- a/aspnet-core/tests/LINGYUN.Abp.EntityFrameworkCore.Tests/LINGYUN.Abp.EntityFrameworkCore.Tests.csproj +++ b/aspnet-core/tests/LINGYUN.Abp.EntityFrameworkCore.Tests/LINGYUN.Abp.EntityFrameworkCore.Tests.csproj @@ -1,4 +1,4 @@ - + net6.0 diff --git a/aspnet-core/tests/LINGYUN.Abp.EntityFrameworkCore.Tests/LINGYUN/Abp/EntityFrameworkCore/AbpEntityFrameworkCoreTestModule.cs b/aspnet-core/tests/LINGYUN.Abp.EntityFrameworkCore.Tests/LINGYUN/Abp/EntityFrameworkCore/AbpEntityFrameworkCoreTestModule.cs index cf6b484e4..418c73dc2 100644 --- a/aspnet-core/tests/LINGYUN.Abp.EntityFrameworkCore.Tests/LINGYUN/Abp/EntityFrameworkCore/AbpEntityFrameworkCoreTestModule.cs +++ b/aspnet-core/tests/LINGYUN.Abp.EntityFrameworkCore.Tests/LINGYUN/Abp/EntityFrameworkCore/AbpEntityFrameworkCoreTestModule.cs @@ -4,6 +4,7 @@ using Microsoft.Extensions.DependencyInjection; using System; using Volo.Abp.EntityFrameworkCore; using Volo.Abp.Modularity; +using Volo.Abp.Threading; using Volo.Abp.Uow; namespace LINGYUN.Abp.EntityFrameworkCore.Tests @@ -13,9 +14,22 @@ namespace LINGYUN.Abp.EntityFrameworkCore.Tests )] public class AbpEntityFrameworkCoreTestModule : AbpModule { + //private string _testDbFile = "./abp-ef-test-db.db"; public override void ConfigureServices(ServiceConfigurationContext context) { context.Services.AddEntityFrameworkInMemoryDatabase(); + //var connectionString = $"Data Source={_testDbFile}"; + + //var sqliteConnection = CreateDatabaseAndGetConnection(connectionString); + + var memoryDbName = Guid.NewGuid().ToString(); + + var dbConetxt = CreateDatabaseAndGetDbContext(memoryDbName); + + AsyncHelper.RunSync(async () => + await new EfCoreTestEntityDataSeeder(dbConetxt).SeedAsync()); + + context.Services.AddSingleton(dbConetxt); var databaseName = Guid.NewGuid().ToString(); @@ -27,7 +41,7 @@ namespace LINGYUN.Abp.EntityFrameworkCore.Tests abpDbContextConfigurationContext.DbContextOptions.EnableSensitiveDataLogging(); abpDbContextConfigurationContext.DbContextOptions.UseEFCoreLogger(); - abpDbContextConfigurationContext.DbContextOptions.UseInMemoryDatabase(databaseName); + abpDbContextConfigurationContext.DbContextOptions.UseInMemoryDatabase(memoryDbName); }); }); @@ -36,5 +50,12 @@ namespace LINGYUN.Abp.EntityFrameworkCore.Tests options.TransactionBehavior = UnitOfWorkTransactionBehavior.Disabled; //EF in-memory database does not support transactions }); } + + private EfCoreTestDbContext CreateDatabaseAndGetDbContext(string dbName) + { + return new EfCoreTestDbContext( + new DbContextOptionsBuilder().UseInMemoryDatabase(dbName).Options + ); + } } } diff --git a/aspnet-core/tests/LINGYUN.Abp.EntityFrameworkCore.Tests/LINGYUN/Abp/EntityFrameworkCore/EfCoreTestDbContext.cs b/aspnet-core/tests/LINGYUN.Abp.EntityFrameworkCore.Tests/LINGYUN/Abp/EntityFrameworkCore/EfCoreTestDbContext.cs new file mode 100644 index 000000000..023d428f8 --- /dev/null +++ b/aspnet-core/tests/LINGYUN.Abp.EntityFrameworkCore.Tests/LINGYUN/Abp/EntityFrameworkCore/EfCoreTestDbContext.cs @@ -0,0 +1,24 @@ +using Microsoft.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore.Modeling; + +namespace LINGYUN.Abp.EntityFrameworkCore; +public class EfCoreTestDbContext : AbpDbContext +{ + public virtual DbSet TestEntities { get; set; } + + public EfCoreTestDbContext( + DbContextOptions options) : base(options) + { + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + + modelBuilder.Entity(b => + { + b.ConfigureByConvention(); + }); + } +} diff --git a/aspnet-core/tests/LINGYUN.Abp.EntityFrameworkCore.Tests/LINGYUN/Abp/EntityFrameworkCore/EfCoreTestEntity.cs b/aspnet-core/tests/LINGYUN.Abp.EntityFrameworkCore.Tests/LINGYUN/Abp/EntityFrameworkCore/EfCoreTestEntity.cs new file mode 100644 index 000000000..85d086077 --- /dev/null +++ b/aspnet-core/tests/LINGYUN.Abp.EntityFrameworkCore.Tests/LINGYUN/Abp/EntityFrameworkCore/EfCoreTestEntity.cs @@ -0,0 +1,25 @@ +using System; +using Volo.Abp.Domain.Entities; + +namespace LINGYUN.Abp.EntityFrameworkCore; + +public class EfCoreTestEntity : Entity +{ + public virtual string PropString { get; set; } + public virtual int? PropInt32 { get; set; } + public virtual long? PropInt64 { get; set; } + public virtual DateTime? DateTime { get; set; } + public EfCoreTestEntity( + Guid id, + string propString = null, + int? propInt32 = null, + long? propInt64 = null, + DateTime? dateTime = null) + : base(id) + { + PropString = propString; + PropInt32 = propInt32; + PropInt64 = propInt64; + DateTime = dateTime; + } +} diff --git a/aspnet-core/tests/LINGYUN.Abp.EntityFrameworkCore.Tests/LINGYUN/Abp/EntityFrameworkCore/EfCoreTestEntityDataSeeder.cs b/aspnet-core/tests/LINGYUN.Abp.EntityFrameworkCore.Tests/LINGYUN/Abp/EntityFrameworkCore/EfCoreTestEntityDataSeeder.cs new file mode 100644 index 000000000..879a59c3e --- /dev/null +++ b/aspnet-core/tests/LINGYUN.Abp.EntityFrameworkCore.Tests/LINGYUN/Abp/EntityFrameworkCore/EfCoreTestEntityDataSeeder.cs @@ -0,0 +1,33 @@ +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage; +using System; +using System.Threading.Tasks; + +namespace LINGYUN.Abp.EntityFrameworkCore; +public class EfCoreTestEntityDataSeeder +{ + private readonly EfCoreTestDbContext _dbContext; + + public EfCoreTestEntityDataSeeder( + EfCoreTestDbContext dbContext) + { + _dbContext = dbContext; + } + + public async virtual Task SeedAsync() + { + //_dbContext.GetService().CreateTables(); + + await _dbContext.TestEntities.AddAsync( + new EfCoreTestEntity(Guid.NewGuid(), "1223", 1024, 1024L, new DateTime(2021, 10, 1, 0, 0, 0))); + + await _dbContext.TestEntities.AddAsync( + new EfCoreTestEntity(Guid.NewGuid(), null, 2048, 2048L, new DateTime(2022, 10, 1, 12, 0, 0))); + + await _dbContext.TestEntities.AddAsync( + new EfCoreTestEntity(Guid.NewGuid(), "3221", null, 4096L, null)); + + await _dbContext.TestEntities.AddAsync( + new EfCoreTestEntity(Guid.NewGuid(), null, null, null, new DateTime(2022, 1, 1, 12, 0, 0))); + } +} diff --git a/aspnet-core/tests/LINGYUN.Linq.Dynamic.Queryable.Tests/LINGYUN.Linq.Dynamic.Queryable.Tests.csproj b/aspnet-core/tests/LINGYUN.Linq.Dynamic.Queryable.Tests/LINGYUN.Linq.Dynamic.Queryable.Tests.csproj new file mode 100644 index 000000000..06a5a5e68 --- /dev/null +++ b/aspnet-core/tests/LINGYUN.Linq.Dynamic.Queryable.Tests/LINGYUN.Linq.Dynamic.Queryable.Tests.csproj @@ -0,0 +1,18 @@ + + + + net6.0 + + false + + + + + + + + + + + + diff --git a/aspnet-core/tests/LINGYUN.Linq.Dynamic.Queryable.Tests/LINGYUN/Linq/Dynamic/Queryable/DynamicQueryableTestBase.cs b/aspnet-core/tests/LINGYUN.Linq.Dynamic.Queryable.Tests/LINGYUN/Linq/Dynamic/Queryable/DynamicQueryableTestBase.cs new file mode 100644 index 000000000..ce3deeb06 --- /dev/null +++ b/aspnet-core/tests/LINGYUN.Linq.Dynamic.Queryable.Tests/LINGYUN/Linq/Dynamic/Queryable/DynamicQueryableTestBase.cs @@ -0,0 +1,5 @@ +namespace LINGYUN.Linq.Dynamic.Queryable; + +public abstract class DynamicQueryableTestBase : AbpTestsBase +{ +} diff --git a/aspnet-core/tests/LINGYUN.Linq.Dynamic.Queryable.Tests/LINGYUN/Linq/Dynamic/Queryable/DynamicQueryableTestModule.cs b/aspnet-core/tests/LINGYUN.Linq.Dynamic.Queryable.Tests/LINGYUN/Linq/Dynamic/Queryable/DynamicQueryableTestModule.cs new file mode 100644 index 000000000..d9949255f --- /dev/null +++ b/aspnet-core/tests/LINGYUN.Linq.Dynamic.Queryable.Tests/LINGYUN/Linq/Dynamic/Queryable/DynamicQueryableTestModule.cs @@ -0,0 +1,8 @@ +using Volo.Abp.Modularity; + +namespace LINGYUN.Linq.Dynamic.Queryable; + +[DependsOn(typeof(AbpTestsBaseModule))] +public class DynamicQueryableTestModule : AbpModule +{ +} diff --git a/aspnet-core/tests/LINGYUN.Linq.Dynamic.Queryable.Tests/LINGYUN/Linq/Dynamic/Queryable/DynamicQueryableTests.cs b/aspnet-core/tests/LINGYUN.Linq.Dynamic.Queryable.Tests/LINGYUN/Linq/Dynamic/Queryable/DynamicQueryableTests.cs new file mode 100644 index 000000000..b106f7313 --- /dev/null +++ b/aspnet-core/tests/LINGYUN.Linq.Dynamic.Queryable.Tests/LINGYUN/Linq/Dynamic/Queryable/DynamicQueryableTests.cs @@ -0,0 +1,337 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Text; +using System.Threading.Tasks; + +namespace LINGYUN.Linq.Dynamic.Queryable; + +public class LinqTestClass +{ + public string StringNull { get; set; } + public string StringRequired { get; set; } + public long? Int64Null { get; set; } + public long Int64Required { get; set; } + public DateTime? DateTimeNull { get; set; } + public DateTime DateTimeRequired { get; set; } + public DateOnly? DateOnlyNull { get; set; } + public DateOnly DateOnlyRequired { get; set; } + public TimeOnly? TimeOnlyNull { get; set; } + public TimeOnly TimeOnlyRequired { get; set; } +} + +public class DynamicQueryableTests : DynamicQueryableTestBase +{ + private readonly static List _testClasses; + static DynamicQueryableTests() + { + _testClasses = new List + { + new LinqTestClass + { + StringNull = null, + StringRequired = "3211", + DateOnlyNull = new DateOnly(2022, 1, 1), + DateOnlyRequired = new DateOnly(2022, 10, 1), + TimeOnlyNull = null, + TimeOnlyRequired = new TimeOnly(12, 0, 0), + DateTimeNull = new DateTime(2021, 1, 1, 0, 0, 0), + DateTimeRequired = new DateTime(2022, 10, 1, 12, 0, 0), + Int64Null = null, + Int64Required = 1024L + }, + new LinqTestClass + { + StringNull = "not null", + StringRequired = "1123", + DateOnlyNull = null, + DateOnlyRequired = new DateOnly(2021, 10, 1), + TimeOnlyNull = new TimeOnly(0, 0, 0), + TimeOnlyRequired = new TimeOnly(1, 0, 0), + DateTimeNull = null, + DateTimeRequired = new DateTime(2021, 1, 1, 0, 0, 0), + Int64Null = null, + Int64Required = 2048L + }, + }; + } + + [Fact] + public void Should_Null() + { + Expression> exp = (_) => true; + + var dynamicQueryable = new DynamicQueryable(); + dynamicQueryable.Paramters.Add(new DynamicParamter + { + Comparison = DynamicComparison.Null, + Field = nameof(LinqTestClass.Int64Null), + Logic = DynamicLogic.And + }); + + exp = exp.DynamicQuery(dynamicQueryable); + + var result = _testClasses.Where(exp.Compile()).ToList(); + result.Count.ShouldBe(2); + } + + [Fact] + public void Should_Not_Null() + { + Expression> exp = (_) => true; + + var dynamicQueryable = new DynamicQueryable(); + dynamicQueryable.Paramters.Add(new DynamicParamter + { + Comparison = DynamicComparison.NotNull, + Field = nameof(LinqTestClass.StringNull), + Logic = DynamicLogic.And + }); + + exp = exp.DynamicQuery(dynamicQueryable); + + var result = _testClasses.Where(exp.Compile()).ToList(); + result.Count.ShouldBe(1); + } + + [Fact] + public void Should_Equal() + { + Expression> exp = (_) => true; + + var dynamicQueryable = new DynamicQueryable(); + dynamicQueryable.Paramters.Add(new DynamicParamter + { + Comparison = DynamicComparison.Equal, + Field = nameof(LinqTestClass.StringRequired), + Logic = DynamicLogic.And, + Value = "1123" + }); + + exp = exp.DynamicQuery(dynamicQueryable); + + var result = _testClasses.Where(exp.Compile()).ToList(); + result.Count.ShouldBe(1); + } + + [Fact] + public void Should_Not_Equal() + { + Expression> exp = (_) => true; + + var dynamicQueryable = new DynamicQueryable(); + dynamicQueryable.Paramters.Add(new DynamicParamter + { + Comparison = DynamicComparison.NotEqual, + Field = nameof(LinqTestClass.StringRequired), + Logic = DynamicLogic.And, + Value = "1123" + }); + + exp = exp.DynamicQuery(dynamicQueryable); + + var result = _testClasses.Where(exp.Compile()).ToList(); + result.Count.ShouldBe(1); + } + + [Fact] + public void Should_Less_Than() + { + Expression> exp = (_) => true; + + var dynamicQueryable = new DynamicQueryable(); + dynamicQueryable.Paramters.Add(new DynamicParamter + { + Comparison = DynamicComparison.LessThan, + Field = nameof(LinqTestClass.Int64Required), + Logic = DynamicLogic.And, + Value = 2048L + }); + + exp = exp.DynamicQuery(dynamicQueryable); + + var result = _testClasses.Where(exp.Compile()).ToList(); + result.Count.ShouldBe(1); + } + + [Fact] + public void Should_Less_Than_Or_Equal() + { + Expression> exp = (_) => true; + + var dynamicQueryable = new DynamicQueryable(); + dynamicQueryable.Paramters.Add(new DynamicParamter + { + Comparison = DynamicComparison.LessThanOrEqual, + Field = nameof(LinqTestClass.Int64Required), + Logic = DynamicLogic.And, + Value = 2048L + }); + + exp = exp.DynamicQuery(dynamicQueryable); + + var result = _testClasses.Where(exp.Compile()).ToList(); + result.Count.ShouldBe(2); + } + + [Fact] + public void Should_Greater_Than() + { + Expression> exp = (_) => true; + + var dynamicQueryable = new DynamicQueryable(); + dynamicQueryable.Paramters.Add(new DynamicParamter + { + Comparison = DynamicComparison.GreaterThan, + Field = nameof(LinqTestClass.Int64Null), + Logic = DynamicLogic.And, + Value = 1024L + }); + + exp = exp.DynamicQuery(dynamicQueryable); + + var result = _testClasses.Where(exp.Compile()).ToList(); + result.Count.ShouldBe(0); + } + + [Fact] + public void Should_Greater_Than_Or_Equal() + { + Expression> exp = (_) => true; + + var dynamicQueryable = new DynamicQueryable(); + dynamicQueryable.Paramters.Add(new DynamicParamter + { + Comparison = DynamicComparison.GreaterThanOrEqual, + Field = nameof(LinqTestClass.Int64Required), + Logic = DynamicLogic.And, + Value = 1024L + }); + + exp = exp.DynamicQuery(dynamicQueryable); + + var result = _testClasses.Where(exp.Compile()).ToList(); + result.Count.ShouldBe(2); + } + + [Fact] + public void Should_Starts_With() + { + Expression> exp = (_) => true; + + var dynamicQueryable = new DynamicQueryable(); + dynamicQueryable.Paramters.Add(new DynamicParamter + { + Comparison = DynamicComparison.StartsWith, + Field = nameof(LinqTestClass.StringNull), + Logic = DynamicLogic.And, + Value = "not" + }); + + exp = exp.DynamicQuery(dynamicQueryable); + + var result = _testClasses.Where(exp.Compile()).ToList(); + result.Count.ShouldBe(1); + } + + [Fact] + public void Should_Not_Starts_With() + { + Expression> exp = (_) => true; + + var dynamicQueryable = new DynamicQueryable(); + dynamicQueryable.Paramters.Add(new DynamicParamter + { + Comparison = DynamicComparison.NotStartsWith, + Field = nameof(LinqTestClass.StringNull), + Logic = DynamicLogic.And, + Value = "not" + }); + + exp = exp.DynamicQuery(dynamicQueryable); + + var result = _testClasses.Where(exp.Compile()).ToList(); + result.Count.ShouldBe(1); + } + + [Fact] + public void Should_Ends_With() + { + Expression> exp = (_) => true; + + var dynamicQueryable = new DynamicQueryable(); + dynamicQueryable.Paramters.Add(new DynamicParamter + { + Comparison = DynamicComparison.EndsWith, + Field = nameof(LinqTestClass.StringNull), + Logic = DynamicLogic.And, + Value = "null" + }); + + exp = exp.DynamicQuery(dynamicQueryable); + + var result = _testClasses.Where(exp.Compile()).ToList(); + result.Count.ShouldBe(1); + } + + [Fact] + public void Should_Not_Ends_With() + { + Expression> exp = (_) => true; + + var dynamicQueryable = new DynamicQueryable(); + dynamicQueryable.Paramters.Add(new DynamicParamter + { + Comparison = DynamicComparison.NotEndsWith, + Field = nameof(LinqTestClass.StringNull), + Logic = DynamicLogic.And, + Value = "null" + }); + + exp = exp.DynamicQuery(dynamicQueryable); + + var result = _testClasses.Where(exp.Compile()).ToList(); + result.Count.ShouldBe(1); + } + + [Fact] + public void Should_Contains() + { + Expression> exp = (_) => true; + + var dynamicQueryable = new DynamicQueryable(); + dynamicQueryable.Paramters.Add(new DynamicParamter + { + Comparison = DynamicComparison.Contains, + Field = nameof(LinqTestClass.StringNull), + Logic = DynamicLogic.And, + Value = "null" + }); + + exp = exp.DynamicQuery(dynamicQueryable); + + var result = _testClasses.Where(exp.Compile()).ToList(); + result.Count.ShouldBe(1); + } + + [Fact] + public void Should_Not_Contains() + { + Expression> exp = (_) => true; + + var dynamicQueryable = new DynamicQueryable(); + dynamicQueryable.Paramters.Add(new DynamicParamter + { + Comparison = DynamicComparison.NotContains, + Field = nameof(LinqTestClass.StringNull), + Logic = DynamicLogic.And, + Value = "test" + }); + + exp = exp.DynamicQuery(dynamicQueryable); + + var result = _testClasses.Where(exp.Compile()).ToList(); + result.Count.ShouldBe(2); + } +} diff --git a/aspnet-core/tests/LINGYUN.Linq.Dynamic.Queryable.Tests/Usings.cs b/aspnet-core/tests/LINGYUN.Linq.Dynamic.Queryable.Tests/Usings.cs new file mode 100644 index 000000000..5a47b21df --- /dev/null +++ b/aspnet-core/tests/LINGYUN.Linq.Dynamic.Queryable.Tests/Usings.cs @@ -0,0 +1,4 @@ +global using Xunit; +global using Shouldly; +global using LINGYUN.Abp.Tests; +global using LINGYUN.Linq.Dynamic.Queryable;