1

我编写了这个过滤器来只从数据库中获取与特定时间段匹配的文档:

Period实体很简单,包含两个属性:DateFromDateTo

我需要从 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));

如您所料,我必须动态构建此过滤器,因为构建过滤器的提交周期数可能会有所不同。

(以下是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 Entities 不支持 LINQ 表达式节点类型“Invoke”。

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

现在困扰我的是,从属于我的实体模式的实体(文档期间)进行的这种简单的 DateTime 比较如何会弄乱提供者?

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

4

2 回答 2

0

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

于 2013-04-29T18:32:09.717 回答
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;
    }
}
于 2013-04-29T23:13:24.937 回答