前言:前篇搭建了下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相互转换(上)。虽然不少高级用法在实际项目中很难用上,但多了解一点彷佛也并无坏处。