Linq to Entities中where子句中的扩展方法

7
在LINQ to Entities中,我们需要一个类似于"SQL like"的方法。我们已经实现了自己的扩展方法到IQueryable,因为contains方法对我们不起作用,因为它不接受像'%a%b%'这样的模式。
创建的代码如下:
private const char WildcardCharacter = '%';

public static IQueryable<TSource> WhereLike<TSource>(this IQueryable<TSource> _source, Expression<Func<TSource, string>> _valueSelector, string _textSearch)
{
    if (_valueSelector == null)
    {
        throw new ArgumentNullException("valueSelector");
    }

        return _source.Where(BuildLikeExpressionWithWildcards(_valueSelector, _textSearch));
}

private static Expression<Func<TSource, bool>> BuildLikeExpressionWithWildcards<TSource>(Expression<Func<TSource, string>> _valueSelector, string _textToSearch)
{
    var method = GetPatIndexMethod();

    var body = Expression.Call(method, Expression.Constant(WildcardCharacter + _textToSearch + WildcardCharacter), _valueSelector.Body);

    var parameter = _valueSelector.Parameters.Single();
    UnaryExpression expressionConvert = Expression.Convert(Expression.Constant(0), typeof(int?));
    return Expression.Lambda<Func<TSource, bool>> (Expression.GreaterThan(body, expressionConvert), parameter);
}

private static MethodInfo GetPatIndexMethod()
{
    var methodName = "PatIndex";

    Type stringType = typeof(SqlFunctions);
    return stringType.GetMethod(methodName);
}

这个代码可以正常工作,且完全在SqlServer中执行。但是现在我们想要在where子句中使用这个扩展方法:

myDBContext.MyObject.Where(o => o.Description.Like(patternToSearch) || o.Title.Like(patterToSerch));

问题在于where子句中使用的方法必须返回bool结果,如果与' || '等运算符一起使用,我不知道如何使我创建的代码返回bool并保持在sqlserver中执行。我想我必须将BuildLinqExpression方法返回的表达式转换为bool,但我不知道如何做到这一点。
总之!首先,在Linq to Entities中创建自己的扩展方法以在SqlServer中执行代码是否可能?如果可能,我该怎么做?
感谢您的帮助。

2
你尝试使用SqlMethods.Like了吗? - Sergey Kalinichenko
@dasblinkenlight SqlMethods 不能在 Linq to Entity 中使用。 - Marc Cals
2个回答

3
不可以让EF处理自定义扩展方法,即使您的代码构建了可以用于EF的表达式。要么直接在EF Where表达式中使用SqlFunctions方法,要么使用ExpressionVisitor将多个表达式组合成一个复合表达式(OrElse / AndAlso),然后对它们执行||操作(注意,这样做不能满足你想要的代码要求,但是可以使用你的两个方法并执行||操作-看起来会很复杂)。第一种方法更简单、更清晰。

如果我没错的话,SqlMethods不能在Linq to Entities中使用,我已经尝试过了,它会崩溃。其他用户也报告了同样的问题https://dev59.com/qE3Sa4cB1Zd3GeqPrQHf - Marc Cals
@Marc 抱歉,我可能是指 SqlFunctions - 也就是你已经在幕后使用的代码。 - Marc Gravell
@Marc,如果那是您自己的类,则无能为力。也许只需编写 SQL 并通过 EF 推送它下去,而不是尝试编写 LINQ。 - Marc Gravell
好的,谢谢你的帮助。不直接使用SqlFunctions的原因是为了尽可能保持代码的清晰简洁。 - Marc Cals
我有一个相关但不同的问题,如果有人能帮忙:http://stackoverflow.com/questions/40620704/how-can-i-modify-the-logic-of-a-linq-to-entities-where-expression-inside-a-method - Patrick Szalapski

2

这里有答案:https://dev59.com/-2445IYBdhLWcg3wapvF#10726256

他的代码:http://pastebin.com/4fMjaCMV

可以将扩展方法标记为[Expandable],只要它返回的表达式与 linq2entities 兼容,就会用内部表达式替换您的函数调用。请注意,内联 lambda 会导致错误,因此我不得不将它们声明为局部变量或静态变量,例如这里的 IsCurrent 变量:

static Expression<Func<PropertyHistory, bool>> IsCurrent = (p) => p.Starts <= DateTime.Now;

[ExpandableMethod]
public static IQueryable<PropertyHistory> AsOf(this IQueryable<PropertyHistory> source)
{
  return source.Where(IsCurrent);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接