ASP.NET MVC应用程序使用异步及存储过程
ASP.NET MVC应用程序使用异步及存储过程
是微软官方教程Getting Started with Entity Framework 6 Code First using MVC 5 系列的翻译,这里是第七篇:为ASP.NET MVC应用程序更新相关数据
原文:Async and Stored Procedures with the Entity Framework in an ASP.NET MVC Application
译文版权所有,谢绝全文转载——但您可以在您的网站上添加到该教程的链接。
在之前的教程中,您已经学习了如何使用同步编程模型来读取和更新数据。在本教程中您将看到如何实现异步编程模型。因为能够更好地使用服务器资源,异步代码可以帮助应用程序更好地执行。
在本教程中您还会看到如何使用存储过程对实体进行插入、更新和删除操作。
下面的插图显示了您将要编写的页面:


为什么要使用麻烦的异步代码
一个WEB服务器只有很有限的可用线程,并且在高负载的情况下,可能所有的线程都会在使用中。当发生这种情况时,服务器将无法处理新的请求,直到有线程被释放。在同步代码的情况下,多个线程可能会关联起来,但实际上它们并不作任何工作而只是在等待IO完成。使用异步代码,当一个进程正在等待IO完成时,它的线程可以服务器腾出从而用于处理其他请求。因此,异步代码可以更高效地使用服务器资源,并且服务器能够在不延迟的情况下处理更多的流量。
在较早版本的.NET中,编写和测试异步代码是一件复杂、容易出错且难以调试的工作。在.Net 4.5中,编写、测试和调试异步代码变得简单起来,你应当总是使用异步代码,除非有理由不允许你这样做。异步代码会花费很少量的开销,但对于低流量情况下性能的损失是微不足道的。对于高流量的情况下,潜在的性能提示是巨大的。
有关异步编程的更多信息,请参阅Use .NET 4.5’s async support to avoid blocking calls。
创建系控制器
使用之前你创建其他控制器相同的方式来创建一个系控制器,但这次我们选择使用异步控制器操作选项。

下面的代码中,高亮部分显示了异步方法和同步方法的不同之处:
public async Task<ActionResult> Index()
{
var departments = db.Departments.Include(d => d.Administrator);
return View(await departments.ToListAsync());
}
我们应用了四个更改来启用实体框架数据库执行异步查询:
- 该方法使用了async关键字,它告诉编译器生成回调方法体的部分,并自动创建Task<ActionResult>返回对象。
- 返回类型从ActionResult更改为Task<ActionResult>。Task<T>类型表示正在进行的任务具有类型为T的结果。
- await关键字被应用到web服务调用。当编译器看到有此关键字时,在后台将该方法分为两个部分。第一部分结束于异步操作启动,第二部分被放入一个操作完成时的回调方法。
- 调用了ToList扩展方法的异步版本。
为什么只修改departments.ToList语句而不是departments= db.Departments语句?原因是只有被发送的数据库执行的查询或语句才能够使用异步执行。departments=db.Departments语句设置了一个查询,但直到调用ToList方法时该查询都不会执行。因此,只有ToList方法是异步执行的。
在Details方法和Httpget的Edit和Delete方法中,Find方法是导致查询被发送到数据库进行检索的方法,所以该方法是可以异步执行的。

public async Task<ActionResult> Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Department department = await db.Departments.FindAsync(id);
if (department == null)
{
return HttpNotFound();
}
return View(department);
}

在Create,HttpPost的Edit和DeleteConfirmed方法中,是SaveChanges方法导致命令执行,而像db.Department.Add(department)方法只是导致实体在内存中的修改。

public async Task<ActionResult> Create([Bind(Include="DepartmentID,Name,Budget,StartDate,InstructorID")] Department department)
{
if (ModelState.IsValid)
{
db.Departments.Add(department);
await db.SaveChangesAsync();
return RedirectToAction("Index");
} ViewBag.InstructorID = new SelectList(db.Instructors, "ID", "LastName", department.InstructorID);
return View(department);
}

打开Views\Department\Index.cshtml,并使用下面的代码替换原来的:

@model IEnumerable<ContosoUniversity.Models.Department>
@{
ViewBag.Title = "Departments";
}
<h2>Departments</h2>
<p>
@Html.ActionLink("Create New", "Create")
</p>
<table class="table">
<tr>
<th>
@Html.DisplayNameFor(model => model.Name)
</th>
<th>
@Html.DisplayNameFor(model => model.Budget)
</th>
<th>
@Html.DisplayNameFor(model => model.StartDate)
</th>
<th>Administrator</th>
<th></th>
</tr>
@foreach (var item in Model)
{
<tr>
<td>
@Html.DisplayFor(modelItem => item.Name)
</td>
<td>
@Html.DisplayFor(modelItem => item.Budget)
</td>
<td>
@Html.DisplayFor(modelItem => item.StartDate)
</td>
<td>
@Html.DisplayFor(modelItem => item.Administrator.FullName)
</td>
<td>
@Html.ActionLink("Edit", "Edit", new { id = item.DepartmentID }) |
@Html.ActionLink("Details", "Details", new { id = item.DepartmentID }) |
@Html.ActionLink("Delete", "Delete", new { id = item.DepartmentID })
</td>
</tr>
}
</table>

代码修改了标题,并将系主任列移动到右边,同时提供了系主任的姓名。
在创建、删除、详情和编辑视图中,将InstructorID字段的标题更改为"系主任",类似之前你在课程视图中将系名称字段更改为"系"中那样。
在创建和编辑视图使用下面的代码:
@Html.DisplayFor(model => model.Department.Name)
在删除和详细视图使用下面的代码:
<dt>
Administrator
</dt>
运行该应用程序,并点击系选项卡。

程序正常地运行,就跟其他的控制器一样。但在此控制器中,所有SQL查询都是异步执行的。
当您在实体框架中使用异步编程要注意的一些事情:
- 异步代码不是线程安全的。换言之,不要使用同一个上下文实例以并行方式来执行多个操作。
- 如果你想要利用异步代码的性能优势,请确保您正在使用的所有库软件包(例如分页),在包中进行的数据库查询等任何实体框架方法也使用异步执行。
用于插入、更新和删除的存储过程
一些开发人员和DBA更愿意使用存储过程来访问数据库。早期版本的实体框架中,您可以使用执行原始SQL查询的方式来检索数据来执行存储过程,但您不能使用存储过程来用于更新操作。在实体框架6中,您可以很容易地配置Code First来使用存储过程。
- 在DAL\SchoolContext.cs中,将高亮的代码添加到OnModelCreating方法。

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>(); modelBuilder.Entity<Course>()
.HasMany(c => c.Instructors).WithMany(i => i.Courses)
.Map(t => t.MapLeftKey("CourseID")
.MapRightKey("InstructorID")
.ToTable("CourseInstructor"));
modelBuilder.Entity<Department>().MapToStoredProcedures();
}
此代码指示实体框架使用存储过程来进行Department实体的插入、更新和删除操作。
- 在程序包管理控制台中,输入以下命令:
add-migration DepartmentSP
打开Migrations\<时间戳>_DepartmentSP.cs,参阅Up方法中的代码,你会看到插入、更新和删除的存储过程:

public override void Up()
{
CreateStoredProcedure(
"dbo.Department_Insert",
p => new
{
Name = p.String(maxLength: 50),
Budget = p.Decimal(precision: 19, scale: 4, storeType: "money"),
StartDate = p.DateTime(),
InstructorID = p.Int(),
},
body:
@"INSERT [dbo].[Department]([Name], [Budget], [StartDate], [InstructorID])
VALUES (@Name, @Budget, @StartDate, @InstructorID) DECLARE @DepartmentID int
SELECT @DepartmentID = [DepartmentID]
FROM [dbo].[Department]
WHERE @@ROWCOUNT > 0 AND [DepartmentID] = scope_identity() SELECT t0.[DepartmentID]
FROM [dbo].[Department] AS t0
WHERE @@ROWCOUNT > 0 AND t0.[DepartmentID] = @DepartmentID"
); CreateStoredProcedure(
"dbo.Department_Update",
p => new
{
DepartmentID = p.Int(),
Name = p.String(maxLength: 50),
Budget = p.Decimal(precision: 19, scale: 4, storeType: "money"),
StartDate = p.DateTime(),
InstructorID = p.Int(),
},
body:
@"UPDATE [dbo].[Department]
SET [Name] = @Name, [Budget] = @Budget, [StartDate] = @StartDate, [InstructorID] = @InstructorID
WHERE ([DepartmentID] = @DepartmentID)"
); CreateStoredProcedure(
"dbo.Department_Delete",
p => new
{
DepartmentID = p.Int(),
},
body:
@"DELETE [dbo].[Department]
WHERE ([DepartmentID] = @DepartmentID)"
); }
- 在软件包管理器控制台中,输入以下命令:
update-database
- 在调试模型下运行该应用程序,单击系选项卡,然后单击创建。
- 为一个新系输入相关数据然后单击创建。

- 在VS中的输出窗口查看日志。

Code First使用默认名创建了存储过程。如果您正在使用现有的数据库,您可能需要自定义存储过程的名称,有关如何操作的信息,请参阅Entity Framework Code First Insert/Update/Delete Stored Procedures。
如果你想要自定义存储过程,你可以编辑迁移中的脚手架代码里的Up方法来创建存储过程。使用这种方法时您的更改将在应用迁移时或部署到生产环境后自动进行。
如果你想修改一个在之前的迁移中已经创建的的存储过程,你可以使用Add-Migration命令来生成一个空白的迁移,然后手动编写代码调用AlterStoredProcedure方法。
部署到Windows Azure
本章跳过……
总结
在本教程中,您看到了如何提高服务器效率。通过编写异步执行代码以及使用存储过程来进行插入、更新和删除操作。在接下来的教程中,您将看到当多个用户试图编辑同一条纪录时如何防止数据丢失。
作者信息
Tom Dykstra - Tom Dykstra是微软Web平台及工具团队的高级程序员,作家。
ASP.NET MVC应用程序使用异步及存储过程的更多相关文章
- [渣译文] 使用 MVC 5 的 EF6 Code First 入门 系列:为ASP.NET MVC应用程序使用异步及存储过程
这是微软官方教程Getting Started with Entity Framework 6 Code First using MVC 5 系列的翻译,这里是第九篇:为ASP.NET MVC应用程序 ...
- [渣译文] 使用 MVC 5 的 EF6 Code First 入门 系列:为ASP.NET MVC应用程序更新相关数据
这是微软官方教程Getting Started with Entity Framework 6 Code First using MVC 5 系列的翻译,这里是第八篇:为ASP.NET MVC应用程序 ...
- ASP.NET MVC应用程序更新相关数据
为ASP.NET MVC应用程序更新相关数据 这是微软官方教程Getting Started with Entity Framework 6 Code First using MVC 5 系列的翻译, ...
- 【ASP.NET】第一个ASP.NET MVC应用程序
本系列目录:ASP.NET MVC4入门到精通系列目录汇总 开发流程 新建Controller 创建Action 根据Action创建View 在Action获取数据并生产ActionResult传递 ...
- ASP.NET MVC 学习笔记-7.自定义配置信息 ASP.NET MVC 学习笔记-6.异步控制器 ASP.NET MVC 学习笔记-5.Controller与View的数据传递 ASP.NET MVC 学习笔记-4.ASP.NET MVC中Ajax的应用 ASP.NET MVC 学习笔记-3.面向对象设计原则
ASP.NET MVC 学习笔记-7.自定义配置信息 ASP.NET程序中的web.config文件中,在appSettings这个配置节中能够保存一些配置,比如, 1 <appSettin ...
- 使用Metrics.NET 构建 ASP.NET MVC 应用程序的性能指标
通常我们需要监测ASP.NET MVC 或 Web API 的应用程序的性能时,通常采用的是自定义性能计数器,性能计数器会引发无休止的运维问题(损坏的计数器.权限问题等).这篇文章向你介绍一个新的替代 ...
- asp.net MVC 应用程序的生命周期
下面这篇文章总结了 asp.net MVC 框架程序的生命周期.觉得写得不错,故转载一下. 转载自:http://www.cnblogs.com/yplong/p/5582576.html ...
- 学习ASP.NET MVC(一)——我的第一个ASP.NET MVC应用程序
学习ASP.NET MVC系列: 学习ASP.NET MVC(一)——我的第一个ASP.NET MVC应用程序 学习ASP.NET MVC(二)——我的第一个ASP.NET MVC 控制器 学习ASP ...
- [渣译文] 使用 MVC 5 的 EF6 Code First 入门 系列:为ASP.NET MVC应用程序创建更复杂的数据模型
这是微软官方教程Getting Started with Entity Framework 6 Code First using MVC 5 系列的翻译,这里是第六篇:为ASP.NET MVC应用程序 ...
随机推荐
- 修改Oracle XE Listener 占用的1521、8080端口
修改Oracle XE Listener 占用的1521.8080端口今天在帮开发人员Demon安装oracleXE时,有这么一段提示: [sql] Destination Folder: D:\ ...
- Oracle 数据库 有用的sql语句
; SELECT to_date('2014-12-01', 'yyyy-mm-dd') + numtodsinterval(rownum , 'day') FROM DUAL CONNECT BY ...
- 假设动态运行java文字,当在脚本式配置,这是非常方便的
package com.bfrj.core.groovy; import java.util.HashMap; import java.util.Map; import org.jeecgframew ...
- MVC 编程模型及其变种
MVC 编程模型及其变种 MVC全称是Model View Controller, 这是一个模型(model)-查看(view)-调节器(controller)缩写,这是通过通用的编程模型非.MVC当 ...
- POJ 2502 Subway (Dijkstra 最短+建设规划)
Subway Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 6689 Accepted: 2176 Descriptio ...
- A hard puzzle 1097
Problem Description lcy gives a hard puzzle to feng5166,lwg,JGShining and Ignatius: gave a and b,how ...
- React.js终探(七)(完)
我们在前面介绍了组件的各种特性,这一节我们来说说多组件的情况. 在实际开发中,我们的组件难免会遇到有公共部分的情况,如果是个别情况还好,但如果数量比较多的话,那这时候,就需要公用了. 怎么公用呢? R ...
- Android开发手册 (Android的手工教程MtAndroid开发手册)
放出版许可协议 1.0 或者更新版本号. 未经版权全部者明白授权,禁止发行本文档及其被实质上改动的版本号. 未经版权全部者事先授权.禁止将此作品及其衍生作品以标准(纸质)书籍形式发行. 假设有兴趣再 ...
- Cookie基础
周末百度笔试,答得题都会,就是不仔细不心细,提前一个小时交卷子,想起来就已经晚了.问了一个cookie的问题,我SB的蒙住了,于是乎,似乎是跪掉了,回来后总结了下Cooke的相关问题.###获取coo ...
- MVC 发布到 windows2003遇到 'System.Web.WebPages.Razor 错误提示
摘自: http://blog.csdn.net/lanqiao825/article/details/7840606 http://bbs.maticsoft.com/forum.php?mod=v ...