带有自定义类型转换器和 DI 的 AutoMapper 会抛出 AutoMapperMappingException

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

我正在检查一个使用

AutoMapper
Microsoft.Extensions.DependencyInjection
的项目。全部 映射器按预期工作,但对于其中一个 - 唯一 - 它使用自定义类型转换器

// there is some abstractions here, but MapperProfileProvider inherits AutoMapper.Profile
public class Mappings : MapperProfileProvider
{
    public Mappings()
    {
        CreateMap<MyItemTypeA, MyItemTypeB>().ConvertUsing<MyItemTypeConverter>();
        CreateMap<MyItemTypeB, MyItemTypeA>().ConvertUsing<MyItemTypeConverter>();
    }

    public class MyItemTypeConverter : ITypeConverter<MyItemTypeA, MyItemTypeB>,
        ITypeConverter<MyItemTypeB, MyItemTypeA>
    {
        public MyItemTypeB Convert(MyItemTypeA source, MyItemTypeB destination, ResolutionContext context)
        {
            // do the conversion and return
            return new MyItemTypeB()
            {
                // assignments...
            };
        }

        public MyItemTypeA Convert(MyItemTypeB source, MyItemTypeA destination, ResolutionContext context)
        {
            // do the conversion and return
            return new MyItemTypeA()
            {
                // assignments...
            };
        }
    }
}

映射器被注入到使用它的类中。

internal class MyApiService : ApiServiceBase
{
    private readonly IMapper _mapper;

    public MyApiService(IMapper mapper) { _mapper = mapper; }

    public async Task<object> Post(MyRequest request)
    {
        // here the exception occurs
        var myItemA = _mapper.Map<MyItemTypeA>(request.ItemB);
    }
}

使用自定义类型映射器会抛出异常

AutoMapperMappingException: Cannot create an instance of type MyTypeConverter

我几乎可以肯定这个问题与 DI 有关。 DI 不知道和/或无法创建类型转换器的实例。这个答案为我指明了方向:

DI 不知道您的自定义解析器类型,因此它甚至不会尝试。 由于您没有使用 DI 包的扩展方法,因此解析器不会添加到服务集合中。如果需要,您可以手动添加这些服务

除了手动添加服务外,答案建议使用

services.AddAutoMapper
。这就是我正在做的事情。
AddAutoMapper
代码如下所示:

builder.Services.AddAutoMapper((IServiceProvider f, IMapperConfigurationExpression cfg) =>
{
    foreach (var profile in f.GetService<IEnumerable<IMapperProfileProvider>>())
    {
        cfg.AddProfile((Profile)profile);
    }
}, new Assembly[] { });

(这里也有一些抽象。但我相信它符合docs中的示例)

我尝试过的:

当使用

MyItemTypeConverter
显式添加
services.AddScoped<MyTypeConverter>();
作为服务时,映射有效。

我还从 AutoMapper docs

找到了以下内容

并向 AutoMapper 提供自定义类型转换器的实例,或者简单地提供 AutoMapper 将在运行时实例化的类型。上述源/目标类型的映射配置将变为:

var configuration = new MapperConfiguration(cfg => {
  cfg.CreateMap<string, int>().ConvertUsing(s => Convert.ToInt32(s));
  cfg.CreateMap<string, DateTime>().ConvertUsing(new DateTimeTypeConverter());
  cfg.CreateMap<string, Type>().ConvertUsing<TypeTypeConverter>();
  cfg.CreateMap<Source, Destination>();
});

第 3 行引起了我的注意,它为 AutoMapper 提供了一个实例。如果我使用它,它也有效。

CreateMap<MyItemTypeA, MyItemTypeB>().ConvertUsing(new MyItemTypeConverter());
CreateMap<MyItemTypeB, MyItemTypeA>().ConvertUsing(new MyItemTypeConverter());

因此,提供实例或添加类型转换器作为服务即可。我无法理解它。为什么类型转换器不能在运行时实例化?文档表明这是可能的(“...或者只是 AutoMapper 将在运行时实例化的类型)。为什么 DI 不知道自定义类型转换器?我错过了什么?

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

类型转换器未在运行时实例化的原因是它位于不同的程序集中,因此未找到。现在很清楚了...

https://docs.automapper.org/en/stable/Dependency-injection.html#asp-net-core

您可以使用配置文件定义配置。然后,您可以通过在启动时调用 IServiceCollection 扩展方法 AddAutoMapper 让 AutoMapper 知道这些配置文件在哪些程序集中定义。

我终于使用了这个覆盖

public static IServiceCollection AddAutoMapper(this IServiceCollection services, Action<IServiceProvider, IMapperConfigurationExpression> configAction, params Assembly[] assemblies)
        => AddAutoMapperClasses(services, configAction, assemblies);

并将程序集传递给它

var assemblies = new Assembly[]
{
    typeof(typeFromAssembly1).Assembly,
    typeof(typeFromAssembly2).Assembly
};

builder.Services.AddAutoMapper((IServiceProvider f, IMapperConfigurationExpression cfg) =>
{
    foreach (var profile in f.GetService<IEnumerable<IMapperProfileProvider>>())
    {
        cfg.AddProfile((Profile)profile);
    }
}, assemblies);
© www.soinside.com 2019 - 2024. All rights reserved.