IQueryable擴充方法QueryableExtensions.Include

 

在使用scaffolded時無意發現有這個QueryableExtensions.Include方法,

到底他的作用是什麼呢?如何用?以下重現原問題。

 

一、先準備資料庫

CREATE TABLE [dbo].[CATEGORY] 
  ( 
     [category_id]  [TINYINT] NOT NULL, 
     [categoryType] [NVARCHAR](5) NOT NULL, 
     PRIMARY KEY (category_id) 
  ) 


CREATE TABLE [dbo].[NEWS] 
  ( 
     [news_id]     [UNIQUEIDENTIFIER] NOT NULL, 
     [title]       [NVARCHAR](50) NOT NULL, 
     [date]        [DATE] NOT NULL, 
     [category_id] [TINYINT] NULL REFERENCES CATEGORY(category_id), 
     [context]     [NVARCHAR](max) NULL, 
     PRIMARY KEY (news_id) 
  ) 


-- `dbo.CATEGORY`
INSERT dbo.CATEGORY VALUES (1, N'公告訊息')
INSERT dbo.CATEGORY VALUES (2, N'臨時公告')

-- `dbo.NEWS`
INSERT dbo.NEWS VALUES (N'77289FB6-A7BA-770A-F801-09FF58D932B8', N'中華民國口腔病理學會-口腔扁平苔癬症病人口腔預保健計畫研討會', '20170724', 2, N'<p><img src="/Dentist/AdminConsoleNEWS/GetImage?img_id=1583980530&amp;news_id=77289fb6-a7ba-770a-f801-09ff58d932b8" style="height:1415px; width:1000px" /></p>
')
INSERT dbo.NEWS VALUES (N'64C56473-A7CC-B749-BC01-9B93A17468C7', N'嬰幼兒齲齒 何時該做口腔檢查?', '20170811', 1, N'<p><img src="/Dentist/AdminConsoleNEWS/GetImage?img_id=1101658763&amp;news_id=64c56473-a7cc-b749-bc01-9b93a17468c7" style="height:350px; width:610px" /></p>

<p>嬰幼兒究竟該何時開始檢查牙齒呢?根據美國兒童牙科醫學會兒童家庭牙醫觀念,當第一顆乳牙長出後,最慢在一歲前,家長就可帶嬰幼兒接受口腔檢查;兒童牙科醫師鄧醫師指出,透過專業醫師協助,給予適合的潔牙衛教以及嬰幼兒飲食習慣等建議,讓家長們可以確保嬰幼兒的口腔健康。&nbsp;<br />
<br />
')
INSERT dbo.NEWS VALUES (N'CF74B42D-A7BC-6C45-F501-A4431F8F248F', N'植牙也要精準!?醫師透露關鍵', '20170726', 1, N'<p><img src="/Dentist/AdminConsoleNEWS/GetImage?img_id=1554305787&amp;news_id=cf74b42d-a7bc-6c45-f501-a4431f8f248f" style="height:479px; width:800px" /></p>

<p>缺牙會降低進食功能、說話發音,更會影響信心與門面,長期下來,對身心健康影響很大!隨著醫療科技發展,透過植牙手術能夠再度擁有一口好牙;牙科醫師並指出,藉由電腦3D軟體設計及列印技術製作的導引式植牙手術板,可以輔助植牙的精準判讀,不但大幅降低手術時間、傷口大小,就連復原時間也較快。&nbsp;<br />
<br />
')
INSERT dbo.NEWS VALUES (N'5CFB5EF1-A7BA-2152-F801-A9108AC4E6B1', N'社法人中華民國牙醫師公會全國聯合會-106年國小學童含氟漱口水齲二年計畫', '20170724', 2, N'<p><img src="/Dentist/AdminConsoleNEWS/GetImage?img_id=2050639876&amp;news_id=5cfb5ef1-a7ba-2152-f801-a9108ac4e6b1" style="height:1414px; width:1000px" /></p>
')
INSERT dbo.NEWS VALUES (N'07F4173D-A7BA-80C1-4C02-D4BFB878E5B3', N'寶寶看牙時機 醫:1歲前最佳', '20170724', 1, N'<p><img src="/Dentist/AdminConsoleNEWS/GetImage?img_id=1334095780&amp;news_id=07f4173d-a7ba-80c1-4c02-d4bfb878e5b3" style="height:356px; width:550px" /></p>

<p>何時該帶嬰幼兒去檢查牙齒,許多新手爸媽的共同疑問,根據美國兒童牙科醫學會提倡的「兒童家庭牙醫」觀念,嬰兒長出第1顆乳牙後,最慢在1歲前,家長就應該帶嬰幼兒接受口腔檢查,切勿等到孩子口腔問題浮現時才治療,如此容讓牙痛與牙醫聯結,形成不良的看牙經驗。&nbsp;<br />
<br />
')

 

二、Model

於預設的ASP.NET MVC專案連結並新增該資料庫,

有些基本步驟一步一步列出來會太壟長了,故會省略一些。

next

next

next

next

完成後畫面如下

 

三、在controller資料夾使用scaffolded

next

next

新增完成後在NEWSController裡的Index Action的程式中,

看到了程式為何使用了「Include(n => n.CATEGORY)」?

 

四、探討

不修改程式試著把NEWSController的Index View跑起來

Index View內容為

@model IEnumerable<WebApplication1.Models.NEWS>

@{
    ViewBag.Title = "Index";
}

<h2>Index</h2>

<p>
    @Html.ActionLink("Create New", "Create")
</p>
<table class="table">
    <tr>
        <th>
            @Html.DisplayNameFor(model => model.title)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.date)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.context)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.CATEGORY.categoryType)
        </th>
        <th></th>
    </tr>

@foreach (var item in Model) {
    <tr>
        <td>
            @Html.DisplayFor(modelItem => item.title)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.date)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.context)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.CATEGORY.categoryType)
        </td>
        <td>
            @Html.ActionLink("Edit", "Edit", new { id=item.news_id }) |
            @Html.ActionLink("Details", "Details", new { id=item.news_id }) |
            @Html.ActionLink("Delete", "Delete", new { id=item.news_id })
        </td>
    </tr>
}

</table>

執行結果

再來看不使用「Include(n => n.CATEGORY)」也是可以正常跑起來

經由網友的解說,原來用途為

「當類別內導航屬性(Navigation Properties)確定會被使用到且頻繁時,

可透過IQueryable擴充方法Include將導航屬性資料也一併取出並儲存於記憶體中。」

其效能可比喻為原本要執行101條SQL指令瞬間變成了1條,這其中的性能可想而知。

 

要注意使用QueryableExtensions.Include擴充方法時,需事先做「using System.Data.Entity;」才行。

 

參考資料:

關於IQueryable<T>特性的小實驗

[Entity Framework][LINQ] IEnumerable與IQueryable差異比較

[Entity Framework][LINQ] IQueryable擴充方法Include的使用時機

IQueryable<T> 介面

QueryableExtensions 類別