ASP.NET MVC2 ModelMetadataProviders: What's the difference between overriding CreateMetadata() and GetMetadataForProperty()?
I'm statring to explore the framework's extension points, starting with MetadataProviders. I've cur开发者_StackOverflow社区rently implemented populating ModelMetadata.IsRequired property using RequiredAttribute
succesfully, but I can't seem to find the difference between
overriding CreateMetadata()
or GetMetadataForProperty()
, since both options seem to work.
In general, the examples I've seen override CreateMetadata()
.
- What are the pro and cons of using either options?
- Are there any scenarios where one of these are the preferred options?
As an extra: are there any good resources (blogs, books) to learn from this extension point?
The GetMetadataForProperty()
is declared on the class ModelMetadataProvider
.
AssociatedMetadataProvider
derives from ModelMetadataProvider
. CreateMetadata()
is declared on AssociatedMetadataProvider
. The DataAnnotationsMetadataProvider
that is overridden in the link you provide is derived from AssociatedMetadataProvider
.
The MVC framework makes calls to ModelMetadataProvider
's GetMetadataForProperty()
method.
The reason overriding CreateMetadata()
is working for you is because the AssociatedModelMetadataProvider
's default implementation of GetMetadataForProperty()
makes a call to CreateMetadata()
. It looks like this:
public override ModelMetadata GetMetadataForProperty(Func<object> modelAccessor, Type containerType, string propertyName)
{
if (containerType == null)
{
throw new ArgumentNullException("containerType");
}
if (string.IsNullOrEmpty(propertyName))
{
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "propertyName");
}
PropertyDescriptor propertyDescriptor = this.GetTypeDescriptor(containerType).GetProperties().Find(propertyName, true);
if (propertyDescriptor == null)
{
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, MvcResources.Common_PropertyNotFound, new object[] { containerType.FullName, propertyName }));
}
return this.GetMetadataForProperty(modelAccessor, containerType, propertyDescriptor);
}
protected virtual ModelMetadata GetMetadataForProperty(Func<object> modelAccessor, Type containerType, PropertyDescriptor propertyDescriptor)
{
IEnumerable<Attribute> attributes = this.FilterAttributes(containerType, propertyDescriptor, propertyDescriptor.Attributes.Cast<Attribute>());
return this.CreateMetadata(attributes, containerType, modelAccessor, propertyDescriptor.PropertyType, propertyDescriptor.Name);
}
If you are subclassing the AssociatedMetadataProvider
as you are in the link you provided, then your preferred extensibility point is the CreateMetadata
method, because the AssociatedMetadataProvider.GetMetadataForProperty()
method pre-validates the contract of your CreateMetadata()
method. That way, you know that if there is an error in your CreateMetadata()
method, you already know that the source of the error is in your method and not in the arguments that were passed to it.
Also, here is the source of the FilterAttributes()
method, in case you were wondering:
protected virtual IEnumerable<Attribute> FilterAttributes(Type containerType, PropertyDescriptor propertyDescriptor, IEnumerable<Attribute> attributes)
{
if (!typeof(ViewPage).IsAssignableFrom(containerType) && !typeof(ViewUserControl).IsAssignableFrom(containerType))
{
return attributes;
}
return attributes.Where<Attribute>(delegate (Attribute a) {
return !(a is ReadOnlyAttribute);
});
}
精彩评论