Entity Framework 4.0 と ObjectDataSource コントロールの使用、パート 1: はじめに
このチュートリアル シリーズは、Entity Framework 4.0 チュートリアル シリーズを使用してはじめにによって作成された Contoso University Web アプリケーションに基づいています。 以前のチュートリアルを完了していない場合は、このチュートリアルの開始点として、作成 したアプリケーションをダウンロード できます。 完全なチュートリアル シリーズによって作成された アプリケーションをダウンロード することもできます。
Contoso University サンプル Web アプリケーションは、Entity Framework 4.0 と Visual Studio 2010 を使用して ASP.NET Web Forms アプリケーションを作成する方法を示しています。 サンプル アプリケーションは、架空の Contoso University の Web サイトです。 学生の受け付け、講座の作成、講師の割り当てなどの機能が含まれています。
このチュートリアルでは、C# の例を示します。 ダウンロード可能なサンプルには、C# と Visual Basic の両方のコードが含まれています。
Database First
Entity Framework でデータを操作する方法には、 データベースファースト、 Model First、 Code First の 3 つの方法があります。 このチュートリアルは Database First 用です。 これらのワークフローの違いと、シナリオに最適なものを選択する方法に関するガイダンスについては、「 Entity Framework 開発ワークフロー」を参照してください。
Web フォーム
はじめに シリーズと同様に、このチュートリアル シリーズでは、ASP.NET Web Forms モデルを使用し、Visual Studio で ASP.NET Web Formsを操作する方法を把握していることを前提としています。 そうでない場合は、「ASP.NET 4.5 Web Formsを使用したはじめに」を参照してください。 ASP.NET MVC フレームワークを使用する場合は、「ASP.NET MVC を使用した Entity Framework のはじめに」を参照してください。
ソフトウェア バージョン
チュートリアルに示されている また、 で動作します。 Windows 7 Windows 8 Visual Studio 2010 Visual Studio 2010 Express for Web。 このチュートリアルは、Visual Studio の新しいバージョンではテストされていません。 メニューの選択、ダイアログ ボックス、テンプレートには多くの違いがあります。 .NET 4 .NET 4.5 は .NET 4 と下位互換性がありますが、チュートリアルは .NET 4.5 でテストされていません。 Entity Framework 4 このチュートリアルは、新しいバージョンの Entity Framework ではテストされていません。 Entity Framework 5 以降では、EF は EF 4.1 で導入された を DbContext API
既定で使用します。 EntityDataSource コントロールは、API を使用ObjectContext
するように設計されています。 API で EntityDataSource コントロールDbContext
を使用する方法については、 こちらのブログ投稿を参照してください。質問
チュートリアルに直接関連しない質問がある場合は、ASP.NET Entity Framework フォーラム、Entity Frameworkおよび LINQ to Entities フォーラム、または StackOverflow.com に投稿できます。
この EntityDataSource
コントロールを使用すると、アプリケーションをすばやく作成できますが、通常は 、.aspx ページに大量のビジネス ロジックとデータ アクセス ロジックを保持する必要があります。 アプリケーションの複雑さが増し、継続的なメンテナンスが必要になると予想される場合は、 n 層 または 階層化された アプリケーション構造をより保守しやすいものにするために、開発時間を事前に増やすことができます。 このアーキテクチャを実装するには、プレゼンテーション層をビジネス ロジック 層 (BLL) とデータ アクセス層 (DAL) から分離します。 この構造体を実装する方法の 1 つは、 コントロールではなく コントロールをObjectDataSource
EntityDataSource
使用することです。 コントロールを ObjectDataSource
使用する場合は、独自のデータ アクセス コードを実装し、他のデータ ソース コントロールと同じ機能の多くを持つコントロールを使用して .aspx ページで呼び出します。 これにより、n 層アプローチの利点と、データ アクセスにWeb Forms制御を使用する利点を組み合わせることができます。
コントロールを ObjectDataSource
使用すると、他の方法でも柔軟性が向上します。 独自のデータ アクセス コードを記述するため、特定のエンティティ型 (コントロールが実行するように設計されているタスク EntityDataSource
) の読み取り、挿入、更新、または削除以外の操作を行う方が簡単です。 たとえば、エンティティが更新されるたびにログ記録を実行したり、エンティティが削除されるたびにデータをアーカイブしたり、外部キー値を持つ行を挿入するときに必要に応じて関連データを自動的にチェックして更新したりできます。
ビジネス ロジックとリポジトリ クラス
コントロールは ObjectDataSource
、作成したクラスを呼び出すことによって機能します。 クラスには、データを取得および更新するメソッドが含まれており、それらのメソッドの名前をマークアップでコントロールに ObjectDataSource
指定します。 レンダリングまたはポストバック処理中に、 は ObjectDataSource
指定したメソッドを呼び出します。
基本的な CRUD 操作に加えて、コントロールで使用するために作成するクラスは、データの ObjectDataSource
読み取りまたは更新時にビジネス ロジックを ObjectDataSource
実行する必要がある場合があります。 たとえば、ある部署を更新する場合、1 人のユーザーが複数の部署の管理者になれないので、他の部門に同じ管理者がいないことを検証する必要がある場合があります。
ObjectDataSource クラスの概要などの一部ObjectDataSource
のドキュメントでは、ビジネス ロジックとデータ アクセス ロジックの両方を含むビジネス オブジェクトと呼ばれるクラスを呼び出します。 このチュートリアルでは、ビジネス ロジックとデータ アクセス ロジック用に個別のクラスを作成します。 データ アクセス ロジックをカプセル化するクラスは、 リポジトリと呼ばれます。 ビジネス ロジック クラスにはビジネス ロジック メソッドとデータ アクセス メソッドの両方が含まれますが、データ アクセス メソッドはリポジトリを呼び出してデータ アクセス タスクを実行します。
また、BLL と DAL の間に抽象化レイヤーを作成して、BLL の自動単体テストを容易にします。 この抽象化レイヤーは、インターフェイスを作成し、ビジネス ロジック クラスでリポジトリをインスタンス化するときに インターフェイスを使用して実装されます。 これにより、リポジトリ インターフェイスを実装する任意のオブジェクトへの参照をビジネス ロジック クラスに提供できます。 通常の操作では、Entity Framework で動作するリポジトリ オブジェクトを指定します。 テスト用に、コレクションとして定義されたクラス変数など、簡単に操作できる方法で格納されたデータを操作するリポジトリ オブジェクトを提供します。
次の図は、リポジトリのないデータ アクセス ロジックを含むビジネス ロジック クラスと、リポジトリを使用するクラスの違いを示しています。
まず、コントロールが基本的なデータ アクセス タスクのみを実行するため、コントロールがリポジトリに直接バインドされる Web ページ ObjectDataSource
を作成します。 次のチュートリアルでは、検証ロジックを使用してビジネス ロジック クラスを作成し、リポジトリ クラスではなく、そのクラスにコントロールをバインド ObjectDataSource
します。 検証ロジックの単体テストも作成します。 このシリーズの 3 番目のチュートリアルでは、並べ替えとフィルター処理の機能をアプリケーションに追加します。
このチュートリアルで作成するページは、はじめに チュートリアル シリーズで作成したデータ モデルのエンティティ セットと連携Departments
します。
データベースとデータ モデルの更新
このチュートリアルを開始するには、データベースに 2 つの変更を加えます。両方とも、Entity Framework とWeb Formsチュートリアルを使用してはじめにで作成したデータ モデルに対応する変更が必要です。 これらのチュートリアルの 1 つでは、データベース変更後にデータ モデルをデータベースと同期するようにデザイナーで手動で変更を行いました。 このチュートリアルでは、デザイナーの データベースからのモデルの更新 ツールを使用して、データ モデルを自動的に更新します。
データベースへのリレーションシップの追加
Visual Studio で、Entity Framework と Web Forms チュートリアル シリーズを使用してはじめにで作成した Contoso University Web アプリケーションを開き、データベース ダイアグラムをSchoolDiagram
開きます。
データベース ダイアグラムのテーブルを Department
見ると、列が含まれている Administrator
ことがわかります。 この列はテーブルの Person
外部キーですが、データベースに外部キーリレーションシップは定義されていません。 エンティティ フレームワークがこのリレーションシップを自動的に処理できるように、リレーションシップを作成し、データ モデルを更新する必要があります。
データベース ダイアグラムで、テーブルを Department
右クリックし、[リレーションシップ] を選択 します。
[ 外部キーリレーションシップ ] ボックスの [ 追加] をクリックし、[ テーブルと列の指定] の省略記号をクリックします。
[テーブルと列] ダイアログ ボックスで、主キー テーブルとフィールドを と PersonID
にPerson
設定し、外部キー テーブルとフィールドを と Administrator
にDepartment
設定します。 (この操作を行うと、リレーションシップ名が から FK_Department_Department
に FK_Department_Person
変更されます)。
[テーブルと列] ボックスで [OK] をクリックし、[外部キーリレーションシップ] ボックスの [閉じる] をクリックして、変更を保存します。 テーブルと Department
テーブルを保存Person
するかどうかを確認するメッセージが表示されたら、[はい] をクリックします。
Note
列に既に含まれているデータに対応する行をAdministrator
削除Person
した場合、この変更を保存することはできません。 その場合は、サーバー エクスプローラーのテーブル エディターを使用して、すべてのDepartment
行の値に、テーブルに実際に存在するレコードの ID が含まれていることをPerson
確認Administrator
します。
変更を保存した後、そのユーザーが部門管理者である場合、テーブルから Person
行を削除することはできません。 運用アプリケーションでは、データベース制約によって削除が妨げられる場合、または連鎖削除を指定するときに、特定のエラー メッセージが表示されます。 連鎖削除を指定する方法の例については、「The Entity Framework and ASP.NET – はじめに Part 2」を参照してください。
データベースへのビューの追加
作成する新しい Departments.aspx ページで、ユーザーが部門管理者を選択できるように、"last, first" 形式の名前を含む講師のドロップダウン リストを提供する必要があります。 これを簡単にするために、データベースにビューを作成します。 ビューは、ドロップダウン リストに必要なデータ (完全な名前 (適切な形式) とレコード キー) のみで構成されます。
[サーバー エクスプローラー] で[School.mdf] を展開し、[ビュー] フォルダーを右クリックして、[新しいビューの追加] を選択します。
[テーブルの追加] ダイアログ ボックスが表示されたら [閉じる] をクリックし、次の SQL ステートメントを [SQL] ペインに貼り付けます。
SELECT LastName + ',' + FirstName AS FullName, PersonID
FROM dbo.Person
WHERE (HireDate IS NOT NULL)
ビューを として vInstructorName
保存します。
データ モデルの更新
DAL フォルダーで SchoolModel.edmx ファイルを開き、デザイン画面を右クリックして、[データベースからモデルの更新] を選択します。
[ データベース オブジェクトの選択 ] ダイアログ ボックスで、[ 追加 ] タブを選択し、先ほど作成したビューを選択します。
[完了] をクリックします。
デザイナーでは、ツールによってエンティティが作成され、 エンティティと エンティティ間Department
Person
の新しい関連付けが作成vInstructorName
されていることがわかります。
Note
[出力とエラー一覧] ウィンドウに、ツールによって新しいvInstructorName
ビューの主キーが自動的に作成されたことを示す警告メッセージが表示される場合があります。 これは正しい動作です。
コードで新しい vInstructorName
エンティティを参照する場合は、小文字の "v" の前に "v" を付けるというデータベース規則を使用したくありません。 そのため、モデル内のエンティティとエンティティ セットの名前を変更します。
モデル ブラウザーを開きます。 vInstructorName
エンティティの種類とビューとして一覧表示されます。
[ SchoolModel ] ( SchoolModel.Store ではなく) で 、vInstructorName を右クリックし、[ プロパティ] を選択します。 [ プロパティ ] ウィンドウで、 Name プロパティを "InstructorName" に変更し 、Entity Set Name プロパティを "InstructorNames" に変更します。
データ モデルを保存して閉じ、プロジェクトを再構築します。
リポジトリ クラスと ObjectDataSource コントロールの使用
DAL フォルダーに新しいクラス ファイルを作成し、SchoolRepository.cs という名前を付け、既存のコードを次のコードに置き換えます。
using System;
using System.Collections.Generic;
using System.Linq;
using ContosoUniversity.DAL;
namespace ContosoUniversity.DAL
{
public class SchoolRepository : IDisposable
{
private SchoolEntities context = new SchoolEntities();
public IEnumerable<Department> GetDepartments()
{
return context.Departments.Include("Person").ToList();
}
private bool disposedValue = false;
protected virtual void Dispose(bool disposing)
{
if (!this.disposedValue)
{
if (disposing)
{
context.Dispose();
}
}
this.disposedValue = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
}
このコードは、エンティティ セット内のすべてのエンティティを返す 1 つの GetDepartments
メソッドを Departments
提供します。 返されるすべての行のナビゲーション プロパティに Person
アクセスすることがわかっているため、 メソッドを使用してその Include
プロパティの一括読み込みを指定します。 クラスは、 インターフェイスも実装 IDisposable
して、オブジェクトが破棄されたときにデータベース接続が解放されるようにします。
Note
一般的な方法は、エンティティの種類ごとにリポジトリ クラスを作成することです。 このチュートリアルでは、複数のエンティティ型に対して 1 つのリポジトリ クラスを使用します。 リポジトリ パターンの詳細については、 Entity Framework チームのブログ と Julie Lerman のブログの投稿を参照してください。
GetDepartments
リポジトリ オブジェクト自体がIEnumerable
破棄された後でも、返されたコレクションを確実に使用できるようにするために、 メソッドは オブジェクトではなく IQueryable
オブジェクトを返します。 IQueryable
オブジェクトは、アクセスされるたびにデータベース アクセスを引き起こす可能性がありますが、データバインド コントロールがデータのレンダリングを試みるまでにリポジトリ オブジェクトが破棄される可能性があります。 オブジェクトの代わりに オブジェクトなどの IList
別のコレクション型を IEnumerable
返す場合があります。 ただし、オブジェクトを IEnumerable
返すと、ループや LINQ クエリなどの foreach
一般的な読み取り専用リスト処理タスクを実行できますが、コレクション内の項目を追加または削除することはできません。これは、このような変更がデータベースに保持されることを意味する可能性があります。
Site.Master マスター ページを使用する Departments.aspx ページを作成し、 という名前Content2
のコントロールに次のマークアップをContent
追加します。
<h2>Departments</h2>
<asp:ObjectDataSource ID="DepartmentsObjectDataSource" runat="server"
TypeName="ContosoUniversity.DAL.SchoolRepository"
DataObjectTypeName="ContosoUniversity.DAL.Department"
SelectMethod="GetDepartments" >
</asp:ObjectDataSource>
<asp:GridView ID="DepartmentsGridView" runat="server" AutoGenerateColumns="False"
DataSourceID="DepartmentsObjectDataSource" >
<Columns>
<asp:CommandField ShowEditButton="True" ShowDeleteButton="True"
ItemStyle-VerticalAlign="Top">
</asp:CommandField>
<asp:DynamicField DataField="Name" HeaderText="Name" SortExpression="Name" ItemStyle-VerticalAlign="Top" />
<asp:DynamicField DataField="Budget" HeaderText="Budget" SortExpression="Budget" ItemStyle-VerticalAlign="Top" />
<asp:DynamicField DataField="StartDate" HeaderText="Start Date" ItemStyle-VerticalAlign="Top" />
<asp:TemplateField HeaderText="Administrator" SortExpression="Person.LastName" ItemStyle-VerticalAlign="Top" >
<ItemTemplate>
<asp:Label ID="AdministratorLastNameLabel" runat="server" Text='<%# Eval("Person.LastName") %>'></asp:Label>,
<asp:Label ID="AdministratorFirstNameLabel" runat="server" Text='<%# Eval("Person.FirstMidName") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
このマークアップでは、 ObjectDataSource
先ほど作成したリポジトリ クラスを使用するコントロールと、データを GridView
表示するコントロールが作成されます。 コントロールは GridView
Edit コマンドと Delete コマンドを指定しますが、それらをサポートするコードをまだ追加していません。
複数の列でコントロールが使用 DynamicField
されるため、自動データの書式設定と検証機能を利用できます。 これらを機能させるには、イベント ハンドラーで Page_Init
メソッドをEnableDynamicData
呼び出す必要があります。 (DynamicControl
コントロールはナビゲーション プロパティでは Administrator
機能しないため、フィールドでは使用されません)。
Vertical-Align="Top"
入れ子になったGridView
コントロールを持つ列をグリッドに追加すると、後で属性が重要になります。
Departments.aspx.cs ファイルを開き、次using
のステートメントを追加します。
using ContosoUniversity.DAL;
次に、ページのイベントに対して次の Init
ハンドラーを追加します。
protected void Page_Init(object sender, EventArgs e)
{
DepartmentsGridView.EnableDynamicData(typeof(Department));
}
DAL フォルダーで、Department.cs という名前の新しいクラス ファイルを作成し、既存のコードを次のコードに置き換えます。
using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
namespace ContosoUniversity.DAL
{
[MetadataType(typeof(DepartmentMetaData))]
public partial class Department
{
}
public class DepartmentMetaData
{
[DataType(DataType.Currency)]
[Range(0, 1000000, ErrorMessage = "Budget must be less than $1,000,000.00")]
public Decimal Budget { get; set; }
[DisplayFormat(DataFormatString="{0:d}",ApplyFormatInEditMode=true)]
public DateTime StartDate { get; set; }
}
}
このコードは、メタデータをデータ モデルに追加します。 Budget
エンティティの Department
プロパティは実際には通貨を表しますが、そのデータ型は であり、値は Decimal
0 から $1,000,000.00 の間である必要があることを指定します。 また、プロパティを StartDate
mm/dd/yyyy 形式の日付として書式設定する必要があることを指定します。
Departments.aspx ページを実行します。
Budget 列または開始日列の Departments.aspx ページ マークアップで書式指定文字列を指定しなかったが、Department.cs ファイルで指定したメタデータを使用して、既定の通貨と日付の書式設定がコントロールによってDynamicField
適用されていることに注意してください。
挿入と削除の機能の追加
SchoolRepository.cs を開き、メソッドと メソッドを作成するために次のコードをInsert
Delete
追加します。 このコードには、 メソッドで使用できる次に使用可能なレコード キー値を計算する という名前 GenerateDepartmentID
の Insert
メソッドも含まれています。 これは、テーブルに対してこれを自動的に計算するようにデータベースが構成されていないため、 Department
必須です。
public void InsertDepartment(Department department)
{
try
{
department.DepartmentID = GenerateDepartmentID();
context.Departments.AddObject(department);
context.SaveChanges();
}
catch (Exception ex)
{
//Include catch blocks for specific exceptions first,
//and handle or log the error as appropriate in each.
//Include a generic catch block like this one last.
throw ex;
}
}
public void DeleteDepartment(Department department)
{
try
{
context.Departments.Attach(department);
context.Departments.DeleteObject(department);
context.SaveChanges();
}
catch (Exception ex)
{
//Include catch blocks for specific exceptions first,
//and handle or log the error as appropriate in each.
//Include a generic catch block like this one last.
throw ex;
}
}
private Int32 GenerateDepartmentID()
{
Int32 maxDepartmentID = 0;
var department = (from d in GetDepartments()
orderby d.DepartmentID descending
select d).FirstOrDefault();
if (department != null)
{
maxDepartmentID = department.DepartmentID + 1;
}
return maxDepartmentID;
}
Attach メソッド
メソッドは DeleteDepartment
、 メソッドを Attach
呼び出して、メモリ内のエンティティと、それが表すデータベース行との間のオブジェクト コンテキストのオブジェクト状態マネージャーで保持されるリンクを再確立します。 これは、メソッドが メソッドを呼び出す前に発生する SaveChanges
必要があります。
オブジェクト コンテキストという用語は、エンティティ セットとエンティティへのアクセスに使用するObjectContext
クラスから派生した Entity Framework クラスを指します。 このプロジェクトのコードでは、 クラスに という名前 SchoolEntities
が付けられ、そのインスタンスには常に という名前が付けられます context
。 オブジェクト コンテキストの オブジェクト状態マネージャー は、 クラスから ObjectStateManager
派生したクラスです。 オブジェクト連絡先は、オブジェクト状態マネージャーを使用してエンティティ オブジェクトを格納し、各オブジェクトがデータベース内の対応するテーブル行または行と同期しているかどうかを追跡します。
エンティティを読み取ると、オブジェクト コンテキストはそれをオブジェクト状態マネージャーに格納し、そのオブジェクトの表現がデータベースと同期しているかどうかを追跡します。 たとえば、プロパティ値を変更すると、変更したプロパティがデータベースと同期しなくなったことを示すフラグが設定されます。 次に、 メソッドを SaveChanges
呼び出すと、オブジェクト状態マネージャーはエンティティの現在の状態とデータベースの状態の違いを正確に把握しているため、オブジェクト コンテキストはデータベースで実行する処理を認識します。
ただし、このプロセスは通常、Web アプリケーションでは機能しません。これは、エンティティを読み取るオブジェクト コンテキスト インスタンスとそのオブジェクト状態マネージャー内のすべてのものが、ページがレンダリングされた後に破棄されるためです。 変更を適用する必要があるオブジェクト コンテキスト インスタンスは、ポストバック処理用にインスタンス化された新しいインスタンスです。 メソッドの DeleteDepartment
場合、コントロールは ObjectDataSource
ビュー ステートの値から元のバージョンのエンティティを再作成しますが、この再作成された Department
エンティティはオブジェクト状態マネージャーに存在しません。 この再作成されたエンティティで メソッドを DeleteObject
呼び出した場合、オブジェクト コンテキストはエンティティがデータベースと同期しているかどうかを認識しないため、呼び出しは失敗します。 ただし、 メソッドを Attach
呼び出すと、再作成されたエンティティと、オブジェクト コンテキストの以前のインスタンスでエンティティが読み取られたときに最初に自動的に実行されたデータベース内の値との間で同じ追跡が再確立されます。
オブジェクト コンテキストでオブジェクト状態マネージャー内のエンティティを追跡したくない場合があり、フラグを設定して、それを防ぐことができます。 この例は、このシリーズの後のチュートリアルで示されています。
SaveChanges メソッド
この単純なリポジトリ クラスは、CRUD 操作を実行する方法の基本的な原則を示しています。 この例では、各更新の直後に SaveChanges
メソッドが呼び出されます。 運用アプリケーションでは、別のメソッドから メソッドを SaveChanges
呼び出して、データベースが更新されるタイミングをより細かく制御できます。 (次のチュートリアルの最後には、関連する更新プログラムを調整するための 1 つのアプローチである作業単位パターンについて説明するホワイト ペーパーへのリンクがあります)。また、この例では、 DeleteDepartment
メソッドにコンカレンシーの競合を処理するためのコードが含まれていないことに注意してください。これを行うコードは、このシリーズの後のチュートリアルで追加されます。
挿入時に選択する講師名の取得
ユーザーは、新しい部門を作成するときに、ドロップダウン リストの講師の一覧から管理者を選択できる必要があります。 したがって、 SchoolRepository.cs に次のコードを追加して、前に作成したビューを使用して講師の一覧を取得するメソッドを作成します。
public IEnumerable<InstructorName> GetInstructorNames()
{
return context.InstructorNames.OrderBy("it.FullName").ToList();
}
部署を挿入するためのページの作成
Site.Master ページを使用する DepartmentsAdd.aspx ページを作成し、 という名前Content2
のコントロールに次のマークアップをContent
追加します。
<h2>Departments</h2>
<asp:ObjectDataSource ID="DepartmentsObjectDataSource" runat="server"
TypeName="ContosoUniversity.DAL.SchoolRepository" DataObjectTypeName="ContosoUniversity.DAL.Department"
InsertMethod="InsertDepartment" >
</asp:ObjectDataSource>
<asp:DetailsView ID="DepartmentsDetailsView" runat="server"
DataSourceID="DepartmentsObjectDataSource" AutoGenerateRows="False"
DefaultMode="Insert" OnItemInserting="DepartmentsDetailsView_ItemInserting">
<Fields>
<asp:DynamicField DataField="Name" HeaderText="Name" />
<asp:DynamicField DataField="Budget" HeaderText="Budget" />
<asp:DynamicField DataField="StartDate" HeaderText="Start Date" />
<asp:TemplateField HeaderText="Administrator">
<InsertItemTemplate>
<asp:ObjectDataSource ID="InstructorsObjectDataSource" runat="server"
TypeName="ContosoUniversity.DAL.SchoolRepository"
DataObjectTypeName="ContosoUniversity.DAL.InstructorName"
SelectMethod="GetInstructorNames" >
</asp:ObjectDataSource>
<asp:DropDownList ID="InstructorsDropDownList" runat="server"
DataSourceID="InstructorsObjectDataSource"
DataTextField="FullName" DataValueField="PersonID" OnInit="DepartmentsDropDownList_Init">
</asp:DropDownList>
</InsertItemTemplate>
</asp:TemplateField>
<asp:CommandField ShowInsertButton="True" />
</Fields>
</asp:DetailsView>
<asp:ValidationSummary ID="DepartmentsValidationSummary" runat="server"
ShowSummary="true" DisplayMode="BulletList" />
このマークアップは、新しいDepartment
エンティティを挿入するためのコントロールと、部門管理者の選択に使用されるコントロールのインストラクター名DropDownList
を取得するためのコントロールの 2 つObjectDataSource
を作成します。 マークアップは、 DetailsView
新しい部門を入力するためのコントロールを作成し、外部キー値を設定できるように、コントロールの ItemInserting
イベントのハンドラーを Administrator
指定します。 最後には、エラー メッセージを ValidationSummary
表示するコントロールがあります。
DepartmentsAdd.aspx.cs を開き、次using
のステートメントを追加します。
using ContosoUniversity.DAL;
次のクラス変数とメソッドを追加します。
private DropDownList administratorsDropDownList;
protected void Page_Init(object sender, EventArgs e)
{
DepartmentsDetailsView.EnableDynamicData(typeof(Department));
}
protected void DepartmentsDropDownList_Init(object sender, EventArgs e)
{
administratorsDropDownList = sender as DropDownList;
}
protected void DepartmentsDetailsView_ItemInserting(object sender, DetailsViewInsertEventArgs e)
{
e.Values["Administrator"] = administratorsDropDownList.SelectedValue;
}
メソッドを Page_Init
使用すると、動的データ機能が有効になります。 コントロールのDropDownList
イベントのInit
ハンドラーはコントロールへの参照を保存し、コントロールのイベントのInserting
ハンドラーDetailsView
はその参照を使用して、選択したインストラクターの値を取得PersonID
し、エンティティの外部キー プロパティをDepartment
更新Administrator
します。
ページを実行し、新しい部署の情報を追加し、[ 挿入 ] リンクをクリックします。
別の新しい部署の値を入力します。 [ 予算 ] フィールドに 1,000,000.00 を超える数値を入力し、次のフィールドにタブを表示します。 フィールドにアスタリスクが表示され、その上にマウス ポインターを置くと、そのフィールドのメタデータに入力したエラー メッセージが表示されます。
[ 挿入] をクリックすると、ページの下部にコントロールによって ValidationSummary
エラー メッセージが表示されます。
次に、ブラウザーを閉じて、 Departments.aspx ページを開きます。 コントロールに属性を追加し、コントロールに属性ObjectDataSource
をDeleteMethod
追加して、DataKeyNames
Departments.aspx ページに削除機能をGridView
追加します。 これらのコントロールの開始タグは、次の例のようになります。
<asp:ObjectDataSource ID="DepartmentsObjectDataSource" runat="server"
TypeName="ContosoUniversity.DAL.SchoolRepository"
DataObjectTypeName="ContosoUniversity.DAL.Department"
SelectMethod="GetDepartments"
DeleteMethod="DeleteDepartment" >
<asp:GridView ID="DepartmentsGridView" runat="server" AutoGenerateColumns="False"
DataSourceID="DepartmentsObjectDataSource" DataKeyNames="DepartmentID" >
ページを実行します。
[ 部署][追加.aspx ] ページの実行時に追加した部門を削除します。
更新機能の追加
SchoolRepository.cs を開き、次Update
のメソッドを追加します。
public void UpdateDepartment(Department department, Department origDepartment)
{
try
{
context.Departments.Attach(origDepartment);
context.ApplyCurrentValues("Departments", department);
context.SaveChanges();
}
catch (Exception ex)
{
//Include catch blocks for specific exceptions first,
//and handle or log the error as appropriate in each.
//Include a generic catch block like this one last.
throw ex;
}
}
Departments.aspx ページで [更新] をクリックすると、ObjectDataSource
メソッドに渡す 2 つのDepartment
エンティティがコントロールによってUpdateDepartment
作成されます。 1 つはビュー ステートに格納されている元の値を格納し、もう 1 つはコントロールに入力された新しい値を GridView
含みます。 メソッドの UpdateDepartment
コードは、元の Department
値を持つエンティティを メソッドに渡して Attach
、エンティティとデータベース内の追跡を確立します。 次に、コードは、新しい値を Department
持つエンティティを メソッドに ApplyCurrentValues
渡します。 オブジェクト コンテキストは、古い値と新しい値を比較します。 新しい値が古い値と異なる場合、オブジェクト コンテキストによってプロパティ値が変更されます。 次に、 メソッドは SaveChanges
、データベース内の変更された列のみを更新します。 (ただし、このエンティティの更新関数がストアド プロシージャにマップされている場合、変更された列に関係なく行全体が更新されます)。
Departments.aspx ファイルを開き、次の属性をコントロールにDepartmentsObjectDataSource
追加します。
UpdateMethod="UpdateDepartment"
ConflictDetection="CompareAllValues"
これにより、古い値がビュー ステートに格納され、 メソッド内Update
の新しい値と比較できるようになります。OldValuesParameterFormatString="orig{0}"
これにより、元の値パラメーターの名前が であることをコントロールにorigDepartment
通知します。
コントロールの開始タグの ObjectDataSource
マークアップは、次の例のようになります。
<asp:ObjectDataSource ID="DepartmentsObjectDataSource" runat="server"
TypeName="ContosoUniversity.DAL.SchoolRepository"
DataObjectTypeName="ContosoUniversity.DAL.Department"
SelectMethod="GetDepartments" DeleteMethod="DeleteDepartment"
UpdateMethod="UpdateDepartment"
ConflictDetection="CompareAllValues"
OldValuesParameterFormatString="orig{0}" >
コントロールに OnRowUpdating="DepartmentsGridView_RowUpdating"
属性を GridView
追加します。 これを使用して、ユーザーが Administrator
ドロップダウン リストで選択した行に基づいてプロパティ値を設定します。 開始タグは GridView
次の例のようになります。
<asp:GridView ID="DepartmentsGridView" runat="server" AutoGenerateColumns="False"
DataSourceID="DepartmentsObjectDataSource" DataKeyNames="DepartmentID"
OnRowUpdating="DepartmentsGridView_RowUpdating">
その列の EditItemTemplate
コントロールの Administrator
直後に GridView
、列のコントロールを ItemTemplate
コントロールに追加します。
<EditItemTemplate>
<asp:ObjectDataSource ID="InstructorsObjectDataSource" runat="server" DataObjectTypeName="ContosoUniversity.DAL.InstructorName"
SelectMethod="GetInstructorNames" TypeName="ContosoUniversity.DAL.SchoolRepository">
</asp:ObjectDataSource>
<asp:DropDownList ID="InstructorsDropDownList" runat="server" DataSourceID="InstructorsObjectDataSource"
SelectedValue='<%# Eval("Administrator") %>'
DataTextField="FullName" DataValueField="PersonID" OnInit="DepartmentsDropDownList_Init" >
</asp:DropDownList>
</EditItemTemplate>
このEditItemTemplate
コントロールは、DepartmentsAdd.aspx ページのコントロールに似ていますInsertItemTemplate
。 違いは、 属性を使用して SelectedValue
コントロールの初期値が設定されていることです。
コントロールの前にGridView
、DepartmentsAdd.aspx ページで行ったようにコントロールを追加ValidationSummary
します。
<asp:ValidationSummary ID="DepartmentsValidationSummary" runat="server"
ShowSummary="true" DisplayMode="BulletList" />
Departments.aspx.cs を開き、部分クラス宣言の直後に次のコードを追加して、コントロールを参照するプライベート フィールドをDropDownList
作成します。
private DropDownList administratorsDropDownList;
次に、コントロールのDropDownList
イベントとコントロールのInit
イベントのハンドラーをGridView
RowUpdating
追加します。
protected void DepartmentsDropDownList_Init(object sender, EventArgs e)
{
administratorsDropDownList = sender as DropDownList;
}
protected void DepartmentsGridView_RowUpdating(object sender, GridViewUpdateEventArgs e)
{
e.NewValues["Administrator"] = administratorsDropDownList.SelectedValue;
}
イベントのハンドラーは、 Init
コントロールへの参照を DropDownList
クラス フィールドに保存します。 イベントのハンドラーは、 RowUpdating
参照を使用してユーザーが入力した値を取得し、エンティティの Department
プロパティにAdministrator
適用します。
[DepartmentsAdd.aspx] ページを使用して新しい部署を追加し、[Departments.aspx] ページを実行し、追加した行の [編集] をクリックします。
Note
データベース内のデータが無効なため、追加しなかった行 (つまり、データベースに既に存在していた行) を編集することはできません。データベースで作成された行の管理者は学生です。 そのうちの 1 つを編集しようとすると、次のようなエラーを報告するエラー ページが表示されます。 'InstructorsDropDownList' has a SelectedValue which is invalid because it does not exist in the list of items.
無効な 予算 額を入力し、[ 更新] をクリックすると、 Departments.aspx ページに表示されたのと同じアスタリスクとエラー メッセージが表示されます。
フィールド値を変更するか、別の管理者を選択して [ 更新] をクリックします。 変更が表示されます。
これにより、Entity Framework での基本的な CRUD (作成、読み取り、更新、削除) 操作のコントロールの使用 ObjectDataSource
の概要が完了します。 単純な n 層アプリケーションを構築しましたが、ビジネス ロジックレイヤーはデータ アクセス層に密に結合されているため、自動化された単体テストが複雑になります。 次のチュートリアルでは、リポジトリ パターンを実装して単体テストを容易にする方法について説明します。
フィードバック
https://aka.ms/ContentUserFeedback」を参照してください。
以下は間もなく提供いたします。2024 年を通じて、コンテンツのフィードバック メカニズムとして GitHub の issue を段階的に廃止し、新しいフィードバック システムに置き換えます。 詳細については、「フィードバックの送信と表示