前言
隨着.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系列文章,感謝您的閱讀,若有敘述不當或錯誤之處,還望指正,謝謝。
