zoukankan      html  css  js  c++  java
  • ABP项目中的使用AutoMapper

    AutoMapper之ABP项目中的使用

    最近在研究ABP项目,昨天写了Castle Windsor常用介绍以及其在ABP项目的应用介绍 欢迎各位拍砖,有关ABP的介绍请看阳光铭睿 博客

    AutoMapper只要用来数据转换,在园里已经有很多这方面文章了,本文主要介绍其在实际项目常用总结,以及在ABP项目中的应用介绍。AutoMapper应用非常简单,大家稍微看下文档就可以上手,但是性能不好啊,所以一般用在后台项目,对外的项目千万不要用。就那NOP来说吧,它也是把AutoMapper放在后台项目使用,商城前台的项目是不敢用的。

    有关性能的问题本文没有涉及到,想了解的请参考EmitMapper,AutoMapper,NLiteMapper和手工映射性能大比拼 和 NLiteMapper与EmitMapper性能简单比较

    下面主要讲下项目的入门和项目中的使用。

    AutoMapper使用只要两步,配置和Mapper,一般的在项目中我们会在Global中进行配置

    配置映射关系

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public class Source
    {
       public int SomeValue { getset; }
    }
     
    public class Destination
    {
       public int SomeValue { getset; }
    }
     
    //这个就是配置映射关系
    Mapper.CreateMap<Source, Destination>();

    然后就是Mapper

    1
    2
    3
    4
    5
    6
    7
    Source source = new Source()
    {
        SomeValue = 1
    };
     
    var destination = Mapper.Map<Source, Destination>(source);
    Console.WriteLine(destination.SomeValue);//1

    是不是很简单,这是最简单的使用了,当然AutoMapper是个“机关枪”,这个只是它的最简单使用。下面在介绍几点常用的功能。还是上面那个例子,只是字段变了

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public class Source
    {
        public int SomeValue { getset; }
    }
     
    public class Destination
    {
        public int SomeValuefff { getset; }
    }
     
    Mapper.CreateMap<AddressDto, Address>();

    这样子字段都不一样明细是不能映射的嘛,所有呢我们可以用Mapper.AssertConfigurationIsValid()来验证,就会AutoMapperConfigurationException异常,

    选择忽略相关字段

    1
    2
    Mapper.CreateMap<Source, Destination>()
            .ForMember(dest => dest.SomeValuefff, opt => opt.Ignore());

    类型转换,自定义类型转换

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public class Source
    {
        public string Value1 { getset; }
        public string Value2 { getset; }
        public string Value3 { getset; }
    }
     
    public class Destination
    {
        public int Value1 { getset; }
        public DateTime Value2 { getset; }
        public Type Value3 { getset; }
    }

    Source要转Destination,但是第二和第三的字段类型都不一致,所以我们可以自定义类型转换,下面看下转换函数ConvertUsing一般最常用第二种,接受一个ITypeConverter

    1
    2
    3
    void ConvertUsing(Func<TSource, TDestination> mappingFunction);
    void ConvertUsing(ITypeConverter<TSource, TDestination> converter);
    void ConvertUsing<TTypeConverter>() where TTypeConverter : ITypeConverter<TSource, TDestination>;

    下面看下ITypeConverter接口

    1
    2
    3
    4
    public interface ITypeConverter<TSource, TDestination>
    {
        TDestination Convert(ResolutionContext context);
    }

    我们可以继承这个接口队Convert进行重写

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    public class DateTimeTypeConverter : ITypeConverter<string, DateTime>
    {
        public DateTime Convert(ResolutionContext context)
        {
            return System.Convert.ToDateTime(context.SourceValue);
        }
    }
     
    public class TypeTypeConverter : ITypeConverter<string, Type>
    {
        public Type Convert(ResolutionContext context)
        {
              return context.SourceType;
        }
    }

    这样我们就可以映射了,下面看下完整代码

    好了,上面把 AutoMapper在项目中常用的方法都介绍完了,再介绍ABP之前我们先看下NOP是怎么使用的吧,由于代码较长省略部分

    好了,终于可以到ABP的了,ABP对AutoMapper的使用总结出来两点,1、在模块中初始化配置,2、遍历bin目录下所有的Types判断哪些类是否被定义为需要转换的Attribute

    在模块中初始化配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    public class AbpAutoMapperModule : AbpModule
    {
        private readonly ITypeFinder _typeFinder;
     
        private static bool _createdMappingsBefore;
        private static readonly object _syncObj = new object();
         
        public AbpAutoMapperModule(ITypeFinder typeFinder)
        {
            _typeFinder = typeFinder;
        }
     
        private void FindAndAutoMapTypes()
        {
            var types = _typeFinder.Find(type =>
                type.IsDefined(typeof(AutoMapAttribute)) ||
                type.IsDefined(typeof(AutoMapFromAttribute)) ||
                type.IsDefined(typeof(AutoMapToAttribute))
                );
     
            foreach (var type in types)
            {
                AutoMapperHelper.CreateMap(type);
            }
        }
    }

    AbpAutoMapperModule 模块会在Global的时候被初始化,然后在PreInitialize的时候回调用到FindAndAutoMapTypes,有关模块是怎么初始化的我想再写一篇介绍。下面我们看下_typeFinder吧

    上面_typeFinder.Find调用的是TypeFinder的Find方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    public Type[] Find(Func<Type, bool> predicate)
    {
        return GetAllTypes().Where(predicate).ToArray();
    }
     
    public Type[] FindAll()
    {
        return GetAllTypes().ToArray();
    }
     
    private List<Type> GetAllTypes()
    {
        var allTypes = new List<Type>();
     
        foreach (var assembly in AssemblyFinder.GetAllAssemblies().Distinct())
        {
            try
            {
                Type[] typesInThisAssembly;
     
                try
                {
                    typesInThisAssembly = assembly.GetTypes();
                }
                catch (ReflectionTypeLoadException ex)
                {
                    typesInThisAssembly = ex.Types;
                }
     
                if (typesInThisAssembly.IsNullOrEmpty())
                {
                    continue;
                }
     
                allTypes.AddRange(typesInThisAssembly.Where(type => type != null));
            }
            catch (Exception ex)
            {
                Logger.Warn(ex.ToString(), ex);
            }
        }
     
        return allTypes;
    }

    好吧,上面代码有点多,但是很简单,就是获取所有的Types,我们看下关键代码AssemblyFinder.GetAllAssemblies()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    public class WebAssemblyFinder : IAssemblyFinder
    {
        /// <summary>
        /// This return all assemblies in bin folder of the web application.
        /// </summary>
        /// <returns>List of assemblies</returns>
        public List<Assembly> GetAllAssemblies()
        {
            var assembliesInBinFolder = new List<Assembly>();
     
            var allReferencedAssemblies = BuildManager.GetReferencedAssemblies().Cast<Assembly>().ToList();
            var dllFiles = Directory.GetFiles(HttpRuntime.AppDomainAppPath + "bin\""*.dll", SearchOption.TopDirectoryOnly).ToList();
     
            foreach (string dllFile in dllFiles)
            {
                var locatedAssembly = allReferencedAssemblies.FirstOrDefault(asm => AssemblyName.ReferenceMatchesDefinition(asm.GetName(), AssemblyName.GetAssemblyName(dllFile)));
                if (locatedAssembly != null)
                {
                    assembliesInBinFolder.Add(locatedAssembly);
                }
            }
     
            return assembliesInBinFolder;
        }
    }

    看看吧,这代码是或bin目录下面的dll,好丧心病狂啊,回到刚刚AbpAutoMapperModule 的获取FindAndAutoMapTypes方法。在获取所有的Types之后我们就要判断这个类是否是被标识了

    AutoMapAttribute、AutoMapFromAttribute和AutoMapToAttribute

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    private void FindAndAutoMapTypes()
    {
        var types = _typeFinder.Find(type =>
            type.IsDefined(typeof(AutoMapAttribute)) ||
            type.IsDefined(typeof(AutoMapFromAttribute)) ||
            type.IsDefined(typeof(AutoMapToAttribute))
            );
     
        foreach (var type in types)
        {
            AutoMapperHelper.CreateMap(type);
        }
    }

    获取之后再我下的Demo中就只有一个UserDto类被标识了 

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    namespace AbpDemo.Application.Users.Dto
    {
        [AutoMapFrom(typeof(User))]
        public class UserDto : EntityDto<long>
        {
            public string UserName { getset; }
     
            public string Name { getset; }
             
            public string Surname { getset; }
             
            public string EmailAddress { getset; }
        }
    }

    接下来就是遍历所有的types进行配置了AutoMapperHelper.CreateMap(type);配置也很简单 看下代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    public static void CreateMap<TAttribute>(Type type)
        where TAttribute : AutoMapAttribute
    {
        if (!type.IsDefined(typeof (TAttribute)))
        {
            return;
        }
     
        foreach (var autoMapToAttribute in type.GetCustomAttributes<TAttribute>())
        {
            if (autoMapToAttribute.TargetTypes.IsNullOrEmpty())
            {
                continue;
            }
     
            foreach (var targetType in autoMapToAttribute.TargetTypes)
            {
                if (autoMapToAttribute.Direction.HasFlag(AutoMapDirection.To))
                {
                    Mapper.CreateMap(type, targetType);
                }
     
                if (autoMapToAttribute.Direction.HasFlag(AutoMapDirection.From))
                {
                    Mapper.CreateMap(targetType, type);                               
                }
            }
        }
    }

    好了,表达能力不是很好,各位就勉强看下吧。终于写完了。发现作者很喜欢用Attribute来过滤。这边AutoMapper是这样,UnitOfWork也是这样,其实这样也是挺方便的,有点AOP的切面的感觉,值得学习。 

    参考文章:

    http://www.cnblogs.com/netcasewqs/archive/2011/04/13/2014684.html

    http://www.cnblogs.com/repository/archive/2011/04/08/2009713.html

    https://github.com/AutoMapper/AutoMapper/wiki/Configuration-validation

    http://www.cnblogs.com/youring2/p/automapper.html

    http://www.cnblogs.com/1-2-3/p/AutoMapper-Best-Practice.html

  • 相关阅读:
    微信小程序开发教程目录
    Head First设计模式之目录
    CentOS安装NodeJS
    docker镜像打包
    .net core 2.2部署到Windows Server 2012 R2 standard
    MySQL job/定时任务/event 学习
    “sgen.exe”未能运行。文件名或扩展名太长
    Linux 服务器如何设置文件和文件夹的读写权限
    添加“Git Bash Here”到右键菜单
    .Net Core中文编码问题
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/4836973.html
Copyright © 2011-2022 走看看