原文: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. spring占位符解析器---PropertyPlaceholderHelper

    一.PropertyPlaceholderHelper 职责 扮演者占位符解析器的角色,专门用来负责解析路劲中or名字中的占位符的字符,并替换上具体的值 二.例子 public class Prope ...

  2. JavaScript学习笔记之下拉选择框的操作

    对于下拉框的操作十分繁多,这几天项目须要就总结一下 一.动态构建option 有时候我们须要动态构建下拉选择框里面的值,这里我们就要用到 var varItem = new Option(" ...

  3. EXE文件结构及读取方法

    一.EXE文件概念 EXE File英文全名executable file ,译作可运行文件,可移植可运行 (PE) 文件格式的文件,它能够载入到内存中,并由操作系统载入程序运行.是可在操作系统存储空 ...

  4. shell重定向调试信息

    shell重定向调试信息 fulinux ******************************************************************************* ...

  5. WebView的应用 持续积累

    在我的项目中载入网页时我们会用到WebView这个控件,关于这个控件的相关的比較有用的API在这里记录一下. 第一 webview 设置javascript可用,  mWebView = (WebVi ...

  6. hibernate uniqueResult方法

    假设查询返回多个值用list()方法 public void testQuery(){ Configuration config = new Configuration().configure(); ...

  7. 常用Linux运维命令

    1.查看TCP连接状态 netstat -nat |awk '{print $6}'|sort|uniq -c|sort -rn netstat -n | awk '/^tcp/ {++S[$NF]} ...

  8. Servlet, Listener 、 Filter.

    Java Web的三大组件:Servlet, Listener . Filter. 使用Listener监听器:八大监听器: 第一组:用于监听Servlet三个域对象的创建与销毁 1. Servlet ...

  9. C#总结1

    C#摘要 第一章: 数据类型: 在定义变量的时候,记下规则,开头不能是数字,变量名只能包括 字母 “_” 数字 整型类型 名称 CTS类型 说明 范围 sbyte System.SByte 8位有符号 ...

  10. CountDownLatch(闭锁)

    一.闭锁(Latch)    闭锁(Latch):一种同步方法,可以延迟线程的进度直到线程到达某个终点状态.通俗的讲就是,一个闭锁相当于一扇大门,在大门打开之前所有线程都被阻断,一旦大门打开所有线程都 ...