【问题标题】:My ObjectSet filter doesn't get translated into SQL我的 ObjectSet 过滤器没有被翻译成 SQL
【发布时间】:2013-04-29 17:30:18
【问题描述】:

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

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

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

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

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 不支持 LINQ 表达式节点类型“Invoke” 实体。

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

现在困扰我的是,从属于我的 Entity 架构的实体(DocumentPeriod)进行如此简单的 DateTime 比较如何会弄乱提供者?

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

【问题讨论】:

  • 你试过this.ObjectSet.Where(resultExpression)吗?
  • @svick :如果我不编译 resultExpression,我会收到以下异常:LINQ to Entities 不支持 LINQ 表达式节点类型“Invoke”。
  • @JoanLeaven 这意味着您实际上是第一次使用查询提供程序,而不仅仅是在 linq 中对对象执行全部操作。现在您需要弄清楚如何创建查询提供者知道如何翻译的查询。
  • @JoanLeaven 不是它不知道如何翻译的单个条件表达式,而是你将它们组合起来的方法是创建一个它不知道如何解析的复杂结构。
  • @Servy :这是我发现动态组合我的谓词的唯一方法。你知道更简单或更好的方法吗?

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


【解决方案1】:

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

【讨论】:

  • 请注意,我想在不使用 LinqKit 库的情况下实现这一目标。 ObjectSet 没有这样的方法可用。但是,它有 ToExpandable(),我试过这样:var query = this.ObjectSet.ToExpandable().Where(resultExpression); 我得到相同的异常:LINQ to Entities 不支持 LINQ 表达式节点类型“Invoke”。
  • @JoanLeaven 为什么要在没有 LinqKit 的情况下这样做?这正是 LinqKit 的用途。
  • @svick :我简直不敢相信仅使用开箱即用的表达式是不可能的。我们当前的项目依赖于一个类似于 LinqKit 的库:它来自名为 EekSoft 的个人或公司。我的目标是从项目中删除它。它有点晦涩难懂,并且会生成垃圾和格式错误的 SQL 代码,以至于它需要永远在服务器上执行。
  • @JoanLeaven 这不是不可能的,但也不是微不足道的。基本上,这意味着您必须重新实现 LinqKit。而且我认为这不是一个好主意。
  • @svick。我懂了。我也不喜欢重新发明轮子。我对 EekSoft 以及它如何通过简单的 linq 查询毒害我们的数据访问层感到非常痛苦,这些查询平均转化为 150 KB 的查询,并且使用它真的很痛苦。我想我应该试试 LinqKit。谢谢。
【解决方案2】:

问题在于您的组合功能。 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;
    }
}

【讨论】:

    猜你喜欢
    • 2021-03-29
    • 1970-01-01
    • 2017-06-26
    • 1970-01-01
    • 1970-01-01
    • 2023-03-04
    • 1970-01-01
    • 2023-03-13
    • 1970-01-01
    相关资源
    最近更新 更多