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

1.为什么使用异步代码

  一个服务器可用的线程数量是有限的,在高负载的情况下所有的可用线程都可能在使用。在这种情况下,在线程被释放之前服务器不能处理新的请求。在同步代码中,很多线程被占用但是实际上没有做任何操作,因为它们在等待I/O完成。在异步代码中,当一个进程在等待I/O完成的过程中,它的线程将会被释放用于处理其他请求。这样,异步代码可以更高效地使用服务器资源,并且使服务器可以没有延迟地处理更多访问。

  在之前的.NET版本中,编写和测试异步代码比较复杂、容易出错并且难以调试。在.NET 4.5中,编写、测试和调试异步代码变得非常简单,通常我们需要编写异步代码,除非我们有一个不这样做的理由。异步代码引入少量的开销,对于低流量情况下性能下降可以忽略不计,而对于高流量情况下性能改善的潜力巨大。

  更多关于异步编程信息请查看:Use .NET 4.5’s async support to avoid blocking calls

2.新建Department控制器

  查看产生的Index方法的代码:

public async Task<ActionResult> Index()
{
var departments = db.Departments.Include(d => d.Administrator);
return View(await departments.ToListAsync());
}
  • 方法使用async 关键字标记,用来告诉编译器为方法体的部分产生回调,并且自动创建返回的Task<ActionResult>对象。
  • 返回类型从ActionResult变为 Task<ActionResult>
  • await关键字被用于web service调用。当编译器遇到这个关键字,会在幕后将该方法分为两部分。第一部分以异步操作开始时结束。第二部分被放入操作完成后调用的回调方法中。
  • 异步版本的ToList扩展被调用。

  为什么 departments.ToList语句被修改而departments = db.Departments语句没有被修改呢?因为,只有执行查询或者被发送到数据库的命令才会异步执行。departments = db.Departments语句生成查询,但是查询在 departments.ToList语句才被执行。因此,只有ToList方法被异步执行。下面的代码中亦是同一原因:

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);
}
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>

  在Create、Delete、Details和Edit视图,将 InstructorID的标题改为“Administrator”:

    在Create和Edit视图:

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

    在Delete和Details视图:

<dt>
Administrator
</dt>

  运行:

  在使用EF异步编程需要注意的问题:

  • 异步代码是线程不安全的。因此,不要试图使用同一个上下文实例并行处理多个操作。
  • 如果想要利用异步代码的性能优势,需要保证我们使用的库包(例如分页)在调用任何EF方法查询数据库时同样使用异步。

3.使用存储过程插入、更新和删除

  在早期的EF版本中,我们可以通过执行原生的SQL查询来使用存储过程查询数据,但是不能使用存储过程执行更新操作。在EF6中很容易配置Code First使用存储过程。

3.1.在DAL\SchoolContext.cs中添加代码:

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();
}

  该行代码告诉EF在对Department进行插入、更新和删除操作时使用存储过程。

3.2.在Package Manage Console输入命令:

add-migration DepartmentSP

  打开Migrations\<timestamp>_DepartmentSP.cs,可以看到:

public override void Up()
{
CreateStoredProcedure(
"dbo.Department_Insert",
p => new
{
Name = p.String(maxLength: ),
Budget = p.Decimal(precision: , scale: , 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: ),
Budget = p.Decimal(precision: , scale: , 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.3.在Package Manage Console输入命令:

update-database

3.4.运行:

  Code First创建默认的存储过程名。如果我们使用一个已经存在的数据库,为了使用数据库中已经定义的存储过程,我们需要指定存储过程的名字。更多信息请参考: Entity Framework Code First Insert/Update/Delete Stored Procedures

  如果我们想要指定产生的存储过程做什么,我们可以编辑迁移中的Up方法中产生存储过程的代码。通过这种方式,我们修改的代码不管是迁移运行时还是当部署后迁移自动在产品数据库运行都会起作用。

  如果我们想要修改一个在早期的迁移中创建的存储过程,我们可以使用Add-Migration命令来产生一个空白的迁移,然后手动编写调用AlterStoredProcedure方法的代码。

4.部署到Azure

  本节操作需要完成了之前可选的部署到Azure。如果我们遇到迁移错误,并且决定通过删除本地项目中的数据库来解决此问题,那么可以跳过本节。

4.1.在Visual Studio中选中项目右键,Solution Explorer选中Publish

4.2.点击Publish

4.3.测试程序是否正常工作。

[翻译][MVC 5 + EF 6] 9:异步和存储过程的更多相关文章

  1. [翻译][MVC 5 + EF 6] 12[完结]:高级场景

    原文:Advanced Entity Framework 6 Scenarios for an MVC 5 Web Application 1.执行原生SQL查询: EF Code First API ...

  2. [翻译][MVC 5 + EF 6] 11:实现继承

    原文:Implementing Inheritance with the Entity Framework 6 in an ASP.NET MVC 5 Application 1.选择继承映射到数据库 ...

  3. [翻译][MVC 5 + EF 6] 10:处理并发

    原文:Handling Concurrency with the Entity Framework 6 in an ASP.NET MVC 5 Application 1.并发冲突: 当一个用户编辑一 ...

  4. [翻译][MVC 5 + EF 6] 8:更新相关数据

    原文:Updating Related Data with the Entity Framework in an ASP.NET MVC Application 1.定制Course的Create和E ...

  5. [翻译][MVC 5 + EF 6] 7:加载相关数据

    原文:Reading Related Data with the Entity Framework in an ASP.NET MVC Application 1.延迟(Lazy)加载.预先(Eage ...

  6. [翻译][MVC 5 + EF 6] 6:创建更复杂的数据模型

    原文:Creating a More Complex Data Model for an ASP.NET MVC Application 前面的教程中,我们使用的是由三个实体组成的简单的数据模型.在本 ...

  7. [翻译][MVC 5 + EF 6] 5:Code First数据库迁移与程序部署

    原文:Code First Migrations and Deployment with the Entity Framework in an ASP.NET MVC Application 1.启用 ...

  8. [翻译][MVC 5 + EF 6] 4:弹性连接和命令拦截

    原文:Connection Resiliency and Command Interception with the Entity Framework in an ASP.NET MVC Applic ...

  9. [翻译][MVC 5 + EF 6] 3:排序、过滤、分页

    原文:Sorting, Filtering, and Paging with the Entity Framework in an ASP.NET MVC Application 1.添加排序: 1. ...

随机推荐

  1. iOS开发-在表单元中添加子视图

    #import <UIKit/UIKit.h> @interface NameAndColorCellTableViewCell : UITableViewCell @property(c ...

  2. JNI-使用RegisterNatives注册本地方法

    转自: http://blog.chinaunix.net/uid-26009923-id-3410141.html 1. 以前在jni中写本地方法时,都会写成 Java_com_example_he ...

  3. Central Authentication Service

    国内私募机构九鼎控股打造APP,来就送 20元现金领取地址:http://jdb.jiudingcapital.com/phone.html内部邀请码:C8E245J (不写邀请码,没有现金送)国内私 ...

  4. HDU 5074 Hatsune Miku(DP)

    Problem Description Hatsune Miku is a popular virtual singer. It is very popular in both Japan and C ...

  5. php获取内容中第一张图片地址

    $note = '<img src="http://images.xxx.com/article/cover/201601/20/141539161273.png?imageView2 ...

  6. 2015北京网络赛 H题 Fractal 找规律

    Fractal Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://hihocoder.com/contest/acmicpc2015beijingo ...

  7. git reset and git checkout

    git reset --hard <commit>: 1.替换引用的指向.引用指向新的提交ID; 2.替换暂存区.替换后,暂存区的内容和引用指向的文件夹树一致; 3.替换工作区.替换后,工 ...

  8. Android系统Google Maps开发实例浅析

    Google Map(谷歌地图)是Google公司提供的电子地图服务.包括了三种视图:矢量地图.卫星图片.地形地图.对于Android系统来说,可以利用Google提供的地图服务来开发自己的一些应用. ...

  9. Spring MVC page render时jsp中元素相对路径的解决办法

    前段时间做了用Spring Security实现的登录和访问权限控制的功能,但是page render使用的是InternalResourceResolver,即在spring的servlet配置文件 ...

  10. Android 电子邮件发送成功与失败的提示

    前言          欢迎大家我分享和推荐好用的代码段~~ 声明          欢迎转载.但请保留文章原始出处:          CSDN:http://www.csdn.net        ...