AutoMapper - передача параметра странному поведению пользовательского преобразователя

Хотя я относительно новичок в AutoMapper, я использую его в небольшом проекте, который разрабатываю. Раньше у меня никогда не было проблем с его использованием, но теперь я столкнулся с каким-то странным поведением при передаче параметров в Custom Resolver.

Вот сценарий: я получаю список сообщений из своего репозитория, а затем сопоставляю их с удобной для внешнего интерфейса версией. Ничего необычного, просто какое-то сопоставление нормалей между объектами. У меня есть поле в этом внешнем объекте, которое сообщает, проголосовал ли уже определенный пользователь за это сообщение, и именно для этого я использую Custom Resolver (это второй «ForMember»):

    public List<SupportMessageUi> GetAllVisible(string userId)
    {
        Mapper.CreateMap<SupportMessage, SupportMessageUi>()
              .ForMember(dest => dest.Votes,
                         opt => opt.ResolveUsing<SupportMessageVotesResolver>())
              .ForMember(dest => dest.UserVoted,
                         opt => opt.ResolveUsing<SupportMessagesUserVotedResolver>()
                                   .ConstructedBy(() => new SupportMessagesUserVotedResolver(userId)));

        var messages = _unitOfWork.MessagesRepository.Get(m => m.Visible);

        var messagesUi = Mapper.Map<List<SupportMessageUi>>(messages);

        return messagesUi;
    }


Я вызываю этот метод для веб-службы, и возникает проблема: при первом вызове веб-службы (используя консоль веб-службы) все работает отлично. Например, если я передам «555» в качестве идентификатора пользователя, я получу этот метод с правильным значением:

введите здесь описание изображения


И в Custom Resolver значение было правильно передано конструктору: введите здесь описание изображения


Возвращаемые результаты верны. Проблема идет дальше. Во второй раз, когда я вызываю службу, передавая другой аргумент (на этот раз «666»), аргумент, который попадает в конструктор Custom Resolver, является старым ('555'). Вот что я имею в виду:

Прямо перед сопоставлением объектов мы видим, что значение, переданное конструктору, было правильным ('666'): введите описание изображения  здесь


Но когда он попадает в конструктор Resolver, значение неверное и является старым ('555'): введите здесь описание изображения


Все последующие вызовы службы используют исходное значение в конструкторе Custom Resolver ('555'), независимо от значения, которое я передаю службе (также происходит, если я делаю вызов из другого браузера). Если я выключу сервер и перезапущу его, я могу передать новый параметр (который будет использоваться во всех других вызовах, пока я снова не выключу его).

Любая идея о том, почему это происходит?


person palroj    schedule 17.09.2014    source источник


Ответы (2)


Это происходит потому, что AutoMapper.CreateMap является статическим методом, и его нужно вызывать только один раз. С кодом CreateMap в вашем веб-методе вы пытаетесь вызывать его каждый раз, когда вызываете этот метод в своем веб-сервисе. Поскольку процесс веб-сервера остается активным между вызовами (если вы не перезапустите его, как вы сказали), статические сопоставления остаются на месте. Отсюда и необходимость вызова AutoMapper.Reset, как вы сказали в своем ответе.

Но рекомендуется поместить создание сопоставления в AppStart или Global, или в статический конструктор, или в что-то еще, чтобы вы вызывали его только один раз. Существуют способы вызова Map, которые позволяют вам передавать значения, поэтому вам не нужно пытаться что-то усовершенствовать с помощью конструктора вашего ValueResolver.

Вот пример использования ValueResolver (обратите внимание на изменение реализации IValueResolver вместо наследования ValueResolver<TSource, TDestination>):

[Test]
public void ValueTranslator_ExtraMapParameters()
{
    const int multiplier = 2;
    ValueTranslator translator = new ValueTranslator();
    Mapper.AssertConfigurationIsValid();

    ValueSource source = new ValueSource { Value = 4 };
    ValueDest dest = translator.Translate(source, multiplier);
    Assert.That(dest.Value, Is.EqualTo(8));

    source = new ValueSource { Value = 5 };
    dest = translator.Translate(source, multiplier);
    Assert.That(dest.Value, Is.EqualTo(10));
}

private class ValueTranslator
{
    static ValueTranslator()
    {
        Mapper.CreateMap<ValueSource, ValueDest>()
            .ForMember(dest => dest.Value, opt => opt.ResolveUsing<ValueResolver>().FromMember(src => src.Value));
    }

    public ValueDest Translate(ValueSource source, int multiplier)
    {
        return Mapper.Map<ValueDest>(source, opt => opt.Items.Add("multiplier", multiplier));
    }

    private class ValueResolver : IValueResolver
    {
        public ResolutionResult Resolve(ResolutionResult source)
        {
            return source.New((int)source.Value * (int)source.Context.Options.Items["multiplier"]);
        }
    }
}

private class ValueSource { public int Value { get; set; } }
private class ValueDest { public int Value { get; set; } }

А вот пример использования TypeConverter:

[Test]
public void TypeTranslator_ExtraMapParameters()
{
    const int multiplier = 3;
    TypeTranslator translator = new TypeTranslator();
    Mapper.AssertConfigurationIsValid();

    TypeSource source = new TypeSource { Value = 10 };
    TypeDest dest = translator.Translate(source, multiplier);
    Assert.That(dest.Value, Is.EqualTo(30));

    source = new TypeSource { Value = 15 };
    dest = translator.Translate(source, multiplier);
    Assert.That(dest.Value, Is.EqualTo(45));
}

private class TypeTranslator
{
    static TypeTranslator()
    {
        Mapper.CreateMap<TypeSource, TypeDest>()
            .ConvertUsing<TypeConverter>();
    }

    public TypeDest Translate(TypeSource source, int multiplier)
    {
        return Mapper.Map<TypeDest>(source, opt => opt.Items.Add("multiplier", multiplier));
    }

    private class TypeConverter : ITypeConverter<TypeSource, TypeDest>
    {
        public TypeDest Convert(ResolutionContext context)
        {
            TypeSource source = (TypeSource)context.SourceValue;
            int multiplier = (int)context.Options.Items["multiplier"];

            return new TypeDest { Value = source.Value * multiplier };
        }
    }
}

private class TypeSource { public int Value { get; set; } }
private class TypeDest { public int Value { get; set; } }
person sliderhouserules    schedule 07.03.2015

Отвечаю себе: я не использовал AutoMapper.Reset(). Как только я это сделал, все стало работать правильно.

Полезное чтение: http://www.markhneedham.com/blog/2010/01/27/automapper-dont-forget-mapper-reset-at-the-start/

person palroj    schedule 23.09.2014