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