AutoMapperを使うことで、オブジェクト同士を簡単にマッピングできます。
そんな中、ArrayListをオブジェクトへマッピングすることがあったため、対応したときのメモを残します。
目次
環境
- C# 8.0
- .NET Core 3.1
- AutoMapper 9.0.0
今回は
new ArrayList {"1", "すいか", "夏", 1000};
のようなArrayListを
public class Fruit { public int Id { get; set; } public string Name { get; set; } public string Season { get; set; } public decimal UnitPrice { get; set; } }
へとマッピングすることを考えます。
ArrayListをオブジェクトへマッピング
以下のstackoverflowを参考にしました。
c# - How to map an array to multiple properties using AutoMapper? - Stack Overflow
ただ、AutoMapperからstatic APIが取り除かれたため、それに対応して実装します。
Removing the static API from AutoMapper · Los Techies
まずはマッピングです。
// ArrayListの要素をプロパティにマッピング var config = new MapperConfiguration(c => c.CreateMap<ArrayList, Fruit>() .ForMember(d => d.Id, o => o.MapFrom(s => s[0])) .ForMember(d => d.Name, o => o.MapFrom(s => s[1])) .ForMember(d => d.Season, o => o.MapFrom(s => s[2])) .ForMember(d => d.UnitPrice, o => o.MapFrom(s => s[3])) ); // インスタンス化 var mapper = config.CreateMapper();
続いて変換します。Mapメソッドに、変換先のクラスを指定します。
// 元データ var source = new ArrayList {"1", "すいか", "夏", 1000}; // 変換 var dest = mapper.Map<Fruit>(source);
結果を確認します。マッピングできています。
Console.WriteLine("ArrayList to Fruit"); Console.WriteLine( $"ID: {dest.Id}, Name: {dest.Name}, Season: {dest.Season}, UnitPrice: {dest.UnitPrice}"); // => // ArrayList to Fruit // ID: 1, Name: すいか, Season: 夏, UnitPrice: 1000
ListをListへマッピング
AutoMapperではListなどもマッピングできますので、試してみます。
Lists and Arrays — AutoMapper documentation
マッピング方法は先ほどと変わりません。
var config = new MapperConfiguration(c => c.CreateMap<ArrayList, Fruit>() .ForMember(d => d.Id, o => o.MapFrom(s => s[0])) .ForMember(d => d.Name, o => o.MapFrom(s => s[1])) .ForMember(d => d.Season, o => o.MapFrom(s => s[2])) .ForMember(d => d.UnitPrice, o => o.MapFrom(s => s[3])) ); var mapper = config.CreateMapper();
Map()の使い方が変わります。
Map()の第一引数に変換元のクラスを、第二引数に変換先のクラスをそれぞれ指定します。
// データ var source = new List<ArrayList> { new ArrayList {"1", "すいか", "夏", 1000}, new ArrayList {"2", "りんご", "秋", 100}, new ArrayList {"3", "みかん", "冬", 150} }; // 変換 var dest = mapper.Map<List<ArrayList>, List<Fruit>>(source);
結果です。List同士のマッピングもできました。
Console.WriteLine("List<ArrayList> to List<Fruit>"); foreach (var fruit in dest) { Console.WriteLine( $"ID: {fruit.Id}, Name: {fruit.Name}, Season: {fruit.Season}, UnitPrice: {fruit.UnitPrice}"); } // => // List<ArrayList> to List<Fruit> // ID: 1, Name: すいか, Season: 夏, UnitPrice: 1000 // ID: 2, Name: りんご, Season: 秋, UnitPrice: 100 // ID: 3, Name: みかん, Season: 冬, UnitPrice: 150
双方向マッピング
AutoMapperでは、
を同時に設定できます。
逆方向をマッピングするには ReverseMap()
を使います。
Reverse Mapping and Unflattening — AutoMapper documentation
通常同じプロパティへ自動的にマッピングしますが、今回はArrayListへのマッピングとなるため、手動でマッピングする必要があります。
そのため、 ConstructUsing()
を使って、マッピングを定義しています。
https://docs.automapper.org/en/stable/Queryable-Extensions.html#custom-destination-type-constructors
var config = new MapperConfiguration(c => c.CreateMap<ArrayList, Fruit>() .ForMember(d => d.Id, o => o.MapFrom(s => s[0])) .ForMember(d => d.Name, o => o.MapFrom(s => s[1])) .ForMember(d => d.Season, o => o.MapFrom(s => s[2])) .ForMember(d => d.UnitPrice, o => o.MapFrom(s => s[3])) // 逆マップ .ReverseMap() .ConstructUsing(x => new ArrayList { x.Id, x.Name, x.Season, x.UnitPrice }) ); var mapper = config.CreateMapper();
使い方です。
// 結果 var reverseSource = new Fruit {Id = 1, Name = "すいか", Season = "夏", UnitPrice = 1000}; // 変換 var reverseDst = mapper.Map<ArrayList>(reverseSource);
結果確認です。
Console.WriteLine( $"ID: {reverseDst[0]}, Name: {reverseDst[1]}, Season: {reverseDst[2]}, UnitPrice: {reverseDst[3]}"); // => // 逆方向 // ID: 1, Name: すいか, Season: 夏, UnitPrice: 1000
双方向マッピング (List)
Listも可能です。
定義は変わりません。
var config = new MapperConfiguration(c => c.CreateMap<ArrayList, Fruit>() .ForMember(d => d.Id, o => o.MapFrom(s => s[0])) .ForMember(d => d.Name, o => o.MapFrom(s => s[1])) .ForMember(d => d.Season, o => o.MapFrom(s => s[2])) .ForMember(d => d.UnitPrice, o => o.MapFrom(s => s[3])) // 逆マップ .ReverseMap() .ConstructUsing(x => new ArrayList { x.Id, x.Name, x.Season, x.UnitPrice }) ); var mapper = config.CreateMapper();
使い方が変わります。
Map()の第一引数が変換元のList、第二引数が変換先のListを指定します。
var reverseSourceList = new List<Fruit> { new Fruit {Id = 1, Name = "すいか", Season = "夏", UnitPrice = 1000}, new Fruit {Id = 2, Name = "りんご", Season = "秋", UnitPrice = 100}, new Fruit {Id = 3, Name = "みかん", Season = "冬", UnitPrice = 150} }; // 変換 var reverseDestList = mapper.Map<List<Fruit>, List<ArrayList>>(reverseSourceList); // 結果 foreach (var dst in reverseDestList) { Console.WriteLine( $"ID: {dst[0]}, Name: {dst[1]}, Season: {dst[2]}, UnitPrice: {dst[3]}"); } // => // 逆方向List // ID: 1, Name: すいか, Season: 夏, UnitPrice: 1000 // ID: 2, Name: りんご, Season: 秋, UnitPrice: 100 // ID: 3, Name: みかん, Season: 冬, UnitPrice: 150
マッピング設定をクラス化 (Profile)
Profileを使うことで、マッピング設定をクラス化できます。 https://docs.automapper.org/en/stable/Configuration.html#profile-instances
先ほどの双方向マッピングをクラスにします。
public class AutoMapperProfile : Profile { public AutoMapperProfile() { CreateMap<ArrayList, Fruit>() .ForMember(d => d.Id, o => o.MapFrom(s => s[0])) .ForMember(d => d.Name, o => o.MapFrom(s => s[1])) .ForMember(d => d.Season, o => o.MapFrom(s => s[2])) .ForMember(d => d.UnitPrice, o => o.MapFrom(s => s[3])) // 逆マップ .ReverseMap() .ConstructUsing(x => new ArrayList { x.Id, x.Name, x.Season, x.UnitPrice }); } }
設定クラスを読み込みます。
AutoMapperでは明示的に指定する他、アセンブリから自動で検索することもできます。
http://docs.automapper.org/en/stable/Configuration.html#assembly-scanning-for-auto-configuration
今回はアセンブリを指定してみます。
// Profileを検索 var config = new MapperConfiguration(c => c.AddMaps(new [] { "MyApp", }) ); // インスタンス化 var mapper = config.CreateMapper();
あとは今までと同じです。
ここでは正方向だけ示しますが、逆方向も動作します。
Console.WriteLine("正方向 (Profile使用)"); var source = new ArrayList {"1", "すいか", "夏", 1000}; var dest = mapper.Map<Fruit>(source); Console.WriteLine( $"ID: {dest.Id}, Name: {dest.Name}, Season: {dest.Season}, UnitPrice: {dest.UnitPrice}"); // => // 正方向 (Profile使用) // ID: 1, Name: すいか, Season: 夏, UnitPrice: 1000