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 的一部分)有另一个。它们彼此不兼容并使用不同的方法,但两者都可以根据您的场景工作。


以上是IEnumerable和IQueryable-两全其美的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>