【问题标题】:AutoMapper 5 Custom Value Resolver "Cannot convert expression type X to return type Y"AutoMapper 5 自定义值解析器“无法将表达式类型 X 转换为返回类型 Y”
【发布时间】:2017-07-04 10:18:00
【问题描述】:

我从旧版本的 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>'`.

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

【问题讨论】:

    标签: c# automapper automapper-5


    【解决方案1】:

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

    【讨论】:

    • 如果我希望我的解析器更通用,我应该使用不同类型的“解析器”吗?我有几种类型,上面有 short? 代表 ModelId,我希望能够重用相同的解析器
    • 请注意.ForMember(dest =&gt; dest.ModelCode ...,其中目标是字符串,源是 TSource(Product 类型)上的 short? 属性。
    • @sergey-berezovskiyey 我正在尝试使用IMemberValueResolver 从源类型String 解析值目标类型List&lt;Dto&gt;,但它从未奏效。您可以提供任何工作回购吗?谢谢;
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多