I have a model like below:
public class CreateStockcheckJobModel
{
    [Engineer(true)]
    public EngineerModel Engineer { get; set; }
}
I'm rendering the Engineer property in a View<CreateStockcheckJobModel> using Html.EditorFor(m => m.Engineer, "EngineerEditor").
How do I access the value in the Engineer attribute (in this case true) from within the code in my partial view (EngineerEditor.ascx)?
Below is my editor code
<%@ Control Language="C#" Inherits="ViewUserControl<EngineerModel>" %>
<% if (PropertyImRenderingHasAttributeWithTrueBooleanValue) // What goes here?
   { %>
<p>Render one thing</p>
<% }
   else
   { %>
<p>Render another thing</p>
<% } %>
I'm aware of reflection, however i'm unsure how to use it as the attribute isn't added to the EngineerModel class it's added to the Engineer property of the CreateStockcheckJobModel class. If i could get the PropertyInfo that I'm rendering from the editor code then I'd be sorted, but I don't know how to get that information. If I go down the route of enumerate all properties in the CreateStockcheckJobModel class then I'm going to get issues if I have more than one EngineerModel property (one might have the attribute with True, another might have False).
This could be done easily in ASP.NET MVC 3 and later by implementing the IMetadataAware interface on your custom EngineerAttribute:
public class EngineerAttribute : Attribute, IMetadataAware
{
    public EngineerAttribute(bool isFoo)
    {
        IsFoo = isFoo;
    }
    public bool IsFoo { get; private set; }
    public void OnMetadataCreated(ModelMetadata metadata)
    {
        metadata.AdditionalValues["IsFoo"] = IsFoo;
    }
}
and then inside the template:
<%@ Control Language="C#" Inherits="ViewUserControl<EngineerModel>" %>
<%
    var isFoo = (bool)ViewData.ModelMetadata.AdditionalValues["IsFoo"];
%>
<% if (isFoo) { %>
    <p>Render one thing</p>
<% } else { %>
    <p>Render another thing</p>
<% } %>
Unfortunately this interface doesn't exist in ASP.NET MVC 2. To achieve the same functionality you could write a custom metadata provider:
public class MyMetadataProvider : DataAnnotationsModelMetadataProvider
{
    protected override ModelMetadata CreateMetadata(
        IEnumerable<Attribute> attributes, 
        Type containerType, 
        Func<object> modelAccessor, 
        Type modelType, 
        string propertyName
    )
    {
        var metadata = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);
        var engineer = attributes.OfType<EngineerAttribute>().FirstOrDefault();
        if (engineer != null)
        {
            metadata.AdditionalValues["IsFoo"] = engineer.IsFoo;
        }
        return metadata;
    }
}
that you will register in your Application_Start in order to replace the default one:
ModelMetadataProviders.Current = new MyMetadataProvider();
And now you could access this metadata in your template the same way as I showed earlier, using ViewData.ModelMetadata.AdditionalValues["IsFoo"]. Obviously you could put an arbitrarily complex object inside the AdditionalValues property, not just booleans.
Also you might find the following article useful about metadata.
NOTE: Below is a possible way to do this but using IMetadataAware as described in the accepted answer is a far better way. It's usually better to avoid reflection if you can.
You can only do this via reflection. Here's a good example on how to do this for method attributes.
Code example for getting value of EngineerAttribute on property Engineer:
PropertyInfo pi = Model.GetType().GetProperty("Engineer");
EngineerAttribute a =
    System.Attribute.GetCustomAttribute(pi, typeof(EngineerAttribute));
Code example for getting all properties that have the attribute [Engineer(true)]:
var t = Model.GetType();
var engineerProperties =
    from p in t.GetProperties()
    let ca = System.Attribute.GetCustomAttribute(p, typeof(EngineerAttribute))
    where ca != null && ca.BooleanProperty == true
    select p;
And when you have these properties, you can use an overload of EditorFor that allows you to specify additional view data, that you can then use in your subview:
Html.EditorFor(m => m.Engineer, new { IsEngineer = isEngineer });
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With