Search code examples
c#solid-principles

initializing a sub viewmodel from within a viewmodel


I've got my IndexViewModel which sends (currently ) one field to the view

  • List<StaffMemberViewModel> StaffMembers

The StaffMemberViewModel just holds the Full Name of the staff member as well as their CorporateTitle.

All of the Staff data is contained within a JSON file (but could just as easily be in a database).

Here's how I'm currently building the IndexViewModel

   public class IndexViewModel : LayoutViewModel
    {
        public IndexViewModel()
        {
            // initialize _staffMembers
            _staffMembers = new List<StaffMemberViewModel>();

            var staffModel = new JsonFileReader()
                .GetModelFromFile<List<Staff>>
                (@"~/App_Data/Staff.json");

            foreach (var member in staffModel)
            {
                _staffMembers.Add(new StaffMemberViewModel
                {
                    FullName = string.Format("{0} {1} {2}", member.FirstName, member.MiddleInitial, member.LastName),
                    CorporateTitle = member.CorporateTitle
                });
            }
        }

        private readonly List<StaffMemberViewModel> _staffMembers;
        public List<StaffMemberViewModel> StaffMembers {get {return _staffMembers;}}
    }

    public class StaffMemberViewModel
    {
        public string FullName { get; set; }
        public string CorporateTitle { get; set; }
    }

Then in the controller I just use something like this.

[HttpGet]
public ActionResult Index()
{
    var viewModel = new IndexViewModel;
    return View(viewModel);
}

If I want my code to follow S.O.L.I.D principals, how could I better wire this up? Keeping in mind that Controllers are supposed to be thin, and therefore I don't want to bloat the controller with the call to the json file. Should I create a service to populate the StaffMemberViewModel?


I should also note: the IndexViewModel inherits from an abstract LayoutViewModel that contains things like the page title and other reusable properties. For brevity, I didn't show it. I will also be adding more properties to the IndexViewModel, so solving this by removing the IndexViewModel layer and going straight to the StaffMemberViewModel is not a valid solution.


Solution

  • It looks like StaffMemberViewModel is, in fact, a model? It is a model that your ViewModel is attempting to display. As for wiring it up, I'd do a little tidying and dependency injection, as well as create a Mapper Factory:

    public class IndexViewModel : LayoutViewModel
    {
        public IndexViewModel(IEnumerable<StaffModel> staffModel)
        {
            _staffMembers = StaffModelMapperFactory.Map(staffModel);
        }
    
        private readonly List<PartialStaffViewModel> _staffMembers;
        public List<PartialStaffViewModel> StaffMembers {get {return _staffMembers;}}
    }
    
    public class PartialStaffViewModel
    {
        public string FullName { get; set; }
        public string CorporateTitle { get; set; }
    }
    
    public class StaffModelMapperFactory
    {
        public static List<PartialStaffViewModel> Map(IEnumerable<StaffModel> staff)
        {
            return staff.Select(member => new PartialStaffViewModel
                {
                    FullName = string.Format("{0} {1} {2}", member.FirstName, member.MiddleInitial, member.LastName), 
                    CorporateTitle = member.CorporateTitle
                }).ToList();
        }
    }
    
    [HttpGet]
    public ActionResult Index()
    {
        var staffModel = new JsonFileService().ReadFileToModel<List<StaffModel>>(@"~/App_Data/Staff.json");
    
        var model = new IndexViewModel(staffModel)
            {
                Title = "Future State Mobile"
            };
    
        return View(model);
    }