Как заставить AutoMapper не кэшировать сопоставленные объекты?

Когда AutoMapper встречает объект, который уже был отображен, кажется, что он снова использует этот объект, вместо того, чтобы его повторно отображать. Я считаю, что это делается на основе .Equals().

У меня есть дерево, которое отображается. Итак, node с некоторыми свойствами и дочерними. Более одного из узлов имеют одинаковое значение .Equals(), поскольку оно основывается на свойстве Id. Дети узлов различны, и мне нужны эти повторные отображения, но они используют значение кешированной карты.

Есть ли способ отключить кеширование? Все, что я могу представить, это внедрение нового конвертера, но это полностью поражает цель использования AutoMapper.

Вот пример того, как воспроизводить.

void Main()
{
    var source = new List<Tag>
    {
        new Tag 
        { 
            Id = 1, 
            Name = "Tag 1", 
            ChildTags = new List<Tag>
            {
                new Tag 
                { 
                    Id = 2, 
                    Name = "Tag 2", 
                    ChildTags = new List<Tag> 
                    {
                        new Tag {Id = 3, Name = "Tag 3"},
                        new Tag {Id = 4, Name = "Tag 4"}
                    }
                }
            }
        },
        new Tag { Id = 1, Name = "Tag 1" },
        new Tag 
        {
            Id = 3, Name = "Tag 3", ChildTags = new List<Tag>
            {
                new Tag {Id = 4, Name = "Tag 4"}
            }
        }
    };

    Mapper.CreateMap<Tag, Tag>();
    var results = Mapper.Map<IList<Tag>, IList<Tag>>(source);

    results.Dump();
}

public class Tag
{
    public int Id { get; set; }
    public string Name { get; set; }
    public IEnumerable<Tag> ChildTags { get; set; }

    public override bool Equals(Object obj)
    {
        if (obj == null)
        {
            return false;
        }

        var x = this;
        var y = (Tag)obj;

        return x.Id.Equals(y.Id);
    }

    public override int GetHashCode()
    {
        return Id.GetHashCode();
    }
}

Ответ 1

Теперь есть возможность отключить кеш.

Mapper.CreateMap<Tag, Tag>();
var results = Mapper.Map<IList<Tag>, IList<Tag>>(source, opt => opt.DisableCache = true);

Ответ 2

Я столкнулся с той же проблемой с картографом, оглядевшись, обнаружил, что решение для него, добавив

Mapper.Reset();

Исходный блог

Ответ 3

Я также получаю ту же проблему. Это не происходит, если вы дважды сопоставляете один и тот же объект - это происходит, когда у вас есть иерархия дерева объектов, и одно и то же значение существует в двух местах дерева (но с разными дочерними значениями) При сопоставлении второго экземпляра элемента - он использует дочерние значения первого экземпляра, вместо того, чтобы переоценивать значения дочерних значений.

Вот мой пример:

class Tag { 
  int Id {get; set;}
  string Name {get; set;}
  IEnumerable<Tag> ChildTags  {get; set;}
}

public void Test()
{
var source =  new List<Tag>
            {
                new Tag { Id = 1, Name = "Tag 1", ChildTags = new List<Tag>
                            {
                                new Tag { Id = 2, Name = "Tag 2", ChildTags = new List<Tag> 
                                            {
                                                new Tag {Id = 3, Name = "Tag 3"},
                                                new Tag {Id = 4, Name = "Tag 4"}
                                            }
                                    }
                            }
                    },
                new Tag { Id = 1, Name = "Tag 1" },
                new Tag {
                        Id = 3, Name = "Tag 3", ChildTags = new List<Tag>
                            {
                                new Tag {Id = 4, Name = "Tag 4"}
                            }
                    }
            };

Mapper.CreateMap<Tag, Tag>()
    .ForMember(dest => dest.ChildTags,
        opt => opt.MapFrom(src => src.ChildTags));
var result = Mapper.Map<IList<Tag>, IList<Tag>>(tags);
}

В результате

  • первый экземпляр тега 1 (т.е. источник [0]), и все его дети являются совершенными

  • второй экземпляр тега 1 (т.е. источник [1]) имеет все дочерние элементы первого экземпляра - у него не должно быть детей

  • второй экземпляр тега 3 (т.е. источник [2]) не имеет детей - он должен иметь тег 4 как дочерний

Ответ 4

Когда AutoMapper встречает объект, который уже был отображен, он похоже, снова использует этот объект, вместо того, чтобы пытаться повторно отобразить его. я верьте, что это делается на основе .Equals()

Можете ли вы объяснить, почему и когда вы это видите?

После быстрого просмотра исходного кода я уверен, что для объектов нет кеша. Вот тест, который иллюстрирует это:

   public class CustomerSource
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public DateTime DateOfBirth { get; set; }

        public int NumberOfOrders { get; set; }
    }

    public class CustomerTarget
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public DateTime DateOfBirth { get; set; }

        public int NumberOfOrders { get; set; }
    }

    [TestMethod]
    public void Test_AutoMapper()
    {
        Mapper.CreateMap<CustomerSource, CustomerTarget>();

        var source = new CustomerSource() { DateOfBirth = DateTime.Now, FirstName = "FirstName", LastName = "LastName", NumberOfOrders = int.MaxValue };

        var res1 = Mapper.Map<CustomerSource, CustomerTarget>(source);
        Console.WriteLine(res1.FirstName); // PRINT FirstName

        source.FirstName += "[UPDATED]";
        source.LastName += "[UPDATED]";

        var res2 = Mapper.Map<CustomerSource, CustomerTarget>(source);
        Console.WriteLine(res1.FirstName); // PRINT FirstName[UPDATED]

    }

Без вашего кода трудно глубже проникнуть. Существует также метод Mapper.Reset(), который очищает MapperEngine и MapingConfiguration (все внутренние выражения отображения будут потеряны)

Ответ 5

Похоже, что поведение Equals, которое вы сопоставляете, является неуместным.

Метод должен возвращать только true, если "указанный объект равен текущему объекту". - http://msdn.microsoft.com/en-us/library/bsc2ak47.aspx

В вашем случае у вас есть два дерева объектов, имеющих один и тот же идентификатор, но ясно, что они не являются "равными", поскольку у них разные дети.

Я предлагаю посмотреть, почему метод Equals был подвергнут насилию таким образом, и можете ли вы получить нужное поведение, не переопределяя метод Equals, вместо этого используя другой метод для проверки поля идентификатора дерева с более подходящим именем, например, TreeIdsAreEqual.