虽然我对AutoMapper比较陌生,但我正在开发一个小项目中使用它。我之前从未遇到过使用它的问题,但现在我面临一些将参数传递给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;
}
我在Web服务上调用这个方法,问题是:第一次调用webservice(使用webservice控制台)时,它运行完美。例如,如果我通过'555'作为userId,我会使用正确的值来获取此方法:
在Custom Resolver中,值正确传递给构造函数:
返回的结果是正确的。问题接下来。第二次调用服务时,传递一个不同的参数(这次是'666'),获取Custom Resolver构造函数的参数是旧参数('555')。这就是我的意思:
在映射对象之前,我们可以看到传递给构造函数的值是正确的('666'):
但当它到达解析器的构造函数时,值是错误的,并且是旧的('555'):
对服务的所有后续调用都使用Custom Resolver构造函数('555')中的原始值,与我传递给服务的值无关(如果我从另一个浏览器进行调用,也会发生这种情况)。如果我关闭服务器并重新启动它,我可以传递一个新参数(将在所有其他调用中使用,直到我再次关闭它)。
有关为何发生这种情况的任何想法?
答案 0 :(得分:7)
这种情况正在发生,因为AutoMapper.CreateMap
是一个静态方法,只需要调用一次。使用Web方法中的CreateMap
代码,每次在Web服务上调用该方法时,都会尝试调用它。由于Web服务器进程在调用之间保持活动状态(除非您重新启动它,就像您说的那样),因此静态映射保持不变。因此,正如你在答案中所说的那样,有必要致电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; } }
答案 1 :(得分:2)
回答自己:我没有使用AutoMapper.Reset()。一旦我这样做,一切都开始正常。
有用的阅读:http://www.markhneedham.com/blog/2010/01/27/automapper-dont-forget-mapper-reset-at-the-start/