EmitMapper自動映射工具

         在實體與DTO之間,咱們通常都須要進行映射。若是手動的來進行轉換,實在是太麻煩。因此就產生了不少映射工具,好比AutoMapper,EmitMapper。而通過一些對比,EmitMapper比較快,接近於手工轉換的速度。html

          EmitMapper使用很是簡單,對於那種屬性名同樣的,直接使用默認映射便可。如:app

ObjectsMapper<From,To> mapper = ObjectMapperManager.DefaultInstance.GetMapper<From,To>();
to = mapper.Map(from);

        它就會自動的把From對象的值賦給To對象;ide

       對於有必定規則的,可使用一些擴展的方法,如:工具

image

      經過使用ConvertUsing能夠配置一些組合映射.好比這個字段能夠是某兩個字段拼接,或者其餘一些計算.具體能夠參見一些使用例子:ui

       http://www.cnblogs.com/aaa6818162/archive/2012/06/21/2557879.htmlthis

      http://www.cnblogs.com/wuhong/archive/2011/09/21/2184313.htmlspa

          可是在使用的過程當中,發現了一個問題,就是自己默認是若是屬性名相同就直接映射,而使用ConvertUsing是能夠自定義映射條件的.可是若是使用了ConvertUsing,那麼你必需爲裏面每一個字段都指定映射的條件.而其實真正想用的是,在實體與DTO之間,其實大部分字段的屬性名稱是相同的,對於這些字段,咱們是想直接默認映射,而對於其餘一些須要自定義映射的字段,來自定義映射規則.但實際上卻達不到這種效果,通常人也不想去把每一個字段的映射規則配置一下,若是這樣,還不如手動寫呢.code

          後來在網上找到了一個例子,可使用FlatteringConfig這個自定義配置,這個類在EmitMapper的源碼中是有的,可是不是在當前的解決方案下,而是在一個叫EMConfigurations的項目裏.能夠這樣使用這個自定義配置.htm

public class User
{
  public Guid Id { get; set; }
  public Company Company { get; set; }
}    
 
public class Company
{
  public Guid Id { get; set; }
}
 
public class UserDTO
{
  public Guid Id { get; set; }
  public Guid CompanyId{ get; set; }
}
ObjectMapperManager.DefaultInstance.GetMapper<User, UserDTO>(
                new FlatteringConfig()
            );
var dto = mapper.Map(new User());

          這樣,它就會自動的給DTO中的CompanyId賦值,也會給Id賦值.不過有一個規則就是CompanyId的命名要有必定的規則,必定要是那個實體的名稱再加上這個實體裏面屬性的名稱.這樣才能進行自動的映射.(不過這時,若是那個實體爲null,那也有可能會報錯,沒有試過,後面有時間試一下).對象

          在運行的過程當中,發現了FlatteringConfig類的一個bug;這個類的原始代碼以下:

public class FlatteringConfig : DefaultMapConfig 
    {
        protected Func<string, string, bool> nestedMembersMatcher;
 
        public FlatteringConfig()
        {
            nestedMembersMatcher = (m1, m2) => m1.StartsWith(m2);
        }
 
        public override IMappingOperation[] GetMappingOperations(Type from, Type to)
        {
            var destinationMembers = GetDestinationMemebers(to);
            var sourceMembers = GetSourceMemebers(from);
            var result = new List<IMappingOperation>();
            foreach (var dest in destinationMembers)
            {
                var matchedChain = GetMatchedChain(dest.Name, sourceMembers).ToArray();
                if (matchedChain == null || matchedChain.Length == 0)
                {
                    continue;
                }
                result.Add(
                    new ReadWriteSimple
                    {
                        Source = new MemberDescriptor(matchedChain),
                        Destination = new MemberDescriptor(new[] { dest })
                    }
                );
            }
            return result.ToArray();
        }
 
        public DefaultMapConfig MatchNestedMembers(Func<string, string, bool> nestedMembersMatcher)
        {
            this.nestedMembersMatcher = nestedMembersMatcher;
            return this;
        }
 
        private List<MemberInfo> GetMatchedChain(string destName, List<MemberInfo> sourceMembers)
        {
            var matches = sourceMembers.Where(s => MatchMembers(destName, s.Name) || nestedMembersMatcher(destName, s.Name));
            int len = 0;
            MemberInfo match = null;
            foreach (var m in matches)
            {
                if (m.Name.Length > len)
                {
                    len = m.Name.Length;
                    match = m;
                }
            }
            if (match == null)
            {
                return null;
            }
            var result = new List<MemberInfo> { match };
            if (!MatchMembers(destName, match.Name))
            {
                result.AddRange(
                    GetMatchedChain(destName.Substring(match.Name.Length), GetDestinationMemebers(match))
                );
            }
            return result;
        }
 
        private static List<MemberInfo> GetSourceMemebers(Type t)
        {
            return GetMemebers(t)
                .Where(
                    m => 
                        m.MemberType == MemberTypes.Field || 
                        m.MemberType == MemberTypes.Property ||
                        m.MemberType == MemberTypes.Method
                )
                .ToList();
        }
 
        private static List<MemberInfo> GetDestinationMemebers(MemberInfo mi)
        {
            Type t;
            if (mi.MemberType == MemberTypes.Field)
            {
                t = mi.DeclaringType.GetField(mi.Name).FieldType;
            }
            else
            {
                t = mi.DeclaringType.GetProperty(mi.Name).PropertyType;
            }
            return GetDestinationMemebers(t);
        }
 
        private static List<MemberInfo> GetDestinationMemebers(Type t)
        {
            return GetMemebers(t).Where(m => m.MemberType == MemberTypes.Field || m.MemberType == MemberTypes.Property).ToList();
        }
 
        private static List<MemberInfo> GetMemebers(Type t)
        {
            BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public;
            return t.GetMembers(bindingFlags).ToList();
        }
    }

      在GetMappingOperations方法中,修改瞭如下代碼:(原代碼中是一個判斷在一塊兒,改爲分做兩個判斷)

var matchedChainSingle = GetMatchedChain(dest.Name, sourceMembers);
               if (matchedChainSingle == null)
               {
                   continue;
               }
               var matchedChain = matchedChainSingle.ToArray();
               if (matchedChain.Length == 0)
               {
                   continue;
               }

 

      參考資料:

      http://emitmapper.codeplex.com/

      http://stackoverflow.com/questions/9619265/emit-mapper-flattering-and-property-name-mismatch

      http://emitmapper.codeplex.com/SourceControl/changeset/view/69894#1192663

      http://emitmapper.codeplex.com/SourceControl/changeset/view/42128#691132

     http://stackoverflow.com/questions/12542749/emitmapper-flattering-config-nullreferenceexception?rq=1

相關文章
相關標籤/搜索