SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

japan.internet.com翻訳記事

Visual Studio .NETのウィザードを使ってN階層アプリケーションを作成する:パート1

コードを複数の層に分離して効率的なアプリケーション開発を行う

データオブジェクト層

 オブジェクト指向プログラミング(OOP)の原則に従って、まず取り扱う情報(通常はデータベースの情報)を含むデータオブジェクトを作成します。.NETでは、型指定されたデータセットを使うのが最も簡単です。このツールを使うと、通常のデータセットクラスがラップされ、オブジェクト指向に基づいたデータアクセスや、プロパティ(フィールド)の厳密な型指定が可能になります。コレクションや配列リストなどの構造でもデータベーステーブルオブジェクトを表現できますが、VSのほとんどのウィザードでは、型指定されたデータセットが使用されます。

 新しいデータセットを作成するには、プロジェクトを右クリックして[Add]、[Add New Item]の順にクリックします。ウィザードの場合は、Datasetオブジェクトを選択して、新しいデータオブジェクトに「OrdersDO」などの適当な名前を指定します。ウィザードによって、XSDと型指定されたデータセットに必要なコードが生成され、デザインサーフェイスが表示されます。開発者はこのデザインサーフェイスを使ってテーブルをサーバーエクスプローラからデータセットのスキーマにドラッグして追加したり、あるいは他のウィザードを使用してリレーションシップや制約を追加したりできます。テーブルからフィールドをいくつか除外する場合は、スキーマから削除します。本稿の例では、「Orders」テーブルと「Order Details」テーブルを、サーバーエクスプローラからデザインサーフェイスにドラッグしました。出荷のデータは一切必要ないので、「Orders」テーブルでは4つのフィールドだけ残してすべて削除しました。ツールボックスの[XML Schema]のセクションでデータリレーションシップを選び、Ordersオブジェクトにドラッグアンドドロップします。リレーションシップウィザードでは「Order Details」を子テーブルに選んでください。ここまでの作業を終えると、図1のように表示されるはずです。

図1 型指定されたデータセット(データオブジェクト)
図1 型指定されたデータセット(データオブジェクト)

生成されるコード

 型指定されたデータセットの場合、データテーブルや行を抽象化するため、およびデータフィールド(プロパティ)の厳密な型指定を可能にするために、数百行のコードが生成されます。これにより、構文やIntelliSenseがデザイン時に検証され、オブジェクト指向的な方法でデータオブジェクトを参照できるようになります。生成されたコードを表示するには、プロジェクトを選択してソリューションエクスプローラの最上部にあるアイコンをクリックし、ファイルを表示します。次に、データセットの隣の[+]を展開してください。表示される2つのファイルのうち、拡張子が.vb(または.cs)であるファイルにコードが格納されています。このコードはデータセットを保存するたびに再生成されるので、編集はしないでください。

 生成されるコードについて、以下に簡単にまとめます。この例では、「MyTab」という名前のテーブルを含む型指定されたデータセットが、「MyDS」という名前で生成されます。

 型指定されたDataSetは、DataSetから派生したMyDSという名前のクラスを生成することで厳密な型指定を実現します。DataSetクラスのサブクラスの名前は、XMLスキーマを生成した元のDataSetに含まれるDataSet.DataSetNameと同じになります。次の4つのパブリックな入れ子のクラスが公開されます。

  • DataTableを継承しIEnumerableを実装するMyDS.MyTabDataTable
  • DataRowを継承するMyDS.MyTabRow
  • EventArgsを継承するMyDS.MyTabRowChangeEvent
  • MyDS.MyTabRowChangeEventHandler

 このとき、

  • MyDSDataSet.DataSetNameの値
  • MyTabDataTable.TableNameの値
  • MyTabRowDataTable.TableName + Rowの値

 を表します。

 MyDS.MyTabDataTableには、一連のプライベートなDataColumnメンバが含まれています(テーブルまたはクエリ/ストアドプロシージャの結果セットの列ごとに1つのデータメンバがあります)。これらに関するgetメソッドが用意されていますが、実行時にDataColumnsを追加または削除することは許可されないので、Internalに指定されています。また、変更中(Changing)、変更済み(Changed)、削除中(Deleting)、削除済み(Deleted)の行に関する型指定された4つのデリゲートもあります。

 型指定されたDataTableには以下のメソッドが含まれます。

  • Rowsに関するIndexerGetEnumeratorメソッド
  • 2つの追加メソッド。メソッド名はどちらもAddMyTabRowですが、それぞれ少し違う方法で使用されます。Rowを受け取るAddMyTabRow(row)は、型指定された空の行であるNewMyTabRowで使用されます。AddMyTabRow(n1,n2,n3)はN個のパラメータを受け取ります(Nはテーブル内の列数で、MyTabにはテーブル名が入ります)
  • 削除メソッドRemoveMyTabRow

 DataColumnsDataTableInitClassメソッドで作成され、DataTableに追加されます。メタデータが使用できる場合は、このメソッドでメタデータの入力も行われます。主キーまたは一意の列が存在する場合は、入力に主キーを使用するFindBykeycolnameメソッドが用意されます。

 MyTabRowクラスは列をパブリックプロパティとして公開します。列にnullを入力できる場合は、IsColumnnameNullSetColumnnameNullという2つのヘルパー関数があらかじめ定義されます(Columnnameにはいずれも列名が入ります)。

 厳密に型指定されたDataSetに、複数のテーブルが含まれる場合があります。テーブルに親子のリレーションシップを指定(DataSetRelationsコレクションにDataRelationを作成)すると、データとメソッドがいくつか追加で生成されます。DataSetRelationが含まれる場合、生成されるコードは次のようになります。

  • PrimaryKeyプロパティが親テーブルのDataColumnプロパティに追加され、ForeignKeyConstraintが子テーブルに追加されます。また、DataRelationが追加されます。元のスキーマでDataSetNestedプロパティが設定されていた場合は、型指定されたDataSetでもこれが維持されます。
  • ChildTabRowParentTabRow型のプロパティを持ちます。プロパティのgetメソッドはGetParentRowを呼び出し、setメソッドはSetParentを呼び出します。
  • ParentRowGetChildTabRowというメソッドが追加されます。このメソッドはGetChildRowsを呼び出すことによって、型指定された子行の配列を返します。

 このとき、

  • ParentTabは親テーブルのDataTable.TableNameの値
  • ChildTabは子テーブルのDataTable.TableNameの値

 を表します。

 最後に、XMLの永続性に関連する特定のメソッドとプロパティが、厳密に型指定されたDataSetに含まれます。これらはDataSetのメソッドをオーバーライドします。

  • protectedコンストラクタはSerializationInfoの情報と、StreamingContextのコンテキストを受け取ります。このコンストラクタはGetSerializationDataを呼び出す前にInitClassを呼び出します。これは、ISerializableを実装する場合に必須の動作です。
  • ReadXmlSerializableは単純に基本クラスのReadXmlメソッドを呼び出します。
  • GetSchemaSerializableWriteXmlSchemaを呼び出して、スキーマをXmlTextWriterに書き込みます。続いて、System.Xml.Schema.XmlSchemaにそれを読み込みます。この動作は基本クラス(DataSet)のコードと同様です。
  • ShouldSerializeTablesプロパティとShouldSerializeRelationsプロパティ、およびShouldSerialize[MyTable]という追加のプロパティはfalseを返します。

 本稿の例では、2つの関連付けられたテーブルを使用します。およそ720行のコードを自動生成して、データオブジェクト層が構築されます。

データアクセス層

 データアクセスとビジネスロジックの間でも、必要に応じて層を分離することがあります。バックエンドのデータストレージエンジンが更新されたとき、それらの変更はすべてこの層で処理されます。データアクセス層では、データオブジェクトとデータストアとの連携を行い、データベースからデータセットを入力および更新するためのメソッドを提供します。また、この層によって、ビジネスロジックに必要なストアドプロシージャとのやり取りを行う場所が追加されます。この層を作成するには、プロジェクトに新しい項目を追加し、一覧からコンポーネントクラスオブジェクトを選択して、「OrdersDA」などの名前を指定します。すると、デザインサーフェイスが表示され、その他のオブジェクトを画面上で追加することができます。

 ツールボックスのデータセクションで、データアダプタオブジェクト(SQL Serverの場合はSqlDataAdapter、その他のデータベースの場合はOleDataAdapter)をデザインサーフェイスにドラッグします。通常、ウィザードは自動的に起動しますが、起動しない場合はデータアダプタを右クリックして、[Configure Data Adapter]をクリックしてください。

 最初の画面では接続を指定します。データベースへの既存の接続を選択するか、新しい接続を作成してください。2つ目の画面では、SQLまたはストアドプロシージャを作成するか、既存のストアドプロシージャを使用するかを選択できます。新しく作成する場合は、次の画面で必要なクエリを作成します。ストアドプロシージャを作成する場合は、各自の命名方法に従って名前を入力してください。なお、既存のストアドプロシージャを使用する場合は、3つ目の画面でストアドプロシージャ名を指定します。

 SQLステートメントを使用する場合、3つ目の画面では、必要なデータをデータベースから返すSelectステートメントを入力します。[Query Builder]ボタンを使うと、テーブルやフィールドを選択することができます。テストすることも可能です。[Advanced]ボタンでは、指定したSelectステートメントから更新、挿入、および削除のコマンドを生成するかどうかを選択できます。[Next]をクリックすると最後の画面に進み、生成された内容を表示します。

 デザインサーフェイスに戻り、先ほど作成したデータアダプタを選択します。プロパティ一覧の名前フィールドで、このオブジェクトに自分の命名方法に合った名前を指定してください(「daOrders」など)。次に、データ接続を調整する必要があります。作成した接続オブジェクトを選択し、プロパティで名前を「con」に変更します。接続文字列をハードコーディングする代わりに、この情報をapp構成ファイルに移動します。[Configurations]セクションのDynamicPropertiesエントリを展開し、その下(Dataセクションの下ではないことに注意)にあるConnectionStringプロパティを選択してください。プロパティの値の右にある、ビルドを実行する楕円形のボタンをクリックし、[map to a key in configuration file]チェックボックスをオンにして任意の名前を入力します(既定の名前を変更してください)。これで、「App.Config」ファイルにエントリが正しく作成され、実行時にそれを取得するためのコードが生成されます。

図2 データアクセス層
図2 データアクセス層

 次に、先ほどと同じ手順で「Order Details」テーブルにデータアダプタをもう1つ追加し(必ず同じ接続を使用してください)、名前を「daOrderDetails」に変更します。

 それぞれのデータアダプタを右クリックし、[Generate dataset]をクリックします。1つ目の手順で作成したデータセット「ProjectName.OrdersDO」を選択して、正しいテーブルが指定されていることを確認してください。[OK]をクリックすると、データアダプタからデータセットへのリンクに必要なすべてのコードがウィザードによって作成されます。

 最後に、デザインサーフェイス上で右クリックし、[View Code]をクリックして表示をコードウィンドウに切り替えます。コンポーネントではなく型指定されたデータセットを継承するように、クラスの定義を変更してください。以上で、すべてのデータセットオブジェクトにビジネスオブジェクトからアクセスできるようになります。

Public Class OrdersDA
    Inherits OrdersDO  'System.ComponentModel.Component

 生成されたコードは、次の折り畳み領域の中に隠されています。

+Component Designer generated code

 生成されたコードに、データセットの入力と更新に必要な2つのメソッドを自分で追加する必要があります。以下のコードを追加してください。

    Public Function FillDataSet() As DataSet
        con.Open()
        Me.daOrders.Fill(Me.Orders)
        Me.daOrderDetails.Fill(Me.Order_Details)
        con.Close()
        Return Me
    End Function
    Public Function UpdateDataSet(Optional ByVal TableName _
        As String = "All") As DataSet
        con.Open()
        If TableName = "Orders" Or TableName = "All" Then _
            Me.daOrders.Update(Me.Tables("Orders"))
        If TableName = "Order Details" Or TableName = "All" Then _
            Me.daOrders.Update(Me.Tables("Order Details"))
        con.Close()
    End Function
    'Add any other business object functionality here

End Class 

 最後に、すべてを保存してプロジェクトをビルドします。Sub Mainがないというエラーが表示されますが、これは次の手順で作成します。

生成されるコード

 「+Component Designer generated code」のセクションを展開すると、生成されたコードを確認できます。以下のコードが含まれています。

  • Connectionオブジェクト。接続文字列は構成ファイルに分離されています。
  • 各テーブルのデータアダプタ。テーブルマップとコマンドオブジェクトが関連付けられています。
  • 各データアダプタにCRUD機能を実装するための、4つのCommandオブジェクト。
  • パラメータを必要とする各コマンドのParameterオブジェクト。
  • データセットのインスタンス生成コード(含まれない場合もあります)。

 今回の2つのテーブルの場合、約150行のコードが生成されます。

ビジネス層

 次に、ビジネスオブジェクトクラスを作成する手順に移ります。このクラスにはすべての検証ロジックとその他のビジネスロジックを含めます。Bizプロジェクトの場合は、クラス項目を「OrdersBO」という名前で新規作成します。コードウィンドウで、OrdersDAクラスを継承する新しいクラスを作成してください。このクラスについては、後で必要なロジックを追加する方法を説明します。

注意
 ビジネスロジックがそれほど多くなければ、データアクセスクラスに入れても構いませんが、ビジネス層の分離ができなくなります。

次のページ
プレゼンテーション層

この記事は参考になりましたか?

japan.internet.com翻訳記事連載記事一覧

もっと読む

この記事の著者

japan.internet.com(ジャパンインターネットコム)

japan.internet.com は、1999年9月にオープンした、日本初のネットビジネス専門ニュースサイト。月間2億以上のページビューを誇る米国 Jupitermedia Corporation (Nasdaq: JUPM) のニュースサイト internet.comEarthWeb.com からの最新記事を日本語に翻訳して掲載するとともに、日本独自のネットビジネス関連記事やレポートを配信。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

David Catherman(David Catherman)

CMI Solutions所属。データベースアプリケーションのデザイン/開発に20年以上の経験を持ち、ここ4、5年は特にMicrosoft .NETとSQL Serverに仕事が集中している。現在はCMI Solutionsのアプリケーション設計者および上級開発者であり、Visual Studioと...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/320 2006/04/11 19:11

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー