[英]How to write string.Contains(someText) in expression Tree
这是我正在学习表达式树的教程 。
我要显示超过35列,但用户可以选择一次显示10列。 因此,用户在搜索框中键入内容,我只想搜索用户可见的列。
SELECT FirstName, LastName, Address, ..., State
FROM Students
WHERE Id == @Id col1 AND (
FirstName LIKE '%@searchText%' OR
LastName LIKE '%@searchText%' OR
Address LIKE '%@searchText%' OR
...
State LIKE '%@searchText%')
回到Linq,这是我试图完成它的方式:
var result = db.Students
.Where(GetPredicate(id, listOfColumns))
.ToList();
这个私有方法:
private Expression<Func<Student, bool>> GetPredicate(int id, List<string> listOfColumns)
{
ParameterExpression pe = Expression.Parameter(typeof(Student), "s");
Expression left0 = Expression.Property(pe, "Id");
Expression right0 = Expression.Constant(id);
Expression e0 = Expression.Equal(left0, right0);
//Here ... omitted code because it's not working...
//
var expr = Expression.Lambda<Func<Student, bool>>(e0, new ParameterExpression[] { pe });
return expr;
}
如上所述,它的工作正常。 但是,我甚至编写此方法的原因是只能通过用户选择的列进行过滤。
我希望能够根据UI中可见的列进行撰写。
if(!string.IsNullOrEmpty(searchText))
{
foreach (string columnName in columnList)
{
Expression col = Expression.Property(pe, columnName);
Expression left = Expression.Call(pe, typeof(string).GetMethod("Contains"));
Expression right = Expression.Constant(searchText);
Expression e = Expression.IsTrue(left, right);
}
}
我完全迷失了。 我知道我需要访问字符串类的Contains方法然后我不知道接下来是什么。 想法是得到这样的东西:
Where(d => d.Id == id && (d.FirstName.Contains(searchText)
|| d.LastName.Contains(searchText)
|| ...
|| d.State.Contains(searchText)))
谢谢你的帮助
你非常接近,除了构建Contains
的调用没有右边:
Expression col = Expression.Property(pe, columnName);
Expression contains = Expression.Call(
pe
, typeof(string).GetMethod("Contains") // Make a static field out of this
, Expression.Constant(searchText) // Prepare a shared object before the loop
);
获得调用表达式后,将它们与OrElse
结合使用以生成lambda的主体。 你可以用循环来做,或者你可以使用LINQ:
private static readonly MethodInfo Contains = typeof(string).GetMethod(nameof(string.Contains));
public static Expression<Func<Student,bool>> SearchPredicate(IEnumerable<string> properties, string searchText) {
var param = Expression.Parameter(typeof(Student));
var search = Expression.Constant(searchText);
var components = properties
.Select(propName => Expression.Call(Expression.Property(param, propName), Contains, search))
.Cast<Expression>()
.ToList();
// This is the part that you were missing
var body = components
.Skip(1)
.Aggregate(components[0], Expression.OrElse);
return Expression.Lambda<Func<Student, bool>>(body, param);
}
对于像这样的场景,我喜欢PredicateBuilder类:
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Collections.Generic;
public static class PredicateBuilder
{
public static Expression<Func<T, bool>> True<T> () { return f => true; }
public static Expression<Func<T, bool>> False<T> () { return f => false; }
public static Expression<Func<T, bool>> Or<T> (this Expression<Func<T, bool>> expr1,
Expression<Func<T, bool>> expr2)
{
var invokedExpr = Expression.Invoke (expr2, expr1.Parameters.Cast<Expression> ());
return Expression.Lambda<Func<T, bool>>
(Expression.OrElse (expr1.Body, invokedExpr), expr1.Parameters);
}
public static Expression<Func<T, bool>> And<T> (this Expression<Func<T, bool>> expr1,
Expression<Func<T, bool>> expr2)
{
var invokedExpr = Expression.Invoke (expr2, expr1.Parameters.Cast<Expression> ());
return Expression.Lambda<Func<T, bool>>
(Expression.AndAlso (expr1.Body, invokedExpr), expr1.Parameters);
}
}
使用此代码看起来像:
var predicate = PredicateBuilder.True<Student>().And(i=>i.Id==id);
if(!string.IsNullOrEmpty(searchText))
{
if (firstNameColumnVisible) {
predicate = predicate.And(i=>i.FirstName.Contains(searchText));
}
if (lastNameColumnVisible) {
predicate = predicate.And(i=>i.LastName.Contains(searchText));
}
// more columns here.
}
最后,使用PredicateBuilder实例作为Linq查询中Where运算符的参数。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.