原文:Reading Related Data with the Entity Framework in an ASP.NET MVC Application

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 DataUsing 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):

  修改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

    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);
}

[翻译][MVC 5 + EF 6] 7:加载相关数据的更多相关文章

  1. [翻译][MVC 5 + EF 6] 8:更新相关数据

    原文:Updating Related Data with the Entity Framework in an ASP.NET MVC Application 1.定制Course的Create和E ...

  2. [翻译 EF Core in Action 2.4] 加载相关数据

    Entity Framework Core in Action Entityframework Core in action是 Jon P smith 所著的关于Entityframework Cor ...

  3. Mego开发文档 - 加载关系数据

    加载关系数据 Mego允许您使用模型中的导航属性来加载相关数据对象.目前只支持强制加载数据对象.只有正确配置了关系才能加载关系数据,相关内容可参考关系配置文档. 加载对象属性 您可以使用该Includ ...

  4. 《Entity Framework 6 Recipes》中文翻译系列 (22) -----第五章 加载实体和导航属性之延迟加载

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 第五章 加载实体和导航属性 实体框架提供了非常棒的建模环境,它允许开发人员可视化地使 ...

  5. 在MVC应用程序中动态加载PartialView

    原文:在MVC应用程序中动态加载PartialView 有时候,我们不太想把PartialView直接Render在Html上,而是使用jQuery来动态加载,或是某一个事件来加载.为了演示与做好这个 ...

  6. EF之贪婪加载和延迟加载

    这篇文章将讨论查询结果的控制 在使用EF(Entity Framework)的过程中,很多时候我们会进行查询的操作,因此知道哪些数据会被加载到内存当中就至关重要.在多多的情况下,你可能并并不需要加载全 ...

  7. 【Spring MVC】Properties文件的加载

    [Spring MVC]Properties文件的加载 转载:https://www.cnblogs.com/yangchongxing/p/10726885.html 参考:https://java ...

  8. Entity Framework加载相关实体——延迟加载Lazy Loading、贪婪加载Eager Loading、显示加载Explicit Loading

    Entity Framework提供了三种加载相关实体的方法:Lazy Loading,Eager Loading和Explicit Loading.首先我们先来看一下MSDN对三种加载实体方法的定义 ...

  9. geotrellis使用(二十三)动态加载时间序列数据

    目录 前言 实现方法 总结 一.前言        今天要介绍的绝对是华丽的干货.比如我们从互联网上下载到了一系列(每天或者月平均等)的MODIS数据,我们怎么能够对比同一区域不同时间的数据情况,采用 ...

随机推荐

  1. C#实现在Winform中嵌入Word和Excel

    http://www.cnblogs.com/wuzi145/archive/2012/05/08/2490680.html 在此只是介绍一个简单控件:dsoframer.ocx的使用,这个控件需要通 ...

  2. 让DataGridView显示行号

          http://www.cnblogs.com/JuneZhang/archive/2011/11/21/2257630.html 为了表示行号,我们可以在DataGridView的RowP ...

  3. 彻底解决android读取中文txt的乱码(自动判断文档类型并转码

    原文:http://blog.csdn.net/handsomedylan/article/details/6138400 public String convertCodeAndGetText(St ...

  4. SAP 库存查询算法

    SAP 的后台有关库存的表,是一种很有意思的表.库存表分两种,一种是当前库存表,另一种是历史库存表.例如有MARD\MCHB\MSPR\MKOL等表,对应的历史库存表就是MARDH\MCHBH\MSP ...

  5. python内存管理

    python对象三要素: identity(值):对应于内存的地址,不可修改 type(类型):不可修改 value(值): mutable :可以修改 immutable:不可以修改 引用计数 当引 ...

  6. MS OFFICE 2010破解版安装

    受人所托发布一个MS OFFICE 2010破解版的傻瓜安装教程,刚好新本本也没有安装,安装过程中截了几个图就搞定了. 安装包: http://www.itopdog.cn/soft/office20 ...

  7. hadoop的相关资料链接

    hadoop的资料整理博客 http://www.itpub.net/thread-1588509-1-1.html

  8. [记录]calculate age based on date of birth

    calculate age based on date of birth know one new webiste:eval.in run php code

  9. http请求方法与响应状态码

    请求方法:GET POST HEAD PUT DELETE OPTIONS常用的方法是:GET POST GET提交可以在浏览器地址栏看到提交的信息 POST提交方式不显示在地址栏,相对于GET方式较 ...

  10. 查看源代码查找获取sd卡剩余容量的代码

    下载android源码,找到app下的Settings应用源码,导入Settings项目(android项目源码) 查找“可用空间”得到 <string name="memory_av ...