Jan*_*ski 14 .net asp.net entity-framework-core .net-core asp.net-core
EF Core 中存储库实现的正确方法是什么?
public IAsyncEnumerable<Order> GetOrder(int orderId)
{
return blablabla.AsAsyncEnumerable();
}
Run Code Online (Sandbox Code Playgroud)
或者
public Task<IEnumerable<Order>> GetOrder(int orderId)
{
return blablabla.ToListAsync();
}
Run Code Online (Sandbox Code Playgroud)
打电话是否明智AsAsyncEnumerable()
?这种方法安全吗?一方面它不会创建List<T>
对象,所以它应该稍微快一些。但从订单角度来看,查询并未具体化,因此我们推迟了 SQL 执行,同时结果可能会发生变化。
根据消息来源 .ToListAsync
,无论如何都会IAsyncEnumerable
在内部使用,因此其中之一并没有太多的性能优势。但.ToListAsync
or的一个重要特征.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;
}
Run Code Online (Sandbox Code Playgroud)
列表基本上将所有内容保存在内存中,但只有当列表非常大时,才可能是一个严重的性能问题。在这种情况下,您可以考虑对您的大响应进行寻呼。
public Task<List<Order>> GetOrders(int orderId, int offset, int limit)
{
return blablabla.Skip(offset).Take(limit).ToListAsync();
}
Run Code Online (Sandbox Code Playgroud)
这个决定实际上取决于您是否希望缓冲或流式传输。
如果要缓冲结果,请使用ToList()
或ToListAsync()
。
如果您想流式传输结果,请使用AsEnumerable()
或AsAsyncEnumerable()
。
来自文档:
缓冲是指将所有查询结果加载到内存中,而流式传输意味着 EF 每次向应用程序传递一个结果,而不会在内存中包含整个结果集。原则上,流式查询的内存需求是固定的——无论查询返回 1 行还是 1000 行,它们都是相同的;另一方面,缓冲查询返回的行数越多,需要的内存就越多。对于产生大型结果集的查询,这可能是一个重要的性能因素。
一般来说,除非需要缓冲,否则最好进行流式传输。
当您进行流式传输时,一旦读取了数据,就无法在不再次访问数据库的情况下再次读取它。因此,如果您需要多次读取相同的数据,则需要缓冲。
如果存储库流式传输 a IEnumerable
,调用者可以选择通过调用ToList()
(或ToListAsync()
on IAsyncEnumerable
) 来缓冲它。如果存储库选择返回 IList,我们就会失去这种灵活性。
因此,要回答您的问题,您最好向存储库传输结果。并让调用者决定是否要缓冲。
如果从事该项目的团队对流语义不熟悉,或者大多数代码已经缓冲,那么在流式传输的方法后面添加类似AsStream
(例如GetOrdersAsStream()
)的后缀可能是有意义的,以便他们知道他们不应该枚举不止一次。
因此,存储库可以具有:
async Task<List<Order>> GetOrders() => await GetOrdersAsStream.ToListAsync();
IAsyncEnumerable<Order> GetOrdersAsStream() => ...
Run Code Online (Sandbox Code Playgroud)