原文: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. 取消jQuery validate验证

    有时候当我们在编辑页面点保存后加上了validate错误验证后又想用表单提交的方式返回界面没有清除验证就返回不了 加上这句话就清除验证了      注意:remove()是删除了相关标签  我这需求是 ...

  2. android 处理网络状态——无网,2g,3g,wifi,ethernet,other

    今天在一位很牛逼的学长的博客里面看到了这段代码后,很是激动啊,于是就“偷”了过来,嘿嘿....为自己也为更多需要它的程序媛 直接上代码: public class GetNetWorkStateAct ...

  3. MST最小生成树及克鲁斯卡尔(Kruskal)算法

    最小生成树MST,英文名如何拼写已忘,应该是min spaning tree吧.假设一个无向连通图有n个节点,那么它的生成树就是包括这n个节点的无环连通图,无环即形成树.最小生成树是对边上权重的考虑, ...

  4. iOS开发——面试笔试精华(四)

    面试笔试精华(四) 1.        Object-C有多继承吗?没有的话用什么代替?
 1>  OC是单继承,没有多继承 2>  有时可以用分类和协议来代替多继承 2.        ...

  5. android学习日记23--Android XML解析

    一.简述 XML语言是跨平台,JAVA对XML语言支持得比较好,android系统的应用层平台是JAVA做的,所以用XML.XML解析比较简单.XML解析就是将获取到的数据分离出来,基本的网络数据传输 ...

  6. 跟我学solr---吐槽一下,我的文章被抄袭啦

    今天闲来无事,就在在百度上搜了下面"跟我学solr",看看这几周来自己的努力成果怎么样,不搜还好,搜了就图学了.被原封不动地抄袭了!并且抄袭的文章还在百度排名第一,我自己的却被排到 ...

  7. emplace_back与push_back的区别

    std::vector::emplace_back     C++   Containers library   std::vector   template< class... Args &g ...

  8. poj 1654 Area 多边形面积

    /* poj 1654 Area 多边形面积 题目意思很简单,但是1000000的point开不了 */ #include<stdio.h> #include<math.h> ...

  9. Upgrading Applications

    Upgrading Applications If you have an existing Zend Framework v2 application, and want to update it ...

  10. STC89C52单片机内部EEPROM驱动

    STC89C52单片机自身带有4K的存储空间,分为8个扇区,每个扇区512字节,第一扇区起始地址为:0x2000, 结束地址为:21FF, 第八扇区起始地址为0x2E00,结束地址是2FFF #inc ...