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来使用存储过程。

  1. 在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实体的插入、更新和删除操作。

  2. 在程序包管理控制台中,输入以下命令:
    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)"
    ); }
  3. 在软件包管理器控制台中,输入以下命令:
    update-database 
  4. 在调试模型下运行该应用程序,单击系选项卡,然后单击创建。
  5. 为一个新系输入相关数据然后单击创建。
  6. 在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应用程序使用异步及存储过程的更多相关文章

  1. [渣译文] 使用 MVC 5 的 EF6 Code First 入门 系列:为ASP.NET MVC应用程序使用异步及存储过程

    这是微软官方教程Getting Started with Entity Framework 6 Code First using MVC 5 系列的翻译,这里是第九篇:为ASP.NET MVC应用程序 ...

  2. [渣译文] 使用 MVC 5 的 EF6 Code First 入门 系列:为ASP.NET MVC应用程序更新相关数据

    这是微软官方教程Getting Started with Entity Framework 6 Code First using MVC 5 系列的翻译,这里是第八篇:为ASP.NET MVC应用程序 ...

  3. ASP.NET MVC应用程序更新相关数据

    为ASP.NET MVC应用程序更新相关数据 这是微软官方教程Getting Started with Entity Framework 6 Code First using MVC 5 系列的翻译, ...

  4. 【ASP.NET】第一个ASP.NET MVC应用程序

    本系列目录:ASP.NET MVC4入门到精通系列目录汇总 开发流程 新建Controller 创建Action 根据Action创建View 在Action获取数据并生产ActionResult传递 ...

  5. 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 ...

  6. 使用Metrics.NET 构建 ASP.NET MVC 应用程序的性能指标

    通常我们需要监测ASP.NET MVC 或 Web API 的应用程序的性能时,通常采用的是自定义性能计数器,性能计数器会引发无休止的运维问题(损坏的计数器.权限问题等).这篇文章向你介绍一个新的替代 ...

  7. asp.net MVC 应用程序的生命周期

    下面这篇文章总结了 asp.net MVC 框架程序的生命周期.觉得写得不错,故转载一下. 转载自:http://www.cnblogs.com/yplong/p/5582576.html       ...

  8. 学习ASP.NET MVC(一)——我的第一个ASP.NET MVC应用程序

    学习ASP.NET MVC系列: 学习ASP.NET MVC(一)——我的第一个ASP.NET MVC应用程序 学习ASP.NET MVC(二)——我的第一个ASP.NET MVC 控制器 学习ASP ...

  9. [渣译文] 使用 MVC 5 的 EF6 Code First 入门 系列:为ASP.NET MVC应用程序创建更复杂的数据模型

    这是微软官方教程Getting Started with Entity Framework 6 Code First using MVC 5 系列的翻译,这里是第六篇:为ASP.NET MVC应用程序 ...

随机推荐

  1. css居中方式总结

    方法一: line-height <div class="vertical" style="width:200px;height:200px;border:2px ...

  2. GeeksforGeeks - Adjacency List邻接矩阵C\C++代码

    邻接矩阵的图示: 构建一个这种无向邻接矩阵. 參考站点: http://www.geeksforgeeks.org/graph-and-its-representations/ 这里写了个类,添加删除 ...

  3. ASP.Net MVC 数据处理

    ASP.Net MVC 数据处理   第三天我们将学习Asp.Net中数据处理功能,了解数据访问层,EF,以及EF中常用的代码实现方式,创建数据访问层和数据入口,处理Post数据,以及数据验证等功能. ...

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

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

  5. SP服务商收益究竟有多大?

    揭秘spspsp服务商怎样盈利?代办sp服务商又称持增值电信----移动网信息服务许可证信息提供商,sp主要业务有短信彩信(手机报.短信群发.客服系统).WAP.彩铃.IVR.百宝箱.JAVA游戏.B ...

  6. Oracle OS认证和口令文件认证方法

    OS认证 1.在SQLNET.ORA(位于$ORACLE_HOME/NETWORK/ADMIN文件夹中)文件里,使用vi编辑,凝视掉#SQLNET.AUTHENTICATION_SERVICES = ...

  7. ECLIPSE实现了界面显示所有类

    按下接口名称CTRL+T实现全面实施的接口类显示 版权声明:本文博客原创文章,博客,未经同意,不得转载.

  8. 《浪潮之巅》完全系类——IT人士必读经典

    浪潮之巅 第一章 帝国的余辉(AT&T) 浪潮之巅第二章 — 蓝色巨人(IBM) 浪潮之巅第三章 — “水果”公司的复兴 (乔布斯和苹果公司) 浪潮之巅第四章 — 计算机工业的生态链 浪潮之巅 ...

  9. 《C语言及程序设计初步》网络课程主页

    题记 CSDN要开在线教育频道,向我发出邀请,看能否开些课程. 我近日一直在关注着翻转课堂,试图在传统课堂中引入新的元素,这须要资源建设的积累.没有时间表的工作,非常难把握. 为CSDN做在线课程,为 ...

  10. 数据泵expdp,impdp使用结

    EXPDP,IMPDP远程导出,导入数据库到本地 1.本地建立导出用户hr_exp并全然删除机hr的用户 C:\Users\Administrator>sqlplus / as sysdba S ...