EFCore_IQueryable
IQueryable与IEnumerable
不同的Where方法
1、对于普通集合和DbSet调用的Where方法,虽然用起来一样,但是“转到定义”后看到的是不同的方法。
2、普通集合的版本(IEnumerable)是在内存中过滤(客户端评估),而IQueryable版本则是把查询操作翻译成SQL语句(服务器端评估)。
如果强制使用IEnumerable的版本,两个方式会生成不同的SQL语句。
Program.cs:
1 | static void Main(string[] args) { |
IEnumerable版本:
把所有数据放到客户端再进行筛选
1 | SELECT `t`.`Id`, `t`.`Message`, `t`.`TheArticleId` |
IQueryable版本:
数据在数据库进行筛选然后传给客户端
1 | SELECT `t`.`Id`, `t`.`Message`, `t`.`TheArticleId` |
有时需要客户端评估(很少)
1、如果有些语句在服务器执行时占用资源特别多,可以考虑采用客户端评估。
2、如果有些语句特别复杂,无法转为SQL语句,可以考虑采用客户端评估。
IQueryable延迟执行
IQueryable只查询不遍历,就不会执行SQL语句。
IQueryable表示一个可以放到数据库执行的操作,它没有立即执行只是可以被执行。
对IQueryable接口调用非终结方法时不会执行查询,而调用终结方法时会立即执行查询。
终结方法:遍历、ToArray()、ToList()、Min()、Max()、Count()等。
非终结方法:GroupBy()、OrderBy()、Include()、Skip()、Take()等。
简单判断:如果一个方法的返回值为IQueryable那么这个操作一般为非终结方法,否则就是终结方法。
IQueryable是可以复用的,所以在实际执行终结方法之前可以分部构建IQueryable,常用于用户自定义动态查询规则。
分页查询
通过Skip(3).Take(8)进行分页查询,最好显式指定排序规则。
需要知道满足条件的数据的总条数可以通过LongCount(),因为Count()返回的数据类型是int,数据过多的时候会超出int的最大值,而LongCount()获取的数据类型是long。
1 | static void Main(string[] args) { |
IQueryable一次性加载
DataReader与DataTable
DataReader:分批从数据库服务器读取数据。内存占用小、DB连接占用时间长;
DataTable:把所有数据都一次性从数据库服务器都加载到客户端内存中。内存占用大,节省DB连接;
IQueryable内部就是在调用DataReader进行数据读取。
如何一次性加载
使用IQueryable的ToArray()、ToArrayAsync()、ToList()、ToListAsync()等方法可以一次性加载数据到内存中。
1 | static void Main(string[] args) { |
何时需要一次性加载
1:遍历IQueryable并且进行数据处理的过程很耗时,为了不占用服务器资源时可以使用。
2:如果方法需要返回查询结果,并且在方法里销毁DbContext的话,是不能返回IQueryable的。必须一次性加载返回。
1 | static void Main(string[] args) { |
3:多个IQueryable的遍历嵌套。很多数据库的ADO.NET Core Provider是不支持多个DataReader同时执行的。
在SqlServer中可以通过在连接字符串中添加MultipleActiveResultSets=true使其支持DataReader同时执行。对于别的数据库可以转换为DataTable模式再执行。
1 | static void Main(string[] args) { |
EF Core的异步方法
异步方法
1、SaveChanges()、SaveChangesAsync()
2、异步方法大部分是定义在Microsoft.EntityFrameworkCore这个名称空间下EntityFrameworkQueryableExtensions等类中的拓展方法,记得using.。
3、一般终结方法都有对应的异步方法,而非终结方法如GroupBy、OrderBy、Join、Where等非终结方法没有异步方法。
异步遍历IQueryable
1、ToListAsync()、ToArrayAsync()。
1 | static async Task Main(string[] args) { |
2、await foreach+AsAsyncEnumerable()
1 | static async Task Main(string[] args) { |
一般没必要异步遍历。