zoukankan      html  css  js  c++  java
  • EmitMapper自动映射工具

             在实体与DTO之间,我们一般都需要进行映射。如果手动的来进行转换,实在是太麻烦。所以就产生了很多映射工具,比如AutoMapper,EmitMapper。而经过一些对比,EmitMapper比较快,接近于手工转换的速度。

              EmitMapper使用非常简单,对于那种属性名一样的,直接使用默认映射即可。如:

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

            它就会自动的把From对象的值赋给To对象;

           对于有一定规则的,可以使用一些扩展的方法,如:

    image

          通过使用ConvertUsing可以配置一些组合映射.比如这个字段可以是某两个字段拼接,或者其他一些计算.具体可以参见一些使用例子:

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

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

              但是在使用的过程中,发现了一个问题,就是本身默认是如果属性名相同就直接映射,而使用ConvertUsing是可以自定义映射条件的.但是如果使用了ConvertUsing,那么你必需为里面每个字段都指定映射的条件.而其实真正想用的是,在实体与DTO之间,其实大部分字段的属性名称是相同的,对于这些字段,我们是想直接默认映射,而对于其他一些需要自定义映射的字段,来自定义映射规则.但实际上却达不到这种效果,一般人也不想去把每个字段的映射规则配置一下,如果这样,还不如手动写呢.

              后来在网上找到了一个例子,可以使用FlatteringConfig这个自定义配置,这个类在EmitMapper的源码中是有的,但是不是在当前的解决方案下,而是在一个叫EMConfigurations的项目里.可以这样使用这个自定义配置.

    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

  • 相关阅读:
    docker安装mtproto及报错解决方案
    Centos7下创建和管理用户
    GitHub项目绑定自己的域名
    navicate远程连接mysql8.0失败
    Java反射
    Spring AOP
    Spring注解
    学习进度笔记20
    学习进度笔记19
    学习进度笔记18
  • 原文地址:https://www.cnblogs.com/xiaoxiangfeizi/p/3563874.html
Copyright © 2011-2022 走看看