Linq to entities使用`Func`在select语句中创建属性,该语句生成一个匿名对象

时间:2017-05-04 11:49:53

标签: c# entity-framework linq linq-to-entities

我正在研究一个简单的文本搜索方法,使用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&gt;哪里&gt;将序列选择为一个扩展方法,可以将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实体不知道如何处理.Invoke()的{​​{1}}方法。我还有其他几个问题,我应该使用Func而不仅仅是Expressiong<Func<T,string>>,但我发现做这项工作的唯一方法就是替换Select的整个主体带有表达式的语句,然后要求表达式返回具有值和搜索属性的对象。

有没有办法使用FuncFunc只在匿名对象中创建搜索属性的值?

1 个答案:

答案 0 :(得分:0)

正如您所提到的,您需要在函数中接受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)));
}