創建自己的AutoMapper類,從此不用手動建立對象映射

AutoMapper-對象自動映射

在企業應用開發中,很多時候都需要將一個對象轉換爲另一個對象,比如說在WCF應用的開發中,需要將一個Entity轉換爲一個Contract對象。大部分情況下,這兩個對象會非常相似,有個相同的屬性名和類型。如果每次我們都要手寫這中轉換代碼,不但容易出錯,而且工作量也非常可觀。所以開發一個自動映射類還是非常必要的。我們把此類命名爲AutoMapper,它應該實現以下功能:

如果分屬兩個類的兩個屬性在滿足某一類條件時可以映射:

  1. 集合映射,所有實現了IEnumerable<T>的類型都作爲集合類映射。集合映射又包括以下兩種映射:

    1. 基本集合映射,集合內元素類型相同。

    2. Twin集合映射,集合內元素類型不同,而且都爲引用類型但不是string

  2. 單值映射,非集合映射的屬性,使用單值映射。單值映射也包括以下兩種映射。

    1. 基本類型映射,屬性類型相同者,使用此映射。

    2. Twin映射,屬性類型不同且都爲引用類型但不是string類型,使用此類映射。

對於以上映射,映射成立,必須滿足一下條件:

  1. 屬性名相同

  2. 源屬性必須可讀。

  3. 如果目標屬性是值類型或者string類型,那麼目標屬性必須可寫。

  4. 如果目標屬性是引用類型,那麼目標屬性可讀,而且可以得到一個非空值,否則目標屬性必須可寫。

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

            }

 

//枚舉類型將會按照枚舉類型的基類型進行設置。本質上枚舉類型是一個整數如intshortbyte等等。

            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的時候,只要兩個類型相同,不論誰是sourcetarget類型,都會得到相同的配置。

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

       }

    }

 

 


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章