【问题标题】:Extension Method convertion to LINQ Expressions and Common Methods扩展方法转换为 LINQ 表达式和常用方法
【发布时间】:2011-11-16 22:38:12
【问题描述】:

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

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”方法 之间(System.DateTime,System.DateTime,System.DateTime)'方法, 而且这个方法不能翻译成store表达式。

不过没关系

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

我想有一个通用的方法来调用。我有哪些选择?

【问题讨论】:

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


    【解决方案1】:

    不需要AsExpandable()的Brian解决方案的简单修改:

    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。
    【解决方案2】:

    由于您没有使用 LINQ to Object,因此不会执行查询,而是将其转换为 expression tree 以转换为 SQL。
    LINQ to Entities 不知道如何将您编写的方法转换为 SQL,因为您实现了它。
    我从未使用过 LINQ to Entities,但必须有一种方法来扩展表达式树构建器,以使 LINQ to Entities 能够将您的方法转换为 SQL,LINQ to NHibernate 有办法做到这一点。
    Here 是一个示例如何扩展 LINQ to Entities 提供程序。

    【讨论】:

    • 是否可以这样做:list.Where(item => targetDate.Between(item.StartDate, item.EndDate)) ?我是否需要让它返回一个表达式、Func、Predicate?
    • @Brian:您需要将 LINQ 的抽象语法树解析器扩展为实体。这可能是可能的,但一些微软产品设计得太粗心了。您应该询问实际使用 LINQ to Entities 的人。 This 是我在 google 上找到的第一个示例,但是考虑到您实际上并不知道 LINQ 在幕后是如何工作的,我怀疑如果不了解抽象语法树是什么以及它是如何在 . NET 框架。
    • 这很接近,stackoverflow.com/questions/1447635/linq-between-operator。我正在尝试获取已知目标日期的列表,并且开始和结束是列表项的属性
    • @Brian:这可能不会转换为 SQL BETWEEN 语句。
    【解决方案3】:

    我使用几种不同的方法让它工作。以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 实体进入 Linq To Objects,但执行 SQL。

    感谢您的帮助和建议

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-12-20
      • 1970-01-01
      • 2022-01-19
      • 2012-01-08
      • 2011-07-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多