对象映射模块
一、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
方法中注册上下文化的映射器:
关于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);
}