AutoMapper-对象自动映射
在企业应用开发中,很多时候都需要将一个对象转换为另一个对象,比如说在WCF应用的开发中,需要将一个Entity转换为一个Contract对象。大部分情况下,这两个对象会非常相似,有个相同的属性名和类型。如果每次我们都要手写这中转换代码,不但容易出错,而且工作量也非常可观。所以开发一个自动映射类还是非常必要的。我们把此类命名为AutoMapper,它应该实现以下功能:
如果分属两个类的两个属性在满足某一类条件时可以映射:
-
集合映射,所有实现了IEnumerable<T>的类型都作为集合类映射。集合映射又包括以下两种映射:
-
基本集合映射,集合内元素类型相同。
-
Twin集合映射,集合内元素类型不同,而且都为引用类型但不是string。
-
-
单值映射,非集合映射的属性,使用单值映射。单值映射也包括以下两种映射。
-
基本类型映射,属性类型相同者,使用此映射。
-
Twin映射,属性类型不同且都为引用类型但不是string类型,使用此类映射。
-
对于以上映射,映射成立,必须满足一下条件:
-
属性名相同
-
源属性必须可读。
-
如果目标属性是值类型或者string类型,那么目标属性必须可写。
-
如果目标属性是引用类型,那么目标属性可读,而且可以得到一个非空值,否则目标属性必须可写。
AutoMapper类型还应该提供配置功能,调用这可以为某个映射配置,配置包括忽略某个属性映射,即使该属性满足映射条件。将两个不同名的属性配置为可以映射,属性类型必须满足映射条件,否则运行时,该属性还是不能映射。关于映射配置,将在后面讲述。
AutoMapper类应该有如下方法定义:
public static class AutoMapper
{
/// <summary>
/// 将一个集合转换为另一个集合,两种类型必须是可转换的.
/// </summary>
public static IEnumerable<TTarget>MapTo<TSource, TTarget>(this IEnumerable<TSource> sourceItems)
where TSource : class
where TTarget : class, new();
/// <summary>
/// 将源对象转换为目标类型的对象。
/// </summary>
public static TTarget MapTo<TSource,TTarget>(this TSource source)
where TSource : class
where TTarget : class, new();
/// <summary>
/// 将源对象转换为目标类型的对象。目标对象需要调用者事先创建出来
/// </summary>
public static TTarget MapTo<TSource,TTarget>(this TSource source, TTarget target)
where TSource : class
where TTarget : class;
}
AutoMapper类依赖于TypeMap<TSource,TTarget>类型来映射指定了源和目标类型的映射。TypeMap<TSource,TTarget>类型将会根据两个类型来建立一个PropertyMap对象的集合,PropertyMap对象负责单个属性的映射。从PropertyMap的继承关系如下:
interface IpropertyMap
abstract classPropertyMap : IpropertyMap
class BasicPropertyMap : PropertyMap
class TwinPropertyMap : PropertyMap
abstract class EnumerablePropertyMap :PropertyMap
classBasicEnumerablePropertyMap : EnumerablePropertyMap
classTwinEnumerablePropertyMap : EnumerablePropertyMap
继承层次的叶子类跟前面讲述的集中映射类型一一对应。
为了提高映射性能,使用了OpenDelegate。关于开放委托,网上有详细介绍。
提供了MapConfiguration<TSource,TTarget>类来完成配置映射的任务。该类具有以下两个方法来完成映射配置。
public class MapConfiguration<TSource, TTarget> : MapConfiguration
where TSource : class
where TTarget : class
{
public MapConfiguration<TSource, TTarget>Ignore<TProperty>(Expression<Func<TSource, TProperty>> propExp);
public MapConfiguration<TSource, TTarget>MapName<TProperty>(
Expression<Func<TSource, TProperty>> sourcePropExp,
Expression<Func<TTarget, TProperty>> targetPropExp);
public bool IsIgnored(string propName);
public bool TryGetMappedProperty(string sourceProp, out string targetProp);
}
下面是几个使用AutoMapper类的例子:
User u = new User()
{
Bool = true,
Byte = 1,
Char = 'f',
DateTime = DateTime.Now
};
var c = u.MapTo<User, UserContract>();
Assert.AreEqual(u.Bool, c.Bool);
Assert.AreEqual(u.Byte, c.Byte);
Assert.AreEqual(u.Char, c.Char);
Assert.AreEqual(u.DateTime,c.DateTime);
节省的代码是显而易见的,而且性能也不差。
下面是使用配置的例子,一定要在调用MapTo之前配置好映射关系,否则之后配置的映射关系是不起作用的。
[ClassInitialize()]
public static void MyClassInitialize(TestContext testContext)
{
AutoMapper.Configure<User, UserContract>()
.MapName(user =>user.AccountName, uc => uc.UserName)
.Ignore(user => user.Byte);
}
User u = new User()
{
Bool = true,
Byte = 1,
};
var c = u.MapTo<User, UserContract>();
Assert.AreEqual(u.Byte, 1);
Assert.AreEqual(c.Byte, 0);
为不同名属性指定映射:
User u = new User()
{
AccountName = "mac"
};
var c = u.MapTo<User, UserContract>();
Assert.AreEqual(u.AccountName,c.UserName);
下面是代码部分:
该帮助类用来封装一下反射代码:
internal static class ReflectionUtils
{
//在运行时得到一个实现了IEnumerable<T>类型的泛型参数的类型。
public static Type GetElementTypeOfIEnumerable(Type type)
{
if (type.IsArray)
{
return type.GetElementType();
}
if (type.IsInterface)
{
Type def =type.GetGenericTypeDefinition();
if (def == typeof(IEnumerable<>))
{
returntype.GetGenericArguments()[0];
}
}
else
{
Type interfaceType = type
.GetInterfaces()
.FirstOrDefault(t =>t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IEnumerable<>));
if (interfaceType != null)
{
returninterfaceType.GetGenericArguments()[0];
}
}
return null;
}
//判断一个类型是引用类型但不是string类型。
public static bool IsReferenceTypeButString(Type type)
{
return !type.IsValueType &&type != typeof(string);
}
//判断一个类型是值类型或者是string类型。
public static bool IsValueTypeOrString(Type type)
{
return type.IsValueType || type == typeof(string);
}
//创建一个集合,如果是数组,就会使用count参数创建指定长度的数组
public static IEnumerable<object> CreateCollection(Type collType, int count)
{
object instance = null;
var elementType = ReflectionUtils.GetElementTypeOfIEnumerable(collType);
if (collType.IsArray)
{
instance = Array.CreateInstance(elementType,count);
}
else if (collType.IsInterface)
{
var listType = typeof(List<>).MakeGenericType(elementType);
if(collType.IsAssignableFrom(listType))
{
instance = Activator.CreateInstance(listType);
}
}
else
{
instance =CreateInstance(collType);
}
return instance as IEnumerable<object>;
}
//将sources中的对象拷贝到目标集合中。Target必须是一个数组或者实现了Ilist接口。
public static void CopyElements(object[] source, object target)
{
if(source == null)
{
throw new ArgumentNullException("source");
}
if (target == null)
{
throw new ArgumentNullException("target");
}
Array targetArray = target as Array;
if (targetArray != null)
{
Array.Copy(source, targetArray,targetArray.Length);
}
else if (target is IList)
{
IList targetList = target as IList;
foreach (var item in source)
{
targetList.Add(item);
}
}
}
//为了递归进行映射,需要用反射创建对象实例,如果不能创建,那么映射过程将会失败。
public static object CreateInstance(Type type)
{
try
{
return Activator.CreateInstance(type);
}
catch (Exception e)
{
throw new MappingException("Fail to do mapping,can't create instance for type: " + type, e);
}
}
}
//下面的类使用了开放委托,以非常高的性能获取和设置指定属性的值。
internal class PropertyInvoker
{
public PropertyInvoker(PropertyInfo property)
{
if (property == null)
{
throw new ArgumentNullException("property");
}
//枚举类型将会按照枚举类型的基类型进行设置。本质上枚举类型是一个整数如int,short,byte等等。
Property = property;
Type propType =Property.PropertyType.IsEnum ?
Property.PropertyType.GetEnumUnderlyingType() : Property.PropertyType;
//Get public getter method.
var getMethod =property.GetGetMethod();
if (getMethod != null)
{
Type delType = typeof(Func<,>).MakeGenericType(Property.DeclaringType,propType);
Getter = Delegate.CreateDelegate(delType, null, getMethod);
}
//Get public setter method
var setMethod =property.GetSetMethod();
if (setMethod != null)
{
Type delType = typeof(Action<,>).MakeGenericType(Property.DeclaringType,propType);
Setter = Delegate.CreateDelegate(delType, null, setMethod);
}
}
public PropertyInfo Property { get; private set; }
public Delegate Getter { get; private set; }
public Delegate Setter { get; private set; }
}
//下面的提供了从一个类型上得到所有PropertyInvoker实例的功能。实例被存放到字典中,并使用属性名索引。
internal class PropertyInvokerBuilder
{
private static ConcurrentDictionary<Type, PropertyInvokerBuilder> _builders;
private static Func<Type, PropertyInvokerBuilder> _createBuilder;
static PropertyInvokerBuilder()
{
_builders = new ConcurrentDictionary<Type, PropertyInvokerBuilder>();
_createBuilder = t => new PropertyInvokerBuilder(t);
}
/// <summary>
/// Get PropertyInvokerBuilder for specified type,PropertyInvokerBuilder for same type is cached.
/// </summary>
public static PropertyInvokerBuilder Get(Type owner)
{
return _builders.GetOrAdd(owner, _createBuilder);
}
public Type OwnerType { get; private set; }
public Dictionary<string, PropertyInvoker> Invokers { get; private set; }
private PropertyInvokerBuilder(Type owner)
{
if (owner == null)
{
throw new ArgumentNullException("owner");
}
OwnerType = owner;
Invokers = OwnerType
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.ToDictionary(prop => prop.Name, prop => new PropertyInvoker(prop));
}
}
//下面的接口限定了所有属性映射的需要实现的行为。就是将源对象的属性值映射到目标对象的属性内。
internal interface IPropertyMap
{
void MapValue<TSource, TTarget>(TSource source, TTargettarget)
where TSource : class
where TTarget : class;
}
//下面的类作为所有属性映射的基类,共享了一些实现。
internal abstract class PropertyMap : IPropertyMap
{
protected static MethodInfo _mapGenericValue;
static PropertyMap()
{
//为构建开放委托准备方法对象。
_mapGenericValue = typeof(PropertyMap).GetMethod("MapGenericValue", BindingFlags.Instance | BindingFlags.NonPublic);
}
//用于获取或者设置属性值的PropertyInvoker对象。
protected PropertyInvoker _sourceInvoker;
protected PropertyInvoker _targetInvoker;
public PropertyMap(PropertyInvoker sourceInvoker, PropertyInvoker targetInvoker)
{
_sourceInvoker = sourceInvoker;
_targetInvoker = targetInvoker;
}
//派生类必须实现如何来映射属性值。
public abstract void MapValue<TSource, TTarget>(TSource source, TTargettarget)
where TSource : class
where TTarget : class;
//按照泛型方式来获取和设置属性值将会大大提高性能。
protected void MapGenericValue<TSource, TTarget, TValue>(TSourcesource, TTarget target)
where TSource : class
where TTarget : class
{
TValue v = ((Func<TSource,TValue>)_sourceInvoker.Getter)(source);
((Action<TTarget,TValue>)_targetInvoker.Setter)(target, v);
}
}
//下面的类用于实现基本属性映射。运行时将会直接调用开放委托。性能非常不错。
internal class BasicPropertyMap : PropertyMap
{
private Delegate _mapDelegate;
public BasicPropertyMap(PropertyInvoker sourceInvoker, PropertyInvoker targetInvoker, Type propType)
: base(sourceInvoker,targetInvoker)
{
Type sourceOwnerType =_sourceInvoker.Property.DeclaringType;
Type targetOwnerType =_targetInvoker.Property.DeclaringType;
var method =_mapGenericValue.MakeGenericMethod(sourceOwnerType, targetOwnerType, propType);
var delType = typeof(Action<,,>).MakeGenericType(typeof(PropertyMap), sourceOwnerType,targetOwnerType);
_mapDelegate = Delegate.CreateDelegate(delType, null, method);
}
public override void MapValue<TSource, TTarget>(TSource source, TTargettarget)
{
((Action<PropertyMap, TSource,TTarget>)_mapDelegate)(this, source, target);
}
}
//下面的类用于实现两个不同类(非集合类型)之间的映射。
internal class TwinPropertyMap : PropertyMap
{
public TwinPropertyMap(PropertyInvoker sourceInvoker, PropertyInvoker targetInvoker)
: base(sourceInvoker,targetInvoker)
{ }
public override void MapValue<TSource, TTarget>(TSource source, TTargettarget)
{
//使用了泛型参数的逆变,提高性能
object sourcePropValue = ((Func<TSource, object>)(_sourceInvoker.Getter))(source);
if(sourcePropValue == null)
{
return;
}
object targetPropValue = null;
//Check if property value iscreated by target class
if (_targetInvoker.Getter != null)
{
//使用了泛型参数的逆变,提高性能
targetPropValue = ((Func<TTarget, object>)_targetInvoker.Getter)(target);
}
//Create property value iftarget class doesn't create it.
if (targetPropValue == null &&_targetInvoker.Setter != null)
{
targetPropValue = ReflectionUtils.CreateInstance(_targetInvoker.Property.PropertyType);
//此处只能用后期绑定来调用。性能差。
_targetInvoker.Setter.DynamicInvoke(target, targetPropValue);
}
if (targetPropValue != null)
{
//递归映射引用类型的属性值。
AutoMapper.MapObject(sourcePropValue,targetPropValue);
}
}
}
//集合映射的基类型,提供共享实现。
internal abstract class EnumerablePropertyMap : PropertyMap
{
protected Type _targetCollType;
protected Type _targetElementType;
protected Type _sourceElementType;
public EnumerablePropertyMap(PropertyInvoker sourceInvoker, PropertyInvoker targetInvoker)
: base(sourceInvoker,targetInvoker)
{
_sourceElementType = ReflectionUtils.GetElementTypeOfIEnumerable(sourceInvoker.Property.PropertyType);
_targetCollType =targetInvoker.Property.PropertyType;
_targetElementType = ReflectionUtils.GetElementTypeOfIEnumerable(_targetCollType);
}
public override void MapValue<TSource, TTarget>(TSource source, TTargettarget)
{
var sourceValues = ((Func<TSource, IEnumerable<object>>)(_sourceInvoker.Getter))(source);
if (sourceValues == null)
{
return;
}
var coll = sourceValues as ICollection;
int count = coll != null ? coll.Count :sourceValues.Count();
if (count == 0)
{
return;
}
IEnumerable<object> targetEnumerable = null;
if (_targetInvoker.Getter != null)
{
targetEnumerable = ((Func<TTarget, IEnumerable<object>>)(_targetInvoker.Getter))(target);
}
if (targetEnumerable == null &&_targetInvoker.Setter != null)
{
targetEnumerable = ReflectionUtils.CreateCollection(_targetCollType,count);
_targetInvoker.Setter.DynamicInvoke(target,targetEnumerable);
}
if (targetEnumerable != null)
{
var targetValues =GetTargetValues(sourceValues);
ReflectionUtils.CopyElements(targetValues,targetEnumerable);
}
}
//对于指定源对象集合,得到转换后目标对象集合。
protected abstract object[] GetTargetValues(IEnumerable<object> sourceValues);
}
//当两个集合的元素类型相同时,使用源集合的值作为目标集合的元素值。
internal class BasicEnumerablePropertyMap : EnumerablePropertyMap
{
public BasicEnumerablePropertyMap(PropertyInvoker sourceInvoker, PropertyInvoker targetInvoker)
: base(sourceInvoker,targetInvoker) { }
protected override object[] GetTargetValues(IEnumerable<object> sourceValues)
{
return sourceValues.ToArray();
}
}
//当两个集合元素类型不同,而且都是引用类型但不是string的时候,对集合的每一个元素转换,然后将其添加到目标集合。
internal class TwinEnumerablePropertyMap : EnumerablePropertyMap
{
public TwinEnumerablePropertyMap(PropertyInvoker sourceInvoker, PropertyInvoker targetInvoker)
: base(sourceInvoker,targetInvoker)
{ }
protected override object[] GetTargetValues(IEnumerable<object> sourceValues)
{
return AutoMapper.MapCollection(_sourceElementType,_targetElementType, sourceValues).ToArray();
}
}
//下面的类提供了在两个类型之间建立映射的功能。他创建了一个IpropertyMap的集合,并使用其做实际映射。
internal static class TypeMap<TSource, TTarget>
where TSource : class
where TTarget : class
{
private static List<IPropertyMap> _propertyMaps;
//映射的规则在此处检测,其中用到了映射配置,后面会提到。
static TypeMap()
{
_propertyMaps = new List<IPropertyMap>();
var sourcePropInvokers = PropertyInvokerBuilder.Get(typeof(TSource)).Invokers;
var targetPropInvokers = PropertyInvokerBuilder.Get(typeof(TTarget)).Invokers;
//Create PropertyMap objectif two properties have same name and same type.
foreach (string propName in sourcePropInvokers.Keys)
{
PropertyInvoker sourcePropInvoker =sourcePropInvokers[propName];
if (sourcePropInvoker.Getter ==null)
{
continue;
}
if (MapConfiguration<TSource,TTarget>.Singleton.IsIgnored(propName))
{
continue;
}
string targetPropName;
if(!MapConfiguration<TSource, TTarget>.Singleton.TryGetMappedProperty(propName,out targetPropName))
{
targetPropName = propName;
}
PropertyInvoker targetPropInvoker = null;
if(!targetPropInvokers.TryGetValue(targetPropName, out targetPropInvoker))
{
continue;
}
var sourcePropType =sourcePropInvoker.Property.PropertyType;
var targetPropType =targetPropInvoker.Property.PropertyType;
if (ReflectionUtils.IsValueTypeOrString(sourcePropType)
&& ReflectionUtils.IsValueTypeOrString(targetPropType))
{
if (targetPropInvoker.Setter !=null)
{
Type propType = null;
//Properties have sameproperty type are mappable.
if (sourcePropType ==targetPropType)
{
propType = sourcePropType.IsEnum ?
propType =sourcePropType.GetEnumUnderlyingType() : sourcePropType;
}
else if (sourcePropType.IsEnum&& targetPropType.IsEnum)
{
//if they don't have sameproperty type, they should be enum type and have same underlying type.
var sourceBaseType =sourcePropType.GetEnumUnderlyingType();
var targetBaseType =targetPropType.GetEnumUnderlyingType();
if (sourceBaseType ==targetBaseType)
{
propType =sourceBaseType;
}
}
if (propType != null)
{
var map = new BasicPropertyMap(sourcePropInvoker,targetPropInvoker, propType);
_propertyMaps.Add(map);
}
}
}
else if (ReflectionUtils.IsReferenceTypeButString(sourcePropType)
&& ReflectionUtils.IsReferenceTypeButString(targetPropType))
{
bool isSourceEnumerable = typeof(IEnumerable<object>).IsAssignableFrom(sourcePropType);
bool isTargetEnumerable = typeof(IEnumerable<object>).IsAssignableFrom(targetPropType);
if (isSourceEnumerable&& isTargetEnumerable)
{
var sourceElementType = ReflectionUtils.GetElementTypeOfIEnumerable(sourcePropType);
var targetElementType = ReflectionUtils.GetElementTypeOfIEnumerable(targetPropType);
if (sourceElementType ==targetElementType)
{
var map = new BasicEnumerablePropertyMap(sourcePropInvoker,targetPropInvoker);
_propertyMaps.Add(map);
}
else if (ReflectionUtils.IsReferenceTypeButString(sourceElementType)
&& ReflectionUtils.IsReferenceTypeButString(sourceElementType))
{
var map = new TwinEnumerablePropertyMap(sourcePropInvoker,targetPropInvoker);
_propertyMaps.Add(map);
}
}
else if (sourcePropType ==targetPropType)
{
if (targetPropInvoker.Setter !=null)
{
var map = new BasicPropertyMap(sourcePropInvoker,targetPropInvoker, sourcePropType);
_propertyMaps.Add(map);
}
}
else
{
var map = new TwinPropertyMap(sourcePropInvoker,targetPropInvoker);
_propertyMaps.Add(map);
}
}
}
}
public static void Map(TSource source, TTarget target)
{
foreach (PropertyMap propMap in _propertyMaps)
{
propMap.MapValue<TSource,TTarget>(source, target);
}
}
}
//作为映射配置的基类,为所有泛型映射配置类提供共享存储。
public class MapConfiguration
{
//存储每个类型映射的忽略的属性
private ConcurrentDictionary<TypeMapKey, HashSet<string>> _ignoredProps;
//存储每个类型映射的不同属性名映射
private ConcurrentDictionary<TypeMapKey, Dictionary<string, string>> _mappedProps;
public MapConfiguration()
{
_ignoredProps = new ConcurrentDictionary<TypeMapKey, HashSet<string>>();
_mappedProps = new ConcurrentDictionary<TypeMapKey, Dictionary<string, string>>();
}
internal void IgnoreProperty(TypeMapKey key, string propName)
{
var props =_ignoredProps.GetOrAdd(key, k => new HashSet<string>());
if(!props.Add(propName))
{
throw new MappingException(propName + " has beenignored.");
}
}
internal void MapProperty(TypeMapKey key, string sourceProp, string targetProp)
{
var propMapping =_mappedProps.GetOrAdd(key, k => new Dictionary<string, string>());
if(propMapping.ContainsKey(sourceProp))
{
throw new MappingException(sourceProp + " has beenmapped.");
}
propMapping.Add(sourceProp,targetProp);
}
internal bool IsIgnored(TypeMapKey key, string propName)
{
HashSet<string> props;
if(!_ignoredProps.TryGetValue(key, out props))
{
TypeMapKey key2 = new TypeMapKey(key.Target, key.Source);
if(!_ignoredProps.TryGetValue(key2, out props))
{
return false;
}
_ignoredProps.TryAdd(key,props);
}
return props.Contains(propName);
}
internal bool TryGetMappedProperty(TypeMapKey key, string sourceProp, out string targetProp)
{
Dictionary<string, string> propMappings;
if(!_mappedProps.TryGetValue(key,out propMappings))
{
TypeMapKey key2 = new TypeMapKey(key.Target, key.Source);
if(!_mappedProps.TryGetValue(key2,out propMappings))
{
targetProp = null;
return false;
}
propMappings = propMappings.ToDictionary(pair=> pair.Value, pair => pair.Key);
_mappedProps.TryAdd(key,propMappings);
}
returnpropMappings.TryGetValue(sourceProp, out targetProp);
}
}
//按照Fluent API的方式提供映射配置。通过此派生类可以得到此映射的配置,而不用指定TypeMapKey。
public class MapConfiguration<TSource, TTarget> : MapConfiguration
where TSource : class
where TTarget : class
{
private static TypeMapKey _mapKey;
public static MapConfiguration<TSource, TTarget> Singleton { get; private set; }
static MapConfiguration()
{
_mapKey = new TypeMapKey(typeof(TSource), typeof(TTarget));
Singleton = new MapConfiguration<TSource, TTarget>();
}
public MapConfiguration<TSource, TTarget>Ignore<TProperty>(Expression<Func<TSource, TProperty>> propExp)
{
PropertyInfo prop =GetPropertyFromExpression(propExp, "propExp");
IgnoreProperty(_mapKey, prop.Name);
return this;
}
public MapConfiguration<TSource, TTarget>MapName<TProperty>(
Expression<Func<TSource,TProperty>> sourcePropExp,
Expression<Func<TTarget,TProperty>> targetPropExp)
{
var sourceProp =GetPropertyFromExpression(sourcePropExp, "sourcePropExp");
var targetProp =GetPropertyFromExpression(targetPropExp, "targetPropExp");
MapProperty(_mapKey,sourceProp.Name, targetProp.Name);
return this;
}
public bool IsIgnored(string propName)
{
return base.IsIgnored(_mapKey,propName);
}
public bool TryGetMappedProperty(string sourceProp, out string targetProp)
{
return base.TryGetMappedProperty(_mapKey,sourceProp, out targetProp);
}
private PropertyInfo GetPropertyFromExpression(LambdaExpression exp, string paramName)
{
if (exp == null)
{
throw new ArgumentNullException(paramName);
}
MemberExpression expMember = exp.Body as MemberExpression;
if (expMember == null || !(expMember.Member is PropertyInfo))
{
throw new ArgumentException(paramName, string.Format("Cannot get propertyname from {0} ", paramName));
}
return expMember.Member as PropertyInfo;
}
}
//TypeMapKey类非处简单,它唯一标识一个映射。当作为映射配置的key的时候,只要两个类型相同,不论谁是source或target类型,都会得到相同的配置。
internal class TypeMapKey
{
public TypeMapKey(Type source, Type target)
{
Source = source;
Target = target;
}
public Type Source;
public Type Target;
public override int GetHashCode()
{
return Source.GetHashCode() ^ Target.GetHashCode();
}
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
if (obj.GetType() != typeof(TypeMapKey))
{
return false;
}
TypeMapKey other = (TypeMapKey)obj;
return Source == other.Source && Target == other.Target;
}
}
//最后的AutoMapper类的实现就非常简单了。正如示例中那样,使用AutoMapper的方式非常简单。
public static class AutoMapper
{
private static MethodInfo _openMapToMethod;
private static MethodInfo _openCollMapToMethod;
private static ConcurrentDictionary<TypeMapKey, MethodInfo> _mapToMethods;
private static ConcurrentDictionary<TypeMapKey, MethodInfo> _collMapToMethods;
private static Func<TypeMapKey, MethodInfo> _createMapToMethod;
private static Func<TypeMapKey, MethodInfo> _createCollMapToMethod;
static AutoMapper()
{
MethodInfo[] mapToMethods = typeof(AutoMapper).GetMethods(BindingFlags.Public | BindingFlags.Static);
foreach (MethodInfo method in mapToMethods)
{
if (method.Name == "MapTo")
{
ParameterInfo[] parameters =method.GetParameters();
if (parameters.Length == 2)
{
_openMapToMethod =method;
}
else if (parameters.Length == 1)
{
if (parameters[0].ParameterType.IsGenericType)
{
_openCollMapToMethod = method;
}
}
}
}
_mapToMethods = new ConcurrentDictionary<TypeMapKey, MethodInfo>();
_collMapToMethods = new ConcurrentDictionary<TypeMapKey, MethodInfo>();
_createMapToMethod = key =>_openMapToMethod.MakeGenericMethod(key.Source, key.Target);
_createCollMapToMethod = key =>_openCollMapToMethod.MakeGenericMethod(key.Source, key.Target);
}
public static MapConfiguration<TSource, TTarget>Configure<TSource, TTarget>()
where TSource : class
where TTarget : class
{
return MapConfiguration<TSource,TTarget>.Singleton;
}
/// <summary>
/// Convert source items to target items.
/// Empty collection will be returned if source itemcollection is null.
/// </summary>
public static IEnumerable<TTarget> MapTo<TSource, TTarget>(this IEnumerable<TSource> sourceItems)
where TSource : class
where TTarget : class, new()
{
if (sourceItems == null)
{
yield break;
}
foreach (TSource source in sourceItems)
{
yield return source.MapTo<TSource,TTarget>();
}
}
/// <summary>
/// Map source to target with specified type.
/// </summary>
public static TTarget MapTo<TSource, TTarget>(this TSource source)
where TSource : class
where TTarget : class, new()
{
return source.MapTo<TSource,TTarget>(new TTarget());
}
/// <summary>
/// Map source to target with specified type.
/// Target object will be created if it is null.
/// </summary>
public static TTarget MapTo<TSource, TTarget>(this TSource source, TTargettarget)
where TSource : class
where TTarget : class
{
if (source == null)
{
return null;
}
if(target == null)
{
throw new ArgumentNullException("target");
}
TypeMap<TSource,TTarget>.Map(source, target);
return target;
}
internal static IEnumerable<object> MapCollection(Type sourceElementType, Type targetElementType, IEnumerable<object> sourceColl)
{
var key = new TypeMapKey(sourceElementType,targetElementType);
var mapTo =_collMapToMethods.GetOrAdd(key, _createCollMapToMethod);
var mappedObjs = (IEnumerable<object>)mapTo.Invoke(null, new[] { sourceColl });
return mappedObjs;
}
/// <summary>
/// Support for recursive map
/// </summary>
internal static void MapObject(object source, object target)
{
var key = new TypeMapKey(source.GetType(),target.GetType());
var mapTo =_mapToMethods.GetOrAdd(key, _createMapToMethod);
mapTo.Invoke(null, new[] { source, target });
}
}