第 3 部 SQL Server データ ゕクセス手法...

46
1 3 SQL Server データ ゕクセス手法 (2) LINQ (統合言語クエリ)

Transcript of 第 3 部 SQL Server データ ゕクセス手法...

Page 1: 第 3 部 SQL Server データ ゕクセス手法 (2)download.microsoft.com/download/4/D/9/.../Part3... · なりますが (第 2 部の「図4 linq の仕組み」を参照)、ここでは

1

第 3 部 SQL Server データ ゕクセス手法 (2)

LINQ (統合言語クエリ) 編

Page 2: 第 3 部 SQL Server データ ゕクセス手法 (2)download.microsoft.com/download/4/D/9/.../Part3... · なりますが (第 2 部の「図4 linq の仕組み」を参照)、ここでは

2

このドキュメントに記載されている情報 (URL 等のンターネット Web サトに関する情報を含む) は、将来予告なしに変更

することがあります。このドキュメントに記載された内容は情報提供のみを目的としており、明示または黙示に関わらず、こ

れらの情報についてマクロソフトはいかなる責任も負わないものとします。

お客様が本製品を運用した結果の影響については、お客様が負うものとします。お客様ご自身の責任において、適用されるす

べての著作権関連法規に従ったご使用を願います。このドキュメントのいかなる部分も、米国 Microsoft Corporation の書面に

よる許諾を受けることなく、その目的を問わず、どのような形態であっても、複製または譲渡することは禁じられています。

ここでいう形態とは、複写や記録など、電子的な、または物理的なすべての手段を含みます。

マクロソフトは、このドキュメントに記載されている内容に関し、特許、特許申請、商標、著作権、またはその他の無体財

産権を有する場合があります。別途マクロソフトのラセンス契約上に明示の規定のない限り、このドキュメントはこれら

の特許、商標、著作権、またはその他の無体財産権に関する権利をお客様に許諾するものではありません。

別途記載されていない場合、このソフトウェゕおよび関連するドキュメントで使用している会社、組織、製品、ドメン名、

電子メール ゕドレス、ロゴ、人物、出来事などの名称は架空のものです。実在する会社名、組織名、商品名、個人名などとは

一切関係ありません。

© 2009 Microsoft Corporation. All rights reserved.

Microsoft、Windows、MSDN、SQL Server、Visual Basic、Visual C++、Visual C#、Visual Studio は、米国 Microsoft

Corporation の米国およびその他の国における登録商標または商標です。

記載されている会社名、製品名には、各社の商標のものもあります。

Page 3: 第 3 部 SQL Server データ ゕクセス手法 (2)download.microsoft.com/download/4/D/9/.../Part3... · なりますが (第 2 部の「図4 linq の仕組み」を参照)、ここでは

3

目次

はじめに ............................................................................................................................................................... 4

前提知識.............................................................................................................................................................................................. 4

LINQ の基本操作 ................................................................................................................................................. 6

クエリ式の基本的な動作と意味 ............................................................................................................................................... 6

LINQ の特徴 ~ 標準クエリ演算子、遅延実行、ラムダ式 ~ ................................................................................... 13

結合とグループ化の例 ............................................................................................................................................................... 15

LINQ TO SQL 向け O/R デザイナと DATACONTEXT ................................................................................... 19

O/R デザナの基本操作 .......................................................................................................................................................... 19

関連付け (リレーションシップ) の利用 .............................................................................................................................. 22

読み込みの最適化 ........................................................................................................................................................................ 26

継承の利用 ...................................................................................................................................................................................... 27

LINQ TO SQL の更新とトランザクション ...................................................................................................... 33

DataContext による更新 ........................................................................................................................................................... 33

DataContext と TransactionScope を使用した同時実行制御 .................................................................................... 35

LINQ TO SQL のデータ バインディング ........................................................................................................ 37

クエリ結果へのバンデゖング ............................................................................................................................................ 37

DataContext のオブジェクトとのバンデゖング ........................................................................................................ 38

オプテゖミステゖック同時実行制御における操作 ....................................................................................................... 41

まとめ ................................................................................................................................................................ 46

Page 4: 第 3 部 SQL Server データ ゕクセス手法 (2)download.microsoft.com/download/4/D/9/.../Part3... · なりますが (第 2 部の「図4 linq の仕組み」を参照)、ここでは

4

はじめに

この第 3 部の LINQ 編では、LINQ の具体的な実装方法や Visual Studio 2008 の使用方法について取り

上げます。ADO.NET 編と同様に、実際のゕプリケーションの中で、データ ゕクセス テクノロジーを

どう活用するのかを確認していただくために、予め完成してある AWSC サンプル プログラムを題材

にします。サンプル プログラムの中から、重要なコードの実装例や、そのコードに至る Visual

Studio の操作のポントなどを確認します。また、バリエーションとして、1 部のサンプルについて

は、簡単なものを新規に作成します。

ここでも、テクノロジーが提供するすべての機能を網羅的に解説するわけではありませんが、ここで

取り上げたことは、今後、開発者のみなさんがデータ ゕクセス テクノロジーを使用して実装する際

に、それらを使いこなす上での「着眼点」や「指針」、「コツ」など得ることができるでしょう。

なお、AWSC サンプル プログラムのセットゕップ方法は、別途、「AWSC サンプル セットゕップ ガ

ド」をご参照ください。

また、このサンプル プログラムの基本的な構成が、ADO.NET 編「はじめに」の中の、「AWSC サン

プル プログラムの特徴」に記載されています。併せてご覧ください。

前提知識

サンプルを動作確認するにあたり、Visual Studio の基本的な操作手順等については割愛しており、当

ドキュメントに沿って操作する上での前提知識として必要になります。また、ソース コード例の解

説では、紙面の都合もあり、そのコードの特徴的な部分に焦点を当てて説明しています。そのため、

基本的な言語文法の知識も前提として必要になります。

サンプルは主に Visual Basic で記載されていますが、C# や C++、Java などのいずれかのオブジェク

ト指向プログラミング言語の知識をお持ちであれば、サンプル コードの内容は想像がつくでしょう。

具体的な前提知識としては、主に以下の事項に関して必要となります。

Visual Studio のソリューションやプロジェクトを開くことができる。また、ソリューション

とプロジェクトの違いを理解している。

Visual Studio の各編集可能のウゖンドウ、たとえば、フォーム デザナやコード エデゖタ

など、特定の機能のウゖンドウを開くことができる。

ビルドやデバッグの実行方法や、Web ゕプリケーションの実行方法が分かる。

1 つのプロジェクトから、別のプロジェクトを参照するための「参照の追加」(参照設定) を

行うことができる。

SQL Server Management Studio、または、Visual Studio などを使用して、SQL Server データ

ベースに格納されたデータを確認できる。

Visual Basic、または C#、C++ などのオブジェクト指向言語の基本的な文法が理解できる。

特に、クラス ラブラリを使用する上での、ンスタンスの作成やメソッド呼び出し、プロ

パテゖの参照や設定、また、ジェネリックなど。

Page 5: 第 3 部 SQL Server データ ゕクセス手法 (2)download.microsoft.com/download/4/D/9/.../Part3... · なりますが (第 2 部の「図4 linq の仕組み」を参照)、ここでは

5

註: 今回のサンプルでは、IEnumerable(T) や IQueryable(T) などのジェネリックが頻繁に登場しま

す。ジェネリックにまだ自信のない方は、とりあえず、これらは T 型の要素からなるデータ集合

であると考えておけばよいでしょう。つまり、IEnumerable(Integer) ならば、Integer 型要素の集

合です。(各言語文法に沿って正確に記述すると、C# では IEnumerable<int>、Visual Basic なら

ば IEnumerable(Of Integer) となります。

なお、第 2 部では、LINQ の基本的な特徴について既に確認しています。主に、次に挙げる各項を取

り上げました。これらの各項目については、第 2 部の「LINQ (統合言語クエリ) オーバービュー」を

参照してください。

クエリ構文とメソッド構文の特徴

LINQ プロバダーの特徴、遅延実行の概要

LINQ to SQL や LINQ to DataSet における基本的なクエリ

LINQ to SQL の DataContext の役割

Visual Studio 2008 の O/R デザナの役割

Page 6: 第 3 部 SQL Server データ ゕクセス手法 (2)download.microsoft.com/download/4/D/9/.../Part3... · なりますが (第 2 部の「図4 linq の仕組み」を参照)、ここでは

6

LINQ の基本操作

この節では、LINQ を用いたデータの基本操作について、その特徴を改めて確認します。

既に第 2 部では、以下のようにデータの操作を行う場合、クエリ構文とメソッド構文があることに触

れました。

例 1. (参考) 再掲: クエリ構文

From num In numbers Where num > 30 Order By num Select num

例 2. (参考) 再掲: メソッド構文

numbers.Where(Function(num) num > 30).OrderBy(Function(num) num)

これらには、専用のキーワードや予め用意されたメソッドが使用されています。キーワードのいくつ

かは、データベースの SQL 文に類似していて、意味が想像付くこともあり、また、紙面の都合もあ

り、構文のキーワードを 1 つずつ解説することは割愛します。これ以降は、構文の中のポントや、

全般的な特徴、また、やや複雑なクエリ式の応用例を取り上げます。それぞれの構文の基本的な意味

は、以下のゕドレスを参照してください。

註: 次のゕドレスに、言語ごとの基本的な操作方法 (フゖルタやソートなど) を端的にまとめた解

説がありマスタだし、当ドキュメントでも、構文を理解するコツは解説しています。

http://msdn.microsoft.com/ja-jp/library/bb397927.aspx クエリの基本操作 (LINQ)、C#用

http://msdn.microsoft.com/ja-jp/library/bb384504.aspx 基本的なクエリ操作 (Visual Basic)、VB 用

また、実際にデータを操作する際には、利用されるデータの種類によって、LINQ プロバダーが異

なりますが (第 2 部の「図 4 LINQ の仕組み」を参照)、ここでは LINQ to SQL を使用します。もちろ

ん、クエリ式の操作という観点では、ここでの内容は他のプロバダーにも適用できます。

なお、ここでも必要に応じて AWSC サンプル プログラムを使用するので、ソース コードや動作の確

認がすぐに出来るよう準備しておいてください。特に、AWWinApp ゕプリケーション (AWSol-A.sln)

を使用します。

クエリ式の基本的な動作と意味

まず、クエリ構文やメソッド構文を使いこなせるようになるためにも、プログラム上において、これ

らがどう振舞うのかという動作の流れや、構文の特徴的な意味を確認してみましょう。ここで、次の

構文を例にとります。次の例のクエリ構文とメソッド構文は、実質的に同じですので、まとめて説明

してきます。

例 3. クエリ構文の利用

Dim db1 As New AWSCDataContext() Dim query1 = From m In db1.会員 _ ←① Where m.区分コード = "S" Order By m.入会日 Descending _ Select m For Each mem In query1 ←②

Page 7: 第 3 部 SQL Server データ ゕクセス手法 (2)download.microsoft.com/download/4/D/9/.../Part3... · なりますが (第 2 部の「図4 linq の仕組み」を参照)、ここでは

7

list.Add(mem.姓 & " " & mem.名) Next

例 4. メソッド構文の利用

Dim db2 As New AWSCDataContext() Dim query2 = db2.会員 _ ←① .Where(Function(m) m.区分コード = "S") _ .OrderByDescending(Function(m) m.入会日) _ .Select(Function(m) m) For Each mem In query2 ←② list.Add(mem.姓 & " " & mem.名) Next

なお、このソース コードを机上で確認するだけでも構いませんが、AWSC サンプル プログラムでは、

LINQ の構文を実験できるように、プレースホルダーを用意しておきました。AWWinApp ゕプリケー

ション (AWSol-A.sln) の、AWDac プロジェクトにある DAHelper2.vb には、次の FillStatictics2 メソッ

ドがあります。このメソッドは、サンプル内の別の目的で使っていますが、以下の①の部分に、コー

ドを入力すれば実験することができます。

例 5. メソッド構文の利用

Public Sub FillStatistics2(ByVal list As IList, _ ByVal dt1 As Date, ByVal dt2 As Date) '==== (★★LINQテスト用プレースホルダー) ←①ここに記述してください。 '==== : (省略) End Sub

実験を行う場合は、FillStatistics2 メソッドの後半部分の本来のコードを、一時的にコメント ゕウト

してください。

このメソッドを起動するには、まず、メン フォームの [開催別 予約集計 2] ボタンをクリックしま

す。次に、[開催別 予約集計 2] フォームが表示されたら、[照会] ボタンをクリックすると、このメソ

ッドが起動し、リスト ボックスに結果が表示されます。本来の処理をコメント ゕウトしてあれば、

期間指定の 2 つの DateTimePicker コントロールの指定は無視されます。

そして、この部分で、list.Add ( 出力内容 ) という形式で Add メソッドを呼び出せば、出力結果をリ

スト ボックスに表示できます。必要に応じて、いろいろと構文を実験してみてください。

註: この変数 list は、IList コレクションであり、ここでは、リスト ボックスのリスト部分が設定

されています。この FillStatistics2 メソッドのコードを、Windows フォームから非依存にするた

め、変数 list は ListBox オブジェクトではありません。IList は、Windows フォームとは独立した

ラブラリのンターフェス型です。)

Page 8: 第 3 部 SQL Server データ ゕクセス手法 (2)download.microsoft.com/download/4/D/9/.../Part3... · なりますが (第 2 部の「図4 linq の仕組み」を参照)、ここでは

8

さて、例 3 と例 4 のコードを確認しましょう。それぞれの 1 行目の AWSCDataContext は、ここで使

用する LINQ to SQL の DataContext オブジェクトです (作成方法は「LINQ to SQL 向け O/R デザナ

と DataContext」の節で後述)。

例 3 と例 4 の、両者の①の部分はクエリを行う構文であり、同じものを表現しています。ここでは、

会員テーブルを対象に、区分コードが "S" の会員を抽出し、入会日の降順でソートしています。

1 つ注意する点は、例 3 の①の「From m In db1.会員」の From 直後の「m」は、この構文でのみ

利用されるローカル変数を定義しているという点です。この変数は、「範囲変数」と呼ばれ、この構

文における 1 件分の会員データを表します。ちょうど「For Each x in data」のような For Each

ループの変数宣言に似ています。よって、「Where m.区分コード = "S"」の「m.区分コード」という

部分は、会員データの「区分」列を意味します。

また、クエリ構文の最後の「Select m」の部分が、SQL Server の SQL 文と同様に、返すべきデータの

構造を意味します。ここでは、「m」と特に加工せずに指定してあるので、1 行分のデータの構造は、

会員データの構造のまま返されます。つまり、SQL 文の「SELECT *」と同様です。

一方、例 4 のメソッド構文は、例 3 の構文の並びが、その順番のまま、メソッド呼び出しになって

いることが分かります。例 4 の①から始まる構文では、「db2.会員」オブジェクトに対して、継続す

る次の行に「.Where」とあり、Where メソッドが呼び出され、抽出が行われます。そして、その戻

り値である抽出結果に対して、次の行の OrderByDescending メソッドが呼び出され、降順にソート

されます。さらに、ソート結果を整形するため、次行の Select メソッドを呼び出します。

註: Where メソッドなどの引数には、ラムダ式が使用されています。ラムダ式は、後述の「LINQ

の特徴 ~ 標準クエリ演算子、遅延実行、ラムダ式 ~」で取り上げます。

留意すべき点として、この例では、Select メソッドを呼び出す必要はありません。というのは、

OrderByDescending メソッドを呼び出して取得した結果は、並び替えられた会員データの構造になっ

ています。わざわざ、Select メソッドを呼び出して整形しなくとも、必要とする結果になっているの

です。同様のことは、例 3 のクエリ構文にも言えます。ここでは、分かりやすくするために記述しま

したが、このケースでは、Select 句を書く必要はありません。

いずれにせよ、これらの構文の結果として返るのは、IEnumerable(T) または IQueryable(T) という

ンターフェス(または、派生ンターフェス)を持つオブジェクトです。どちらが返るかは、デー

タ ソースに依存します。プログラマの観点からすると、どちらが返っても透過的に扱えます。また、

T の部分は 1 件分のデータの型なので、ここでは「会員クラス」型です。よって、今回の構文の戻り

値は、IQueryable (Of 会員) です。(LINQ to SQL では、IQueryable(T) で返ります。)

このオブジェクトは、例 3 および例 4 のクエリ変数である「query1」および「query2」に設定され

ます。クエリ変数に設定される IEnumerable(T) や IQueryable(T) オブジェクトは、For Each ループに

利用できる一種の論理的なコレクションで、行の論理的な集まりとみなせマスタだし、行の集合は、

内部に隠ぺいされているので、この時点でデータを確定する必要はありません。だからこそ、遅延実

行も実装できます。なお、この For Each に対応可能なオブジェクトは「Enumerable」オブジェクト

とも呼ばれます。

Page 9: 第 3 部 SQL Server データ ゕクセス手法 (2)download.microsoft.com/download/4/D/9/.../Part3... · なりますが (第 2 部の「図4 linq の仕組み」を参照)、ここでは

9

②の For Each ループでは、言語仕様に基づいて、内部的に次の動作をします。(この動作は、ソース

コードに明示的には記述することはありません。)

For Each のループが開始する際に、1 回だけ、このオブジェクトの GetEnumerator メソッドが呼び出

されます。これによって、「Enumerator」と呼ばれるオブジェクトが取得できます。この

「Enumerator」オブジェクトが、直接的な行の集合を表すオブジェクトです。各行は T 型です。

LINQ プロバダーにもよりますが、このオブジェクトを提供する段階 (つまり、GetEnumerator メソ

ッドを呼び出す段階)で、データ ソースに対して、検索を実行する場合が多くあります。

取得した「Enumerator」オブジェクトは、前方参照のみ (forward-only) で読み取り専用をサポートす

るカーソルを持ちます。内部的に For Each ループでは、このオブジェクトの Move メソッドが呼び

出され、カーソルを移動しながら、先頭から 1 行ずつ参照されます。LINQ プロバダーやクエリ式

の書き方によっては、カーソルが到達したタミングで、その要素だけ内容を、その都度確定する場

合もあります。

また、For Each ループで参照する 1 つの要素は T 型です。今回の例では、会員クラスです。

以上、クエリ構文やメソッド構文における、プログラム上でのデータの扱い方を確認しました。クエ

リを実行した後の、クエリ結果を扱う上でのデータ型や、実際のクエリが実行されるタミングなど

を確認しました。

なお、前述の FillStatistics2 メソッドに、前述の例のいずれかを入力して実行すると、[照会] ボタンを

クリックした際に、次のように表示されます。

図 1. 区分コード "S" のみの会員を抽出

・サンプルにみるクエリ操作と新しい匿名クラスの作成

前述の例では、クエリ結果を返す際に、「Select m」のように 1 行分のデータをまるごと返しました。

今度は、データを加工する例を確認しましょう。

Page 10: 第 3 部 SQL Server データ ゕクセス手法 (2)download.microsoft.com/download/4/D/9/.../Part3... · なりますが (第 2 部の「図4 linq の仕組み」を参照)、ここでは

10

この AWSC サンプル プログラムでは、[プログラム空き情報照会] フォーム (ProgramInfo.vb) では、

現在、特別レッスン (特別プログラム) の一覧を表示するドロップダウン リストでは、ADO.NET デー

タテーブルとの間で、データ バンデゖングを行って表示しています (次図)。

図 2. 特別プログラムの一覧を表示するドロップダウン リスト

このサンプル プログラムには、代替版として LINQ を使用し、このデータ ソースを提供する部分が

あります。AWLogic プロジェクトの BLComp1.vb にある、MakeProgramsFilterList メソッド、および

MakeProgramsFilterList_ByLINQtoSQL メソッドを確認してみてください。次に、これを示します。

例 6. ADO.NET vs. LINQ to SQL ~クエリ結果を取得し、新たに型を作成~

'(★★ADO.NET での基本的なデータテーブルの構築) 'プログラム検索の際のフゖルタで使用する項目のリストを作成 Public Function MakeProgramsFilterList() As DataTable ←① Dim table1 As New DataTable table1.Columns.Add("Caption", GetType(String)) ←② table1.Columns.Add("Expression", GetType(String)) table1.Rows.Add(New Object() {"すべて", ""}) ' Dim progTable As New AWSCDataSet.プログラムDataTable() Dim adapter As New AWSCDataSetTableAdapters.プログラムTableAdapter() adapter.Fill(progTable) ' For Each row As AWSCDataSet.プログラムRow In progTable ←③ table1.Rows.Add(New Object() { _ row.プログラム名, _ "プログラムコード ='" + row.プログラムコード + "'"}) Next Return table1 End Function '(★★代替版:LINQ to SQL) 'プログラム検索の際のフゖルタで使用する項目のリストを作成 Public Function MakeProgramsFilterList_ByLINQtoSQL() As IListSource ←④ Dim db As New AWSCDataContext() Dim query = From row In db.プログラム _ ←⑤ Select Caption = row.プログラム名, _ Expression = "プログラムコード ='" + row.プログラムコード + "'"

Page 11: 第 3 部 SQL Server データ ゕクセス手法 (2)download.microsoft.com/download/4/D/9/.../Part3... · なりますが (第 2 部の「図4 linq の仕組み」を参照)、ここでは

11

Return CType(query, IListSource) End Function

まず、LINQ が記述された ④のメソッドを確認します。このメソッドの目的は、Caption 列と

Expression 列から構成される、論理的な表形式データを作成して、呼び出し元に返すことです。呼び

出し元のフォームでは、このデータをドロップダウン リストに表示します。

⑤のクエリ式では、「プログラム」テーブルをもとに、新たなスキーマの行を生成しています。

Select 句は、次の書式になっています。

Select 列名 = 値, 列名 = 値, 列名 = 値, ...

このようにすると、指定された列名から構成された新しいスキーマを使用して、行の集合が作成され

ます。厳密にいうと、1 つの行データはオブジェクトであり、列名に指定されたプロパテゖを持つオ

ブジェクトです。よって、クエリ結果は、そのオブジェクトの集合になります。⑤の例では、

Caption プロパテゖと Expression プロパテゖを持つオブジェクトの集合になります。

この例では、Caption プロパテゖの値は、元の「プログラム名」列の値であり、Expression 列の値は、

"プログラムコード = '名前'" という文字列で表わされた式になります)。この Expression 列の値は、

フォーム側の DataView のフゖルタリングの条件として使用されます。

⑤において特筆すべき点は、列のデータ型は、設定される値の型を推論して、自動的に決定される点

です。そして行全体は、匿名型として、新たに定義されるのです。わざわざ、プログラマがスキーマ

を明示的に定義する必要はありません。また、このような匿名型を作る場合、文法的には、次のよう

に記述しても同じです。Caption や Expression の前にドットが付く点に注意してください。

Select New With { _ .Caption = row.プログラム名, _ .Expression = "プログラムコード ='" + row.プログラムコード + "'" }

このような暗黙的な型定義の仕組みがあることも、コードを簡潔にする上で貢献します。

①は、従来の ADO.NET を使用して、ドロップダウン リストのためにデータを構築しているメソッド

です。これは、ADO.NET 編のデータ バンデゖングの説明の中で、既に登場しましたコードです。

①のコードに比べ、④のほうが、簡潔であることが分かるでしょう。従来の ①では、②の部分でス

キーマを明示的に定義しています。その分、ソース コードの行数は増えます。また、③ではループ

を行いながら、行を追加しなければならない点でも、ソース コードの行数が増えます。

なお、①と④は、全く同じコードではありません。①では、データの 1 行目に「すべて」という値を

持つ行が追加されていますが、④はありません。(メソッド構文の Concat メソッドを使用すれば、連

結もできますが、ソースの型を合わせるなど、いくつかの工夫が必要です。) ただし、上記のコード

例から、LINQ では特定の状況下で、コード量が格段に減少することが実感できるでしょう。

註: 上記のメソッドの呼び出し部分は、AWWinApp プロジェクトの ProgramInfo.vb の中の、

ProgramInfo_Load です。現状では、①を呼び出すようになっていて、他はコメント ゕウトされ

ていますが、④の動作を確認したい場合は、必要に応じて、コメントを編集して、呼び出し先を

切り替えてください。

Page 12: 第 3 部 SQL Server データ ゕクセス手法 (2)download.microsoft.com/download/4/D/9/.../Part3... · なりますが (第 2 部の「図4 linq の仕組み」を参照)、ここでは

12

・DataContext の Log プロパティでの動作の追跡

ここで、このサンプルで DataContext がどんな SQL 文を発行するか追跡するため、ログを出力でき

るように、修正してみましょう。

ログを出力させる一番簡単な方法は、コンソール (コマンドプロンプト) の標準出力へ出力させるこ

とです。そのためには、このサンプル ゕプリケーションの起動時に、コンソールが表示するよう、

変更する必要があります。次の手順で変更してみてください。

1. ソリューション エクスプロ―ラー上で、AWWinApp プロジェクトを右クリックして、

[プロパテゖ] をクリックします。

2. プロジェクト デザナが開いたら、[ゕプリケーション] タブをクリックして表示を切り

換えて(次図参照)、ゕプリケーションの種類を「コンソール ゕプリケーション」に切り替

えます。

図 3. コンソール アプリケーションに切り替え

3. [フゔル] メニューの [すべてを保存] をクリックし、設定を保存しておきます。

これで、ゕプリケーションを起動すると、メン フォームのほかに、コンソールも開きます。

註: しばらく、この状態にします。元に戻す場合は、ゕプリケーションの種類として、

「Windows フォーム ゕプリケーション」を選んでください。

ログを出力するには、DataContext オブジェクトの Log プロパテゖに、次のように標準出力を指定し

ます。このあと、ログを出力したい場合は、必要に応じて、この指定をしてください。

Page 13: 第 3 部 SQL Server データ ゕクセス手法 (2)download.microsoft.com/download/4/D/9/.../Part3... · なりますが (第 2 部の「図4 linq の仕組み」を参照)、ここでは

13

例 7. ログの出力先の指定

Dim db As New AWSCDataContext() db.Log = System.Console.Out : (ここで DataContext に行った処理は、ログが出力される) :

試しに、この設定を例 6 のコードで行い、すると次のような結果が表示されます。SELECT 文が発行

されていることが分かります。

図 4. SQL Server に送信される SQL 文の追跡

必要に応じて、この方法でログを確認してみてください。

LINQ の特徴 ~ 標準クエリ演算子、遅延実行、ラムダ式 ~

次に、LINQ のクエリを使用する上で、構文や実行にまつわる留意点を、さらに補足しましょう。

・LINQ 対象のデータと標準クエリ演算子

LINQ において、クエリのソースとして利用できるデータ (つまり、In の後ろに記述できるデータ)は、

先に触れたように、IEnumerable(T) ンターフェスか、IQueryable(T) ンターフェスを実装した

オブジェクトです(または、その派生ンターフェス)。このオブジェクトは「シーケンス」とも呼

ばれます。

この 2 つのンターフェスの違いは、IQueryable(T) のほうは、このデータ ソース固有の LINQ プ

ロバダーを提供している場合です。IEnumerable(T) では、メモリー上のオブジェクトを操作するた

めに使用する、ラブラリ標準のものが使用されます。また、任意のオブジェクトに対して、独自の

LINQ プロバダーを利用したい場合、対象のオブジェクトに IQueryable(T) ンターフェスを実装

し、さらにプロバダーのンターフェスを実装したオブジェクトを用意する必要があります。

註: LINQ プロバダーの詳細は、次のゕドレスを参照してください。

http://msdn.microsoft.com/ja-jp/library/bb546158.aspx

チュートリゕル : IQueryable LINQ プロバダーの作成

Page 14: 第 3 部 SQL Server データ ゕクセス手法 (2)download.microsoft.com/download/4/D/9/.../Part3... · なりますが (第 2 部の「図4 linq の仕組み」を参照)、ここでは

14

なお、SQL Server にゕクセスする際に使用する LINQ to SQL では、DataContext のプロパテゖとして

参照できるテーブルが、この要件を満たしているので、改めて用意する必要はありません。

また、メソッド構文で使用するメソッド、すなわち、「標準クエリ演算子」を独自のデータ ソース

に対して使用するためには、先述のンターフェスを実装すれば、十分です。というのは、標準ク

エリ演算子は、IEnumerable(T) または IQueryable(T) を実装したオブジェクトに適用できる、「拡張

メソッド」として実装されていますかく。よって、オブジェクト自身に Where メソッドや Select メ

ソッドなどを実装する必要はありません。

これら 2 つのンターフェスに対する標準クエリ演算子は、それぞれ、Enumerable クラス、およ

び Queryable クラスに実装されています。クラス ラブラリ リフゔレンスを参照する際には、この

クラス名で調べることになります。

註: 標準クエリ演算子については、用途別に、以下のゕドレスにも記載があります。

http://msdn.microsoft.com/ja-jp/library/bb397896.aspx 標準クエリ演算子の概要

・即時実行と遅延実行

既に触れたように、クエリ式 (クエリ構文) を記述しただけでは、実際にクエリが実行されるとは限

りません。スカラー値を求めるクエリ式は、即時実行ですが、通常の結果セットを返すクエリ、つま

り、IEnumerable(T) や IQueryable(T) として結果が変える場合は、遅延実行になります。

LINQ to SQL のクエリでは、通常、For Each ループの開始の地点で実行されます。逆に言えば、クエ

リの元のデータとなる DataContext オブジェクトは、初期状態ではデータベースをロードしていない

ことになります。DataContext オブジェクトは、データ キャッシュとしての性格を持ちますが、この

データ キャッシュにデータがロードされるのは、プログラムからそのキャッシュに対して、ゕクセ

スを試みたときです。よって、DataContext オブジェクトにデータベースをロードしたい場合、必要

な部分にゕクセスすればよいのです。

なお、データベースのテーブルには、一対多などリレーションシップがあり、テーブル間で相互に関

係している場合があります。DataContext オブジェクトを介して、特定のテーブルにゕクセスした場

合、ゕクセス対処のテーブルのデータはロードされますが、リレーションシップで関連付いたデータ

は、既定ではロードされません。このようなデータのロードのタミングは、制御するオプションが

あります。詳しくは、「LINQ to SQL 向け O/R デザナと DataContext」の中の、「読み込みの最適

化」の項で扱います。

・ラムダ式の役割

例 4 のメソッド構文の Where メソッドの引数には、次のような構文がります。これを「ラムダ式」

と呼びます。

Function(m) m.区分コード = "S"

この式は、文法的には関数と等価であり、関数名が無い点を除けば、次と等価と言えます。(ここの

関数名 CheckKubun は仮の名前です。)

Page 15: 第 3 部 SQL Server データ ゕクセス手法 (2)download.microsoft.com/download/4/D/9/.../Part3... · なりますが (第 2 部の「図4 linq の仕組み」を参照)、ここでは

15

Function CheckKubun( m As 会員) As Boolean Return m.区分コード = "S"

End Function

引数の型や戻り値は、書かれた場所や関数の中の式から、コンパラが推論します。Where メソッ

ドの引数として、この関数に相当するラムダ式が渡されると、Where メソッドの中で、式が抽出条

件として利用されます。

このラムダ式は、抽出条件などの式を渡す際に、不可欠なものとなりマスタとえば、次のように式を

書いても、ラムダ式の代わりにはなりません。(仮に変数 m は定義済みとします。)

.Where( m.区分コード = "S" )

上記の例のような単なる条件式は、Where メソッドを呼び出す前に、式そのものが評価され、評価

結果として True か False が算出されます。そして、True または False が Where メソッドに渡るだけ

です。これでは、Where メソッドの中では、抽出処理のための評価関数として利用できません。

Where メソッドの引数として渡るべきものは、True か False という値ではなく、関数そのものです。

その関数そのものを渡すには、ラムダ式が必要なのです。

なお、コンパラは、ラムダ式を状況に応じて、2 種類の異なる形態に翻訳します。1 つは、文字通

り関数として実態を残す翻訳と、もう 1 つは、「式ツリー」と呼ばれる特殊なオブジェクトに翻訳す

る方法です。

式ツリーは、ラブラリに用意された Expression クラスのオブジェクトです。このオブジェクトは、

ラムダ式の構文要素 (左辺や右辺、演算子) などの情報を記憶するデータです。つまり、関数のよう

な手続きではく、単なる情報です。Where メソッドの引数は、この式ツリーを受け取ることが可能

なように定義されており、コンパラは、ラムダ式の条件部分を、構文要素に分解して、式ツリーに

展開します。その結果、実行時に Where メソッドが呼び出された際には、分解された構文の情報が、

データとして渡され、最終的には LINQ プロバダーが受け取ります。そして、LINQ プロバダー

は、データ ソースにゕクセスする上で必要な、SQL 文の WHERE 句など、データ ソース独自の構文

に変換するのです。この仕組みがあるお陰で、ソース プログラム上は Visual Basic や C# で書かれた

条件式が、適切に SQL 文の条件式に展開されるのです。

結合とグループ化の例

クエリ式について、深く立ち入ると切りが無くなりますが、ここで AWSC サンプル プログラムの中

で使用されているクエリ式を題材に、クエリ式を読解するコツを説明しましょう。

AWSC サンプル プログラムを実行して、メン フォームの [開催別 予約集計 2] ボタンをクッリクす

ると、[開催別 予約集計 2] フォームが開きます。このとき、[照会] ボタンをクリックすると、次図の

ように、会員別の予約実績が表示されます。(図のように、照会すべき期間を既定値にすれば、この

ように表示されるはずです。)

Page 16: 第 3 部 SQL Server データ ゕクセス手法 (2)download.microsoft.com/download/4/D/9/.../Part3... · なりますが (第 2 部の「図4 linq の仕組み」を参照)、ここでは

16

図 5. 会員別の予約明細

この集計を行っているクエリは、AWDac プロジェクトの DAHelper2.vb の中にある FillStatistics2 メ

ソッドです。以下に示します。(このメソッドには、テスト用のプレースホルダーのコード ブロック

がありますが、その部分は省略します。)

例 8. クエリ式を使用した会員別の予約集計

Public Sub FillStatistics2(ByVal list As IList, _ ByVal dt1 As Date, ByVal dt2 As Date) : (省略) Dim db As New AWSCDataContext() Dim query1 = _ From r In db.予約 _ ←① Join s In db.開催予定 On r.開催コード Equals s.開催コード _ Join p In db.プログラム On s.プログラムコード Equals p.プログラムコード _ Join t In db.時間帯 On s.時間帯コード Equals t.時間帯コード _ Join o In db.オプション On r.オプションコード Equals o.オプションコード _ Order By r.会員コード _ Where s.日付 >= dt1 AndAlso s.日付 <= dt2 _ Select r.会員コード, _ ←② s.日付, t.説明, p.プログラム名, o.オプション名, r.合計 _ Group By 会員コード _ ←③ Into 回数 = Count(), 個人計 = Sum(合計), Details = Group _ Join m In db.会員 On 会員コード Equals m.会員コード _ Select 会員コード, 氏名 = m.姓 + " " + m.名, 回数, 個人計, Details ←④ For Each item In query1 ←⑤ list.Add(String.Format( _ "{0,3} {1} 回数:{2:#,##0} 個人計:{3:#,##0}", _ item.会員コード, item.氏名, item.回数, item.個人計)) For Each dt In item.Details ←⑥ list.Add(String.Format( _ " {0:yyyy/MM/dd} {1} {2} ({3})", _ dt.日付, dt.説明, dt.プログラム名, dt.オプション名)) Next

Page 17: 第 3 部 SQL Server データ ゕクセス手法 (2)download.microsoft.com/download/4/D/9/.../Part3... · なりますが (第 2 部の「図4 linq の仕組み」を参照)、ここでは

17

list.Add("") Next db.Dispose() End Sub

①から④までが、1 つのクエリ式です。非常に複雑にも見えますが、見方のコツをつかむと、比較的

分かりやすくなります。詳細はあまり気にせずに、全体的な見方を確認しましょう。

クエリ式にもバリエーションはありますが、基本的に、先頭の From ... In ... では、加工する元のデー

タが決まります。ここでは、先頭の In の後ろの「予約テーブル」です。

この予約テーブルのあとに、Join 句や、Order By 句や、Where 句が続き、徐々に加工されていきま

す。2 行目から 5 行目までは、Join ... In ... が続き、どんどんテーブルが結合していることが分かりま

す。Join 句のそれぞれの In の後ろに書かれたテーブルが、結合するテーブルです。

ここまで複数のテーブルが結合すると、全体像が見えづらくなりますが、②から始まる Select 句が

マルストーンになります。Select 句は、それよりも前に書かれたデータ操作のクエリ結果を整形す

る作用があり、②の Select 句 (2 行) の記述によって、以下の列からなる表形式になります。

会員コード, 日付, 説明, プログラム名, オプション名, 合計 ←(A)

さらに、③の Group By 会員コード Into ... で、会員コードをキーに、会員コードごとに 1 つの集計行

にまとめられます。Group By 句については、Group By ... Into ... までを、1 つのまとまりと考えてく

ださい。

③では、会員コードがキーなので、集計結果には会員コードの列を必ず含みます。また、その他の列

は、Into の後ろに定義されており、「列名 = 値」形式になっています。結果的に、集計されて次の

構造になます。

会員コード, 回数, 個人計, Details ←(B)

この時点で、集計キーである「会員コード」列はユニークな値を持ち、各行は、会員ごとの集計行に

なります。

ここで、Details 列の内容が LINQ ならではのポントになります。③の集計では Into の後ろに

「Details = Group」とあります。Details は任意の列名ですが、「Group」は予約語です。この

「Group」は、集計された明細テーブルを表します。つまり、③の集計結果である前述の (B) におい

て、会員コードが 「10」という値の集計行では、Details 列には、会員コード「10」の人の明細テー

ブルが入っています。なお、明細テーブルの形式は、集計の元になった (A)です。

つまり、集計後の (B) のテーブルの Details 列の中に、入れ子になって明細テーブルが入っているの

です。これは、従来のリレーショナル データベースでは表現できない構造でした。これを図に示す

と、次図のようになります。

Page 18: 第 3 部 SQL Server データ ゕクセス手法 (2)download.microsoft.com/download/4/D/9/.../Part3... · なりますが (第 2 部の「図4 linq の仕組み」を参照)、ここでは

18

図 6. LINQ での表形式の入れ子

このような入れ子構造は、オブジェクト指向プログラミングでは、ごく一般的に使用されているもの

です。LINQ を使用することで、表形式のリレーショナル データが、オブジェクト指向プログラミン

グに馴染みやすい形式に変換できます。

そして、④の Select 句の段階では、上記の表の集計行の列の並びが次のようになります。

会員コード, 氏名, 回数, 個人計, Details

最後に、クエリ結果を表示する⑤と⑥のループも、入れ子になっています。⑤の外側のループでは、

集計行にある、会員コード、指名、回数、個人計を出力し、⑥の内側のループでは、Details 列に格

納された明細テーブルを出力しています。

会員コード 回数 個人計 Details

: : : :

10 2 1000

11 3 1800

15 2 1500

: : : :

会員コード10 番の明細テーブル

会員コード11番の明細テーブル

会員コード15番の明細テーブル

Page 19: 第 3 部 SQL Server データ ゕクセス手法 (2)download.microsoft.com/download/4/D/9/.../Part3... · なりますが (第 2 部の「図4 linq の仕組み」を参照)、ここでは

19

LINQ to SQL 向け O/R デザイナと DataContext

この節では、LINQ to SQL のテータ操作で使用する DataContext と、その DataContext を編集するた

めのデザン ツールもある O/R デザナについて取り上げます。O/R デザナの基本的な特徴のほ

か、O/R デザナで設定可能な「関連付け」や「継承」に関する機能も取り上げます。

O/R デザイナの基本操作

AWSC サンプル プログラムにおいて、前節で取り上げたクエリ構文などでは、データ ソースとして

AWSCDataContext というクラスの、DataContext オブジェクトを使用しました。DataContext とは、

SQL Server データベース内のリレーショナル データと、メモリー上のオブジェクトとのマッピング

を管理するためのものです。このマッピングをビジュゕルな環境で行うことができるのが、Visual

Studio 2008 が提供する O/R デザナ (オブジェクト リレーショナル デザナ) です。今回のサンプ

ルの AWSCDataContext も、O/R デザナを使用して作成しました。

まず、既存のサンプルで、デザナの全体的な構成を確認してみます。ソリューション エスクプロ

ーラ上で、AWDac プロジェクトに含まれる、AWSC.dbml というフゔルをダブル クリックして開

いてください。次図のように、O/R デザナが開きます (新規に作成する方法は、後ほど行います)。

O/R デザナの編集対象となるフゔルは、拡張子 .dbml のフゔルです。

図 7. O/R デザイナでの AWSCDataContext のデザイン

まず、このデザン画面ひとつ分が、AWSCDataContext クラスに相当します。クラス名は、このデ

ザナを開いた状態で、プロパテゖ ウゖンドウの Name プロパテゖの値として設定できます。

デザナの中の、「予約」や「会員」というタトルの付いた四角形が、リレーショナル データと

マップされるオブジェクトの定義に当たります。つまり、クラスです。「予約」や「会員」という名

Page 20: 第 3 部 SQL Server データ ゕクセス手法 (2)download.microsoft.com/download/4/D/9/.../Part3... · なりますが (第 2 部の「図4 linq の仕組み」を参照)、ここでは

20

前は、ソース コード上のクラス名に反映されます。また同時に、AWSCDataContext のプロパテゖに

なります。よって、前節のサンプルでも「db.会員」のように参照することができました。

各クラスのオブジェクトは、1 つ分のテーブルにマップされます。デザナ上で、会員クラスを選択

して、プロパテゖ ウゖンドウを確認すると、次のように会員テーブルにマップされていることが分

かります。

図 8 会員テーブルとのマップ

デザナ上の各クラスは、ツール ボックスからドロップするか、データベースに接続したサーバー

エクスプロ―ラーから、必要なテーブルをドロップすることで追加します。また、ツール ボックス

にあるように、クラス間の「関連付け」や「継承」も設定できます(後述)。

ここで新規に作成してみます。このあと、データベースを参照する簡単なゕプリケーションを作るの

で、できれば作成してみてください。

1. まず、Visua Studio でプロジェクトの新規作成します ([フゔル]メニューの [新規作成]、

[プロジェクト])。プロジェクトの作成には、次の設定を使用してください。

プロジェクトの種類: Visual Basic

テンプレート: Windows フォーム ゕプリケーション

プロジェクト名とパス: (任意、この例では TestLtoSQL)

2. 次に作成が済んだら、O/R デザナ用のフゔルを追加します。[プロジェクト] メニュー

の [新しい項目の追加] をクリックします。[新しい項目の追加] ダゕログボックスが開

いたら、次図のように「LINQ to SQL クラス」をクリックして、フゔル名は

「AWSC.dbml」と指定し、[追加]をクリックします。

Page 21: 第 3 部 SQL Server データ ゕクセス手法 (2)download.microsoft.com/download/4/D/9/.../Part3... · なりますが (第 2 部の「図4 linq の仕組み」を参照)、ここでは

21

図 9 LINQ to SQL クラスの追加

3. 空の O/R デザナの画面が開いた後、サーバー エスクプローラを開き、AWSC データベ

ースへの接続があるか確認し、次のように、 [テーブル] フォルダを展開します。

図 10 サーバー エスクプローラ

註: サーバー エクスプロ―ラーが見つからない場合は、[表示] メニューの [サーバー エクスプロ

―ラー] をクリックすれば、表示できます。

Page 22: 第 3 部 SQL Server データ ゕクセス手法 (2)download.microsoft.com/download/4/D/9/.../Part3... · なりますが (第 2 部の「図4 linq の仕組み」を参照)、ここでは

22

註: サーバー エクスプロ―ラー上に、テータベースへの接続が見つからない場合は、ツリー上の

[データ接続] ノードを右クリックして、[接続の追加] をクリックしてください。そのあとは、対

話形式で、SQL Server に接続することができます。今回のサンプルで使用しているンスタンス

の AWSC データベースに接続してください。

4. 次に、サーバー エクスプロ―ラー上の「プログラム」テーブルと、「開催予定」テーブ

ルを、それぞれドラッグして、デザナ上にドロップします。次のように表示されるこ

とを確認します。

図 11 関連付けされた 2 つのクラス (テーブル)

5. 念のため、[フゔル] メニューの [すべてを保存] をクリックして保存しておきます。

これで、「プログラム」テーブルと「開催予定」テーブルにマップされた 2 つのクラス定義ができま

した。また同時に、AWSCDataContext のプロパテゖになっています。

なお、2 つのクラスの間は、矢印で結びつけられ、「関連付け」がされています。もともと、データ

ベース上の 2 つのテーブルには、1 対多のリレーションシップが設定されていたので、デザナ上に

も既定で反映されました。次に、この関連付けの意味について確認します。

関連付け (リレーションシップ) の利用

関連付けがあると、2 つのテーブルのオブジェクトの一方のデータから、対応するもう一方のデータ

にたどりつくことができます。ここで、プログラムで確認してみましょう。

ここで、このあとの実験のために、フォームにいつくか、コントロールを貼り付けておきます。

Page 23: 第 3 部 SQL Server データ ゕクセス手法 (2)download.microsoft.com/download/4/D/9/.../Part3... · なりますが (第 2 部の「図4 linq の仕組み」を参照)、ここでは

23

6. 次のフォームと同じになるように、ボタン 3 個、リスト ボックス 1 個、そして、テキス

ト ボックス 1 個を、フォーム Form1.vb に貼り付けます。

図 12 実験用のフォーム

7. ここで、フォームデザナ上の [Button1] をダブル クリックして、ベント ハンドラ

Button1_Click を生成させておきます。

8. この TestLtoSQL プロジェクトについて、図 3 と同様のプロジェクトデザナを開きます。

(ソリューション エクスプロ―ラー上の TestLtoSQL プロジェクトを右クリックして、[プ

ロパテゖ] をクリックします。)

9. 図 3 と同様に、「コンソール ゕプリケーション」に切り替えておきます。(DataContext

のログをコンソールに出力するためです。)

10. 念のため、ここまでの変更を保存しておきます。

既に触れたように DataContext は、データベース内のテーブルなどにマッピングされたオブジェクト

を提供します。具体的には、DataContext オブジェクトのプロパテゖとしてゕクセスできます。

実は、クエリ構文やメソッド構文などを使用しなくとも、単に DataContext のオブジェクトにゕクセ

スするだけで、データベースにゕクセスできるのです。よって、複雑な集計でなければ、プロパテゖ

にゕクセスする手段のほうが簡単です。

また、上記の「プログラム」テーブルの場合、「開催予定」テーブルに対して、一対多の関連付けが

あるので、プログラム テーブルのプロパテゖには、対応する開催テーブルの明細を参照するプロパ

テゖが追加されます。

具体例で確認しましょう。

Page 24: 第 3 部 SQL Server データ ゕクセス手法 (2)download.microsoft.com/download/4/D/9/.../Part3... · なりますが (第 2 部の「図4 linq の仕組み」を参照)、ここでは

24

さきほど生成した、Button1_Click ベント ハンドラ に、次のコードを入力してください。このコー

ドは、マスタ/詳細の関係にある、プログラム テーブルと開催予約テーブルについて、プログラムご

との開催の詳細情報を表示するものです。

例 9. クエリ式を使用した会員別の予約集計

Private Sub Button1_Click(ByVal sender As System.Object, ... Dim db As New AWSCDataContext() ←① db.Log = System.Console.Out For Each p In db.プログラム ←② ListBox1.Items.Add( _ p.プログラム名 & " " & CStr(p.開催予定.Count) & "件") ←③ For Each s In p.開催予定 ←④ ListBox1.Items.Add(" " & s.開催コード & " " & s.日付) Next Next End Sub

このコードには、クエリ式が使われていない点に注意してください。①のように、DataContext オブ

ジェクトを作成した後、②のように、直接的にプログラム テーブルを参照しています。DataContext

オブジェクトを使用すると、このようにオブジェクトを介して、直接的にゕクセスできます。

ここでは、入れ子のループになっており、②の外側のループでは、マスタ テーブルであるプログラ

ム テーブルのデータについて、1 件ごとにループします。1 件のデータである変数 p は、③のように

「プログラム名」列が参照できます。

特筆すべき点は、同じ③にある「p.開催予定」の部分です。これは、1 件のプログラム データである

p の「開催予定」プロパテゖです。しかし、単なる列ではなく、プログラム データ p に対応する開

催予定テーブルの明細です。よって、「p.開催予定.Count」は、明細の件数を求めるものです。

さらに、④では、明細である「p.開催明細」について、入れ子の内側のループを使って、1 つずつリ

ストゕップしています。

本来、このような一対多のテーブルの対応付けを参照するには、JOIN などの結合操作が必要でした

が、DataContext を使用すれば、プロパテゖを参照するだけで、関連付けされたデータにたどりつけ

るのです。

それでは、実行してみましょう。起動した後、[Button1] ボタンをクリックすると、次のように、マ

スタデータであるプログラムごとに、開催の明細日時が表示されます。

Page 25: 第 3 部 SQL Server データ ゕクセス手法 (2)download.microsoft.com/download/4/D/9/.../Part3... · なりますが (第 2 部の「図4 linq の仕組み」を参照)、ここでは

25

図 13 マスタ/詳細の集計結果

また、コンソールに出力したログを確認してみましょう。

図 14 詳細を複数回取得

複数回、SELECT 文が実行されています。確かに、これはコードに記述した通りの動作です。

DataContext の既定の動作は、DataContext にゕクセスを試みたデータだけがロードされるので、詳

細の取得は、プログラム上の外側のループ毎に発生します。関連付けがあったとしても、マスタを参

照しただけでは、明細は読み込まれず、明細が参照されて初めてその部分が読み込まれます。

マスタ全件取得

マスタ 1 件目の詳細

マスタ 2 件目の詳細

マスタ 3 件目の詳細

Page 26: 第 3 部 SQL Server データ ゕクセス手法 (2)download.microsoft.com/download/4/D/9/.../Part3... · なりますが (第 2 部の「図4 linq の仕組み」を参照)、ここでは

26

しかし、これは最適化の余地があります。今回の処理では、結局のところ、明細データは全件参照し

ています。ということは、最初から、マスタと対応付けて、一度に詳細の全件を取得したほうが、サ

ーバーとのやり取り回数を削減できます。DataContext には、関連付けのあるデータを、予めまとめ

て読み込むオプションが用意されています。

次に、この方法について確認します。

読み込みの最適化

ここで、前述の例 9 のコードのループの前に、次のように追加してください(黄色の部分)。

例 10. 関連するテーブルを読み込むよう指定する

Private Sub Button1_Click(ByVal sender As System.Object, ... Dim db As New AWSCDataContext() db.Log = System.Console.Out Dim opt As New System.Data.Linq.DataLoadOptions() ←① opt.LoadWith(Function(p As プログラム) p.開催予定) ←② db.LoadOptions = opt ←③ For Each p In db.プログラム

ポントは、③の LoadOptions プロパテゖです。このプロパテゖに、①から②で用意した

DataLoadOptions ンスタンスを設定します。このンスタンスには、読み込む際のオプション動作

を設定でき、②のようにラムダ式で指定します。②では、読み込むテーブルが「プログラム」ならば、

それに紐付いた明細である「開催予定」も読み込むという指示です。

この状態で実行すると、ログが次図のように変化します。

図 15 クエリを 1 回だけ実行

DataContext の既定構成では、その時点でゕクセスしたところだけしか読み込みませんが、このオプ

ションを使用すると、関連付いたものも、同時に読み込ますことができます。

Page 27: 第 3 部 SQL Server データ ゕクセス手法 (2)download.microsoft.com/download/4/D/9/.../Part3... · なりますが (第 2 部の「図4 linq の仕組み」を参照)、ここでは

27

継承の利用

次に、O/R デザナでの継承ついて確認します。リレーショナル データをオブジェクトにマップす

る際に、オブジェクト側で、オブジェクト間で(正確にはクラス間で)、継承を実現することができま

す。継承を適切に使用すれば、本来のオブジェクト指向プログラミングの手法に沿ったコーデゖング

を行うことができます。

継承を構成するクラスに対して、リレーショナル データをマップする方法は 2 つあります。

1 つは、単一のテーブルを、基底クラスと派生クラスにマップする「Table-Per-Hierarchy」です。も

う 1 つは、複数のテーブルを、基底クラスと派生クラスにマップする「Table-Per-Type」です。

LINQ to SQL の O/R マップでは、前者のみをサポートします。一方、ADO.NET Entity Framework で

は両方をサポートします。

この LINQ 編では、前者の実装方法を確認し、後者は Entity Framework 編で確認します。そして、後

者を確認した時点で、2 つを比較することにします。

AWSC サンプル プログラムは、後者の「Table-Per-Type」を前提にマッピングを行っているので、こ

こでは、前項で実験に使用したゕプリケーションに、引き続き作り込んでいきます。

・Table-Per-Hierarchy 形式の継承

リレーショナル データでは、継承を表現する専用の方法はありませんが、既存の構造の中で、それ

を表現することもできます。サンプルを例に考えましょう。ここでは、サンプルのスポーツ クラブ

において、会員テーブルのデータに対して、次のように運用していたとします。

まず、会員には、Visitor、Silver、Gold の区分があります。このうち、Silver と Gold 会員は、正規会

員であり、入会日やプロフゖール、生年月日の情報が必要です。一方で、Visitor は正規会員でないの

で、これらの情報を管理する必要がありません。

結局のところ、3 つの区分で共通して使用する情報は、残りの部分である、会員コード、姓、名、住

所、および区分です。これらの共通情報は、Visitor、Silver、Gold のいずれのクラブ利用者でも必要

となる情報です。

これを継承の観点から表現すると、共通部分の情報は利用者としての基本情報であり、入会日やプロ

フゖール、生年月日を追加して拡張すると、Silver や Gold 会員の情報になります。

よって、3 区分共通の「利用者」クラスを基底クラス(基本クラス) にして、それから派生する形で、

「Silver 会員」や 「Gold 会員」のクラスを用意することにします。(ここでは、Silver と Gold のデー

タ構造が同じですが、実行するロジックが異なるという前提で、クラスを分けることにします。)

・O/R デザイナでの継承表現

では、前項で試験用に作成した AWSCDataContext (AWSC.dbml) に追加してみましょう。

1. サーバー エクスプロ―ラーから、会員テーブルを O/R デザナ上にドロップします。

2. ツール ボックスから、2 回、クラスを O/R デザナ上にドロップします。

Page 28: 第 3 部 SQL Server データ ゕクセス手法 (2)download.microsoft.com/download/4/D/9/.../Part3... · なりますが (第 2 部の「図4 linq の仕組み」を参照)、ここでは

28

3. 追加した 3 個のクラスについて、それぞれ、名前の部分をクリックして編集モードにし

て、次のように名前を変更します。(次図参照)

会員 → 利用者

Class1 → Gold 会員

Class2 → Silver 会員

図 16 3 個のクラスを追加する

ここで、利用者クラスの内部には、共通部分の列だけを残し、その他は、Gold 会員クラスと、Silver

会員クラスに移行することにします。列はコピー ペーストできます。一旦、列を削除してしまうと、

最初から列を作るのは手間がかかるので、削除は慎重に行いましょう。

4. 利用者クラスにある「生年月日」、「プロフゖール」、および「入会日」を、それぞれ

コピーとペーストを繰り返し、Gold 会員クラスと、Silver 会員クラスに追加します。

5. 利用者クラスから、「生年月日」、「プロフゖール」、および「入会日」を削除します。

6. ツール ボックスの「継承」をクリックし、Gold 会員から利用者へ、線を引くようにマウ

スポンタをドラッグします。

7. 同様に、ツール ボックスの「継承」をクリックし、Silver 会員から利用者へ、線を引くよ

うにマウス ポンタをドラッグします。

8. 次のように、継承の線が引かれることを確認します。

Page 29: 第 3 部 SQL Server データ ゕクセス手法 (2)download.microsoft.com/download/4/D/9/.../Part3... · なりますが (第 2 部の「図4 linq の仕組み」を参照)、ここでは

29

図 17 継承関係のクラス

最後に、継承関係のプロパテゖを設定する必要があります。

デザナ上のすべての列は、もともと、会員テーブルにマップしたものをコピーしたので、この 3 つ

のクラスすべては、会員テーブルにマップされています。そのため、会員テーブルの 1 件ごとのデー

タが、どのクラスに分類されるか指定する必要があります。分類の識別には、区分コードを用いるこ

とにします。よって、次のように設定します。

9. Gold 会員から両者に向けて伸びる継承の線をクリックして選択します。プロパテゖウゖ

ドウで、次のプロパテゖを設定します。

継承の既定値: 利用者

基本クラスの識別子の値: V

識別プロパテゖ: 区分コード

派生クラスの識別子の値: G

10. 同様に、Silver 会員から両者に向けて伸びる継承の線をクリックして選択します。プロパ

テゖウゖドウで、次のプロパテゖを設定します

継承の既定値: 利用者

基本クラスの識別子の値: V

Page 30: 第 3 部 SQL Server データ ゕクセス手法 (2)download.microsoft.com/download/4/D/9/.../Part3... · なりますが (第 2 部の「図4 linq の仕組み」を参照)、ここでは

30

識別プロパテゖ: 区分コード

派生クラスの識別子の値: S

11. ここまでの設定を念のため保存しておきます。

これで、区分コードに応じて、各会員データは、3 つのうちのいずれかのクラスにマップされます。

・O/R マップされたオブジェクトで多態性を実現

最後に、オブジェクト指向らしいコードを、各クラスに追加しましょう。各クラスとも、ボーナス

ポントを計算するメソッドを追加する必要があるとします。このとき、基底クラスである「利用者」

にオーバーラド可能な CalcBonus メソッドを定義します。そして、それをオーバーラドする形式

で、派生クラス Gold 会員と Silver 会員には、独自のロジックを実装する CalcBonus メソッドを記述

することにします。

この O/R デザナのクラスには、プログラマが任意のロジックを追加することができます。

1. O/R デザナ上で、利用者クラスのタトルを右クリックして、[コードの表示] をクッリ

クします。すると、AWSC..vb が開き、利用者クラスのパーシャルクラスが生成されます。

註: この利用者のパーシャル クラスは、デザナが生成した非表示フゔルにある利用者クラス

にマージされます。結果として、O/R マップされた利用者クラスに、独自のロジックを追加する

ことができます。

2. 同様に、O/R デザナ上で、Gold 会員クラスのタトルを右クリックして、[コードの表

示] をクッリクします。すると、AWSC..vb が開き、Gold 会員クラスのパーシャルクラス

が生成されます。

3. 同様に、O/R デザナ上で、Silver 会員クラスのタトルを右クリックして、[コードの

表示] をクッリクします。すると、AWSC..vb が開き、Siver 会員クラスのパーシャル クラ

スが生成されます。

4. このソース コードに、次のように 3 つの CalcBonus メソッドを追加します。クラスによ

って、ボーナス算出のロジックを変えます。また、基底クラスである利用者の CalcBonus

メソッドには、Overridable 修飾子が付き、他では Overrides 修飾子が付きます。

例 11. 関連するテーブルを読み込むよう指定する

Partial Class Silver 会員 Public Overrides Function CaclBonus() As Integer Return 200 End Function End Class Partial Class Gold 会員 Public Overrides Function CaclBonus() As Integer Return 300

Page 31: 第 3 部 SQL Server データ ゕクセス手法 (2)download.microsoft.com/download/4/D/9/.../Part3... · なりますが (第 2 部の「図4 linq の仕組み」を参照)、ここでは

31

End Function End Class Partial Class 利用者 Public Overridable Function CaclBonus() As Integer Return 100 End Function End Class

最後にこれを利用するコードをフォームに記述します。フォームデザナ上で、[Button2] ボタンを

ダブル クリックして、Button2_Click ベント ハンドラ を追加し、次のコードを入力してください。

例 12. CalcBonus メソッドを呼び出して多態性を実現

Private Sub Button2_Click(ByVal sender As System.Object, ... Dim db As New AWSCDataContext() For Each usr In db.利用者 ←① ListBox1.Items.Add( _ usr.区分コード & " " & _ usr.姓 & " " & usr.名 & " " & usr.CaclBonus()) ←② Next End Sub

①のループでは、「db.利用者」というように、利用者に対してゕクセスしています。利用者は継承

元のクラスであり、Visitor も、Silver も、Gold も利用者であることには違いないので、「db.利用者」

は利用者全員を意味します。(オブジェクト指向の Is-a の関係が成立します。)

しかし、実際のンスタンスのクラスは区分によって異なります。よって、②の「usr.CalcBonus()」

という呼び出しは、ンスタンスに応じて、オーバーラドされた異なるメソッドが実行され、それ

ぞれのンスタンスにふさわしい結果を取得できます。

これを実行すると次図のようになります。ボーナス ポントが、人によって異なることが分かりま

す。

Page 32: 第 3 部 SQL Server データ ゕクセス手法 (2)download.microsoft.com/download/4/D/9/.../Part3... · なりますが (第 2 部の「図4 linq の仕組み」を参照)、ここでは

32

図 18 異なるボーナス ポイントを表示

以上、O/R のマップしたオブジェクトを、オブジェクト指向らしい使い方ができるように、O/R デザ

ナ上で継承関係を設定する方法を確認しました。

Page 33: 第 3 部 SQL Server データ ゕクセス手法 (2)download.microsoft.com/download/4/D/9/.../Part3... · なりますが (第 2 部の「図4 linq の仕組み」を参照)、ここでは

33

LINQ to SQL の更新とトランザクション

この節では、LINQ to SQL の機能の中で、SQL Server データベースの更新に係る機能を取り上げます。

既に触れたように、LINQ to SQL を使用してデータベースを更新するには、DataContext を使用しま

す。ここでは、この DataContext を用いた更新方法について改めて確認します。

特に、AWSC サンプル プログラムには、トランザクション環境下で DataContext を使用した更新処

理が含まれます。この更新処理を題材に使います。

DataContext による更新

AWDac プロジェクトの DAHelper1.vb の中にある、Reserve_ByLINQtoSQL メソッドを確認してくだ

さい。ここには、LINQ to SQL 版の予約登録を行う処理が書かれています。この予約登録では、今ま

でと同様に、空席数を管理する「開催予定テーブル」から、予約対処となる特別プログラムの空席数

を 1 つ減算し、さらに同時に、「予約テーブル」に予約情報を新規に追加します。次にそのコードを

示します。(1 部、コメントを削除しています。)

例 13. LINQ to SQL による更新処理

Public Function Reserve_ByLINQtoSQL( _ ByVal memid As Integer, _ ByVal sch As AWSCDataSet.開催結合情報Row, _ ByVal opt As AWSCDataSet.オプションRow, _ ByRef rid As Integer, _ ByRef stat As DAStatus) As Boolean Dim result As Boolean = False Try Using ts = New TransactionScope() ←① Dim db As New AWSCDataContext() '当該開催スケジュールの空席確認 Dim select1 = From p In db.開催予定 _ Where p.開催コード = sch.開催コード Select p ←② Dim plan = select1.First() '1行分のデータが存在すことが前提 ←③ If plan.空席 <= 0 Then ←④ stat = DAStatus.NoRoom Exit Try 'ts.Complete を呼び出さずに処理を中断するのでロールバック End If ←⑤ '当該開催スケジュールの空席減算 plan.空席 -= 1 ←⑥ '予約データの新規挿入 Dim rsv As New 予約 With { _ ←⑦ .会員コード = memid, _ .開催コード = sch.開催コード, _ .適用価格 = sch.プログラム料金, _ .消費税 = sch.消費税, _ .オプションコード = opt.オプションコード, _

Page 34: 第 3 部 SQL Server データ ゕクセス手法 (2)download.microsoft.com/download/4/D/9/.../Part3... · なりますが (第 2 部の「図4 linq の仕組み」を参照)、ここでは

34

.オプション適用価格 = opt.オプション料金, _ .オプション消費税 = opt.消費税, _ .合計 = sch.合計 + opt.合計, _ .予約受付 = Date.Now _ } db.予約.InsertOnSubmit(rsv) ←⑧ db.SubmitChanges() ←⑨ '新規に割り振った予約番号を取得 rid = rsv.予約コード ←⑩ '正常終了に投票(Vote) ts.Complete() ←⑪ End Using 'トランザクション スコープを終了することで処理が確定 stat = DAStatus.Success result = True Catch ex As Exception ←⑫ stat = DAStatus.AccessFailed End Try ←⑬ '戻り値 Return result End Function

①のトランザクションや同時実行制御の対応については説明を保留にして、ここではデータ操作の手

順そのものに着目します。

この処理では、空席数を減算する前に、念のため最新の空席数を取得し、空席数が正の数であること

を確認しています。しかし、 元々の ADO.NET の予約処理 (Reserve メソッド、Reserve_ByTranScope

メソッド) の場合の、空席数の確認方法と異なります。

元々の ADO.NET の例では、次のようにスカラー値として、「空席」列を確認するだけでした。

SELECT 空席 FROM 開催予定 WHERE 開催コード = @sid

しかし、DataContext を使用した上記の例では、該当する空席数を持つ行を、1 件分まるごと読み込

みます。③の First メソッドは、②のクエリ式で取得するクエリ結果から、先頭行を丸ごと取得して

います。

このように行ごと習得する理由は、そのあとに続く空席数の「更新処理」のためです。DataContext

を用いて、特定の行を更新するには、あらかじめ当該データ(当該行)をまるごと読み込む必要があり

ます。つまり、更新対象のデータを、メモリー上の相応のオブジェクトにマップする必要があるので

す。②と③は、そのためのマップ処理を兼ねていたのです。

ここでは、③のところで、更新対象の行は変数 plan に設定されます。その直後、④で空席数が残っ

ていること (正の数であること) を確認した後、⑥でメモリー キャッシュのデータに対して、空席数

を減算しています。そして、実際にデータベースの更新は、後ほど⑨の SubmitChanges メソッドの

呼び出しによって、他の更新と合わせて、まとめて行うことになります。

Page 35: 第 3 部 SQL Server データ ゕクセス手法 (2)download.microsoft.com/download/4/D/9/.../Part3... · なりますが (第 2 部の「図4 linq の仕組み」を参照)、ここでは

35

このように DataContext を用いた更新では、一旦、対象データを読み込んで (オブジェクトにマッピ

ングし)、メモリー上のオブジェクトに対して修正し、最後に SubmitChanges メソッド呼び出しによ

って、データベースを更新するという手順を取ります。

新規挿入の場合も同様です。まず⑦では、予約データのオブジェクトをメモリー上に新規作成します。

⑧で InsertOnSubmit メソッドを使用して、メモリー上の予約テーブル相当の「db.予約」プロパテゖ

に追加し、そして⑨の SubmitChanges メソッドの呼び出しで、データベースに反映します。

また、今回の例でも、新規挿入とそれに伴って割り振られた ID 値の取得を行っています。⑦では、

新規予約データにあたる予約オブジェクトを生成する際に、ID に当たる「予約コード」を設定して

いません。そして、⑨の SubmitChanges メソッドの呼び出しによって新規挿入されると、割り振ら

れた ID 値 (予約コード) は、メモリー上のオブジェクトにも反映されます。そして、⑩のところで、

その ID 値 (予約コード) を参照しています。

註: この例では、更新 (UPDATE) と 挿入 (INSERT) を取り上げました。削除 (DELETE) についても、

更新と類似しており、一旦、削除対象をメモリーに読み込んだ後、DeleteOnSubmit メソッドを

呼び出します。詳しくは、次のゕドレスを参照してください。

http://msdn.microsoft.com/ja-jp/library/bb386925.aspx

方法 : 行をデータベースから削除する (LINQ to SQL)

また、DataContext による全般的な操作は、以下のゕドレスを起点とする、一連のリンクを参照

してください。

http://msdn.microsoft.com/ja-jp/library/bb399408.aspx プログラミング ガド (LINQ to SQL)

DataContext と TransactionScope を使用した同時実行制御

前述の例 13 の更新処理において、トランザクションの観点から確認します。

DataContext でも、ローカルトランザクションと TransactionScope をサポートしています。第 2 部

でも触れたように、ローカルトランザクションを使用するには、DataContext の Transaction プロパ

テゖに、SqlTransaction オブジェクトを設定すればよいのです。その点を除けば、ADO.NET 編で取り

上げた SqlTransaction オブジェクトの制御方法と同じです。あとは、Commit メソッド、または

Rollback メソッドを呼び出すだけです。ここでは、より簡潔に表現できる TransactionScope を使用

することにします。実際のコードで確認してみましょう。

例 13 の①では、TransactionScope オブジェクトを作成し、トランザクション処理を行うブロックを

開始しています。このブロックの中に、DataContext によるデータ操作を記述すれば、トランザクシ

ョンに参加できます。

ここでは、②の空席数の参照 (SELECT)から、⑨の SubmitChanges による空席数更新 (UPDATE) と予

約挿入 (INSERT) までが、1 つのトランザクションの中で行われます。トランザクションでは、デー

タの整合性を保つために、他から更新されないよう、自動的にデータがロックされます。つまり、②

の参照の時点で自動的にロックが開始し、他からデータが保護されるので、この例ではデータの競合

は発生しません。

Page 36: 第 3 部 SQL Server データ ゕクセス手法 (2)download.microsoft.com/download/4/D/9/.../Part3... · なりますが (第 2 部の「図4 linq の仕組み」を参照)、ここでは

36

たしかに、DataContext のデータ キャッシュは、メモリー上の非接続型のオブジェクトですが、②の

参照から⑨の更新までは、トランザクション スコープの範囲内なので、実質的には接続型ゕプロー

チです。よって、②から⑨まではデータ競合を意識せずに更新できます。

註: たしかに、この予約登録をするに当たり、現在の空き状況を確認した上で、予約申し込みを

する一連の作業は、非接続環境で行われマスタとえば、空き状況を照会した際には、空きが 30

人だったとします。この 30 人という情報はクラゕント側にキャッシュされます。そして、こ

の 30 人という情報を閲覧している間に、他から何人か申込し、サーバー側では 30 人から 25 人

に最新の空席数が変化したとします。このとき、空きは 30 人であると認識している人が予約登

録する場合、もし、キャッシュされた 30 人という情報を 29 人に変更して、データペースに反映

すれば、確かにデータ競合になります。つまり、サーバー側では、29 人と 25 人のどちらを採用

すべきか判断できません。しかし、このサンプルでは予約登録の際に、キャッシュした 30 人と

いう数字を破棄して、②のところで改めて最新情報を取得します。つまり、最新の空きである 25

人を認識でき、それを正しく 24 人へと更新できます。もっとも、さすがに、最新の空席数が既

にゼロになっていた場合に、減算して「-1」にすることは不適切なので、④では改めて空席数を

チェックしています。

次に、コミットとロールバックの制御を改めて確認します。

既に ADO.NET 編で触れたように、⑪の Complete メソッド呼び出しが、コミットに同意した合図に

なり、最終的にはトランザクション スコープを終了するタミング (Using ブロックから抜け出すタ

ミング) でコミットされます。

もし、このトランザクション スコープ内で実行エラーが発生した場合は、すぐに⑫の Catch ブロッ

クに移動し、トランザクション スコープを抜け出ます。このときは、Complete メソッドを呼び出し

ていないので、ロールバックすることになります。

また、④の If 文でも、空席数がゼロならばロールバックしたいので、④から⑤の If ブロックの中で、

Exit Try を実行しています。この場合、⑬の End Try の次行まで移動することになります。このとき

も、Complete メソッドを呼び出さずに、トランザクション スコープを抜けるので、ロールバックし

ます。

以上、DataContext と TransactionScope を組み合わせた、トランザクション制御による更新を確認し

ました。もともと、ADO.NET 編の Reserve メソッドや Reserve_ByTranScope メソッドで行っていた

更新処理に比べると、記述が非常に簡潔になっていることが分かります。

Page 37: 第 3 部 SQL Server データ ゕクセス手法 (2)download.microsoft.com/download/4/D/9/.../Part3... · なりますが (第 2 部の「図4 linq の仕組み」を参照)、ここでは

37

LINQ to SQL のデータ バインディング

この節では、データ バンデゖングについて取り上げます。

第 2 部で触れたように、データ バンデゖングのデータ ソースとして対応できるか否かは、対象と

なるオブジェクトの種類によって異なります。LINQ to SQL の主要なオブジェクトである

DataContext は、データ バンデゖングに対応しています。厳密にいうと、対応しているのは、

DataContext によるクエリ結果や、DataContext のプロパテゖとしてゕクセスできるテーブル オブジ

ェクトです。ここでは、このような形態別に実際のコード例を確認します。とくに、DataContext の

テーブル相当のオブジェクトとバンデゖングする場合、データベースへの反映も可能です。その際

には、オプテゖミステゖック同時実行制御に係るデータ競合の問題を考慮する必要があります。この

問題に対処する基本的な方法も示します。

クエリ結果へのバインディング

まず、LINQ to SQL を利用したクエリ結果とバンデゖングをしてみます。既に、クエリ結果を取得

するコードは、AWSC サンプル プログラムに用意してあります。例 6 で取り上げた

MakeProgramsFilterList_ByLINQtoSQL メソッド (以下に再掲) では、クエリ結果として、Caption 列と

Expression 列を持つ論理的なテーブルデータ (実際はオブジェクトの集合) を生成するコードを確認し

ました。ここでは、このメソッドが生成したデータを使って、バンデゖングを行ってみます。

例 14. LINQ to SQL ~クエリ結果を取得し、新たに型を作成~

'(★★代替版:LINQ to SQL) 'プログラム検索の際のフゖルタで使用する項目のリストを作成 Public Function MakeProgramsFilterList_ByLINQtoSQL() As IListSource ←④ Dim db As New AWSCDataContext() Dim query = From row In db.プログラム _ ←⑤ Select Caption = row.プログラム名, _ Expression = "プログラムコード ='" + row.プログラムコード + "'" Return CType(query, IListSource) End Function

このデータを利用しているフォームは、[プログラム空き状況照会] (ProgramInfo.vb)です。ここで、

AWWinApp プロジェクトの中の ProgramInfo.vb のソースフゔルを開いてください。(ソリューショ

ンエクスプロ―ラー上で、ProgramInfo.vb を右クリックして、[コードの表示] をクリックします。)

この ProgramInfo.vb の ProgramInfo_Load ベント ハンドラ には、コメント ゕウトした形式で、す

でに前述のデータを使用するデータ バンデゖングのコードが用意されています。次のように、既

存の ADO.NET のバンデゖングはコメント ゕウトして、逆に LINQ to SQL 版のほうはコメントを解

除し、利用できるように変更しましょう。

例 15. LINQ to SQL のクエリ結果とのデータ バインディング

'(★★ADO.NETの基本的な使用) 'フゖルタ用のプログラム一覧の作成とバンデゖング 'Dim table As DataTable = BLComp1.MakeProgramsFilterList() 'ProgramsList.ValueMember = "Expression"

Page 38: 第 3 部 SQL Server データ ゕクセス手法 (2)download.microsoft.com/download/4/D/9/.../Part3... · なりますが (第 2 部の「図4 linq の仕組み」を参照)、ここでは

38

'ProgramsList.DisplayMember = "Caption" 'ProgramsList.DataSource = table 'ProgramsList.SelectedIndex = 0 '(★★代替版:LINQ to SQL) 'フゖルタ用のプログラム一覧の作成とバンデゖング() Dim table As IListSource = BLComp1.MakeProgramsFilterList_ByLINQtoSQL() ProgramsList.ValueMember = "Expression" ProgramsList.DisplayMember = "Caption" ProgramsList.DataSource = table ProgramsList.SelectedIndex = 0

ADO.NET 版と LINQ to SQL 版を並べると分かりますが、設定手順に特別な違いはないので、理解す

る上での問題はないでしょう。基本的には、バンデゖングに必要なコントロール側のプロパテゖに、

必要な情報を設定するだけです。ここでは、DataSource プロパテゖにクエリ結果を設定している点

が異なります。クエリ結果にも、Expression 列や Caption 列があるので、それぞれ、ValueMember

プロパテゖや、DisplayMember プロパテゖの扱いは同じです。

これを実行すると、[プログラム空き状況照会] フォームのドロップダウン リストに、次図のように

表示されます。

図 19 LINQ to SQL のクエリ結果とのデータ バインディング

ここで使用したデータ ソースは、新たにスキーマを定義して作成されたデータでした。

次項では、DataContext のテーブルオブジェクトに直接バンデゖングする方法を確認します。

DataContext のオブジェクトとのバインディング

そもそも DataContext は SQL Server 上のデータベース上のテーブルと、メモリー上にキャッシュさ

れたオブジェクトをマッピングすることが、主な機能の 1 つです。このメモリー上のオブジェクトと

ユーザー ンターフェスとの間でバンドして、ユーザー ンターフェスからデータベースへ、

変更を反映するには、どうすればよいでしょうか。この方法を確認します。

ここでは、前節までに使用した Windows フォーム ゕプリケーションを引き続き使用します。

Page 39: 第 3 部 SQL Server データ ゕクセス手法 (2)download.microsoft.com/download/4/D/9/.../Part3... · なりますが (第 2 部の「図4 linq の仕組み」を参照)、ここでは

39

註: 前節のクラス継承定義は使用しませんが、O/R デザナ上には、「プログラム」テーブルに

マップされたクラスが必要です。フォームの体裁は、図 12 のように、ボタン 3 個とリスト ボッ

クス 1 個、テキスト ボックス 1 個の状態にしておいてください。

それでは、DataContext の「プログラム」テーブルに相当するオブジェクトと、フォーム上のリスト

ボックスおよびテキスト ボックスとの間で、バンデゖングの設定を行います。

1. フォームデザナ上のフォームのタトルをダブル クリックして、Form1_Load ベント

ハンドラ を生成します。

2. 次のように、Form1_Load ベント ハンドラ の外側に、DataContext オブジェクトをメ

ンバ変数として定義します。また、Form1_Load ベント ハンドラ の中には、バンデ

ゖングを行うコードを、次のように記述します。データ ソースは、「db.プログラム」を

使用します。

例 16. DataContext の「プログラム」オブジェクトとのバインディング

Private db1 As New AWSCDataContext() ←① Private Sub Form1_Load(ByVal sender As System.Object, ... db1.Log = System.Console.Out ←② ListBox1.ValueMember = "プログラムコード" ListBox1.DisplayMember = "プログラム名" ListBox1.DataSource = db1.プログラム ←③ TextBox1.DataBindings.Add("Text", db1.プログラム, "プログラム名") ←④ End SUb

①のように、DataContext オブジェクトは、Form1_Load ベント ハンドラ の外側に記述してくださ

い。この変数は、のちほど、別のベント ハンドラ からも参照します。

②は、今までと同様に、送信される SQL 文をログとして、コンソールに出力するためのものです。

③は、リスト ボックスでのバンデゖングの設定、④はテキスト ボックスのデータ バンデゖング

の設定です。黄色の部分がデータ ソースです。今回は、プログラム テーブルにマップされたオブジ

ェクトを指定しています。

ここで、ビルドして実行してみましょう。すると、フォームが開く直前に、Form1_Load ベント ハ

ンドラ が実行され、次図のように、このスポーツ クラブのプログラム一覧が表示されます。

なお、リスト ボックスで、任意の項目を選択すると、それに連動して、テキスト ボックスの内容も

変化します。前述の③と④のように、同一のデータを指定すると、Windows フォームでは、すべて

のコントロールでゕクテゖブな選択項目が、連動して移動します。Windows フォームでは、ユーザ

ー ンターフェス レベルでのカーソル管理が備わっています。

Page 40: 第 3 部 SQL Server データ ゕクセス手法 (2)download.microsoft.com/download/4/D/9/.../Part3... · なりますが (第 2 部の「図4 linq の仕組み」を参照)、ここでは

40

図 20 リスト ボックスおよびテキスト ボックスと、プログラム情報とのバインディング

ここで、いずれかの項目について、テキスト ボックスで変更した後、リスト ボックスで別の項目を

選択してカーソルを移動してください。次図のように、リスト ボックスにも変更内容が反映されま

す。以下の例では、テキスト ボックスで「パワーヨガ A 特別企画」と変更した後、リスト ボックス

で別の項目を選択した例です。

図 21 テキスト ボックスで修正して、カーソルを移動するとリスト ボックスにも反映

このように連動して、テキスト ボックスの内容が、リスト ボックスに反映されるのは、共通のバ

ンデゖングの元となるデータ ソースが変更されたからです。

しかし、あくまで DataContext オブジェクトとのバンデゖングなので、このままではデータベース

へは反映されません。試しに、このゕプリケーションを一旦閉じて、もう一度、起動してみてくださ

自動連携

テキスト ボックスで

「パワーヨガ A 特別企画」と入力

Page 41: 第 3 部 SQL Server データ ゕクセス手法 (2)download.microsoft.com/download/4/D/9/.../Part3... · なりますが (第 2 部の「図4 linq の仕組み」を参照)、ここでは

41

い。すると、再び Form1_Load ベント ハンドラ が実行され、データベースからデータが取り込ま

れます。フォームのリスト ボックスに表示されるのは、図 20 の変更前の状態です。

DataContext オブジェクトでは、このオブジェクトのプロパテゖなどにゕクセスするだけで、必要な

データは自動的にデータベースから読み込まれます。ADO.NET のように、テーブルゕダプタを使用

して読み込む必要はありません。しかし、データベースへの更新は、明示的に行う必要があるのです。

次に、この更新処理を実装しマスタだし、非接続環境なので、データの競合などを考慮する必要があ

ります。

オプティミスティック同時実行制御における操作

今回の状況での更新処理は、非接続の状態の「プログラム」データのキャッシュに関して、変更分を

データベースに書き込むものです。よって、他のユーザーがデータベースに変更を加えていた場合、

データの競合が発生します。データの一貫性を保ちつつ更新するには、オプテゖミステゖック同時実

行制御に基づく、データ操作を行う必要があります。

DataContext は、オプテゖミステゖック同時実行制御に対応する機能が、既定で備わっています。

DataContext では、以前にデータを読み込んで非接続状態でキャッシュしてあるとき、キャッシュ内

のデータを修正して、データベースへの更新を試みるとき、既に他のユーザーによって、以前と異な

る内容に変更されていると、データ競合として例外を発生させます。

ここでは、簡潔な実装で工数をかけずに、テータの一貫性を保たせるため、更新時の際にトランザク

ション (TransactionScope) を実行し、競合が発生したら、すべてロールバックすることにします。

たとえば、非接続の状態で 4 件のキャッシュデータを変更した後、これの更新をデータベースに試み

たとします。2 件は更新に成功し、3 件目でデータ競合が発生したとします。この場合も、すべての

更新を無かったものとして、ロールバックすることにします。

TransactionScope を使用するには、System.Transactions.dll への参照設定が必要です。ここで、この

DLL (.NET ゕセンブリ) への参照を、この実験用ゕプリケーションのプロジェクトに追加しましょう。

(以下では、プロジェクト名を「TestLtoSQL」と想定しています。)

3. 引き続き使用中のプロジェクトについて、ソリューション エスクプローラ上の、プロジ

ェクト名「TestLtoSQL」を右クリックします。ショートカットメニューから、[参照の追

加] をクリックします。

4. 次図のように、[参照の追加] ダゕログボックスが表示されたら、[.NET] タブをクリック

した後、[コンポーネント名] 一覧から「System.Transactions」をクリックして選択します。

Page 42: 第 3 部 SQL Server データ ゕクセス手法 (2)download.microsoft.com/download/4/D/9/.../Part3... · なりますが (第 2 部の「図4 linq の仕組み」を参照)、ここでは

42

図 22 System.Transactions への参照の追加

5. System.Transactions を選択したら、[OK] をクリックして、このダゕログボックスを閉

じます。

次にコードを入力します。今回は、フォーム上の [Button3] がクリックされたら、データベースを更

新するようにします。

6. フォーム デザナ上で [Button3] ボタンをダブルクッリクして、Button3_Click ベント

ハンドラ を生成します。

7. Form1.vb のコード エデゖタに切り替わったら、この後、クラス名を使う際に、完全修飾

名を使用しなくて済むようにするため、Form1.vb フゔルの中の一番先頭に、次の 2 つ

の Imports ステートメントを追加します。これでクラス名の指定に、相対パスが利用でき

ます。

例 17. 名前空間のインポート

Imports System.Transactions 'TransactionScope のため Imports System.Data.Linq 'ConflictMode のため

8. Button3_Click ベント ハンドラ に次のコードを追加します。

例 18. DataContext による更新と、データ競合の対処

Private Sub Button3_Click(ByVal sender As System.Object, ... Try Using scope As New TransactionScope() ←① db1.SubmitChanges(ConflictMode.FailOnFirstConflict) ←② scope.Complete() ←③

Page 43: 第 3 部 SQL Server データ ゕクセス手法 (2)download.microsoft.com/download/4/D/9/.../Part3... · なりますが (第 2 部の「図4 linq の仕組み」を参照)、ここでは

43

End Using Catch ex As Exception ←④ MsgBox(ex.Message & vbCr _ & "すべてロールバックします。") End Try End Sub

データベースへの更新を試みるのは、②の SubmitChanges メソッドです。このメソッドに渡した引

数 (FailOnFirstConflict) は、複数行の書き換えを試みたとき、1 件でも競合が発生したら、それ以降の

更新処理はせずに、例外を発生するモードを指定したものです。これ以外のモードとしては、競合が

発生しても、残りの行は継続して一通り更新を試みる ContinueOnConflict があります。

TransactionScope の使用方法は、今までと同じです。①でトランザクション スコープを開始して、

スコープのブロックを終了する手前の③で、Complete メソッドを呼び出します。もし、例外が発生

しなければ、③を通過するはずです。

例外が発生した場合は、③を実行せすに、④の Catch ブロックに制御が移るので、ロールバックする

ことになります。

それでは実行して、変更の実験をしましょう。

1. ゕプリケーションを起動した後、まずは、1 件だけテキスト ボックス上で書き換えてみ

マスタとえば、「ステップ A」を「ステップ A 定期開催」に変更します。

2. [Button3] ボタンをクリックして、データベースへの変更を試みます。

3. コンソールのログには、次のように、UPDATE 文を送信したログが表示されますることを

確認します。

図 23 DataContext によるデータベースの更新

このログを見ると、パラメータ付きクエリを使用して、元のデータがあるかを抽出条件に指定し、元

のテータがあったときだけ更新を行っています。この更新に失敗した場合は、競合とみなされ、

DataContext のラブラリは例外を発生させます。

元の値

修正後の値

Page 44: 第 3 部 SQL Server データ ゕクセス手法 (2)download.microsoft.com/download/4/D/9/.../Part3... · なりますが (第 2 部の「図4 linq の仕組み」を参照)、ここでは

44

4. これでデータベースの更新ができたはずなので、一旦、ゕプリケーションを終了して、

再び起動し、更新後のデータが表示されることを確認します。

もう 1 つ、データ競合の実験をしましょう。併せて、SQL Server Management Studio など、データ

ベースを変更できるツールを起動してください。

5. 実験用のフォームゕプリケーションを起動して、5 件くらい修正します。まだデータベー

スには送信しません。[Button3] をクリックしないでください。

6. 次に、SQL Server Management Studio などから、この実験用ゕプリケーションが編集し

た複数件の行のうち、中間あたりの同じ行を 1 つだけ修正します。

7. そして、実験用ゕプリケーションから、[Button3] をクリックして更新します。

8. 次のエラーメッセージが表示されることを確認します。

図 24 データ競合の検出

エラーメッセージは、書き換えた行によって異なります。上記の場合は、SumbitChages メソッドを

呼び出した際に、2 件目まで更新に成功し、3 件目でデータ競合が検出された例です。

ただし、TransactionScope を使用しているので、結局、データベース側に対する更新は、すべてロー

ルバックするはずです。

9. 上記の [OK] をクリックして、メッセージ ボックスを閉じます。

10. 一旦、ゕプリケーションを終了し、再び起動して、データベースからデータを読み込み

ます。

11. データが元のままで、修正されていないことを確認します。

以上、データ競合の検出と安全にロールバックする方法を確認しました。DataContext の

SubmitChanges メソッドと TransactionScope を使用すると、簡潔にオプテゖミステゖック同時実行

制御に対応できます。

Page 45: 第 3 部 SQL Server データ ゕクセス手法 (2)download.microsoft.com/download/4/D/9/.../Part3... · なりますが (第 2 部の「図4 linq の仕組み」を参照)、ここでは

45

註: このサンプルでは、簡単にするため、変更対象のテーブルを全件読み込んだ上で、変更作業

行っています。パフォーマンスの考慮や、競合の軽減ためにも、一度に読み込むべきデータ量を

制限することも考慮する必要があります。また、非接続のままで、あまり長時間の間、大量の変

更をせずに、こまめに書き込むような運用も、競合の軽減に役立つでしょう。

Page 46: 第 3 部 SQL Server データ ゕクセス手法 (2)download.microsoft.com/download/4/D/9/.../Part3... · なりますが (第 2 部の「図4 linq の仕組み」を参照)、ここでは

46

まとめ

第 3 部の LINQ 編では、参照や更新などの基本的な機能を実装した、AWSC サンプル プログラムを

使用して、LINQ が 1 つのゕプリケーションの中で、その用途や実装箇所を確認しました。そして、

簡単な実験用のゕプリケーションを構築し、基本的な実装方法やキーとなる機能について確認しまし

た。

また、従来の ADO.NET とのコード実装方法の違いを示すことによって、LINQ の位置づけや役割、

特徴を確認しました。

ここでは、LINQ が提供するすべての機能を網羅的に解説したわけではありません。しかし、このサ

ンプルを使用して、主要な利用の形態ごとに、その特徴や実装する上での注意点や、取り組む上での

着眼点、ヒントなどを確認しました。

さらに、単にクラス ラブラリを使用するだけでなく、Visual Studio 2008 を有効活用することを踏

まえ、O/R デザナを使用し、「関連付け」の設定と、そのソース コードでの活用や、クラス継承

をビジュゕルにデザンする方法も確認しました。

本書で取り上げた内容が、LINQ を今後使用する上で、役立って頂ければ幸いです。