Search code examples
c#asp.net-mvcmodel-view-controllerasp.net-mvc-viewmodel

MVC Model > View Model Design Pattern


Apologies if this has already been answered, but I am looking for the best practice when dealing with the below scenario.

I have a fairly large MVC5 application which will have numerous forms/pages.

For this example say I have a patient class like the below (this a stripped down version of a larger class)

    Public Class Patient {

    [Display(Name = "Patient Trial Number")]
    public int ID { get; set; }

    [Display(Name = "First Name")]
    public string FirstName { get; set; }

    [Display(Name = "Last Name")]
    public string LastName { get; set; }

    [Display(Name = "Patient DOB")]
    public DateTime PatientDOB { get; set; }

    [Required]
    [Display(Name = "Patient Gender")]
    public Gender PatientGender { get; set; }

    } 

I have various models for each form that needs to be completed. Some of these are very large 80+ properties.

Below is a stripped down example of a form model.

public class ExamplePatientForm

    {
        [Key]
        public Patient PatientID { get; set; }

        [Required]
        [Display(Name = "Was Sample One taken?")]
        public bool? SampleOneTaken{ get; set; }

        [Required]
        [Display(Name = "Date Sample One Taken")]
        public DateTime DateSampleOneTaken { get; set; }

        [Required]
        [Display(Name = "Was Sample Two taken?")]
        public bool? SampleTwoTaken{ get; set; }

        [Required]
        [Display(Name = "Date Sample Two Taken")]
        public DateTime DateSampleTwoTaken { get; set; }

        public int PatientRating {get;set;}

        public string Comments { get; set; }


    }

In reality, both the patient class and individual form classes are a lot larger.

Initially I was using html.HiddenFor to hold the details of the Patient class within the individual forms though this didn't feel right.

I then created ViewModels (see below)

        [Key]
        public int PatientID { get; set; }

        [Required]
        [Display(Name = "Was Sample One taken?")]
        public bool? SampleOneTaken{ get; set; }

        [Required]
        [Display(Name = "Date Sample One Taken")]
        public DateTime DateSampleOneTaken { get; set; }

        [Required]
        [Display(Name = "Was Sample Two taken?")]
        public bool? SampleTwoTaken{ get; set; }

        [Required]
        [Display(Name = "Date Sample Two Taken")]
        public DateTime DateSampleTwoTaken { get; set; }

        public int PatientRating {get;set;}

        public string Comments { get; set; }


    }

The ViewModels removed the relationship and stored the PatientID as an Integer. I then mapped this back to the entity model in my controller.

Though having to duplicate each form model to create a view model seems a but crazy to me, especially as some contain 80+ properties.

Does anyone know the best way to approach this? I will have around 50 unique forms.

Also I have created an editor template for the patient model so I can display some of the patient information at the top of each form. But only need to display certain properties so not sure if I need to create a separate PatientViewModel for this or just hide the other elements.

Hope this makes sense. Any help would be greatly appreciated.

Regards, Rob


Solution

  • I would strongly advice against using your models (entities) as your view models, since these are the models that connects directly to your ORM, e.g. though Entity Framework. This could mean that if someone is sneaky enough, they can update properties they might not should have access to (though POST or GET requests). Your domain models should also be very thin with as less attributes as possible - no DisplayName(...) and such.

    If your view models are very large, you should split them up and reuse them as properties on smaller view models. Maybe even create a factory for building them.

    If need to map them one-to-one tools like AutoMapper can be handy, but I would recommend to do the mapping by hand or using a simple self-made mapper.