ASP.NET MVCアプリケーションに非同期およびストアドプロシージャを使用する
これは、MVC 5シリーズを使用したMicrosoftの公式チュートリアルGetting Entity Framework 6 Code Firstの翻訳です。9番目の記事は、ASP.NET MVCアプリケーションでの非同期およびストアドプロシージャの使用です。
原文:ASP.NET MVCアプリケーションのエンティティフレームワークを使用した非同期およびストアドプロシージャ
前のチュートリアルでは、同期プログラミングモデルを使用してデータを読み取り、更新する方法を学びました。このチュートリアルでは、非同期プログラミングモデルを実装する方法について説明します。サーバーリソースをより適切に使用するため、非同期コードはアプリケーションの実行を改善するのに役立ちます。
このチュートリアルでは、エンティティを挿入、更新、および削除するためにストアドプロシージャを使用する方法についても説明します。
次の図は、作成するページを示しています。
面倒な非同期コードを使用する理由
WEBサーバーは使用可能なスレッドの数に制限があり、高負荷の場合、すべてのスレッドが使用中である可能性があります。これが発生すると、スレッドが解放されるまで、サーバーは新しい要求を処理できません。同期コードの場合、複数のスレッドが関連付けられている可能性がありますが、実際にはそれらは何の処理も行わず、IOが完了するまで待機します。非同期コードを使用すると、プロセスがIOの完了を待機しているときに、サーバーがそのスレッドを解放して、他の要求を処理できます。したがって、非同期コードはサーバーリソースをより効率的に使用でき、サーバーは遅延なくより多くのトラフィックを処理できます。
以前のバージョンの.NETでは、非同期コードの作成とテストは複雑でエラーが発生しやすく、デバッグが困難でした。.Net 4.5では、非同期コードの記述、テスト、およびデバッグが簡単になりました。理由がない限り、常に非同期コードを使用する必要があります。非同期コードのオーバーヘッドはほとんどありませんが、トラフィックの少ない状況ではパフォーマンスの低下はごくわずかです。トラフィックの多い状況では、潜在的なパフォーマンスのヒントは非常に大きくなります。
非同期プログラミングの詳細については、「。NET 4.5の非同期サポートを使用して呼び出しのブロックを回避する」を参照してください。
システムコントローラーを作成する
以前に他のコントローラーを作成したのと同じ方法でシステムコントローラーを作成しますが、今回は非同期コントローラー操作オプションを使用することを選択しました。
次のコードの強調表示された部分は、非同期メソッドと同期メソッドの違いを示しています。
public async Task<ActionResult> Index()
{
var departments = db.Departments.Include(d => d.Administrator);
return View(await departments.ToListAsync());
}
Entity Frameworkデータベースが非同期クエリを実行できるように、4つの変更を適用しました。
- このメソッドはasyncキーワードを使用します。これは、コールバックメソッド本体の一部を生成し、自動的に
Task<ActionResult>
戻りオブジェクトを作成するようコンパイラーに指示します。 - 戻り値の型がActionResultから変更されました
Task<ActionResult>
。Task<T>
タイプは、進行中のタスクがタイプTの結果を持つことを示します。 - awaitキーワードは、Webサービス呼び出しに適用されます。コンパイラはこのキーワードを検出すると、メソッドをバックグラウンドで2つの部分に分割します。最初の部分は非同期操作が開始すると終了し、2番目の部分は操作が完了するとコールバックメソッドに入れられます。
- ToList拡張メソッドの非同期バージョンが呼び出されます。
departments = db.Departmentsステートメントではなく、departments.ToListステートメントのみを変更するのはなぜですか?その理由は、送信されるデータベースによって実行されるクエリまたはステートメントのみが非同期で実行できるためです。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>
コードはタイトルを変更し、部門長の列を右に移動し、部門長の名前を提供します。
「作成」、「削除」、「詳細」、および「編集」ビューで、コースビューで「部門名」フィールドを「部門」に変更したのと同様に、InstructorIDフィールドのタイトルを「部門長」に変更します。
ビューの作成と編集には次のコードを使用します。
<label class="control-label col-md-2" for="InstructorID">Administrator</label>
削除ビューと詳細ビューで次のコードを使用します。
<dt>
Administrator
</dt>
アプリケーションを実行し、「部門」タブをクリックします。
プログラムは、他のコントローラーと同じように通常どおり実行されます。ただし、このコントローラーでは、すべてのSQLクエリが非同期で実行されます。
Entity Frameworkで非同期プログラミングを使用する場合の注意事項:
- 非同期コードはスレッドセーフではありません。つまり、同じコンテキストインスタンスを使用して複数の操作を並行して実行しないでください。
- 非同期コードのパフォーマンス上の利点を活用する場合は、使用しているすべてのライブラリパッケージ(ページングなど)、パッケージ内のデータベースクエリなどのすべてのEntity Frameworkメソッドも非同期で実行されることを確認してください。
挿入、更新、削除のためのストアドプロシージャ
一部の開発者やDBAは、ストアドプロシージャを使用してデータベースにアクセスすることを好みます。Entity Frameworkの以前のバージョンでは、元のSQLクエリを使用してデータを取得してストアドプロシージャを実行できますが、更新操作にストアドプロシージャを使用することはできません。Entity Framework 6では、ストアドプロシージャを使用するようにCode Firstを簡単に構成できます。
- 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エンティティを挿入、更新、削除するように指示します。
- パッケージ管理コンソールで、次のコマンドを入力します。
add-migration DepartmentSP
Migrations \ <timestamp> _DepartmentSP.csを開き、Upメソッドのコードを参照すると、挿入、更新、削除のストアドプロシージャが表示されます。
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)"
);
}
- パッケージマネージャコンソールで、次のコマンドを入力します。
update-database
- デバッグモデルの下でアプリケーションを実行し、[部門]タブをクリックして、[作成]をクリックします。
- 新しい部門の関連データを入力し、[作成]をクリックします。
- VSの出力ウィンドウでログを表示します。
コード最初に、デフォルト名を使用してストアドプロシージャを作成しました。既存のデータベースを使用している場合、ストアドプロシージャの名前をカスタマイズする必要がある場合があります。これを行う方法については、「Entity Framework Code First First Insert / Update / Delete Stored Procedures」を参照してください。
ストアドプロシージャをカスタマイズする場合は、移行のスキャフォールディングコードでUpメソッドを編集して、ストアドプロシージャを作成できます。この方法では、アプリケーションの移行時または本番環境へのデプロイ後に、変更が自動的に行われます。
以前の移行で作成されたストアドプロシージャを変更する場合は、Add-Migrationコマンドを使用して空の移行を生成し、手動でコードを記述してAlterStoredProcedureメソッドを呼び出すことができます。
Windows Azureにデプロイする
ちょっと...
まとめ
このチュートリアルでは、サーバーの効率を向上させる方法を学びました。非同期実行コードを記述し、ストアドプロシージャを使用して、操作を挿入、更新、および削除します。次のチュートリアルでは、複数のユーザーが同じレコードを編集しようとしたときにデータの損失を防ぐ方法について説明します。
著者情報
Tom Dykstra -Tom Dykstraは、Microsoft Web Platform and Toolsチームのシニアプログラマー兼ライターです。