异步编程

当在数据库中执行查询时,异步操作可避免阻止线程。 对于在富客户端应用程序中保持响应式 UI,异步操作很重要,还可以增加 Web 应用程序中的吞吐量,从而释放线程来为 Web 应用程序中的其他请求提供服务。

按照 .NET 标准,EF Core 将为所有执行 I/O 的同步方法提供异步对应项。 它们具有与同步方法相同的效果,并且可与 C# asyncawait 关键字结合使用。 例如,不使用在执行数据库 I/O 时将阻塞线程的 DbContext.SaveChanges,而是改为使用 DbContext.SaveChangesAsync:

var blog = new Blog { Url = "http://sample.com" };
context.Blogs.Add(blog);
await context.SaveChangesAsync();

有关详细信息,请参阅通用 C# 异步编程文档

警告

EF Core 不支持在同一上下文实例上运行多个并行操作。 应始终等待操作完成,然后再开始下一个操作。 这通常是通过在每个异步操作上使用 await 关键字完成的。

警告

遗憾的是,Microsoft.Data.SqlClient 的异步实现存在一些已知问题(例如 #593#601 等)。 如果遇到意外的性能问题,请尝试改用同步命令执行,尤其是在处理大型文本或二进制值时。

注意

EF Core 将取消令牌传递给正在使用的基础数据库提供程序(例如 Microsoft.Data.SqlClient)。 可以接受这些令牌,也可以不接受 - 请参阅数据库提供程序的文档。

异步 LINQ 运算符

为了支持以异步方式执行 LINQ 查询,EF Core 提供了一组可执行查询并返回结果的异步扩展方法。 这些与标准同步 LINQ 运算符对应的运算符包括 ToListAsyncSingleAsyncAsAsyncEnumerable 等:

var blogs = await context.Blogs.Where(b => b.Rating > 3).ToListAsync();

请注意,某些 LINQ 运算符(如 WhereOrderBy)没有对应的异步版本,因为这些方法仅用于构建 LINQ 表达式树,而不会导致在数据库中执行查询。 只有导致查询执行的操作符才有异步对应项。

重要

EF Core 异步扩展方法在 Microsoft.EntityFrameworkCore 命名空间中定义 。 必须导入此命名空间才能使这些方法可用。

客户端异步 LINQ 运算符

上述异步 LINQ 运算符只能用于 EF 查询,你不能将其用于客户端 LINQ to Objects 查询。 若要在 EF 之外执行客户端异步 LINQ 操作,请使用 System.Linq.Async;对于在客户端上执行无法在服务器上转换为评估的操作,此包非常有用。

在 EF Core 6.0 及更低版本中,可惜的是,引用 System.Linq.Async 会导致应用到 EF 的 DbSet 的 LINQ 运算符出现不明确的调用编译错误;这使得很难在同一个项目中同时使用 EF 和 System.Linq.Async。 若要解决此问题,请将 AsQueryable 添加到 DbSet:

var groupedHighlyRatedBlogs = await context.Blogs
    .AsQueryable()
    .Where(b => b.Rating > 3) // server-evaluated
    .AsAsyncEnumerable()
    .GroupBy(b => b.Rating) // client-evaluated
    .ToListAsync();