APS.NET MVC + EF (06)--- 模型
在实际开发中, 模型往往被划分为视图模型和业务模型两部分, 视图模型靠近视图, 业务模型靠近业务, 但是在具体编码上, 它们之间并不是隔离的.
6.1 视图模型和业务模型
模型大多数时候都是用来传递数据的. 然而即使在传递数据这一点上, 也可以看出, 视图需要的模型更加灵活一点, 因为视图变化性更大, 而处理业务的模型更加稳定一些. 因此, 在实际开发中, 往往有视图模型和业务模型的区分. 在实际开发中, 为了体现逻辑的分离, 往往是视图模型和业务模型分别定义.
例如, 在传统三层开发中, 我们定义的实体类, 可以看作是业务模型的定义, 在开发视图时, 可能会开发各种和 User 相关的视图, 如用户注册视图, 用户状态列表视图, 登录视图, 这时候往往根据视图构造不同的模型, 因为它们依赖的数据都不太一样, 这些模型可称为视图模型.
这种方式的好处就是可以让业务模型比较稳定, 不会因为视图的变更而频繁地去修改业务模型. 但是从另一方面来看, 这种分离的方式也会有坏处. 坏处之一就是带来很多重复的代码. 另一个坏处就是在动作方法中, 模型自动绑定完视图模型后, 还需要再手动编码把视图模型映射到业务模型, 带来额外的工作量.
在小型项目开发中, 为了简化代码, 并不会完全区分视图模型和业务模型, 但不意味着实际开发都这样做.
6.2 AutoMapper 框架
AutoMapper 是一个能够实现由一个对象到另一个对象间映射的轻量级框架, 利用 AutoMapper 框架可以让我们从视图模型到业务模型转换的繁琐工作中解放出来.
AutoMapper 框架是基于约定的.
6.2.1 AutoMapper 框架的安装
新建 ASP.NET mvc 项目 AutoMapperExample, 点击 工具→NuGetB 包管理器→管理解决方案的 NuGet 程序包, 在弹出的界面中, 搜索 autoMapper, 在搜索出的程序包然后安装.
6.2.2 AutoMapper 框架的使用
我们以常见的用户角色案例来讲解 AutoMapper 的使用.
首先我们创建角色和用户两个类. 代码如下. 其中 User 类中的 Role 属性是对 Role 类的引用.
- // 角色
- publicclass Role
- {
- publicint Id {
- get; set;
- }
- publicstring Name {
- get; set;
- }
- }
- // 用户
- publicclass User
- {
- publicint Id {
- get; set;
- }
- publicstring LoginName {
- get; set;
- }
- publicstring LoginPwd {
- get; set;
- }
- publicstring Phone {
- get; set;
- }
- publicint RoleId {
- get; set;
- }
- public Role Role {
- get; set;
- }
- }
在创建注册用户时所需的视图模型. 代码如下.
- // 注册视图中使用的视图模型
- publicclass RegUserVM
- {
- publicint Id {
- get; set;
- }
- publicstring LoginName {
- get; set;
- }
- publicstring LoginPwd {
- get; set;
- }
- public string ConfirmPwd {
- get; set;
- }
- publicstring Phone {
- get; set;
- }
- publicint RoleId {
- get; set;
- }
- }
在视图模型中增加了 ConfirmPwd 属性, 并将 Role 属性去除.
接下来我们在程序中新建 AutoMapper 文件夹, 用于存放对象映射的类, 该文件夹下新建类 AutoMapperConfig, 该类处理所有的对象映射, 即从一个对象转化到另一个对象. 如示例 1 所示.
示例 1
- using AutoMapper; // 引用命名空间
- publicclass AutoMapperConfig
- {
- public staticvoid Config()
- {
- Mapper.Initialize(cfg =>
- cfg.CreateMap<RegUserVM, User>());
- }
- }
Mapper.Initialize() 方法执行 AutoMapper 的初始化操作, 此操作在一个应用程序中只能执行一次. 在初始化方法中可以初始化映射中的任何操作.
CreateMap() 泛型方法, 用来创建两个类型的映射, 第一个类型为原类型, 第二个类型为目标类型. 在这里配置为将视图模型映射为业务模型.
然后, 在项目的 Global.asax 文件的 Application_Start() 方法中调用该静态方法. 代码如下所示.
- protectedvoid 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 ActionResultRegister()
- {
- return View();
- }
- [HttpPost]
- public ActionResultRegister(RegUserVM userVM)
- {
- // 将视图模型对象映射为业务模型对象
- User user = Mapper.Map<User>(userVM);
- if (UserManager.Add(user))
- {
- return RedirectToAction("Login");
- }
- return View();
- }
Mapper.Map 执行映射方法 S 为源类型, T 为目标类型, 参数为源类型.
示例 2 中, AutoMapper 根据字段名称去自动对应, 并忽略大小写. 我们应用 AutoMapper 省去了繁琐的赋值操作.
6.2.3 映射规则 默认规则 默认情况下, 我们的 "原类型" 和 "目标类型" 是根据属性名称进行匹配映射的. 如果在目标类型的属性与源类型中配有对应的属性, 则映射失败 (为空). 在映射过程中, 会执行自动类型转换.(6.2.0 以上版本) 反向映射
在 AutoMapper 中 ReverseMap() 方法可以配置为反向映射. 如示例 3 所示.
示例 3
- publicclass AutoMapperConfig
- {
- public staticvoid Config()
- {
- Mapper.Initialize(cfg =>
- cfg.CreateMap<RegUserVM, User>().ReverseMap());
- }
- }
指定映射字段
在实际的业务环境中, 我们的源类型和目标类型的字段不可能一对一的匹配, 这个时候我们就需要来指定他们的实际映射关系. 如示例 3 所示.
示例 3
- // 源类型
- publicclass User
- {
- publicint Id {
- get; set;
- }
- publicstring LoginName {
- get; set;
- }
- publicstring LoginPwd {
- get; set;
- }
- publicstring Phone {
- get; set;
- }
- publicint RoleId {
- get; set;
- }
- public Role Role {
- get; set;
- }
- }
- // 源目标类型
- publicclass UserVM
- {
- publicint UserId {
- get; set;
- }
- publicstring UserName {
- get; set;
- }
- publicstring RoleName {
- get; set;
- }
- }
- public staticvoid 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 staticvoid 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
- // 显示用户信息的视图模型
- publicclass ListUserVM
- {
- publicint Id {
- get; set;
- }
- publicstring LoginName {
- get; set;
- }
- publicstring Phone {
- get; set;
- }
- publicstring RoleName {
- get; set;
- }
- }
- // AutoMapper 配置
- public staticvoid Config()
- {
- Mapper.Initialize(cfg => {
- cfg.CreateMap<RegUserVM, User>().ReverseMap();
- cfg.CreateMap<User, ListUserVM>().ReverseMap();
- });
- }
- // 控制器方法
- public ActionResultIndex()
- {
- Role role = new Role() {
- Id = 1, Name = "管理员"
- };
- User user = new User() {
- Id = 1, LoginName = "admin",
- Phone = "111", Role=role
- };
- ListUserVMuserVM = Mapper.Map(user);
- return View(userVM);
- }
程序运行后, userVM 对象的 RoleName 属性会被赋值为 "管理员".(RoleName 属性会和 User 类的 Role.Name 属性进行匹配)
来源: https://www.2cto.com/kf/201905/806730.html