当表达式的某些部分作为参数传递时,如何构建表达式树?
E.g。如果我想创建这样的表达式树,该怎么办:
IQueryable<LxUser> test1(IQueryable<LxUser> query, string foo, string bar)
{
query=query.Where(x => x.Foo.StartsWith(foo));
return query.Where(x => x.Bar.StartsWith(bar));
}
但通过间接创建它们:
IQueryable<LxUser> test2(IQueryable<LxUser> query, string foo, string bar)
{
query=testAdd(query, x => x.Foo, foo);
return testAdd(query, x => x.Bar, bar);
}
IQueryable<T> testAdd<T>(IQueryable<T> query,
Expression<Func<T, string>> select, string find)
{
// how can I combine the select expression with StartsWith?
return query.Where(x => select(x) .. y => y.StartsWith(find));
}
结果:
虽然样本没有多大意义(抱歉,但我试图保持简单),这是结果(感谢Quartermeister)。
它可以与Linq-to-Sql一起使用,以搜索以findText开头或等于的字符串。
public static IQueryable<T> WhereLikeOrExact<T>(IQueryable<T> query,
Expression<Func<T, string>> selectField, string findText)
{
Expression<Func<string, bool>> find;
if (string.IsNullOrEmpty(findText) || findText=="*") return query;
if (findText.EndsWith("*"))
find=x => x.StartsWith(findText.Substring(0, findText.Length-1));
else
find=x => x==findText;
var p=Expression.Parameter(typeof(T), null);
var xpr=Expression.Invoke(find, Expression.Invoke(selectField, p));
return query.Where(Expression.Lambda<Func<T, bool>>(xpr, p));
}
e.g。
var query=context.User;
query=WhereLikeOrExact(query, x => x.FirstName, find.FirstName);
query=WhereLikeOrExact(query, x => x.LastName, find.LastName);
答案 0 :(得分:5)
您可以使用Expression.Invoke创建表示将一个表达式应用于另一个表达式的表达式,并使用Expression.Lambda为组合表达式创建新的lambda表达式。像这样:
IQueryable<T> testAdd<T>(IQueryable<T> query,
Expression<Func<T, string>> select, string find)
{
Expression<Func<string, bool>> startsWith = y => y.StartsWith(find);
var parameter = Expression.Parameter(typeof(T), null);
return query.Where(
Expression.Lambda<Func<T, bool>>(
Expression.Invoke(
startsWith,
Expression.Invoke(select, parameter)),
parameter));
}
内部Expression.Invoke表示表达式select(x)
,外部表达式表示对y => y.StartsWith(find)
返回的值调用select(x)
。
您还可以使用Expression.Call来表示对StartsWith的调用,而不使用第二个lambda:
IQueryable<T> testAdd<T>(IQueryable<T> query,
Expression<Func<T, string>> select, string find)
{
var parameter = Expression.Parameter(typeof(T), null);
return query.Where(
Expression.Lambda<Func<T, bool>>(
Expression.Call(
Expression.Invoke(select, parameter),
"StartsWith",
null,
Expression.Constant(find)),
parameter));
}
答案 1 :(得分:3)
本作品:
public IQueryable<T> Add<T>(IQueryable<T> query, Expression<Func<T, string>> Selector1,
Expression<Func<T, string>> Selector2, string data1, string data2)
{
return Add(Add(query, Selector1, data1), Selector2, data2);
}
public IQueryable<T> Add<T>(IQueryable<T> query, Expression<Func<T, string>> Selector, string data)
{
var row = Expression.Parameter(typeof(T), "row");
var expression =
Expression.Call(
Expression.Invoke(Selector, row),
"StartsWith", null, Expression.Constant(data, typeof(string))
);
var lambda = Expression.Lambda<Func<T, bool>>(expression, row);
return query.Where(lambda);
}
你可以像使用它一样:
IQueryable<XlUser> query = SomehowInitializeIt();
query = Add(query, x => x.Foo, y => y.Bar, "Foo", "Bar");
答案 2 :(得分:1)
通常你不会以你所描述的方式(使用IQueryable接口)这样做,而是使用像Expression<Func<TResult, T>>
这样的表达式。话虽如此,您可以将更高阶函数(例如where
或select
)组合到查询中,并传入将“填充”所需功能的表达式。
例如,考虑Enumerable.Where
方法的签名:
Where<TSource>(IEnumerable<TSource>, Func<TSource, Boolean>)
该函数将委托作为在每个元素上调用的第二个参数。从该委托返回的值表示高阶函数是否应该产生当前元素(包括在结果中)。
现在,让我们来看看Queryable.Where
:
Queryable.Where<TSource>-Methode (IQueryable<TSource>, Expression<Func<TSource, Boolean>>)
我们可以观察到更高阶函数的相同模式,但它不是Func<>
委托,而是表达式。表达式基本上是代码的数据表示。编译该表达式将为您提供真实(可执行)委托。编译器为你分配给Expression<...>
的lambda建立表达式树做了很多繁重的工作。表达式树可以针对不同的数据源(如SQL Server数据库)编译所描述的代码。
回到你的例子,我认为你正在寻找的是 选择器 。选择器获取每个输入元素并返回它的投影。它的签名如下:Expression<Func<TResult, T>>
。例如,您可以指定这个:
Expression<Func<int, string>> numberFormatter = (i) => i.ToString(); // projects an int into a string
要传入选择器,您的代码需要如下所示:
IQueryable<T> testAdd<T>(IQueryable<T> query, Expression<Func<string, T>> selector, string find)
{
// how can I combine the select expression with StartsWith?
return query.Select(selector) // IQueryable<string> now
.Where(x => x.StartsWith(find));
}
此选择器允许您将输入字符串投影到所需类型。我希望我能够正确地理解你的意图,很难看出你想要实现的目标。