stevejgordon
stevejgordon

Reputation: 447

MVC How to map from my domain model to a specific view model

I'm getting started with the concept of mapping domain models to view models in ASP.NET MVC after watching a recommendation to do this to pass specific viewModels to the views.

I've been able to manage a basic mapping of one domain model to a simpler viewmodel with less properties but now need to produce a more complex viewmodel and can't figure it out. I have the following domain models

public class Club
{
    public int ClubID { get; set; }
    public string FullName { get; set; }
    public string Description { get; set; }
    public string Telephone { get; set; }
    public string URL { get; set; }
    public DateTime CreatedDate { get; set; }

    public virtual ICollection<Member> Members{ get; set; }
}

public class Member
{
    public int MemberID{ get; set; }
    public string Name { get; set; }
    public MemberType Membership{ get; set; }
    public virtual Club Club { get; set; }
    public virtual int ClubID { get; set; }
}

public enum MemberType
{
    Standard,
    Special,
    Limited
}

I want to map to a view model such as this (note: I've split it like this because I think it makes sense but I'm not sure)...

public class ClubDetailsViewModel
{
    public int ClubID { get; set; }
    public string FullName { get; set; }
    public string Description { get; set; }
    public IList<ClubDetailsMemberSummaryViewModel> Members { get; set; }
}

public class ClubDetailsMemberSummaryViewModel
{
    public MemberType Membership { get; set; }
    public int MemberCount { get; set; }
}

What I'm trying to end up with is a page which displays some of the club details plus a summary report of the member types at the club with a count of the members. Such as:

Some Club Name
Description of the club.....

CLUB MEMBERS
Limited - 15
Standard - 100

So I think the viewmodel makes sense for this (although might be a better way to do it). Where I'm struggling is how to map the elements. I can get the Club to map the main fields to the club viewmodel but really can't work out how to map the result of the list of clubs onto their view model and then add that to the main view model as a list.

I'm getting the clubs from my repository using this

var clubs = _clubRepository.GetClubByID(ID);

Then I can transform the Courts which are returned using an include in the data access layer from entity framework using this

var grpCourts = from c in clubs.Members
                group c by c.Membership into grp 
                select new { st = grp.Key, count = grp.Distinct().Count() };

How would I loop through the resulting records and map those to the ClubDetailsMemberSummaryViewModel and then add the list of those to the main ClubDetailsViewModel?

Upvotes: 2

Views: 2094

Answers (2)

Andrew Whitaker
Andrew Whitaker

Reputation: 126052

Your mapping from Club to ClubDetailsViewModel will be trivial with the exception of Members. For that property, you could write a quick resolver inline or write your own custom resolver. An inline resolver would look something like this:

Mapper.CreateMap<Club, ClubDetailsViewModel>()
    .ForMember(dest => dest.Members, opt => opt.ResolveUsing(src => 
    {
        return src.Members
            .GroupBy(m => m.Membership)
            .Select(grp => new ClubDetailsMemberSummaryViewModel
            {
                Membership = grp.Key,
                MemberCount = grp.Distinct().Count()
            });
    }));

I think it's good practice to refactor more complex resolvers like this out to their own classes:

public class MembershipClubDetailsResolver : ValueResolver<Club, IList<ClubDetailsMemberSummaryViewModel>>
{
    protected override IList<ClubDetailsMemberSummaryViewModel> ResolveCore (Club source) 
    {
        return source.Members
            .GroupBy (m => m.Membership)
            .Select(grp => new ClubDetailsMemberSummaryViewModel
            {
                Membership = grp.Key,
                MemberCount = grp.Distinct().Count()
            })
            .ToList();
    }
}

And then use that resolver in your mapping:

Mapper.CreateMap<Club, ClubDetailsViewModel>()
    .ForMember(dest => dest.Members, opt => opt.ResolveUsing<MembershipClubDetailsResolver>());

Upvotes: 1

thmsn
thmsn

Reputation: 1996

Your mapping appears to be rather complex, I think I would use the .ConvertUsing method of automapper

Mapper.CreateMap<List<Club>,List<ClubDetailsViewModel>>()
.ConvertUsing<ClubToClubDetailsViewModel>();

The conversion class has the following inheritance

public class ClubToClubDetailsViewModel: TypeConverter<List<Club>,List<ClubDetailsViewModel>>
{
....
}

Alternatively you can tinker with creating two "simple" mappings

Mapper.CreateMap<Club,ClubDetailsViewModel>()

That will map everything except the property called Members

Then you need to create a mapping for the members to ClubDetailsMemberSummaryViewModel, you can do that mapping manually or you can configure this in automapper aswell.

For more specific details on automapper you can visit https://github.com/AutoMapper/AutoMapper/wiki

Upvotes: 0

Related Questions