ViewModel with Collection of another ViewModel - Can AutoMapper Help Me Here?
I have a ViewModel that looks like this:
public class CreateReviewViewModel
{
public string Title { get; set; }
public decimal Score { get; set; }
public ICollection<RecommendationViewModel> Recommendations { get; set; }
}
So, the first two are basic native types - easy.
The third property is a collection of another ViewModel:
public class RecommendationViewModel
{
public RecommendationType RecommendationType { get; set; }
public bool IsRecommendedFor { get; set; }
}
RecommendationType
is an enum
in my domain model, which has byte
values representing a different "recommendation".
On my [HttpGet]
action, i do this:
var model = new CreateReviewViewModel
{
Recommendations = SomeMethodWhichLoopsThroughTheEnumMembersAndCreatesTheModel();
}
return View(model);
So i end up with a list of RecommendationViewModel
, with the bool
properties set to false
.
Then on my View, i use EditorTemplates:
@Html.EditorFor(model => model.Recommendations)
Which calls a custom Editor template th开发者_如何学Goat renders a label and checkbox for the two properties. Cool.
So - there's the background, which hopefully makes sense.
How do i map that ViewModel to a Review
domain model in the [HttpPost]
action?
The part of the Review
object looks like this:
public class Review
{
public bool IsRecommendedForA { get; set; }
public bool IsRecommendedForB { get; set; }
// etc
}
I'm currently doing custom mapping like this:
var review = new Review();
review.IsRecommendedForA = this.Recommendations.SingleOrDefault(x => x.RecommendationType == RecommendationType.A).IsRecommendedFor;
review.IsRecommendedForB = this.Recommendations.SingleOrDefault(x => x.RecommendationType == RecommendationType.B).IsRecommendedFor;
Which is very tedious.
Can i do the above with AutoMapper?
Of course, i could just add all the different RecommendationType's as basic properties on the ViewModel instead of a collection, but then my View's become complicated, and i can't use EditorTemplates to implicitly loop through the collection - i would have to write out Html.EditorFor
for each property.
Any ideas?
I have one possible solution:
Mapper.CreateMap<CreateReviewViewModel, Review>()
.ForMember(dest => dest.IsRecommendedForA, opt => opt.MapFrom(src => src.IsRecommendedFor(RecommendationType.A)))
.ForMember(dest => dest.IsRecommendedForB, opt => opt.MapFrom(src => src.IsRecommendedFor(RecommendationType.B)));
Where IsRecommendedFor
is a hook property, using the LINQ expression stated earlier in my question to work out if the model contains the property, and it's checked.
Which is better than the manual left-to-right, but still not great.
I've taken a look at custom converters/resolvers, but none take additional parameters (e.g resolve from this to this, using this parameter also), so i can't see how i can use them.
I'll go with this for now, but hoping someone knows how to do it better.
Any reason you need to store those boolean values all separately, for every possible enum value? A flag means you could have multiple types enabled... I feel like this would be better:
public class Review {
public RecommendationType Types { get; set; }
public bool IsReviewFor(RecommendationType types) {
// bitwise comparison, check if the flags
// given are in the Types property also
}
}
Easier mapping, less code, more extensible when adding/removing new types.
myReview.IsReviewFor(RecommendationType.X | RecommendationType.Y | RecommendationType.Z)
精彩评论