[英]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.