lundi 8 mai 2017

ASP.NET MVC Iterate over Model Properties with Data Annotations

Problem Description

My problem is similar to this question but instead of applying Data Annotations to the property Name via reflection (handled by ModelMetadata.DisplayName) I am applying them to the value (not handled by ModelMetadata).

Detailed Description

In the context of an ASP.NET MVC program.
Suppose I have a Model class

public class Model
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string NickName { get; set; }
    public string Address { get; set; }
    public int Phone { get; set; }

    [Display(Name = "Start Date")]
    [DataType(DataType.Date)]
    [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:dd/MM/yyyy}")]
    public DateTime StartDate { get; set }

    [Display(Name = "End Date")]
    [DataType(DataType.Date)]
    [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:dd/MM/yyyy}")]
    public DateTime EndDate { get; set }
}

Then suppose this Model is used in at least 5 different views where the value of every property must be displayed in full. (Sometimes for every instance, other times for a handful, other times for one).

I can manually list each Property access within it's own
<td>@Html.DisplayFor(item.<Property>)</td>
for every view.

But that won't help me if later on the definition for Model expands to include new Properties (like Description and Relation and Reliability). Then I'd need to manually update every occurrence of the complete Model listing.

I can use reflection to iterate over a list of PropertyInfo's and save having to manually list each property by using
<td>@property.GetValue(item)</td>

But DisplayFor(x) does not support an expression as complex as x=>property.GetValue(item), and this means I lose the Data Annotations that format my DateTime as
01/01/1990
instead of
01-Jan-90 12:00:00 AM
and would likely also result in the loss of all types of annotation including validation.

Problem Solutions

So far I have considered (and in some cases attempted) the following solutions:

  • [Failed] Manually craft an Expression which emulates the functionality of @property.GetValue(item)
  • Retrieve the value manually as normal, and
    • execute a method on it to manually retrieve and implement Annotation Data on it prior to insertion in the view element as this question suggests, or
    • Re-implement the DisplayFor handling of Data Annotations on an as-needed basis in a Display Template View and apply that directly to the value via DisplayFor as this question suggested
  • Refactor the Model class to contain only a list(SortedList?) of 'Prop' instances, where 'Prop' is a class representing a Property with a Name and Value element.

This last solution would turn the broken
@Html.DisplayFor(m=>property.GetValue(item)
into a theoretically working
@Html.DisplayFor(m=>item.Properties[i].Value)
Which aside from the slightly unintuitive need for getting a property called Name (Properties["Name"]) by (.Value), seems the most workable solution, at the cost of Model clarity.

The Question

This is at the moment, purely a learning exercise, but I would like to know...
Is it possible to succeed where I have failed and both have my Reflection cake and eat the Data Annotations too? Or must I seek an alternative solution?
If I must seek an alternative solution, are there routes I have missed, or am I at least on the right track?





Aucun commentaire:

Enregistrer un commentaire