AutoMapper: объединение значений элементов в коллекции из одного экземпляра в другой - PullRequest
4 голосов
/ 15 марта 2012

Я пытаюсь выяснить, как объединить два сложных объекта, используя AutoMapper.У родительского объекта есть свойство, которое является коллекцией дочерних объектов:

public class Parent
{
    public List<Child> Children { get; set; }
}

public class Child
{
    public string A { get; set; }
    public string B { get; set; }
    public string C { get; set; }
}

У меня есть два экземпляра Parent:

var parentOne = new Parent()
{
    Children = new List<Child>() { new Child() { A = "A", B = "B", C = "C" } }
};

var parentTwo = new Parent()
{
    Children = new List<Child>() { new Child() { C = "Updated value" } }
};

Я хотел бы иметь возможность объединять значенияот parentOne до parentTwo без перезаписи значения C в parentTwo.Карты, которые я создал, следующие:

Mapper.CreateMap<Parent, Parent>()
    .ForMember(parent => parent.Children, opt => opt.UseDestinationValue());

Mapper.CreateMap<Child, Child>()
    .ForMember(child => child.C, opt => opt.Ignore());

Mapper.Map(parentOne, parentTwo);

Насколько я понимаю, AutoMapper будет создавать новые экземпляры сложных свойств, если вы не используете опцию UseDestinationValue().Однако после выполнения приведенного выше кода parentTwo.C равняется "C" вместо "Updated value".

Мне кажется, что он сохраняет экземпляр List<Child>, но создает новые экземпляры Child в пределах Списка.К сожалению, я изо всех сил пытаюсь придумать карту, которая будет содержать каждый экземпляр Child.

Любая помощь будет высоко ценится!

Ответы [ 3 ]

6 голосов
/ 19 марта 2012

Насколько я знаю, AutoMapper не поддерживает это, и я полагаю, что это связано со сложностью поддержки такого сценария.

UseDestinationValue действительно работает только с экземпляром коллекции, как вы и предполагали -не элементы коллекции.В вашем сценарии есть только один элемент в каждом списке, и (я полагаю) вы хотите, чтобы объекты дочерних списков обновлялись «синхронно» (т.е. первый элемент обновляет первый элемент, второй обновляет второй и т. Д.).Но что если один список равен 3, а другой - 5?Что означает «UseDestinationValue», если нет целевого значения?И как вы выбираете, какой десинтационный объект отображать среди двух дочерних списков?В сценарии базы данных (который звучит как основа для вопроса) будут уникальные идентификаторы или какой-то внешний ключ, который позволит вам сопоставить дочерние объекты, чтобы узнать, какой из них сопоставить.В этом случае слишком сложно (IMO) написать общее отображение, которое будет поддерживать UseDestinationValue для отдельных элементов коллекции.

4 голосов
/ 19 марта 2012

Все более и более вероятно, что я не смогу придумать сопоставления, чтобы справиться с этим сценарием; действительно достаточно справедливо, поскольку AutoMapper никогда не предназначался для объединения таким образом значений.

Поэтому я прибегнул к обработке коллекции вручную в цикле. Для целей этого ответа я предполагаю, что Parent.A уникально идентифицирует свойство:

Mapper.CreateMap<Parent, Parent>()
    .ForMember(parent => parent.Children, opt => opt.Ignore());

Mapper.CreateMap<Child, Child>()
    .ForMember(child => child.C, opt => opt.Ignore());

Mapper.Map(parentOne, parentTwo);

foreach (var childTwo in parentTwo.Children)
{
    var childOne = parentOne.Children.Where(child => child.A == childTwo.A).Single();
    Mapper.Map(childOne, childTwo);
}
2 голосов
/ 09 июля 2014

Я столкнулся с этой же проблемой при отображении из ViewModel (DTO) в EntitySet.Вот метод, который я написал, чтобы решить проблему.Он синхронизирует коллекцию ViewModels в коллекцию Entities.

В вашем отображении autopper вам нужно будет полностью игнорировать коллекцию.

public void SyncronizeEntitySet<TViewModel, TEntity>(IEnumerable<TViewModel> modelSet, ICollection<TEntity> entitySet,
        Func<TViewModel, int> sourceKey, Func<TEntity, int> destinationKey, Action<TEntity> setParentKey)
    where TViewModel : class, new()
    where TEntity : class, new()
{
    var toDelete = new List<TEntity>();
    foreach (var entityItem in entitySet)
    {
        var modelItem = modelSet.FirstOrDefault(i => sourceKey(i) == destinationKey(entityItem));
        if (modelItem == null)
        {
            toDelete.Add(entityItem);
        }
        else
        {
            Mapper.Map(modelItem, entityItem);
        }
    }

    toDelete.ForEach(i => Delete(i));

    foreach (var modelItem in modelSet)
    {
        if (sourceKey(modelItem) == 0)
        {
            var entityItem = Mapper.Map<TEntity>(modelItem);
            setParentKey(entityItem);
            Add(entityItem);
        }
    }
}
...