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平台及工具团队的高级程序员,作家。
  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应用程序 ... 
随机推荐
- ASP.NET MVC:Razor 引入命名空间
			原文:ASP.NET MVC:Razor 引入命名空间 页面中引用 c# @using MvcApplication83.Models @using MvcApplication83.Common 行 ... 
- 页面加速Chromium 预压 Prerendering
			前者已经推出了一个博客prefetch预加载的网页资源,以提高网页加载速度,下面我们一起来看一下,以加速网页chromium prerendering.介绍prerendering之前.先介绍两个概念 ... 
- android tv 全屏幕垂直画
			它们的定义view 采纳canvas双缓冲方式,它可以减少呈现时间.提高性能. StaggeredView.java 源码例如以下: package com.xxx.demo; import andr ... 
- 如何让格斗游戏的横版过关(2) Cocos2d-x 2.0.4
			在第一章<如何使横版格戏>基础上.添加角色运动.碰撞.敌人.AI和音乐音效,原文<How To Make A Side-Scrolling Beat 'Em Up Game Like ... 
- iOS ... NS_REQUIRES_NIL_TERMINATION
			看到官方的一个样例不错,这里留记. #import <Cocoa/Cocoa.h> @interface NSMutableArray (variadicMethodExample) - ... 
- 关联A850刷机包 高级电源 时间中心 优化 ROOT 动力 美化 简化
			ROM简介 1.合并app以及framewok框架apk 2.破解安卓核心验证 3.加入busybox指令集 4.加入Root权限 5.时间居中显示.通知图标不会重叠 6.加入网速显示 7.加入%1精 ... 
- (一)spring MVC基本概念和流程
			MVC的概念 Model(模型):包含数据和行为.不过现在一般都分离开来:Value Object(数据) 和 服务层(行为). View(视图):负责进行模型的展示,一般就是展示给用户的界面. Co ... 
- 细说 ASP.NET Cache 及其高级用法【转】
			阅读目录 开始 Cache的基本用途 Cache的定义 Cache常见用法 Cache类的特点 缓存项的过期时间 缓存项的依赖关系 - 依赖其它缓存项 缓存项的依赖关系 - 文件依赖 缓存项的移除优先 ... 
- shell 命名管道,进程间通信
			命名管道基础 命名管道也被称为FIFO文件, 在文件系统中是可见的,并且跟其它文件一样可以读写! 命名管道特点: 当写进程向管道中写数据的时候,如果没有进程读取这些数据,写进程会堵塞 当读取管道中的数 ... 
- oracle_彻底删除oracle
			例如ORACLE安装路径为:C:\ORACLE 实现方法: 1. 开始->设置->控制面板->管理工具->服务 停止所有Oracle服务. 2. 开始->程序->O ... 
