AutoMapperを使って異なるオブジェクト間のデータコピーを自動化するには?(名前変換ルール編):.NET TIPS
データの転送元と転送先のプロパティ名に一定の変換ルールがある場合に、AutoMapperによりデータコピーを自動化する方法を解説する。
対象:.NET 4以降
オープンソースのライブラリ「AutoMapper」を使うと、異なるクラス間のデータコピーを自動化できる。「独自マッピング編」ではプロパティ名が異なるときに独自のマッピングを定義する方法を紹介した。しかし、例えばデータベースの多数の列名をマッピングするコードを書くのは、うんざりする作業だ。何とかならないだろうか? データベースの列名とコピー先のプロパティ名の間に一定の名前変換規則があれば、そのルールに基づいてAutoMapperにマッピングさせることができるのだ。本稿では、そのようなAutoMapperのマッピング機能の使い方を紹介する。
なお、本稿執筆時点でAutoMapperがサポートしているのは.NET Framework 4以降であるが、NuGetから導入するため、本稿ではVisual Studio 2012を使って説明する。また、本稿のサンプルは「MSDN Code Recipe:.NET Tips #1104」からダウンロードできる。
AutoMapperを導入するには?
「.NET TIPS:AutoMapperを使ってオブジェクト間のデータコピーを自動化するには?(基本編)」をご覧いただきたい。
例題
ここでは例題として、催事の情報を格納してあるデータベースがあって、データベースにSQLを発行して得られた結果を型付きデータセットに格納しているものとしよう。その型付きデータセットは「EventDataSet」クラスとして、次の画像と表に示すように定義してある。この中に定義した「EventTable」クラスをデータのコピー元とする。
型付きデータセット「EventDataSet」クラスの編集画面(Visual Studio 2012)
Visual Studio 2012で型付きデータセットを編集している画面の一部である。
型付きデータセットを作成するには、プロジェクトに新しい項目を追加するダイアログで[データセット]を選び、ファイル名を「EventDataSet.xsd」とする。作成したファイルを開いた画面の中を右クリックし、そのメニューから[追加]−[DataTable]を選ぶと、画面上にDataTableの定義が追加される。そのDataTableの名前を「EventTable」に変更する。また、DataTableを右クリックして出てくるメニューの[追加]−[列]を使って、データ列の定義を追加してプロパティを設定する(次の表)。
なお、実際の開発では、データベースエクスプローラーから目的のテーブルを型付きデータセットの編集画面にドラッグ&ドロップするだけで、このようなDataTableの定義が自動的に生成される。そのとき生成される列名はデータベースに定義されている名前になる。
列名 | 型(DataTypeプロパティ) |
---|---|
event_id | System.UInt16 |
event_title | System.String |
date_time | System.DateTime |
型付きデータセット「EventDataSet」内の「EventTable」クラスの列定義 |
また、データのコピー先は、次のコードのような「EventData」クラスとする。
public class EventData
{
public DateTime DateTime { get; set; }
public string EventTitle { get; set; }
public static IList<EventData> GetData()
{
// 後述
}
}
Public Class EventData
Public Property DateTime As DateTime
Public Property EventTitle As String
Public Shared Function GetData() As IList(Of EventData)
' 後述
End Function
End Class
以上の二つのクラスのオブジェクト間で、次の図のようなデータコピーを行いたいのである。ここではコピー対象のプロパティが二つだけだが、実際の開発では何十個もあったりすることが珍しくないだろう。
本稿で実装するデータマッピング
EventTableクラス(左)とEventDataクラス(右)で、コピーしたいプロパティの名前の間に一定の変換ルールがある。単語の区切りであるアンダースコア「_」を削除し、代わりに単語の先頭を大文字にする(=Pascal形式)、という変換規則だ。
AutoMapperで名前変換ルールを適用することにより、この図に示したようなコピーが簡単にできる。
AutoMapperで名前変換ルールを適用するには?
AutoMapperのInitializeメソッドで名前変換ルールを指定すればよい。
名前変換ルールは、INamingConventionインターフェース(AutoMapper名前空間)を実装したクラスとして定義する。ただし上の例題の場合は、すでに用意されている名前変換クラスが利用できる。データのコピーを前述した「EventData」クラスの「GetData」メソッドで行うには、次のコードのようになる。
public static IList<EventData> GetData()
{
// データベースからデータを取得してくる
// (ここではその代わりに、仮のデータをハードコーディングしている)
EventDataSet ds = new EventDataSet();
EventDataSet.EventTableDataTable dt = ds.EventTable;
dt.AddEventTableRow(1, event_title: "とっておきイベント☆その1",
date_time: new DateTime(2015, 3, 31, 15, 0, 0));
dt.AddEventTableRow(2, event_title: "とっておきイベント☆その2",
date_time: new DateTime(2015, 4, 30, 0, 30, 0));
dt.AddEventTableRow(3, event_title: "とっておきイベント☆その3",
date_time: new DateTime(2015, 5, 26, 10, 0, 0));
// AutoMapperを使って、型付きデータセットをこのクラスのリストに変換する
// まず、プロパティ名の名前変換ルールを指定する
AutoMapper.Mapper.Initialize(cfg =>
{
// 変換元のプロパティ名が、小文字をアンダースコアでつなげた形式
// (例:「event_title」)であるときに…、
cfg.SourceMemberNamingConvention
= new AutoMapper.LowerUnderscoreNamingConvention();
// 変換先のプロパティ名として、パスカル形式(例:「EventTitle」)を探させる
cfg.DestinationMemberNamingConvention
= new AutoMapper.PascalCaseNamingConvention();
});
// 後は、基本編で説明したようにCreateMapしてMapするだけ
AutoMapper.Mapper.CreateMap<EventDataSet.EventTableRow, EventData>();
return AutoMapper.Mapper.Map<List<EventData>>(dt);
}
Public Shared Function GetData() As IList(Of EventData)
' データベースからデータを取得してくる
' (ここではその代わりに、仮のデータをハードコーディングしている)
Dim ds As EventDataSet = New EventDataSet()
Dim dt As EventDataSet.EventTableDataTable = ds.EventTable
dt.AddEventTableRow(1, event_title:="とっておきイベント☆その1",
date_time:=New DateTime(2015, 3, 31, 15, 0, 0))
dt.AddEventTableRow(2, event_title:="とっておきイベント☆その2",
date_time:=New DateTime(2015, 4, 30, 0, 30, 0))
dt.AddEventTableRow(3, event_title:="とっておきイベント☆その3",
date_time:=New DateTime(2015, 5, 26, 10, 0, 0))
' AutoMapperを使って、型付きデータセットをこのクラスのリストに変換する
' まず、プロパティ名の名前変換ルールを指定する
AutoMapper.Mapper.Initialize(
Sub(cfg)
' 変換元のプロパティ名が、小文字をアンダースコアでつなげた形式
' (例:「event_title」)であるときに…、
cfg.SourceMemberNamingConvention _
= New AutoMapper.LowerUnderscoreNamingConvention()
' 変換先のプロパティ名として、パスカル形式(例:「EventTitle」)を探させる
cfg.DestinationMemberNamingConvention _
= New AutoMapper.PascalCaseNamingConvention()
End Sub)
' 後は、基本編で説明したようにCreateMapしてMapするだけ
AutoMapper.Mapper.CreateMap(Of EventDataSet.EventTableRow, EventData)()
Return AutoMapper.Mapper.Map(Of List(Of EventData))(dt)
End Function
実行結果
別途公開のサンプルを実行している様子を次の画像に示す。このサンプルではAutoMapperを使って、「EventTable」クラス→「EventData」クラス→「EventDataForDisplay」クラスという2段階のデータコピーを行っている(後半のコピー方法は「独自マッピング編」を参照)。
まとめ
本稿ではINamingConventionインターフェースの実装方法を説明しなかったが、正規表現が分かっていればそれほど難しくない。本稿の例とは異なる名前変換ルールの場合はINamingConventionインターフェースの実装が必要になるかもしれないが、ぜひチャレンジしてみてほしい。
AutoMapperにはまだ多くの機能があり、適用範囲はさらに広い。そのドキュメントは英語だが、読み解いてみる価値はあるだろう。
カテゴリ:オープンソース・ライブラリ 処理対象:データ型
カテゴリ:C# 処理対象:データ型
カテゴリ:Visual Basic 処理対象:データ型
関連TIPS:AutoMapperを使ってオブジェクト間のデータコピーを自動化するには?(基本編)
関連TIPS:AutoMapperを使ってオブジェクト間のデータコピーを自動化するには?(独自マッピング編)
■この記事と関連性の高い別の.NET TIPS
Copyright© Digital Advantage Corp. All Rights Reserved.