欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

APS.NET MVC + EF (06)---模型

程序员文章站 2022-04-09 17:37:03
在实际开发中,模型往往被划分为视图模型和业务模型两部分,视图模型靠近视图,业务模型靠近业务,但是在具体编码上,它们之间并不是隔离的。 6.1 视图模型和业务模型 模型大多数时候都是用来传递数据的。然而即使在传递数据这一点上,也可以看出,视图需要的模型更加灵活一点,因为视图变化性更大,而处理业务的模型 ......

在实际开发中,模型往往被划分为视图模型和业务模型两部分,视图模型靠近视图,业务模型靠近业务,但是在具体编码上,它们之间并不是隔离的。

   

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类的引用。

//角色

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 省去了繁琐的赋值操作。

   

6.2.3 映射规则

  • 默认规则
    • 默认情况下,我们的"原类型"和"目标类型"是根据属性名称进行匹配映射的。
    • 如果在目标类型的属性与源类型中配有对应的属性,则映射失败(为空)。
    • 在映射过程中,会执行自动类型转换。(6.2.0以上版本)
  • 反向映射

    在automapper中 reversemap() 方法可以配置为反向映射。如示例3所示。

示例3

public class automapperconfig

{

public static void config()

{

mapper.initialize(cfg =>

cfg.createmap<reguservm, user>().reversemap());

}

}

   

  1. 指定映射字段

在实际的业务环境中,我们的源类型和目标类型的字段不可能一对一的匹配,这个时候我们就需要来指定他们的实际映射关系。如示例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:执行操作的选择。

  1. 空值替换

    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());

});

}

   

  1. 扁平化映射

    在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 属性进行匹配)