用户配置

默认的配置器可以很容易的实现用户配置:创建一个DefaultMapConfig的实例,调用特定的方法并将其传入GetMapper 方法

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


"DefaultMapConfig"包含了许多的方法,一些方法接受一个委托。委托的参数就是该方法的泛型类型的字段或者属性

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

委托方法接受参数就是Foo对象或者其子类的所有字段或者属性

如果你想配置方法能够接受任何的类型,你能够使用object类型:

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

 

自定义转换

你能够自定义源类型的每个元素到目标类型每个元素的转换:

/// <summary>
/// 定义用户转换
/// </summary>
/// <typeparam name="From">源类型</typeparam>
/// <typeparam name="To">目标类型</typeparam>
/// <param name="converter">定义源类型转换为目标类型的函数</param>
/// <returns></returns>
DefaultMapConfig ConvertUsing<From, To>(Func<From, To> converter)


例子:

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

泛型的自定义转换

你也许需要将一个泛型类型转换为另一个泛型类型,比如: "HashSet<T>" 转化为 "List<T>" 。应为你不知道具体的参数类型,所以不能使用"ConvertUsing",而应该使用 "ConvertGeneric"方法:

/// <summary>
/// 定义泛型转换。 It is able to convert not one particular class but all generic family
/// providing a generic converter.
/// </summary>
/// <param name="from">源类型。 Can be also generic class or abstract array.</param>
/// <param name="to">目标类型。 Can be also generic class or abstract array.</param>
/// <param name="converterProvider">类型转换提供器</param>
/// <returns></returns>
public IMappingConfigurator ConvertGeneric(Type from, Type to, ICustomConverterProvider converterProvider);
...
/// <summary>
/// 类型转换提供器
/// </summary>
public interface ICustomConverterProvider
{
	/// <summary>
	/// 类型转换
	/// </summary>
	/// <param name="from">源类型。 Can be also generic class or abstract array.</param>
	/// <param name="to">目标类型。 Can be also generic class or abstract array.</param>
	/// <param name="mappingConfig">映射配置</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; }
}


为了自定义泛型转换,你需要创建一个包含泛型转换方法的泛型类:

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


下一步,是为 "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>"

使用泛型转换:

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

空转换

他用于源类型中可空的属性或者字段转化为目标类型中不可空的属性或者字段:

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


如果我们将“Source”类型的实体映射到“Destination”类型的实体,Emit Mapper 决定Source对象的“i”字段为空的时候,如何映射到“Destination”的“i”字段:

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

忽略元素

如果你不想映射某些属性或者字段,使用"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);
}

自定义构造对象

有时候,我们不想使用默认的构造函数,而是想自定义构造方法:

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

浅映射和深映射

 

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

名称匹配

如果源类型的元素名称和目标类型的元素名称不匹配。你能够使用"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);
}


第一个参数是源类型的元素名称,第二个参数是目标类型的元素名称

映射后操作

在映射执行以后的操作:

/// <summary>
/// 定义需要执行的操作
/// </summary>
/// <typeparam name="T">需要操作的对象类型</typeparam>
/// <param name="postProcessor"></param>
/// <returns></returns>
public IMappingConfigurator PostProcess<T>(ValuesPostProcessor<T> postProcessor)


例子:

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;} )
	);
posted @ 2014-11-19 10:24  争世不悔  阅读(267)  评论(0编辑  收藏  举报