MVC5 Entity Framework学习之异步和存储过程

在之前的文章中,你已经学习了如何使用同步编程模型来读取和更新数据,在本节中你将学习如何实现异步编程模型。异步可以使应用程序执行更有效率,因为它可以更有效的使用服务器资源。

同样在本节中你还将学习如何针对实体的insert, update, 和delete操作使用存储过程。

最后将应用程序部署到 Windows Azure。

下面是完成后的页面

为什么要使用异步代码

一个web服务器的可用线程是有限的,在高负载情况下,所有的可用线程可能都在被使用。当出现这种情况时,服务器将无法处理新的请求,直到有线程被释放。使用同步代码,大量线程将被锁定,但实际上它们并未作任何工作而只是在等待IO完成。使用异步代码,当一个进程正在等待IO完成时,它的线程会被服务器释放并去处理其它的请求。因此,异步代码可以更高效地使用服务器资源,并且能够在没有延迟的情况下处理更多的流量。

在.NET的早期版本中,编写和测试异步代码是复杂的、易于出错的,且难以调试。但在.Net 4.5中,编写、测试和调试异步代码是如此简单,所以你应该经常使用异步代码。异步代码会花费较少的开销,在低流量情况下,对性能的影响是可以忽略不计的,但在高流量的情况下,潜在性能的提升是巨大的。

创建Department控制器

创建一个Department控制器,选中Use async controller actions 复选框

查看Index方法中添加的异步代码

public async Task<ActionResult> Index()
{    var departments = db.Departments.Include(d => d.Administrator);    return View(await departments.ToListAsync());
}

共有四处更改来让Entity Framework使用异步执行数据库查询:

  • 方法使用了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(Department department)
{    if (ModelState.IsValid)
    {
        db.Departments.Add(department);
    await db.SaveChangesAsync();        return RedirectToAction("Index");
    }

打开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>

上面的代码将标题从Index 更改为Departments,将Administrator 名称移动到右侧,并提供了Administrator 的全名。

在Create, Delete,,Details和Edit视图中,将InstructorID字段的标题修改为Administrator

在Create 和Edit视图中使用下面的代码

<label class="control-label col-md-2" for="InstructorID">Administrator</label>

在Delete和Details视图中使用下面的代码

<dt>
    Administrator</dt>

运行项目,点击Departments 选项卡

程序运行一切正常,但在此控制器中,所有SQL查询都是异步执行的。

当你使用Entity Framework来进行异步编程时要注意:

  • 异步代码不是线程安全的。换句话说,不要使用同一个上下文实例并行执行多个操作。
  • 如果你希望能够利用异步代码的性能优势,请确保你正在使用的所有库包(例如分页)在调用任何Entity Framework方法并将查询发送至数据库时也同样要使用异步执行。

在insert, update和delete操作中使用存储过程

某些开发人员和DBA喜欢使用存储过程来进行数据库访问。在Entity Framework的早期版本中,你可以通过原始SQL查询来使用存储过程来检索数据,但是你不能在更新操作中使用存储过程。在Entity Framework 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();
}

上面的代码指定Entity Framework对于Department 实体的insert,update和delete操作使用存储过程。

2.在Package Manage Console中输入如下命令

add-migration DepartmentSP

打开Migrations\<timestamp>_DepartmentSP.cs,查看Up方法中创建的Insert, Update和Delete存储过程

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.在Package Manage Console中输入如下命令

update-database

4.运行项目,点击Departments选项卡,然后点击Create New

5.输入数据,点击Create

6.在 Visual Studio的Output窗口可以看到使用了存储过程来插入了Department行

Code First使用默认名称创建了存储过程。如果你正在使用现有的数据库,你可能需要自定义存储过程的名称以便使用数据库中已定义的存储过程。

如果你希望自定义存储过程,你可以编辑Up方法中创建存储过程的框架代码。当不论何时进行迁移时,你所做的这些更改会被表现出来,当在部署后迁移自动在生产环境中运行时,你所做的这些更改就会被应用到生产环境数据库。

如果你希望修改在之前的迁移中创建的的存储过程,你可以使用Add-Migration命令来生成一个空的迁移,然后手动编写代码调用AlterStoredProcedure方法。

部署到Windows Azure

本节需要你完成之前的 MVC5 Entity Framework学习之Code First迁移和部署 教程中的将应用程序部署到Windows Azure章节,如果在迁移中出现错误,你需要删除本地数据库来解决它。

1.在Visual Studio的Solution Explorer中,右键单击项目,选择Publish

2.点击Publish,Visual Studio会将应用程序部署到Windows Azure并在浏览器中打开该程序

3.测试应用程序以验证其是否工作正常

当你第一次运行应用程序并访问数据库时,Entity Framework会执行所有迁移中的Up方法来确保数据模型的一致性。

原文: Async and Stored Procedures with the Entity Framework in an ASP.NET MVC Application

本文分享自微信公众号 - 我为Net狂(dotNetCrazy)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2016-02-25

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏软件开发

前端MVC Vue2学习总结(七)——ES6与Module模块化、Vue-cli脚手架搭建、开发、发布项目与综合示例

使用vue-cli可以规范项目,提高开发效率,但是使用vue-cli时需要一些ECMAScript6的知识,特别是ES6中的模块管理内容,本章先介绍ES6中的基...

49770
来自专栏DOTNET

ASP.NET MVC编程——缓存

Web缓存分为服务端缓存和客户端缓存。 1 服务端缓存 1.1请求域内的缓存:HttpContext.Items 类型: HttpContext.Items的类...

32760
来自专栏DOTNET

ASP.NET MVC编程——模型

1 ViewModel 是一种专门提供给View使用的模型,使用ViewModel的理由是实体或领域模型所包含的属性比View使用的多或少,这种情况下实体或领域...

34880
来自专栏DOTNET

ASP.NET MVC编程——视图

1Razon语法 使用@符号后接C#或VB.NET语句的方式。 基本规则 1)变量 @后直接变量即可 2)代码块 为使用表达式或多行代码,@后跟大括号将多行代码...

367100
来自专栏软件开发

前端MVC Vue2学习总结(六)——axios与跨域HTTP请求、Lodash工具库

一、axios Vue更新到2.0之后宣告不再对vue-resource更新,推荐使用axios,axios是一个用于客户端与服务器通信的组件,axios 是一...

639100
来自专栏DOTNET

ASP.NET MVC编程——验证、授权与安全

1 验证 一般采用表单验证完成登陆验证,建议结合SSL使用。为限制控制器只能执行HTTPS,使用RequireHttpsAttribute 2 授权 对账户的...

41360
来自专栏DOTNET

ASP.NET MVC编程——控制器

每一个请求都会经过控制器处理,控制器中的每个方法被称为控制器操作,它处理具体的请求。 1操作输入参数 控制器的操作的输入参数可以是内置类型也可以是自定义类型。 ...

30790
来自专栏DOTNET

ASP.NET MVC编程——路由

框架自动生成的路由配置 ? 上图中,路由配置文件为App_Start文件夹下的RouteConfig.cs。 代码如下: public class RouteC...

399120
来自专栏DOTNET

ASP.NET MVC编程——错误处理与日记

ASP.NET MVC的错误处理应考虑到这几个方面:模型绑定期间发生的错误,未能路由到指定操作,针对控制器的错误处理。使用配置文件可以帮助我们处理异常,但是不够...

38660
来自专栏软件开发

前端MVC Vue2学习总结(五)——表单输入绑定、组件

一、表单输入绑定 1.1、基础用法 你可以用 v-model 指令在表单控件元素上创建双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素。尽管有些神奇,...

533140

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励