AutoMapper 5 自定义值解析器 "无法将表达式类型 X 转换为返回类型 Y"

5

我从旧版AutoMapper升级并转换了我的自定义解析器,但是我遇到了困难。

public class ProductMappingProfile : Profile
{
    public ProductMappingProfile()
    {
        CreateMap<Product, ProductViewModel>()
            .ForMember(
                dest => dest.Model,
                opt => opt.ResolveUsing<ModelNameResolver>(src => src.ModelId));
        // won't compile
    }
}

在 Product 类中有一个 int? ModelId 属性,在 ProductViewModel 类中有一个 string Name 属性。

自定义解析器

public class ModelNameResolver : IValueResolver<short?, string, string>
{
    private readonly InventoryService _inventoryService;
    public ModelNameResolver(InventoryService inventoryService)
    {
        _inventoryService = inventoryService;
    }

    public string Resolve(short? source, string destination, string destMember, ResolutionContext context)
    {
        if (!source.HasValue)
            return "n/a";

        return _inventoryService.GetModel(source.Value)
            .Name;
    }
}

编译错误:

The type 'MyNamespace.Web.Resolvers.ModelCodeResolver' cannot be used as type parameter 'TValueResolver' in the generic type or method `'AutoMapper.IMemberConfigurationExpression<TSource,TDestination,TMember>.ResolveUsing<TValueResolver>()'`.

There is no implicit reference conversion from `'MyNamespace.Web.Resolvers.ModelCodeResolver'` to `'AutoMapper.IValueResolver<Data.Models.Product,Web.ViewModels.ProductViewModel,string>'`.

我做错了什么?我怀疑我没有正确理解新的自定义解析器接口。
1个回答

6

IValueResolver接口应该使用源对象类型目标对象类型目标成员的类型(应该是Resolve方法结果的类型)进行参数化。在您的情况下,参数应该是

 IValueResolver<Product, ProductViewModel, string>

但是您正在创建一个解析器,该解析器带有参数

 IValueResolver<short?, string, string>

short? 不是您的源对象类型

string 不是您的目标对象类型

您应该使用类似以下的内容:

public class ModelNameResolver : IValueResolver<Product, ProductViewModel, string>
{
    private readonly InventoryService _inventoryService;
    public ModelNameResolver(InventoryService inventoryService)
    {
        _inventoryService = inventoryService;
    }

    public string Resolve(Product source, ProductViewModel destination,
       string destMember, ResolutionContext context)
    {
        var modelId = source.ModelId;
        if (!modelId.HasValue)
            return "n/a";

        return _inventoryService.GetModel(modelId.Value).Name;
    }
}

您的映射应该如下所示:
var inventoryService = new InventoryService();
var modelNameResolver = new ModelNameResolver(inventoryService);

Mapper.Initialize(c =>
{
    c.CreateMap<Product, ProductViewModel>()
        .ForMember(dest => dest.Model, opt => opt.ResolveUsing(modelNameResolver));
});

当然,你可以向IoC容器请求模型名称解析器的实例。


更新:如果你想让解析器在不同的源数据和目标数据类型之间重复使用,那么有两个选项:

如果你的解析器可以有无参数构造函数,则可以使用IMemberValueResolver

public class ModelNameResolver : IMemberValueResolver<object, object, int?, string>
{
    // create or assign _inventoryService
    // also note objects as source and destination
    public string Resolve(object source, object destination,
       int? sourceMember, string destMember,
       ResolutionContext context)
    {
         if (!sourceMember.HasValue)
            return "n/a";

         return _inventoryService.GetModel(sourceMember.Value).Name;
    }
}

使用方法

.ForMember(dest => dest.Model, 
           opt => opt.ResolveUsing<ModelNameResolver, int?>(src => src.ModelId)

第二种选择-不使用解析器。只需使用具有自定义映射的类的MapFrom即可:
.ForMember(dest => dest.Model, 
           opt => opt.MapFrom(src => someClass.GetModelName(src.ModelId)));     

有些类应该包含通过id获取模型名称的方法

public string GetModelName(int? modelId)
{
    if (!modelId.HasValue)
        return "n/a";

    return _inventoryService.GetModel(modelId.Value).Name;
}

如果我想让我的解析器更通用,我应该使用不同类型的“解析器”吗?我有几种类型,它们上面有short?表示ModelId,并且我希望能够重复使用相同的解析器。 - Petrus Theron
请注意 .ForMember(dest => dest.ModelCode ...,其中目标是 TSource(类型为 Product)上的 short? 属性,而源是字符串。 - Petrus Theron
@sergey-berezovskiyey,我正在尝试使用IMemberValueResolver从源类型String解析目标类型为List<Dto>的值,但它从未起作用。您能提供任何有效的存储库吗?谢谢; - Robin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接