Automapper не отображается внутри вложенной коллекции не работает должным образом
Все. Например, у нас есть такие простые классы
public class SimpleModel
{
public int PropertyId { get; set; }
public ICollection<SimpleModelCollectionItem> SimpleCollection { get; set; }
}
public class SimpleModelCollectionItem
{
public int PropertyId { get; set; }
}
public class SimpleEntity
{
public int Id { get; set; }
public int PropertyId { get; set; }
public virtual ICollection<SimpleEntityCollectionItem> SimpleCollection { get; set; }
}
public class SimpleEntityCollectionItem
{
public int Id { get; set; }
public int PropertyId { get; set; }
}
и у нас есть код конфигурации
AutoMapper.Mapper.CreateMap<SimpleModel, SimpleEntity>()
.ForMember(dest => dest.Id, src => src.Ignore())
.ForMember(dest => dest.SimpleCollection, src => src.UseDestinationValue());
AutoMapper.Mapper.CreateMap<SimpleModelCollectionItem, SimpleEntityCollectionItem>()
.ForMember(dest => dest.Id, src => src.Ignore());
AutoMapper.Mapper.AssertConfigurationIsValid();
и инициализация тестовых данных
var model = new SimpleModel
{
PropertyId = 2,
SimpleCollection =
new List<SimpleModelCollectionItem>
{
new SimpleModelCollectionItem
{
PropertyId = 7
}
}
};
var entity = new SimpleEntity
{
Id = 1,
PropertyId = 3,
SimpleCollection =
new List<SimpleEntityCollectionItem>
{
new SimpleEntityCollectionItem
{
Id = 5,
PropertyId = 4
}
}
};
AutoMapper.Mapper.Map(model, entity);
и я ожидаю увидеть
Console.WriteLine(entity.Id); // 1
Console.WriteLine(entity.PropertyId); // 2
Console.WriteLine(entity.SimpleCollection.First().Id); // 5 but was 0
Console.WriteLine(entity.SimpleCollection.First().PropertyId); // 7
Можно ли установить Id для внутренней коллекции равной 5, как это было изначально?
1 ответ
Таким образом, когда AutoMapper отображал вашу коллекцию, он фактически удалял старый элемент из целевой коллекции, а затем добавлял новый элемент.
Вы можете проверить это с помощью этого кода:
var item_before = entity.SimpleCollection.First();
AutoMapper.Mapper.Map(model, entity);
var item_after = entity.SimpleCollection.First();
bool item_same_references = object.ReferenceEquals(item_before, item_after);
Значение item_same_references
будет false
,
Это происходит, даже если вы используете src => src.UseDestinationValue()
, Использование только этого означает, что сам объект коллекции должен использоваться повторно, но не элементы в этой коллекции.
Я не уверен, есть ли способ сказать AutoMapper также использовать те же элементы коллекции.
Кроме того, если подумать, что произойдет, если в целевой коллекции будет больше элементов, чем в исходной коллекции?
Итак, вы получаете ноль, потому что когда AutoMapper создает новый элемент для добавления в коллекцию, значение по умолчанию Id
свойство default(int)
который равен нулю.
Я предлагаю следующее:
Я предполагаю, что количество элементов в исходной и целевой коллекциях одинаково.
Во-первых, измените конфигурацию сопоставления, чтобы дать AutoMapper возможность игнорировать коллекцию следующим образом:
AutoMapper.Mapper.CreateMap<SimpleModel, SimpleEntity>()
.ForMember(dest => dest.Id, opt => opt.Ignore())
.ForMember(dest => dest.SimpleCollection, opt => opt.Ignore());
Затем создайте такой метод, который отображает элементы коллекции на месте (не удаляя и не добавляя новые элементы), например:
public void MapCollectionsInPlace<TSource, TDestination>(IEnumerable<TSource> source_collection,
IEnumerable<TDestination> destination_collection)
{
var source_enumerator = source_collection.GetEnumerator();
var destination_enumerator = destination_collection.GetEnumerator();
while (source_enumerator.MoveNext())
{
if(!destination_enumerator.MoveNext())
throw new Exception("Source collection has more items than destination collection");
AutoMapper.Mapper.Map(source_enumerator.Current, destination_enumerator.Current);
}
}
Теперь вы можете использовать следующий код:
AutoMapper.Mapper.Map(model, entity);
MapCollectionsInPlace(model.SimpleCollection, entity.SimpleCollection);