MVC3+EF4.1學習系列(五)----- EF查找導航屬性的幾種方式


通過上一篇的學習 我們把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語句

View Code

繼續分析

  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 @:&nbsp; @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沒有

我想問的是 是我哪操作的不對么? 造成了這個原因? 請高手解答下

四.總結

關系的加載就結束了 其實寫關系加載的園子中有不少好文章了 我這里寫的少了些~~

不過關系的操作還沒有結束


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM