我应该从存储库返回任务<IEnumerable<T>>还是IAsyncEnumerable<T>?



在EF Core中仓库的正确实现方式是什么?

public IAsyncEnumerable<Order> GetOrder(int orderId)
{
return blablabla.AsAsyncEnumerable();
}

public Task<IEnumerable<Order>> GetOrder(int orderId)
{
return blablabla.ToListAsync();
}

调用AsAsyncEnumerable()是性能明智的吗?这种方法安全吗?一方面,它不创建List<T>对象,所以它应该稍微快一点。但是从order hand来看,查询并没有实现,所以我们延迟了SQL的执行,并且结果可以在此期间更改。

根据源代码,.ToListAsync将在内部使用IAsyncEnumerable,因此在一个或另一个中没有太多的性能优势。但是.ToListAsync.ToArrayAsync的一个重要特性是消去。

public static async Task<List<TSource>> ToListAsync<TSource>(
this IQueryable<TSource> source,
CancellationToken cancellationToken = default)
{
var list = new List<TSource>();
await foreach (var element in source.AsAsyncEnumerable().WithCancellation(cancellationToken))
{
list.Add(element);
}
return list;
}

List基本上会将所有内容保存在内存中,但只有当列表非常大时,它可能会引起严重的性能问题。在这种情况下,您可以考虑分页您的大响应。

public Task<List<Order>> GetOrders(int orderId, int offset, int limit)
{
return blablabla.Skip(offset).Take(limit).ToListAsync();
}

这个决定实际上取决于你是想缓冲还是流。

如果你想缓冲区
如果想要流式传输,请使用ToList()ToListAsync()。结果,使用AsEnumerable()AsAsyncEnumerable()

From the docs:

缓冲是指将所有查询结果加载到内存中,而流意味着EF每次只向应用程序传递一个结果,而不会将整个结果集包含在内存中。原则上,流查询的内存需求是固定的——无论查询返回1行还是1000行,它们都是相同的;另一方面,缓冲查询返回的行越多,需要的内存就越多。对于产生大型结果集的查询,这可能是一个重要的性能因素。

一般来说,最好是流,除非你需要缓存。

当你流式传输时,一旦数据被读取,你就不能在不再次访问DB的情况下再次读取它。因此,如果您需要多次读取相同的数据,则需要使用缓冲区。

如果一个存储库流一个IEnumerable,调用者可以选择通过调用ToList()(或IAsyncEnumerable上的ToListAsync())来缓冲它。如果存储库选择返回illist,则失去这种灵活性。

因此,要回答您的问题,您最好是对存储库流式处理结果。并让调用者决定是否要缓冲。


如果在项目上工作的团队对流语义不满意,或者如果大多数代码已经缓冲,那么用AsStream之类的东西来后缀流方法可能是有意义的。GetOrdersAsStream())这样他们就知道他们不应该枚举它超过一次。

所以存储库可以有:

async Task<List<Order>> GetOrders() => await GetOrdersAsStream.ToListAsync();
IAsyncEnumerable<Order> GetOrdersAsStream() => ...

最新更新