扩展方法转换为LINQ表达式和常用方法

3

我有一个扩展方法,它可以执行以下操作:

public static bool Between(this DateTime target, DateTime startDate, DateTime endDate)
{
    return target >= startDate && target <= endDate;
}

我可以这样调用它:

if (expectedDate.Between(minDate, maxDate)) { do code }

我现在正在尝试在Linq/Lambda表达式中使用它,例如

    return list.Where(item => targetDate.Between(item.StartDate, item.EndDate));
OR  if (!list.Any(pp => targetDate.Between(pp.StartDate, pp.EndDate)))

在运行时,我遇到了以下错误:

LINQ to Entities不识别方法'Boolean Between(System.DateTime, System.DateTime, System.DateTime)',无法将此方法转换为存储表达式。

但这没关系。

if (!list.Any(item => targetDate >= item.StartDate && quoteDate.EventDate <=item.EndDate)))

我希望有一个通用的调用方法,我有哪些选择?

3个回答

4

这是对Brian解决方案的简单修改,不需要使用AsExpandable()

public static IQueryable<TSource> Between<TSource, TKey>(
    this IQueryable<TSource> source, TKey key,
    Expression<Func<TSource, TKey>> lowSelector,
    Expression<Func<TSource, TKey>> highSelector)
    where TKey : IComparable<TKey>
{
    Expression low = lowSelector.Body;
    Expression high = highSelector.Body;

    Expression lowerBound = Expression.LessThanOrEqual(
        low, Expression.Constant(key));
    Expression upperBound = Expression.LessThanOrEqual(
        Expression.Constant(key), high);

    var lowLambda = Expression.Lambda<Func<TSource, bool>>(
        lowerBound, lowSelector.Parameters);
    var highLambda = Expression.Lambda<Func<TSource, bool>>(
        upperBound, highSelector.Parameters);

    return source.Where(lowLambda).Where(highLambda);
}

检查翻译后的SQL,看它是否真正被翻译成了BETWEEN。 - the_drow

3

由于您没有使用LINQ to Object,查询没有被执行,而是被翻译为表达式树以便翻译为SQL。
LINQ to Entities不知道如何将您编写的方法翻译为SQL,因为您实现了它。
我从未使用过LINQ to Entities,但一定有一种方法可以扩展表达式树构建器,使LINQ to Entities能够将您的方法翻译为SQL,LINQ to NHibernate也有一种方法可以这样做。
这里是扩展LINQ to Entities提供程序的示例。


有没有可能像这样做:list.Where(item => targetDate.Between(item.StartDate, item.EndDate))?我需要让它返回一个表达式、Func、Predicate吗? - Brian
@Brian:你需要扩展LINQ to Entities的抽象语法树解析器。这可能是可行的,但有些微软产品设计得太草率了。你应该问一下那些真正使用过LINQ to Entities的人。这个是我在谷歌上找到的第一个例子,然而考虑到你实际上不知道LINQ在幕后是如何工作的,我怀疑你在不理解什么是抽象语法树以及它在.NET Framework中是如何实现的情况下将无法成功。 - the_drow
这个很接近,https://dev59.com/H3M_5IYBdhLWcg3wRw91。我正在尝试获取一个列表,其中目标日期已知,而开始和结束是列表项的属性。 - Brian
@Brian:虽然这可能无法转换为SQL BETWEEN语句。 - the_drow

1

我使用了几种不同的方法让它工作。在LINQ Between Operator的基础上,我创建了一个新的方法

public static IQueryable<TSource> Between<TSource, TKey>(this IQueryable<TSource> source, TKey key, Expression<Func<TSource, TKey>> lowSelector, Expression<Func<TSource, TKey>> highSelector)
    where TKey : IComparable<TKey>
{
    Expression low = Expression.Invoke(lowSelector, lowSelector.Parameters.ToArray());
    Expression high = Expression.Invoke(highSelector, highSelector.Parameters.ToArray());

    Expression lowerBound = Expression.LessThanOrEqual(low, Expression.Constant(key));
    Expression upperBound = Expression.LessThanOrEqual(Expression.Constant(key),  high);

    Expression<Func<TSource, bool>> lambda = Expression.Lambda<Func<TSource, bool>>(lowerBound, lowSelector.Parameters);
    Expression<Func<TSource, bool>> lambda2 = Expression.Lambda<Func<TSource, bool>>(upperBound, highSelector.Parameters);
    return source.AsExpandable().Where(lambda).Where(lambda2);
}

除非我使用AsExpandable或ToList(),否则这不起作用。

AsExpandable()来自LinkKit

ToList()将其从Linq to entities强制转换为Linq To Objects,但会执行SQL。

感谢您的帮助和建议。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接