Wondering why DisplayName attribute is ignored in

2020-03-08 07:56发布

问题:

today I got confused when doing a couple of <%=Html.LabelFor(m=>m.MyProperty)%> in ASP.NET MVC 2 and using the [DisplayName("Show this instead of MyProperty")] attribute from System.ComponentModel.

As it turned out, when I put the attribute on an overridden property, LabelFor didn't seem to notice it.
However, the [Required] attribute works fine on the overridden property, and the generated errormessage actually uses the DisplayNameAttribute.

This is some trivial examplecode, the more realistic scenario is that I have a databasemodel separate from the viewmodel, but for convenience, I'd like to inherit from the databasemodel, add View-only properties and decorating the viewmodel with the attributes for the UI.

public class POCOWithoutDataAnnotations
{
    public virtual string PleaseOverrideMe { get; set; }        
} 
public class EditModel : POCOWithoutDataAnnotations
{
    [Required]
    [DisplayName("This should be as label for please override me!")]
    public override string PleaseOverrideMe 
    {
        get { return base.PleaseOverrideMe; }
        set { base.PleaseOverrideMe = value; }
    }

    [Required]
    [DisplayName("This property exists only in EditModel")]
    public string NonOverriddenProp { get; set; }
}

The strongly typed ViewPage<EditModel> contains:

        <div class="editor-label">
            <%= Html.LabelFor(model => model.PleaseOverrideMe) %>
        </div>
        <div class="editor-field">
            <%= Html.TextBoxFor(model => model.PleaseOverrideMe) %>
            <%= Html.ValidationMessageFor(model => model.PleaseOverrideMe) %>
        </div>

        <div class="editor-label">
            <%= Html.LabelFor(model => model.NonOverriddenProp) %>
        </div>
        <div class="editor-field">
            <%= Html.TextBoxFor(model => model.NonOverriddenProp) %>
            <%= Html.ValidationMessageFor(model => model.NonOverriddenProp) %>
        </div>

The labels are then displayed as "PleaseOverrideMe" (not using the DisplayNameAttribute) and "This property exists only in EditModel" (using the DisplayNameAttribute) when viewing the page.
If I post with empty values, triggering the validation with this ActionMethod:

    [HttpPost]
    public ActionResult Edit(EditModel model)
    {
        if (!ModelState.IsValid)
            return View(model);
        return View("Thanks");
    }

the <%= Html.ValidationMessageFor(model => model.PleaseOverrideMe) %> actually uses [DisplayName("This should be as label for please override me!")] attribute, and produces the default errortext "The This should be as label for please override me! field is required."

Would some friendly soul shed some light on this?

回答1:

Model binding and metadata using the strongly-typed helpers looks at the declared, rather than the runtime, type of the model. I consider this a bug, but apparently the MVC team disagrees with me, as my Connect issue on this was closed as "By Design."



回答2:

I ran into this problem using [DisplayName("Profile Name")] and instead used [Display(Name = "Profile Name")] which fixed the problem in my case. I'm not sure if this would be useful.

The former is from System.ComponentModel whilst the latter is from System.ComponentModel.DataAnnotations.



回答3:

I had the same issue when I had a partial view strongly-typed to an interface. The interface defined a DisplayName and the class that implemented the interface tried to override it. The only way I found to get it to respect the override was to type to the implementing class. I had to either change the view's model type or cast. Unfortunately, that completely negates the benefits of using the interface as the model type. I am guessing that I will end up with some level of duplicated view markup for each implementing class while not casting within the strongly-typed "helpers".

In the remote chance that this type of workaround is even remotely helpful (not getting my hopes up), here is an example. There are certainly ways of working handling into this for all possible implementing classes that try to override a name, but it is definitely more hassle than it should be.

public interface IAddressModel {
    ...
    [DisplayName("Province")]
    public string Province { get; set; }
    ...
}
public class UsAddressModel : IAddressModel {
    ...
    [DisplayName("State")]
    public string Province { get; set; }
    ...
}

<%= Html.LabelFor(m => m.State) %> <!--"Province"-->
<%= Html.LabelFor(m => (m as UsAddressModel).State) %> <!--"State"-->


回答4:

Ok, I seem to have found a workaround if you don't use the Required tag with it! just use a regular expression or length attribute to determine if there is a valid entry. Hope this helps, though it's a little late.

[RegularExpression(@"^[1-9][0-9][0-9]$")]   //validates that there is at least 1 in the quantity and no more than 999
[DisplayName("Quantity:")]
public string quantity { get; set; }

Still works.



回答5:

In my case I was forgotten to make it a property by using getters and setters. Instead of

public string CompanyName;

I should have used

public string CompanyName {get;set;}