C# 自动映射:从多个源属性映射到集合

C# 自动映射:从多个源属性映射到集合,c#,automapper,C#,Automapper,2018年4月13日更新:Automapper 6.1.0通过引入反向映射支持取消平台。请参阅发行说明 我正在尝试使用AutoMapper解除对象的平台 我有如下消息来源 public class Source { public string Name {get;set;} public string Child1Property1 {get;set;} public string Child1Property2 {get;set;} public string

2018年4月13日更新:Automapper 6.1.0通过引入
反向映射
支持取消平台。请参阅发行说明

我正在尝试使用AutoMapper解除对象的平台

我有如下消息来源

public class Source
{
    public string Name {get;set;}
    public string Child1Property1 {get;set;}
    public string Child1Property2 {get;set;}
    public string Child2Property1 {get;set;}
    public string Child2Property2 {get;set;}
}
我想把它映射到目的地

public class Destination
{
    public string Name {get;set;}
    public List<Child> Children {get;set;}
}

public class Child
{
    public string Property1 {get;set;}
    public string Property2 {get;set;}
}
公共类目的地
{
公共字符串名称{get;set;}
公共列表子项{get;set;}
}
公营儿童
{
公共字符串属性1{get;set;}
公共字符串属性2{get;set;}
}
我的映射配置

public static class AutoMapperConfiguration
{
    public static MapperConfiguration Configure()
    {
        var config = new MapperConfiguration(
            cfg =>
            {
                cfg.CreateMap<Source, Destination>()
                    .ForMember(dest => dest.Children, /* What do I put here?*/))
                // I don't think this is correct
                cfg.CreateMap<Source, Child>()
                    .ForMember(dest => dest.Property1, opt => opt.MapFrom(src => src.Child1Property1))
                    .ForMember(dest => dest.Property2, opt => opt.MapFrom(src => src.Child1Property2))
                    .ForMember(dest => dest.Property1, opt => opt.MapFrom(src => src.Child2Property1))
                    .ForMember(dest => dest.Property2, opt => opt.MapFrom(src => src.Child2Property2));

            });
        return config;
    }
}
公共静态类自动映射器配置
{
公共静态MapperConfiguration配置()
{
var config=新的MapperConfiguration(
cfg=>
{
cfg.CreateMap()
.ForMember(dest=>dest.Children,/*我在这里放什么?*/)
//我认为这是不对的
cfg.CreateMap()
.FormMember(dest=>dest.Property1,opt=>opt.MapFrom(src=>src.Child1Property1))
.FormMember(dest=>dest.Property2,opt=>opt.MapFrom(src=>src.Child1Property2))
.FormMember(dest=>dest.Property1,opt=>opt.MapFrom(src=>src.Child2Property1))
.ForMember(dest=>dest.Property2,opt=>opt.MapFrom(src=>src.Child2Property2));
});
返回配置;
}
}
现在,当我测试代码时,我使用
mapper.Map(source)
得到一个
自动映射异常:缺少类型映射配置或不支持的映射。
这很有意义,因为没有配置到
列表的映射。如果我执行
mapper.Map(source)
,我会得到一个
子实例,其中包含属性的所有
null

不幸的是,我无法修改
源代码


使用AutoMapper是否可能实现这一点?如果是这样的话,怎么做?

您可以在源类上添加一个方法来获取子列表。
这样就很容易绘制地图。

至少有两个选项。可以使用简单的扩展方法简化映射,也可以创建自定义类型转换器

public class ConvertSourceToDestination : ITypeConverter<Source, Destination>
{
    public Destination Convert(Source source, Destination destination, ResolutionContext context)
    {
        destination = destination ?? new Destination();
        destination.Children = destination.Children ?? new List<Child>();
        destination.Children.Add(new Child() { Property1 = source.Child1Property1, Property2 = source.Child1Property2 });
        destination.Children.Add(new Child() { Property1 = source.Child2Property1, Property2 = source.Child2Property2 });
        destination.Name = source.Name;

        return destination;
    }
}

public static class SourceExtension
{
    public static IEnumerable<Child> Children(this Source source)
    {
        yield return new Child() { Property1 = source.Child1Property1, Property2 = source.Child1Property2 };
        yield return new Child() { Property1 = source.Child2Property1, Property2 = source.Child2Property2 };
    }

    public static MapperConfiguration CreateMapping()
    {
        var config = new MapperConfiguration(
            cfg =>
            {
                cfg.CreateMap<Source, Destination>()
                .ForMember(dest => dest.Children, opt => opt.MapFrom(src => src.Children()));
            });

        return config;
    }

    public static MapperConfiguration CreateMapping2()
    {
        var config = new MapperConfiguration(
            cfg =>
            {
                cfg.CreateMap<Source, Destination>().ConvertUsing(new ConvertSourceToDestination());

            });

        return config;
    }
}
公共类ConvertSourceToDestination:ITypeConverter
{
公共目标转换(源、目标、ResolutionContext上下文)
{
目的地=目的地??新目的地();
destination.Children=destination.Children??新列表();
Add(new Child(){Property1=source.Child1Property1,Property2=source.Child1Property2});
Add(new Child(){Property1=source.Child2Property1,Property2=source.Child2Property2});
destination.Name=source.Name;
返回目的地;
}
}
公共静态类SourceExtension
{
公共静态IEnumerable子项(此源)
{
返回new Child(){Property1=source.Child1Property1,Property2=source.Child1Property2};
返回new Child(){Property1=source.Child2Property1,Property2=source.Child2Property2};
}
公共静态MapperConfiguration CreateMapping()
{
var config=新的MapperConfiguration(
cfg=>
{
cfg.CreateMap()
.ForMember(dest=>dest.Children,opt=>opt.MapFrom(src=>src.Children());
});
返回配置;
}
公共静态MapperConfiguration CreateMapping2()
{
var config=新的MapperConfiguration(
cfg=>
{
CreateMap().ConvertUsing(新的ConvertSourceToDestination());
});
返回配置;
}
}

与其使用自定义类型转换器,不如使用a并将其余映射保留到AutoMapper。在这种情况下,将
source.Name
映射到
destination.Name
并不困难,但是想象一下,您有10个其他属性,AutoMapper可以处理,或者您可以使用默认的
opt.MapFrom
进行映射

自定义值解析器示例:

public class SourceToDestinationChildResolver : IValueResolver<Source, Destination, List<Child>>
{
    public List<Child> Resolve(Source source, Destination destination, List<Child> member, ResolutionContext context)
    {
        destination = destination ?? new Destination();
        destination.Children = destination.Children ?? new List<Child>();
        destination.Children.Add(new Child() { Property1 = source.Child1Property1, Property2 = source.Child1Property2 });
        destination.Children.Add(new Child() { Property1 = source.Child2Property1, Property2 = source.Child2Property2 });
        // This is not needed then
        // destination.Name = source.Name;
        return destination.Children;
    }
}
public static class AutoMapperConfiguration
{
    public static MapperConfiguration Configure()
    {
        var config = new MapperConfiguration(
            cfg =>
            {
                cfg.CreateMap<Source, Destination>()
                   .ForMember(dest => dest.Children, opt => opt.MapFrom<SourceToDestinationChildResolver>())
            });
        return config;
    }
}
公共类SourceToDestinationChildResolver:IValueResolver
{
公共列表解析(源、目标、列表成员、解析上下文)
{
目的地=目的地??新目的地();
destination.Children=destination.Children??新列表();
Add(new Child(){Property1=source.Child1Property1,Property2=source.Child1Property2});
Add(new Child(){Property1=source.Child2Property1,Property2=source.Child2Property2});
//这是不需要的
//destination.Name=source.Name;
返回目的地。儿童;
}
}
使用解析程序的配置:

public class SourceToDestinationChildResolver : IValueResolver<Source, Destination, List<Child>>
{
    public List<Child> Resolve(Source source, Destination destination, List<Child> member, ResolutionContext context)
    {
        destination = destination ?? new Destination();
        destination.Children = destination.Children ?? new List<Child>();
        destination.Children.Add(new Child() { Property1 = source.Child1Property1, Property2 = source.Child1Property2 });
        destination.Children.Add(new Child() { Property1 = source.Child2Property1, Property2 = source.Child2Property2 });
        // This is not needed then
        // destination.Name = source.Name;
        return destination.Children;
    }
}
public static class AutoMapperConfiguration
{
    public static MapperConfiguration Configure()
    {
        var config = new MapperConfiguration(
            cfg =>
            {
                cfg.CreateMap<Source, Destination>()
                   .ForMember(dest => dest.Children, opt => opt.MapFrom<SourceToDestinationChildResolver>())
            });
        return config;
    }
}
公共静态类自动映射器配置
{
公共静态MapperConfiguration配置()
{
var config=新的MapperConfiguration(
cfg=>
{
cfg.CreateMap()
.FormMember(dest=>dest.Children,opt=>opt.MapFrom())
});
返回配置;
}
}

有一件事可以帮助我自己澄清我的解决方案,那就是如何准确地使用
List member
。我在文档中不清楚,所以有人请评论:)

请提供一个快速的例子我认为这个答案类似于@mikej的“扩展方法”解决方案,我在2018年4月13日看到,您包括一个链接到
ReverseMap
,它应该有助于取消融资,但我仍然不知道它是如何显示这种情况的。更新它的人能提供一些示例代码来回答OP的问题吗?