チュートリアル: ASP.NET MVC アプリで EF で非同期プロシージャとストアド プロシージャを使用する
以前のチュートリアルでは、同期プログラミング モデルを使用してデータの読み取りと更新を行う方法を学習しました。 このチュートリアルでは、非同期プログラミング モデルを実装する方法について説明します。 非同期コードは、サーバー リソースをより適切に使用するため、アプリケーションのパフォーマンスを向上させることができます。
このチュートリアルでは、エンティティに対する挿入、更新、および削除操作にストアド プロシージャを使用する方法についても説明します。
最後に、初めてデプロイしてから実装したすべてのデータベース変更と共に、アプリケーションを Azure に再デプロイします。
以下の図は、使用するページの一部を示しています。
このチュートリアルでは、次の作業を行いました。
- 非同期コードについて学習する
- 部門コントローラーを作成する
- ストアド プロシージャの使用
- Azure にデプロイ
前提条件
非同期コードを使用する理由
Web サーバーでは、利用できるスレッド数に限りがあります。負荷が高い状況では、利用できるスレッドが全部使われる可能性があります。 その場合、スレッドが解放されるまでサーバーは新しい要求を処理できません。 同期コードの場合、たくさんのスレッドが関連付けられていても、I/O の完了を待っているため、実際には何の作業も行っていないということがあります。 非同期コードの場合、あるプロセスが I/O の完了を待っているとき、そのスレッドは解放され、サーバーによって他の要求の処理に利用できます。 その結果、非同期コードを使用すると、サーバー リソースをより効率的に使用でき、サーバーはより多くのトラフィックを遅延なく処理できるようになります。
以前のバージョンの .NET では、非同期コードの記述とテストが複雑で、エラーが発生しやすく、デバッグが困難でした。 .NET 4.5 では、非同期コードの記述、テスト、デバッグが非常に簡単であるため、理由がない限り、通常は非同期コードを記述する必要があります。 非同期コードでは少量のオーバーヘッドが発生しますが、トラフィックが少ない状況ではパフォーマンスの低下はごくわずかですが、トラフィックが多い状況ではパフォーマンスが大幅に向上する可能性があります。
非同期プログラミングの詳細については、「 .NET 4.5 の非同期サポートを使用して呼び出しをブロックしないようにする」を参照してください。
部門コントローラーを作成する
前のコントローラーと同じ方法で Department コントローラーを作成します。ただし、今回は [非同期コントローラー アクションを使用チェック] ボックスを選択します。
次の強調表示は、 メソッドを非同期にするために同期コード Index
に追加された内容を示しています。
public async Task<ActionResult> Index()
{
var departments = db.Departments.Include(d => d.Administrator);
return View(await departments.ToListAsync());
}
Entity Framework データベース クエリを非同期的に実行できるようにするために、次の 4 つの変更が適用されました。
- メソッドは キーワード (keyword)で
async
マークされます。これにより、メソッド本体の一部のコールバックを生成し、返されるオブジェクトを自動的に作成Task<ActionResult>
するようにコンパイラに指示されます。 - 戻り値の型が から
ActionResult
にTask<ActionResult>
変更されました。 型はTask<T>
、 型の結果を使用して進行中の作業を表しますT
。 - キーワード (keyword)が
await
Web サービス呼び出しに適用されました。 コンパイラはこのキーワード (keyword)を見ると、バックグラウンドで メソッドを 2 つの部分に分割します。 最初の部分は、非同期的に開始される操作で終了します。 2 番目の部分は、操作の完了時に呼び出されるコールバック メソッドに配置されます。 - 拡張メソッドの非同期バージョンが
ToList
呼び出されました。
ステートメントは変更されるが departments.ToList
、 ステートメントは変更されないのは departments = db.Departments
なぜですか? その理由は、クエリまたはコマンドをデータベースに送信する原因となるステートメントのみが非同期的に実行されるためです。 ステートメントは departments = db.Departments
クエリを設定しますが、 メソッドが呼び出されるまで ToList
クエリは実行されません。 そのため、 メソッドのみが ToList
非同期的に実行されます。
Details
メソッドと メソッドと Delete
HttpGet
Edit
メソッドでは、 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.Departments.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>
このコードでは、タイトルを [インデックス] から [部署] に変更し、管理者名を右側に移動し、管理者の完全な名前を指定します。
[作成]、[削除]、[詳細]、および [編集] ビューで、コース ビューで部門名フィールドを "Department" に変更したのと同じ方法で、フィールドのキャプションを "管理者" に変更InstructorID
します。
[作成] ビューと [編集] ビューでは、次のコードを使用します。
<label class="control-label col-md-2" for="InstructorID">Administrator</label>
[削除] ビューと [詳細] ビューでは、次のコードを使用します。
<dt>
Administrator
</dt>
アプリケーションを実行し、[ 部門 ] タブをクリックします。
すべてが他のコントローラーと同じように動作しますが、このコントローラーでは、すべての SQL クエリが非同期的に実行されます。
Entity Framework で非同期プログラミングを使用する場合に注意すべき点がいくつかあります。
- 非同期コードはスレッド セーフではありません。 つまり、同じコンテキスト インスタンスを使用して複数の操作を並列で実行しないでください。
- 非同期コードのパフォーマンス上の利点を最大限に活用する場合、(ページングなどのために) ライブラリ パッケージを利用しているのであれば、それがクエリをデータベースに送信させる Entity Framework メソッドを呼び出す場合、非同期を利用する必要があります。
ストアド プロシージャの使用
一部の開発者や DBA は、データベース アクセスにストアド プロシージャを使用することを好みます。 以前のバージョンの Entity Framework では、 生の SQL クエリを実行してストアド プロシージャを使用してデータを取得できますが、更新操作にストアド プロシージャを使用するように EF に指示することはできません。 EF 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 を開き、Insert、Update、Delete ストアド プロシージャを
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
デバッグ モードでアプリケーションを実行し、[ 部門 ] タブをクリックし、[ 新規作成] をクリックします。
新しい部署のデータを入力し、[ 作成] をクリックします。
Visual Studio の [出力 ] ウィンドウのログを確認して、ストアド プロシージャを使用して新しい Department 行が挿入されたことを確認します。
Code First は、既定のストアド プロシージャ名を作成します。 既存のデータベースを使用している場合は、データベースで既に定義されているストアド プロシージャを使用するために、ストアド プロシージャ名のカスタマイズが必要になる場合があります。 その方法については、「 Entity Framework Code First Insert/Update/Delete ストアド プロシージャ」を参照してください。
生成されたストアド プロシージャの処理をカスタマイズする場合は、ストアド プロシージャを作成する移行 Up
メソッドのスキャフォールディング されたコードを編集できます。 そうすることで、移行が実行されるたびに変更が反映され、デプロイ後に運用環境で移行が自動的に実行されるときに運用データベースに適用されます。
以前の移行で作成された既存のストアド プロシージャを変更する場合は、Add-Migration コマンドを使用して空の移行を生成し、 AlterStoredProcedure メソッドを呼び出すコードを手動で記述できます。
Azure にデプロイ
このセクションでは、このシリーズの「移行とデプロイ」チュートリアルのオプションの「Azure へのアプリのデプロイ」セクションを完了している必要があります。 ローカル プロジェクトのデータベースを削除して解決した移行エラーがある場合は、このセクションをスキップしてください。
Visual Studio のソリューション エクスプローラーで、プロジェクトを右クリックし、コンテキスト メニューの [発行] をクリックします。
[発行] をクリックします。
Visual Studio によってアプリケーションが Azure にデプロイされ、Azure で実行されている既定のブラウザーでアプリケーションが開きます。
アプリケーションをテストして、動作していることを確認します。
データベースにアクセスするページを初めて実行すると、Entity Framework は、現在のデータ モデルでデータベースを最新の状態にするために必要なすべての移行
Up
方法を実行します。 このチュートリアルで追加した部門ページを含め、前回のデプロイ以降に追加したすべての Web ページを使用できるようになりました。
コードを取得する
その他のリソース
他の Entity Framework リソースへのリンクは、「 ASP.NET データ アクセス - 推奨リソース」にあります。
次の手順
このチュートリアルでは、次の作業を行いました。
- 非同期コードについて学習しました
- 部署コントローラーを作成しました
- 使用されるストアド プロシージャ
- Azure にデプロイ済み
次の記事に進み、複数のユーザーが同じエンティティを同時に更新するときに競合を処理する方法について説明します。
フィードバック
https://aka.ms/ContentUserFeedback」を参照してください。
以下は間もなく提供いたします。2024 年を通じて、コンテンツのフィードバック メカニズムとして GitHub の issue を段階的に廃止し、新しいフィードバック システムに置き換えます。 詳細については、「フィードバックの送信と表示