用通用目的地映射不起作用?

时间:2019-02-20 19:33:59

标签: c# generics automapper

我正在尝试构建映射概要文件的层次结构,以简化为适合该层次结构的新对象创建映射的过程。但是我无法使某些映射正常工作。我使用通用类型参数添加的映射在目标类型时似乎不起作用,但是在源类型时有效。

请考虑以下类型:

public interface IRequest
{
    string Input { get; }
}
public interface IResponse
{
    string Output { get; }
}
public class SomeObject
{
    public string Value { get; set; }
}
abstract class BaseProfile<TSource, TDestination> : Profile
    where TSource : IRequest
    where TDestination : IResponse
{
    protected BaseProfile()
    {
        CreateMap<TSource, SomeObject>()
            .ForMember(src => src.Value, opt => opt.MapFrom(dest => dest.Input));
        CreateMap<SomeObject, TDestination>()
            .ForMember(src => src.Output, opt => opt.MapFrom(dest => dest.Value));
    }
}

我希望能够做到这一点,以便具体类型的配置文件可以使用此父配置文件中的映射,因此我可以定义以下内容:

class FooRequest : IRequest
{
    public string Input { get; set; }
}
class FooResponse : IResponse
{
    public string Output { get; set; }
}
class FooProfile : BaseProfile<FooRequest, FooResponse>
{
    public FooProfile()
    {
    }
}

但是,到响应对象的映射根本不映射。

Mapper.Initialize(c => c.AddProfiles(typeof(FooProfile).Assembly));
var request = new FooRequest { Input = "FOO" };
var obj = Mapper.Map<SomeObject>(request); // this works
// obj.Value = "FOO"
var response = Mapper.Map<FooResponse>(obj); // this doesn't
// response.Output = null

似乎可以解决此问题,我必须复制我认为已经创建但使用具体类型的映射。

public FooProfile()
{
    // Adding this works
    CreateMap<SomeObject, FooResponse>()
        .ForMember(src => src.Output, opt => opt.MapFrom(dest => dest.Value));
}

要进行这项工作,我缺少什么吗?还是我每次都必须创建那些显式映射?


正如斯科特的答案中指出的那样,问题最终是通用参数所约束的接口没有可设置的属性。但是Nader还发现,使用ForMember()方法的特定重载是可行的。因此,我们不是直接绑定到成员,而是绑定到成员名称。可以满足我的需求。

protected BaseProfile()
{
    CreateMap<TSource, SomeObject>()
        .ForMember(src => src.Value, opt => opt.MapFrom(dest => dest.Input));
    CreateMap<SomeObject, TDestination>()
        .ForMember(nameof(IResponse.Output), opt => opt.MapFrom(dest => dest.Value));
}

2 个答案:

答案 0 :(得分:3)

将设置器添加到Output的{​​{1}}属性中。

IResponse

它正在尝试映射到public interface IResponse { string Output { get; set; } } ,并且由于无法写而无法设置属性。

也许这不是您想要的,因为您不希望IReponse具有可写属性。但这就是为什么它没有被设置的原因。

在那种情况下,我唯一可以确定的解决方法是创建一个实现IResponse并具有可写属性的基本响应。

IResponse

然后将通用约束更改为基类,而不是接口:

class ResponseBase : IResponse
{
    public string Output { get; set; }
}

class FooResponse : ResponseBase
{
}

该通用约束将确定目标类型的转换方式,进而确定该属性是否可写。

答案 1 :(得分:1)

我尝试了很多方法,但是没有机会,但是这种超载仅适用于私人合同制定者

abstract class BaseProfile<TSource, TDestination> : Profile
        where TSource : IRequest
        where TDestination : IResponse
    {
        protected BaseProfile()
        {
            CreateMap<TSource, SomeObject>()
                .ForMember(dest => dest.Value, opt => opt.MapFrom(src => src.Input));
            CreateMap(typeof(SomeObject), typeof(TDestination))
                .ForMember("Output", opt => opt.MapFrom("Value"));
        }
    }

也许吉米以多种方式使用结果