Linq to entity 使用“Func”在生成匿名对象的选择语句中创建属性
Posted
技术标签:
【中文标题】Linq to entity 使用“Func”在生成匿名对象的选择语句中创建属性【英文标题】:Linq to entities use `Func` to create property in a select statement that produces an anonymous object 【发布时间】:2017-10-02 14:06:09 【问题描述】:我正在研究一种使用 linq 到实体的简单文本搜索方法,我想在几个看起来像这样的地方重用它:
IQueryable<MyObject> query = db.MyObjects.Where(o => /* some criteria */);
query = query
.Select(o => new
value = o,
search = o.Foo + " " + o.Bar.X + " " + o.Bar.Y
)
.Where(o => o.search.contains("foo"))
.Select(o => o.value);
query = query.Where(o => /* some other criteria */);
我希望能够将 Select > Where > Select 序列转换为一个扩展方法,该方法可以给定一个 Func
将 search
属性拉在一起,如下所示:
public static IQueryable<T> Search<T>(this IQueryable<T> query, Func<T, string> selector, string phrase)
return query
.Select(o => new
value = o,
search = selector.Invoke(o)
)
.Where(o => o.search.Contains(phrase))
.Select(o => o.value);
然后可以这样使用:
query.Search(o => o.Foo + " " + o.Bar.X + " " + o.Bar.Y, "foo");
我认为这很简洁,它编译得很愉快,但它不会运行,因为 Linq to entity 不知道如何处理 Func
的 .Invoke()
方法。我还有其他一些 SO 问题,我可能应该使用 Expressiong<Func<T,string>>
而不仅仅是 Func
,但我发现使这项工作起作用的唯一方法是用表达式替换 Select 语句的整个主体,然后要求表达式返回具有 value 和 search 属性的对象。
有没有办法使用Func
或Expression
只在匿名对象中创建搜索属性的值?
【问题讨论】:
正在转换为 IEnumerableWhere
编写该查询,如o => (o.Foo + " " + o.Bar.X + " " + o.Bar.Y).Contains("foo")
。鉴于该查询编写起来非常简单,我也认为不需要扩展方法来帮助您编写它。
@Servy 上面的代码说明了我正在尝试做的事情,而不是生产代码。我希望能够根据我正在查询的实体类型向它抛出不同的Func
s。此外,在我的真实代码中,.Where(o => o.search.Contains(phrase))
将扩展为使用多个搜索词,并根据它们的匹配程度对结果进行排名。以上只是一个简化的例子来说明手头的问题。
【参考方案1】:
正如您所提到的,您需要在函数中接受 Expression
,而不是 Func
,EF 才能实际翻译查询。
您正在寻找的是编写表达式的能力,就像您可以编写函数一样:
public static Expression<Func<T, TResult>> Compose<T, TIntermediate, TResult>(
this Expression<Func<T, TIntermediate>> first,
Expression<Func<TIntermediate, TResult>> second)
return Expression.Lambda<Func<T, TResult>>(
second.Body.Replace(second.Parameters[0], first.Body),
first.Parameters[0]);
这依赖于以下方法将一个表达式的所有实例替换为另一个:
public class ReplaceVisitor:ExpressionVisitor
private readonly Expression from, to;
public ReplaceVisitor(Expression from, Expression to)
this.from = from;
this.to = to;
public override Expression Visit(Expression ex)
if(ex == from) return to;
else return base.Visit(ex);
public static Expression Replace(this Expression ex,
Expression from,
Expression to)
return new ReplaceVisitor(from, to).Visit(ex);
现在你可以很容易地编写你的方法了:
public static IQueryable<T> Search<T>(this IQueryable<T> query,
Expression<Func<T, string>> selector,
string phrase)
return query.Where(selector.Compose(search => search.Contains(phrase)));
【讨论】:
以上是关于Linq to entity 使用“Func”在生成匿名对象的选择语句中创建属性的主要内容,如果未能解决你的问题,请参考以下文章
在CQ中使用动态表达式在LINQ to Entities中使用INT列
LINQ to Entities 不支持 LINQ 表达式节点类型“Invoke”