IEnumerable 和 IQueryable - 两全其美

IEnumerable and IQueryable - the best of both worlds

出于性能原因,我的许多服务现在都在 returning IQueryables,因此调用者可以决定在具体化之前过滤哪些元素。出于设计原因(接口等),我更喜欢 return 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());

我不可能是唯一一个在想这个的人。这些方法有什么改进吗?那里已经有一个好的集合/解决方案了吗?或者这是一个糟糕的设计?所以请告诉我原因。

谢谢!

就我个人而言,这会让我变得谨慎; sequences 和 queries 相似但不同,我非常想确定它是如何处理的。生成表达式树然后编译它也有一些不平凡的开销,与仅在表达式树的捕获-context.the 开销上使用直接编译器生成的方法是合理的,如果它将是用作查询组合和翻译的一部分,但在序列情况下不需要。

也许更明显的方法是在序列上使用 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 的一部分)有另一个。它们彼此不兼容并使用不同的方法,但两者都可以根据您的情况使用。