前言:前篇搭建了下WCF的代碼,就提到了DTO的概念,對於爲何要有這麼一個DTO的對象,上章可能對於這點不太詳盡,在此不厭其煩再來提提它的做用:html
既然咱們要使用DTO,那麼有一件事咱們就非作不可了,咱們從領域層獲得的是領域Model,如何把領域Model轉換成只帶有數據屬性的DTO傳遞到前臺呢?又或者咱們從前臺提交一個DTO對象,如何將DTO轉換成領域Model而提交到後臺呢?這個時候就須要咱們的對象映射工具,目前市面上對象映射工具較多,但博主最熟悉的仍是Automapper,這章就來分享下Automapper的使用。安全
DDD領域驅動設計初探系列文章:網絡
Automapper是一個object-object mapping(對象映射)工具,通常主要用於兩個對象之間數據映射和交換。固然你也能夠本身經過反射去寫對象的映射,對於簡單的兩個屬性間的數據轉換,確定沒什麼問題。可是若是遇到某些複雜的數據轉換,好比指定某一個對象的某個屬性映射到另外一個對象的某一個屬性,這種狀況若是咱們本身手動映射,恐怕就有點麻煩了吧。既然咱們有現成的工具,爲何不用呢?app
向項目中添加AutoMapper的引用有兩種方式:分佈式
在須要使用AutoMapper的項目文件上面右鍵→管理Nuget程序包,打開Nuget界面,搜索Automapper,而後安裝第一個便可。以下圖:工具
點擊Visual Studio的工具菜單→程序包管理控制檯,而後選擇須要安裝Automapper的項目(下圖中的默認項目),最後在控制檯裏面輸入命令「Install-Package AutoMapper」命令便可按照Automapper包:post
AutoMapper使用起來仍是比較簡單的,最簡單的用法你只須要兩句話:this
var oMenu = new TB_MENU() { MENU_NAME="權限管理", MENU_LEVEL="1" };
Mapper.CreateMap<TB_MENU, DTO_TB_MENU>(); var oDto = Mapper.Map<DTO_TB_MENU>(oMenu);
首先建立映射,而後傳入須要映射的對象執行映射。相信在項目中使用過AutoMapper的緣由確定也寫過相似這樣的AutoMapperHelper url
/// <summary> /// AutoMapper幫助類 /// </summary> public static class AutoMapperHelper { /// <summary> /// 單個對象映射 /// </summary> public static T MapTo<T>(this object obj) { if (obj == null) return default(T); Mapper.CreateMap(obj.GetType(), typeof(T)); return Mapper.Map<T>(obj); } /// <summary> /// 集合列表類型映射 /// </summary> public static List<TDestination> MapToList<TSource, TDestination>(this IEnumerable<TSource> source) { Mapper.CreateMap<TSource, TDestination>(); return Mapper.Map<List<TDestination>>(source); } }
固然,這是最簡單的用法,稍微複雜點的用法咱們在後面慢慢介紹。spa
前面說了,對於指定某一個對象的某個屬性映射到另外一個對象的某一個屬性,這種場景,咱們先來看看下面代碼:
public partial class TB_USERS : BaseEntity { public string USER_ID { get; set; } public string USER_NAME { get; set; } public string USER_PASSWORD { get; set; } public string FULLNAME { get; set; } public string DEPARTMENT_ID { get; set; } public virtual TB_DEPARTMENT TB_DEPARTMENT { get; set; } //...後面確定還有其餘領域行爲 }
public partial class TB_DEPARTMENT : BaseEntity { public string DEPARTMENT_ID { get; set; } public string NAME { get; set; } }
領域層有這兩個實體model,而後咱們須要獲得下面的DTO_TB_USERS這一個對象
public class DTO_TB_USERS { [DataMember] public string USER_ID { get; set; } [DataMember] public string USER_NAME { get; set; } [DataMember] public string USER_PASSWORD { get; set; } [DataMember] public string FULLNAME { get; set; } [DataMember] public string DEPARTMENT_ID { get; set; }
[DataMember]
public string DEPARTMENT_NAME { get; set; }
}
這個時候DTO_TB_USERS這個對象的屬性分佈在其餘兩個領域實體裏面,咱們看看AutoMapper如何解決:
var oDomainUser = userRepository.Entities.FirstOrDefault(); var map = Mapper.CreateMap<TB_USERS, DTO_TB_USERS>(); map.ForMember(d => d.DEPARTMENT_NAME, opt => opt.MapFrom(x => x.TB_DEPARTMENT.NAME)); var oDto = Mapper.Map<TB_USERS, DTO_TB_USERS>(oDomainUser);
經過上面的代碼,ForMember()方法會指定哪一個字段轉換爲哪一個字段,這樣就完美的將對象的層級結構由二級變成了一級(即將TB_USERS下面TB_DEPARTMENT對象的NAME值轉換成了DTO_TB_USERS的DEPARTMENT_NAME值)。除此以外,Automapper裏面還能夠經過ForMember幫咱們作其餘不少咱們想不到的事情,好比能夠設置某個屬性值保留初始值,只須要經過
map.ForMember(d => d.DEPARTMENT_NAME, opt => opt.Ignore());
這一句就幫咱們搞定。
還記得咱們在倉儲裏面封裝了傳遞lamada表達式的查詢方法麼?試想,若是咱們在Web層裏面也但願傳遞lamada表達式去後臺查詢,那麼這個時候就有點問題了,由於咱們Web裏面只能訪問DTO的Model,因此只能傳入DTO Model的lamada,而咱們倉儲裏面須要傳入的是領域Model的lamada,那麼問題就來了,這兩個lamada表達式之間必須存在一個轉換關係,試想,這些東西若是讓咱們手動去處理,仍是有難度的吧!還好,咱們神奇的Automapper替咱們想到了。它可以幫咱們將DTO的lamada轉換成領域Model的lamada,來看看代碼吧:
[Import] public IUserRepository userRepository { get; set; } public virtual IList<DTO> Find(Expression<Func<DTO, bool>> selector) { //獲得從Web傳過來和DTOModel相關的lamaba表達式的委託 Func<DTO, bool> match = selector.Compile(); //建立映射Expression的委託 Func<T, DTO> mapper = AutoMapper.QueryableExtensions.Extensions.CreateMapExpression<T, DTO>(Mapper.Engine).Compile(); //獲得領域Model相關的lamada Expression<Func<T, bool>> lamada = ef_t => match(mapper(ef_t)); List<T> list = userRepository.Find(lamada).ToList(); return Mapper.Map<List<T>, List<DTO>>(list); }
上面方法完美實現了兩種lamada之間的轉換,但根據博主的使用經歷,這種轉換對屬性的類型有很嚴格的要求,必須保證領域model和DTO的Model同一個屬性的類型徹底相同,不然容易報異常。使用的時候須要注意。實際使用的方法:
public List<DtoModel> GetDtoByLamada<DtoModel,DomainModel>(IRepository<DomainModel> oRepository, Expression<Func<DtoModel, bool>> selector = null) where DomainModel : AggregateRoot where DtoModel : DTO_BASEMODEL { if (selector == null) { var lstDomainModel = oRepository.Entities.ToList(); return Mapper.Map<List<DomainModel>, List<DtoModel>>(lstDomainModel); } //獲得從Web傳過來和DTOModel相關的lamaba表達式的委託 Mapper.CreateMap<DtoModel, DomainModel>(); Mapper.CreateMap<DomainModel, DtoModel>(); Func<DtoModel, bool> match = selector.Compile(); //建立映射Expression的委託 Func<DomainModel, DtoModel> mapper = AutoMapper.QueryableExtensions.Extensions.CreateMapExpression<DomainModel, DtoModel>(Mapper.Engine).Compile(); //獲得領域Model相關的lamada Expression<Func<DomainModel, bool>> lamada = ef_t => match(mapper(ef_t)); List<DomainModel> list = oRepository.Find(lamada).ToList(); return Mapper.Map<List<DomainModel>, List<DtoModel>>(list); }
調用
public class PowerManageWCFService :BaseService, IPowerManageWCFService { #region Fields [Import] private IUserRepository userRepository { get; set; } [Import] private IDepartmentRepository departmentRepository { get; set; } [Import] private IRoleRepository roleRepository { get; set; } [Import] private IMenuRepository menuRepository { get; set; } #endregion #region Constust public PowerManageWCFService() { //註冊MEF Regisgter.regisgter().ComposeParts(this); } #endregion #region WCF服務接口實現 public List<DTO_TB_USERS> GetUsers(Expression<Func<DTO_TB_USERS, bool>> selector) { return base.GetDtoByLamada<DTO_TB_USERS, TB_USERS>(userRepository, selector); } public List<DTO_TB_DEPARTMENT> GetDepartments(Expression<Func<DTO_TB_DEPARTMENT, bool>> selector) { return base.GetDtoByLamada<DTO_TB_DEPARTMENT, TB_DEPARTMENT>(departmentRepository, selector); } public List<DTO_TB_ROLE> GetRoles(Expression<Func<DTO_TB_ROLE, bool>> selector) { return base.GetDtoByLamada<DTO_TB_ROLE, TB_ROLE>(roleRepository, selector); } public List<DTO_TB_MENU> GetMenus(Expression<Func<DTO_TB_MENU, bool>> selector) { return base.GetDtoByLamada<DTO_TB_MENU, TB_MENU>(menuRepository, selector); } #endregion }
除了上面介紹的Automapper的幾個簡單使用,其餘還有其餘的一些用法。
網上不少介紹DataReader對象和實體類之間的映射:
using (IDataReader reader = db.ExecuteReader(command)) { if (reader.Read()) { return AutoMapper.Mapper.DynamicMap<Product>(reader); } }
至此,AutoMapper的常見用法基本分享完了,至於更高級的用法,有興趣能夠看看蟋蟀兄的【AutoMapper官方文檔】DTO與Domin Model相互轉換(上)。雖然不少高級用法在實際項目中很難用上,但多瞭解一點彷佛也並無壞處。