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);
}
相關文章
- MVC5+EF6 入門完整教程七MVC
- [翻譯 EF Core in Action 2.4] 載入相關資料
- MVC5+EF6 入門完整教程九MVC
- MVC5+EF6 入門完整教程八MVC
- MVC5+EF6 入門完整教程四MVC
- MVC5 + EF6 入門完整教程MVC
- MVC5+EF6 入門完整教程五MVC
- MVC5+EF6 入門完整教程六MVC
- MVC5+EF6 入門完整教程十MVC
- MVC5 + EF6 入門完整教程二MVC
- MVC5 + EF6 完整入門教程三MVC
- 【第三篇】ASP.NET MVC快速入門之安全策略(MVC5+EF6)ASP.NETMVC
- MVC5+EF6 入門完整教程11--細說MVC中倉儲模式的應用MVC模式
- MVC5+EF6 入門完整教程13 -- 動態生成多級選單MVC
- MVC 5 + EF6 完整教程16 -- 控制器詳解MVC
- MVC 5 + EF6 入門完整教程14 -- 動態生成麵包屑導航MVC
- MVC5+EF6 入門完整教程12--靈活控制Action許可權MVC
- MVC 5 + EF6 完整教程15 -- 使用DI進行解耦MVC解耦
- MVC5+EF6 完整教程17--升級到EFCore2.0MVC
- java相關資料下載Java
- OCP相關資料下載
- 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資料庫
- ASP.NET MVC5+EF6+EasyUI 後臺管理系統(41)-組織架構ASP.NETMVCUI架構
- Handler相關------方法,更新UI,非同步,解決資料載入量等UI非同步
- [快速閱讀七] Halcon裡emphasize函式相關資料.函式
- 5G與WiFi6相愛相殺的關係WiFi
- Android資源動態載入以及相關原理分析Android
- 網站資料統計分析相關入門網站
- 深入理解 EF Core:EF Core 寫入資料時發生了什麼?
- 資料庫相關資料庫
- 大資料相關大資料
- dtrace 相關資料
- Retrofit相關資料
- DNN 相關資料DNN