对多个成员使用AutoMapper自定义值解析器

时间:2019-04-11 01:18:31

标签: c# asp.net-core automapper

我正在尝试使用“自定义值解析器”方法将源类型中的成员数据解析为目标类型中的成员。我已尽可能仔细地关注AutoMapper网站http://docs.automapper.org/en/stable/Custom-value-resolvers.html上的文档。我已经仔细检查以确保我遵循提供的示例,但是有一个区别。源上的成员与目标上的成员类型不同。

IValueResolver.cs

public interface IValueResolver<in TSource, in TDestination, in TSourceMember, TDestinationMember>
{
    TDestinationMember Resolve(TSource source, TDestination destination, TSourceMember sourceMember, TDestinationMember destinationMember, ResolutionContext context);
}

DisplayIdResolver.cs

public class DisplayIdResolver : IValueResolver<object, object, int, string>
{
    // Seems like this may be useless unless its mapping to a destination member of the same type as the source member
    public string Resolve(object source, object destination, int sourceMember, string destinationMember, ResolutionContext context)
    {
        if (source.GetType().Equals(typeof(Developer)))
        {
            return IdentifierType.ConvertToAppId(sourceMember, IdentifierType.DEVELOPER.Code);
        }
        . . . etcetera . . .
     }
}

AutoMapperProfile.cs

public class AutoMapperProfile : Profile
{
    public AutoMapperProfile()
    {
        CreateMap<Developer, DeveloperDetailModel>().ForMember(dest => dest.DisplayId, opt => opt.MapFrom<DisplayIdResolver, int>(src => src.DeveloperId));
    }
}

从AutoMapperProfile构造函数的这一行代码中,我收到以下编译时错误:

  

类型'WebSite.Utilities.AutoMapper.DisplayIdResolver'不能为   在通用类型或方法中用作类型参数“ TValueResolver”   'IMemberConfigurationExpression <开发人员,DeveloperDetailModel,   字符串> .MapFrom (Expression >)'。没有隐式引用转换   从“ WebSite.Utilities.AutoMapper.DisplayIdResolver”到   'AutoMapper.IMemberValueResolver '。

从AutoMapper网站上显示的示例来看,自定义值解析器Resolve方法似乎仅允许两个成员具有相同类型的映射。但是我已经确保从resolver方法返回的类型确实是目标成员期望的类型。

很可能我对泛型感到困惑,并且我正在犯一个简单的错误,但是我无法弄清楚那是什么,我在这里失去了所有客观性。我正在做的事情是否可行,或者这不是解析器的设计目的?

编辑

Lucian Bargaoanu先生指出,在以下link下还有一个AutoMapper文档的备用站点。由于此新文档未提供接口示例,因此我可以假定IValueConverter的接口为:

public interface IValueConverter<in TSourceMember, TDestinationMember>
{
    TDestinationMember Resolve(TSourceMember sourceMember, TDestinationMember destinationMember, ResolutionContext context);
}

如果是这种情况,那么这将没有什么用,因为我确实需要源对象类型才能将源成员映射到目标成员。我也尝试过将其与其他对象参数一起用作源,但是ConvertUsing方法没有具有3个参数的重载。我也看不到如何用新文档来完成此操作。

编辑2

建议我使用IMemberValueResolver。这就是我所做的:

IMemberValueResolver.cs

public interface IMemberValueResolver<in TSource, in TDestination, TSourceMember, TMember>
{
    TMember Resolve(TSource source, TDestination destination, TSourceMember sourceMember, TMember destinationMember, ResolutionContext context);
}

DisplayIdResolver.cs

public class DisplayIdResolver : IMemberValueResolver<object, object, int, string>
{
    public string Resolve(object source, object destination, int sourceMember, string destinationMember, ResolutionContext context)
    {
        if (source.GetType().Equals(typeof(Developer)))
        {
            return IdentifierType.ConvertToDisplayId(sourceMember, IdentifierType.DEVELOPER.Code);
        }

        ... more type comparisons here, etc ...

        return null;
    }
}

AutoMapperProfile.cs

public class AutoMapperProfile : Profile
{
    public AutoMapperProfile()
    {
        CreateMap<Developer, DeveloperDetailModel>().ForMember(dest => dest.DisplayId, opt => opt.MapFrom<DisplayIdResolver, int>(src => src.DeveloperId));
    }
}

这会导致编译时错误:

  

类型'WebSite.Utilities.AutoMapper.DisplayIdResolver'不能为   在通用类型或方法中用作类型参数“ TValueResolver”   'IMemberConfigurationExpression <开发人员,DeveloperDetailModel,   字符串> .MapFrom (Expression >)'。没有隐式引用转换   从“ WebSite.Utilities.AutoMapper.DisplayIdResolver”到   'AutoMapper.IMemberValueResolver '。

这太糟糕了,因为我真的想使用AutoMapper的所有功能。我遵循了使用IValueResolver的更简单示例的示例,但该示例也不起作用。我将ASP.Net Core 2.2和AutoMapper.Extensions.Microsoft.DependencyInjection库用于Automapper-以及本示例中刚刚包含的Profile类。完全按照网站上提供的文档,我无法在AutoMapper网站上使用最简单的情况下工作,也无法使更高级的IMemberValueResolver案例下工作。该网站提供了实现类的示例,但没有提供接口的示例。该示例中的MapFrom调用似乎是不正确的,因为没有参数组合为我提供了.NET编译器将接受的语法。

请注意,我知道该错误指出以下内容:

  

不能用作类型参数'TValueResolver'

您可以清楚地看到,我没有在任何地方使用IValueResolver。我只是在类IMemberValueResolver中实现DisplayIdResolver接口,并按照AutoMapper网站上的示例 进行操作,以了解如何在Profile的CreateMap调用中对其进行引用。

编辑3

使用link到Github上的示例,我能够找到我要完成的确切测试用例场景。但是,Github上的示例与我尝试编译的代码之间没有区别:

    public class CustomResolver : IMemberValueResolver<object, object, int, int>
    {
        public int Resolve(object s, object d, int source, int dest, ResolutionContext context)
        {
            return source + 5;
        }
    }

    protected override MapperConfiguration Configuration { get; } = new MapperConfiguration(cfg =>
    {
        cfg.CreateMap<Source, Dest>()
            .ForMember(dto => dto.SomeValue,
                opt => opt.MapFrom<CustomResolver, int>(m => m.SomeOtherValue));

    });

如您所见-他们正在使用通用接口来使用IMemberValueResolver并使用我正在做的签名,除了返回值是字符串而不是整数,所以它变成了(如我所之前发布):

public interface IMemberValueResolver<in TSource, in TDestination, TSourceMember, TMember>
{
    TMember Resolve(TSource source, TDestination destination, TSourceMember sourceMember, TMember destinationMember, ResolutionContext context);
}

但是,以与示例中相同的方式调用它会导致未编译的代码,如前所述(我已经两次发布了堆栈跟踪错误):

CreateMap<Developer, DeveloperDetailModel>().ForMember(dest => dest.DisplayId, opt => opt.MapFrom<DisplayIdResolver, int>( src => src.DeveloperId));

0 个答案:

没有答案