在这种情况下,我需要为 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
实现新的变体。
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 扩展方法应该没有其他副作用。