EntityFramework Core 3.0查询
前言
随着.NET Core 3.0的发布,EF Core 3.0也随之正式发布,关于这一块最近一段时间也没太多去关注,陆续会去对比之前版本有什么变化没有,本节我们来看下两个查询。
分组
我们知道在EF Core 3.0版本之前,对于分组查询是在客户端评估,也就是说在内存中操作,在EF Core 3.0版本后对于分组查询可以翻译成SQL在数据库进行,但是事实情况真的是这样吗?接下来我们来看下吧,如下给出代码例子。
public class EFCoreDbContext : DbContext { public EFCoreDbContext() { } public DbSet<Blog> Blogs { get; set; } public DbSet<Post> Posts { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) => optionsBuilder.UseSqlServer(@"Server=.;Database=EFTest;Trusted_Connection=True;"); } public class Blog { public int Id { get; set; } public string Name { get; set; } public List<Post> Posts { get; set; } } public class Post { public int Id { get; set; } public int BlogId { get; set; } public string Title { get; set; } public string Content { get; set; } public Blog Blog { get; set; } }
接下来我们在控制台进行如下查询:
var context = new EFCoreDbContext(); var posts = context.Posts.GroupBy(d => d.BlogId) .Select(g => new { id = g.Key, count = g.Count() }) .ToList();
上述我们查询每一篇博客的文章数组,我们通过SQL Profiler跟踪到上述示例代码最终翻译成的SQL如我们所期望的那样,如下图:
假设现在有这样一个场景:查询所有博客发表的第一篇博客文章。基于这种场景我们需要对发表博客文章进行分组,然后取第一篇,所以接下来我们进行如下查询:
var context = new EFCoreDbContext(); var posts = context.Posts.GroupBy(d => d.BlogId) .Select(g => g.FirstOrDefault()) .ToList();
既然这样无法翻译,根据官方文档可以使用Linq to Object进行查询《https://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-3.0/》 ,那么我们就修改成如下代码查询看看:
var context = new EFCoreDbContext(); var posts = context.Posts.GroupBy(d => d.BlogId).AsEnumerable() .Select(d => d.FirstOrDefault()) .ToList();
咋客户端都无法支持了呢?我们只是想查询所有博客列表中第一篇文章,按照我们的理解,理论上是可以进行翻译的对不对,比如翻译成如下直接写的SQL语句:
SELECT Id,BlogId,Title,Content FROM ( SELECT * ,ROW_NUMBER() OVER ( PARTITION BY BlogId ORDER BY Id ) AS [ROW NUMBER] FROM dbo.Posts ) groups WHERE groups.[ROW NUMBER] = 1 ORDER BY groups.Id DESC
所以到这里我们大概可以猜测出EF Core对分组查询支持的并不是那么好,目前应该只支持简单的分组求和而已,稍微复杂一点则无法翻译,所以我们还是老老实实将分组还是完全放在客户端评估吧,如下:
var context = new EFCoreDbContext(); var posts = context.Posts.ToList().GroupBy(d => d.BlogId) .Select(d => d.FirstOrDefault()) .ToList();
查找
我们可以通过 EF.Functions.Like 来进行模糊查询,我们可以通过StartWith或EndWith来查询开头或结尾的数据,要是现在需要查询出博客文章标题中包含某一字符的文章列表,我们又该如何查询呢?我们想到通过IndexOf来查询,接下来我们来看看:
var context = new EFCoreDbContext(); var posts = context.Posts.Where(d => d.Title.IndexOf('C') == 2).ToList();
难道我们又只能将所有查询出来,然后在内存中操作吗?代码如下:
var context = new EFCoreDbContext(); var posts = context.Posts.ToList().Where(d => d.Title.IndexOf('C') == 2).ToList();
其实我们只要将上述单引号修改双引号即可解决完全在客户端评估的问题,如下:
var context = new EFCoreDbContext(); var posts = context.Posts.Where(d => d.Title.IndexOf("C") == 2).ToList();
根据我们的查询描述,我们明明是想查询在标题中查询指定字符,为何对字符不能支持,只支持字符串呢,不知道官方是出于何种原因。同时这里我们也注意到,无论是MySQL还是SQL Server等等,尽量不要将表中列设置为可空,即使是可空也要设置为不可空,给定一个默认值即可,一旦数据量巨大时,会发现查询很慢,因为通过IS NULL或者IS NOT NULL不走索引导致。比如上述我们查询的Title,我们无论是通过Data Annotations还是Fluent Api,都必须配置成不可空,比如这里我们通过Data Annotations配置如下:
[Required] public string Title { get; set; }
此时我们继续进行上述查询时候,会发现对空值的判断已经没有了,同时也减少了查询语句,如下:
总结
请注意上述我所演示EF Core版本为3.0.1。本节我也只是通过简单的示例稍微给大家看了EF Core 3中一些小的问题,当然可能还存在其他的问题,更多细节等我后续研究会继续给出EF Core 3.x系列文章,感谢您的阅读,若有叙述不当或错误之处,还望指正,谢谢。