在这种情况下,我需要为 IEnumerable 和 IQueryable 创建两种不同的扩展方法

本文关键字:两种 创建 方法 扩展 IQueryable 这种情况下 IEnumerable | 更新日期: 2023-09-27 17:56:48

假设我需要一个扩展方法,它只从不同的来源中选择所需的属性。源可以是数据库或内存中集合。所以我定义了这样的扩展方法:

 public IQueryable<TResult> SelectDynamic<TResult>(
            this IQueryable<T> source,
            ...)

这适用于IQueryable s。但是,我也必须调用此函数IEnumerable s。

在这种情况下,我可以在.AsQueryable()的帮助下调用它:

  myEnumerable.AsQueryable()
        .SelectDynamic(...)
        .ToList();

两者都工作正常。如果两者都工作正常,在哪些条件下我必须为同一目的创建两种不同的扩展方法,一种用于IEnumerable,另一种用于IQueryable

我的方法必须在Queryable的情况下将查询发送到数据库。

例如,下面是命名空间内.Select扩展方法System.Linq源代码:

  • IEnumerable 的.Select
  • IQueryable的.Select

再次重复我的主要问题:

我的方法必须在Queryable的情况下向数据库发送查询,但在使用IEnumerable时则不然。现在,我正在使用AsQueryable()作为枚举项。因为,我不想为Enumerable编写相同的代码.它会有一些副作用吗?

在这种情况下,我需要为 IEnumerable 和 IQueryable 创建两种不同的扩展方法

如果您的代码仅在其处理的对象加载到内存中时才实际工作,只需提供 IEnumerable 变体,并让您的调用方决定何时要将IQueryable转换为内存中IEnumerable

通常,除非

编写新的数据库提供程序,否则不会围绕IQueryable实现新的变体。

myEnumerable.AsQueryable() 返回一个自定义对象:new EnumerableQuery<TElement>(myEnumerable);(源代码)

这个EnumerableQuery类实现IEnumerable<T>IQueryable<T>

当使用 .AsQueryable()EnumerableQuery结果作为IEnumerable时,接口方法的实现IEnumerable<T>.GetIterator()简单地返回原始源迭代器,因此没有变化和最小的开销。

当使用.AsQueryable()的结果作为IQueriable时,接口属性的实现IQueriable.Expression只是返回Expression.Constant(this),准备在以后使用整个表达式树时作为IEnumerable进行计算。

(据我所知,当EnumerableQuery直接从IEnumerable构造时,EnumerableQuery的所有其他方法和代码路径并不真正相关)

如果我理解正确,您已经实现了您的方法selectDynamic<TResult>(),即您在方法中构造一个表达式树,该表达式树在编译时会产生所需的结果。

据我了解源代码,当您调用例如 myEnumerable.AsEnumerable().selectDynamic().ToList(),您构造的表达式树是在myEnumerable上编译和执行的,并且总开销应该相当小,因为所有这些工作每个查询只完成一次,而不是每个元素一次。

所以我认为像这样实现你的 IEnumerable 扩展方法没有错:

public IEnumerable<TResult> SelectDynamic<TResult>(
        this IEnumerable<T> source,...)
    return source.AsQueryable().SelectDynamic();
}

有一些轻微的开销,因为每次调用此方法时都会编译一次查询,并且我不确定 JITer 是否足够聪明来缓存此编译。但我认为在大多数情况下这不会引起注意,除非您每秒执行此查询一千次。

除了轻微的性能问题外,以这种方式实现 IEnumerable 扩展方法应该没有其他副作用。