设置一个AutoMapper约定,不合并集合属性
本文关键字:合并 集合 属性 约定 AutoMapper 一个 设置 | 更新日期: 2023-09-27 18:09:36
假设我有一个域:
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; }
}
我有两个现有的bar:
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 }
}
};
如果我做一个map
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中设置约定(而不是特定的' remember '语句,假设在编译时我不知道属性名称/表达式)丢弃目标集合值并用源值覆盖它们?所以最后是:
{
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包含相当大的一组不同的映射器(参见源代码),其中可能会找到CollectionMapper
(参见源代码)。如果它不是只读的,则该映射器能够将项从源集合添加到目标集合。您可以做的是在初始化映射器之前,通过运行以下代码将其从映射器集合中删除:
var collectionMapper = MapperRegistry.Mappers.OfType<CollectionMapper>().Single();
MapperRegistry.Mappers.Remove(collectionMapper);
删除CollectionMapper
不会删除映射集合的功能,这将由第二个名为EnumerableMapper
的映射器处理(参见源代码)。它们之间的区别在第26行可以看到——这个映射器在映射时不使用destination属性——它创建了一个新的集合,其中填充了来自源集合的项。
我在本地用AutoMapper 5.1.1
和您提供的类进行了测试。结果如您所愿