1 namespace System.Web.Mvc {
3 using System.Collections.Generic;
4 using System.ComponentModel;
5 using System.ComponentModel.DataAnnotations;
6 using System.Globalization;
8 using System.Web.Mvc.Resources;
10 // This class provides a good implementation of ModelMetadataProvider for people who will be
11 // using traditional classes with properties. It uses the buddy class support from
12 // DataAnnotations, and consolidates the three operations down to a single override
13 // for reading the attribute values and creating the metadata class.
14 public abstract class AssociatedMetadataProvider : ModelMetadataProvider {
15 private static void ApplyMetadataAwareAttributes(IEnumerable<Attribute> attributes, ModelMetadata result) {
16 foreach (IMetadataAware awareAttribute in attributes.OfType<IMetadataAware>()) {
17 awareAttribute.OnMetadataCreated(result);
21 protected abstract ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName);
23 protected virtual IEnumerable<Attribute> FilterAttributes(Type containerType, PropertyDescriptor propertyDescriptor, IEnumerable<Attribute> attributes) {
24 if (typeof(ViewPage).IsAssignableFrom(containerType) || typeof(ViewUserControl).IsAssignableFrom(containerType)) {
25 return attributes.Where(a => !(a is ReadOnlyAttribute));
31 public override IEnumerable<ModelMetadata> GetMetadataForProperties(object container, Type containerType) {
32 if (containerType == null) {
33 throw new ArgumentNullException("containerType");
36 return GetMetadataForPropertiesImpl(container, containerType);
39 private IEnumerable<ModelMetadata> GetMetadataForPropertiesImpl(object container, Type containerType) {
40 foreach (PropertyDescriptor property in GetTypeDescriptor(containerType).GetProperties()) {
41 Func<object> modelAccessor = container == null ? null : GetPropertyValueAccessor(container, property);
42 yield return GetMetadataForProperty(modelAccessor, containerType, property);
46 public override ModelMetadata GetMetadataForProperty(Func<object> modelAccessor, Type containerType, string propertyName) {
47 if (containerType == null) {
48 throw new ArgumentNullException("containerType");
50 if (String.IsNullOrEmpty(propertyName)) {
51 throw new ArgumentException(MvcResources.Common_NullOrEmpty, "propertyName");
54 ICustomTypeDescriptor typeDescriptor = GetTypeDescriptor(containerType);
55 PropertyDescriptor property = typeDescriptor.GetProperties().Find(propertyName, true);
56 if (property == null) {
57 throw new ArgumentException(
59 CultureInfo.CurrentCulture,
60 MvcResources.Common_PropertyNotFound,
61 containerType.FullName, propertyName));
64 return GetMetadataForProperty(modelAccessor, containerType, property);
67 protected virtual ModelMetadata GetMetadataForProperty(Func<object> modelAccessor, Type containerType, PropertyDescriptor propertyDescriptor) {
68 IEnumerable<Attribute> attributes = FilterAttributes(containerType, propertyDescriptor, propertyDescriptor.Attributes.Cast<Attribute>());
69 ModelMetadata result = CreateMetadata(attributes, containerType, modelAccessor, propertyDescriptor.PropertyType, propertyDescriptor.Name);
70 ApplyMetadataAwareAttributes(attributes, result);
74 public override ModelMetadata GetMetadataForType(Func<object> modelAccessor, Type modelType) {
75 if (modelType == null) {
76 throw new ArgumentNullException("modelType");
79 IEnumerable<Attribute> attributes = GetTypeDescriptor(modelType).GetAttributes().Cast<Attribute>();
80 ModelMetadata result = CreateMetadata(attributes, null /* containerType */, modelAccessor, modelType, null /* propertyName */);
81 ApplyMetadataAwareAttributes(attributes, result);
85 private static Func<object> GetPropertyValueAccessor(object container, PropertyDescriptor property) {
86 return () => property.GetValue(container);
89 protected virtual ICustomTypeDescriptor GetTypeDescriptor(Type type) {
90 return TypeDescriptorHelper.Get(type);