我的ObjectSet过滤器没有被翻译成SQL

本文关键字:翻译 SQL ObjectSet 过滤器 我的 | 更新日期: 2023-09-27 18:11:28

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

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

我需要从lambdas构建一个过滤器,每一个周期提交一个过滤器。

过滤器,当完全构建时,必须看起来像这样:

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));

您可以猜到,我必须动态地构建此过滤器,因为提交的周期构建过滤器的数量可以变化。

(以下是我用来组合lambdas的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代码。

现在让我烦恼的是如何从实体(DocumentPeriod)进行简单的DateTime比较,它们都是我的实体模式的一部分,可以弄乱提供者?

如何实现这样的过滤?

我的ObjectSet过滤器没有被翻译成SQL

在查询前尝试.AsExpandable()。更多信息请点击

问题在于您的组合函数。SQL到实体并不像调用。下面是一个对我有用的组合函数:

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;
    }
}