映射元素而不创建重复项
Mapping elements without creating duplicates
我有两个类:
public class Element
{
public Item Item { get; set; }
}
public class Item
{
public Element Element { get; set; }
}
而且我有与此 类 结构相同的 DTO。
此方法为映射创建源数据:
static Element[] CreateElements()
{
var element2 = new Element();
return new[]
{
new Element(new Item(element2)),
element2,
new Element()
};
}
然后我配置映射和映射元素:
Mapper.CreateMap<Element, ElementDto>();
Mapper.CreateMap<Item, ItemDto>();
var elements = CreateElements();
var mappedElements = elements
.Select(_ => Mapper.Map(_, typeof(Element), typeof(ElementDto)))
.OfType<ElementDto>()
.ToArray();
我查看映射结果后:
foreach (var element in mappedElements)
{
Console.WriteLine(mappedElements.Any(e => e?.Item?.Element == element));
}
此代码显示 "False" 三次。因此 "CreateElements" 中的 "element2" 创建了两个副本。
源元素的相同测试将 return "False True False":
foreach (var element in elements)
{
Console.WriteLine(elements.Any(e => e?.Item?.Element == element));
}
因为我需要配置映射以免元素重复?可能吗?
我认为这不是 AutoMapper 问题。
您正在创建三个不同的 Element
项并将它们映射到某种 ElementDto
。它们是三个不同的对象(在结构和引用方面),你不能指望如果将它们映射到同一类型,它们就会相等。
如果您考虑您的物品:
var element2 = new Element();
return new[]
{
new Element(new Item(element2)),
element2,
new Element()
};
比较一下,你会发现 none 是相等的。你没有提供 ElementDto
class 但我的猜测是你应该实现 IEquatable
接口,这将确保正确的比较(或重载运算符)。
这可以手动完成。首先忽略属性 Item
AutoMapper没有复制元素链:
Mapper.CreateMap<Item, ItemDto>()
.ForMember(_ => _.Element, _ => _.Ignore());
其次,手动复制带有标记已查看项目的链:
static IEnumerable<ElementDto> MapElements(Element[] elements)
{
var elementToDtoMap = new Dictionary<Element, ElementDto>();
foreach (var element in elements)
{
MapElement(element, null, elementToDtoMap);
}
return elementToDtoMap.Select(_ => _.Value);
}
static void MapElement(Element element, ItemDto parentItem, Dictionary<Element, ElementDto> elementToDtoMap)
{
ElementDto elementDto = null;
if (elementToDtoMap.TryGetValue(element, out elementDto))
return;
elementDto = Mapper.Map<ElementDto>(element);
elementToDtoMap.Add(element, elementDto);
if (parentItem != null)
{
parentItem.Element = elementDto;
}
if (element.Item != null)
{
MapElement(element.Item.Element, elementDto.Item, elementToDtoMap);
}
}
我有两个类:
public class Element
{
public Item Item { get; set; }
}
public class Item
{
public Element Element { get; set; }
}
而且我有与此 类 结构相同的 DTO。 此方法为映射创建源数据:
static Element[] CreateElements()
{
var element2 = new Element();
return new[]
{
new Element(new Item(element2)),
element2,
new Element()
};
}
然后我配置映射和映射元素:
Mapper.CreateMap<Element, ElementDto>();
Mapper.CreateMap<Item, ItemDto>();
var elements = CreateElements();
var mappedElements = elements
.Select(_ => Mapper.Map(_, typeof(Element), typeof(ElementDto)))
.OfType<ElementDto>()
.ToArray();
我查看映射结果后:
foreach (var element in mappedElements)
{
Console.WriteLine(mappedElements.Any(e => e?.Item?.Element == element));
}
此代码显示 "False" 三次。因此 "CreateElements" 中的 "element2" 创建了两个副本。
源元素的相同测试将 return "False True False":
foreach (var element in elements)
{
Console.WriteLine(elements.Any(e => e?.Item?.Element == element));
}
因为我需要配置映射以免元素重复?可能吗?
我认为这不是 AutoMapper 问题。
您正在创建三个不同的 Element
项并将它们映射到某种 ElementDto
。它们是三个不同的对象(在结构和引用方面),你不能指望如果将它们映射到同一类型,它们就会相等。
如果您考虑您的物品:
var element2 = new Element();
return new[]
{
new Element(new Item(element2)),
element2,
new Element()
};
比较一下,你会发现 none 是相等的。你没有提供 ElementDto
class 但我的猜测是你应该实现 IEquatable
接口,这将确保正确的比较(或重载运算符)。
这可以手动完成。首先忽略属性 Item
AutoMapper没有复制元素链:
Mapper.CreateMap<Item, ItemDto>()
.ForMember(_ => _.Element, _ => _.Ignore());
其次,手动复制带有标记已查看项目的链:
static IEnumerable<ElementDto> MapElements(Element[] elements)
{
var elementToDtoMap = new Dictionary<Element, ElementDto>();
foreach (var element in elements)
{
MapElement(element, null, elementToDtoMap);
}
return elementToDtoMap.Select(_ => _.Value);
}
static void MapElement(Element element, ItemDto parentItem, Dictionary<Element, ElementDto> elementToDtoMap)
{
ElementDto elementDto = null;
if (elementToDtoMap.TryGetValue(element, out elementDto))
return;
elementDto = Mapper.Map<ElementDto>(element);
elementToDtoMap.Add(element, elementDto);
if (parentItem != null)
{
parentItem.Element = elementDto;
}
if (element.Item != null)
{
MapElement(element.Item.Element, elementDto.Item, elementToDtoMap);
}
}