NBear.Mapping使用教程

在新版本的NBearMapping中,你不需要手工去实例化ObjectMapper 对象。取而代之的是ObjectConvertor类,在这个类中提供了多种的静态重载方法,来方便你使用。主要的方法重载有:ToObjectToListToDataTable。

ToObject

   1: public static OutputType ToObject<OutputType>(object inputObject)
   2: public static OutputType ToObject<InputType, OutputType>(InputType inputObject)
   3: public static OutputType ToObject<InputType, OutputType, InitType>(InputType inputObject)
   4: public static OutputType ToObject<InputType, OutputType, InitType>(InputType inputObject, string viewName)
   5: public static OutputType ToObject<OutputType>(object inputObject, string viewName)
   6: public static OutputType ToObject<InputType, OutputType>(InputType inputObject, string viewName)
   7: public static object ToObject(Type inputType, Type outputType, Type initType, object inputObject)
   8: public static object ToObject(Type inputType, Type outputType, Type initType, object inputObject, string viewName)

这些重载方法几乎涵盖了,有可能的多种映射需 求。重载(1)描述的是一种最简单的映射需求,你只需要传入源对象,指定目标对象的类型,NBear就会自动帮你创建好对象,并将源对象的字段值赋给目标 对象对应的字段,返回输出给你。重载(2)除了要你传入源对象外,还要求你指定输入和输出类型。为什么要指定输入类型?当源对象的类型inputObject是这里指定的输入类型InputType的一个子类,inputObject可能会有很多字段并不是在InputType定义的,但是我们只想对InputType里的字段进行映射,这里显式的指定一个输入类型可以保证你的需求能够被正确描述:比如

   1: interface IUser
   2: {
   3:     string Name { get;set;}
   4: }
   5: public class UserObject : IUser
   6: {
   7:     #region IUser Members
   8:  
   9:     public string Name
  10:     {
  11:         get
  12:         {
  13:             throw new Exception("The method or operation is not implemented.");
  14:         }
  15:         set
  16:         {
  17:             throw new Exception("The method or operation is not implemented.");
  18:         }
  19:     }
  20:  
  21:     #endregion
  22:  
  23:     public string Password;
  24: }

IUser里定义了一个用户的所有属性,UserObject继承实现了这些属性,但是此时它本身还有一个Password字段,由于这个字段的特殊意义,我们将不希望它参与对象间的映射。所以我们可以指定它的InputTypeIUser。 重载(3)中,除了指定输入和输出类型外,还必须指定一个初始化类型。与上面的输入类型相同,有些情况下我们指定的输出类型可能并没有公有构造函数,或者 输入类型是一个接口,这时为了让NBearMapping能够正确实例化目标对象,你就必须指定一个带有公有无参构造的初始化类型,这个类型正常情况下是InputType的一个子类。重载(4)指定的viewName是显式的指定你所希望使用的配置节点。这种重载的使用,你必须保证当前的NBearMapping配置中存在这个viewName的配置节点。NBearMapping的配置系统在稍后的章节中会详细介绍。

以上就是ToObject这个方法使用的重点,其它重载都是为了方便使用,对参数进行一定的组合。

ToList

   1: public static List<OutputType> ToList<InputType, OutputType>(object inputList)
   2: public static List<OutputType> ToList<InputType, OutputType, InitType>(object inputList)
   3: public static List<OutputType> ToList<InputType, OutputType>(object inputList, string viewName)
   4: public static List<OutputType> ToList<InputType, OutputType, InitType>(object inputList, string viewName)
   5: public static List<OutputType> ToList<OutputType>(object inputList, Type inputType, Type initType, string viewName)

ToList方法,对数组或集合进行成批的映射,或将DataTable,IDataReader转为数据对象集合。这里的 InputTye,OutputType是数组或集合内所存放的对象类型。需要注意的是,ToList方法与ToObject共用配置。换句 话,ToList方法相当只是循环使用ToObject方法,但是在进行集合映射时,推荐你使用这个接口,这样可以保证性能最优化。ToList传入的 inputList参数,必须是一个DataTable,IDataReader,或是IEnumerable的对象。

ToDataTable

   1: public static DataTable ToDataTable(object inputList, Type inputType)
   2: public static DataTable ToDataTable(object inputList, Type inputType, string viewName)

ToDataTable,将数组或集合转为DataTable。

除此之外,你也可以通过自己实现例化ObjectMapper 对象,来实现完成自己的特定的映射需求。

 

在NBear.Mapping中提供了一种灵活的配置方式,允许你在不改变原有代码的情况下,随时对映射需求进行修改,同时即使你在开发初始期不进行配置,它仍然可以正常的工作。极大的方便你的开发和维护工作。

在你使用ObjectConvertor的接口进行对象映射时,系统会根据你指定的输入类型(如果没有指定则以inputObject的类型为准)、输出类型和viewName,去查找系统已经存在的对应的配置(表现为一个缓存的ObjectMapper对象),如果找到这个配置,则会使用这个ObjectMapper对象,否则会新创建一个ObjectMapper对象来执行映射动作。

在NBear.Mapping.Test中,你可以找到NBear.Mapping.config最完整的配置语法案例,它主要有以一些配置节点:

   1: <NBear.Mapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   2:   <typeAliases>
   3:   </typeAliases>
   4:   <defaultInitTypes>
   5:   </defaultInitTypes>
   6:   <mappings>
   7:   </mappings>
   8: </NBear.Mapping>
根配置节点名称 说明
typeAliases
在这里配置一些类型的别名,比如System.Collections.ArrayList你每次都要写完成类名很麻烦,使用ArrayList就方便又简单
defaultInitTypes
指定输入类型的默认实例化类型,如果你的配置节中没有显式的指定实例化类型,而这里有对应的输入类型的实例化类型的话,那就会使用这里的配置。
mappings
对象的映射配置都在这个配置节下面。
typeAliases 配置节
typeAliases 配置节点名称 说明
remove
如果在其它的配置文件已经存在了相同的配置,则清除已经有的冲突配置。如下:<remove type="ArrayList"></remove>
add
增加一个类型别名配置,如下:
<add type="ArrayList" fullTypeName="System.Collections.ArrayList"></add>

defaultInitTypes 配置节

defaultInitTypes 的子节点名称 说明
remove
如果在其它的配置文件已经存在了相同的配置,则清除已经有的冲突配置。如下:
<remove type="IList"></remove>
add
增加一个默认初始化类型的配置,如下:
<add type="System.Collections.IList" initType="ArrayList"></add>

mappings 配置节

mappings下的节点,由object组成。每一个object节点有两个属性

object的属性 说明 要求
inputType
映射的输入类型 必填
outputType
映射的输出类型 必填

由于每一个输入和输出类型可能会有多种的映射方案,我们将不同的映射方案都称之为view,对应的都有一个viewName,放在object的 views节点下面,同时在views节点中,你还可以指定它默认使用哪个view,如果.NET 2.0中的provider模型。

views节点的属性

views的属性 说明 要求
defaultView
指定默认使用哪种配置方案,如果没有指定,则默认使用最后一种配置方案。 可选

views节点下,就是配置各种不同映射方案的地方了。它由一些view配置组成,view配置的属性:

view的属性 说明 要求
name
设定这种映射方案的名称 可选
initType
指定在这种映射方案下它所使用的初始化类型 可选
mappingSpecifiedOnly
指定在这种映射方案下,它是否为显式映射(只映射指定的字段) 可选

view配置节下,可以配置一些指定的映射字段和映射顺序,希望被忽略的字段,以及自定义的映射行为。

view的子配置节点 说明
properties
不同源字段名和目标字段的映射关系配置,如果view的mappingSpecifiedOnly设为true,也需显式指定配置字段,配置示例:
<add srcName="UserID" destName="ID" order="1"/>
ignoreProperties
希望被排除的字段名,这里指定的是输出字段名。
<add destName="Status"></add>
customMapping
如果默认的NBear.Mapping行为不能满足您的要求,你可以定义一个ICustomObjectMemberMapping的实现,来完成您的自定义映射工作。
<add className="NBear.Mapping.Test.CustomUserToUser2"></add>

 

properties子节点可配置的属性
说明 要求
srcName
映射的源字段名 必填
destName
映射的目标(输出)字段名 必填
order
映射的顺序,您可以在这里指定这个字段在第几位被映射,但是不可以重复。如果第1的位字段不能有两个。 可选

 

ignoreProperties子节点可配置的属性
说明 要求
destName
希望被除排除的目标字段 必填

 

customMapping子节点可配置的属性
说明 要求
className
实现ICustomObjectMemberMapping的类名 必填

以上就是NBear.Mapping配置的所有内容了,可以说是相当简单。只要你看一个完整的例子应该就可以明白了,下面是NBear.Mapping.Test中的完整配置例子。

   1: <?xml version="1.0" encoding="utf-8"?>
   2: <NBear.Mapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   3:   <typeAliases>
   4:     <remove type="ArrayList"></remove>
   5:     <add type="ArrayList" fullTypeName="System.Collections.ArrayList"></add>
   6:     <add type="NameValueCollection" fullTypeName="System.Collections.Specialized.NameValueCollection"></add>
   7:     <add type="User" fullTypeName="NBear.Mapping.Test.User"></add>
   8:     <add type="User2" fullTypeName="NBear.Mapping.Test.User2"></add>
   9:     <add type="DataRow" fullTypeName="System.Data.DataRow"/>
  10:     <add type="IDataReader" fullTypeName="System.Data.IDataReader"/>
  11:     <add type="IList" fullTypeName="System.Collections.IList"/>
  12:     <add type="IUser" fullTypeName="NBear.Mapping.Test.IUser"></add>
  13:   </typeAliases>
  14:   <defaultInitTypes>
  15:     <remove type="IList"></remove>
  16:     <add type="System.Collections.IList" initType="ArrayList"></add>
  17:   </defaultInitTypes>
  18:   <mappings>
  19:     <object inputType="DataRow" outputType="User">
  20:         <views>
  21:             <view name="Default" mappingSpecifiedOnly="false">
  22:                 <properties>
  23:                     <add srcName="UserID" destName="ID" order="1"/>
  24:                 </properties>
  25:             </view>
  26:         </views>
  27:     </object>
  28:     
  29:     <object inputType="DataRow" outputType="User2">
  30:       <views>
  31:         <view name="Default" mappingSpecifiedOnly="false">
  32:           <properties>
  33:             <add srcName="UserID" destName="ID" order="1"/>
  34:           </properties>
  35:         </view>
  36:       </views>
  37:     </object>
  38:  
  39:     <object inputType="IDataReader" outputType="User2">
  40:       <views>
  41:         <view name="Default" mappingSpecifiedOnly="false">
  42:           <properties>
  43:             <add srcName="UserID" destName="ID" order="1"/>
  44:           </properties>
  45:         </view>
  46:       </views>
  47:     </object>
  48:     
  49:     
  50:     <object inputType="IUser" outputType="User">
  51:         <views>
  52:             <view mappingSpecifiedOnly="true" name="Default">
  53:                 <properties>
  54:                     <add srcName="UserID" destName="ID" />
  55:                 </properties>
  56:             </view>
  57:         </views>
  58:     </object>
  59:     
  60:     <object inputType="User" outputType="User2">
  61:         <views defaultView="default">
  62:             <view mappingSpecifiedOnly="true"  name="default">
  63:                 <properties>
  64:                     <add srcName="UserID" destName="ID" order="1"/>
  65:                     <add srcName="Status" destName="Status" order="2"/>
  66:                 </properties>
  67:             </view>
  68:             <view mappingSpecifiedOnly="false" name="ignoreStatus">
  69:                 <ignoreProperties>
  70:                     <add destName="Status"></add>
  71:                 </ignoreProperties>
  72:             </view>
  73:             <view  mappingSpecifiedOnly="true" name="customMapping">
  74:                 <customMapping>
  75:                     <add className="NBear.Mapping.Test.CustomUserToUser2"></add>
  76:                 </customMapping>
  77:             </view>
  78:         </views>
  79:     </object>
  80:     
  81:     <object inputType="IUser" outputType="IUser">
  82:         <views>
  83:             <view mappingSpecifiedOnly="false" initType="User2" name="initTypeASUser2">
  84:             </view>
  85:         </views>
  86:     </object>
  87:     
  88:     <object inputType="NBear.Mapping.Test.OrderedClass" outputType="NBear.Mapping.Test.OrderedClass">
  89:         <views>
  90:             <view name="Default">
  91:                 <properties>
  92:                     <add srcName="Property3" destName="Property3" order="2"/>
  93:                 </properties>
  94:             </view>
  95:         </views>
  96:     </object>
  97:   </mappings>
  98: </NBear.Mapping>

最后,如何将配置添加到系统中? 在系统配置文件中(web.config或app.config),增加一个配置节名称:

<configSections>
<section name="nbearMapping" type="NBear.Mapping.NBearMappingSection,NBear.Mapping"/>
</configSections>
然后增加配置:
<nbearMapping>

<
includes>

<
add key="test" value="NBear.Mapping.config"></add>

</
includes>

</
nbearMapping>

当然,includes下是可以支持多个文件的。也就是说,NBear.Mapping是支持多个配置文件。

不断有朋友希望能提供一些示例,同时我也发现,单纯发一些文字性的教程。大家好像都没有看明白NBear.Mapping是干嘛用的,所以从这篇开 始我会陆续以实例的方式介绍NBear.Mapping的使用,以便给大家直观的印象。这篇我们首先关注的是对象与对象间映射,在这篇文章里你将会看到。 即使你的项目不需要与数据库的映射,NBear.Mapping在日常开发中也会给你带来非常大的帮助。

就如之前教程介绍的那样,有一个IUser接口,这个接口定义了用户的一些基本属性:

   1: public enum UserStatus
   2: {
   3:     Normal,
   4:     Admin
   5: }
   6: public interface IUser
   7: {
   8:     int? ID { get;set;}
   9:     string Name { get;set;}
  10:     string Address { get;set;}
  11:     int Age { get;set;}
  12:     UserStatus Status { get;set;}
  13: }

我们用一个类UserObject来实现它,这个类型除了实现IUser接口外,还有一个自定义的属性Password:

   1: public class UserObject : IUser
   2:     {
   3:         #region IUser Members
   4:         private string name;
   5:         public string Name
   6:         {
   7:             get
   8:             {
   9:                 return name;
  10:             }
  11:             set
  12:             {
  13:                 name = value;
  14:             }
  15:         }
  16:  
  17:         private int? id;
  18:         public int? ID
  19:         {
  20:             get
  21:             {
  22:                 return id;
  23:             }
  24:             set
  25:             {
  26:                 id = value;
  27:             }
  28:         }
  29:         string address;
  30:         public string Address
  31:         {
  32:             get
  33:             {
  34:                 return address;
  35:             }
  36:             set
  37:             {
  38:                 address = value;
  39:             }
  40:         }
  41:         int age;
  42:         public int Age
  43:         {
  44:             get
  45:             {
  46:                 return age;
  47:             }
  48:             set
  49:             {
  50:                 age = value;
  51:             }
  52:         }
  53:  
  54:         UserStatus status;
  55:         public UserStatus Status
  56:         {
  57:             get
  58:             {
  59:                 return status;
  60:             }
  61:             set
  62:             {
  63:                 status = value;
  64:             }
  65:         }
  66:  
  67:         #endregion
  68:  
  69:         private string password;
  70:  
  71:         public string Password
  72:         {
  73:             get { return password; }
  74:             set { password = value; }
  75:         }
  76:  
  77:         public UserObject() { }
  78:  
  79:         public UserObject(int? id, string name, string address, int age, UserStatus status, string password)
  80:         {
  81:             this.id = id;
  82:             this.name = name;
  83:             this.address = address;
  84:             this.age = age;
  85:             this.status = status;
  86:             this.password = password;
  87:         }
  88:     }

这时,我们希望将一个UserObject实例的属性拷贝到另一个UserObject实例中,那么我们可能会使用如下的代码:

   1: UserObject user = new UserObject(1, "abu", "fuzhou", 24, UserStatus.Admin,"*&3345-+¥");
   2: UserObject userObject = new UserObject();
   3: userObject.Address = user.Address;
   4: userObject.Age = user.Age;
   5: userObject.ID = user.ID;
   6: userObject.Name = user.Name;
   7: userObject.Password = user.Password;
   8: userObject.Status = user.Status;

当然,如果单纯的都是这样的属性拷贝的话,那完全可以通过实现ICloneable接口,来实现对象的浅拷贝。但是如果我们使用NBear.Mapping的话,那我们的代码将会变成这样:

   1: UserObject user = new UserObject(1, "abu", "fuzhou", 24, UserStatus.Admin,"*&3345-+¥");
   2: UserObject outputObject = ObjectConvertor.ToObject<UserObject>(user);

你会发现你的对象转换代码将会变的非常干净简单。此时,在保证系统安全的情况下,我并不希望Password字段参与拷贝工作,那么我们有几下几种做法:

1、修改代码,使用ObjectConvertor.ToObject<InputType,OutputType>(user)的形式,如下:

   1: UserObject outputObject = ObjectConvertor.ToObject<IUser,UserObject>(user);

这时,参与对象拷贝的就只有IUser接口里所定义的属性字段了。

2、不改变原有代码,在配置文件中增加这样的一段配置,就可以让过滤掉Password的拷贝:

   1: <object inputType="UserObject" outputType="UserObject">
   2:   <views>
   3:     <view name="Default">
   4:       <ignoreProperties>
   5:         <add destName="Password"></add>
   6:       </ignoreProperties>
   7:     </view>
   8:   </views>
   9: </object>
  10: <mappings>

以上是两种最简单的做法,通过NBear.Mapping,还有其它的调用方法来达到这个目的。

这篇博客比较短,目的是希望能让大家对NBear.Mapping有直观的印象。示例代码下载


 


 

在你的项目中,如果不使用ADO.NET对象,那么都要做一件事,就是要把查询的DataTable或IDataReader对象转换成实体对象, 或者叫映射。比如ORM工具,它帮我们做好了所有事情,iBatis它帮我们完成了执行SQL和实体映射的工具。还有一些是使用Attribute + 反射的方式来实现。现在你又多了一种选择,你完全可以使用NBear.Mapping来帮你完成这个工作。这里的例子仍然使用上篇中定义的User类。

实体对象转DataRow

在有的情况下,你可能会希望把一个对象转换成DataRow,那么你这时可能会去定义与对象对应的DataTable的Schema,然后再用这个 DataTable.NewRow()来创建一个DataRow对象,再将属性一个一个的进行赋值,会需要很多繁琐的步骤。这时候如果使用 NBear.Mapping,情况会变成什么样呢?让我们来看看吧:

DataRow outputObject = ObjectConvertor.ToObject<DataRow>(user);

正如你所看到的,就一句代码就可以完成所有的工作。需要注意的是这时输出的DataRow仍是游离状态,并没有加到所属性的Table中。

DataRow转实体对象

反过来,如果你希望把一个DataRow对象转成实体对象的话,那么你可以使用类似的代码:

UserObject user = ObjectConvertor.ToObject<UserObject>(dataRow);

类似地,你也可以把IDataReader转成实体对象:

UserObject user = ObjectConvertor.ToObject<UserObject>(dataReader);

实体数组转成DataTable

把实体数组转成DataTable是一种最常见的需求了,使用NBear.Mapping这样就可以完成了:

DataTable dataTable = ObjectConvertor.ToDataTable(users, typeof(UserObject));

DataTable转成实体数组

反过来,把DataTable转成实体数据组呢?看下面代码吧:

List<UserObject> userList = ObjectConvertor.ToList<DataRow, UserObject>(dataTable);

那么把DataReader转成实体数组又该如何呢?

List<UserObject> userList = ObjectConvertor.ToList<IDataReader, UserObject>(dataTable);

正如你看到的,NBear.Mapping在处理这些工作的时候非常的游刃有余,而且确实是可以让你的工作更加轻松的。下篇你将会看到它是如何与其它弱类型(NameValueCollection,Dictionary)互转的。

示例代码下载

 

 

 

上篇中, 介绍了使用NBear.Mapping进行实体对象与ADO.NET对象的互转功能,这可能是我们开发过程中最常的一种映射了。除此之 外,NBear.Mapping还支持实体对象与NameValueCollection,IDictionary等弱类型的互换转,下面就让我们来看看 吧。

NameValueCollection实体对象

当你希望把Request.QueryString的字段赋值到一个对象时,手工写这段代码你可能会觉得浪费时间了。现在如果NBear.Mapping可能帮你完成这个工作,难道你不会接受吗?只要简单的这样一句代码就可以完成你的工作了。

   1: UserObject outputObject = ObjectConvertor.ToObject<UserObject>(nameValue);

因为NameValueCollection的Value类型是字符串的,但是没有关系,NBear.Mapping会自动进行类型转换的。反过来,如果想把实体对象转换为NameValueCollection对象时,仍然是一样的简单:

   1: NameValueCollection outputObject = ObjectConvertor.ToObject<NameValueCollection>(user);

IDictionary与实体对象

NBear.Mapping支持将实体对象转换为实现了IDictionary的对象,比如Dictionary<string,object>,Hashtable。分别用下面没行代码就可以完成这样的转换:

Dictionary<string, object> outputObject = ObjectConvertor.ToObject<Dictionary<string, object>>(user);
Hashtable outputObject = ObjectConvertor.ToObject<Hashtable>(user);

相反的转换:

Dictionary<string, object> dic = ObjectConvertor.ToObject<Dictionary<string, object>>(user);
Hashtable hashTable = ObjectConvertor.ToObject<Hashtable>(user);
接口使用都是一样的,这里就不再嗸诉(有谁能告知一下Ao Shu是哪两个字 :( )。

性能

前面不止一次的说到,NBear.Mapping有比较优的性能。它有比较好的性能是因为它的转换使用的是动态生IL代码来进行转换的,并且生成的 IL代码是有缓存的。其实相当于就是动态的手写代码。在NBear.Mapping.Test的MappingTest中,有4个测试 TestPerformance1~4就是测试性能的,每一次转换分别执行100000次,随机进行的一次测试结果如下(手写 VS NBearMapping VS 反射 的性能对比,以手写代码为基准):

DataRow 转实体对象:

Test performance of converting 100000 data rows to class objects
Manually Coding vs NBearMapping vs Reflection
8906250 (1)
vs
11718750 (1.31578947368421)
vs
26093750 (2.92982456140351)

实体对象转实体对象

Test performance of converting 100000 class objects to class objects
Manually Coding vs NBearMapping vs Reflection
312500 (1)
vs
2187500 (7)
vs
15625000 (50)

DataReader 转实体对象

Test performance of converting 100000 dataReader to class objects
Manually Coding vs NBearMapping vs Reflection
7812500 (1)
vs
11406250 (1.46)
vs
29531250 (3.78)

NameValueCollection转实体对象

Test performance of converting 100000 namevaluecollections to class objects
Manually Coding vs NBearMapping vs Reflection
5625000 (1)
vs
12812500 (2.27777777777778)
vs
16875000 (3)

到些为止,NBear.Mapping的使用介绍就告一段落了。除此之外,NBear.Mapping还有一些其它功能,比如配置,自定义映射接 口,扩展功能等等,这些功能只要你愿意花一些时间很容易就能够掌握了。NBear.Mapping使用其实就是这么简单,它自己本身也是很简单的,只要你 愿意尝试,相信它会给你带来意想不到的效果的。

示例下载

阿不

NBear开发团队

 

 

posted @ 2010-03-04 15:42  悲伤的第七音  Views(391)  Comments(0)    收藏  举报