对象映射模块

一、ObjectMapping 对象映射

在应用层应用

    public override void ConfigureServices(ServiceConfigurationContext context)
        {
            context.Services.AddAutoMapperObjectMapper<***ApplicationModule>(); 
            Configure<AbpAutoMapperOptions>(options =>
            {
                options.AddProfile<***ApplicationAutoMapperProfile>(validate: true);
            }


  public class ***ApplicationAutoMapperProfile : Profile
    {
        public ***ApplicationAutoMapperProfile()
        {
            CreateMap<***, **Dto>().Ignore(t=>t.**);
            CreateMap<***, ***Dto>();
        }
    }

  public abstract class ***AppServiceBase : ApplicationService
    {
        protected ***AppServiceBase()
        {
            ObjectMapperContext = typeof(***ApplicationModule);
            LocalizationResource = typeof(***Resource);
        }
    }

 

 定义的接口IObjectMapper和默认的实现类DefaultObjectMapper

1、它首先使用IObjectMapper<TSource, TDestination>的实现方法,这个泛型方法注入需要单独注入,它在模块的ConfigureServices的OnExposing进行注册,只要servicecollection的实现类实现IObjectMapper<,>,将其注册类型全部增加,检查包括自身以及其下所有接口类型

2、然后是需要映射的Source是否是IMapTo<TDestination>,mapperSource.MapTo();

3、是否是typeof(IMapFrom<TSource>).IsAssignableFrom(typeof(TDestination)

4、最后是使用AutoObjectMappingProvider进行映射

 

/// <summary>
    /// Defines a simple interface to automatically map objects.
    /// </summary>
    public interface IObjectMapper
    {
        /// <summary>
        /// Converts an object to another. Creates a new object of <see cref="TDestination"/>.
        /// </summary>
        /// <typeparam name="TDestination">Type of the destination object</typeparam>
        /// <typeparam name="TSource">Type of the source object</typeparam>
        /// <param name="source">Source object</param>
        TDestination Map<TSource, TDestination>(TSource source);

        /// <summary>
        /// Execute a mapping from the source object to the existing destination object
        /// </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 the same <see cref="destination"/> object after mapping operation</returns>
        TDestination Map<TSource, TDestination>(TSource source, TDestination destination);
    }
    /// <summary>
    /// Maps an object to another.
    /// Implement this interface to override object to object mapping for specific types.
    /// </summary>
    /// <typeparam name="TSource"></typeparam>
    /// <typeparam name="TDestination"></typeparam>
    public interface IObjectMapper<in TSource, TDestination>
    {
        /// <summary>
        /// Converts an object to another. Creates a new object of <see cref="TDestination"/>.
        /// </summary>
        /// <param name="source">Source object</param>
        TDestination Map(TSource source);

        /// <summary>
        /// Execute a mapping from the source object to the existing destination object
        /// </summary>
        /// <param name="source">Source object</param>
        /// <param name="destination">Destination object</param>
        /// <returns>Returns the same <see cref="destination"/> object after mapping operation</returns>
        TDestination Map(TSource source, TDestination destination);
    }

2、AutoMapper

 

automapper已经删除静态使用Initialize方法, new MapperConfiguration(Action<IMapperConfigurationExpression> configure)来初始化AutoMapper

如果很多的Mapper.CreateMap调用把它们移动到一个Profile,而且IMapperConfigurationExpression.AddMaps(assembly)方法将查找模块内所有Profile增加到配置里面

var config = new MapperConfiguration(cfg => cfg.CreateMap<Order, OrderDto>());
var mapper = config.CreateMapper(); 

为了实现上述操作,Abp创建AbpAutoMapperOptions,封装了ValidatingProfiles、Configurators即List<Action<IAbpAutoMapperConfigurationContext>>,而将IMapperConfigurationExpression封装在IAbpAutoMapperConfigurationContext里面

体会下述封装的思想和操作,为什么要把ServiceProvider封装进去啊

 using (var scope = serviceProvider.CreateScope())
            {
                var options = scope.ServiceProvider.GetRequiredService<IOptions<AbpAutoMapperOptions>>().Value;
                void ConfigureAll(IAbpAutoMapperConfigurationContext ctx)
                {
                    foreach (var configurator in options.Configurators)
                    {
                        configurator(ctx);
                    }
                }
                void ValidateAll(IConfigurationProvider config)
                {
                    foreach (var profileType in options.ValidatingProfiles)
                    {
                        config.AssertConfigurationIsValid(((Profile)Activator.CreateInstance(profileType)).ProfileName);
                    }
                }
                var mapperConfiguration = new MapperConfiguration(mapperConfigurationExpression =>
                {                   
                    ConfigureAll(new AbpAutoMapperConfigurationContext(mapperConfigurationExpression, scope.ServiceProvider));
                });
                ValidateAll(mapperConfiguration);
                scope.ServiceProvider.GetRequiredService<MapperAccessor>().Mapper = mapperConfiguration.CreateMapper();
            }

 

使用时,先创建一个Profile 类

public class MyProfile : Profile
{
    public MyProfile()
    {
        CreateMap<User, UserDto>();
    }
}

配置AbpAutoMapperOptions,AddMaps 注册给定类的程序集中所有的配置类,通常使用模块类. 它还会注册 attribute 映射.

 Configure<AbpAutoMapperOptions>(options =>
 {
      options.AddMaps<AutoMapperTestModule>();
 });
options.AddMaps,实则执行了增加委托列表,在上面ConfigureAll执行,在AbpAutoMapperModule的OnPreApplicationInitialization执行
Configurators.Add(context =>
            {
                context.MapperConfiguration.AddMaps(assembly);
 });

 

Because AutoMapper already provides it out of the box.

I should think on that. If AutoMapper already has these attributes, we may consider to remove ours.

Remove ABP's AutoMap attributes #1706

 

 

配置验证

 

AddMaps 使用可选的 bool 参数控制模块配置验证:

 

options.AddMaps<MyModule>(validate: true);

 

如果此选项默认是 false , 但最佳实践建议启用.

 

可以使用 AddProfile 而不是 AddMaps 来控制每个配置文件类的配置验证:

 

options.AddProfile<MyProfile>(validate: true);

 

如果你有多个配置文件,并且只需要为其中几个启用验证,那么首先使用AddMaps而不进行验证,然后为你想要验证的每个配置文件使用AddProfile

 

 

 

IObjectMapper<TContext>将对象映射器上下文化,你可以为不同的 模块/上下文 使用不同的库

 虽然使用上下文化的对象映射器与普通的对象映射器相同, 但是也应该在模块的 ConfigureServices 方法中注册上下文化的映射器:

 IObjectMapper<MyModule>是可重用模块的一项基本功能,可在多个应用程序中使用,每个模块可以使用不同的库进行对象到对象的映射. 所有预构建的ABP模块都在使用它. 但是对于最终应用程序,你可以忽略此接口,始终使用默认的 IObjectMapper 接口.

 

 

关于OnExposing与OnRegistred委托方法

IOnServiceExposingContext与IOnServiceRegistredContext作为操作对象分别注册到ServiceRegistrationActionList与ServiceExposingActionList里面

 public interface IOnServiceExposingContext
    {
        Type ImplementationType { get; }
        List<Type> ExposedTypes { get; }
    }
  public interface IOnServiceRegistredContext
    {
        ITypeList<IAbpInterceptor> Interceptors { get; }
        Type ImplementationType { get; }
    }

 》ServiceRegistrationActionList在Autofac进行服务注册时使用,每个服务与实现都执行委托的内容,如果拦截器Interceptors不为空,则egistrationBuilder.InterceptedBy里面,当创建服务注入按顺序创建相应的服务

》ServiceExposingActionList在ConventionalRegistrarBase在服务自动注册时使用,它首先在Assembly得到所有type,筛选出type.IsClass列表,根据type和属性查到了LifeTime,根据type找到ExposedService作为serviceType,就是在实现类class注册到IServiceCollection,可以根据用户定义将多个ServiceType注册成该实现类

 var serviceDescriptor = ServiceDescriptor.Describe(serviceType, type, lifeTime.Value);
IObjectMapper<,>  IObjectSerializer<>

ExposedTypes就是ServiceType,implementationType就是Type

    context.Services.OnExposing(onServiceExposingContext =>
            {
                //Register types for IObjectMapper<TSource, TDestination> if implements
                onServiceExposingContext.ExposedTypes.AddRange(
                    ReflectionHelper.GetImplementedGenericTypes(
                        onServiceExposingContext.ImplementationType,
                        typeof(IObjectMapper<,>)
                    )
                );
            });

result是ServiceType列表,用于注册,GivenType是ClassType,genericType是IObjectMapper<,>
private static void AddImplementedGenericTypes(List<Type> result, Type givenType, Type genericType)  
        {
            var givenTypeInfo = givenType.GetTypeInfo();
            if (givenTypeInfo.IsGenericType && givenType.GetGenericTypeDefinition() == genericType)
            {
                result.Add(givenType);
            }

            foreach (var interfaceType in givenTypeInfo.GetInterfaces())
            {
                if (interfaceType.GetTypeInfo().IsGenericType && interfaceType.GetGenericTypeDefinition() == genericType)
                {
                    result.Add(interfaceType);
                }
            }
            if (givenTypeInfo.BaseType == null)
            {
                return;
            }

            AddImplementedGenericTypes(result, givenTypeInfo.BaseType, genericType);
        }

posted on 2019-07-25 10:53  dollymi  阅读(908)  评论(0编辑  收藏  举报

导航