特集:Visual Studio 2008 SP1新機能解説(1)

DBアプリをコーディングレスで構築する「ASP.NET Dynamic Data」

山田 祥寛(http://www.wings.msn.to/
2008/09/19
Page1 Page2 Page3 Page4

メタデータ編集によるDynamic Dataアプリケーションのカスタマイズ

 前節では、取りあえずテーブルから取得した内容をそのまま一覧/明細表示してみた。しかし、実際のアプリケーションであれば、取得した数値や日付をより見やすい形に整形したい、取得したカラム値に基づいて画像やリンクを生成したい、そうでなくとも、最低限、タイトル行はより分かりやすい日本語で表記したい、など、なかなか自動生成された結果をそのまま利用するわけにはいかないはずだ。

 そのようなときに、開発者は個別にテーブル単位にページを自作する必要があるのだろうか。であるとすれば、Dynamic Dataの魅力は大幅に減じてしまうはずであるが、幸いにして、そのような必要はない。

 Dynamic Dataの世界では、「メタデータ・クラス」を定義することでページそのものに手を加えることなく、一覧表や明細の表示を容易にカスタマイズできるのだ。メタデータ・クラスとは、データの構成や挙動を定義するためのクラスである。

■メタデータ編集の基本

 具体的な例をいくつか見てみよう。ここではBooksテーブルの構成情報(メタデータ)に手を加えて、以下の図のような一覧/明細画面を作成するものとする。


図16 データ・モデルによって公開されたテーブルを一覧表示(トップページ)
  Pubsテーブルを非表示に。


Booksテーブルをクリックすると……



図17 カスタマイズされたBooksテーブルの一覧画面
  各列のタイトル行を日本語表示。
  価格/刊行日の表示を「9,999円」「YYYY年MM月DD日」の形式に整形。
  出版社列に出版社名(カナ)を表示。


[編集]リンクをクリックすると……



図18 カスタマイズされたBooksテーブルの明細画面(編集モード)
  最大文字列長の制限を定義(エラー・メッセージも変更)。
  出版社列に出版社名(カナ)を表示。

 ここでの変更のポイントは、次のとおりだ。

  • 各列のタイトル行を日本語に置き換える
  • 価格、刊行日列の表示をそれぞれ「9,999円」「YYYY年MM月DD日」の形式に整形する
  • 出版社列に(出版社名ではなく)出版社名(カナ)を表示する
  • 書名の最大文字列長を20文字に制限し、検証エラー時のメッセージを「書名は20文字以内で!」に変更する
  • Pubsテーブルは非表示にする

 それではさっそく、具体的な手順を見ていこう。まずは、App_Codeフォルダに新規のクラスとしてBook.vb/Book.cs、Pubs.vb/Pubs.csを追加してほしい。そのうえで、以下のコードを追加してみよう。

using System.ComponentModel;
using System.ComponentModel.DataAnnotations;

namespace WingsModel {
  // メタデータを定義したBooksMetaDataクラスを関連付け
  [MetadataType(typeof(WingsModel.BooksMetadata))]
  public partial class Books {
  }

  // Booksテーブルのメタデータを定義するBooksMetaDataクラス
  public class BooksMetadata {

    // isbn列の表示名を定義
    [DisplayName("ISBNコード")]
    public object isbn { get; set; }

    // title列の表示名、検証ルール(文字列長検証)を定義
    [DisplayName("書名")]
    [StringLength(20, ErrorMessage = "書名は20文字以内で!")]
    public object title { get; set; }

    // price列の表示名、値の整形フォーマットを定義
    [DisplayName("価格")]
    [DisplayFormat(DataFormatString = "{0:#,###円}")]
    public object price { get; set; }

    // Pubs列(p_id列からの参照)の表示名を定義
    [DisplayName("出版社")]
    public object Pubs { get; set; }

    // published列の表示名、値の整形フォーマットを定義
    [DisplayName("刊行日")]
    [DisplayFormat(DataFormatString = "{0:yyyy年MM月dd日}")]
    public object published { get; set; }

    // cd列の表示名を定義
    [DisplayName("CD-ROM?")]
    public object cd { get; set; }

    // memo列の表示名を定義
    [DisplayName("備考")]
    public object memo { get; set; }
  }
}
Imports System.ComponentModel
Imports System.ComponentModel.DataAnnotations

Namespace WingsModel
  ' メタデータを定義したBooksMetaDataクラスを関連付け
  <MetadataType(GetType(WingsModel.BooksMetadata))> _
  Partial Public Class Books
  End Class

  ' Booksテーブルのメタデータを定義するBooksMetaDataクラス
  Public Class BooksMetadata

    ' isbn列の表示名を定義
    Private _isbn As Object
    <DisplayName("ISBNコード")> _
    Public Property isbn() As Object
      Get
        Return _isbn
      End Get
      Set(ByVal value As Object)
        _isbn = value
      End Set
    End Property

    ' title列の表示名、検証ルール(文字列長検証)を定義
    Private _title As Object
    <DisplayName("書名")> _
    <StringLength(20, ErrorMessage:="書名は20文字以内で!")> _
    Public Property title() As Object
      Get
        Return _title
      End Get
      Set(ByVal value As Object)
        _title = value
      End Set
    End Property

    ' price列の表示名、値の整形フォーマットを定義
    Private _price As Object
    <DisplayName("価格")> _
    <DisplayFormat(DataFormatString:="{0:#,###円}")> _
    Public Property price() As Object
      Get
        Return _price
      End Get
      Set(ByVal value As Object)
        _price = value
      End Set
    End Property

    ' Pubs列(p_id列からの参照)の表示名を定義
    Private _Pubs As Object
    <DisplayName("出版社")> _
    Public Property Pubs() As Object
      Get
        Return _Pubs
      End Get
      Set(ByVal value As Object)
        _Pubs = value
      End Set
    End Property

    ' published列の表示名、値の整形フォーマットを定義
    Private _published As Object
    <DisplayName("刊行日")> _
    <DisplayFormat(DataFormatString:="{0:yyyy年MM月dd日}")> _
    Public Property published() As Object
      Get
        Return _published
      End Get
      Set(ByVal value As Object)
        _published = value
      End Set
    End Property

    ' cd列の表示名を定義
    Private _cd As Object
    <DisplayName("CD-ROM?")> _
    Public Property cd() As Object
      Get
        Return _cd
      End Get
      Set(ByVal value As Object)
        _cd = value
      End Set
    End Property

    ' memo列の表示名を定義
    Private _memo As Object
    <DisplayName("備考")> _
    Public Property memo() As Object
      Get
        Return _memo
      End Get
      Set(ByVal value As Object)
        _memo = value
      End Set
    End Property
  End Class
End Namespace
Booksテーブルのメタデータを定義するコード(上:Books.cs、下:Books.vb)

using System.ComponentModel;
using System.ComponentModel.DataAnnotations;

namespace WingsModel {
  // メタデータを定義したPubsMetaDataクラスを関連付け
  [MetadataType(typeof(WingsModel.PubsMetadata))]
  public partial class Pubs {
  }

  // Pubsテーブルを非表示にするとともに、
  // 外部キー参照で表示する列をname_kana列に変更
  [ScaffoldTable(false)]
  [DisplayColumn("name_kana")]
  public class PubsMetadata {
  }
}
Imports System.ComponentModel
Imports System.ComponentModel.DataAnnotations

Namespace WingsModel
  ' メタデータを定義したPubsMetaDataクラスを関連付け
  <MetadataType(GetType(WingsModel.PubsMetadata))> _
  Partial Public Class Pubs
  End Class

  ' Pubsテーブルを非表示にするとともに、
  ' 外部キー参照で表示する列をname_kana列に変更
  <ScaffoldTable(False)> _
  <DisplayColumn("name_kana")> _
  Public Class PubsMetadata
  End Class
End Namespace
Pubsテーブルのメタデータを定義するコード(上:Pubs.cs、下:Pubs.vb)

 一見してコードこそ長いが、よくよく見てみると、実はコードの大部分は単なるプロパティ定義であるにすぎない。ここで注目していただきたいポイントは、以下の2点である。

(1)メタデータは属性構文で宣言する

 メタデータは、BooksMetadataやPubsMetaDataなどのように、「<テーブル名>Metadata」クラス(メタデータ・クラス)として定義しておく必要がある*3

*3 命名規則に特に決まりはないが、後から見たときの分かりやすさを考えれば、この形式に従っておくのが無難だろう。

 メタデータ・クラスには、それぞれメタ情報を定義したい列をプロパティとして定義しておく必要がある(特にメタ情報の指定が必要ない列については、省略しても構わない)。また、外部キーでテーブルを参照している列(ここでは「p_id」)のプロパティ名は、「p_id」ではなく、参照先のテーブル名(ここでは「Pubs」)となる点に注意してほしい。

 それぞれのフィールド、またはテーブルそのものに対してメタデータを定義するには、以下のようにプロパティ、またはクラスの先頭で属性宣言を追加するだけだ。

[属性(パラメータ, ……,プロパティ = 値,……)]
<属性(パラメータ, ……,プロパティ := 値,……)> _
メタデータを定義する属性の構文(上:C#版、下:Visual Basic版)

 メタデータを定義する際に利用可能な、主な属性は以下のとおりである。属性にはパラメータやプロパティ情報を指定できる。パラメータは指定された順番で指定し、追加のプロパティ情報はパラメータ指定の後方に、任意の順番で指定できる。

分類 属性 概要
基本 ScaffoldColumn(flag) 対象のフィールドを自動生成の対象とするか
ScaffoldTable(flag) 対象のテーブルを自動生成の対象とするか
DefaultValue(value) 新規データ入力時に適用するデフォルト値
表示 Description(msg) データ入力時にツールチップとして表示するメッセージ
DisplayColumn(displayCol [,sortCol [,sortDesc]]) 外部キーで関連付けられた参照先テーブルの表示すべき列を指定(sortDescはsortCol列について降順でソートするか)
DisplayFormat() データ・フィールドを表示するための書式を指定

プロパティ 概要
ApplyFormatInEditMode 編集モード時に書式文字列を適用するか
ConvertEmptyStringToNull 空文字列をNothing/nullに自動変換するか
DataFormatString 書式文字列
NullDisplayText 値がNothing/nullのときの表示テキスト
UIHint(name) フィールド値の表示/編集に使用するユーザー・コントロールを指定
検証 Range(min, max) データ・フィールドが取り得る値の範囲を定義
RegularExpression(pattern) 指定された正規表現でフィールド値を検証
Required() フィールド値が必須かどうかを指定
StringLength(max) フィールドが受け入れ可能な最大文字数
DataType(type) データ・フィールドと関連付いた追加的な型情報を指定
Validation() 検証にかかわる属性の基本クラス(Validation属性のプロパティは、すべての検証属性で共通して利用可)

プロパティ 概要
ErrorMessage 検証エラー時に表示するエラー・メッセージ
ErrorMessageResourceType エラー・メッセージとして使用するリソースの型
ErrorMessageResourceName エラー・メッセージとして使用するリソースの名前
メタデータ・クラスで利用可能な主な属性
DefaultValue属性とDescription属性はSystem.ComponentModel名前空間で、それ以外はSystem.ComponentModel.DataAnnotations名前空間で定義されている。

 メタデータは、フィールド(列)単位だけではなく、例えばScaffoldTable/DisplayColumn属性のようにテーブル単位で指定するものもある点に注意してほしい。

(2)メタデータ・クラスを関連付けるのはMetaDataType属性

 メタデータ・クラスは、それ自体を宣言しただけではDynamic Dataから認識することはできない。Entity Data Modelで自動生成されたエンティティ・クラス(ここではBooks/Pubsテーブル)に関連付けておく必要がある。そして、この関連付けを行うのがMetaDataType属性の役割である。MetaDataType属性のパラメータには、関連付けるメタデータ・クラスを指定すればよい(上記リストの太字部分)。

 なお、エンティティ・クラスを定義する際には、これを部分クラス(パーシャルクラス)として定義している点にも要注目だ。部分クラスとは、.NET Framework 2.0から使える機能の1つで、1つのクラスの定義を複数に分割して記述できる機能のこと。部分クラスを利用することで、自動生成された元のクラスには手を加えずに、拡張部分のみ(ここではメタデータの関連付け)を定義しておき、コンパイル時に1つのクラスとしてマージすることが可能になるわけだ。

 以上が理解できたら、再びDefault.aspxを実行してみよう。本節冒頭の図16〜18のように、メタデータ・クラスで定義された属性情報が個々のページにも反映されていれば成功である。


 INDEX
  Visual Studio 2008 SP1新機能解説(1)
  DBアプリをコーディングレスで構築する「ASP.NET Dynamic Data」
    1.Dynamic Dataアプリケーションの基本
    2.データ・モデルの作成/Global.asaxの編集
  3.メタデータ編集によるDynamic Dataアプリケーションのカスタマイズ
    4.自作ユーザー・コントロールの利用/ページ・テンプレートのカスタマイズ
 
  Visual Studio 2008 SP1新機能解説(2)
  .NETの新データアクセス・テクノロジ「ADO.NET Entity Framework」
    1.ADO.NET Entity Framework概要
    2.Entity Data Model(EDM)の作成と利用
    3.EDMに対するクエリの利用方法(Entity SQL)
    4.EDMに対するクエリの利用方法(Object Services/LINQ to Entities)
 
  Visual Studio 2008 SP1新機能解説(3)
  RESTスタイルのWebサービスを手軽に公開する「ADO.NET Data Services」
    1.ADO.NET Data Services概要/対応するクライアント
    2.サンプル − ADO.NET Data Servicesの公開
    3.ASP.NET AJAX環境のデータを取得/追加する


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間