データ バインディング

LINQ to SQL では、グリッド コントロールなどのコモン コントロールに対するバインディングがサポートされています。 具体的に、LINQ to SQL では、データ グリッドにバインドし、マスターと詳細のバインディングを処理するための基本パターンが、表示と更新の両方に関して定義されています。

基本原則

LINQ to SQL では、LINQ クエリがデータベースで実行するために、SQL に変換されます。 その結果は、厳密に型指定された IEnumerable になります。 これらのオブジェクトは通常の共通言語ランタイム (CLR: Common Language Runtime) オブジェクトであるため、一般的なオブジェクト データ バインディングを使用して結果を表示できます。 一方、変更操作 (挿入、更新、および削除) には、追加的な手順が必要です。

操作

Windows フォーム コントロールに対する暗黙バインディングは、IListSource を実装することで実現されます。 データ ソースのジェネリック Table<TEntity> (C# では Table<T>、Visual Basic では Table(Of T)) およびジェネリック DataQuery は、IListSource を実装するように更新されています。 ユーザー インターフェイス (UI) データ バインディング エンジン (Windows フォームおよび Windows Presentation Foundation) はいずれも、データ ソースが IListSource を実装しているかどうかをテストします。 したがって、コントロールのデータ ソースに対するクエリの直接表示を作成すると、次の例のように LINQ to SQL のコレクション生成が暗黙的に呼び出されます。

DataGrid dataGrid1 = new DataGrid();
DataGrid dataGrid2 = new DataGrid();
DataGrid dataGrid3 = new DataGrid();

var custQuery =
    from cust in db.Customers
    select cust;
dataGrid1.DataSource = custQuery;
dataGrid2.DataSource = custQuery;
dataGrid2.DataMember = "Orders";

BindingSource bs = new BindingSource();
bs.DataSource = custQuery;
dataGrid3.DataSource = bs;
Dim dataGrid1 As New DataGrid()
Dim dataGrid2 As New DataGrid()
Dim dataGrid3 As New DataGrid()

Dim custQuery = _
    From cust In db.Customers _
    Select cust

dataGrid1.DataSource = custQuery
dataGrid2.DataSource = custQuery
dataGrid2.DataMember = "Orders"

Dim bs = _
    New BindingSource()
bs.DataSource = custQuery
dataGrid3.DataSource = bs

Windows Presentation Foundation でも同じようになります。

ListView listView1 = new ListView();
var custQuery2 =
    from cust in db.Customers
    select cust;

ListViewItem ItemsSource = new ListViewItem();
ItemsSource = (ListViewItem)custQuery2;
Dim listView1 As New ListView()
Dim custQuery2 = _
From cust In db.Customers _
Select cust

Dim ItemsSource As New ListViewItem
ItemsSource = custQuery2

コレクション生成は、ジェネリック Table<TEntity> およびジェネリック DataQuery によって GetList で実装されます。

IListSource の実装

LINQ to SQL では、次の 2 か所で IListSource が実装されています。

  • データ ソースが Table<TEntity> の場合: LINQ to SQL によってテーブルが参照され、テーブルへの参照を保持する DataBindingList コレクションが設定されます。

  • データ ソースが IQueryable<T> の場合 : 次の 2 つのシナリオがあります。

    • LINQ to SQL によって、IQueryable<T> から基になる Table<TEntity> が検出された場合、ソースは編集に対応し、上の場合と同じ状況になります。

    • LINQ to SQL で基になる Table<TEntity> が検出できない場合、ソースは編集に対応していません (たとえば groupby)。 LINQ to SQL では、クエリが参照されて、ジェネリック SortableBindingList が設定されます。これは、指定されたプロパティに対する T エンティティの並べ替え機能を実装する単純な BindingList<T> です。

専用コレクション

このドキュメントでここまでに説明した多くの機能について、BindingList<T> を特化したクラスが用意されています。 ジェネリック SortableBindingList クラスと、ジェネリック DataBindingList クラスです。 いずれも内部クラスとして宣言されています。

ジェネリック SortableBindingList

これは BindingList<T> を継承したクラスで、並べ替え可能な BindingList<T> です。 並べ替えはメモリ内で処理され、データベース自体とのやり取りは行われません。 BindingList<T>IBindingList を実装していますが、既定では並べ替えをサポートしていません。 ただし、BindingList<T> では、仮想 "コア" メソッドで IBindingList が実装されています。 これらのメソッドを簡単にオーバーライドできます。 ジェネリック SortableBindingList は、SupportsSortingCoreSortPropertyCoreSortDirectionCore、および ApplySortCore をオーバーライドします。 ApplySortCoreApplySort によって呼び出され、指定のプロパティで T の項目のリストを並べ替えます。

そのプロパティが T にない場合、例外が発生します。

並べ替えを行うため、LINQ to SQL により、ジェネリック IComparer.Compare を継承するジェネリック SortableBindingList.PropertyComparer クラスが作成され、特定の型 T、PropertyDescriptor、方向に対する既定の比較子が実装されます。 このクラスは、T の Comparer を動的に作成します (T は PropertyTypePropertyDescriptor)。 そして、静的なジェネリック Comparer から既定の比較子が取得されます。 既定のインスタンスはリフレクションを使用して取得されます。

ジェネリック SortableBindingListDataBindingList の基本クラスでもあります。 ジェネリック SortableBindingList には、項目の追加と削除の追跡を中断および再開するための 2 つの仮想メソッドがあります。 これら 2 つのメソッドは、並べ替えなどの基本機能にも使用できますが、実際にはジェネリック DataBindingList などの上位クラスによって実装されます。

ジェネリック DataBindingList

このクラスは、ジェネリック SortableBindingLIst を継承しています。 ジェネリック DataBindingList は、最初にコレクションの読み込みに使用したジェネリック Table の基になるジェネリック IQueryable に対する参照を保持しています。 ジェネリック DatabindingList では、InsertItem() と RemoveItem() がオーバーライドされて、コレクションに対する項目の追加と削除を追跡する処理が追加されています。 また、追跡を中断および再開する機能の抽象メソッドが実装され、条件に応じた追跡が可能となっています。 この結果、ジェネリック DataBindingList では、親クラスが持つ追跡機能のポリモーフィックな使用法がすべて活用されています。

EntitySet へのバインディング

EntitySet へのバインディングは特別なケースです。EntitySet は既に、IBindingList を実装したコレクションであるためです。 LINQ to SQL により、並べ替えとキャンセル (ICancelAddNew) のサポートが追加されます。 EntitySet クラスは内部リストを使用してエンティティを格納します。 このリストは、ジェネリック配列 (ジェネリック ItemList クラス) を基にした低水準のコレクションです。

並べ替え機能の追加

Array には、T の Comparer を使用できる並べ替えメソッド (Array.Sort()) があります。LINQ to SQL は、このトピックで前に説明したジェネリック SortableBindingList.PropertyComparer クラスを使用して、プロパティに応じた Comparer と、並べ替えの方向を取得します。 ジェネリック ApplySort には、この機能を呼び出すための ItemList メソッドが追加されています。

EntitySet 側では、並べ替えのサポートを次のように宣言する必要があります。

  • SupportsSorting は、true を返します。

  • ApplySort では、entities.ApplySort() を呼び出した後で OnListChanged() を呼び出します。

  • SortDirection プロパティおよび SortProperty プロパティでは、現在の並べ替えの定義を公開します。これはローカル メンバーに格納されています。

System.Windows.Forms.BindingSource を使用して EntitySet<TEntity> を System.Windows.Forms.BindingSource.DataSource にバインドした場合、BindingSource.List を更新するには EntitySet<TEntity>.GetNewBindingList を呼び出す必要があります。

System.Windows.Forms.BindingSource を使用して BindingSource.DataMember プロパティを設定し、BindingSource.DataMember で指定した、EntitySet<TEntity> を公開するプロパティを含むクラスに BindingSource.DataSource を設定した場合、BindingSource.List を更新するときに EntitySet<TEntity>.GetNewBindingList を呼び出す必要はありませんが、並べ替え機能が失われます。

キャッシュ

LINQ to SQL クエリでは、GetList が実装されています。 Windows フォームの BindingSource クラスは、このインターフェイスがあると、1 つの接続に対して GetList() を 3 回呼び出します。 この状況に対処するため、LINQ to SQL では、格納するインスタンスごとにキャッシュが実装され、生成された同じコレクションが常に返されます。

キャンセル

IBindingList には AddNew メソッドが定義されています。バインドされたコレクションから新しい項目を作成するためにコントロールが使用するメソッドです。 DataGridView コントロールでは、表示されている最後の行のヘッダーにアスタリスクが表示されている状況で、この機能が明確に示されます。 このアスタリスクは、新しい項目を追加できることを示します。

コレクションは、この機能に加えて、ICancelAddNew も実装することができます。 この機能によって、コントロールでは、キャンセルを行ったり、新しく編集された項目が検証されているかどうかを確認したりできるようになります。

ICancelAddNew は、LINQ to SQL のすべてのデータ バインド コレクション (ジェネリック SortableBindingList およびジェネリック EntitySet) に実装されます。 どちらの実装でも、コードは次のように動作します。

  • いったん挿入した項目をコレクションから削除できます。

  • UI が編集をコミットしていない場合、変更を追跡しません。

  • 編集がキャンセルされた場合 (CancelNew)、変更を追跡しません。

  • 編集がコミットされた場合 (EndNew)、追跡を行うことができます。

  • 新しい項目が AddNew で追加されたものでない場合、コレクションは通常どおり動作します。

トラブルシューティング

このセクションでは、LINQ to SQL のデータ バインディング アプリケーションのトラブルシューティングに役立つ可能性がある項目について説明します。

  • プロパティを使用する必要があります。フィールドのみの使用では不十分です。 この使用は Windows フォームで必要です。

  • 既定では、imagevarbinarytimestamp の各データベース型は、バイト配列にマップされます。 このシナリオでは ToString() がサポートされていないため、これらのオブジェクトは表示できません。

  • 主キーにマップされたクラス メンバーには setter がありますが、LINQ to SQL ではオブジェクト ID の変更はサポートされていません。 したがって、対応付けで使用されているデータベースの主キー/一意キーは更新できません。 グリッドを変更すると、SubmitChanges を呼び出したときに例外が発生します。

  • 1 つのエンティティが 2 つの別個のグリッド (たとえばマスター グリッドと詳細グリッド) にバインドされている場合、マスター グリッドで Delete を行っても、詳細グリッドには反映されません。

関連項目