(25)ASP.NET Core EF查詢(複雜查詢運算符、原生SQL查詢、異步查詢)

1.複雜查詢運算符

在生產場景中,我們經常用到LINQ運算符進行查詢獲取數據,現在我們就來了解下生產場景經常出現幾種複雜查詢運算符。

1.1聯接(INNER JOIN)

藉助LINQ Join運算符,可根據每個源的鍵選擇器連接兩個數據源,並在鍵匹配時生成值的元組。

var query = from blog in _context.Set<Blog>()
            join post in _context.Set<Post>()
                on blog.BlogId equals post.BlogId
            select new { blog, post };

SQL:

SELECT [blog].[BlogId], [blog].[Createtime], [blog].[Updatetime], [blog].[Url], [post].[PostId], [post].[BlogId], [post].[Content], [post].[Title]
FROM [Blog] AS [blog]
INNER JOIN [Post] AS [post] ON [blog].[BlogId] = [post].[BlogId]

SQL Server Profiler:

1.2左聯接(Left Join)

雖然Left Join不是LINQ運算符,但關係數據庫具有常用於查詢的Left Join的概念。LINQ查詢中的特定模式提供與服務器上的LEFT JOIN相同的結果。

var query = from blog in _context.Set<Blog>()
            join post in _context.Set<Post>()
                on blog.BlogId equals post.BlogId into grouping
            from post in grouping.DefaultIfEmpty()
            select new { blog, post };

SQL:

SELECT [blog].[BlogId], [blog].[Createtime], [blog].[Updatetime], [blog].[Url], [post].[PostId], [post].[BlogId], [post].[Content], [post].[Title]
FROM [Blog] AS [blog]
LEFT JOIN [Post] AS [post] ON [blog].[BlogId] = [post].[BlogId]

SQL Server Profiler:

1.3分組(GroupBy)

LINQ GroupBy運算符創建IGrouping<TKey, TElement>類型的結果,其中TKey和TElement可以是任意類型。此外,IGrouping實現了IEnumerable<TElement>,這意味着可在分組后使用任意LINQ運算符來對其進行組合。

var query = from blog in _context.Set<Blog>()
            group blog by blog.Url into g
            select new
            {
                g.Key,
                Count = g.Count()
            };

SQL:

SELECT [blog].[Url] AS [Key], COUNT(*) AS [Count]
FROM [Blog] AS [blog]
GROUP BY [blog].[Url]

SQL Server Profiler:

分組的聚合運算符出現在Where或OrderBy(或其他排序方式)LINQ運算符中。它在SQL中將Having子句用於Where子句。

var query = from blog in _context.Set<Blog>()
            group blog by blog.Url into g
            where g.Count() > 0
            orderby g.Key
            select new
            {
                g.Key,
                Count = g.Count()
            };

SQL:

SELECT [blog].[Url] AS [Key], COUNT(*) AS [Count]
FROM [Blog] AS [blog]
GROUP BY [blog].[Url]
HAVING COUNT(*) > 0
ORDER BY [Key]

SQL Server Profiler:

EF Core支持的聚合運算符如下所示:
●Avg
●Count
●LongCount
●Max
●Min
●Sum

1.4SelectMany

藉助LINQ SelectMany運算符,可為每個外部元素枚舉集合選擇器,並從每個數據源生成值的元組。

var query0 = from b in _context.Set<Blog>()
                from p in _context.Set<Post>()
                select new { b, p };

var query1 = from b in _context.Set<Blog>()
                from p in _context.Set<Post>().Where(p => b.BlogId == p.BlogId).DefaultIfEmpty()
                select new { b, p };

var query2 = from b in _context.Set<Blog>()
                from p in _context.Set<Post>().Select(p => b.Url + "=>" + p.Title).DefaultIfEmpty()
                select new { b, p };

SQL:

SELECT [b].[BlogId], [b].[Createtime], [b].[Updatetime], [b].[Url], [p].[PostId], [p].[BlogId], [p].[Content], [p].[Title]
FROM [Blog] AS [b]
CROSS JOIN [Post] AS [p]

SELECT [b].[BlogId], [b].[Createtime], [b].[Updatetime], [b].[Url], [t0].[PostId], [t0].[BlogId], [t0].[Content], [t0].[Title]
FROM [Blog] AS [b]
CROSS APPLY (
    SELECT [t].[PostId], [t].[BlogId], [t].[Content], [t].[Title]
    FROM (
        SELECT NULL AS [empty]
    ) AS [empty]
    LEFT JOIN (
        SELECT [p].[PostId], [p].[BlogId], [p].[Content], [p].[Title]
        FROM [Post] AS [p]
        WHERE [b].[BlogId] = [p].[BlogId]
    ) AS [t] ON 1 = 1
) AS [t0]

SELECT [b].[BlogId], [b].[Createtime], [b].[Updatetime], [b].[Url], [t0].[c]
FROM [Blog] AS [b]
CROSS APPLY (
    SELECT [t].[c]
    FROM (
        SELECT NULL AS [empty]
    ) AS [empty]
    LEFT JOIN (
        SELECT ([b].[Url] + N'=>') + [p].[Title] AS [c]
        FROM [Post] AS [p]
    ) AS [t] ON 1 = 1
) AS [t0]

SQL Server Profiler:

好了,這裏就不多寫關於LINQ其他示例了,如果需要了解的小夥伴們,可以移步“”這裏了解。

2.原生SQL查詢

有一些複雜業務場景,使用LINQ查詢可能會導致SQL查詢效率低下並不適用,那麼這時候就需要到原生SQL查詢了。EF Core為我們提供FromSql擴展方法基於原始SQL查詢。 FromSql只能在直接位於DbSet<>上的查詢根上使用。

var blogs = _context.Blog.FromSql("SELECT * FROM dbo.Blog").ToList();

原生SQL查詢可用於執行存儲過程。

var blogs = _context.Blog
    .FromSql("EXECUTE dbo.GetMostPopularBlogs")
    .ToList();

2.1原始SQL查詢使用參數化

向原始SQL查詢引入任何用戶提供的值時,必須注意防範SQL注入攻擊。除了驗證確保此類值不包含無效字符,還要將值與SQL文本參數化處理。
下面的示例通過在SQL查詢字符串中包含形參佔位符並提供額外的實參,將單個形參傳遞到存儲過程。雖然此語法可能看上去像String.Format語法,但提供的值包裝在DbParameter中,且生成的參數名稱插入到指定{0}佔位符的位置。

var url = "http://blogs.msdn.com/webdev";
var blogs = _context.Blog
    .FromSql("EXECUTE dbo.GetMostPopularBlogForUrl {0}", url)
.ToList();

SQL:

exec sp_executesql N'EXECUTE dbo.GetMostPopularBlogForUrl @p0
',N'@p0 nvarchar(4000)',@p0=N'http://blogs.msdn.com/webdev'

SQL Server Profiler:


還可以構造DbParameter並將其作為參數值提供。由於使用了常規SQL參數佔位符而不是字符串佔位符,因此可安全地使用FromSql:

var urlParams = new SqlParameter("Url", "http://blogs.msdn.com/webdev");
var blogs = _context.Blog
    .FromSql("EXECUTE dbo.GetMostPopularBlogForUrl @Url", urlParams)
.ToList();

SQL:

exec sp_executesql N'EXECUTE dbo.GetMostPopularBlogForUrl @Url
',N'@Url nvarchar(28)',@Url=N'http://blogs.msdn.com/webdev'

SQL Server Profiler:

2.2使用LINQ編寫SQL

可使用LINQ運算符在初始的原始SQL查詢基礎上進行組合。EF Core將其視為子查詢,並在數據庫中對其進行組合。下面的示例使用原始SQL查詢,該查詢從表值函數 (TVF) 中進行選擇。然後,使用LINQ進行篩選和排序,從而對其進行組合。

var searchTerm = "http://blogs.msdn.com/visualstudio";
var blogs = _context.Blog
    .FromSql($"SELECT * FROM dbo.Blog")
    .Where(b => b.Url == searchTerm)
    .Include(c=>c.Post)
    .OrderByDescending(b => b.Createtime)
.ToList();

SQL:

exec sp_executesql N'SELECT [b].[BlogId], [b].[Createtime], [b].[Updatetime], [b].[Url]
FROM (
    SELECT * FROM dbo.Blog
) AS [b]
WHERE [b].[Url] = @__searchTerm_1
ORDER BY [b].[Createtime] DESC, [b].[BlogId]',N'@__searchTerm_1 nvarchar(4000)',@__searchTerm_1=N'http://blogs.msdn.com/visualstudio'

exec sp_executesql N'SELECT [b.Post].[PostId], [b.Post].[BlogId], [b.Post].[Content], [b.Post].[Title]
FROM [Post] AS [b.Post]
INNER JOIN (
    SELECT [b0].[BlogId], [b0].[Createtime]
    FROM (
        SELECT * FROM dbo.Blog
    ) AS [b0]
    WHERE [b0].[Url] = @__searchTerm_1
) AS [t] ON [b.Post].[BlogId] = [t].[BlogId]
ORDER BY [t].[Createtime] DESC, [t].[BlogId]',N'@__searchTerm_1 nvarchar(4000)',@__searchTerm_1=N'http://blogs.msdn.com/visualstudio'

SQL Server Profiler:

3.異步查詢

當在數據庫中執行查詢時,異步查詢可避免阻止線程。異步查詢對於在客戶端應用程序中保持響應式UI非常重要。 異步查詢還可以增加Web應用程序中的吞吐量,即通過釋放線程,以處理其他Web應用程序中的請求。

public async Task<IActionResult> Index()
{
    var id1 = Thread.CurrentThread.ManagedThreadId.ToString();

    var blogs = await _context.Blog.ToListAsync();

    var id2 = Thread.CurrentThread.ManagedThreadId.ToString();

    return View(blogs);
}

當我們運行以上代碼時候,通過在關鍵字await上下文加入兩段獲取線程ID代碼,我們會看到如下結果:

看到兩段線程代碼輸出ID結果沒有?從上圖可以觀察到,當我們在進入某個視圖或者方法時候,執行到await某一個方法,當前線程不會一直等待下去,會立馬回收到線程池,供其他地方調用!當該await方法返回數據時候,才從線程池調用空閑線程執行await方法下文餘下的步驟。所以UI界面才不會進入假死狀態。

參考文獻:

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

※幫你省時又省力,新北清潔一流服務好口碑

※別再煩惱如何寫文案,掌握八大原則!

前國務卿柯瑞 找柯林頓、狄卡皮歐「結盟」抗暖化

摘錄自2019年12月2日世界新聞網報導

全球暖化和氣候變遷的影響已經成為國安問題,甚至被喻為將可能導致「第三次世界大戰」,前國務卿柯瑞(John Kerry)近日召集許多有影響力人士,如國會議員、軍事專家及好萊塢名人等,組成跨黨派聯盟——「第零次世界大戰」(World War Zero),聯合對抗氣候變遷。

該組織成員除了前總統柯林頓(Bill Clinton)和卡特(Jimmy Carter),也聚集許多好萊塢名人如影星李奧納多‧狄卡皮歐(Leonardo DiCaprio)、史汀(Sting)及艾希頓庫奇(Ashton Kutcher)等人;該組織成員目前約60人,目標是在明年募到超過1000萬元資金,並舉辦跨黨派的「氣候會談」。

柯瑞在1日連同組織成員之一,共和黨籍前加州州長阿諾‧史瓦辛格( Arnold Schwarzenegger),對於聯盟的成立發言表示:「沒有任何國家真正把事情做好,他們甚至把問題變得更糟,所以我們才召集這些人」。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

新北清潔公司,居家、辦公、裝潢細清專業服務

2020年生物多樣性總體檢 15項保育人士尚未關注的重大威脅與機會

環境資訊中心綜合外電;姜唯 編譯;林大利 審校

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

※幫你省時又省力,新北清潔一流服務好口碑

※別再煩惱如何寫文案,掌握八大原則!

孟加拉 氣候脆弱國家的綠能百分百之路

文:陳秉亨(蠻野心足生態協會理事長)

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

新北清潔公司,居家、辦公、裝潢細清專業服務

青年與未來世代日開跑 以能力建置實踐巴黎協定

文:林佳儀(政大外交所)

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

新北清潔公司,居家、辦公、裝潢細清專業服務

雪梨再度籠罩有毒煙霾 野火恐加劇東澳居民撤離

摘錄自2019年12月10日中央社報導

澳洲廣播公司(ABC News)報導,雪梨部分地區10日空氣品質顯著惡化,市區各地紛紛傳出煙霧警報器聲響。氣象預報警告將出現高溫強風,恐令雪梨北方叢林大火火勢加劇,部分居民預防性撤離家園。造成大眾運輸服務暫停,當局也發出健康警告。

澳洲東部新南威爾斯省(NSW)和維多利亞省(VIC)目前仍有超過100多處叢林火勢,其中許多是從11月燃燒至今。截至目前至少已有4人因野火喪命,超過680間房屋被毀,燒掉的灌木林達100萬餘公頃。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

※幫你省時又省力,新北清潔一流服務好口碑

※別再煩惱如何寫文案,掌握八大原則!

特斯拉下調2016中國區銷量目標至5000輛

近日,特斯拉CEO伊隆•馬斯克(ElonMusk)表示,將下調2016年中國區銷量目標至5000輛。另外,特斯拉正在努力尋找當地的合作夥伴,希望能在今年上半年順利於國內建廠。

據悉,2015年前9個月,特斯拉在中國銷售了3025台電動汽車,離2014年定下的目標1萬輛相差很遠,業績不佳是馬斯克下調了中國銷售目標的主要原因。

資料顯示,香港已成為特斯拉ModelS全球保有密度最高的城市,在2015年全球共計交付的50,580輛ModelS中,有2,221輛是來自香港的訂單,占全球約4.39%。近兩年,香港政府鼓勵市民使用電動汽車,並在香港推行電動汽車首次免登記稅政策,使電動車註冊量增長了270%,而特斯拉ModelS占到全部電動車份額的80%。

相比而言,大陸市場對特斯拉的政策支持力度顯得不足。馬斯克稱,自己的公司希望能在今年上半年順利於國內建廠,並借此減輕進口稅收方面的壓力,以及爭取更多政府為新能源車專供的政策優惠。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

新北清潔公司,居家、辦公、裝潢細清專業服務

中國電動超跑「前途 K50」預計 2017 亮相

Tesla 去(2015)年前三季在中國賣了約3000 輛Model S 和Model X 頂級款電動車,在全球全年交車輛更是達到50,580 輛,但有沒有可能,中國冒出的競爭對手會威脅Tesla 在電動車市場的領先地位?中國最大的汽車設計公司之一「北京長城華冠」在2015 年發表該公司第一款、也是中國第一款電動超跑「K50」,性能和價格都相當於Tesla Model S,而負責K50 生產的子公司「前途汽車」2 月於蘇州動土設廠,也意味著K50 量產在即。

K50 號稱5 秒就能加速至100公里,北京長城華冠董事長陸群受訪時說到,K50 有著碳纖維的車身、鋁製車架,預計2017 年就會亮相,售價約在70 萬人民幣(相當於10 萬6000 美元、345 萬台幣),價格相當於鋁製車身的Tesla Model S,而比起同樣是碳纖維車身的插電式油電混合車BMW i8,K50 的售價只有1/3。

陸群說,公司從Tesla 身上和其他電動車廠商身上學習,他們的目標就是打造高品質的汽車。前途汽車這個全資子公司自2015 年成立,任務就是打造電動汽車,而自己設廠,對他們來說是唯一的解決方案,因為在中國沒有其他在做碳纖維模型的工廠,這個花20 億人民幣打造的廠房,未來也會為其他車廠代工製造,預計初期的產值可以達到一年5 萬輛車。

至於在K50 之外,前途汽車也計畫再發表兩款便宜一些的電動車,陸群為前途汽車訂立目標,在2020 年獲得電動車市場15% 的市佔,又或者是45 萬輛。「我們會讓消費者相信,中國也是有能力打造一個全新的汽車品牌,一個頂尖的汽車品牌,一個頂尖的純電動車品牌。」陸群說。

分析師:打造電動車要看長期續航力

Bloomberg Intelligence 駐香港的汽車分析師Steve Man 是這樣看的,他認為前途汽車最終若要成功,得看他的行銷和市場聲望,但在那之前,打造一台經得起時間考驗的電動車可是需要相當多的耐心和資本,「我們就來看看前途有沒有這麼長的續航力」。

Elon Musk  對此表示,聽聽就好?

這番話透露出些許不信任的意味,至於身為市場領先者、被挑戰的對象,Tesla 創辦人兼執行長Elon Musk 又是怎麼看的呢?Elon Musk 曾在日前一場演講中對市場上充滿電動車和自駕車新近者眾多做出以下回應,「現在有太多新創公司發布電動車、自駕車的消息,我都在等哪天我媽也跳出來發布一輛。」

Elon Musk 這番話不是針對前途汽車,光是在中國市場,這類宣布要做電動車的新創公司可真不少。中國科技創業家李斌在2015 年創立「未來汽車」(NextEV,Inc),獲得騰訊和高瓴資本投資;樂視創辦人兼CEO賈躍亭在今年4 月也揭示一輛四門電動跑車「LeSEE」的概念;傳統車廠長安汽車也和福特合夥,投資180 億人民幣研發低能源汽車。

  • 資料來源:

本文授權轉載自《科技新報》──〈〉

(首圖來源:截自官網)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

新北清潔公司,居家、辦公、裝潢細清專業服務

大眾在華投40億歐/年 到2020年在中國新能源車份額佔據第一

大眾汽車集團管理董事會成員、大眾汽車集團(中國)總裁兼CEO海茲曼接受媒體專訪時表示,大眾將以年均40億歐元(約287億人民幣)以上的投資規模加大對中國市場投資,資金主要來自大眾在中國合資企業;
 
部署新能源戰略達成百公里油耗5L目標

新能源車相關的投資將是大眾未來投資重點,對此,海茲曼表示,這一部署也是為了達成2020年前要達到產品平均油耗5L的目標。不僅是新能源,對於傳統能源乘用車大眾也在不斷的引入最新技術,將節油降耗的潛力進一步挖掘。

大眾汽車集團在華的新能源車戰略是一個階段性的戰略,分為三個階段。海茲曼稱,目前大眾正處於第一個階段,就是通過進口的方式來為中國車主提供插電 式混合動力車型以及純電動車,這包括保時捷Panamera插電式混合動力車型、奧迪A3插電式混合動力、GolfGTE、e-Golf以及e-up!等。

第二階段,從2016年開始,大眾將會尋求插電式混合動力車型在中國的本土生產。在插電式混合動力車型上實現國產的首先是奧迪A6,接下來就是大眾品牌一款C Model,與奧迪A6同級別的一款插電式混合動力車型。

第三階段,大眾計畫實現純電動新能源車在中國本土的生產,2020年之前大眾將實現第一款基於MQB平臺的純電動車型的國產。海茲曼強調,大眾戰略實現全面的國產化,包括零部件的國產化以及研發的當地語系化。

海茲曼表示,大眾會在2-3年期間內不斷的實現插電式混合動力車型的國產。在此之後的第三階段,大眾會啟動純電動車型在中國本土的生產。第三階段實 現國產化的新能源車就基於MLB和MQB平臺。MLB和MQB平臺可以實現協同增效的作用,能夠實現傳統發動機車型、插電式混合動力車型和純電動車型的共 線生產。

不僅如此,海茲曼稱,大眾將在華引入一條全新的電動車生產線,一汽大眾和上汽大眾都將會生產純電動車型。大眾在MLB和MQB平臺的基礎上,定制了一個MEB電動車模組化平臺,在續航里程上可以支援400公里-600公里長途續航里程。

目標:2020年在中國新能源車份額佔據第一

對於大眾新能源車銷售目標,海茲曼並不掩飾對於未來的信心,他表示,2020年中國的新能源車年銷售大約是200萬輛的規模,屆時大眾集團新能源車 在中國市場的銷量應該在幾十萬輛的水準。或者說,2020年,大眾集團在中國新能源車市場的份額應該是與其乘用車在中國整體乘用車的市場份額相似。
 

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

※幫你省時又省力,新北清潔一流服務好口碑

※別再煩惱如何寫文案,掌握八大原則!

[ASP.NET Core 3框架揭秘] 依賴注入[8]:服務實例的生命周期

生命周期決定了IServiceProvider對象採用怎樣的方式提供和釋放服務實例。雖然不同版本的依賴注入框架針對服務實例的生命周期管理採用了不同的實現,但總的來說原理還是類似的。在我們提供的中,我們已經模擬了三種生命周期模式的實現原理,接下來我們結合“服務範圍”的概念來對這個話題做進一步講述。

一、服務範圍(Service Scope)

對於依賴注入框架採用的三種生命周期模式(Singleton、Scoped和Transient)來說,Singleton和Transient都具有明確的語義,但是Scoped代表一種怎樣的生命周期模式,很多初學者往往搞不清楚。這裏所謂的Scope指的是由IServiceScope接口表示的“服務範圍”,該範圍由IServiceScopeFactory接口表示的“服務範圍工廠”來創建。如下面的代碼片段所示,IServiceProvider的擴展方法CreateScope正是利用提供的IServiceScopeFactory服務實例來創建作為服務範圍的IServiceScope對象。

public interface IServiceScope : IDisposable
{
    IServiceProvider ServiceProvider { get; }
}

public interface IServiceScopeFactory
{
    IServiceScope CreateScope();
}

public static class ServiceProviderServiceExtensions
{
   public static IServiceScope CreateScope(this IServiceProvider provider) => provider.GetRequiredService<IServiceScopeFactory>().CreateScope();
}

任何一個IServiceProvider對象都可以利用其註冊的IServiceScopeFactory服務創建一個代表服務範圍的IServiceScope對象,後者代表的“範圍”內具有一個新創建的IServiceProvider對象(對應着接口IServiceScope的ServiceProvider屬性),該對象與當前IServiceProvider在邏輯上具有如下圖所示的“父子關係”。

如上圖所示的樹形層次結構只是一種邏輯結構,從對象引用層面來看,通過某個IServiceScope封裝的IServiceProvider對象不需要知道自己的“父親”是誰,它只關心作為根節點的IServiceProvider在哪裡就可以了。下圖從物理層面揭示了IServiceScope / IServiceProvider對象之間的關係,任何一個IServiceProvider對象都具有針對根容器的引用。

二、服務實例的提供

只有在充分了解IServiceScope對象的創建過程以及它與IServiceProvider對象之間的關係之後,我們才會對三種生命周期管理模式(Singleton、Scoped和Transient)具有深刻的認識。就服務實例的提供方式來說,它們之間具有如下的差異:

  • Singleton:IServiceProvider對象創建的服務實例保存在作為根容器的IServiceProvider對象上,所以多個同根的IServiceProvider對象提供的針對同一類型的服務實例都是同一個對象。
  • Scoped:IServiceProvider對象創建的服務實例由自己保存,所以同一個IServiceProvider對象提供的針對同一類型的服務實例均是同一個對象。
  • Transient:針對每一次服務提供請求,IServiceProvider對象總是創建一個新的服務實例

三、服務實例的釋放

IServiceProvider除了為我們提供所需的服務實例之外,對於由它提供的服務實例,它還肩負起回收釋放的責任。這裏所說的回收釋放與.NET Core自身的垃圾回收機制無關,僅僅針對於自身類型實現了IDisposable或者IAsyncDisposable接口的服務實例(下面簡稱為Disposable服務實例),針對服務實例的釋放體現為調用它們的Dispose或者DisposeAsync方法。IServiceProvider對象針對服務實例採用的回收釋放策略取決於採用的生命周期模式,具體策略主要體現為如下兩點:

  • Singleton:提供Disposable服務實例保存在作為根容器的IServiceProvider對象上,只有在這個IServiceProvider對象被釋放的時候這些Disposable服務實例才能被釋放。
  • Scoped和Transient:當前IServiceProvider對象會保存由它提供的Disposable服務實例,當自己被釋放的時候,這些Disposable服務實例就會被釋放。

綜上所述,每個作為依賴注入容器的IServiceProvider對象都具有如下圖所示的兩個列表來存放服務實例,我們將它們分別命名為“Realized Services”和“Disposable Services”,對於一個作為非根容器的IServiceProvider對象來說,由它提供的Scoped服務保存在自身的Realized Services列表中,Singleton服務實例則會保存在根容器的Realized Services列表中。如果服務實現類型實現了IDisposable或者IAsyncDisposable接口,Scoped和Transient服務實例會被保存到自身的Disposable Services列表中,而Singleton服務實例則會保存到根容器的Disposable Services列表中。

對於作為根容器的IServiceProvider對象來說,Singleton和Scoped模式對它來說是兩種等效的生命周期模式,由它提供的Singleton和Scoped服務實例會被存放到自身的Realized Services列表中,而所有需要被釋放的服務實例則被存放到Disposable Services列表中。當某個IServiceProvider對象被用於提供針對指定類型的服務實例時,它會根據服務類型提取出表示服務註冊的ServiceDescriptor對象並根據它得到對應的生命周期模式:

  • 如果生命周期模式為Singleton,並且作為根容器的Realized Services列表中包含對應的服務實例,它將作為最終提供的服務實例。如果這樣的服務實例尚未創建,那麼新的服務將會被創建出來並作為提供的服務實例。這個服務實例會被添加到根容器的Realized Services列表中。如果實例類型實現了IDisposable或者IAsyncDisposable接口,創建的服務實例會被添加到根容器的Disposable Services列表中。
  • 如果生命周期為Scoped,那麼IServiceProvider會先確定自身的Realized Services列表中是否存在對應的服務實例,存在的服務實例將作為最終的返回值。如果Realized Services列表不存在對應的服務實例,那麼新的服務實例會被創建出來。在作為最終的服務實例被返回之前,創建的服務實例會被添加到自身的Realized Services列表中,如果實例類型實現了IDisposable或者IAsyncDisposable接口,創建的服務實例會被添加到自身的Disposable Services列表中。
  • 如果提供服務的生命周期為Transient,那麼IServiceProvider會直接創建一個新的服務實例。在作為最終的服務實例被返回之前,如果實例類型實現了IDisposable或者IAsyncDisposable接口,創建的服務實例會被添加到自身的Disposable Services列表中。

對於非根容器的IServiceProvider對象來說,它的生命周期是由“包裹”着它的IServiceScope對象控制的。從前面給出的定義可以看出IServiceScope實現了IDisposable接口,Dispose方法的執行不僅標志著當前服務範圍的終結,也意味着對應IServiceProvider對象生命周期的結束。

當代表服務範圍的IServiceScope對象的Dispose方法被調用的時候,它會調用對應IServiceProvider對象的Dispose方法。一旦IServiceProvider對象因自身Dispose方法的調用而被釋放的時候,它會從自身的Disposable Services列表中提取出所有需要被釋放的服務實例,並調用它們的Dispose或者DisposeAsync方法。在這之後,Disposable Services和Realized Services列表會被清空,列表中的服務實例和IServiceProvider對象自身會成為垃圾對象被GC回收。

四、ASP.NET Core應用

依賴注入框架所謂的服務範圍在ASP.NET Core應用中具有明確的邊界,指的是針對每個HTTP請求的上下文,也就是服務範圍的生命周期與每個請求上下文綁定在一起。如下圖所示,ASP.NET Core應用中用於提供服務實例的IServiceProvider對象分為兩種類型,一種是作為根容器並與應用具有相同生命周期的IServiceProvider對象,另一個類則是根據請求及時創建和釋放的IServiceProvider對象,我們一般將它們分別稱為ApplicationServicesRequestServices

在ASP.NET Core應用初始化過程(即請求管道構建過程)中使用的服務實例都是由ApplicationServices提供的。在具體處理每個請求時,ASP.NET Core框架會利用註冊的一个中間件來針對當前請求創建一個代表服務範圍的IServiceScope對象,該服務範圍提供的RequestServices用來提供當前請求處理過程中所需的服務實例。一旦服務請求處理完成,IServiceScoped對象代表的服務範圍被終結,在當前請求處理過程中的Scoped服務會變成垃圾對象並最終被GC回收。對於實現了IDisposable或者IAsyncDisposable接口的Scoped或者Transient服務實例來說,在變成垃圾對象之前,它們的Dispose或者DisposeAsync方法會被調用。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

新北清潔公司,居家、辦公、裝潢細清專業服務