我的ObjectSet过滤器未转换为SQL

时间:2013-04-29 17:30:18

标签: c# linq-to-entities linq-expressions

我已编写此过滤器以仅获取与数据库中某些时间段匹配的文档:

期间实体很简单,包含两个属性: DateFrom DateTo

我需要从lambdas构建一个过滤器,每个 Period 用于构建过滤器。

过滤器完全构建后,必须如下所示:

ObjectSet.Where(d => 
    (d.Date >= Period1.DateFrom && d.Date <= Period1.DateTo)
    || (d.Date >= Period2.DateFrom && d.Date <= Period2.DateTo)
    || (d.Date >= Period3.DateFrom && d.Date <= Period3.DateTo));

您可以猜测,我必须动态构建此过滤器,因为用于构建过滤器的提交的期间的数量可能会有所不同。 < / p>

(以下是Expression我用来组合lambdas(每个时间段都已提交以构建过滤器)

private Expression<Func<T, bool>> CombineWithOr<T>(
    Expression<Func<T, bool>> firstExpression, 
    Expression<Func<T, bool>> secondExpression)
{
    var parameter = Expression.Parameter(typeof(T), "x");

    var resultBody = Expression.Or(
        Expression.Invoke(firstExpression, parameter), 
        Expression.Invoke(secondExpression, parameter));

    return Expression.Lambda<Func<T, bool>>(resultBody, parameter);
}

在这里,我将每个lambda组合在一起,以便添加到文档过滤器:

public IList<Document> GetDocuments(IList<Periods> periods)
{
    Expression<Func<Document, bool>> resultExpression = n => false;

    foreach (var submittedPeriod in periods)
    {
        var period = submittedPeriod;
        Expression<Func<Document, bool>> expression =
            d => (d.Date >= period.DateFrom && d.Date <= period.DateTo);
        resultExpression = this.CombineWithOr(resultExpression, expression);
    }

    var query = this.ObjectSet.Where(resultExpression.Compile());
}

问题是,当我启动查询的延迟执行时......

var documents = query.ToList();

...我查看生成的SQL,没有添加任何内容到SELECT语句。


如果我执行查询而不编译生成的表达式,如下所示:

var query = this.ObjectSet.Where(resultExpression);

我得到了这个例外:

  

LINQ to不支持LINQ表达式节点类型'Invoke'   实体。

这意味着Linq-To-Entities查询提供程序不知道如何将我的过滤器转换为SQL代码。

现在困扰我的是如何将实体(文档期间)中的这种简单的DateTime比较作为我的实体架构的一部分会让提供商陷入困境?

我有什么想法可以实现这样的过滤?

2 个答案:

答案 0 :(得分:0)

在查询之前尝试.AsExpandable()。更多信息是here

答案 1 :(得分:0)

问题出在你的组合功能上。 SQL to Entities并不真正喜欢Invoke。这是一个对我有用的组合功能:

public static class ExpressionCombiner
{
    public static Expression<Func<T, bool>> Or<T>(Expression<Func<T, bool>> a, Expression<Func<T, bool>> b)
    {
        var parameter = Expression.Parameter(typeof(T), "x");
        var substituter = new SubstituteParameter(parameter, p => true);

        var resultBody = Expression.Or(
            substituter.Visit(a.Body),
            substituter.Visit(b.Body));

        Expression<Func<T, bool>> combined = Expression.Lambda<Func<T, bool>>(resultBody, parameter);
        return combined;
    }
}

public class SubstituteParameter : ExpressionVisitor
{
    private ParameterExpression toReplace;
    private Func<ParameterExpression, bool> isReplacementRequiredFunc;
    public SubstituteParameter(ParameterExpression toReplace, Func<ParameterExpression, bool> isReplacementRequiredFunc)
    {
        this.toReplace = toReplace;
        this.isReplacementRequiredFunc = isReplacementRequiredFunc;
    }

    protected override Expression VisitParameter(ParameterExpression node)
    {
        return isReplacementRequiredFunc(node) ? toReplace : node;
    }
}