开发者

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)
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜