40 changed files with 762 additions and 12 deletions
@ -0,0 +1,3 @@ |
|||||
|
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
||||
|
<ConfigureAwait ContinueOnCapturedContext="false" /> |
||||
|
</Weavers> |
||||
@ -0,0 +1,30 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> |
||||
|
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. --> |
||||
|
<xs:element name="Weavers"> |
||||
|
<xs:complexType> |
||||
|
<xs:all> |
||||
|
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1"> |
||||
|
<xs:complexType> |
||||
|
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" /> |
||||
|
</xs:complexType> |
||||
|
</xs:element> |
||||
|
</xs:all> |
||||
|
<xs:attribute name="VerifyAssembly" type="xs:boolean"> |
||||
|
<xs:annotation> |
||||
|
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation> |
||||
|
</xs:annotation> |
||||
|
</xs:attribute> |
||||
|
<xs:attribute name="VerifyIgnoreCodes" type="xs:string"> |
||||
|
<xs:annotation> |
||||
|
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation> |
||||
|
</xs:annotation> |
||||
|
</xs:attribute> |
||||
|
<xs:attribute name="GenerateXsd" type="xs:boolean"> |
||||
|
<xs:annotation> |
||||
|
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation> |
||||
|
</xs:annotation> |
||||
|
</xs:attribute> |
||||
|
</xs:complexType> |
||||
|
</xs:element> |
||||
|
</xs:schema> |
||||
@ -0,0 +1,19 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<Import Project="..\..\..\configureawait.props" /> |
||||
|
<Import Project="..\..\..\common.props" /> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>netstandard2.0</TargetFramework> |
||||
|
<RootNamespace /> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<PackageReference Include="Volo.Abp.Ddd.Application.Contracts" Version="$(VoloAbpPackageVersion)" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\LINGYUN.Linq.Dynamic.Queryable\LINGYUN.Linq.Dynamic.Queryable.csproj" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,10 @@ |
|||||
|
using Volo.Abp.Application; |
||||
|
using Volo.Abp.Modularity; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Dynamic.Queryable; |
||||
|
|
||||
|
[DependsOn(typeof(AbpDddApplicationContractsModule))] |
||||
|
public class AbpDynamicQueryableApplicationContractsModule : AbpModule |
||||
|
{ |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,8 @@ |
|||||
|
namespace LINGYUN.Abp.Dynamic.Queryable; |
||||
|
|
||||
|
public class DynamicParamterDto |
||||
|
{ |
||||
|
public string Name { get; set; } |
||||
|
public string Description { get; set; } |
||||
|
public string Type { get; set; } |
||||
|
} |
||||
@ -0,0 +1,11 @@ |
|||||
|
using LINGYUN.Linq.Dynamic.Queryable; |
||||
|
using System.ComponentModel.DataAnnotations; |
||||
|
using Volo.Abp.Application.Dtos; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Dynamic.Queryable; |
||||
|
|
||||
|
public class GetListByDynamicQueryableInput : PagedAndSortedResultRequestDto |
||||
|
{ |
||||
|
[Required] |
||||
|
public DynamicQueryable Queryable { get; set; } |
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Application.Dtos; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Dynamic.Queryable; |
||||
|
|
||||
|
public interface IDynamicQueryableAppService<TEntityDto> |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 获取可用字段列表
|
||||
|
/// </summary>
|
||||
|
/// <returns></returns>
|
||||
|
Task<ListResultDto<DynamicParamterDto>> GetAvailableFieldsAsync(); |
||||
|
/// <summary>
|
||||
|
/// 根据动态条件查询数据
|
||||
|
/// </summary>
|
||||
|
/// <param name="input"></param>
|
||||
|
/// <returns></returns>
|
||||
|
Task<PagedResultDto<TEntityDto>> GetListAsync(GetListByDynamicQueryableInput input); |
||||
|
} |
||||
@ -0,0 +1,3 @@ |
|||||
|
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
||||
|
<ConfigureAwait ContinueOnCapturedContext="false" /> |
||||
|
</Weavers> |
||||
@ -0,0 +1,30 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> |
||||
|
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. --> |
||||
|
<xs:element name="Weavers"> |
||||
|
<xs:complexType> |
||||
|
<xs:all> |
||||
|
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1"> |
||||
|
<xs:complexType> |
||||
|
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" /> |
||||
|
</xs:complexType> |
||||
|
</xs:element> |
||||
|
</xs:all> |
||||
|
<xs:attribute name="VerifyAssembly" type="xs:boolean"> |
||||
|
<xs:annotation> |
||||
|
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation> |
||||
|
</xs:annotation> |
||||
|
</xs:attribute> |
||||
|
<xs:attribute name="VerifyIgnoreCodes" type="xs:string"> |
||||
|
<xs:annotation> |
||||
|
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation> |
||||
|
</xs:annotation> |
||||
|
</xs:attribute> |
||||
|
<xs:attribute name="GenerateXsd" type="xs:boolean"> |
||||
|
<xs:annotation> |
||||
|
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation> |
||||
|
</xs:annotation> |
||||
|
</xs:attribute> |
||||
|
</xs:complexType> |
||||
|
</xs:element> |
||||
|
</xs:schema> |
||||
@ -0,0 +1,19 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<Import Project="..\..\..\configureawait.props" /> |
||||
|
<Import Project="..\..\..\common.props" /> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>netstandard2.0</TargetFramework> |
||||
|
<RootNamespace /> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<PackageReference Include="Volo.Abp.Ddd.Application" Version="$(VoloAbpPackageVersion)" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\LINGYUN.Abp.Dynamic.Queryable.Application.Contracts\LINGYUN.Abp.Dynamic.Queryable.Application.Contracts.csproj" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,11 @@ |
|||||
|
using Volo.Abp.Application; |
||||
|
using Volo.Abp.Modularity; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Dynamic.Queryable; |
||||
|
|
||||
|
[DependsOn( |
||||
|
typeof(AbpDynamicQueryableApplicationContractsModule), |
||||
|
typeof(AbpDddApplicationModule))] |
||||
|
public class AbpDynamicQueryableApplicationModule : AbpModule |
||||
|
{ |
||||
|
} |
||||
@ -0,0 +1,21 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using Volo.Abp.Auditing; |
||||
|
using Volo.Abp.MultiTenancy; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Dynamic.Queryable; |
||||
|
|
||||
|
public class AbpDynamicQueryableOptions |
||||
|
{ |
||||
|
public List<string> IgnoreFields { get; } |
||||
|
|
||||
|
public AbpDynamicQueryableOptions() |
||||
|
{ |
||||
|
IgnoreFields = new List<string> |
||||
|
{ |
||||
|
nameof(IMultiTenant.TenantId), |
||||
|
nameof(IDeletionAuditedObject.IsDeleted), |
||||
|
nameof(IDeletionAuditedObject.DeleterId), |
||||
|
nameof(IDeletionAuditedObject.DeletionTime), |
||||
|
}; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,74 @@ |
|||||
|
using Microsoft.Extensions.Options; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Linq.Expressions; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Application.Dtos; |
||||
|
using Volo.Abp.Application.Services; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Dynamic.Queryable; |
||||
|
|
||||
|
public abstract class DynamicQueryableAppService<TEntity, TEntityDto> : ApplicationService, IDynamicQueryableAppService<TEntityDto> |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 获取可用字段列表
|
||||
|
/// </summary>
|
||||
|
/// <returns></returns>
|
||||
|
public virtual Task<ListResultDto<DynamicParamterDto>> GetAvailableFieldsAsync() |
||||
|
{ |
||||
|
var options = LazyServiceProvider.LazyGetRequiredService<IOptions<AbpDynamicQueryableOptions>>().Value; |
||||
|
|
||||
|
var entityType = typeof(TEntity); |
||||
|
var dynamicParamters = new List<DynamicParamterDto>(); |
||||
|
|
||||
|
var propertyInfos = entityType |
||||
|
.GetProperties() |
||||
|
.Where(p => !options.IgnoreFields.Contains(p.Name)); |
||||
|
|
||||
|
foreach (var propertyInfo in propertyInfos) |
||||
|
{ |
||||
|
// 字段本地化描述规则
|
||||
|
// 在本地化文件中定义 DisplayName:PropertyName
|
||||
|
var localizedProp = L[$"DisplayName:{propertyInfo.Name}"]; |
||||
|
dynamicParamters.Add( |
||||
|
new DynamicParamterDto |
||||
|
{ |
||||
|
Name = propertyInfo.Name, |
||||
|
Type = propertyInfo.PropertyType.FullName, |
||||
|
Description = localizedProp.Value ?? propertyInfo.Name |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
return Task.FromResult(new ListResultDto<DynamicParamterDto>(dynamicParamters)); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 根据动态条件查询数据
|
||||
|
/// </summary>
|
||||
|
/// <param name="input"></param>
|
||||
|
/// <returns></returns>
|
||||
|
public async virtual Task<PagedResultDto<TEntityDto>> GetListAsync(GetListByDynamicQueryableInput input) |
||||
|
{ |
||||
|
Expression<Func<TEntity, bool>> condition = (e) => true; |
||||
|
|
||||
|
condition = condition.DynamicQuery(input.Queryable); |
||||
|
|
||||
|
var totalCount = await GetCountAsync(condition); |
||||
|
var entities = await GetListAsync(condition, input); |
||||
|
|
||||
|
return new PagedResultDto<TEntityDto>(totalCount, |
||||
|
MapToEntitiesDto(entities)); |
||||
|
} |
||||
|
|
||||
|
protected abstract Task<int> GetCountAsync(Expression<Func<TEntity, bool>> condition); |
||||
|
|
||||
|
protected abstract Task<List<TEntity>> GetListAsync( |
||||
|
Expression<Func<TEntity, bool>> condition, |
||||
|
PagedAndSortedResultRequestDto pageRequest); |
||||
|
|
||||
|
protected virtual List<TEntityDto> MapToEntitiesDto(List<TEntity> entities) |
||||
|
{ |
||||
|
return ObjectMapper.Map<List<TEntity>, List<TEntityDto>>(entities); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,3 @@ |
|||||
|
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
||||
|
<ConfigureAwait ContinueOnCapturedContext="false" /> |
||||
|
</Weavers> |
||||
@ -0,0 +1,30 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> |
||||
|
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. --> |
||||
|
<xs:element name="Weavers"> |
||||
|
<xs:complexType> |
||||
|
<xs:all> |
||||
|
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1"> |
||||
|
<xs:complexType> |
||||
|
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" /> |
||||
|
</xs:complexType> |
||||
|
</xs:element> |
||||
|
</xs:all> |
||||
|
<xs:attribute name="VerifyAssembly" type="xs:boolean"> |
||||
|
<xs:annotation> |
||||
|
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation> |
||||
|
</xs:annotation> |
||||
|
</xs:attribute> |
||||
|
<xs:attribute name="VerifyIgnoreCodes" type="xs:string"> |
||||
|
<xs:annotation> |
||||
|
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation> |
||||
|
</xs:annotation> |
||||
|
</xs:attribute> |
||||
|
<xs:attribute name="GenerateXsd" type="xs:boolean"> |
||||
|
<xs:annotation> |
||||
|
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation> |
||||
|
</xs:annotation> |
||||
|
</xs:attribute> |
||||
|
</xs:complexType> |
||||
|
</xs:element> |
||||
|
</xs:schema> |
||||
@ -0,0 +1,19 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<Import Project="..\..\..\configureawait.props" /> |
||||
|
<Import Project="..\..\..\common.props" /> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>net6.0</TargetFramework> |
||||
|
<RootNamespace /> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<PackageReference Include="Volo.Abp.AspNetCore.Mvc" Version="$(VoloAbpPackageVersion)" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\LINGYUN.Abp.Dynamic.Queryable.Application.Contracts\LINGYUN.Abp.Dynamic.Queryable.Application.Contracts.csproj" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,18 @@ |
|||||
|
using Volo.Abp.AspNetCore.Mvc; |
||||
|
using Volo.Abp.Modularity; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Dynamic.Queryable; |
||||
|
|
||||
|
[DependsOn( |
||||
|
typeof(AbpDynamicQueryableApplicationContractsModule), |
||||
|
typeof(AbpAspNetCoreMvcModule))] |
||||
|
public class AbpDynamicQueryableHttpApiModule : AbpModule |
||||
|
{ |
||||
|
public override void PreConfigureServices(ServiceConfigurationContext context) |
||||
|
{ |
||||
|
//PreConfigure<IMvcBuilder>(mvcBuilder =>
|
||||
|
//{
|
||||
|
// mvcBuilder.AddApplicationPartIfNotExists(typeof(AbpDynamicQueryableHttpApiModule).Assembly);
|
||||
|
//});
|
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,31 @@ |
|||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Application.Dtos; |
||||
|
using Volo.Abp.AspNetCore.Mvc; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Dynamic.Queryable; |
||||
|
|
||||
|
public abstract class DynamicQueryableControllerBase<TEntityDto> : AbpControllerBase, IDynamicQueryableAppService<TEntityDto> |
||||
|
{ |
||||
|
protected IDynamicQueryableAppService<TEntityDto> DynamicQueryableAppService { get; } |
||||
|
|
||||
|
protected DynamicQueryableControllerBase( |
||||
|
IDynamicQueryableAppService<TEntityDto> dynamicQueryableAppService) |
||||
|
{ |
||||
|
DynamicQueryableAppService = dynamicQueryableAppService; |
||||
|
} |
||||
|
|
||||
|
[HttpGet] |
||||
|
[Route("available-fields")] |
||||
|
public Task<ListResultDto<DynamicParamterDto>> GetAvailableFieldsAsync() |
||||
|
{ |
||||
|
return DynamicQueryableAppService.GetAvailableFieldsAsync(); |
||||
|
} |
||||
|
|
||||
|
[HttpPost] |
||||
|
[Route("search")] |
||||
|
public Task<PagedResultDto<TEntityDto>> GetListAsync(GetListByDynamicQueryableInput input) |
||||
|
{ |
||||
|
return DynamicQueryableAppService.GetListAsync(input); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,3 @@ |
|||||
|
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
||||
|
<ConfigureAwait ContinueOnCapturedContext="false" /> |
||||
|
</Weavers> |
||||
@ -0,0 +1,30 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> |
||||
|
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. --> |
||||
|
<xs:element name="Weavers"> |
||||
|
<xs:complexType> |
||||
|
<xs:all> |
||||
|
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1"> |
||||
|
<xs:complexType> |
||||
|
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" /> |
||||
|
</xs:complexType> |
||||
|
</xs:element> |
||||
|
</xs:all> |
||||
|
<xs:attribute name="VerifyAssembly" type="xs:boolean"> |
||||
|
<xs:annotation> |
||||
|
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation> |
||||
|
</xs:annotation> |
||||
|
</xs:attribute> |
||||
|
<xs:attribute name="VerifyIgnoreCodes" type="xs:string"> |
||||
|
<xs:annotation> |
||||
|
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation> |
||||
|
</xs:annotation> |
||||
|
</xs:attribute> |
||||
|
<xs:attribute name="GenerateXsd" type="xs:boolean"> |
||||
|
<xs:annotation> |
||||
|
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation> |
||||
|
</xs:annotation> |
||||
|
</xs:attribute> |
||||
|
</xs:complexType> |
||||
|
</xs:element> |
||||
|
</xs:schema> |
||||
@ -0,0 +1,11 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<Import Project="..\..\..\configureawait.props" /> |
||||
|
<Import Project="..\..\..\common.props" /> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>netstandard2.0</TargetFramework> |
||||
|
<RootNamespace /> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,53 @@ |
|||||
|
namespace LINGYUN.Linq.Dynamic.Queryable; |
||||
|
public enum DynamicComparison |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 等于
|
||||
|
/// </summary>
|
||||
|
Equal = 0, |
||||
|
/// <summary>
|
||||
|
/// 不等于
|
||||
|
/// </summary>
|
||||
|
NotEqual = 1, |
||||
|
/// <summary>
|
||||
|
/// 小于
|
||||
|
/// </summary>
|
||||
|
LessThan = 2, |
||||
|
/// <summary>
|
||||
|
/// 小于等于
|
||||
|
/// </summary>
|
||||
|
LessThanOrEqual = 3, |
||||
|
/// <summary>
|
||||
|
/// 大于
|
||||
|
/// </summary>
|
||||
|
GreaterThan = 4, |
||||
|
/// <summary>
|
||||
|
/// 大于等于
|
||||
|
/// </summary>
|
||||
|
GreaterThanOrEqual = 5, |
||||
|
/// <summary>
|
||||
|
/// 左包含
|
||||
|
/// </summary>
|
||||
|
StartsWith = 6, |
||||
|
/// <summary>
|
||||
|
/// 左不包含
|
||||
|
/// </summary>
|
||||
|
NotStartsWith = 7, |
||||
|
/// <summary>
|
||||
|
/// 右包含
|
||||
|
/// </summary>
|
||||
|
EndsWith = 8, |
||||
|
/// <summary>
|
||||
|
/// 右不包含
|
||||
|
/// </summary>
|
||||
|
NotEndsWith = 9, |
||||
|
/// <summary>
|
||||
|
/// 包含
|
||||
|
/// </summary>
|
||||
|
Contains = 10, |
||||
|
/// <summary>
|
||||
|
/// 不包含
|
||||
|
/// </summary>
|
||||
|
NotContains = 11 |
||||
|
} |
||||
|
|
||||
@ -0,0 +1,13 @@ |
|||||
|
namespace LINGYUN.Linq.Dynamic.Queryable; |
||||
|
|
||||
|
public enum DynamicLogic |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 且
|
||||
|
/// </summary>
|
||||
|
And = 0, |
||||
|
/// <summary>
|
||||
|
/// 或
|
||||
|
/// </summary>
|
||||
|
Or = 1 |
||||
|
} |
||||
@ -0,0 +1,13 @@ |
|||||
|
namespace LINGYUN.Linq.Dynamic.Queryable; |
||||
|
|
||||
|
public class DynamicParamter |
||||
|
{ |
||||
|
public string Filed { get; set; } |
||||
|
|
||||
|
public DynamicLogic Logic { get; set; } = DynamicLogic.And; |
||||
|
|
||||
|
public DynamicComparison Comparison { get; set; } = DynamicComparison.Equal; |
||||
|
|
||||
|
public object Value { get; set; } |
||||
|
public string Type { get; set; } |
||||
|
} |
||||
@ -0,0 +1,8 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace LINGYUN.Linq.Dynamic.Queryable; |
||||
|
|
||||
|
public class DynamicQueryable |
||||
|
{ |
||||
|
public List<DynamicParamter> Paramters { get; set; } = new List<DynamicParamter>(); |
||||
|
} |
||||
@ -0,0 +1,12 @@ |
|||||
|
# LINGYUN.Linq.Dynamic.Queryable |
||||
|
|
||||
|
动态查询基本库, 扩展Linq, 动态构建表达式树 |
||||
|
|
||||
|
## 配置使用 |
||||
|
|
||||
|
模块按需引用, 只提供针对Linq的扩展 |
||||
|
|
||||
|
```csharp |
||||
|
|
||||
|
``` |
||||
|
|
||||
@ -0,0 +1,119 @@ |
|||||
|
using LINGYUN.Linq.Dynamic.Queryable; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Reflection; |
||||
|
|
||||
|
namespace System.Linq.Expressions; |
||||
|
|
||||
|
public static class ObjectQueryableExtensions |
||||
|
{ |
||||
|
public static Expression<T> DynamicQuery<T>( |
||||
|
this Expression<T> condition, |
||||
|
DynamicQueryable queryable) |
||||
|
{ |
||||
|
var typeExpression = condition.Parameters.FirstOrDefault(); |
||||
|
|
||||
|
return BuildExpressions(condition, typeExpression, queryable.Paramters); |
||||
|
} |
||||
|
|
||||
|
private static Expression<T> BuildExpressions<T>( |
||||
|
Expression<T> condition, |
||||
|
Expression typeExpression, |
||||
|
List<DynamicParamter> paramters) |
||||
|
{ |
||||
|
var expressions = new Stack<Expression>(); |
||||
|
foreach (var paramter in paramters) |
||||
|
{ |
||||
|
Expression exp = null; |
||||
|
Type propertyType = null; |
||||
|
var leftParamter = Expression.PropertyOrField(typeExpression, paramter.Filed); |
||||
|
if (!string.IsNullOrWhiteSpace(paramter.Type)) |
||||
|
{ |
||||
|
propertyType = Type.GetType(paramter.Type, true); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
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); |
||||
|
break; |
||||
|
case DynamicComparison.LessThan: |
||||
|
exp = Expression.LessThan(leftParamter, rightParamter); |
||||
|
break; |
||||
|
case DynamicComparison.LessThanOrEqual: |
||||
|
exp = Expression.LessThanOrEqual(leftParamter, rightParamter); |
||||
|
break; |
||||
|
case DynamicComparison.GreaterThan: |
||||
|
exp = Expression.GreaterThan(leftParamter, rightParamter); |
||||
|
break; |
||||
|
case DynamicComparison.GreaterThanOrEqual: |
||||
|
exp = Expression.GreaterThanOrEqual(leftParamter, rightParamter); |
||||
|
break; |
||||
|
case DynamicComparison.StartsWith: |
||||
|
exp = Expression.Call( |
||||
|
leftParamter, |
||||
|
typeof(string).GetMethod(nameof(String.StartsWith), new[] { typeof(string) }), |
||||
|
rightParamter); |
||||
|
break; |
||||
|
case DynamicComparison.NotStartsWith: |
||||
|
exp = Expression.Not( |
||||
|
Expression.Call( |
||||
|
leftParamter, |
||||
|
typeof(string).GetMethod(nameof(String.StartsWith), new[] { typeof(string) }), |
||||
|
rightParamter)); |
||||
|
break; |
||||
|
case DynamicComparison.EndsWith: |
||||
|
exp = Expression.Call( |
||||
|
leftParamter, |
||||
|
typeof(string).GetMethod(nameof(String.EndsWith), new[] { typeof(string) }), |
||||
|
rightParamter); |
||||
|
break; |
||||
|
case DynamicComparison.NotEndsWith: |
||||
|
exp = Expression.Not( |
||||
|
Expression.Call( |
||||
|
leftParamter, |
||||
|
typeof(string).GetMethod(nameof(String.EndsWith), new[] { typeof(string) }), |
||||
|
rightParamter)); |
||||
|
break; |
||||
|
case DynamicComparison.Contains: |
||||
|
exp = Expression.Call( |
||||
|
leftParamter, |
||||
|
typeof(string).GetMethod(nameof(String.Contains), new[] { typeof(string) }), |
||||
|
rightParamter); |
||||
|
break; |
||||
|
case DynamicComparison.NotContains: |
||||
|
exp = Expression.Not( |
||||
|
Expression.Call( |
||||
|
leftParamter, |
||||
|
typeof(string).GetMethod(nameof(String.Contains), new[] { typeof(string) }), |
||||
|
rightParamter)); |
||||
|
break; |
||||
|
} |
||||
|
expressions.Push(exp); |
||||
|
|
||||
|
while (expressions.Count > 1) |
||||
|
{ |
||||
|
var exp1 = expressions.Pop(); |
||||
|
var exp2 = expressions.Pop(); |
||||
|
|
||||
|
switch (paramter.Logic) |
||||
|
{ |
||||
|
case DynamicLogic.And: |
||||
|
expressions.Push(Expression.AndAlso(exp1, exp2)); |
||||
|
break; |
||||
|
case DynamicLogic.Or: |
||||
|
expressions.Push(Expression.Or(exp1, exp2)); |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return Expression.Lambda<T>(expressions.Pop(), condition.Parameters.ToArray()); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,20 @@ |
|||||
|
# Dynamic.Queryable |
||||
|
|
||||
|
动态查询支持库, 为以后台服务提供动态查询支持 |
||||
|
|
||||
|
* 应用程序接口定义层引用**LINGYUN.Abp.Dynamic.Queryable.Application.Contracts**模块, 实现**IDynamicQueryableAppService**接口可对外提供动态查询支持 |
||||
|
|
||||
|
* 应用程序接口实现层引用**LINGYUN.Abp.Dynamic.Queryable.Application**模块,继承自**DynamicQueryableAppService**接口,实现**GetCountAsync**与**GetListAsync**方法即可(为仓储层接口提供传递的**Expression<Func<TEntity, bool>>**结构) |
||||
|
|
||||
|
* 控制器层引用**LINGYUN.Abp.Dynamic.Queryable.HttpApi**模块,继承**DynamicQueryableControllerBase**接口,自动公开动态查询接口。 |
||||
|
|
||||
|
## 接口说明 |
||||
|
|
||||
|
* **GetAvailableFieldsAsync**(<font color="green">[GET]</font> <font color="blue">/{controller}/available-fields</font>): 对外提供可选的属性明细, 默认返回所有字段(不包含已定义的排除字段). |
||||
|
|
||||
|
* **GetListAsync**(<font color="orange">[POST]</font> <font color="blue">/{controller}/search</font>): 根据用户传递的动态条件返回查询结果列表. |
||||
|
|
||||
|
## 配置使用 |
||||
|
|
||||
|
* AbpDynamicQueryableOptions.IgnoreFields: 定义不对用户公开的字段列表,默认排除租户标识、软删除过滤器接口字段. |
||||
|
|
||||
Loading…
Reference in new issue