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