ASP.NET MVCアプリケーションの高度な機能を使用する
これは、Microsoftの公式チュートリアル「Entity Framework 6コードの開始」をMVC 5シリーズを使用して最初に翻訳したものです。これが12番目です。ASP.NETMVCアプリケーションの高度な機能の使用
原文:MVC 5 WebアプリケーションのAdvanced Entity Framework 6シナリオ
前のチュートリアルでは、継承を実装しました。このチュートリアルでは、Entity Framework Code Firstを使用してASP.NET Webアプリケーションを開発するときに使用できる高度な機能を紹介します。
- 生のSQLクエリを実行する
- 非追跡クエリを実行する
- データベースに送信されたSQLを確認する
さらに、このチュートリアルでは、次のトピックの簡単な紹介と参照リンクを提供します。
- 倉庫保管およびユニット作業モード
- エージェントクラス
- 自動変更監視
- 自動検証
- Visua Studioエンティティフレームワークツール
- エンティティフレームワークのソースコード
このチュートリアルで説明するほとんどのトピックでは、作成したWebページを使用し、バッチ更新には元のSQLを使用します。次に、データベース内のすべてのコースの単位を更新するための新しいページを作成します。
生のSQLクエリを実行する
Entity Framework Code First APIには、SQLコマンドをデータベースに直接送信できるメソッドが含まれています。次のオプションがあります。
使用DbSet.SqlQuery照会し、エンティティタイプを返すメソッド。返されるオブジェクトタイプは、予想されるDbSetオブジェクトである必要があります。これらは、データベースコンテキストによって自動的に追跡されます。トラッキングをオフにしない限り。(次のセクションのAsNoTrackingメソッドを参照してください)
非エンティティ型をクエリして返すには、Database.SqlQueryメソッドを使用します。このメソッドを使用してエンティティタイプを取得しても、返されたオブジェクトはデータベースコンテキストによって追跡されません。
Database.ExecuteSqlCommandは、非クエリタイプのコマンドに使用されます。
Entity Frameworkを使用する利点の1つは、大量のコードを手動で入力しなくても、データにアクセスする特定の方法を実装できることです。SQLクエリとコマンドを自動的に生成することにより、面倒な手動コーディングから解放されます。ただし、特殊なケースでは、手動で作成された特定のSQLクエリを実行する必要がある場合があります。これらのメソッドはこの機能を実現し、例外処理を提供します。
WebアプリケーションでSQLコマンドを頻繁に実行する場合、SQLインジェクション攻撃からサイトを保護するために必要な予防策を講じる必要があります。1つの方法は、パラメーター化されたクエリを使用して、Webページの文字列がSQLコマンドとして解釈されないようにすることです。このチュートリアルでは、ユーザーを使用してクエリを入力するときに、パラメーター化されたクエリを使用します。
エンティティを返すクエリを呼び出します
DbSet<TEntity>
このクラスは、クエリを実行してエンティティタイプを返すために使用できるメソッドを提供します。このメソッドの動作を確認するには、DepartmentコントローラーのDetailsメソッドにいくつかの変更を加える必要があります。
DepartmentController.csで、Detailsメソッドを次のコードに置き換え、必要な変更を強調表示します。
public async Task<ActionResult> Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
// Commenting out original code to show how to use a raw SQL query.
//Department department = await db.Departments.FindAsync(id);
// Create and execute raw SQL query.
string query = "SELECT * FROM Department WHERE DepartmentID = @p0";
Department department = await db.Departments.SqlQuery(query, id).SingleOrDefaultAsync();
if (department == null)
{
return HttpNotFound();
}
return View(department);
}
新しいコードが適切に機能していることを確認するには、アプリケーションを実行し、部門のページに移動して部門の詳細をクリックします。
すべてが以前と同じように機能していることがわかります。
クエリを呼び出して他のタイプのオブジェクトを返す
前のチュートリアルでは、学生統計グリッドを作成して、各登録日に登録された学生の数を表示しました。このコードは、LINQを使用して以下を操作します。
var data = from student in db.Students
group student by student.EnrollmentDate into dateGroup
select new EnrollmentDateGroup()
{
EnrollmentDate = dateGroup.Key,
StudentCount = dateGroup.Count()
};
LINQを使用する代わりに、このクエリのSQLコードを直接記述したいとします。エンティティタイプ以外のオブジェクトを返すクエリを実行する必要があります。つまり、Database.SqlQueryメソッドを使用する必要があります。
HomeController.csで、Aboutメソッドを次のコードに置き換えます。強調表示された部分は、必要な変更を示しています。
public ActionResult About()
{
// Commenting out LINQ to show how to do the same thing in SQL.
//IQueryable<EnrollmentDateGroup> = from student in db.Students
// group student by student.EnrollmentDate into dateGroup
// select new EnrollmentDateGroup()
// {
// EnrollmentDate = dateGroup.Key,
// StudentCount = dateGroup.Count()
// };
// SQL version of the above LINQ code.
string query = "SELECT EnrollmentDate, COUNT(*) AS StudentCount "
+ "FROM Person "
+ "WHERE Discriminator = 'Student' "
+ "GROUP BY EnrollmentDate";
IEnumerable<EnrollmentDateGroup> data = db.Database.SqlQuery<EnrollmentDateGroup>(query);
return View(data.ToList());
}
ページを実行すると、以前と同じデータが表示されます。
更新クエリを呼び出す
管理者が、各コースの単位の変更など、データベースでバッチ操作を実行できるようにしたいとします。学校に多数のコースがある場合、各コースを個別に更新することは間違いなく非常に非効率的です。このセクションでは、ユーザーがすべてのコースの単位を変更できるようにするWebページを実装し、次に示すように、SQL Updateステートメントを使用してこの変更を行います。
CourseController.csに、HttpGetおよびHttpPostのUpdateCourseCreditsメソッドを追加します。
public ActionResult UpdateCourseCredits()
{
return View();
}
[HttpPost]
public ActionResult UpdateCourseCredits(int? multiplier)
{
if (multiplier != null)
{
ViewBag.RowsAffected = db.Database.ExecuteSqlCommand("UPDATE Course SET Credits = Credits * {0}", multiplier);
}
return View();
}
コントローラーがHttpGet要求を処理するとき、ViewBag.RowsAffectedは値を返しません。ビューには空のテキストボックスと送信ボタンが表示されます。
更新ボタンをクリックすると、HttpPostメソッドが呼び出され、テキストボックスに入力された値が取得されます。コードがSQLを実行してコースを更新し、影響を受けた行の数をViewBag.RowsAffectedに返します。次の図に示すように、ビューが変数の値を取得すると、テキストボックスと送信ボタンの代わりに、更新されたコースの数を説明するメッセージが表示されます。
CourseController.csで、UpdateCourseCreditsメソッドを右クリックし、ビューを追加します。
次のコードを使用して、ビューを置き換えます。
@model ContosoUniversity.Models.Course
@{
ViewBag.Title = "UpdateCourseCredits";
}
<h2>Update Course Credits</h2>
@if (ViewBag.RowsAffected == null)
{
using (Html.BeginForm())
{
<p>
Enter a number to multiply every course's credits by: @Html.TextBox("multiplier")
</p>
<p>
<input type="submit" value="Update" />
</p>
}
}
@if (ViewBag.RowsAffected != null)
{
<p>
Number of rows updated: @ViewBag.RowsAffected
</p>
}
<div>
@Html.ActionLink("Back to List", "Index")
</div>
アプリケーションを実行し、「/ UpdateCourseCredits」をHttp:// localhost:xxxx / UpdateCourseCreditsなどのブラウザーアドレスバーの末尾に追加し、ページを開いて、テキストボックスに数値を入力します。
[更新]をクリックすると、影響を受けるコースが表示されます。
次にリストに戻ると、すべてのコースが更新されていることがわかります。
Raw SQLクエリの使用の詳細については、MSDNのRaw SQLクエリを参照してください。
非追跡クエリ
データベースコンテキストがデータ行を取得してエンティティオブジェクトを作成すると、デフォルトで、メモリ内のエンティティがデータベースと同期しているかどうかが追跡されます。エンティティを更新すると、メモリ内のデータはキャッシュとして機能します。コンテキストインスタンスは通常存続期間が短く(リクエストごとに新しいインスタンスが作成されます)、エンティティが読み取られて使用された後にコンテキストが破棄されることが多いため、この種のキャッシングはWebアプリケーションでは使用できません。 。
AsNoTrackingメソッドを使用して、メモリ内のエンティティオブジェクトの追跡を無効にできます。次の一般的なシナリオでは、これを行う必要がある場合があります。
大量のデータを取得する必要があり、トレースをオフにするとパフォーマンスが大幅に向上する場合があります。
エンティティをアタッチして更新する必要がありますが、これは以前に別の目的で取得したエンティティオブジェクトと同じです。エンティティは既にデータベースコンテキストによって追跡されているため、エンティティをアタッチして変更を加えることはできません。この場合、以前のクエリではAsNoTrackingオプションを使用する必要があります。
データベースに送信されたSQLを確認する
実際にデータベースに送信されるSQLクエリを表示すると役立つ場合があります。前のチュートリアルでは、インターセプターコードを使用してこのジョブを実行する方法を確認しました。次に、インターセプターを使用しない方法を確認しますメソッド。この方法を試すには、簡単なクエリをチェックし、プリロード、フィルタリング、ソートなどの追加を監視して、何が起こったかを確認します。
CourseController.csで、元のコードを次のコードに置き換えて、プリロードを停止します。
public ActionResult Index()
{
var courses = db.Courses;
var sql = courses.ToString();
return View(courses.ToList());
}
次に、returnステートメントにブレークポイントを設定し、F5キーを押してプロジェクトをデバッグモードで実行し、コースインデックスページを選択します。コードがブレークポイントに達したら、クエリ変数を確認すると、送信されたSQLクエリが表示されます。単純な選択ステートメントです。
{SELECT
[Extent1].[CourseID] AS [CourseID],
[Extent1].[Title] AS [Title],
[Extent1].[Credits] AS [Credits],
[Extent1].[DepartmentID] AS [DepartmentID]
FROM [Course] AS [Extent1]}
テキスト視覚化ツールを使用して、モニタリングウィンドウにSQLを表示できます。
これで、ドロップダウンリストがコースインデックスページに追加され、ユーザーはこのリストを使用して特定の学科をフィルタリングできます。タイトルを使用して、システムのナビゲーションプロパティのプリロードをソートおよび指定します。
CourseController.csで、Indexメソッドを次のコードに置き換えます。
public ActionResult Index(int? SelectedDepartment)
{
var departments = db.Departments.OrderBy(q => q.Name).ToList();
ViewBag.SelectedDepartment = new SelectList(departments, "DepartmentID", "Name", SelectedDepartment);
int departmentID = SelectedDepartment.GetValueOrDefault();
IQueryable<Course> courses = db.Courses
.Where(c => !SelectedDepartment.HasValue || c.DepartmentID == departmentID)
.OrderBy(d => d.CourseID)
.Include(d => d.Department);
var sql = courses.ToString();
return View(courses.ToList());
}
戻り時にブレークポイントを設定します。
このメソッドは、ドロップダウンリストで選択された値を受け取ります。項目が選択されていない場合、このパラメーターはnullです。
すべての部門を含むSelectListコレクションは、ビューのドロップダウンリストに渡されます。SelectListコンストラクターに渡されるパラメーターは、値フィールド名、テキストフィールド名、および選択されたアイテムを指定します。
コースウェアハウスのGetメソッドの場合、コードは、Departmentナビゲーション属性のフィルター式、並べ替え、遅延読み込みを指定します。ドロップダウンテーブルでアイテムが選択されていない場合、フィルター式は常にtrueを返します。
Views \ Course \ Index.cshtmlで、テーブルの開始タグの前に次のコードを挿入して、ドロップダウンリストと送信ボタンを作成します。
@using (Html.BeginForm())
{
<p>Select Department: @Html.DropDownList("SelectedDepartment","All")
<input type="submit" value="Filter" /></p>
}
インデックスページを実行し、ブレークポイントが検出されたときに実行を継続してページを表示し、ドロップダウンリストから部門を選択して、フィルターをクリックします。
今すぐメソッドに従ってSQLステートメントを表示すると、内部結合クエリを含むSQLが表示されます。
SELECT
[Project1].[CourseID] AS [CourseID],
[Project1].[Title] AS [Title],
[Project1].[Credits] AS [Credits],
[Project1].[DepartmentID] AS [DepartmentID],
[Project1].[DepartmentID1] AS [DepartmentID1],
[Project1].[Name] AS [Name],
[Project1].[Budget] AS [Budget],
[Project1].[StartDate] AS [StartDate],
[Project1].[InstructorID] AS [InstructorID],
[Project1].[RowVersion] AS [RowVersion]
FROM ( SELECT
[Extent1].[CourseID] AS [CourseID],
[Extent1].[Title] AS [Title],
[Extent1].[Credits] AS [Credits],
[Extent1].[DepartmentID] AS [DepartmentID],
[Extent2].[DepartmentID] AS [DepartmentID1],
[Extent2].[Name] AS [Name],
[Extent2].[Budget] AS [Budget],
[Extent2].[StartDate] AS [StartDate],
[Extent2].[InstructorID] AS [InstructorID],
[Extent2].[RowVersion] AS [RowVersion]
FROM [dbo].[Course] AS [Extent1]
INNER JOIN [dbo].[Department] AS [Extent2] ON [Extent1].[DepartmentID] = [Extent2].[DepartmentID]
WHERE @p__linq__0 IS NULL OR [Extent1].[DepartmentID] = @p__linq__1
) AS [Project1]
ORDER BY [Project1].[CourseID] ASC
上記のクエリは、部門とコースのデータを読み込み、WHERE句を含む接続クエリであることがわかります。
を削除しますvar sql = conrses.ToString();
倉庫保管およびユニット作業モード
多くの開発者は、Entity Frameworkの倉庫保管およびユニット作業モードを実装するためのパッケージとしてコードを記述します。これらのパターンは、ビジネスロジックレイヤーとデータアクセスレイヤーの間に抽象化レイヤーを作成します。これらのパターンを実装すると、アプリケーションがデータストレージの変更から分離し、自動化された単体テストの開発が容易になります。ただし、Entity Frameworkを使用するプログラムにこれらのパターンを実装するための追加のコードを記述することは、いくつかの理由で最良の選択ではありません。
Entity Frameworkコンテキストクラス自体は、特定のコードのデータストレージからコードを分離できます。
Entity Frameworkを使用する場合、データベース更新操作用のEntity Frameworkコンテキストクラスを作業ユニットクラスとして使用できます。
Entity Framework 6バージョンで導入された機能により、ウェアハウスコードを記述せずに単体テストで駆動できます。
倉庫保管モードとユニット作業モードの実装方法の詳細については、このチュートリアルシリーズのEntity Framework 5バージョンを参照してください。Entity Framework 6バージョンでユニットテストドライバーを実行する方法については、以下を参照してください。
エージェントクラス
Entity Frameworkによってエンティティインスタンスが作成されるとき(たとえば、クエリを実行するとき)、エンティティから派生した動的に生成されたエンティティオブジェクトとして常にプロキシが作成されます。たとえば、次の2つのデバッガーのスクリーンショットの最初の画像には、タイプがStudentであると予想されるStudent変数が表示されています。エンティティをインスタンス化すると、2番目の画像にプロキシクラスが表示されます。
プロキシクラスは、エンティティのいくつかの仮想属性を書き換えて、属性にアクセスするときにアクションを自動的に実行するフックを挿入します。このメカニズムを使用する関数の1つは遅延読み込みです。
ほとんどの場合、エージェントに気付かないでしょうが、例外があります。
場合によっては、Entity Frameworkがプロキシインスタンスを作成しないようにする必要があります。たとえば、通常は、プロキシクラスではなくPOCOクラスエンティティをシリアル化する必要があります。シリアル化の問題を回避する1つの方法は、Entity FrameworkでのWeb APIの使用など、エンティティオブジェクトの代わりにデータ転送オブジェクト(DTO)をシリアル化することです。別の方法は、プロキシの作成を無効にすることです。
new演算子を使用してエンティティークラスをインスタンス化する場合、得られるのはプロキシインスタンスではありません。つまり、遅延読み込みや自動追跡などの機能は利用できません。これは通常は適切です。通常、データベースに存在しない新しいエンティティを作成する必要があるため、遅延読み込みは必要ありません。エンティティを明示的に「追加済み」としてマークすると、通常、変更の追跡は不要になります。ただし、遅延読み込みが必要で、追跡を変更する必要がある場合は、DbSetクラスのCreateメソッドを使用して、プロキシを介して新しいエンティティオブジェクトを作成できます。
プロキシタイプから実際のエンティティタイプを取得することもできます。ObjectContextクラスのGetObjectTypeメソッドを使用して、プロキシタイプの実際のエンティティタイプを取得できます。
詳細については、MSDNのプロキシの操作を参照してください。
自動変更監視
Entity Frameworkは、エンティティの現在の値と元の値の比較を使用して、エンティティが変更されているかどうかを判断します(したがって、更新を実行するにはデータベースに送信する必要があります)。エンティティが照会または追加されると、元の値が保存されます。自動変更監視につながるいくつかの方法は次のとおりです。
- DbSet.Find
- DbSet.Local
- DbSet.Remove
- DbSet.Add
- DbSet.Attach
- DbContext.Savechanges
- DbContext.GetValidationErrors
- DbContext.Entry
- DbChangeTracker.Entries
多数のエンティティを追跡していて、これらのメソッドをループ内で複数回呼び出す場合、AutoDetectChangesEnabledプロパティを使用して自動変更監視を一時的にオフにして、プログラムのパフォーマンスを向上させることができます。
自動検証
SaveChangesメソッドを呼び出すと、デフォルトで、Entity Frameworkはデータベースに更新する前に、変更されたすべてのエンティティのすべてのプロパティを確認します。多数のエンティティを更新してデータを検証した場合、この作業は不要であり、検証を一時的にオフにすることで処理時間を短縮できます。ValidateOnSaveEnabledプロパティを使用できます。
Entity Framework Power Tools
Entity Framework Power Toolsは、このチュートリアルで示すデータモデル図を作成するために使用できる単純なVS拡張機能です。このツールは、Code Firstを使用すると、既存のデータベーステーブルに基づいてエンティティクラスを生成するなど、他のタスクも実行できます。ツールをインストールすると、コンテキストメニューにいくつかの追加オプションが表示されます。たとえば、ソリューションエクスプローラーのコンテキストクラスを右クリックすると、グラフを生成するオプションが表示されます。コードファーストを使用する場合、関係図のデータモデルを変更することはできませんが、図を移動して理解しやすくすることができます。
エンティティフレームワークのソースコード
Entity Framework 6のソースコードはhttp://entityframework.codeplex.com/で入手できます。ソースコードに加えて、問題の生成、追跡、機能の調査などを行うことができます。バグを送信して、独自の拡張機能を提供することができます。エンティティフレームワークのソースコード。
ソースコードはオープンですが、物理フレームワークはMicrosoftによって完全にサポートされている製品です。Microsoft Entity Frameworkチームは、フィードバックを受け取り、変更をテストして、各バージョンの品質を確認します。
まとめ
このようにして、ASP.NET MVCアプリケーションでのEntity Frameworkの使用に関する一連のチュートリアルがすべて完了します。Entity Frameworkの使用方法の詳細については、MSDNおよびASP.NETデータアクセス推奨リソースのEFドキュメントページを参照してください。
アプリケーションをビルドした後でデプロイする方法については、ASP.NET Webデプロイメント推奨リソースを参照してください。
MVCの詳細については、ASP.NET MVC推奨リソースを参照してください。
ありがとう
Tom Dykstraは、Entity Framework 5に基づいてこのチュートリアルのオリジナルバージョンを作成し、それに基づいてチュートリアルを作成しました。彼は、Microsoft Webプラットフォームおよびツールチームのシニアプログラマライターです。
Rick Andersonは、Entity Framework 5およびMVC4のチュートリアルで多くの作業を行い、Entity Framework 6の更新を共同執筆しました。彼はMicrosoft AzureおよびMVCのシニアプログラマーライターです。
Rowan Millerと他のEntity Frameworkチームはチュートリアルをレビューし、多数のバグをデバッグしました。
著者情報
Tom Dykstra -Tom Dykstraは、Microsoft Web Platform and Toolsチームのシニアプログラマー兼ライターです。