若要消除可为空引用类型的警告,请从 ContosoUniversity.csproj 项目文件中删除以下行:
<Nullable>enable</Nullable>
ASP.NET Core Web 应用中的异步 EF 方法
异步编程是 ASP.NET Core 和 EF Core 的默认模式。
Web 服务器的可用线程是有限的,而在高负载情况下的可能所有线程都被占用。 当发生这种情况的时候,服务器就无法处理新请求,直到线程被释放。 使用同步代码时,可能会出现多个线程被占用但不能执行操作的情况,因为它们正在等待 I/O 完成。 使用异步代码时,当进程正在等待 I/O 完成,服务器可以将其线程释放用于处理其他请求。 因此,使用异步代码可以更有效地利用服务器资源,并且服务器可以无延迟地处理更多流量。
异步代码会在运行时引入少量开销。 流量较低时,对性能的影响可以忽略不计,但流量较高时,潜在的性能改善非常显著。
在以下代码中,async 关键字和 返回值,await
关键字和 ToListAsync
方法让代码异步执行。
public async Task<Student> OnGetAsync()
{
Students = await _context.Students.ToListAsync();
return Students;
}
async
关键字让编译器执行以下操作:- 为方法主体的各部分生成回调。
- 创建返回的 Task 对象。
- 返回类型
Task
表示正在进行的工作。 await
关键字让编译器将该方法拆分为两个部分。 第一部分是以异步方式结束已启动的操作。 第二部分是当操作完成时注入调用回调方法的地方。ToListAsync
是ToList
扩展方法的异步版本。
编写使用 EF Core 的异步代码时需要注意的一些事项:
- 只有导致查询或发送数据库命令的语句才能以异步方式执行。 这包括
ToListAsync
、SingleOrDefaultAsync
、FirstOrDefaultAsync
和SaveChangesAsync
。 不包括只会更改IQueryable
的语句,例如var students = context.Students.Where(s => s.LastName == "Davolio")
。 - EF Core 上下文并非线程安全:请勿尝试并行执行多个操作。
- 若要利用异步代码的性能优势,请验证在调用向数据库发送查询的 EF Core 方法时,库程序包(如用于分页)是否使用异步。
性能注意事项
创建 PaginatedList 类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
namespace ContosoUniversity
{
public class PaginatedList<T> : List<T>
{
public int PageIndex { get; private set; }
public int TotalPages { get; private set; }
public PaginatedList(List<T> items, int count, int pageIndex, int pageSize)
{
PageIndex = pageIndex;
TotalPages = (int)Math.Ceiling(count / (double)pageSize);
this.AddRange(items);
}
public bool HasPreviousPage => PageIndex > 1;
public bool HasNextPage => PageIndex < TotalPages;
public static async Task<PaginatedList<T>> CreateAsync(
IQueryable<T> source, int pageIndex, int pageSize)
{
var count = await source.CountAsync();
var items = await source.Skip(
(pageIndex - 1) * pageSize)
.Take(pageSize).ToListAsync();
return new PaginatedList<T>(items, count, pageIndex, pageSize);
}
}
}