AutoMapper 是一个对象映射工具,
安装时只需要安装 如下即可:
有关于它的介绍,参考官网:http://automapper.org/
AutoMapper使用比较简单,还是直奔主题,看一下ABP是如何使用它的:
首先Abp使用AutoMapper专门写的了一个模块Abp.AutoMapper,如下:
跟其它模块一样,有一个继承自AbpModule的AbpAutoMapperModule类。
在ABP自动生成的项目中,是在应用层依赖这个模块,从而对AutoMapper进行了初始化的配置:
[DependsOn(typeof(AuditCoreModule), typeof(AbpAutoMapperModule))] //这里 public class AuditApplicationModule : AbpModule { public override void PreInitialize() { } public override void Initialize() { IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly()); } }
在AbpAutoMapperModule类中,我们看到这句:
public override void PreInitialize() { IocManager.Register<IAbpAutoMapperConfiguration, AbpAutoMapperConfiguration>(); Configuration.ReplaceService<ObjectMapping.IObjectMapper, AutoMapperObjectMapper>();//这句 Configuration.Modules.AbpAutoMapper().Configurators.Add(CreateCoreMappings); }
原来只要实现了IObjectMapper的对象映射工具,可以在abp项目中使用。
再来看看,模块是如何创建映射配置的,CreateMappings方法调用FindAndAutoMapTypes()
[DependsOn(typeof(AbpKernelModule))] public class AbpAutoMapperModule : AbpModule { private readonly ITypeFinder _typeFinder; private static volatile bool _createdMappingsBefore; private static readonly object SyncObj = new object(); public AbpAutoMapperModule(ITypeFinder typeFinder) { _typeFinder = typeFinder; } public override void PostInitialize() { CreateMappings(); } private void CreateMappings() { lock (SyncObj) { Action<IMapperConfigurationExpression> configurer = configuration => { FindAndAutoMapTypes(configuration); foreach (var configurator in Configuration.Modules.AbpAutoMapper().Configurators) { configurator(configuration); } }; if (Configuration.Modules.AbpAutoMapper().UseStaticMapper) { //We should prevent duplicate mapping in an application, since Mapper is static. if (!_createdMappingsBefore) { Mapper.Initialize(configurer); _createdMappingsBefore = true; } IocManager.IocContainer.Register( Component.For<IMapper>().Instance(Mapper.Instance).LifestyleSingleton() ); } else { var config = new MapperConfiguration(configurer); IocManager.IocContainer.Register( Component.For<IMapper>().Instance(config.CreateMapper()).LifestyleSingleton() ); } } } private void FindAndAutoMapTypes(IMapperConfigurationExpression configuration) { var types = _typeFinder.Find(type => { var typeInfo = type.GetTypeInfo(); return typeInfo.IsDefined(typeof(AutoMapAttribute)) || typeInfo.IsDefined(typeof(AutoMapFromAttribute)) || typeInfo.IsDefined(typeof(AutoMapToAttribute)); } ); Logger.DebugFormat("Found {0} classes define auto mapping attributes", types.Length); foreach (var type in types) { Logger.Debug(type.FullName); configuration.CreateAutoAttributeMaps(type); } } }
FindAndAutoMapTypes方法中_typeFinder.Find(),看名字就知道是根据里面的条件,找到符合条件的类型。看一下源码,它是如何找的,
public AbpAutoMapperModule(ITypeFinder typeFinder) { _typeFinder = typeFinder; }
typeFinder是接口,那它默认实现就是TypeFinder,我们在源码中很快找到了它:
public class TypeFinder : ITypeFinder {public TypeFinder(IAssemblyFinder assemblyFinder) { _assemblyFinder = assemblyFinder; Logger = NullLogger.Instance; } public Type[] Find(Func<Type, bool> predicate) { return GetAllTypes().Where(predicate).ToArray(); } private Type[] GetAllTypes() { if (_types == null) { lock (_syncObj) { if (_types == null) { _types = CreateTypeList().ToArray(); } } } return _types; } private List<Type> CreateTypeList() { var allTypes = new List<Type>(); var assemblies = _assemblyFinder.GetAllAssemblies().Distinct(); foreach (var assembly in assemblies) { 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; } }
可以看到_assemblyFinder.GetAllAssemblies().Distinct();
通过_assemblyFinder 是接口IAssemblyFinder,那它的实现是谁呢,看下面的代码。 这里是castle Windsor实现注册的方式之一。
internal class AbpCoreInstaller : IWindsorInstaller { public void Install(IWindsorContainer container, IConfigurationStore store) { container.Register( Component.For<IAbpModuleManager, AbpModuleManager>().ImplementedBy<AbpModuleManager>().LifestyleSingleton(), Component.For<IAssemblyFinder, AbpAssemblyFinder>().ImplementedBy<AbpAssemblyFinder>().LifestyleSingleton(), ); } }
我们看到AbpAssemblyFinder是我们要找到的类。
public class AbpAssemblyFinder : IAssemblyFinder { private readonly IAbpModuleManager _moduleManager; public AbpAssemblyFinder(IAbpModuleManager moduleManager) { _moduleManager = moduleManager; } public List<Assembly> GetAllAssemblies() { var assemblies = new List<Assembly>(); foreach (var module in _moduleManager.Modules) { assemblies.Add(module.Assembly); assemblies.AddRange(module.Instance.GetAdditionalAssemblies()); } return assemblies.Distinct().ToList(); } }
看到这里就可以知道,ABP是通过module来找到assemblies的。 我们可以 AbpModuleManager : IAbpModuleManager这个类得到所有的模块。
得到assemblies之后,可以得到所有Type信息,再根据过滤条件,得到autoMapper需要映射的类。
再来看来这里的过滤条件:
var types = _typeFinder.Find(type => { var typeInfo = type.GetTypeInfo(); return typeInfo.IsDefined(typeof(AutoMapAttribute)) || typeInfo.IsDefined(typeof(AutoMapFromAttribute)) || typeInfo.IsDefined(typeof(AutoMapToAttribute)); } );
三个Attribute. AutoMapAttribute, AutoMapFromAttribute, AutoMapToAttribute,通过名称可以知道它们的用途。
此外,ABP还给我们提供了一个有用的扩展AutoMapExtensions,使用起来更加方便
public static class AutoMapExtensions { /// <summary> /// Converts an object to another using AutoMapper library. Creates a new object of <typeparamref name="TDestination"/>. /// There must be a mapping between objects before calling this method. /// </summary> /// <typeparam name="TDestination">Type of the destination object</typeparam> /// <param name="source">Source object</param> public static TDestination MapTo<TDestination>(this object source) { return Mapper.Map<TDestination>(source); } /// <summary> /// Execute a mapping from the source object to the existing destination object /// There must be a mapping between objects before calling this method. /// </summary> /// <typeparam name="TSource">Source type</typeparam> /// <typeparam name="TDestination">Destination type</typeparam> /// <param name="source">Source object</param> /// <param name="destination">Destination object</param> /// <returns></returns> public static TDestination MapTo<TSource, TDestination>(this TSource source, TDestination destination) { return Mapper.Map(source, destination); } }