【问题标题】:AutoMapper - passing parameter to custom resolver weird behaviorAutoMapper - 将参数传递给自定义解析器的奇怪行为
【发布时间】:2016-12-22 00:30:43
【问题描述】:

虽然我对 AutoMapper 比较陌生,但我在我正在开发的一个小项目中使用它。我以前从未遇到过使用它的问题,但现在我在将参数传递给自定义解析器时遇到了一些奇怪的行为。

场景如下:我从存储库中获取消息列表,然后将它们映射到它的前端友好版本。没什么特别的,只是对象之间的一些正常映射。我在该前端对象中有一个字段,它告诉某个用户是否已经为该消息投票,这就是我使用自定义解析器的目的(它是第二个“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;
    }


我在 Web 服务上调用此方法,问题是:我第一次调用 Web 服务(使用 Web 服务控制台)时,它都运行良好。例如,如果我将 '555' 作为 userId 传递,我将使用正确的值访问此方法:


在自定义解析器中,值被正确传递给构造函数:


返回的结果是正确的。问题来了。我第二次调用服务时,传递了一个不同的参数(这次是'666'),到达自定义解析器的构造函数的参数是旧的参数('555')。这就是我的意思:

在映射对象之前,我们可以看到传递给构造函数的值是正确的('666'):


但是当它到达解析器的构造函数时,值是错误的,并且是旧的('555'):


对服务的所有后续调用都使用自定义解析器构造函数 ('555') 中的原始值,与我传递给服务的值无关(如果我从另一个浏览器进行调用,也会发生这种情况)。如果我关闭服务器并重新启动它,我可以传递一个新参数(将在所有其他调用中使用,直到我再次关闭它)。

知道为什么会这样吗?

【问题讨论】:

    标签: parameter-passing automapper resolver


    【解决方案1】:

    这是因为AutoMapper.CreateMap 是一个静态方法,只需要调用一次。使用 Web 方法中的 CreateMap 代码,每次在 Web 服务上调用该方法时,您都会尝试调用它。由于网络服务器 process 在调用之间保持活动状态(除非您像您说的那样重新启动它),因此静态映射保持不变。因此,正如您在回答中所说,有必要致电AutoMapper.Reset

    但建议您将映射创建放在AppStartGlobal 或静态构造函数或其他任何东西中,因此您只调用一次。有一些方法可以调用 Map 允许您传入值,因此您无需尝试使用 ValueResolver 的构造函数来处理问题。

    这是一个使用ValueResolver 的示例(注意更改为实现IValueResolver 而不是继承ValueResolver&lt;TSource, TDestination&gt;):

    [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; } }
    

    【讨论】:

      【解决方案2】:

      回答自己:我没有使用 AutoMapper.Reset()。一旦我这样做了,一切都开始正常工作。

      帮助阅读:http://www.markhneedham.com/blog/2010/01/27/automapper-dont-forget-mapper-reset-at-the-start/

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-03-27
        • 2015-11-24
        • 2021-01-07
        • 2016-02-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多