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. Pro Aspnet MVC 4读书笔记(2) - The MVC Pattern

    Listing 3-1. The C# Auction Domain Model using System; using System.Collections.Generic; using Syste ...

  2. thinkphp 删除该表的最后一行

    问题叙述性说明: 文章连接动态连接表格,因为有被添加.有必须删除.动态添加到表格这似乎有点不合理.它应该只被添加到表格行.而不是增加一个新表格. 发布完整的代码在这里,加入表格新行和删除表格最后一行. ...

  3. 【剑指offer】堆栈推弹出序列

    转载请注明出处:http://blog.csdn.net/ns_code/article/details/26077863 剑指offer上的第22题,九度OJ上AC. 题目描写叙述: 输入两个整数序 ...

  4. 为什么解析 array_column不可用,

    不久前,自己在一个小项目,我要拿出每个元素的一维数组key的值,一维阵列的形成,突然想到php 有一种功能,实现. $array = array ( array ( "id" =& ...

  5. Oracle 修改字符集

    出现ORA-12899,是字符集引起的,中文在UTF-8中占3个字节,ZHS16GBK中占2个字节,而源dmp文件字符集是ZHS16GBK库里倒出来的数据,现在要导入到目标字符集为UTF-8的库里,所 ...

  6. [Network]Introduction and Basic concepts

    [该系列是检讨计算机网络知识.因为现在你想申请出国.因此,在写这篇博客系列的大多数英语.虽然英语,但大多数就是我自己的感受和理解,供大家学习和讨论起来] 1 Network Edge The devi ...

  7. java字节中的基本类型的职业的数目 (采访总是问)

    因为移动装置存储器中的移动开发的局限性,数据的字节数需要考虑往往在占领中使用的类型. 下面介绍下一个Java,以加深记忆. 在Java中一共同拥有8种基本数据类型,当中有4种整型,2种浮点类型,1种用 ...

  8. 多普勒失真信号采样Matlab模拟分析

    多普勒失真信号采样Matlab模拟分析 方案 水声通信指的是使用声信号在水中数据传输. 相对而言.电磁信号在水中吸收严重衰减过快,光信号受水中悬浮颗粒的影响,也无法完毕远距离传输. 这两种信号的传播距 ...

  9. Java生成CSV文件

    1.新CSVUtils.java文件: package com.saicfc.pmpf.internal.manage.utils; import java.io.BufferedWriter; im ...

  10. linq 分组求和

    static void Main()         {             DataTable dt = new DataTable();             dt.Columns.Add( ...