Model Property Display Name using Reflection

2019-06-07 20:03发布

问题:

I am working on a project where I need to output a few hundred properties on to the screen. To save myself lots of tedious markup, I decided to use reflection.

//markup removed to keep this concise
@for (var i = 0; i < Model.SiteAndJobDetails.GetType().GetProperties().Count(); i++)
{
  @Model.SiteAndJobDetails.GetType().GetProperties()[i].Name
  @Model.SiteAndJobDetails.GetType().GetProperties()[i].GetValue(Model.SiteAndJobDetails, null)
}

Although slower to render, this will save me writing out about 2 hundred properties and values with HTML helpers. At least, that was the plan. However, I need to use @Html.DisplayNameFor or something similar to pick up the Display attribute value from the property.

My intial thoughts were

@Html.DisplayNameFor(m=>@Model.SiteAndJobDetails.GetType().GetProperties()[i].Name)

But that does not work, I would imagine because I am using reflection here to get the property name. Is there another way?

回答1:

You can get it using the Metadata (that's what the framework does anyway):

string displayName = ViewData.ModelMetadata.Properties
      .Where(x => x.PropertyName == Model.SiteAndJobDetails.GetType()
                                   .GetProperties()[i].Name)
      .SingleOrDefault()
      .DisplayName;


回答2:

@Andrei was correct to use the ViewData.ModelMetadata but had the syntax slightly off. The correct syntax is

@ViewData.ModelMetadata.Properties.First(x => x.PropertyName == "SiteAndJobDetails")
.Properties.SingleOrDefault(x => x.PropertyName == Model.SiteAndJobDetails.GetType().GetProperties()[i].Name)
.DisplayName

The final solution is to check the property exists, if it does use it, otherwise use the property name

@if (!string.IsNullOrEmpty(@ViewData.ModelMetadata.Properties.First(x => x.PropertyName == "SiteAndJobDetails").Properties.SingleOrDefault(x => x.PropertyName == Model.SiteAndJobDetails.GetType().GetProperties()[i].Name).DisplayName))
{
    @ViewData.ModelMetadata.Properties.First(x => x.PropertyName == "SiteAndJobDetails").Properties.SingleOrDefault(x => x.PropertyName == Model.SiteAndJobDetails.GetType().GetProperties()[i].Name).DisplayName
}
else
{
    @Model.SiteAndJobDetails.GetType().GetProperties()[i].Name
}


回答3:

Alternatively, add this to your razor:

@functions {
string R<TCLASS>(Expression<Func<TCLASS, Object>> expression) //Lambda = x => x.TPROPERTY
{
    var memberExpression = expression.Body as MemberExpression;
    if(memberExpression == null)
    {
        memberExpression = (MemberExpression) ((UnaryExpression)expression.Body).Operand;
    }

    return memberExpression.Member.Name;
}
}

Then you can:

@(R<ModelClassName>(x => x.PropertyName)) //Outputs "PropertyName"

I like using this when writing JavaScript that receives JSON. It lets you do this:

<script>
    function recieveJsonResult(classNameDto) {
        var classProperty = classNameDto.@(R<ClassNameDto>(x => x.PropertyName));
    }
</script>

This way you get autocomplete inside the lambda and you are free to rename the property without worrying about breaking your front end code.



回答4:

I prefer this answer: https://stackoverflow.com/a/31448493/9266796

Updated to today's syntax:

var displayName = property.GetCustomAttribute<DisplayAttribute>().Name;

Or this one's even easier actually: https://stackoverflow.com/a/19940277/9266796

@Html.Label(property.Name);