asp.net core系列 32 EF查詢數據 必備知識(1)


一.查詢的工作原理

  Entity Framework Core 使用語言集成查詢 (LINQ) 來查詢數據庫中的數據。 通過 LINQ 可使用 C#(或你選擇的其他 .NET 語言)基於派生上下文和實體類編寫強類型查詢。 LINQ 查詢的表示形式會傳遞給數據庫提供程序,進而轉換為特定的數據庫查詢語言(例如,適用於關系數據庫的 SQL)。

 

  1.1 查詢的生命周期, 下面是每個查詢所經歷的過程概述:

    (1) LINQ 查詢由 E F處理,用於生成已准備好的表示形式,由數據庫提供程序處理。緩存結果,以便每次執行查詢時都不需要執行此處理。

    (2) 結果將傳遞給數據庫提供程序

      a.數據庫提供程序會識別出查詢的哪些部分可以在數據庫中求值。

      b. 查詢的這些部分會轉換為特定數據庫的查詢語言(例如,關系數據庫的 SQL)

      c. 將一個或多個查詢發送到數據庫並返回結果集(結果是來自數據庫的值,而不是實體實例)

    (3) 返回結果集處理

      a.如果這是跟蹤查詢,EF會檢查數據是否代表一個實體,已存在於上下文實例的更改跟蹤器中。

        如果是,則會返回現有實體

        如果不是,則會創建新實體、設置更改跟蹤並返回該新實體

      b.如果這是非跟蹤查詢,EF 會檢查數據是否表示此查詢結果集中的現有實體

        如果是,則會返回現有實體

        如果不是,則會創建新實體並返回該新實體

 

  1.2 執行查詢時:

    調用LINQ運算符時,只會構建查詢在內存中的表示形式。 只有在使用結果時,查詢才會發送到數據庫。觸發查詢發送到數據庫的最常見操作如下:

    (1) 在 for 循環中循環訪問結果

         var blogs = from b in BloggingContext.Blogs
                     select {....}

            //觸發數據庫查詢
            foreach (var item in blogs)
            {
                int maxID = item.ID;
            }

    (2) 使用 ToList、ToArray、Single、Count 等操作都會觸發數據庫查詢

        BloggingContext.Blogs.ToList();
        BloggingContext.Blogs.ToArray();
        BloggingContext.Blogs.Count();
        BloggingContext.Blogs.Single();
        BloggingContext.Blogs.First();

    (3) 將查詢結果數據綁定到 UI

 

二.LINQ 查詢

  Entity Framework Core 使用語言集成查詢 (LINQ) 來查詢數據庫中的數據。 通過 LINQ 可使用 C#(或你選擇的其他 .NET 語言)基於派生上下文和實體類編寫強類型查詢。 LINQ 查詢的表示形式會傳遞給數據庫提供程序,進而轉換為特定的數據庫查詢語言(例如,適用於關系數據庫的 SQL)。 

 

    // (1)加載所有數據
     var blogs = BloggingContext.Blogs.ToList();
    SELECT [b].[BlogId], [b].[Name], [b].[Title], [b].[Url] FROM [Blogs] AS [b]

 

    //(2)加載單個實體
     var blog = BloggingContext.Blogs.Single(b => b.BlogId == 1);
    SELECT TOP(2) [b].[BlogId], [b].[Name], [b].[Title], [b].[Url]
    FROM [Blogs] AS [b]
    WHERE [b].[BlogId] = 1

 

    //(3)篩選
    var blogs = BloggingContext.Blogs.Where(b => b.Url.Contains("dotnet")).ToList();
    SELECT [b].[BlogId], [b].[Name], [b].[Title], [b].[Url]
  FROM [Blogs] AS [b]
  WHERE CHARINDEX(N'dotnet', [b].[Url]) > 0

    

    //(4)排序
  var blogs = BloggingContext.Blogs.OrderByDescending(b => b.BlogId).Select(b=> new { b.BlogId,b.Name }).ToList();
  SELECT [b].[BlogId], [b].[Name]
  FROM [Blogs] AS [b]
  ORDER BY [b].[BlogId] DESC

 

    //(5) group  找出重復的url,取出最大BlogId
     var blogs = from b in BloggingContext.Blogs
                        group b by new { b.Url} into gs
                        where gs.Count() >1 
                        select new
                        {
                            ID= gs.Max(b=>b.BlogId)
                        };
       //top 1
     int maxID = blogs.First().ID;
    SELECT TOP(1) MAX([b].[BlogId]) AS [ID]
    FROM [Blogs] AS [b]
    GROUP BY [b].[Url]
    HAVING COUNT(*) > 1

 

      // (6)多表join查詢
       var query = from b in context.Blogs
                        join p in context.Posts  on  b.BlogId equals p.BlogId
                        where b.BlogId == 1
                        select new { b.Name,p.Title } ;
       var bloglinq= query.ToList();
    SELECT [b].[Name], [p].[Title]
    FROM [Blogs] AS [b]
    INNER JOIN [Posts] AS [p] ON [b].[BlogId] = [p].[BlogId]
    WHERE [b].[BlogId] = 1

      有關顯示 LINQ 可完成的任務的大量示例,請參閱 101 個 LINQ 示例

 

三. 客戶端求值

  EF支持部分查詢在客戶端上求值,而將其他部分推送到數據庫執行。 由數據庫提供程序確定查詢的哪些部分會在數據庫中求值。 下面示例中 客戶端通過執行StandardizeUrl方法來返回 URL,查詢的其余部分都是在數據庫中執行的。

    var blogs = context.Blogs
    .OrderByDescending(blog => blog.Rating)
    .Select(blog => new
    {
        Id = blog.BlogId,
        Url = StandardizeUrl(blog.Url)
    })
    .ToList();

public static string StandardizeUrl(string url) { url = url.ToLower(); if (!url.StartsWith("http://")) { url = string.Concat("http://", url); } return url; }

  

  3.1 可能的性能問題

    雖然客戶端求值非常有用,但在某些情況下可能會導致性能不佳。 請考慮以下查詢,該where中使用輔助方法。 由於無法在數據庫中執行此操作,因此blog的所有數據將被拉入內存中,然后會在客戶端上應用篩選器。 根據數據量以及過濾掉多少數據,可能會導致性能下降。

    var blogs = context.Blogs
    .Where(blog => StandardizeUrl(blog.Url).Contains("dotnet"))
    .ToList();

 

  3.2 為客戶端評估拋出異常

    默認情況下,當執行客戶端求值時,EF Core 將記錄警告在日志中。可以改為引發異常或不執行任何操作。 設置如下所示

       services.AddDbContext<BloggingContext>
                (options => 
                options.UseSqlServer(connection)
                //改為引發異常
                .ConfigureWarnings(warnings => warnings.Throw(RelationalEventId.QueryClientEvaluationWarning))
                );

 

四. 跟蹤與非跟蹤查詢

  跟蹤行為可控制 EF是否將有關實體實例的信息保留在其更改跟蹤器中。 如果已跟蹤某個實體,則該實體中檢測到的任何更改都會在 SaveChanges() 期間永久保存到數據庫。 EF 還會修正從跟蹤查詢中獲取的實體與先前已加載到 DbContext 實例中的實體兩者之間的導航屬性。

 

   4.1 跟蹤查詢

    默認情況下,會跟蹤返回實體類型的查詢。 這表示可以更改這些實體實例,然后通過 SaveChanges() 持久化這些更改。在以下示例中,將檢測到對Blog評分所做的更改,並在 SaveChanges() 期間將這些更改持久化到數據庫中。

        var blog = context.Blogs.SingleOrDefault(b => b.BlogId == 1);
        blog.Rating = 5;
        context.SaveChanges();

        //顯示設置與上面一樣,開啟了跟蹤查詢
        var blog = context.Blogs. AsTracking().SingleOrDefault(b => b.BlogId == 1);

    

  4.2 非跟蹤查詢

    只需要讀取數據結果方案時,非跟蹤查詢十分有用。 可以更快速地執行非跟蹤查詢,因為無需設置更改跟蹤信息。

    //設置當前查詢為非跟蹤查詢
    var blogs = context.Blogs
        .AsNoTracking()
        .ToList();
//還可以在上下文實例級別, 設置默認為非跟蹤查詢 using (var context = new BloggingContext()) { context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; var blogs = context.Blogs.ToList(); }

 

   4.3跟蹤和投影

    即使查詢的結果類型不是實體類型,只要結果包含實體類型,則默認情況下也會跟蹤這些實體類型。 在以下返回匿名類型的查詢中,會跟蹤結果集中 Blog 的實例。

    var blog = context.Blogs
        .Select(b =>
            new
            {
                Blog = b,
                Posts = b.Posts.Count()
            });

    如果結果集不包含任何實體類型,則不會執行跟蹤。 在以下返回匿名類型(具有實體中的某些值,但沒有實際實體類型的實例)的查詢中,不會執行跟蹤。

      var blog = context.Blogs
        .Select(b =>
            new
            {
                Id = b.BlogId,
                Url = b.Url
            });

 

 

參考文獻

  EF查詢數據

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM