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

时间:2017-02-15 16:20:54

标签: c# automapper automapper-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
    }
}

产品具有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 个答案:

答案 0 :(得分:5)

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)));     

someSlass应该包含通过id

获取模型名称的方法
public string GetModelName(int? modelId)
{
    if (!modelId.HasValue)
        return "n/a";

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