通過上一篇的學習 我們把demo的各種關系終於搭建里起來 以及處理好了如何映射到數據庫等問題 但是 只是搭建好了關系 問題還遠沒有解決
這篇就來寫如何查找導航屬性 和查找導航屬性的幾種方式 已經跟蹤生成的SQL來檢測是否滿意 通過這節學習 來明白什么時候用哪個~~
一.三種加載
1.延遲加載
這是原文中的圖 大家可以去看下 我模仿上面的做了個測試 出現了 已有打開的與此 Command 相關聯的 DataReader,必須首先將它關閉。
我的解決辦法是 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 等於 迭代到這次的 DepartmentID
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,能夠重用執行計划,這就大大提供了執行性能
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語句

繼續分析
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呢
如

這里面加了 院系ID 我以前一直覺得沒有必要加這個 今天在做這個導航屬性查找時 發現一個問題 做個小實驗
比如我想得到其中一個課程的ID 如果有院系ID 屬性 可以這么寫
var courses = db.Courses.ToList();
int i = courses[0].DepartmentID;
如果沒 可以這么寫
int i = courses[0].Department.DepartmentID;
首先 這個都沒有用貪婪加載 默認的延遲加載 如果你使用上面的 則不會往數據庫里去執行一條根據課程ID查找院系的SQL語句
但你使用下面的 則會往數據庫里發送一條查找語句
這點 EF做的是並不好的 在NH里 兩種方法 都不會發送 因為在下面那里使用了代理 而EF沒有
我想問的是 是我哪操作的不對么? 造成了這個原因? 請高手解答下
四.總結
關系的加載就結束了 其實寫關系加載的園子中有不少好文章了 我這里寫的少了些~~
不過關系的操作還沒有結束