IEnumerable和IQueryable-两全其美
c#
出于性能原因,我的许多服务现在都返回 IQueryables,因此调用者可以在它们被具体化之前决定要过滤哪些元素。出于设计原因(接口等),我更愿意返回 IEnumerables,但这确实消除了 IQueryables 的优点:完整结果集的物化加上没有-Async方法。
我已经开始编写一些更喜欢 IQueryable 而不是 IEnumerable 的扩展方法,例如:
public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Expression<Func<TSource, bool>> predicate)
=> source is IQueryable<TSource> ? Queryable.Where(source as IQueryable<TSource>, predicate) : Enumerable.Where(source, predicate.Compile());
public static Task<TSource[]> ToArrayAsync<TSource>(this IEnumerable<TSource> source)
=> (source as IQueryable<TSource>)?.ToArrayAsync() ?? Task.FromResult(source.ToArray());
我不能是唯一一个思考这个问题的人。这些方法有什么改进吗?是否已经有一个很好的收藏/解决方案?或者这是一个糟糕的设计?所以请告诉我原因。
谢谢!
回答
就个人而言,这会让我变得谨慎;序列和查询是相似的,但足够不同,我非常想确定它是如何处理的。与仅在捕获上下文上使用直接编译器生成的方法相比,生成表达式树然后编译它也涉及一些非平凡的开销。如果将表达式树用作查询组合和翻译的一部分,但在序列情况下不需要。
也许更明显的方法是使用AsQueryable()序列来模拟真正的查询。
但是:我想你所拥有的应该可以工作。作为一个小语法说明,在最近的 C# 版本中,您可以捕获类型化值作为is避免双重转换的一部分:
public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Expression<Func<TSource, bool>> predicate)
=> source is IQueryable<TSource> q ? Queryable.Where(q, predicate) : Enumerable.Where(source, predicate.Compile());
尝试打开布尔标志也可能很有趣Compile()- 使用解释模式会降低执行速度,但可以避免 IL 生成。这对小数据集可能是积极的影响,而对大数据集可能是消极的。
附带说明:可查询源有多种异步实现;EF Core 有一个,IX(Reactive 的一部分)有另一个。它们彼此不兼容并使用不同的方法,但两者都可以根据您的场景工作。