日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

开源实体映射框架EmitMapper介绍

發布時間:2025/5/22 编程问答 14 豆豆
生活随笔 收集整理的這篇文章主要介紹了 开源实体映射框架EmitMapper介绍 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
  • 綜述
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 Dest
11 {
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 Inner
20 {
21 public long D1;
22 public Guid D2;
23 }
24
25 Sourse src = new Sourse
26 {
27 A = 1,
28 B = 0M,
29 C = "2011/9/21 0:00:00",
30 D = new Inner
31 {
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 Dest
11 {
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 Inner
20 {
21 public long D1;
22 public Guid D2;
23 }
24
25 public class Inner2
26 {
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 members
15 .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 else
31 {
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介绍的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。