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

一.查詢的工作原理

Entity Framework Core 使用語(yǔ)言集成查詢 (LINQ) 來(lái)查詢數(shù)據(jù)庫(kù)中的數(shù)據(jù)。 通過(guò) LINQ 可使用 C#(或你選擇的其他 .NET 語(yǔ)言)基于派生上下文和實(shí)體類編寫(xiě)強(qiáng)類型查詢。 LINQ 查詢的表示形式會(huì)傳遞給數(shù)據(jù)庫(kù)提供程序,進(jìn)而轉(zhuǎn)換為特定的數(shù)據(jù)庫(kù)查詢語(yǔ)言(例如,適用于關(guān)系數(shù)據(jù)庫(kù)的 SQL)。

1.1 查詢的生命周期, 下面是每個(gè)查詢所經(jīng)歷的過(guò)程概述:

(1) LINQ 查詢由 E F處理,用于生成已準(zhǔn)備好的表示形式,由數(shù)據(jù)庫(kù)提供程序處理。緩存結(jié)果,以便每次執(zhí)行查詢時(shí)都不需要執(zhí)行此處理。
(2) 結(jié)果將傳遞給數(shù)據(jù)庫(kù)提供程序

a.數(shù)據(jù)庫(kù)提供程序會(huì)識(shí)別出查詢的哪些部分可以在數(shù)據(jù)庫(kù)中求值。
b. 查詢的這些部分會(huì)轉(zhuǎn)換為特定數(shù)據(jù)庫(kù)的查詢語(yǔ)言(例如,關(guān)系數(shù)據(jù)庫(kù)的 SQL)
c. 將一個(gè)或多個(gè)查詢發(fā)送到數(shù)據(jù)庫(kù)并返回結(jié)果集(結(jié)果是來(lái)自數(shù)據(jù)庫(kù)的值,而不是實(shí)體實(shí)例)

(3) 返回結(jié)果集處理

a.如果這是跟蹤查詢,EF會(huì)檢查數(shù)據(jù)是否代表一個(gè)實(shí)體,已存在于上下文實(shí)例的更改跟蹤器中。
如果是,則會(huì)返回現(xiàn)有實(shí)體
如果不是,則會(huì)創(chuàng)建新實(shí)體、設(shè)置更改跟蹤并返回該新實(shí)體
b.如果這是非跟蹤查詢,EF 會(huì)檢查數(shù)據(jù)是否表示此查詢結(jié)果集中的現(xiàn)有實(shí)體
如果是,則會(huì)返回現(xiàn)有實(shí)體
如果不是,則會(huì)創(chuàng)建新實(shí)體并返回該新實(shí)體

1.2 執(zhí)行查詢時(shí):

調(diào)用LINQ運(yùn)算符時(shí),只會(huì)構(gòu)建查詢?cè)趦?nèi)存中的表示形式。 只有在使用結(jié)果時(shí),查詢才會(huì)發(fā)送到數(shù)據(jù)庫(kù)。觸發(fā)查詢發(fā)送到數(shù)據(jù)庫(kù)的最常見(jiàn)操作如下:
 (1) 在 for 循環(huán)中循環(huán)訪問(wèn)結(jié)果

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

            //觸發(fā)數(shù)據(jù)庫(kù)查詢
            foreach (var item in blogs)
            {
                int maxID = item.ID;
            }

(2) 使用 ToList、ToArray、Single、Count 等操作都會(huì)觸發(fā)數(shù)據(jù)庫(kù)查詢

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

(3) 將查詢結(jié)果數(shù)據(jù)綁定到 UI

二.LINQ 查詢

Entity Framework Core 使用語(yǔ)言集成查詢 (LINQ) 來(lái)查詢數(shù)據(jù)庫(kù)中的數(shù)據(jù)。 通過(guò) LINQ 可使用 C#(或你選擇的其他 .NET 語(yǔ)言)基于派生上下文和實(shí)體類編寫(xiě)強(qiáng)類型查詢。 LINQ 查詢的表示形式會(huì)傳遞給數(shù)據(jù)庫(kù)提供程序,進(jìn)而轉(zhuǎn)換為特定的數(shù)據(jù)庫(kù)查詢語(yǔ)言(例如,適用于關(guān)系數(shù)據(jù)庫(kù)的 SQL)。

// (1)加載所有數(shù)據(jù)
     var blogs = BloggingContext.Blogs.ToList();
 SELECT [b].[BlogId], [b].[Name], [b].[Title], [b].[Url] FROM [Blogs] AS [b]
 //(2)加載單個(gè)實(shí)體
     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  找出重復(fù)的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

有關(guān)顯示 LINQ 可完成的任務(wù)的大量示例,請(qǐng)參閱 101 個(gè) LINQ 示例

三. 客戶端求值

EF支持部分查詢?cè)诳蛻舳松锨笾担鴮⑵渌糠滞扑偷綌?shù)據(jù)庫(kù)執(zhí)行。 由數(shù)據(jù)庫(kù)提供程序確定查詢的哪些部分會(huì)在數(shù)據(jù)庫(kù)中求值。 下面示例中 客戶端通過(guò)執(zhí)行StandardizeUrl方法來(lái)返回 URL,查詢的其余部分都是在數(shù)據(jù)庫(kù)中執(zhí)行的。

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 可能的性能問(wèn)題

雖然客戶端求值非常有用,但在某些情況下可能會(huì)導(dǎo)致性能不佳。 請(qǐng)考慮以下查詢,該where中使用輔助方法。 由于無(wú)法在數(shù)據(jù)庫(kù)中執(zhí)行此操作,因此blog的所有數(shù)據(jù)將被拉入內(nèi)存中,然后會(huì)在客戶端上應(yīng)用篩選器。 根據(jù)數(shù)據(jù)量以及過(guò)濾掉多少數(shù)據(jù),可能會(huì)導(dǎo)致性能下降。

  var blogs = context.Blogs
    .Where(blog => StandardizeUrl(blog.Url).Contains("dotnet"))
    .ToList();
3.2 為客戶端評(píng)估拋出異常

默認(rèn)情況下,當(dāng)執(zhí)行客戶端求值時(shí),EF Core 將記錄警告在日志中??梢愿臑橐l(fā)異?;虿粓?zhí)行任何操作。 設(shè)置如下所示

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

四. 跟蹤與非跟蹤查詢

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

4.1 跟蹤查詢

默認(rèn)情況下,會(huì)跟蹤返回實(shí)體類型的查詢。 這表示可以更改這些實(shí)體實(shí)例,然后通過(guò) SaveChanges() 持久化這些更改。在以下示例中,將檢測(cè)到對(duì)Blog評(píng)分所做的更改,并在 SaveChanges() 期間將這些更改持久化到數(shù)據(jù)庫(kù)中。

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

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

只需要讀取數(shù)據(jù)結(jié)果方案時(shí),非跟蹤查詢十分有用。 可以更快速地執(zhí)行非跟蹤查詢,因?yàn)闊o(wú)需設(shè)置更改跟蹤信息。

//設(shè)置當(dāng)前查詢?yōu)榉歉櫜樵?    var blogs = context.Blogs
        .AsNoTracking()
        .ToList();

    //還可以在上下文實(shí)例級(jí)別, 設(shè)置默認(rèn)為非跟蹤查詢
        using (var context = new BloggingContext())
        {
            context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;

            var blogs = context.Blogs.ToList();
        }
4.3跟蹤和投影

即使查詢的結(jié)果類型不是實(shí)體類型,只要結(jié)果包含實(shí)體類型,則默認(rèn)情況下也會(huì)跟蹤這些實(shí)體類型。 在以下返回匿名類型的查詢中,會(huì)跟蹤結(jié)果集中 Blog 的實(shí)例。

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

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

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

參考文獻(xiàn)

EF查詢數(shù)據(jù)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容