メタデータ編集による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のように、メタデータ・クラスで定義された属性情報が個々のページにも反映されていれば成功である。
Insider.NET 記事ランキング
本日
月間