經過上一篇的學習 咱們把demo的各類關係終於搭建裏起來 以及處理好了如何映射到數據庫等問題 可是 只是搭建好了關係 問題還遠沒有解決sql
這篇就來寫如何查找導航屬性 和查找導航屬性的幾種方式 已經跟蹤生成的SQL來檢測是否滿意 經過這節學習 來明白何時用哪一個~~數據庫
一.三種加載mvc
1.延遲加載app
這是原文中的圖 你們能夠去看下 我模仿上面的作了個測試 出現了 已有打開的與此 Command 相關聯的 DataReader,必須首先將它關閉。asp.net
個人解決辦法是 var departments = db.Departments.ToList(); 現讀取出來 而後再遍歷. 而不加ToList() 真正執行SQL語句在 foreach的時候性能
而後再說下 這樣寫之後 SQL語句的執行學習
1.上來先查詢出全部的Department測試
SELECT
[Extent1].[DepartmentID] AS [DepartmentID],
[Extent1].[Name] AS [Name],
[Extent1].[Budget] AS [Budget],
[Extent1].[StartDate] AS [StartDate],
[Extent1].[InstructorID] AS [InstructorID]
FROM [dbo].[Department] AS [Extent1]
2.再執行到內層foreach時 這個會執行屢次 每次@EntityKeyValue1 等於 迭代到此次的 DepartmentIDui
exec sp_executesql N'SELECT
[Extent1].[CourseID] AS [CourseID],
[Extent1].[Title] AS [Title],
[Extent1].[Credits] AS [Credits],
[Extent1].[DepartmentID] AS [DepartmentID]
FROM [dbo].[Course] AS [Extent1]
WHERE [Extent1].[DepartmentID] = @EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=1
也就是說 咱們有多少條Department 就要執行多少次上面的方法 固然 這裏使用的是exec sp_executesql 利用sp_executesql,可以重用執行計劃,這就大大提供了執行性能spa
2.貪婪加載
在執行到第一個foreach 時 就執行了SQL語句 這是EF幫咱們生成的
SELECT
[Project1].[DepartmentID] AS [DepartmentID],
[Project1].[Name] AS [Name],
[Project1].[Budget] AS [Budget],
[Project1].[StartDate] AS [StartDate],
[Project1].[InstructorID] AS [InstructorID],
[Project1].[C1] AS [C1],
[Project1].[CourseID] AS [CourseID],
[Project1].[Title] AS [Title],
[Project1].[Credits] AS [Credits],
[Project1].[DepartmentID1] AS [DepartmentID1]
FROM ( SELECT
[Extent1].[DepartmentID] AS [DepartmentID],
[Extent1].[Name] AS [Name],
[Extent1].[Budget] AS [Budget],
[Extent1].[StartDate] AS [StartDate],
[Extent1].[InstructorID] AS [InstructorID],
[Extent2].[CourseID] AS [CourseID],
[Extent2].[Title] AS [Title],
[Extent2].[Credits] AS [Credits],
[Extent2].[DepartmentID] AS [DepartmentID1],
CASE WHEN ([Extent2].[CourseID] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1]
FROM [dbo].[Department] AS [Extent1]
LEFT OUTER JOIN [dbo].[Course] AS [Extent2] ON [Extent1].[DepartmentID] = [Extent2].[DepartmentID]
) AS [Project1]
ORDER BY [Project1].[DepartmentID] ASC, [Project1].[C1] ASC
3.顯示加載
先看圖
這個我測試後 效果是和第一個同樣的 並無看出什麼好處? 期待高手指點下
英文好的也能夠看下原文
4.關閉延遲加載
若是咱們想啓用延遲加載 能夠經過這兩種方式
1.去掉屬性裏的virtual
2.context.Configuration.LazyLoadingEnabled = false;
二.實戰開始 建立教師頁
先上實現後的效果圖
從圖中 咱們能夠看出這個要處理的關係
1對1的 教師和辦公地點
1對多的 教師教的課程
普通的多對多的
多對多的(關係表裏有數據的) 課程和學生 查看選擇課程的學生和學分
1.建立viewmodel
有時 咱們的頁面 顯示的不是一個實體類的內容 這個時候咱們能夠建立一個ViewModel 來展現界面
using System;
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; }
}
}
2.建立控制器添加Index
public ActionResult Index(Int32? id, Int32? 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.InstructorID == 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);
}
先看進來訪問的這一塊
viewModel.Instructors = db.Instructors
.Include(i => i.OfficeAssignment)
.Include(i => i.Courses.Select(c => c.Department))
.OrderBy(i => i.LastName);
從最上面的圖中 咱們能夠看到 要顯示有教師信息 辦公地址 和所教課程
因而 咱們使用貪婪加載出辦公地址和課程 可是 原文教程裏 還Select(c => c.Department) 把院系也一塊兒加載了進來 我認爲這是不必的
因而 我把代碼修改成
db.Instructors
.Include(i => i.OfficeAssignment)
.Include(i => i.Courses)
.OrderBy(i => i.LastName);
去掉了對院系的貪婪加載
看下生成的SQL語句
SELECT
[Project1].[InstructorID1] AS [InstructorID],
[Project1].[InstructorID] AS [InstructorID1],
[Project1].[LastName] AS [LastName],
[Project1].[FirstName] AS [FirstName],
[Project1].[HireDate] AS [HireDate],
[Project1].[InstructorID2] AS [InstructorID2],
[Project1].[Location] AS [Location],
[Project1].[C1] AS [C1],
[Project1].[CourseID] AS [CourseID],
[Project1].[Title] AS [Title],
[Project1].[Credits] AS [Credits],
[Project1].[DepartmentID] AS [DepartmentID]
FROM ( SELECT
[Extent1].[InstructorID] AS [InstructorID],
[Extent1].[LastName] AS [LastName],
[Extent1].[FirstName] AS [FirstName],
[Extent1].[HireDate] AS [HireDate],
[Extent2].[InstructorID] AS [InstructorID1],
[Extent3].[InstructorID] AS [InstructorID2],
[Extent3].[Location] AS [Location],
[Join3].[CourseID1] AS [CourseID],
[Join3].[Title] AS [Title],
[Join3].[Credits] AS [Credits],
[Join3].[DepartmentID] AS [DepartmentID],
CASE WHEN ([Join3].[CourseID2] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1]
FROM [dbo].[Instructor] AS [Extent1]
LEFT OUTER JOIN [dbo].[OfficeAssignment] AS [Extent2] ON [Extent1].[InstructorID] = [Extent2].[InstructorID]
LEFT OUTER JOIN [dbo].[OfficeAssignment] AS [Extent3] ON [Extent2].[InstructorID] = [Extent3].[InstructorID]
LEFT OUTER JOIN (SELECT [Extent4].[CourseID] AS [CourseID2], [Extent4].[InstructorID] AS [InstructorID], [Extent5].[CourseID] AS [CourseID1], [Extent5].[Title] AS [Title], [Extent5].[Credits] AS [Credits], [Extent5].[DepartmentID] AS [DepartmentID]
FROM [dbo].[CourseInstructor] AS [Extent4]
INNER JOIN [dbo].[Course] AS [Extent5] ON [Extent5].[CourseID] = [Extent4].[CourseID] ) AS [Join3] ON [Extent1].[InstructorID] = [Join3].[InstructorID]
) AS [Project1]
ORDER BY [Project1].[LastName] ASC, [Project1].[InstructorID1] ASC, [Project1].[InstructorID] ASC, [Project1].[InstructorID2] ASC, [Project1].[C1] ASC
繼續分析
if (id != null)
{
ViewBag.InstructorID = id.Value;
viewModel.Courses = viewModel.Instructors.Where(i => i.InstructorID == id.Value).Single().Courses;
}
若是點擊教師 則可查看該教師教的課程 這個id 就是教師ID 一下子會在視圖展現這個 這個就是根據教師查看課程
接着是點擊課程 查看所選的學生和分數
if (courseId != null)
{
viewModel.Enrollments = viewModel.Courses.Where(i => i.CourseID == courseId.Value).Single().Enrollments;
}
這裏還給出裏另外一種方法
if (courseID != null)
{
ViewBag.CourseID = courseID.Value;
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 = viewModel.Courses.Where(x => x.CourseID == courseID).Single().Enrollments;
}
最後上視圖
@model ContosoUniversity.ViewModels.InstructorIndexData
@{
ViewBag.Title = "Instructors";
}
<h2>Instructors</h2>
<p>
@Html.ActionLink("Create New", "Create")
</p>
<table>
<tr>
<th></th>
<th>Last Name</th>
<th>First Name</th>
<th>Hire Date</th>
<th>Office</th>
<th>Courses</th>
</tr>
@foreach (var item in Model.Instructors)
{
string selectedRow = "";
if (item.InstructorID == ViewBag.PersonID)
{
selectedRow = "selectedrow";
}
<tr class="@selectedRow" valign="top">
<td>
@Html.ActionLink("Select", "Index", new { id = item.InstructorID }) |
@Html.ActionLink("Edit", "Edit", new { id = item.InstructorID }) |
@Html.ActionLink("Details", "Details", new { id = item.InstructorID }) |
@Html.ActionLink("Delete", "Delete", new { id = item.InstructorID })
</td>
<td>
@item.LastName
</td>
<td>
@item.FirstMidName
</td>
<td>
@String.Format("{0:d}", item.HireDate)
</td>
<td>
@if (item.OfficeAssignment != null)
{
@item.OfficeAssignment.Location
}
</td>
<td>
@{
foreach (var course in item.Courses)
{
@course.CourseID @: @course.Title <br />
}
}
</td>
</tr>
}
</table>
@if (Model.Courses != null)
{
<h3>Courses Taught by Selected Instructor</h3>
<table>
<tr>
<th></th>
<th>ID</th>
<th>Title</th>
<th>Department</th>
</tr>
@foreach (var item in Model.Courses)
{
string selectedRow = "";
if (item.CourseID == ViewBag.CourseID)
{
selectedRow = "selectedrow";
}
<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>
}
@if (Model.Enrollments != null)
{
<h3>
Students Enrolled in Selected Course</h3>
<table>
<tr>
<th>Name</th>
<th>Grade</th>
</tr>
@foreach (var item in Model.Enrollments)
{
<tr>
<td>
@item.Student.FullName
</td>
<td>
@item.Grade
</td>
</tr>
}
</table>
}
三.上節的一個問題與疑問的提出
再上節的創建關係中 有一個這樣的問題 一對多的關係中 是否應該爲導航屬性 再專門創建一個ID
好比咱們可 課程與院系 一個院系能夠有多個課程 一個課程只能屬於一個院系 那咱們是否應該在課程類裏 加入院系ID呢
如
/// <summary>
/// 課程類
/// </summary>
public class Course
{
/// <summary>
/// 課程ID
/// </summary>
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[Display(Name = "Number")]
public int CourseID { get; set; }
/// <summary>
/// 課程名稱
/// </summary>
[Required(ErrorMessage = "Title is required.")]
[MaxLength(50)]
public string Title { get; set; }
/// <summary>
/// 學分
/// </summary>
[Required(ErrorMessage = "Number of credits is required.")]
[Range(0, 5, ErrorMessage = "Number of credits must be between 0 and 5.")]
public int Credits { get; set; }
[Display(Name = "Department")]
public int DepartmentID { get; set; }
/// <summary>
/// 關係表導航屬性 一個課程容許被屢次報名等級
/// </summary>
public virtual ICollection<Enrollment> Enrollments { get; set; }
public virtual Department Department { get; set; }
public virtual ICollection<Instructor> Instructors { get; set; }
}
這裏面加了 院系ID 我之前一直以爲沒有必要加這個 今天在作這個導航屬性查找時 發現一個問題 作個小實驗
好比我想獲得其中一個課程的ID 若是有院系ID 屬性 能夠這麼寫
var courses = db.Courses.ToList();
int i = courses[0].DepartmentID;
若是沒 能夠這麼寫
int i = courses[0].Department.DepartmentID;
首先 這個都沒有用貪婪加載 默認的延遲加載 若是你使用上面的 則不會往數據庫裏去執行一條根據課程ID查找院系的SQL語句
但你使用下面的 則會往數據庫裏發送一條查找語句
這點 EF作的是並很差的 在NH裏 兩種方法 都不會發送 由於在下面那裏使用了代理 而EF沒有
我想問的是 是我哪操做的不對麼? 形成了這個緣由? 請高手解答下
四.總結
關係的加載就結束了 其實寫關係加載的園子中有很多好文章了 我這裏寫的少了些~~
不過關係的操做尚未結束