I have a situation where starting from the 2nd call and subsequent ones (Ajax GET calls), AutoMapper
is reusing the previous value (the value from the 1st call that comes from a click in an action link). It's like a "caching" problem...
public virtual ActionResult List(int assessmentId, int? chapterId, bool? isMenuClick)
{
Mapper.CreateMap<Element, AssessmentQuestionViewModel>().
ForMember(dest => dest.AssessmentId, opt => opt.MapFrom(e => assessmentId));
...
}
It doesn't matter if I use UseValue
, ResolveUsing
or MapFrom
in the above opt =>
lambda. The behavior is the same, that is, it reuses the value from previous calls.
AssessmentId
property does not exist in the source type ( Element
). This way I try to assign AssessmentId
a value that "may" change dynamically during subsequent calls to the method where I have this code. assessmentId
is a parameter in my ASP.NET MVC action method as shown above in the method signature.
Then I call this code in the List
action method:
var questions =
Mapper.Map<IEnumerable<Element>, IEnumerable<AssessmentQuestionViewModel>>
(Database.Elements.Where(e => !elementIds.Contains(e.ElementId) &&
e.Standard.ChapterId == chapterId));
The first time, questions
is OK, that is, all AssessmentQuestionViewModel
objects have the AssessmentId
property set correctly as per the CreateMap
defined.
Starting from the 2nd call, it reuses the assessmentId
from the 1st call and it messes up with my business logic because I expect it to map AssessmentId
to the updated assessmentId
that's being passed as a parameter to the List
method.
Just to be sure: I've set a breakpoint in the code and I can see that the value of the assessmentId
parameter is correct. It's just the returned mapped objects questions
that have the wrong value in the AssessmentId
property - a value that differs from the current assessmentId
value. The values should be equal as I understand it since I'm asking AutoMapper to do the mapping using that current value.
I have AutoMapper 2.2.1-ci9000 (Prerelease), but I tested this with the previous version and I saw this same behavior. I updated to the Prerelease thinking that this "misbehavior" would go away.
I think this is a bug. Please correct me if I'm wrong or if I'm trying to use it in a way not supported. :)
I think the problem here your trying to create multiple mappings of the same type - which AutoMapper doesn't support. Everytime your List
action is called, you create a new mapping (which has a different ForMember(...)
clause). AutoMapper won't throw an exception it just ignores the duplicate mapping so what you are seeing here isn't a bug, it's expected behaviour.
ForMember
is infact called on every map, however, you have a scoping issue here as your variable is hard-coded into the expression. As a work-around you could do something like:
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));
}
}
I will say though, your jumping through a lot of hoops for this to work, it would be much simpler to manually set the property without the help of AutoMapper e.g.
var questions = Mapper.Map<IEnumerable<Element>, IEnumerable<AssessmentQuestionViewModel>>(...);
foreach (var q in questions)
{
q.AssessmentId = assessmentId;
}