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>'`.

我究竟做错了什么?我怀疑我误解了新的自定义解析器界面。

4

1 回答 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)));     

并且 someSlass 应该包含通过 id 获取模型名称的方法

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

    return _inventoryService.GetModel(modelId.Value).Name;
}
于 2017-02-15T16:24:50.747 回答