如何获取最新版本的 AutoMapper 以将复杂的属性路径展平到默认使用“_”作为分割字符命名的目标属性

问题描述 投票:0回答:1

我正在尝试将 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
命名约定,直到它到达我的新行,强制将其设置为我想要的。

有人遇到过同样的问题并设法提出全球解决方案吗?

asp.net-core automapper
1个回答
0
投票

如果在升级过程中减少单个源更改的数量是您的首要任务,那么您可以自行手动注册 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.
© www.soinside.com 2019 - 2024. All rights reserved.