C# 动态Linq 建立模糊查询通用工具类
Posted bosaidongmomo
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C# 动态Linq 建立模糊查询通用工具类相关的知识,希望对你有一定的参考价值。
动态Linq 模糊查询 反射
微软文档
问题描述
假设你有多个实体类型:
record Person(string LastName, string FirstName, DateTime DateOfBirth);
record Car(string Model, int Year);
对于这些实体类型中的任何一个,你都需要筛选并仅返回那些在其某个 string 字段内具有给定文本的实体。 对于 Person,你希望搜索 FirstName 和 LastName 属性:
string term = /* ... */;
var personsQry = new List<Person>()
.AsQueryable()
.Where(x => x.FirstName.Contains(term) || x.LastName.Contains(term));
但对于 Car,你希望仅搜索 Model 属性:
string term = /* ... */;
var carsQry = new List<Car>()
.AsQueryable()
.Where(x => x.Model.Contains(term));
尽管可以为 IQueryable<Person
> 编写一个自定义函数,并为 IQueryable<Car
> 编写另一个自定义函数,但以下函数会将此筛选添加到任何现有查询,而不考虑特定的元素类型如何。
微软示例
// using static System.Linq.Expressions.Expression;
IQueryable<T> TextFilter<T>(IQueryable<T> source, string term)
{
if (string.IsNullOrEmpty(term)) { return source; }
// T is a compile-time placeholder for the element type of the query.
Type elementType = typeof(T);
// Get all the string properties on this specific type.
PropertyInfo[] stringProperties =
elementType.GetProperties()
.Where(x => x.PropertyType == typeof(string))
.ToArray();
if (!stringProperties.Any()) { return source; }
// Get the right overload of String.Contains
MethodInfo containsMethod = typeof(string).GetMethod("Contains", new[] { typeof(string) })!;
// Create a parameter for the expression tree:
// the 'x' in 'x => x.PropertyName.Contains("term")'
// The type of this parameter is the query's element type
ParameterExpression prm = Parameter(elementType);
// Map each property to an expression tree node
IEnumerable<Expression> expressions = stringProperties
.Select(prp =>
// For each property, we have to construct an expression tree node like x.PropertyName.Contains("term")
Call( // .Contains(...)
Property( // .PropertyName
prm, // x
prp
),
containsMethod,
Constant(term) // "term"
)
);
// Combine all the resultant expression nodes using ||
Expression body = expressions
.Aggregate(
(prev, current) => Or(prev, current)
);
// Wrap the expression body in a compile-time-typed lambda expression
Expression<Func<T, bool>> lambda = Lambda<Func<T, bool>>(body, prm);
// Because the lambda is compile-time-typed (albeit with a generic parameter), we can use it with the Where method
return source.Where(lambda);
}
由于 TextFilter 函数采用并返回 IQueryable(而不仅仅是 IQueryable),因此你可以在文本筛选器后添加更多的编译时类型的查询元素。
var qry = TextFilter(
new List<Person>().AsQueryable(),
"abcd"
)
.Where(x => x.DateOfBirth < new DateTime(2001, 1, 1));
var qry1 = TextFilter(
new List<Car>().AsQueryable(),
"abcd"
)
.Where(x => x.Year == 2010);
利用反射 建立模糊查询通用工具类
public static IQueryable<T> TextFilter<T>(IQueryable<T> source, T param)
{
// 如果参数为空
if (param == null) { return source; }
// 获取对象参数
Type elementType = typeof(T);
// 在这个类型参数中获取所有的string类型
// Get all the string properties on this specific type.
PropertyInfo[] stringProperties =
elementType.GetProperties()
.Where(x => x.PropertyType == typeof(string))
.ToArray();
// 数组是否包含任何元素
if (!stringProperties.Any()) { return source; }
// Get the right overload of String.Contains
// 创建 String 方法中的 Contains 重载
MethodInfo containsMethod = typeof(string).GetMethod("Contains", new[] { typeof(string) });
// Create a parameter for the expression tree:
// the 'x' in 'x => x.PropertyName.Contains("term")'
// The type of this parameter is the query's element type
// 这是 Linq 表达式中的 x the 'x' in 'x => x.PropertyName.Contains("term")'
ParameterExpression prm = Expression.Parameter(elementType);
List<Expression> list = new List<Expression>();
// 遍历每个string类型
foreach (PropertyInfo prp in stringProperties)
{
// 获取string类型的值
String propertyValue = (String)prp.GetValue(param);
// 如果是空,或者是null,不做处理
if (String.IsNullOrEmpty(propertyValue))
{
continue;
}
// 定义表达式
var expression = Expression.Call( // .Contains(...)
Expression.Property( // .PropertyName
prm, // x
prp
),
containsMethod,
Expression.Constant(propertyValue) // "term"
);
list.Add(expression);
}
var expressions = list as IEnumerable<Expression>;
// Combine all the resultant expression nodes using ||
Expression body = expressions
.Aggregate(
(prev, current) => Expression.And(prev, current)
);
// Wrap the expression body in a compile-time-typed lambda expression
Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(body, prm);
// Because the lambda is compile-time-typed (albeit with a generic parameter), we can use it with the Where method
return source.Where(lambda);
}
调用
List<Person> src = new List<Person>() { new Person { LastName = "abcd", FirstName = "abcd" }, new Person { LastName = "abcd", FirstName = "a" }, new Person { LastName = "a", FirstName = "abcd" } };
List<Car> src1 = new List<Car>() { new Car { Model = "abcd" }, new Car { Model = "a" }, new Car { Model = "c" } };
var query1 = DynamicLinqUtils.TextFilter(
src.AsQueryable(),
new Person { LastName = "c", FirstName = "c" }
)
.Where(x => x.DateOfBirth < new DateTime(2001, 1, 1));
var list1 = query1.ToList();
var query2 = DynamicLinqUtils.TextFilter(
src1.AsQueryable(),
new Car { Model = "c" }
);
var list2 = query2.ToList();
以上是关于C# 动态Linq 建立模糊查询通用工具类的主要内容,如果未能解决你的问题,请参考以下文章