经过大量阅读和尝试/错误后,我决定在这里发帖以吸引更多受众的意见。这是一个简单的用例,DTO 映射到实体类。 DTO 的属性名称非常奇怪,因为它是从 Salesforce PubSub CDC API 的 Avro 消息正文解码的,并且 Salesforce 有一种微妙的方式为自定义字段添加后缀。
因此,我使用 [SourceMember] 属性来装饰目标成员属性,因此 AutoMapper 将知道如何为相应装饰的目标获取值。除此之外,我还使用 AutoMapper Profile 类为所有映射中的所有成员设置一个条件,以忽略空源字段。这是因为 DTO 的大多数成员属性在大多数情况下可能为 null,因为这是 CDC 事件。
这就是问题 - 使用配置文件(指定地图的条件)并在目标类上使用 [AutoMap] 类属性来支持 [SourceMember] 装饰器时,似乎存在某种配置/设置冲突。查看此处的 AutoMapper 文档 https://docs.automapper.org/en/stable/Attribute-mapping.html 它说“属性映射可以补充或替换流畅的映射配置”。我只能假设正在发生“替换”操作,而不是“补充”。
这里最奇怪的是第二个问题——如果我注释掉
c.AddProfile(new AutoMapperProfile())
那么 AutoMapper 将尊重 [SourceMember] 装饰器,但不尊重配置文件中的忽略 null 条件。
但是如果我注释掉
c.AddMaps(typeof(TargetClass))
AutoMapper 将尊重配置文件中的忽略 null 条件,但它不会尊重 [SourceMember] 装饰器。
最终结果是我可以在从 SourceDtoClass 映射的 TargetEntityClass 上获得良好的属性值(使用 [SourceMember] 装饰器),-或者-我最终用 SourceDtoClass 中的空值覆盖 TargetEntityClass 上的值,这是我想避免的(例如通过使用条件)
下面是源 DTO、目标类和 AutoMapper 配置的示例。
环境为.NET 7、EF Core 7.0.9、AutoMapper 和 AutoMapper 扩展 12.0.1、Visual Studio for Mac v17.6.1。
配置:
var autoMapperConfig = new MapperConfiguration(c =>
{
c.AddProfile(new AutoMapperProfile());
c.AddMaps(typeof(TargetClass));
});
IMapper mapper = autoMapperConfig.CreateMapper();
services.AddSingleton(mapper);
简介:
public class AutoMapperProfile : Profile {
public AutoMapperProfile() {
CreateMap<SourceDtoClass, TargetEntityClass>()
.ForAllMembers(option => option.Condition((src, dst, srcMember) => srcMember != null));
}
}
源D到类:
public class SourceDtoClass {
public string? Name { get; set; } = "";
public string? Phone_number_1__c { get; set; } = "";
}
目标实体类:
[DataContract]
[Serializable]
[AutoMap(typeof(SourceDtoClass))]
public class TargetEntityClass {
public string Name { get; set; } = "";
[DataMember]
[SourceMember("Phone_number_1__c")]
public string? Phone { get; set; } = "";
}
用途:
(假设映射器已通过 IoC 给出,sourceDtoClass 已实例化,targetEntityClass 是一个完全水合的 EF 实体类实例,其值来自 EF SQL Select 交互)
mapper.map(sourceDtoClass,targetEntityClass)
感谢@LucianBargaoanu 的评论,在 AutoMapper InternalAPI 中使用 ForAllPropertyMaps 有效!
虽然我不确定为什么当目标上的 int 数据类型为目标时(当目标已经存在值时,例如覆盖),源类中字符串数据类型上的忽略空源值会产生零赋值发生)我找到了一个解决方法,只是不在该属性上提供 [SourceMember] 装饰就排除了它。我发现我毕竟不需要映射该属性,但我仍然希望我更多地了解 AutoMapper 在这方面的行为。
配置方案如下:
var autoMapperConfig = new MapperConfiguration(config =>
{
config.AddMaps(typeof(TargetEntityClass));
InternalApi.Internal(config).ForAllPropertyMaps(
propertymaps => true, (propertymap, condition) =>
{
condition.Condition((source, destination, srcval) => srcval !=null);
});
});