MVC architecture - re-using the same viewmodel for

2019-04-09 01:36发布

问题:

Say we have the following (overly simple) scenario:

We have a screen to view person details and a screen to edit person details.

The screen display person details has the following fields (as display only):

First Name Last Name Bio

The screen edit person details shows has following fields (in input controls):

ID (hidden) First Name Last Name Bio

Say our display viewmodel looks like this:

    public class DisplayPersonViewModel
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Bio { get; set; }
    }

And our edit viewmodel looks like this:

public class EditPersonViewModel
{
    [Required]
    public int ID { get; set; }

    [Required]
    [StringLength(20)]
    public string FirstName { get; set; }

    [Required]
    [StringLength(20)]
    public string LastName { get; set; }

    [Required]
    public string Bio { get; set; }
}

Not much difference between the 2, eh? The edit model has one extra field (ID) and some of attributes on the properties. Now, if we were to combine the 2 like this:

    public class DisplayPersonViewModel
    {
        [Required]
        [StringLength(20)]
        public string FirstName { get; set; }

        [Required]
        [StringLength(20)]
        public string LastName { get; set; }

        [Required]
        public string Bio { get; set; }
    }

    public class EditPersonViewModel : DisplayPersonViewModel
    {
        [Required]
        public int ID { get; set; }
    }

This is certainly more DRY, since we don't have duplicate fields to maintain, but now we have extraneous info (attributes) on our display viewmodel. I'm leaning more towards the second approach, regardless, because some of our screens have over 25 fields! (...and that's out of my control, so please don't harp on it :) ...) However, I just want to hear opinions to get a better sense of what may be "best practice".

回答1:

Yes, the second approach seems fine to me. No worries other than this itchy feeling in your stomach telling you why on earth are you decorating a display view model with validation attributes. But if you can live with it it's really something that is preferred compared to duplicating the view models.

Unfortunately personally I cannot live with this feeling in my stomach and that's why I use FluentValidation.NET to define my validation rules instead of data annotations. It allows me to have those rules separately from my view models and then I don't worry about polluting the so called display view model with validation rules. So I would define in the same way as you 2 view models and EditPersonViewModel would derive from DisplayPersonViewModel and then define my EditPersonViewModelValidator for the EditPersonViewModel in a separate class.

Oh and a side note: decorating a non-nullable type with the [Required] attribute is not necessary. All non-nullable types are required by their very basic nature. So instead of:

[Required]
public int ID { get; set; }

you should only have:

public int ID { get; set; }


回答2:

Another option is to use the MetadataType Attribute.

public class PersonModel
{
  public int ID { get; set; }   
  public string FirstName { get; set; }  
  public string LastName { get; set; }   
  public string Bio { get; set; }   
}

[MetadataType(typeof(IDisplayPersonViewModel))]
public class DisplayPersonViewModel : PersonModel

[MetadataType(typeof(IEditPersonViewModel))]
public class EditPersonViewModel : PersonModel

public interface IDisplayPersonViewModel
{
  [ScaffoldColumn(false)]
  public int ID { get; set; }   
}

public interface IEditPersonViewModel
{
  [Required]                
  [StringLength(20)]                
  public string FirstName { get; set; }                

  [Required]                
  [StringLength(20)]                
  public string LastName { get; set; }                

  [Required]                
  public string Bio { get; set; }                

  [Required]                
  public int ID { get; set; }             
} 

Your raw Person model is attribute free. Your Display and Edit models only have the attributes you actually need for the View.