首页 > 代码库 > MVC5 Entity Framework学习之异步和存储过程

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");
    }
打开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>

上面的代码将标题从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.打开DAL\SchoolContext.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

欢迎转载,请注明文章出处:http://blog.csdn.net/johnsonblog/article/details/39272637
还大家一个健康的网络环境,从你我做起

THE END

MVC5 Entity Framework学习之异步和存储过程