【问题标题】:Why does this filter work on a List<T> but not on an IQueryable<T>?为什么此过滤器适用于 List<T> 而不适用于 IQueryable<T>?
【发布时间】:2015-03-06 14:02:00
【问题描述】:

我需要创建一个扩展方法,该方法将根据验证规则列表List&lt;&lt;IRule&gt;, bool&gt; 过滤集合List&lt;TSource&gt;。 但是我收到一个错误VisitSubQueryExpression method is not implemented,我看不到/找不到问题原因。

这是我的扩展方法:

public static List<TSource> ValidItems<TSource>(this IQueryable<TSource> source, 
                                  List<IValidationRule<TSource>> validationRules)
{
     return source.Where(testableItem => 
            validationRules.All(rule => rule.RulePassed(testableItem))).ToList();
}

IRule 接口:

public interface IValidationRule<T> //with implementation class ValidationRule<T>
{
    Func<T, bool> RulePassed { get; set; }
    //... +other code
}

扩展方法调用示例

//initialization of List<SampleType> listToValidate = ... 
var validItems = listToValidate.ValidItems(
                  new List<IValidationRule<SampleType>() {
                       new ValidationType<SampleType> {
                       RulePassed = (s) => string.IsNullOrWhiteSpace(SampleType.Name), 
                       //...initalize other ValidationType parameters
                       }
                   });

此示例应过滤 listToValidate 列表并删除所有 SampleType 实例,其 Name 属性为 Null 或 Whitespace

我的扩展方法有什么问题?这个“VisitSubQueryExpression 方法未实现”错误是什么意思?

如果我将其更改为 List&lt;TSource&gt; 而不是 IQueryable&lt;TSource&gt; 的扩展方法 - 它可以工作!为什么?

this List&lt;TSource&gt; 替换this IQueryable&lt;TSource&gt;,它可以工作,为什么? Where 方法应该适用于 IQueryable&lt;T&gt;(继承自 IEnumerable&lt;T&gt;),不是吗?

public static List<TSource> ValidItems<TSource>(this List<TSource> source, 
                                  List<IValidationRule<TSource>> validationRules)
{
     return source.Where(testableItem => 
            validationRules.All(rule => rule.RulePassed(testableItem))).ToList();
}

【问题讨论】:

  • 你是否直接在 linq-2-entities 查询中使用它?
  • 你为什么使用IQueryable&lt;T&gt;而不是IEnumerable&lt;T&gt;?如果您想将过滤器表达式转换为数据库可以理解的内容(例如 SQL),则前者很有用。除非您的查询提供程序对 IValidationRule 有特殊支持(几乎不可能),否则它将无法处理该表达式。
  • @CodesInChaos 我打算在 LinqToExcel 上使用它返回 'ExcelQueryable',它继承自 'QueryableBase',所以我想我可以在将结果集合转换为 List 之前进行过滤

标签: c# linq extension-methods


【解决方案1】:

您的 rule.RulePassed 无法由您的 IQueryable 提供程序翻译。

public static List<TSource> ValidItems<TSource>(this IQueryable<TSource> source, 
                              List<IValidationRule<TSource>> validationRules)
{
    var result = new List<TSource>();
    foreach (var testableItem in source)
    {
        if (validationRules.All(rule => rule.RulePassed(testableItem))
        {
            result.Add(testableItem);
        }
    }
    return result;
}

【讨论】:

  • 您提供的示例是另一个有效的“解决方法”,我只是想知道为什么它不能直接在 IQueryable 上工作 - 所以这个错误消息“VisitSubQueryExpression 方法未实现”来自IQueryable 提供者?
  • @Prokurors 它不是“来自”,您的提供商只是不支持它。
  • 好的,所以在这种情况下,我需要在内存中加载所有列表(使用 IEnumerable 而不是 IQueryable),然后才使用我的验证规则对其进行过滤......这有点奇怪,因为(IQueryable ).Where 与 Func 表达式一起使用,那么为什么它不能与具有 Func 参数的扩展方法一起使用?好的,就我而言,它是 List>... 但除此之外它似乎是一样的...
  • 您希望它如何在您的数据库端工作?它可能没有实现rule.RulePassed...
  • @Prokurors 如果您使用一个委托类型,则将选择 IEnumerable 重载。对于 IQueryable,您需要使用 Expression 类型。
【解决方案2】:

IQueryable&lt;T&gt; 实现 IEnumerable&lt;T&gt; 但反之则不然。而List&lt;T&gt; 没有实现IQueryable&lt;T&gt;

这是 List 公开的接口列表:

由于IQueryableIList 都继承自IEnumerable&lt;T&gt;,您可以使用它并调用.AsQueryable 来转换这两种情况。

【讨论】:

  • 嗯...也许你认为扩展方法应该是 IEnumerable 然后使用它 (ilist).AsEnumerable().(扩展方法) 和 (iqueryable).AsEnumerable().(扩展方法)?
  • IQueryable 已经实现了 IEnumerable 并且 IList 也实现了。所以你不需要强制转换或调用 AsEnumerable。
【解决方案3】:

List&lt;T&gt; 没有实现IQueryable&lt;T&gt;,所以你不能调用IQueryable&lt;T&gt; 上定义的扩展方法。

【讨论】:

  • 其实我是在尝试调用 ExcelQueryable 上的扩展方法(继承自 IQueryable 的 QueryableBase),List 只是返回类型
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-25
  • 1970-01-01
  • 1970-01-01
  • 2011-05-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多