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

相关文章
相关标签/搜索