繁体   English   中英

设置一个AutoMapper约定不合并ICollection属性

[英]Set an AutoMapper convention to not merge ICollection properties

说我有一个域:

public class EmbeddedInBar
{
    public string Name { get; get; }
    public ICollection<int> ListOfInts { get; set; }
}

public class Bar
{
    public int Id { get; set; }
    public string Name { get; set; }
    public ICollection<int> ListOfInts { get; set; }
    public EmbeddedInBar Embedded { get; set; }
}

我有两个现有的酒吧:

var destination = new Bar
{
    Id = 1,
    Name = "Destination",
    ListOfInts = new List<int>( 1,2,3 },
    Embedded = new EmbeddedInBar
    {
        Name = "DestinationEmbedded",
        ListOfInts = new List<int>( 4,5 }
    }
};

var source = new Bar
{
    Id = 2,
    Name = "Source",
    ListOfInts = new List<int>( 6,7,8 },
    Embedded = new EmbeddedInBar
    {
        Name = "SourceEmbedded",
        ListOfInts = new List<int>( 9,10 }
    }   
};

如果我做地图

Mapper.Initialize(conf =>
{
    conf.CreateMap<Bar, Bar>()
        .ForMember(b => b.Id, opts => opts.Ignore());
});

destination = Mapper.Instance.Map(source, destination);

我最终合并了可枚举的属性:

{
    Id: 1,
    Name: "Source",
    ListOfInts: [ 1,2,3,6,7,8 ]
    Embedded: {
        Name: "SourceEmbedded",
        ListOfInts: [ 9,10 ]
    }
}

是否可以在AutoMapper中设置约定(而不是特定的'ForMember'语句,假设在编译时我不知道属性名称/表达式),该约定会丢弃目标ICollection值并用源值覆盖它们? 所以我最终得到:

{
    Id: 1,
    Name: "Source",
    ListOfInts: [ 6,7,8 ]
    Embedded: {
        Name: "SourceEmbedded",
        ListOfInts: [ 9,10 ]
    }
}

您可以通过使用AutoMapper过滤来做到这一点: https : //github.com/AutoMapper/AutoMapper/wiki/Configuration#global-propertyfield-filtering

对于您的示例,您可以使用以下代码:

        Mapper.Initialize(expression =>
        {
            expression.ShouldMapProperty = info => !(
                info.PropertyType.IsGenericType && 
                info.PropertyType.GetGenericTypeDefinition() == typeof(IEnumerable<>));

            expression.CreateMap<Bar, Bar>();
            expression.CreateMap<EmbeddedInBar, EmbeddedInBar>();
        });

        Mapper.Map(source, destination);

编辑:

抱歉,我一开始不了解您。 我为您的问题找到了解决方案。 您可以使用Automapper自定义类型转换器: https : //github.com/AutoMapper/AutoMapper/wiki/Custom-type-converters

整个解决方案如下所示:

创建集合类型转换器:

class CollectionTypeConverter<T> : ITypeConverter<ICollection<T>, ICollection<T>>
{
    public ICollection<T> Convert(ICollection<T> source, ICollection<T> destination, ResolutionContext context)
    {
        return source;
    }
}

将其包括到映射器初始化中:

Mapper.Initialize(expression =>
        {
            expression.CreateMap(typeof(ICollection<>), typeof(ICollection<>)).ConvertUsing(typeof(CollectionTypeConverter<>));

            expression.CreateMap<Bar, Bar>();
            expression.CreateMap<EmbeddedInBar, EmbeddedInBar>();
        });

我发现的解决方案可能会产生一些无法预料的结果-我对AutoMapper经验不足,无法事先考虑所有问题,因此请谨慎使用。 从表面上看,它不应该影响问题中描述的内容。

AutoMapper具有一个称为Mappers的功能,存储在静态集合AutoMapper.Mappers.MapperRegistry.Mappers 内部的类能够改变对象的映射方式。 默认情况下,collection包含大量不同的映射器( 请参阅source ),其中可能会找到CollectionMapper请参见source )。 如果该映射器不是只读的,则可以将其从源集合添加到目标集合。 您可以做的是通过在初始化映射器之前运行以下代码,将其从映射器集合中删除:

    var collectionMapper = MapperRegistry.Mappers.OfType<CollectionMapper>().Single();
    MapperRegistry.Mappers.Remove(collectionMapper);

删除CollectionMapper不会删除映射集合的功能,这将由名为EnumerableMapper的第二个映射器处理( 请参见 EnumerableMapper )。 它们之间的差异在第26行中可见-该映射器在映射时不使用destination属性-它创建一个新集合,其中填充了源集合中的项目。

我使用AutoMapper 5.1.1和您提供的类在本地进行了测试。 结果是您想要的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM