1. 程式人生 > >EntityFramework Core 3.0查詢

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