• 综述
EmitMapper是一个开源实体映射框架,地址:http://emitmapper.codeplex.com/。
EmitMapper映射效率比较高,接近硬编码。EmitMapper采用emit方式在运行时动态生成IL,而其他映射框架多是采用反射机制。此外EmitMapper最大限度地减少了拆箱装箱操作和映射过程中的额外的调用。
EmitMapper支持.net的所有平台:Framework 3.5、Microsoft Silverlight 3、Mono。
EmitMapper的使用非常简单,不需要指定任何的映射策略。系统会采用默认的映射配置器DefaultMapConfig完成映射操作。
映射代码

 1     public class Sourse 2     { 3         public int A; 4         public decimal? B; 5         public string C; 6         public Inner D; 7         public string E; 8     } 9 10     public class Dest11     {12         public int? A;13         public decimal B;14         public DateTime C;15         public Inner D;16         public string F;17     }18 19     public class Inner20     {21         public long D1;22         public Guid D2;23     }24 25     Sourse src = new Sourse26     {27         A = 1,28         B = 0M,29         C = "2011/9/21 0:00:00",30         D = new Inner31         {32             D2 = Guid.NewGuid()33         },34         E = "test"35     };36 37     ObjectsMapper<Sourse, Dest> mapper = 38         ObjectMapperManager.DefaultInstance.GetMapper<Sourse, Dest>();39 Dest dst = mapper.Map(src);40 41     Assert.AreEqual<string>(dst.B.ToString(), src.B);42     Assert.AreEqual<long>(dst.C.C1, 0L);43     Assert.AreEqual<Guid>(dst.C.C2, src.C.C2);44     Assert.IsNull(dst.E);

  • 默认映射配置器
默认的映射配置器能自动转换以下几种类型:
任何类型到string类型使用ToString()方法;
可以使用System.Convert类转换的原始类型;
可空类型、枚举类型、各种集合类型、结构与类;
复杂的嵌套类型采用递归方式转换;
如果默认的转换满足不了需求,默认的映射配置器还允许指定命名约定,自定义构造函数,自定义转换器,忽略成员等。

支持的方法

描述

ConvertUsing

为指定的成员提供自定义的转换逻辑

ConvertGeneric

为指定的泛型类型成员提供自定义的转换逻辑

ConstructBy

为目标对象使用指定的构造函数替代默认构造函数

NullSubstitution

当源对象中指定的成员在为null时,给目标对象的成员赋值

IgnoreMembers

忽略指定成员的映射

PostProcess

在映射完成后执行指定的方法

ShallowMap

指定的成员采用浅拷贝方式映射

DeepMap

指定的成员采用深拷贝方式映射

MatchMembers

如果成员名称的映射不采用精确匹配,可以指定具体的映射逻辑

选择几个方法简单示例如下:
Default映射配置器

 1     public class Sourse 2     { 3         public int A; 4         public decimal? B; 5         public string C; 6         public Inner D; 7         public string E; 8     } 9 10     public class Dest11     {12         public int? A;13         public decimal B;14         public DateTime C;15         public Inner2 D;16         public string F;17     }18 19     public class Inner20     {21         public long D1;22         public Guid D2;23     }24 25     public class Inner226     {27         public long D12;28         public Guid D22;29     }30 31     ObjectsMapper<Sourse, Dest> mapper1 = 32         new ObjectMapperManager().GetMapper<Sourse, Dest>(33             new DefaultMapConfig()34             .IgnoreMembers<Sourse, Dest>(new string[] { "A" })35             .NullSubstitution<decimal?, decimal>((value) => -1M)36             .ConvertUsing<Inner, Inner2>(value => new Inner2 { D12 = value.D1, D22 = value.D2 })37             .PostProcess<Dest>((value, state) => { value.F = "nothing"; return value; })38             );39     Dest dst = mapper1.Map(src);40 41     Assert.IsNull(dst.A);42     Assert.AreEqual<decimal>(dst.B, -1M);43     Assert.AreEqual<Guid>(dst.D.D22, src.D.D2);44     Assert.AreEqual<string>(dst.F, "nothing");

  • 自定义映射配置器
当然EmitMapper是个非常灵活的框架,也可以自定义映射配置器,实现定制的映射操作。
自定义的映射配置器可以继承自DefaultMapConfig或CustomMapConfig,利用基类的一些功能实现定制的映射,也可以继承自接口ImappingConfigurator,完全从头实现。
比如可以实现从HTTP中通过Post方式提交的Form数据到具体业务实体类的映射,下面通过继承ImappingConfigurator来实现。
自定义映射配置器

 1     public class FormCollectionMapConfig : IMappingConfigurator 2     {         3         public static TPostData GetPostData<TPostData>() where TPostData : class , new() 4         { 5             ObjectsMapper<NameValueCollection, TPostData> mapper 6                 = new ObjectMapperManager().GetMapper<NameValueCollection, TPostData>(new FormCollectionMapConfig()); 7  8             return mapper.Map(HttpContext.Current.Request.Form); 9         }10 11         public IMappingOperation[] GetMappingOperations(Type from, Type to)12         {13             var members = ReflectionUtils.GetPublicFieldsAndProperties(to);14             return members15                 .Select(m => new DestWriteOperation()16                             {17                                 Destination = new MemberDescriptor(m),18                                 Getter =19                                     (ValueGetter<object>)20                                     (21                                         (form, status) =>22                                         {23                                             FormCollection forms = new FormCollection((NameValueCollection)form);24                                             IValueProvider valueProvider = forms.ToValueProvider();25                                             ValueProviderResult res = valueProvider.GetValue(m.Name);26                                             if (res != null)27                                             {28                                                 return ValueToWrite<object>.ReturnValue(res.ConvertTo(ReflectionUtils.GetMemberType(m)));29                                             }30                                             else31                                             {32                                                 return ValueToWrite<object>.Skip();33                                             }34                                         }35                                     )36                             }37                 )38                 .ToArray();39         }40 41         public string GetConfigurationName()42         {43             return null;44         }45 46         public IRootMappingOperation GetRootMappingOperation(Type from, Type to)47         {48             return null;49         }50 51         public StaticConvertersManager GetStaticConvertersManager()52         {53             return null;54         }55     }
 
 

Customization using default configurator

Customization using default configurator

Customization overview
Custom converters
Custom converters_for_generics
Null substitution
Ignoring members
Custom constructors
Shallow and_deep_mapping
Names matching
Post processing

Customization overview.

Default configurator is the easiest way to customize mapping. Using is rather simple: create instance of type DefaultMapConfig, setup it and pass it to the method GetMapper of ObjectMapperManager. For example:

var mapper = ObjectMapperManager.DefaultInstance.GetMapper<Source, Destination>(new DefaultMapConfig().NullSubstitution<int?, int>(() => -1));
...
Destination d = mapper.Map(new Source());

Class "DefaultMapConfig" contains number of configuration methods some of them are generic methods and are parameterized by argument types. This types are the types of fields and properties to which this configuration rule should be applied. For example the configuration method:

new DefaultMapConfig().PostProcess<Foo>( (value, state) => Console.WriteLine("Post processing a Foo or it's descendant: " + value.ToString())
)

will be applied to all fields and properties that are instances of class Foo or any it's descendants.

If you want the configuration method to apply to any type then use "object" as an argument type. For example:

new DefaultMapConfig().PostProcess<object>( (value, state) => Console.WriteLine("Post processing: " + value.ToString()) )

will be applied to any field or property and will write to the console all mapped values.

Custom converters.

You can setup custom converters for every type of source member and destination member. It is possible using the following method:

/// <summary>
/// Define custom type converter
/// </summary>
/// <typeparam name="From">Source type</typeparam>
/// <typeparam name="To">Destination type</typeparam>
/// <param name="converter">Function which converts an inctance of the source type to an instance of the destination type</param>
/// <returns></returns>
DefaultMapConfig ConvertUsing<From, To>(Func<From, To> converter)

Example:

public class A
{public string fld1;public string fld2;
}public class B
{public string fld1 = "B::fld1";public string fld2 = "B::fld2";
}[TestMethod]
public void Test_CustomConverter2()
{A a = Context.objMan.GetMapper<B, A>(new DefaultMapConfig().ConvertUsing<object, string>(v => "converted " + v.ToString())).Map(new B());Assert.AreEqual("converted B::fld1", a.fld1);Assert.AreEqual("converted B::fld2", a.fld2);
}

Custom converters for generics.

You may want to specify a custom conversion method for one gneric class to another, for example for "HashSet<T>" to "List<T>" where "T" can be anything. Because you don't know concrete argument type you cannot using method "ConvertUsing". Instead of this method "ConvertGeneric" should be used.

/// <summary>
/// Define conversion for a generic. It is able to convert not one particular class but all generic family
/// providing a generic converter.
/// </summary>
/// <param name="from">Type of source. Can be also generic class or abstract array.</param>
/// <param name="to">Type of destination. Can be also generic class or abstract array.</param>
/// <param name="converterProvider">Provider for getting detailed information about generic conversion</param>
/// <returns></returns>
public IMappingConfigurator ConvertGeneric(Type from, Type to, ICustomConverterProvider converterProvider);
...
/// <summary>
/// Provider for getting detailed information about generic conversion
/// </summary>
public interface ICustomConverterProvider
{/// <summary>/// Getting detailed information about generic conversion/// </summary>/// <param name="from">Type of source. Can be also generic class or abstract array.</param>/// <param name="to">Type of destination. Can be also generic class or abstract array.</param>/// <param name="mappingConfig">Current mapping configuration</param>/// <returns>Detailed description of a generic converter.</returns>CustomConverterDescriptor GetCustomConverterDescr(Type from, Type to, MapConfigBaseImpl mappingConfig);
}
...
/// <summary>
/// Detailed description of a generic converter.
/// </summary>
public class CustomConverterDescriptor
{/// <summary>/// Type of class which performs conversion. This class can be generic which will be parameterized with types /// returned from "ConverterClassTypeArguments" property./// </summary>public Type ConverterImplementation { get; set; }/// <summary>/// Name of conversion method of class which is specified by "ConverterImplementation" property./// </summary>public string ConversionMethodName { get; set; }/// <summary>/// Type arguments for parameterizing generic converter specified by "ConverterImplementation" property./// </summary>public Type[] ConverterClassTypeArguments { get; set; }
}

In order to setup custom generic conversion you should create a generic class with conversion method:

class HashSetToListConverter<T>
{public List<T> Convert(HashSet<T> from, object state){if (from == null){return null;}return from.ToList();}
}

The next step is to specify a custom converter provider for the "HashSetToListConverter<T>". In our case we can directly use class "DefaultCustomConverterProvider" which returns string "Convert" as method name for conversion method and type arguments of the source generic object. So, if we are converting HashSet<int> to List<int> then DefaultCustomConverterProvider returns array of one item: "new Type[]{ typeof(int) }" and this type item from the array will be used as type argument for class "HashSetToListConverter<T>"

Using custom generic converter:

var mapper = ObjectMapperManager.DefaultInstance.GetMapper<Source, Destination>(new DefaultMapConfig().ConvertGeneric(typeof(HashSet<>), typeof(List<>), new DefaultCustomConverterProvider(typeof(HashSetToListConverter<>))));

Null substitution.

There can be situations when if a source object has null in a field (or property) then mapper should write some value (not null!) to the destination field. For example we have the following classes:

class Source
{public field int? i;
}
class Destination
{public field int i;
}

If we map instance of class "Source" to instance of class "Destination", Emit Mapper library should decide what to write to Destination::i when Source::i is null. It can be specified using the following code:

var mapper = ObjectMapperManager.DefaultInstance.GetMapper<Source, Destination>(new DefaultMapConfig().NullSubstitution<int?, int>(() => -1));
var d = mapper.Map(new Source());
Assert.AreEqual(-1, d.i);

Ignoring members

If you don't want to map some fields or properties use method "IgnoreMembers":

[TestMethod]
public class B
{public string str1 = "B::str1";
}public class A
{public string str1 = "A::str1";
}public void GeneralTests_Ignore()
{var a = ObjectMapperManager.DefaultInstance.GetMapper<B, A>(new DefaultMapConfig().IgnoreMembers<B, A>(new[]{"str1"})).Map(new B());Assert.AreEqual("A::str1", a.str1);
}

Custom constructors

In some cases you don't want to use default constructor for a destination member but instead some custom construction method or non-default constructor.

public class ConstructBy_Source
{public class NestedClass{public string str = "ConstructBy_Source::str";}public NestedClass field = new NestedClass();
}public class ConstructBy_Destination
{public class NestedClass{public string str;public int i;public NestedClass(int i){str = "ConstructBy_Destination::str";this.i = i;}}public NestedClass field;
}
[TestMethod]
public void ConstructByTest()
{var mapper = ObjectMapperManager.DefaultInstance.GetMapper<ConstructBy_Source, ConstructBy_Destination>(new DefaultMapConfig().ConstructBy <ConstructBy_Destination.NestedClass>(() => new ConstructBy_Destination.NestedClass(3)));var d = mapper.Map(new ConstructBy_Source());Assert.AreEqual("ConstructBy_Source::str", d.field.str);Assert.AreEqual(3, d.field.i);
}

Shallow and deep mapping

If a field of destination object is the same type as field of source object and this type is complex then the field can be copied by reference (shallow copying) or for the destination field can be constructed separate object and then all nested fields and properties will be copied recursively (deep copying). This behavior can be defined using methods "ShallowMap" or "DeepMap"

public class A1
{public int fld1;public string fld2;
}public class A2
{public string[] fld3;
}public class A3
{public A1 a1 = new A1();public A2 a2 = new A2();
}public class B3
{public A1 a1 = new A1();public A2 a2 = new A2();
}b = new B3();
Context.objMan.GetMapper<B3, A3>(new DefaultMapConfig().ShallowMap<A1>().DeepMap<A2>()).Map(b, a);
b.a1.fld1 = 333;
b.a2.fld3 = new string[1];Assert.AreEqual(333, a.a1.fld1); // shallow map
Assert.IsNull(a.a2.fld3); // deep map

Names matching

If member names of source object don't exactly match to member names of destination object (for example, destination names can have prefix "m_") you can define a function which matches names using method "MatchMembers"

public class Source
{public string field1 = "Source::field1";public string field2 = "Source::field2";public string field3 = "Source::field3";
}public class Destination
{public string m_field1;public string m_field2;public string m_field3;
}[TestMethod]
public void GeneralTests_Example2()
{var mapper = ObjectMapperManager.DefaultInstance.GetMapper<Source, Destination>(new DefaultMapConfig().MatchMembers((m1, m2) => "m_" + m1 == m2));Source src = new Source();Destination dst = mapper.Map(src);Assert.AreEqual(src.field1, dst.m_field1);Assert.AreEqual(src.field2, dst.m_field2);Assert.AreEqual(src.field3, dst.m_field3);
}

The first argument of the matching function is neme of source member, the second argument is name of destination member.

Post processing

If special actions should be executed after mapping you can use the following method:

/// <summary>
/// Define postprocessor for specified type
/// </summary>
/// <typeparam name="T">Objects of this type and all it's descendants will be postprocessed</typeparam>
/// <param name="postProcessor"></param>
/// <returns></returns>
public IMappingConfigurator PostProcess<T>(ValuesPostProcessor<T> postProcessor)

Example:

public class Destination
{public List<int> list;
}public class Source
{public int[] list;
}var mapper = ObjectMapperManager.DefaultInstance.GetMapper<Source, Destination>(new DefaultMapConfig().PostProcess<Destination>( (destination, state) => { destination.list.Sort(); return destination;} ));

Last edited Jan 9 2010 at 2:47 AM by romankovs, version 17

开源实体映射框架EmitMapper介绍相关推荐

  1. 开源C++单元测试框架Google Test介绍

    开源C++单元测试框架Google Test介绍 Google Test Google test是针对c/c++的开源测试项目.采用的协议是BSD license,有很多著名的开源项目采用了它,包括C ...

  2. Fresco图片加载框架的介绍,相关开源库以及工具类的封装

    Fresco图片加载框架的介绍,相关开源库以及工具类的封装 本文已授权微信公众号:鸿洋(hongyangAndroid)在微信公众号平台原创首发. 简介 Fresco 是Facebook开源的安卓上的 ...

  3. 在线支付系列【19】微信支付开源框架汇总介绍

    有道无术,术尚可求,有术无道,止于术. 文章目录 前言 开源框架 WxJava[JAVA SDK 推荐] pay-java-parent[JAVA SDK] IJPay[JAVA SDK] jeepa ...

  4. 国产开源网络编程框架tio的得意之作—谭聊介绍

    想各位对即时通讯源码有追求人,必然有所了解谭聊,谭聊是完全基于开源网络编程框架t-io开发的一款即时通讯软件,也是t-io作者亲自操刀,性能上的强大能力完全继承了t-io的特性,即单机版可以达到近百万 ...

  5. NHibernate.Validator 实体验证框架

    在系统开发时,很多情况下都需要对实体进行验证,比如规定某个属性值不能为空,Email的格式或者电话号码的格式是否正确.这种验证不应该只在UI层进行,主要有以下几方面的考虑:      1.如果每个层次 ...

  6. iOS开源项目MobileProject功能点介绍

    一:MobileProject简介 MobileProject项目是一个以MVC模式搭建的开源功能集合,基于Objective-C上面进行编写,意在解决新项目对于常见功能模块的重复开发,MobileP ...

  7. ORM映射框架总结--数据库操作库(精修版)

    1.       ORM数据库操作原理 前面已经介绍过了个人ORM映射框架中的三个核心库: 实体-数据库 映射特性关系: http://www.cnblogs.com/qingyuan/archive ...

  8. ORM 系列:一个个人ORM映射框架

    转载:http://www.cnblogs.com/qingyuan/category/239086.html 个人ORM映射框架中的三个核心库: 实体-数据库 映射特性关系: http://www. ...

  9. 云计算仿真框架CloudSim介绍

    幻灯片1 云计算仿真框架CloudSim介绍 jiangzw#ihep.ac.cn (以下为本人某次报告做的调研的PPT及其它一些实践记录,为保证清晰度,一些插入的图片较大,可在新标签页中打开) (  ...

最新文章

  1. PHP设计模式之装饰者模式
  2. 逻辑判断 java_写 JS 逻辑判断,不要只知道用 if-else 和 switch
  3. boost::filesystem::copy用法的测试程序
  4. linux执行指定程序的命令,linux 下使用指定的用户来执行命令
  5. 怎样去掉桌面图标和字的蓝色阴影
  6. java分割例子,Java 分割字符串详解及实例代码
  7. linux文件管理课程设计,操作系统原理课程设计-Linux文件管理系统的仿真.doc
  8. JAVA JSP图书管理图书系统 servlet图书管理系统实现简单的图书管理系统源码
  9. Axure8超详细使用教程(含安装包)
  10. 使用Landsat系列数据来检测喜马拉雅地区的冰湖溃决(Georg Veha等人,RSE,2018)
  11. 源码编译安装LAMP
  12. Metamask如何关闭隐私模式,可以正常读取账号信息
  13. cesium空间面积测量,取点比较准,数据不是很准,但是差不太多
  14. 真正意义上能够全部抓取昵图网全站图片
  15. Google登录强制启用二次身份验证与FIDO解决方案
  16. unity挂机游戏技术指南 安卓版
  17. 直方图均衡化(HE, AHE, CLAHE)
  18. 企事业单位人事招聘考试报名系统+HR 招聘网上报名系统
  19. 婚恋交友app开发中需要注意的安全问题
  20. 黑苹果引导工具Clover Configurator 5.14.0.0中文版

热门文章

  1. 【Maven3教程】Maven多工程、多模块
  2. 写一个参数返回二进制中1的个数
  3. 摇滚吧HTML5!Jsonic超声波前端交互!
  4. 苦逼网管员----何时能翻身!
  5. GridView添加自动编号列
  6. 爱情,没有对不起;只有不珍惜……[
  7. TableAdapter 概述
  8. java基础学习(二)数组
  9. OpenCV 图像的混合
  10. Adobe Audition CC 2020中文版