我的ObjectSet过滤器没有被翻译成SQL
本文关键字:翻译 SQL ObjectSet 过滤器 我的 | 更新日期: 2023-09-27 18:11:28
我编写了这个过滤器,以便只从数据库中获取与特定时间段匹配的文档:
Period实体很简单,包含两个属性:DateFrom和DateTo。
我需要从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代码。
现在让我烦恼的是如何从实体(Document和Period)进行简单的DateTime比较,它们都是我的实体模式的一部分,可以弄乱提供者?
如何实现这样的过滤?
在查询前尝试.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;
}
}