iT邦幫忙

DAY 7
6

以Asp .Net MVC 5 為基礎,建立自己的程式開發框架系列 第 7

AutoMapper 介紹 - 簡單化Entity和ViewModel之間的轉換

  • 分享至 

  • xImage
  •  

在上一篇介紹完ViewModel的好處之後,留下的問題是,ViewModel雖然有帶來好處,但是ViewModel和實際Entity之間的對應其實是很麻煩的一件事情,那麼我們如何能夠簡化對應的邏輯呢?

這時候就是AutoMapper這個套件入場的時候。

同步發表於我的部落格:http://alantsai2007.blogspot.tw/2014/09/ithome-07-automapper-entityviewmodel.html

AutoMapper介紹

AutoMapper

AutoMapper的目的就是要解決無聊的左邊資料倒到右邊。我們舉一個例子,如果是在早期的Asp .Net Webform,當一個Form進來的時候,我們常常會需要:

// psuedo 程式碼

string name = Request.Form["name"];

string age = Request.Form["age"];

.....

這些其實很無聊但是又不得不做。在Mvc裡面Model Binding解決了這個問題。

但是如果用ViewModel,還是有這個問題,因此就有人開發了AutoMapper。

AutoMapper簡單來說,使用步奏就是:

  1. 定義好兩個Class之間轉換的邏輯
  2. 把object透過AutoMapper轉換成為另外一個形態的object

測試情景

在介紹AutoMapper之前,我們先設定好我們的測試情景。假設我們有一個DB,裡面一個Table叫做Post,代表著一個部落格網站裡面所擁有的文章。Entity可能如下:

public partial class Post
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string PostContent { get; set; }
    public System.DateTime CreateDateTime { get; set; }
    public Nullable<System.DateTime> LastModifyDateTime { get; set; }
}

那用這個Entity,透過Mvc的Scaffolding,我們建立出基本的CRUD頁面。那Scaffolding出來的CRUD,Entity會是預設的ViewModel。透過上一篇我們知道使用Entity做ViewModel的壞處是什麼,因此我們會開始針對CRUD建立對應的ViewModel。

Index頁面的ViewModel

沒有AutoMapper的做法

假設我說,我們的Index頁面不要顯示CreateDateTime和LastModifyDateTime。我們當然可以只改View的HtmlHelper,不要顯示這兩個Property,不過等一下我們就知道為什麼用ViewModel更好。

因此,我建立了一個如下的ViewModel:

public class IndexViewModel
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string PostContent { get; set; }
}

在沒有時候AutoMapper的情況下,我們會需要做:

List<IndexViewModel> viewModel = new List<IndexViewModel>();
 
foreach (var item in db.Post.ToList())
{
    viewModel.Add(new IndexViewModel()
    {
        Id = item.Id,
        PostContent = item.PostContent,
        Title = item.Title
    });
}

想想,我們才3個property而已就佔了這麼多行數的程式碼,如果有20個property不就很恐怖。而且,明明兩邊的Property都一樣,他不能夠自己對應嗎?

用上AutoMapper

如果用上AutoMapper,程式碼變成:

// 定義Post是來源的Class而IndexViewModel是最後結果
Mapper.CreateMap<Post, IndexViewModel>();
 
// 把List<Post>轉成List<IndexViewModel>
var viewModel2 = Mapper.Map<List<IndexViewModel>>(db.Post.ToList());

用了AutoMapper總共有2個好處:

[*]程式碼變少了:本來要11行,現在只要2行
[*]程式碼的可讀性提高:本來還會跑迴圈,如果property參數多了,看起來不是那麼直覺。但是用AutoMapper,非常直覺知道是在轉換Model形態

可能你會在想,為什麼我們什麼都沒有設定,AutoMapper就知道對應欄位是什麼?其實因為AutoMapper會自動把一樣的Property名字作為一對,以我們的例子,Property都一樣,因此我不需要做額外設定。

需求變更

假設,今天我們Index頁面的需求變了,變成在Index頁面的每一筆Post需要顯示最後一次修改時間距離建立時間過了幾天,如果沒有最後一次修改時間,就用今天日期,這個時候,我們就需要手動設定Property的值要如何產生。

首先我們在IndexViewModel增加一個Property:

?

1

public double HowManyDayPass { get; set; }

然後AutoMapper的轉換邏輯改一下:

...
Mapper.CreateMap<Post, IndexViewModel>()
    .ForMember(member => member.HowManyDayPass, 
    opt => opt.MapFrom(x => x.LastModifyDateTime == null ? 
        (DateTime.Now - x.CreateDateTime).TotalDays : 
        (x.LastModifyDateTime.Value - x.CreateDateTime).TotalDays));
....

應該很好懂,設定member.HowManyDayPass這個property的值要從:如果沒有最後修改時間,就用今天日期減掉建立日期。不然就用 最後修改時間減掉建立時間。

Queryable Extension - 減少Sql Select的欄位

AutoMapper其實有提供一個IQueryable的Extension方法,讓我們EF在對DB下Sql的時候,只下我們需要的部分。我們直接看例子:

//需要先加Using

using AutoMapper.QueryableExtensions;

...

var projectIQueryable = (db.Post.Project().To<IndexViewModel>()).ToList(); // AutoMapper QuerableExtension

var normalIQuerable = db.Post.AsQueryable().ToList();

...

用了AutoMapper產生的Sql(左邊)和沒用的產生Sql(右邊)

有這樣的效果是因為EF用了LazyLoading。

Edit頁面的ViewModel

這邊我快速介紹一下修改頁面的ViewModel。

以我們的例子,只允許修改Title和PostContent而已,同時,LastModifyDateTime應該要自動使用系統時間。

這個時候我們的EditViewModel就會是:

public class EditViewModel
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string PostContent { get; set; }
}

AutoMapper的設定:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(EditViewModel post)
{
    if (ModelState.IsValid)
    {
        // 建立Mapping邏輯,並且LastModifyDateTime使用系統時間
        Mapper.CreateMap<EditViewModel, Post>().
            ForMember(member => member.LastModifyDateTime, 
            opt => opt.UseValue(DateTime.Now));
 
        Post postEntity = db.Post.Find(post.Id);
 
        // 只更新ViewModel的部分到Entity
        Mapper.Map(post, postEntity);
             
        db.Entry(postEntity).State = EntityState.Modified;
        db.SaveChanges();
        return RedirectToAction("Index");
    }
    return View(post);
}

結語

到目前為止我們介紹了AutoMapper的基本概念,和基本的使用。相信在整體使用上面來說,比自己手動轉換來的方便。但是,假設以我們目前介紹的方式去做,AutoMapper還是有點不好用。

其實最主要的問題是在:設定對應邏輯的地方。雖然說AutoMapper簡化了很多設定,可是還是要設定啊,這些設定到底要放在那裡?想像一下,假設我們每一個View有個 ViewModel,一個功能至少有CRUD,很快我們就有一堆ViewModel,這些設定就變的不好維護。雖然說AutoMapper簡化了很多設定,可是還是要設定啊,這些設定到底要放在那裡?想像一下,假設我們每一個View有個 ViewModel,一個功能至少有CRUD (4個ViewModel),很快我們就有一堆ViewModel,這些設定就變的不好維護

因此在下一篇,我們會建立一些功能,讓我們的在開發上面使用起來更方便和好維護。


上一篇
ViewModel的重要性
下一篇
框架簡化建立AutoMapper對應的設定
系列文
以Asp .Net MVC 5 為基礎,建立自己的程式開發框架30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
superpucy
iT邦新手 3 級 ‧ 2014-09-29 23:15:11

等好久喔(敲碗)

我要留言

立即登入留言