AutoMapper UseValue,ResolveUsing或MapFrom重用以前调用的值。它是缓存还是什么?

时间:2013-01-06 15:04:44

标签: ajax dynamic asp.net-mvc-4 automapper generic-collections

我遇到的情况是从第二个调用和后续调用(Ajax GET调用)开始,AutoMapper正在重用前一个值(来自操作链接中单击的第一个调用的值)。这就像是一个“缓存”问题......

public virtual ActionResult List(int assessmentId, int? chapterId, bool? isMenuClick)
{

    Mapper.CreateMap<Element, AssessmentQuestionViewModel>().
    ForMember(dest => dest.AssessmentId, opt => opt.MapFrom(e => assessmentId));

    ...

}

如果我在上述UseValue lambda中使用ResolveUsingMapFromopt =>,则无关紧要。行为是相同的,也就是说,它重用以前调用的值。

源类型(AssessmentId)中不存在

Element属性。通过这种方式,我尝试在后续调用我拥有此代码的方法时为AssessmentId分配一个“可能”动态更改的值。 assessmentId是我的ASP.NET MVC操作方法中的一个参数,如上面方法签名中所示。

然后我在List操作方法中调用此代码:

var questions =
    Mapper.Map<IEnumerable<Element>, IEnumerable<AssessmentQuestionViewModel>>
     (Database.Elements.Where(e => !elementIds.Contains(e.ElementId) &&
                              e.Standard.ChapterId == chapterId));

第一次,questions没问题,也就是说,所有AssessmentQuestionViewModel个对象都按照定义的AssessmentId正确设置了CreateMap属性。

从第二个电话开始,它会重复使用第一个电话中的assessmentId并且它与我的业务逻辑混淆,因为我希望它将AssessmentId映射到正在更新的assessmentId作为参数传递给List方法。

只是为了确定:我在代码中设置了一个断点,我可以看到assessmentId参数的值是正确的。它只是返回的映射对象questions,它在AssessmentId属性中具有错误的值 - 一个与当前assessmentId值不同的值。因为我要求AutoMapper使用当前值进行映射,所以这些值应该是我理解的。

我有AutoMapper 2.2.1-ci9000(预发布),但我用之前的版本测试了这个,我看到了同样的行为。我更新了Prerelease,认为这种“不良行为”会消失。

我认为这是一个错误。如果我错了或者我试图以不支持的方式使用它,请纠正我。 :)

1 个答案:

答案 0 :(得分:4)

我认为你在尝试创建相同类型的多个映射时遇到问题 - 这是AutoMapper不支持的。每次调用List操作时,都会创建一个新映射(具有不同的ForMember(...)子句)。 AutoMapper不会抛出异常,只会忽略重复的映射,所以你在这里看到的不是bug,它是预期的行为。

ForMember实际上是在每个地图上调用的,但是,这里有一个范围问题,因为您的变量被硬编码到表达式中。作为一种解决方法,你可以做类似的事情:

public class MyController
{
    public MyController()
    {
        // define mapping once, but make assessment expression dynamic
        Mapper.CreateMap<Element, AssessmentQuestionViewModel>().
            ForMember(dest => dest.AssessmentId, opt => opt.MapFrom(e => GetCurrentAssessmentId()));
    }

    private int GetCurrentAssessmentId()
    {
        return (int)TempData["AssessmentId"];
    }

    public ActionResult List(int assessmentId, ...)
    {
        // store current assessment temporarily
        TempData.Add("AssessmentId", assessmentId);
        // execute mapping
        var questions = Mapper.Map<IEnumerable<Element>, IEnumerable<AssessmentQuestionViewModel>>
 (Database.Elements.Where(e => !elementIds.Contains(e.ElementId) &&
                          e.Standard.ChapterId == chapterId));
    }
}

我会说,为了实现这一目标,你需要通过大量的工作,在没有AutoMapper帮助的情况下手动设置属性会更简单。

var questions = Mapper.Map<IEnumerable<Element>, IEnumerable<AssessmentQuestionViewModel>>(...);
foreach (var q in questions)
{
    q.AssessmentId = assessmentId;
}