原文: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. JNI函数复杂对象传递

    主要操作内容,包括如下几个部分: 1.在Native层返回一个字符串 2.从Native层返回一个int型二维数组(int a[ ][ ]) 3.从Native层操作Java层的类: 读取/设置类属性 ...

  2. HDU 5478 Can you find it 随机化 数学

    Can you find it Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://acm.hdu.edu.cn/showproblem.php?pi ...

  3. BZOJ [ZJOI2008]泡泡堂BNB 贪心

    [ZJOI2008]泡泡堂BNB Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://www.lydsy.com/JudgeOnline/proble ...

  4. 获取windows版本信息

    procedure TForm1.Button1Click(Sender: TObject); Var     OSVI:OSVERSIONINFO; begin     OSVI.dwOSversi ...

  5. WCF之初体验

    什么是WCF? WCF的全称是:Windows通信基础(WindowsCommunication Foundation).本质来讲,他是一套软件开发包. WCF和WebService的差别 Webse ...

  6. 详解Android Handler的使用

    我们进行Android开发时,Handler可以说是使用非常频繁的一个概念,它的用处不言而喻.本文就详细介绍Handler的基本概念和用法. Handler的基本概念         Handler主 ...

  7. javascript中false值及其常见比较运算

    1. ture或者false的值 if判断中会被隐式转换成false的boolean类型的值有 false, 0, undefined , null , '', NaN(not a number) 除 ...

  8. valgrind 生成mysqld调用图之 select now()跟踪

    1.mysqld起动方式: 1.mysqld以root用户运行 valgrind --tool=callgrind --separate-threads=yes  --trace-children=y ...

  9. Android中的事件分发机制总结

    Android 的事件分发机制 一.View的事件分发总结: View的onTouchEvent和OnTouch区别  还是以自定义的TestButton为例. 我们可以通过重写onTouchEven ...

  10. ios webview 图片自适应屏幕宽度

    //这个知识点主要是自己最近在尝试写后台接口 在移动端展示的时候需要用到这个知识点,在webViewDidFinishLoad方法里面执行一段js代码 拿到各个图片 判断其宽度是否大于当前手机屏幕尺寸 ...