原文: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. MinGW安装和使用总结

    0.前言 最近对开源编译平台和编译器产生了兴趣,借博客平台总结一些东西(包括minGW,eclipse,sourcery和GDB调试等内容).总感觉时间一长某些东西自己都会忘记,过段时间回头看看自己的 ...

  2. ASP终极防下载(转)

    自从搞ASP+ACCESS没少为避免数据库下载而伤过神,网上的奇淫技巧更是数不胜数,本文就是同大家共同探讨各路前辈的留下的秘笈并指中其中的优劣,最后为大家提供一种最佳的解决方案. 一.开篇 自从搞AS ...

  3. hdu2594 Simpsons’ Hidden Talents kmp

    Simpsons’ Hidden Talents Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Ot ...

  4. as。 对象和数组

    ---恢复内容开始--- http://w3help.org/zh-cn/causes/SJ9011 ---恢复内容结束--- http://zengrong.net/post/5.htm

  5. php验证输入字符串中含有非法字符

    $pattern = "/(&|"|<|>|')+/";  preg_match($pattern, $media_name, $matches);  ...

  6. Bleed Brake Master Cylinder with Intelligent Tester IT2

    When the brake fluid level drops too low in the master cylinder reservoir, air bubbles can get caugh ...

  7. Sqlserver的触发器的简单使用

    1,触发器有两种 (1)After触发器(之后触发) 触发器有个好处:就是你之前有过什么操作他会将你的操作的数据信息完整的保存下来,比如你删过什么信息,如果用触发器,那么删除后就会显示两行受影响,那么 ...

  8. php使用mysql_query查询超大结果集超内存的解决方法

    再使用mysql_query查询超大结果集的时候会出现超出内存限制的致命错误,这是因为mysql_query采用的是查询全部结果然后把结果集全部缓存到内存中的方式. mysql的查询还提供了另外一种查 ...

  9. Elasticsearch 搜索不到数据问题(_mapping 设置)

    需求 由于 kibana3 中,不支持直接在请求的 url 中设置搜索的 type (是不是我不知道???). 为了支持特定 type 的搜索,所以我设置了个下每个 panel 的查询语句,让它增加一 ...

  10. posix thread线程

    1. pthread线程通过调用你提供的某些函数开始.这个“线程函数”应该只有一个void*型参数,并返回系统的类型.2. 通过向pthread_create函数传递线程函数的地址和线程函数调用的参数 ...