我正在尝试将 ASP.NET Core 2.1 Web 项目更新到 .NET 8 并使其基本正常工作,但 AutoMapper 13.0.1 中的默认行为是使用
PascalCase
命名约定对映射复杂源进行扁平化.
例如,如果您将目标属性命名为
AccountName
,那么如果源有一个名为 Account
的类属性,并且 Account
对象有一个名为 Name
的属性,那么 AccountName
将自动从 映射Account.Name
。
在 AutoMapper 的早期版本中,如果您使用
_
作为分割字符来命名目的地,例如,也会发生相同的功能。 Account_Name
。默认情况下这不再有效。
有一个配置设置,您可以在单个映射配置文件的构造函数中进行设置:
DestinationMemberNamingConvention = LowerUnderscoreNamingConvention.Instance;
这会恢复之前支持的行为。
但是,作为升级的一部分,我们必须将其包含在所有映射配置文件中,并且我们有近 500 个映射配置文件,因为每个输出模型都有一个。作为升级的一部分,我显然热衷于减少单个源更改的数量,并且正在寻找全局解决方案。
我尝试在应用程序初始化时将 AutoMapper 添加到依赖注入服务中应用配置设置,但它只是被忽略了:
services.AddAutoMapper(cfg =>
{
cfg.DestinationMemberNamingConvention = LowerUnderscoreNamingConvention.Instance;
}, Assembly.GetExecutingAssembly());
当我调试配置文件时,在构造函数中,命名约定仍设置为
PascalCase
命名约定,直到它到达我的新行,强制将其设置为我想要的。
有人遇到过同样的问题并设法提出全球解决方案吗?
如果在升级过程中减少单个源更改的数量是您的首要任务,那么您可以自行手动注册 AutoMapper,但需要预先配置。这有点黑客行为,所以未来的版本有可能会破坏它,但我们开始吧。
像往常一样开始注册 AutoMapper。这将添加 AutoMapper 核心服务和自定义解析器或转换器。由于配置文件也会被添加,我们需要删除它们。最后一次
Configure<>()
服务调用是添加地图,所以让我们删除该服务。由于我们删除了地图,因此我们需要将它们添加回来。但为了做到这一点,我们需要知道要添加的内容,因此扫描程序集以查找映射配置文件。获得匹配类型后,使用 Activator.CreateInstance()
初始化每个配置文件。调用预配置谓词并添加回配置文件,这次已经预配置了实例。
public static void AddAutoMapperWithProfilePreconfiguration(
this IServiceCollection services,
Action<IMapperConfigurationExpression> configAction,
Action<Profile> preconfigureProfile,
params Type[] profileAssemblyMarkerTypes)
{
services.AddAutoMapper(configAction, profileAssemblyMarkerTypes);
// Remove mapping profiles configuration.
var descriptorToRemove = services.Last(x => x.ServiceType == typeof(IConfigureOptions<MapperConfigurationExpression>));
services.Remove(descriptorToRemove);
// Find and initialize profiles.
var profiles = ScanForProfiles(profileAssemblyMarkerTypes)
.Select(type =>
{
var profile = (Profile)Activator.CreateInstance(type);
preconfigureProfile.Invoke(profile);
return profile;
});
// Add mapping profiles back, this time as already preconfigured instances.
services.Configure<MapperConfigurationExpression>(options => options.AddProfiles(profiles));
}
private static IEnumerable<Type> ScanForProfiles(params Type[] markerTypes)
{
return markerTypes
.Select(x => x.GetTypeInfo().Assembly)
.Where(a => !a.IsDynamic && a != typeof(Profile).Assembly)
.SelectMany(a => a.GetTypes())
.Where(x => typeof(Profile).IsAssignableFrom(x) && !x.IsAbstract && !x.ContainsGenericParameters);
}
用途:
services.AddAutoMapperWithProfilePreconfiguration(
cfg => { },
profile => profile.DestinationMemberNamingConvention = LowerUnderscoreNamingConvention.Instance,
profileAssemblyMarkerTypes: typeof(Program)); // Your types marking assembly to scan for profiles, resolvers, converters etc.