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);
Другие вопросы по тегам