在之前的文章中,你已经学习了如何使用同步编程模型来读取和更新数据,在本节中你将学习如何实现异步编程模型。异步可以使应用程序执行更有效率,因为它可以更有效的使用服务器资源。
同样在本节中你还将学习如何针对实体的insert, update, 和delete操作使用存储过程。
最后将应用程序部署到 Windows Azure。
下面是完成后的页面
一个web服务器的可用线程是有限的,在高负载情况下,所有的可用线程可能都在被使用。当出现这种情况时,服务器将无法处理新的请求,直到有线程被释放。使用同步代码,大量线程将被锁定,但实际上它们并未作任何工作而只是在等待IO完成。使用异步代码,当一个进程正在等待IO完成时,它的线程会被服务器释放并去处理其它的请求。因此,异步代码可以更高效地使用服务器资源,并且能够在没有延迟的情况下处理更多的流量。
在.NET的早期版本中,编写和测试异步代码是复杂的、易于出错的,且难以调试。但在.Net 4.5中,编写、测试和调试异步代码是如此简单,所以你应该经常使用异步代码。异步代码会花费较少的开销,在低流量情况下,对性能的影响是可以忽略不计的,但在高流量的情况下,潜在性能的提升是巨大的。
创建一个Department控制器,选中Use async controller actions 复选框
查看Index方法中添加的异步代码
public async Task Index()
{
var departments = db.Departments.Include(d => d.Administrator);
return View(await departments.ToListAsync());
}
共有四处更改来让Entity Framework使用异步执行数据库查询:
为何只修改了departments.ToList语句而不是departments= db.Departments语句?这是因为只有发送到数据库的查询或命令才使用异步执行。departments=db.Departments语句生成了一个查询,但直到调用ToList方法时该查询才会被执行。因此只有ToList方法是异步执行的。
在Details方法和Httpget Edit和Delete方法中,只有Find方法会将查询发送到数据库去执行,所以该方法是异步执行的。
public async Task 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 Create(Department department)
{
if (ModelState.IsValid)
{
db.Departments.Add(department);
await db.SaveChangesAsync();
return RedirectToAction("Index");
}
打开Views\Department\Index.cshtml,使用下面的代码替换
@model IEnumerable
@{
ViewBag.Title = "Departments";
}
Departments
@Html.ActionLink("Create New", "Create")
@Html.DisplayNameFor(model => model.Name)
@Html.DisplayNameFor(model => model.Budget)
@Html.DisplayNameFor(model => model.StartDate)
Administrator
@foreach (var item in Model) {
@Html.DisplayFor(modelItem => item.Name)
@Html.DisplayFor(modelItem => item.Budget)
@Html.DisplayFor(modelItem => item.StartDate)
@Html.DisplayFor(modelItem => item.Administrator.FullName)
@Html.ActionLink("Edit", "Edit", new { id=item.DepartmentID }) |
@Html.ActionLink("Details", "Details", new { id=item.DepartmentID }) |
@Html.ActionLink("Delete", "Delete", new { id=item.DepartmentID })
}
上面的代码将标题从Index 更改为Departments,将Administrator 名称移动到右侧,并提供了Administrator 的全名。
在Create, Delete,,Details和Edit视图中,将InstructorID字段的标题修改为Administrator
在Create 和Edit视图中使用下面的代码
在Delete和Details视图中使用下面的代码
Administrator
运行项目,点击Departments 选项卡
程序运行一切正常,但在此控制器中,所有SQL查询都是异步执行的。
当你使用Entity Framework来进行异步编程时要注意:
某些开发人员和DBA喜欢使用存储过程来进行数据库访问。在Entity Framework的早期版本中,你可以通过原始SQL查询来使用存储过程来检索数据,但是你不能在更新操作中使用存储过程。在Entity Framework 6中,你可以通过配置Code First来使用存储过程。
1.打开DAL\SchoolContext.cs,在OnModelCreating 方法中添加如下代码
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove();
modelBuilder.Entity()
.HasMany(c => c.Instructors).WithMany(i => i.Courses)
.Map(t => t.MapLeftKey("CourseID")
.MapRightKey("InstructorID")
.ToTable("CourseInstructor"));
modelBuilder.Entity().MapToStoredProcedures();
}
上面的代码指定Entity Framework对于Department 实体的insert,update和delete操作使用存储过程。
2.在Package Manage Console中输入如下命令
add-migration DepartmentSP
打开Migrations\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方法。
本节需要你完成之前的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
我的小站:MVC5 Entity Framework学习(9):异步和存储过程
还大家一个健康的网络环境,从你我做起
THE END