using System.Collections;
using System.Collections.Concurrent;
using System.Linq.Expressions;
using System.Reflection;
using AutoMapper.Internal;
namespace Dpz.Core.Entity.Base.ExpressTreeQuery;
/// <summary>
/// 条件过滤表达式树生成器
/// 用于根据请求参数动态生成 LINQ 表达式树,实现灵活的查询条件构建
/// </summary>
public static class GenerateConditionFilter
{
/// <summary>
/// 默认属性值的标识符
/// 当 QueryFilterAttribute 的 PropertyValue 等于此值时,将使用属性自身的值作为比较值
/// </summary>
internal const string DefaultPropertyValue = "10001";
/// <summary>
/// 属性缓存字典
/// 用于缓存类型的属性信息,提高反射性能
/// Key: 类型
/// Value: 该类型的所有属性信息数组
/// </summary>
private static readonly ConcurrentDictionary<Type, PropertyInfo[]> PropertyCache = new();
/// <summary>
/// 获取类型的所有属性,并缓存结果
/// </summary>
/// <param name="type">要获取属性的类型</param>
/// <returns>属性信息数组</returns>
private static PropertyInfo[] GetProperties(Type type)
{
return PropertyCache.GetOrAdd(type, t => t.GetProperties());
}
/// <summary>
/// 为单个实体类型生成条件表达式树
/// </summary>
/// <typeparam name="T">实体类型</typeparam>
/// <typeparam name="TRequest">请求参数类型</typeparam>
/// <param name="request">请求参数实例</param>
/// <returns>条件表达式树,形如:x => x.Property1 == value1 && x.Property2 > value2</returns>
public static Expression<Func<T, bool>> GenerateConditionExpressionTree<T, TRequest>(
TRequest request
)
{
return GenerateConditionExpressionTree<Func<T, bool>, TRequest>(request, typeof(T));
}
/// <summary>
/// 为两个实体类型生成条件表达式树
/// </summary>
/// <typeparam name="T">第一个实体类型</typeparam>
/// <typeparam name="T2">第二个实体类型</typeparam>
/// <typeparam name="TRequest">请求参数类型</typeparam>
/// <param name="request">请求参数实例</param>
/// <returns>条件表达式树,形如:(x, y) => x.Property1 == value1 && y.Property2 > value2</returns>
public static Expression<Func<T, T2, bool>> GenerateConditionExpressionTree<T, T2, TRequest>(
TRequest request
)
{
return GenerateConditionExpressionTree<Func<T, T2, bool>, TRequest>(
request,
typeof(T),
typeof(T2)
);
}
/// <summary>
/// 为三个实体类型生成条件表达式树
/// </summary>
/// <typeparam name="T">第一个实体类型</typeparam>
/// <typeparam name="T2">第二个实体类型</typeparam>
/// <typeparam name="T3">第三个实体类型</typeparam>
/// <typeparam name="TRequest">请求参数类型</typeparam>
/// <param name="request">请求参数实例</param>
/// <returns>条件表达式树,形如:(x, y, z) => x.Property1 == value1 && y.Property2 > value2 && z.Property3 != value3</returns>
public static Expression<Func<T, T2, T3, bool>> GenerateConditionExpressionTree<
T,
T2,
T3,
TRequest
>(TRequest request)
{
return GenerateConditionExpressionTree<Func<T, T2, T3, bool>, TRequest>(
request,
typeof(T),
typeof(T2),
typeof(T3)
);
}
/// <summary>
/// 为四个实体类型生成条件表达式树
/// </summary>
/// <typeparam name="T">第一个实体类型</typeparam>
/// <typeparam name="T2">第二个实体类型</typeparam>
/// <typeparam name="T3">第三个实体类型</typeparam>
/// <typeparam name="T4">第四个实体类型</typeparam>
/// <typeparam name="TRequest">请求参数类型</typeparam>
/// <param name="request">请求参数实例</param>
/// <returns>
/// 条件表达式树,形如:
/// <![CDATA[(x, y, z, w) => x.Property1 == value1 && y.Property2 > value2 && z.Property3 != value3 && w.Property4 <= value4]]>
/// </returns>
public static Expression<Func<T, T2, T3, T4, bool>> GenerateConditionExpressionTree<
T,
T2,
T3,
T4,
TRequest
>(TRequest request)
{
return GenerateConditionExpressionTree<Func<T, T2, T3, T4, bool>, TRequest>(
request,
typeof(T),
typeof(T2),
typeof(T3),
typeof(T4)
);
}
/// <summary>
/// 为五个实体类型生成条件表达式树
/// </summary>
/// <typeparam name="T">第一个实体类型</typeparam>
/// <typeparam name="T2">第二个实体类型</typeparam>
/// <typeparam name="T3">第三个实体类型</typeparam>
/// <typeparam name="T4">第四个实体类型</typeparam>
/// <typeparam name="T5">第五个实体类型</typeparam>
/// <typeparam name="TRequest">请求参数类型</typeparam>
/// <param name="request">请求参数实例</param>
/// <returns>
/// 条件表达式树,形如:
/// <![CDATA[(x, y, z, w, v) => x.Property1 == value1 && y.Property2 > value2 && z.Property3 != value3 && w.Property4 <= value4 && v.Property5 >= value5]]>
/// </returns>
public static Expression<Func<T, T2, T3, T4, T5, bool>> GenerateConditionExpressionTree<
T,
T2,
T3,
T4,
T5,
TRequest
>(TRequest request)
{
return GenerateConditionExpressionTree<Func<T, T2, T3, T4, T5, bool>, TRequest>(
request,
typeof(T),
typeof(T2),
typeof(T3),
typeof(T4),
typeof(T5)
);
}
/// <summary>
/// 核心方法:根据请求参数和实体类型生成条件表达式树
/// </summary>
/// <typeparam name="TFunc">委托类型</typeparam>
/// <typeparam name="TRequest">请求参数类型</typeparam>
/// <param name="request">请求参数实例</param>
/// <param name="additionalTypes">实体类型数组</param>
/// <returns>条件表达式树</returns>
/// <remarks>
/// 处理流程:
/// 1. 为每个实体类型创建参数表达式
/// 2. 获取请求参数类型的所有属性,并处理带有 QueryFilterAttribute 特性的属性
/// 3. 根据属性的 QueryFilterAttribute 特性生成对应的条件表达式
/// 4. 将所有条件表达式用 AND 连接起来
/// 5. 如果没有生成任何条件表达式,则返回始终为 true 的表达式
/// </remarks>
private static Expression<TFunc> GenerateConditionExpressionTree<TFunc, TRequest>(
TRequest request,
params Type[] additionalTypes
)
where TFunc : Delegate
{
// 为每个实体类型创建参数表达式
var parameters = additionalTypes
.Select((x, index) => Expression.Parameter(x, $"__q{(index == 0 ? "" : index)}"))
.ToList();
// 获取请求参数类型的所有属性,并处理带有QueryFilterAttribute特性的属性
var queryFieldsExpression = GetProperties(typeof(TRequest))
.SelectMany(x =>
{
var attributes = x.GetCustomAttributes<QueryFilterAttribute>();
var value = x.GetValue(request);
return HandleField(
parameters,
GetPropertyInfoByAttr,
GetValueByAttr,
attributes,
value
);
// 根据特性获取对应的属性信息
PropertyInfo? GetPropertyInfoByAttr(QueryFilterAttribute attr) =>
(
additionalTypes.Length == 1 ? additionalTypes[0] : attr.EntityType
)?.GetProperty(attr.PropertyName ?? x.Name);
// 获取用于比较的值
object? GetValueByAttr(QueryFilterAttribute attr) =>
attr.PropertyValue?.Equals(DefaultPropertyValue) == true
? value
: attr.PropertyValue;
})
.ToList();
// 如果没有生成任何条件表达式,则返回始终为true的表达式
if (queryFieldsExpression.Count == 0)
{
return Expression.Lambda<TFunc>(Expression.Constant(true), parameters.ToArray());
}
// 将所有条件表达式用AND连接起来
var finalExpression = queryFieldsExpression.Aggregate(Expression.AndAlso);
return Expression.Lambda<TFunc>(finalExpression, parameters.ToArray());
}
/// <summary>
/// 处理单个字段的过滤条件,生成对应的表达式
/// </summary>
/// <param name="parameters">参数表达式列表</param>
/// <param name="propertyFunc">获取属性信息的函数</param>
/// <param name="valueFunc">获取比较值的函数</param>
/// <param name="attributes">查询过滤特性集合</param>
/// <param name="selfValue">属性自身的值</param>
/// <returns>生成的表达式集合</returns>
/// <remarks>
/// 处理流程:
/// 1. 遍历所有 QueryFilterAttribute 特性
/// 2. 获取对应的属性信息和比较值
/// 3. 根据 Comparison 和 FilterCondition 生成相应的表达式
/// 4. 处理特殊条件(如日期范围、false值包含null等)
/// </remarks>
private static IEnumerable<Expression> HandleField(
List<ParameterExpression> parameters,
Func<QueryFilterAttribute, PropertyInfo?> propertyFunc,
Func<QueryFilterAttribute, object?> valueFunc,
IEnumerable<QueryFilterAttribute> attributes,
object? selfValue
)
{
foreach (var attr in attributes)
{
var property = propertyFunc(attr);
var value = valueFunc(attr);
if (property == null)
{
continue;
}
// 确定使用哪个参数表达式
var parameter =
parameters.Count == 1
? parameters[0]
: parameters.Find(x => x.Type == attr.EntityType);
if (parameter == null)
{
continue;
}
// 创建成员访问表达式
Expression memberExpr = Expression.Property(parameter, property);
// 检查是否应该跳过此条件
if (selfValue == null || ShouldSkip(selfValue, attr.Condition, attr.ConditionValue))
{
continue;
}
// 处理日期加一天的情况
if (attr.EndAddDay && value is DateTime end)
{
value = end.AddDays(1);
}
// 处理按天过滤的情况
if (attr.FilterInDay && value is DateTime filterDay)
{
var left = !property.PropertyType.IsNullableType()
? Expression.Convert(memberExpr, typeof(DateTime?))
: memberExpr;
yield return DateRangeFilter(left, filterDay);
continue;
}
// 处理false值包含null的情况
if (value is false && attr.FalseHasNull)
{
var right1 = Expression.Convert(Expression.Constant(value), property.PropertyType);
var right2 = Expression.Convert(Expression.Constant(null), property.PropertyType);
var equalComparison1 = Expression.Equal(memberExpr, right1);
var equalComparison2 = Expression.Equal(memberExpr, right2);
yield return Expression.OrElse(equalComparison1, equalComparison2);
continue;
}
// 创建右侧表达式
Expression right;
if (
attr.Comparison != Comparison.Contains
&& value != null
&& property.PropertyType != value.GetType()
)
{
right = Expression.Convert(Expression.Constant(value), property.PropertyType);
}
else
{
right = Expression.Constant(value);
}
// 根据比较方式生成不同的表达式
yield return attr.Comparison switch
{
Comparison.Equal => Expression.Equal(memberExpr, right),
Comparison.Gt => Expression.GreaterThan(memberExpr, right),
Comparison.GtOrEqual => Expression.GreaterThanOrEqual(memberExpr, right),
Comparison.Lt => Expression.LessThan(memberExpr, right),
Comparison.LtOrEqual => Expression.LessThanOrEqual(memberExpr, right),
Comparison.NotEqual => Expression.NotEqual(memberExpr, right),
Comparison.Contains => CreateContainsExpression(
memberExpr,
attr.Condition == FilterCondition.RightAny ? attr.ConditionValue : value,
attr.Condition
),
Comparison.SplitOrContains => CreateSplitOrContains(
memberExpr,
value,
attr.SplitOrContainsOtherSeparators
),
_ => throw new NotImplementedException(
$"Comparison '{attr.Comparison}' not implemented."
),
};
}
}
/// <summary>
/// 创建日期范围过滤表达式,过滤指定日期当天的数据
/// </summary>
/// <param name="left">左侧表达式(日期字段)</param>
/// <param name="filterDay">要过滤的日期</param>
/// <returns>日期范围过滤表达式,形如:<![CDATA[date >= start && date < end]]></returns>
private static BinaryExpression DateRangeFilter(Expression left, DateTime filterDay)
{
DateTime? start = filterDay.Date;
DateTime? end = start.Value.AddDays(1);
return Expression.AndAlso(
Expression.GreaterThanOrEqual(left, Expression.Constant(start, typeof(DateTime?))),
Expression.LessThan(left, Expression.Constant(end, typeof(DateTime?)))
);
}
/// <summary>
/// 字符串HashSet的Contains方法的懒加载引用
/// 用于优化字符串集合的包含关系检查
/// </summary>
private static readonly Lazy<MethodInfo> StringHashSetContainsMethod = new(
() =>
typeof(HashSet<string>).GetMethod(
"Contains",
BindingFlags.Public | BindingFlags.Instance
) ?? throw new NotSupportedException("not supported 'Contains' method")
);
/// <summary>
/// 字符串Contains方法的缓存
/// 用于优化字符串的包含关系检查
/// </summary>
private static readonly Lazy<MethodInfo> StringContainsMethod = new(
() =>
typeof(string).GetMethod("Contains", [typeof(string)])
?? throw new NotSupportedException("not supported string 'Contains' method")
);
/// <summary>
/// Enumerable.Contains方法的缓存
/// 用于优化集合的包含关系检查
/// Key: 元素类型
/// Value: 对应的 Contains 方法信息
/// </summary>
private static readonly ConcurrentDictionary<Type, MethodInfo> EnumerableContainsMethodCache =
new();
/// <summary>
/// 获取Enumerable.Contains方法
/// </summary>
/// <param name="elementType">集合元素类型</param>
/// <returns>Contains方法信息</returns>
/// <exception cref="NotSupportedException">当找不到对应的Contains方法时抛出</exception>
private static MethodInfo GetEnumerableContainsMethod(Type elementType)
{
return EnumerableContainsMethodCache.GetOrAdd(
elementType,
type =>
typeof(Enumerable)
.GetMethods()
.FirstOrDefault(m => m.Name == "Contains" && m.GetParameters().Length == 2)
?.MakeGenericMethod(type)
?? throw new NotSupportedException(
$"not supported Enumerable 'Contains' method for type {type}"
)
);
}
/// <summary>
/// 创建包含关系的表达式,支持字符串包含和集合包含
/// </summary>
/// <param name="memberExpr">成员表达式</param>
/// <param name="value">要检查的值</param>
/// <param name="condition">过滤条件</param>
/// <returns>包含关系的表达式</returns>
/// <remarks>
/// 支持以下情况:
/// 1. 字符串包含:生成 string.Contains 调用
/// 2. 集合包含:生成 Enumerable.Contains 调用
/// 3. 可空类型:自动处理可空类型的转换
/// </remarks>
private static Expression CreateContainsExpression(
Expression memberExpr,
object? value,
FilterCondition condition = FilterCondition.LeftAny
)
{
if (condition is not (FilterCondition.LeftAny or FilterCondition.RightAny))
{
return Expression.Constant(false);
}
if (value is string)
{
return Expression.Call(
memberExpr,
StringContainsMethod.Value,
Expression.Constant(value)
);
}
if (value is IEnumerable enumerable)
{
// 如果属性类型是可空类型,需要先获取其基础类型
var elementType = memberExpr.Type.IsNullableType()
? Nullable.GetUnderlyingType(memberExpr.Type)
: memberExpr.Type;
// 获取对应基础类型的 Contains 方法
var baseContainsMethod = GetEnumerableContainsMethod(elementType!);
// 如果属性是可空类型,需要先转换为非空类型
var convertedMemberExpr = memberExpr.Type.IsNullableType()
? Expression.Convert(memberExpr, elementType!)
: memberExpr;
return Expression.Call(
baseContainsMethod,
Expression.Constant(enumerable),
convertedMemberExpr
);
}
return Expression.Constant(false);
}
/// <summary>
/// 创建分割或包含表达式
/// 如果字符串不能被空格或其他分隔符分割,则执行模糊查询;否则执行IN查询
/// </summary>
/// <param name="memberExpr">成员表达式</param>
/// <param name="value">要检查的值</param>
/// <param name="splitOrContainsOtherSeparators">用于分隔字符串的除了空格的其他分隔符</param>
/// <returns>分割或包含表达式</returns>
/// <remarks>
/// 处理流程:
/// 1. 如果输入不是字符串或为空,返回false
/// 2. 使用空格和其他分隔符分割字符串
/// 3. 如果分割后只有一个值,执行模糊查询
/// 4. 否则,将分割后的值转换为HashSet,执行IN查询
/// </remarks>
private static Expression CreateSplitOrContains(
Expression memberExpr,
object? value,
char[]? splitOrContainsOtherSeparators
)
{
if (value is not string strValue || string.IsNullOrWhiteSpace(strValue))
{
return Expression.Constant(false);
}
const char splitChar = ' ';
if (splitOrContainsOtherSeparators is { Length: > 0 })
{
//如果有其他分隔符,则空格替换其他分隔符
foreach (var c in splitOrContainsOtherSeparators)
{
strValue = strValue.Replace(c, splitChar);
}
}
var splitValues = strValue.Split(splitChar);
if (!strValue.Contains(splitChar) && splitValues.Length <= 1)
{
return CreateContainsExpression(memberExpr, strValue);
}
var values = splitValues.Where(p => !string.IsNullOrWhiteSpace(p)).ToHashSet();
var valuesConstant = Expression.Constant(values);
return Expression.Call(
instance: valuesConstant,
method: StringHashSetContainsMethod.Value,
arguments: Expression.Convert(memberExpr, typeof(string))
);
}
/// <summary>
/// 根据不同的条件,判断是否应该跳过当前过滤条件
/// </summary>
/// <param name="value">要检查的值</param>
/// <param name="condition">过滤条件</param>
/// <param name="comparisonValue">比较值</param>
/// <returns>如果应该跳过,则返回true;否则返回false</returns>
/// <remarks>
/// 处理以下条件:
/// 1. NotNull:值为null时跳过
/// 2. NotNullOrEmpty:字符串为null或空时跳过
/// 3. NotNullOrWhiteSpace:字符串为null或空白时跳过
/// 4. LeftAny:集合为null或空时跳过
/// 5. RightAny:数组为null或空时跳过
/// 6. EqualValue/NotEqualValue:根据值是否相等决定是否跳过
/// 7. 比较操作符:根据比较结果决定是否跳过
/// </remarks>
private static bool ShouldSkip(
object? value,
FilterCondition condition,
object? comparisonValue
)
{
return condition switch
{
FilterCondition.NotNull => value == null,
FilterCondition.NotNullOrEmpty => value is not string str || string.IsNullOrEmpty(str),
FilterCondition.NotNullOrWhiteSpace => value is not string str
|| string.IsNullOrWhiteSpace(str),
FilterCondition.LeftAny => value is not ICollection collection || collection.Count == 0,
FilterCondition.RightAny => comparisonValue is not Array array || array.Length == 0,
FilterCondition.EqualValue => !AreEqual(value, comparisonValue),
FilterCondition.NotEqualValue => AreEqual(value, comparisonValue),
FilterCondition.GtValue
or FilterCondition.LtValue
or FilterCondition.GtOrEqualValue
or FilterCondition.LtOrEqualValue => OperatorInvoke(value, condition, comparisonValue)
!= true,
_ => false,
};
}
/// <summary>
/// 执行比较操作符,用于大于、小于等比较
/// </summary>
/// <param name="value">左侧值</param>
/// <param name="condition">比较条件</param>
/// <param name="conditionValue">右侧值</param>
/// <returns>比较结果,如果无法比较则返回null</returns>
/// <remarks>
/// 支持以下比较操作:
/// 1. GtValue:大于
/// 2. LtValue:小于
/// 3. GtOrEqualValue:大于等于
/// 4. LtOrEqualValue:小于等于
/// </remarks>
private static bool? OperatorInvoke(
object? value,
FilterCondition condition,
object? conditionValue
)
{
if (value == null || conditionValue == null)
{
return null;
}
if (value.GetType() != conditionValue.GetType())
{
return null;
}
Expression left = Expression.Constant(value);
Expression right;
var currentType = value.GetType();
if (currentType != conditionValue.GetType())
{
right = Expression.Convert(Expression.Constant(conditionValue), currentType);
}
else
{
right = Expression.Constant(conditionValue);
}
Expression<Func<bool>> initExpression = () => false;
Expression comparisonExpression = initExpression;
switch (condition)
{
// >
case FilterCondition.GtValue:
comparisonExpression = Expression.GreaterThan(left, right);
break;
// <
case FilterCondition.LtValue:
comparisonExpression = Expression.LessThan(left, right);
break;
// >=
case FilterCondition.GtOrEqualValue:
comparisonExpression = Expression.GreaterThanOrEqual(left, right);
break;
// <=
case FilterCondition.LtOrEqualValue:
comparisonExpression = Expression.LessThanOrEqual(left, right);
break;
}
if (comparisonExpression != initExpression)
{
return Expression.Lambda<Func<bool>>(comparisonExpression).Compile().Invoke();
}
return null;
}
/// <summary>
/// 判断两个对象是否相等
/// </summary>
/// <param name="value1">第一个对象</param>
/// <param name="value2">第二个对象</param>
/// <returns>如果相等则返回true,否则返回false</returns>
/// <remarks>
/// 处理规则:
/// 1. 如果两个值都为null,返回false
/// 2. 如果只有一个值为null,返回false
/// 3. 否则使用Equals方法比较
/// </remarks>
private static bool AreEqual(object? value1, object? value2)
{
//if (value1 == null && value2 == null) return true;
if (value1 == null || value2 == null)
{
return false;
}
return value1.Equals(value2);
}
}
⚠⚠ 以下内容为AI分析的结果,请根据实际情况进行判断。
条件过滤表达式树生成器代码解析
这段代码是一个用于动态构建LINQ表达式树的工具类,主要用于根据请求参数自动生成查询条件。以下是详细解析:
主要功能
动态构建查询条件:根据请求参数中的属性及其特性(Attribute),自动生成LINQ表达式树,用于数据库查询或其他筛选操作。
支持多种实体类型:提供了1-5个实体类型的泛型方法重载,可以同时为多个实体类型生成联合查询条件。
灵活的过滤条件:支持多种比较操作(等于、大于、小于、包含等)和过滤条件(非空、非空白等)。
核心组件
1. 属性缓存
使用ConcurrentDictionary缓存类型的属性信息,提高反射性能:
private static readonly ConcurrentDictionary<Type, PropertyInfo[]> PropertyCache = new();
2. 主要方法
GenerateConditionExpressionTree<T, TRequest>:单实体版本GenerateConditionExpressionTree<T, T2, TRequest>:双实体版本- 最多支持5个实体类型的联合查询
3. 核心处理流程
创建参数表达式:为每个实体类型创建参数表达式(如
x => x.Property中的x)处理请求参数属性:
- 获取带有
QueryFilterAttribute特性的属性 - 根据特性配置生成对应的条件表达式
- 获取带有
构建表达式树:
- 将各个条件表达式用AND连接
- 如果没有条件,返回
true表达式
关键特性
1. 比较操作支持
支持多种比较操作:
public enum Comparison
{
Equal, // ==
Gt, // >
GtOrEqual, // >=
Lt, // <
LtOrEqual, // <=
NotEqual, // !=
Contains, // 包含
SplitOrContains // 分割后包含
}
2. 特殊处理
- 日期处理:支持日期范围过滤(如某一天的数据)
- 空值处理:可以配置false值是否包含null
- 字符串分割:支持按空格或其他分隔符分割字符串进行模糊查询
3. 性能优化
- 方法缓存:缓存常用的反射方法(如Contains方法)
- 延迟加载:使用
Lazy<T>延迟加载不常用的方法 - 属性缓存:缓存类型的属性信息,避免重复反射
使用示例
假设有一个查询请求类UserQueryRequest:
public class UserQueryRequest
{
[QueryFilter(PropertyName = "Name", Comparison = Comparison.Contains)]
public string? Name { get; set; }
[QueryFilter(PropertyName = "Age", Comparison = Comparison.GtOrEqual)]
public int? MinAge { get; set; }
}
使用方式:
var request = new UserQueryRequest { Name = "John", MinAge = 18 };
var expression = GenerateConditionExpressionTree<User, UserQueryRequest>(request);
// 生成的表达式相当于:x => x.Name.Contains("John") && x.Age >= 18
总结
这段代码是一个强大的动态查询构建工具,它:
- 通过反射和表达式树技术实现了灵活的查询条件构建
- 支持多种比较操作和复杂条件处理
- 具有良好的性能优化
- 可以简化后端服务中常见的动态查询需求
特别适合在需要根据前端传入参数动态构建查询条件的场景中使用,如高级搜索、数据筛选等功能。
评论加载中...