MVC 5 + EF 6(七)【載入相關資料】
1.延遲(Lazy
)載入、預先(Eager
)載入、顯式(Explicit
)載入:
EF
載入相關資料到實體導航屬性有以下幾種方式:
- 延遲載入:當實體第一次讀取時,相關資料沒有載入。當第一次試圖訪問導航屬性時,所需的導航資料自動載入。這導致多條查詢語句被髮送到資料庫:一條查詢實體本身,一條查詢實體相關資料。
DbContext
類預設啟用延遲載入。
- 預先載入:當讀取實體時,相關資料同時讀取。這通常會導致一個連線查詢,查詢所有所需的資料。使用
Include
方法指定預先載入。
- 顯示載入:這種載入方式類似於延遲載入,不同的是我們要在程式碼中明確地查詢相關資料;當我們訪問導航屬性時,它不會自動查詢。我們需要手動獲取實體的物件狀態管理器入口,並且需要呼叫
Collection.Load
方法獲取集合或者呼叫Reference.Load
方法獲取單個實體來載入相關資料(在下面的例子中如果我們想要獲取Administrator
導航屬性,使用Reference(x => x.Administrator)
代替Collection(x => x.Courses)
)。通常情況下,我們不需要使用顯示載入,除非禁用了延遲載入。
因為不立即獲取屬性值,延遲載入和顯式載入同時又被稱為延後(deferred
)載入。
1.1.考慮效能:
如果我們知道,我們需要每個實體的相關資料,預先載入大多數情況下會有最好的效能,因為一條查詢資料通常比單獨查詢每個實體更有效率。例如,在上面的例子中,假設每個department
有10相關的course
。預先載入載入資料只產生一條(連線)查詢語句和1次往返。延遲載入和顯式載入載入資料均會產生11條查詢語句和11次往返。額外的往返尤其不利於效能的提高。
另一方面,在一些情況下,延遲載入會更有效率。預先載入可能會產生一個SQL Server
不能有效處理的非常複雜的連線。或者,我們只是需要訪問導航屬性的子集,延遲載入會比預先載入效能高,因為預先載入返回的資料比我們實際需要的要多。如果效能是至關重要的,最好同時測試這兩種方式的效能以作出做好的選擇。
延遲載入會遮掩程式碼這樣會造成效能問題。例如,程式碼沒有指定預先載入或者顯式載入,但是需要處理大量的實體,並且在每個迭代中使用多個導航屬性,這是效率將會非常低(因為多次往返資料庫)。
一個程式可能在開發時使用SQL Server
效能表現良好,但是當部署在Azure SQL
資料庫上時,由於增加了延遲和延遲載入,可能會遇到效能問題。使用實際負載測試分析資料庫查詢會幫助我們確定延遲載入是否適當。更多資訊請檢視:Demystifying Entity Framework Strategies: Loading Related Data和Using the Entity Framework to Reduce Network Latency to SQL Azure。
1.2.在序列化之前禁用延遲載入:
如果在序列化過程中延遲載入是啟用的,我們獲取到的資料會比我們想要的要多。序列化通常會訪問一個型別每個例項的每個屬性。對屬性的訪問會觸發延遲載入,延遲載入的實體被例項化。序列化過程訪問延遲載入實體的每個屬性,可能會會觸發更多的延遲載入和序列化。為了防止這種連鎖反應失控,我們要在序列化實體前禁用延遲載入。
EF使用的代理類也會使序列化變得複雜,請檢視:Advanced Scenarios tutorial。
一種避免序列化問題的方法是不序列化實體物件而是序列化資料傳輸物件(DTO),請檢視:Using Web API with Entity Framework。
如果不使用DTO
,我們可以禁用延遲載入和避免代理,請檢視:disabling proxy creation。
禁用延遲載入的其他方法:
禁用特定的導航屬性的延遲載入,在定義時不使用virtual關鍵字。
對所有的導航屬性禁用延遲載入,在上下文類的建構函式中新增如下程式碼:
this.Configuration.LazyLoadingEnabled = false;
2.建立Course
頁面,顯示Department
的Name
:
建立CourseController
(選擇MVC 5 Controller with views, using Entity Framework):
Index方法
public ActionResult Index()
{
var courses = db.Courses.Include(c => c.Department);
return View(courses.ToList());
}
修改Views\Course\Index.cshtml:
@model IEnumerable<ContosoUniversity.Models.Course>
@{
ViewBag.Title = "Courses";
}
<h2>Courses</h2>
<p>
@Html.ActionLink("Create New", "Create")
</p>
<table class="table">
<tr>
<th>
@Html.DisplayNameFor(model => model.CourseID)
</th>
<th>
@Html.DisplayNameFor(model => model.Title)
</th>
<th>
@Html.DisplayNameFor(model => model.Credits)
</th>
<th>
Department
</th>
<th></th>
</tr>
@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.CourseID)
</td>
<td>
@Html.DisplayFor(modelItem => item.Title)
</td>
<td>
@Html.DisplayFor(modelItem => item.Credits)
</td>
<td>
@Html.DisplayFor(modelItem => item.Department.Name)
</td>
<td>
@Html.ActionLink("Edit", "Edit", new { id=item.CourseID }) |
@Html.ActionLink("Details", "Details", new { id=item.CourseID }) |
@Html.ActionLink("Delete", "Delete", new { id=item.CourseID })
</td>
</tr>
}
</table>
執行:
3.建立Instructors
頁面顯示Courses
和Enrollments
:
本節最終頁面:
3.1.為Instructor
的Index
檢視建立檢視模型:
在ViewModels
資料夾新建InstructorIndexData
.cs:
using System.Collections.Generic;
using ContosoUniversity.Models;
namespace ContosoUniversity.ViewModels
{
public class InstructorIndexData
{
public IEnumerable<Instructor> Instructors { get; set; }
public IEnumerable<Course> Courses { get; set; }
public IEnumerable<Enrollment> Enrollments { get; set; }
}
}
3.2.建立Instructor
控制器和檢視:
使用EF read/write
建立InstructorController
:
修改Index
方法:
public ActionResult Index(int? id, int? courseID)
{
var viewModel = new InstructorIndexData();
viewModel.Instructors = db.Instructors
.Include(i => i.OfficeAssignment)
.Include(i => i.Courses.Select(c => c.Department))
.OrderBy(i => i.LastName);
if (id != null)
{
ViewBag.InstructorID = id.Value;
viewModel.Courses = viewModel.Instructors.Where(
i => i.ID == id.Value).Single().Courses;
}
if (courseID != null)
{
ViewBag.CourseID = courseID.Value;
viewModel.Enrollments = viewModel.Courses.Where(
x => x.CourseID == courseID).Single().Enrollments;
}
return View(viewModel);
}
當我們知道集合中只有一個專案時我們使用Single
方法。如果傳遞給集合的為空值或者多於一個專案Single
方法會丟擲異常。SingleOrDefault
方法會在集合為空值時返回一個預設值(在本例中會返回空值)。但是在本例中還是會丟擲異常(在一個空引用檢視搜尋Courses
屬性時),並且異常訊息將不會標明問題的原因。如果我們使用Single
方法時,我們同時可以給它傳遞Where
條件,這樣可以不用再呼叫Where
方法:
.Single(i => i.ID == id.Value)
可以取代:
.Where(i => i.ID == id.Value).Single()
3.3.修改Index
檢視:
@model ContosoUniversity.ViewModels.InstructorIndexData
@{
ViewBag.Title = "Instructors";
}
<h2>Instructors</h2>
<p>
@Html.ActionLink("Create New", "Create")
</p>
<table class="table">
<tr>
<th>Last Name</th>
<th>First Name</th>
<th>Hire Date</th>
<th>Office</th>
<th></th>
</tr>
@foreach (var item in Model.Instructors)
{
string selectedRow = "";
if (item.ID == ViewBag.InstructorID)
{
selectedRow = "success";
}
<tr class="@selectedRow">
<td>
@Html.DisplayFor(modelItem => item.LastName)
</td>
<td>
@Html.DisplayFor(modelItem => item.FirstMidName)
</td>
<td>
@Html.DisplayFor(modelItem => item.HireDate)
</td>
<td>
@if (item.OfficeAssignment != null)
{
@item.OfficeAssignment.Location
}
</td>
<td>
@Html.ActionLink("Select", "Index", new { id = item.ID }) |
@Html.ActionLink("Edit", "Edit", new { id = item.ID }) |
@Html.ActionLink("Details", "Details", new { id = item.ID }) |
@Html.ActionLink("Delete", "Delete", new { id = item.ID })
</td>
</tr>
}
</table>
在Index
檢視最後新增:
@if (Model.Courses != null)
{
<h3>Courses Taught by Selected Instructor</h3>
<table class="table">
<tr>
<th></th>
<th>Number</th>
<th>Title</th>
<th>Department</th>
</tr>
@foreach (var item in Model.Courses)
{
string selectedRow = "";
if (item.CourseID == ViewBag.CourseID)
{
selectedRow = "success";
}
<tr class="@selectedRow">
<td>
@Html.ActionLink("Select", "Index", new { courseID = item.CourseID })
</td>
<td>
@item.CourseID
</td>
<td>
@item.Title
</td>
<td>
@item.Department.Name
</td>
</tr>
}
</table>
}
再次在Index
檢視最後新增:
@if (Model.Enrollments != null)
{
<h3>
Students Enrolled in Selected Course
</h3>
<table class="table">
<tr>
<th>Name</th>
<th>Grade</th>
</tr>
@foreach (var item in Model.Enrollments)
{
<tr>
<td>
@item.Student.FullName
</td>
<td>
@Html.DisplayFor(modelItem => item.Grade)
</td>
</tr>
}
</table>
}
3.4.新增顯示載入:
修改Index
方法:
public ActionResult Index(int? id, int? courseID)
{
var viewModel = new InstructorIndexData();
viewModel.Instructors = db.Instructors
.Include(i => i.OfficeAssignment)
.Include(i => i.Courses.Select(c => c.Department))
.OrderBy(i => i.LastName);
if (id != null)
{
ViewBag.InstructorID = id.Value;
viewModel.Courses = viewModel.Instructors.Where(
i => i.ID == id.Value).Single().Courses;
}
if (courseID != null)
{
ViewBag.CourseID = courseID.Value;
// Lazy loading
//viewModel.Enrollments = viewModel.Courses.Where(
// x => x.CourseID == courseID).Single().Enrollments;
// Explicit loading
var selectedCourse = viewModel.Courses.Where(x => x.CourseID == courseID).Single();
db.Entry(selectedCourse).Collection(x => x.Enrollments).Load();
foreach (Enrollment enrollment in selectedCourse.Enrollments)
{
db.Entry(enrollment).Reference(x => x.Student).Load();
}
viewModel.Enrollments = selectedCourse.Enrollments;
}
return View(viewModel);
}
相關文章
- [翻譯 EF Core in Action 2.4] 載入相關資料
- C# 資料操作系列 - 5. EF Core 入門C#
- C# 資料操作系列 - 6 EF Core 配置對映關係C#
- F5負載均衡系列教程七【F5高可用相關知識】負載
- Layui(十一) Layui Tree點選元件則載入相關資料UI元件
- .NET 5/.NET Core使用EF Core 5連線MySQL資料庫寫入/讀取資料示例教程MySql資料庫
- [快速閱讀七] Halcon裡emphasize函式相關資料.函式
- 5G與WiFi6相愛相殺的關係WiFi
- 深入理解 EF Core:EF Core 寫入資料時發生了什麼?
- EF6連線GBase8s資料庫示例資料庫
- python資料視覺化-matplotlib入門(6)-從檔案中載入資料Python視覺化
- 實驗七 檔案相關
- EF中延遲載入的那些事
- KaliLinuxNetHunter教程下載相關資源Linux
- 【轉載】【EF Core】Code first 之使用新資料庫資料庫
- Next.js踩坑入門系列(七) —— 其他相關知識JS
- EF Core 二 、 入門 EF Core
- 資料載入
- 【關係抽取-R-BERT】載入資料集
- 資料庫 (相關練習)資料庫
- 資料分析相關軟體
- JTS相關資料和示例
- camunda相關資料介紹
- 6. JavaScript this指向相關JavaScript
- activiti6基礎01-如何資料庫操作及相關表資料庫
- 用 Python 載入資料的 5 種不同方式Python
- PostgreSQL 原始碼解讀(109)- WAL#5(相關資料結構)SQL原始碼資料結構
- 大資料相關資料論文小結大資料
- php實現七牛雲相關操作PHP
- 6-C/C++實現資料結構連結串列相關操作C++資料結構
- 資料結構相關知識資料結構
- Oracle相關資料字典檢視Oracle
- 蒐集到的Weex 相關資料
- COVID-19相關資料集
- 資料庫事物相關問題資料庫
- 運維相關的資料整理運維
- 強化學習相關資料強化學習
- SpringBoot資料來源相關配置Spring Boot