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");
}
打开ViewsDepartmentIndex.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.打开DALSchoolContext.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
- zookeeper的python客户端安装
- 事件(Event),绝大多数内存泄漏(Memory Leak)的元凶[上篇]
- 震撼:1分钟带你看完IC0的爆炸视频
- 事件(Event),绝大多数内存泄漏(Memory Leak)的元凶[下篇] (提供Source Code下载)
- bt、磁力怎么下载?老司机飙车终极思路……
- Linux进程间通信(五) - 信号灯(史上最全)及其经典应用案例
- 写出完美的snprintf
- 计算CPU利用率
- 详解Hadoop HA 如何运作
- Linux时间时区详解与常用时间函数
- 基于Linux整形时间的常用计算思路
- 如何追踪每一笔记录的来龙去脉:一个完整的Audit Logging解决方案[上篇]
- WCF技术剖析之二十四: ServiceDebugBehavior服务行为是如何实现异常的传播的?
- Linux64位程序移植
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 图解:什么是哈希?
- 【Python基础】一文看懂 Pandas 中的透视表
- 彻底干掉恶心的 SQL 注入漏洞, 一网打尽!
- 某小型公司持续集成工具 Jenkins 实践
- 使用RBAC Impersonation简化Kubernetes资源访问控制
- 求求你别再用 MySQL offset 和 limit 分页了?
- 短视频带货源码,获取购物车中所有商品列表并加载显示
- 【Flutter 实战】菜单(Menu)功能
- 【Flutter 实战】路由堆栈详解
- 【Flutter 实战】全局监听路由堆栈变化
- 数据挖掘从入门到放弃:线性回归和逻辑回归
- 【Flutter 实战】文件系统目录
- 【原创】Spring Boot终极篇《上》
- 【原创】Spring Boot终极篇《下》
- 面试官:JDK、JRE、JVM 三者什么关系?