次の方法で共有


Entity Framework 4.0 と ObjectDataSource コントロールの使用、パート 1: はじめに

作成者: Tom Dykstra

このチュートリアル シリーズは、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 つは、 コントロールではなく コントロールをObjectDataSourceEntityDataSource使用することです。 コントロールを ObjectDataSource 使用する場合は、独自のデータ アクセス コードを実装し、他のデータ ソース コントロールと同じ機能の多くを持つコントロールを使用して .aspx ページで呼び出します。 これにより、n 層アプローチの利点と、データ アクセスにWeb Forms制御を使用する利点を組み合わせることができます。

コントロールを ObjectDataSource 使用すると、他の方法でも柔軟性が向上します。 独自のデータ アクセス コードを記述するため、特定のエンティティ型 (コントロールが実行するように設計されているタスク EntityDataSource ) の読み取り、挿入、更新、または削除以外の操作を行う方が簡単です。 たとえば、エンティティが更新されるたびにログ記録を実行したり、エンティティが削除されるたびにデータをアーカイブしたり、外部キー値を持つ行を挿入するときに必要に応じて関連データを自動的にチェックして更新したりできます。

ビジネス ロジックとリポジトリ クラス

コントロールは ObjectDataSource 、作成したクラスを呼び出すことによって機能します。 クラスには、データを取得および更新するメソッドが含まれており、それらのメソッドの名前をマークアップでコントロールに ObjectDataSource 指定します。 レンダリングまたはポストバック処理中に、 は ObjectDataSource 指定したメソッドを呼び出します。

基本的な CRUD 操作に加えて、コントロールで使用するために作成するクラスは、データの ObjectDataSource 読み取りまたは更新時にビジネス ロジックを ObjectDataSource 実行する必要がある場合があります。 たとえば、ある部署を更新する場合、1 人のユーザーが複数の部署の管理者になれないので、他の部門に同じ管理者がいないことを検証する必要がある場合があります。

ObjectDataSource クラスの概要などの一部ObjectDataSourceのドキュメントでは、ビジネス ロジックとデータ アクセス ロジックの両方を含むビジネス オブジェクトと呼ばれるクラスを呼び出します。 このチュートリアルでは、ビジネス ロジックとデータ アクセス ロジック用に個別のクラスを作成します。 データ アクセス ロジックをカプセル化するクラスは、 リポジトリと呼ばれます。 ビジネス ロジック クラスにはビジネス ロジック メソッドとデータ アクセス メソッドの両方が含まれますが、データ アクセス メソッドはリポジトリを呼び出してデータ アクセス タスクを実行します。

また、BLL と DAL の間に抽象化レイヤーを作成して、BLL の自動単体テストを容易にします。 この抽象化レイヤーは、インターフェイスを作成し、ビジネス ロジック クラスでリポジトリをインスタンス化するときに インターフェイスを使用して実装されます。 これにより、リポジトリ インターフェイスを実装する任意のオブジェクトへの参照をビジネス ロジック クラスに提供できます。 通常の操作では、Entity Framework で動作するリポジトリ オブジェクトを指定します。 テスト用に、コレクションとして定義されたクラス変数など、簡単に操作できる方法で格納されたデータを操作するリポジトリ オブジェクトを提供します。

次の図は、リポジトリのないデータ アクセス ロジックを含むビジネス ロジック クラスと、リポジトリを使用するクラスの違いを示しています。

Image05

まず、コントロールが基本的なデータ アクセス タスクのみを実行するため、コントロールがリポジトリに直接バインドされる Web ページ ObjectDataSource を作成します。 次のチュートリアルでは、検証ロジックを使用してビジネス ロジック クラスを作成し、リポジトリ クラスではなく、そのクラスにコントロールをバインド ObjectDataSource します。 検証ロジックの単体テストも作成します。 このシリーズの 3 番目のチュートリアルでは、並べ替えとフィルター処理の機能をアプリケーションに追加します。

このチュートリアルで作成するページは、はじめに チュートリアル シリーズで作成したデータ モデルのエンティティ セットと連携Departmentsします。

[部署] ページの外観を示すスクリーンショット。

Image02

データベースとデータ モデルの更新

このチュートリアルを開始するには、データベースに 2 つの変更を加えます。両方とも、Entity Framework とWeb Formsチュートリアルを使用してはじめにで作成したデータ モデルに対応する変更が必要です。 これらのチュートリアルの 1 つでは、データベース変更後にデータ モデルをデータベースと同期するようにデザイナーで手動で変更を行いました。 このチュートリアルでは、デザイナーの データベースからのモデルの更新 ツールを使用して、データ モデルを自動的に更新します。

データベースへのリレーションシップの追加

Visual Studio で、Entity Framework と Web Forms チュートリアル シリーズを使用してはじめにで作成した Contoso University Web アプリケーションを開き、データベース ダイアグラムをSchoolDiagram開きます。

データベース ダイアグラムのテーブルを Department 見ると、列が含まれている Administrator ことがわかります。 この列はテーブルの Person 外部キーですが、データベースに外部キーリレーションシップは定義されていません。 エンティティ フレームワークがこのリレーションシップを自動的に処理できるように、リレーションシップを作成し、データ モデルを更新する必要があります。

データベース ダイアグラムで、テーブルを Department 右クリックし、[リレーションシップ] を選択 します

Image80

[ 外部キーリレーションシップ ] ボックスの [ 追加] をクリックし、[ テーブルと列の指定] の省略記号をクリックします。

Image81

[テーブルと列] ダイアログ ボックスで、主キー テーブルとフィールドを と PersonIDPerson設定し、外部キー テーブルとフィールドを と AdministratorDepartment設定します。 (この操作を行うと、リレーションシップ名が から FK_Department_DepartmentFK_Department_Person変更されます)。

Image82

[テーブルと列] ボックスで [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] を展開し、[ビュー] フォルダーを右クリックして、[新しいビューの追加] を選択します。

Image06

[テーブルの追加] ダイアログ ボックスが表示されたら [閉じる] をクリックし、次の SQL ステートメントを [SQL] ペインに貼り付けます。

SELECT        LastName + ',' + FirstName AS FullName, PersonID
FROM          dbo.Person
WHERE        (HireDate IS NOT NULL)

ビューを として vInstructorName保存します。

データ モデルの更新

DAL フォルダーで SchoolModel.edmx ファイルを開き、デザイン画面を右クリックして、[データベースからモデルの更新] を選択します。

Image07

[ データベース オブジェクトの選択 ] ダイアログ ボックスで、[ 追加 ] タブを選択し、先ほど作成したビューを選択します。

Image08

[完了] をクリックします。

デザイナーでは、ツールによってエンティティが作成され、 エンティティと エンティティ間DepartmentPersonの新しい関連付けが作成vInstructorNameされていることがわかります。

Image13

Note

[出力エラー一覧] ウィンドウに、ツールによって新しいvInstructorNameビューの主キーが自動的に作成されたことを示す警告メッセージが表示される場合があります。 これは正しい動作です。

コードで新しい vInstructorName エンティティを参照する場合は、小文字の "v" の前に "v" を付けるというデータベース規則を使用したくありません。 そのため、モデル内のエンティティとエンティティ セットの名前を変更します。

モデル ブラウザーを開きます。 vInstructorNameエンティティの種類とビューとして一覧表示されます。

Image14

[ SchoolModel ] ( SchoolModel.Store ではなく) で 、vInstructorName を右クリックし、[ プロパティ] を選択します。 [ プロパティ ] ウィンドウで、 Name プロパティを "InstructorName" に変更し 、Entity Set Name プロパティを "InstructorNames" に変更します。

Image15

データ モデルを保存して閉じ、プロジェクトを再構築します。

リポジトリ クラスと 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 表示するコントロールが作成されます。 コントロールは GridViewEdit コマンドと 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 プロパティは実際には通貨を表しますが、そのデータ型は であり、値は Decimal0 から $1,000,000.00 の間である必要があることを指定します。 また、プロパティを StartDate mm/dd/yyyy 形式の日付として書式設定する必要があることを指定します。

Departments.aspx ページを実行します。

実行時の [部門] ページを示すスクリーンショット。

Budget 列または開始日列の Departments.aspx ページ マークアップで書式指定文字列を指定しなかったが、Department.cs ファイルで指定したメタデータを使用して、既定の通貨と日付の書式設定がコントロールによってDynamicField適用されていることに注意してください。

挿入と削除の機能の追加

SchoolRepository.cs を開き、メソッドと メソッドを作成するために次のコードをInsertDelete追加します。 このコードには、 メソッドで使用できる次に使用可能なレコード キー値を計算する という名前 GenerateDepartmentIDInsert メソッドも含まれています。 これは、テーブルに対してこれを自動的に計算するようにデータベースが構成されていないため、 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します。

ページを実行し、新しい部署の情報を追加し、[ 挿入 ] リンクをクリックします。

Image04

別の新しい部署の値を入力します。 [ 予算 ] フィールドに 1,000,000.00 を超える数値を入力し、次のフィールドにタブを表示します。 フィールドにアスタリスクが表示され、その上にマウス ポインターを置くと、そのフィールドのメタデータに入力したエラー メッセージが表示されます。

Image03

[ 挿入] をクリックすると、ページの下部にコントロールによって ValidationSummary エラー メッセージが表示されます。

Image12

次に、ブラウザーを閉じて、 Departments.aspx ページを開きます。 コントロールに属性を追加し、コントロールに属性ObjectDataSourceDeleteMethod追加して、DataKeyNamesDepartments.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 コントロールの初期値が設定されていることです。

コントロールの前にGridViewDepartmentsAdd.aspx ページで行ったようにコントロールを追加ValidationSummaryします。

<asp:ValidationSummary ID="DepartmentsValidationSummary" runat="server" 
        ShowSummary="true" DisplayMode="BulletList"  />

Departments.aspx.cs を開き、部分クラス宣言の直後に次のコードを追加して、コントロールを参照するプライベート フィールドをDropDownList作成します。

private DropDownList administratorsDropDownList;

次に、コントロールのDropDownListイベントとコントロールのInitイベントのハンドラーをGridViewRowUpdating追加します。

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.

Image10

無効な 予算 額を入力し、[ 更新] をクリックすると、 Departments.aspx ページに表示されたのと同じアスタリスクとエラー メッセージが表示されます。

フィールド値を変更するか、別の管理者を選択して [ 更新] をクリックします。 変更が表示されます。

[部署] ページを示すスクリーンショット。

これにより、Entity Framework での基本的な CRUD (作成、読み取り、更新、削除) 操作のコントロールの使用 ObjectDataSource の概要が完了します。 単純な n 層アプリケーションを構築しましたが、ビジネス ロジックレイヤーはデータ アクセス層に密に結合されているため、自動化された単体テストが複雑になります。 次のチュートリアルでは、リポジトリ パターンを実装して単体テストを容易にする方法について説明します。