AutoMapper之ABP项目中的使用介绍

摘要:
最近,我在研究ABP项目。昨天,我写了一篇关于CastleWindsor及其在ABP项目中的应用的常见介绍。欢迎发表评论。关于ABP的介绍,请参见阳光明锐博客AutoMapper。只要是用于数据转换的,园区内已经有很多关于这方面的文章。本文主要介绍了它在实际工程中的常见总结及其在ABP项目中的应用。AutoMapper的应用非常简单。您可以通过查看文档开始,但性能不好,因此通常在后台项目中使用,不应在外部项目中使用。就NOP而言,它还在后台项目中使用AutoMapper。不允许使用商场的前台项目。

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

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

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

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

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

配置映射关系

public class Source
{
   public int SomeValue { get; set; }
}

public class Destination
{
   public int SomeValue { get; set; }
}

//这个就是配置映射关系
Mapper.CreateMap<Source, Destination>();

然后就是Mapper

Source source = new Source()
{
    SomeValue = 1
};

var destination = Mapper.Map<Source, Destination>(source);
Console.WriteLine(destination.SomeValue);//1

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

    public class Source
    {
        public int SomeValue { get; set; }
    }

    public class Destination
    {
        public int SomeValuefff { get; set; }
    }

    Mapper.CreateMap<AddressDto, Address>();

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

选择忽略相关字段

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

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

    public class Source
    {
        public string Value1 { get; set; }
        public string Value2 { get; set; }
        public string Value3 { get; set; }
    }

    public class Destination
    {
        public int Value1 { get; set; }
        public DateTime Value2 { get; set; }
        public Type Value3 { get; set; }
    }

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

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

下面看下ITypeConverter接口

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

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

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

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

    public void Example()
    {
        Mapper.CreateMap<string, int>().ConvertUsing(Convert.ToInt32);
        Mapper.CreateMap<string, DateTime>().ConvertUsing(new DateTimeTypeConverter());
        Mapper.CreateMap<string, Type>().ConvertUsing<TypeTypeConverter>();
        Mapper.CreateMap<Source, Destination>();
        Mapper.AssertConfigurationIsValid();

        var source = new Source
        {
            Value1 = "5",
            Value2 = "01/01/2000",
            Value3 = "AutoMapperSamples.GlobalTypeConverters.GlobalTypeConverters+Destination"
        };

        Destination result = Mapper.Map<Source, Destination>(source);
        result.Value3.ShouldEqual(typeof (Destination));
    }

    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是怎么使用的吧,由于代码较长省略部分

    public class AutoMapperStartupTask : IStartupTask
    {
        public void Execute()
        {
            //TODO remove 'CreatedOnUtc' ignore mappings because now presentation layer models have 'CreatedOn' property and core entities have 'CreatedOnUtc' property (distinct names)
            
            //address
            Mapper.CreateMap<Address, AddressModel>()
                .ForMember(dest => dest.AddressHtml, mo => mo.Ignore())
                .ForMember(dest => dest.CustomAddressAttributes, mo => mo.Ignore())
                .ForMember(dest => dest.FormattedCustomAddressAttributes, mo => mo.Ignore())
                .ForMember(dest => dest.AvailableCountries, mo => mo.Ignore())
                .ForMember(dest => dest.AvailableStates, mo => mo.Ignore())
                .ForMember(dest => dest.FirstNameEnabled, mo => mo.Ignore())
                .ForMember(dest => dest.FirstNameRequired, mo => mo.Ignore())
                .ForMember(dest => dest.LastNameEnabled, mo => mo.Ignore())
                .ForMember(dest => dest.LastNameRequired, mo => mo.Ignore())
                .ForMember(dest => dest.EmailEnabled, mo => mo.Ignore())
                .ForMember(dest => dest.EmailRequired, mo => mo.Ignore())
                .ForMember(dest => dest.CompanyEnabled, mo => mo.Ignore())
                .ForMember(dest => dest.CompanyRequired, mo => mo.Ignore())
                .ForMember(dest => dest.CountryEnabled, mo => mo.Ignore())
                .ForMember(dest => dest.StateProvinceEnabled, mo => mo.Ignore())
...

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

在模块中初始化配置

    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方法

        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()

    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

        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类被标识了 

namespace AbpDemo.Application.Users.Dto
{
    [AutoMapFrom(typeof(User))]
    public class UserDto : EntityDto<long>
    {
        public string UserName { get; set; }

        public string Name { get; set; }
        
        public string Surname { get; set; }
        
        public string EmailAddress { get; set; }
    }
}

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

        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

免责声明:文章转载自《AutoMapper之ABP项目中的使用介绍》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇vue中的锚链接跳转问题C#给ExpandoObject动态添加对象下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Equinox OSGi系列之 创建自己的OSGi应用项目

1、摘要 前述文档我向大家展示了Equinox OSGi环境及其搭建配置。从本文开始,我们将详细讨论Bundle的开发及OSGi应用构建。 2、OSGi相关概念 在正式进入Bundle的设计与开发之前,我们先来熟悉一下OSGi框架中的一些概念。用户在设计Bundle时必须要深入理解这些实体概念。 实体概念 实体概念说明 Bundle - 安装到OS...

scala 学习笔记(07) 一等公民的函数

在scala中一切皆对象,一切皆函数,函数跟Int,String、Class等其它类型是处于同等的地位,换句话说,使用函数跟使用普通的类型一样,没什么区别,因此: 1、函数可以赋值给变量,可以当参数传递 def helloWorld(msg: String): Unit = { println("Hello," + msg) }...

FastJSON反序列化学习

反序列化漏洞例子 0x00、fastJSON练习 参考上面的链接,写一个类,并用fastJSON序列化。查阅API,fastJSON的序列化函数有: public abstract class JSON { // 将Java对象序列化为JSON字符串,支持各种各种Java基本类型和JavaBean public static String...

Java 并发——多线程基础

Thead类与Runnable接口Java的线程,即一个Thread实例。 Java的线程执行过程有两种实现方式: 子类继承Thread类,并且重写void run()方法。 自定义类实现Runnable接口,并且实现void run()方法。并在Thead构造时,将Runnable实例放入Thead。 Thread类 创建一个新线程必须实例化一个Th...

@ConfigurationProperties 配置详解

文章转自 https://blog.csdn.net/qq_26000415/article/details/78942494 前言新的一年到了,在这里先祝大家新年快乐.我们在上一篇spring boot 源码解析12-servlet容器的建立 中 分析 ServerProperties时,发现其类上有@ConfigurationProperties 注解...

Java 反射原理

一、Java 反射的定义 反射机制是在运行状态中, 对于任意一个类, 都能够知道这个类的所有属性和方法; 对于任意一个对象,都能够调用它的任意一个方法或者属性; 二、反射提供的功能: 在运行时判断任意一个对象所属的类; 在运行时构造任意一个类的对象; 在运行时判断任意一个类所具有的成员变量和方法; 在运行时调用任意一个对象的方法; 生成...