在实际开发中,模型每每被划分为视图模型和业务模型两部分,视图模型靠近视图,业务模型靠近业务,可是在具体编码上,它们之间并非隔离的。mvc
模型大多数时候都是用来传递数据的。然而即便在传递数据这一点上,也能够看出,视图须要的模型更加灵活一点,由于视图变化性更大,而处理业务的模型更加稳定一些。所以,在实际开发中,每每有视图模型和业务模型的区分。在实际开发中,为了体现逻辑的分离,每每是视图模型和业务模型分别定义。app
例如,在传统三层开发中,咱们定义的实体类,能够看做是业务模型的定义,在开发视图时,可能会开发各类和User相关的视图,如用户注册视图、用户状态列表视图、登陆视图,这时候每每根据视图构造不一样的模型,由于它们依赖的数据都不太同样,这些模型可称为视图模型。 框架
这种方式的好处就是可让业务模型比较稳定,不会由于视图的变动而频繁地去修改业务模型。可是从另外一方面来看,这种分离的方式也会有坏处。坏处之一就是带来不少重复的代码。另外一个坏处就是在动做方法中,模型自动绑定完视图模型后,还须要再手动编码把视图模型映射到业务模型,带来额外的工做量。 asp.net
在小型项目开发中,为了简化代码,并不会彻底区分视图模型和业务模型,但不意味着实际开发都这样作。 工具
AutoMapper 是一个可以实现由一个对象到另外一个对象间映射的轻量级框架,利用AutoMapper框架可让咱们从视图模型到业务模型转换的繁琐工做中解放出来。编码
AutoMapper 框架是基于约定的。 spa
新建asp.net mvc 项目 AutoMapperExample,点击 工具→NuGetB包管理器→管理解决方案的NuGet程序包,在弹出的界面中,搜索autoMapper,在搜索出的程序包而后安装。.net
咱们以常见的用户角色案例来说解AutoMapper 的使用。对象
首先咱们建立角色和用户两个类。代码以下。其中User类中的Role属性是对Role类的引用。 开发
//角色 public class Role { public int Id { get; set; } public string Name { get; set; } } //用户 public class User { public int Id { get; set; } public string LoginName { get; set; } public string LoginPwd { get; set; } public string Phone { get; set; } public int RoleId { get; set; } public Role Role { get; set; } } |
在建立注册用户时所需的视图模型。代码以下。
//注册视图中使用的视图模型 public class RegUserVM { public int Id { get; set; } public string LoginName { get; set; } public string LoginPwd { get; set; } public string ConfirmPwd { get; set; } public string Phone { get; set; } public int RoleId { get; set; } } |
在视图模型中增长了ConfirmPwd属性,并将Role属性去除。
接下来咱们在程序中新建AutoMapper文件夹,用于存放对象映射的类,该文件夹下新建类AutoMapperConfig,该类处理全部的对象映射,即从一个对象转化到另外一个对象。如示例1所示。
示例1
using AutoMapper; //引用命名空间 public class AutoMapperConfig { public static void Config() { Mapper.Initialize(cfg => cfg.CreateMap<RegUserVM, User>()); } } |
Mapper.Initialize()方法执行AutoMapper的初始化操做,此操做在一个应用程序中只能执行一次.在初始化方法中能够初始化映射中的任何操做。
CreateMap()泛型方法,用来建立两个类型的映射,第一个类型为原类型,第二个类型为目标类型。在这里配置为将视图模型映射为业务模型。
而后,在项目的Global.asax文件的Application_Start()方法中调用该静态方法。代码以下所示。
protected void Application_Start() { //调用AutoMapper配置 AutoMapper.AutoMapperConfig.Config();
AreaRegistration.RegisterAllAreas(); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); } |
至此,全部AutoMapper的配置所有配置完成。
下面咱们完成用户注册功能。在控制器中建立两个Action。如示例2所示。
示例2
[HttpGet] public ActionResult Register() { return View(); } [HttpPost] public ActionResult Register(RegUserVM userVM) { //将视图模型对象映射为业务模型对象 User user = Mapper.Map<User>(userVM); if (UserManager.Add(user)) { return RedirectToAction("Login"); } return View(); } |
Mapper.Map<S,T> 执行映射方法 S为源类型,T为目标类型,参数为源类型。
示例2中,AutoMapper 根据字段名称去自动对应,并忽略大小写。咱们应用 AutoMapper 省去了繁琐的赋值操做。
在AutoMapper中 ReverseMap() 方法能够配置为反向映射。如示例3所示。
示例3
public class AutoMapperConfig { public static void Config() { Mapper.Initialize(cfg => cfg.CreateMap<RegUserVM, User>().ReverseMap()); } } |
在实际的业务环境中,咱们的源类型和目标类型的字段不可能一对一的匹配,这个时候咱们就须要来指定他们的实际映射关系。如示例3所示。
示例3
//源类型 public class User { public int Id { get; set; } public string LoginName { get; set; } public string LoginPwd { get; set; } public string Phone { get; set; } public int RoleId { get; set; } public Role Role { get; set; } } //源目标类型 public class UserVM { public int UserId { get; set; } public string UserName { get; set; } public string RoleName { get; set; } }
public static void Config() { Mapper.Initialize(cfg => { cfg.CreateMap<RegUserVM, User>().ReverseMap(); cfg.CreateMap<User, ListUserVM>().ReverseMap();
cfg.CreateMap<User, UserVM>() .ForMember(vm => vm.UserId, opt => opt.MapFrom(s => s.Id)) //指定映射 .ForMember(vm=>vm.UserName,opt=>opt.MapFrom(s=>s.LoginName)) .ReverseMap()); }); }
|
ForMember()方法用来配置匹配信息。参数1:目标类型属性的表达式,参数2:执行操做的选择。
AutoMapper中容许设置一个备用值来代替源类型中的空值。如示例4所示。
示例4
public static void Config() { Mapper.Initialize(cfg => { cfg.CreateMap<RegUserVM, User>().ReverseMap(); cfg.CreateMap<User, ListUserVM>().ReverseMap();
cfg.CreateMap<User, UserVM>() .ForMember(vm => vm.UserId, opt => opt.MapFrom(s => s.Id)) .ForMember(vm=>vm.UserName,opt=>opt.MapFrom(s=>s.LoginName)) .ForMember(vm => vm.RoleName, opt => opt.NullSubstitute("普通用户")).ReverseMap()); }); } |
在AutoMapper中, 若是对目标类型上的任何属性,方法或以"Get"为前缀的方法不存在源类型上,则AutoMapper会将目标成员名称拆分为单个单词。
例如,在显示用户信息时,咱们只想显示用户的登陆名、电话和角色名称三个属性,代码如示例5所示。
示例5
// 显示用户信息的视图模型 public class ListUserVM { public int Id { get; set; } public string LoginName { get; set; } public string Phone { get; set; } public string RoleName { get; set; } } // AutoMapper配置 public static void Config() { Mapper.Initialize(cfg => { cfg.CreateMap<RegUserVM, User>().ReverseMap(); cfg.CreateMap<User, ListUserVM>().ReverseMap(); }); } // 控制器方法 public ActionResult Index() { Role role = new Role() { Id = 1, Name = "管理员" }; User user = new User() { Id = 1, LoginName = "admin", Phone = "111", Role=role }; ListUserVM userVM = Mapper.Map<ListUserVM>(user); return View(userVM); } |
程序运行后,userVM 对象的 RoleName 属性会被赋值为"管理员"。(RoleName属性会和User类的 Role.Name 属性进行匹配)