【ASP.NET】面倒なオブジェクトマッピングはAutoMapperにやらせよう

AutoMapperって?

オブジェクトの詰め替えをしてくれる.NETのライブラリです. マッパーを使うことでオブジェクトの詰め替えをその都度記述する必要がなくなるのでコーディング量を減らすことが出来ます. また、オブジェクト間のプロパティの対応関係を局所化することが出来るので、修正箇所が限定されます.

AutoMapperの取得

NuGetからAutoMapperを取得してください.

AutoMapperを使わない場合

MVCではよくEntityをViewModelに詰め替えることがあるかと思います. 例えばDBから取得したユーザをViewModelに詰め替えて返す場合こんな感じになるかと思います.

public class UserController
{
    public ActionResult Detail(string name)
    {
        // DBからUSERを取得したとする
        var user = Service.FindUserByName(name);
        // USER -> UserViewModel
        var viewModel = new UserViewModel()
        {
            UserId = user.USER_ID;
            UserName = user.USER_NAME;
            Tel = user.TEL;
            Mail = user.MAIL;
        }
        return View(viewModel);
    }
}

Entity

public class USER
{
    public string USER_ID { get; set; }
    public string USER_NAME { get; set; }
    public string TEL { get; set; }
    public string MAIL { get; set; }
}

ViewModel

public class UserViewModel
{
    public string UserId { get; set; }
    public string UserName { get; set; }
    public string Tel { get; set; }
    public string Mail { get; set; }
}

先ほどのControllerに一覧を返すメソッドを追加した場合はこんな感じに.

public class UserController
{
    public ActionResult Detail(string name)
    {
        // DBからUSERを取得したとする
        var user = Service.FindUserByName(name);
        // USER -> UserViewModel
        var viewModel = new UserViewModel()
        {
            UserId = user.USER_ID;
            UserName = user.USER_NAME;
            Tel = user.TEL;
            Mail = user.MAIL;
        }
        return View(viewModel);
    }

    public ActionResult List()
    {
        // DBからUSER一覧を取得したとする
        var users = Service.FindUsers();
        var viewModels = new List<UserViewModel>();
        foreach(var user in users)
        {
            // USER -> UserViewModel
            var viewModel = new UserViewModel()
            {
                UserId = user.USER_ID;
                UserName = user.USER_NAME;
                Tel = user.TEL;
                Mail = user.MAIL;
            };
            viewModels.Add(viewModel);
        }         
        return View(viewModels);
    }    
}

詰め替えまくってますね(笑). 例えば「USERにFAXも追加しよう」となったとき,詰め替えている箇所全て修正する必要がありますね.

public class UserController
{
    public ActionResult Detail(string name)
    {
        // DBからUSERを取得したとする
        var user = Service.FindUserByName(name);
        // USER -> UserViewModel
        var viewModel = new UserViewModel()
        {
            UserId = user.USER_ID;
            UserName = user.USER_NAME;
            Tel = user.TEL;
            // 追加
            Fax = user.FAX;
            Mail = user.MAIL;
        }
        return View(viewModel);
    }

    public ActionResult List()
    {
        // DBからUSER一覧を取得したとする
        var users = Service.FindUsers();
        var viewModels = new List<UserViewModel>();
        foreach(var user in users)
        {
            // USER -> UserViewModel
            var viewModel = new UserViewModel()
            {
                UserId = user.USER_ID;
                UserName = user.USER_NAME;
                Tel = user.TEL;
                // 追加
                Fax = user.FAX;
                Mail = user.MAIL;
            };
            viewModels.Add(viewModel);
        }         
        return View(viewModels);
    }    
}

いちいち該当箇所を探すのは面倒ですよね. そこでAutoMapperです.

AutoMapperConfigの作成

AutoMapperConfig.csにマッピング情報を追加します. App_Start配下に用意してくださいね.

using AutoMapper;

public class AutoMapperConfig
{
    public static void RegisterAutoMappings()
    {
        Mapper.Initialize(cfg =>
        {
            // USER -> UserViewModel
            cfg.CreateMap<USER, UserViewModel>()
                .ForMember(d => d.UserId, o => o.MapFrom(s => s.USER_ID))
                .ForMember(d => d.UserName, o => o.MapFrom(s => s.USER_NAME))
                .ForMember(d => d.Tel, o => o.MapFrom(s => s.TEL))
                .ForMember(d => d.Mail, o => o.MapFrom(s => s.MAIL));
        });
    }
}

Global.asaxへの登録

public class Global : HttpApplication
{
    void Application_Start(object sender, EventArgs e)
    {
        AutoMapperConfig.RegisterAutoMappings();
    }
}

AutoMapperの利用

あとはControllerを修正します.

using AutoMapper;

public class UserController
{
    public ActionResult Detail(string name)
    {
        // DBからUSERを取得したとする
        var user = Service.FindUserByName(name);
        // USER -> UserViewModel
        var viewModel = Mapper.Map<UserViewModel>(user);
        return View(viewModel);
    }

    public ActionResult List()
    {
        // DBからUSER一覧を取得したとする
        var users = Service.FindUsers();
        var viewModels = new List<UserViewModel>();
        foreach(var user in users)
        {
            // USER -> UserViewModel
            var viewModel = Mapper.Map<UserViewModel>(user);
            viewModels.Add(viewModel);
        }         
        return View(viewModels);
    }    
}

超すっきりしましたね! これで「USERにFAXも追加しよう」となったときはAutoMapperConfigを修正するだけで済みます.

using AutoMapper;

public class AutoMapperConfig
{
    public static void RegisterAutoMappings()
    {
        Mapper.Initialize(cfg =>
        {
            // USER -> UserViewModel
            cfg.CreateMap<USER, UserViewModel>()
                .ForMember(d => d.UserId, o => o.MapFrom(s => s.USER_ID))
                .ForMember(d => d.UserName, o => o.MapFrom(s => s.USER_NAME))
                .ForMember(d => d.Tel, o => o.MapFrom(s => s.TEL))
                .ForMember(d => d.Fax, o => o.MapFrom(s => s.FAX))
                .ForMember(d => d.Mail, o => o.MapFrom(s => s.MAIL));
        });
    }
}

最後に

AutoMapperConfigにMapping情報を書けばMapperを呼び出すだけで簡単にマッピングされるようになります. マッピング情報がAutoMapperConfig一箇所にまとまるのでコードの見通しも良くなりメンテも捗るはずです.