private DefaultMetadataDetails CreateSinglePropertyDetails( ModelMetadataIdentity propertyKey, PropertyHelper propertyHelper) { Debug.Assert(propertyKey.MetadataKind == ModelMetadataKind.Property); var containerType = propertyKey.ContainerType; var attributes = ModelAttributes.GetAttributesForProperty( containerType, propertyHelper.Property, propertyKey.ModelType); var propertyEntry = new DefaultMetadataDetails(propertyKey, attributes); if (propertyHelper.Property.CanRead && propertyHelper.Property.GetMethod?.IsPublic == true) { var getter = PropertyHelper.MakeNullSafeFastPropertyGetter(propertyHelper.Property); propertyEntry.PropertyGetter = getter; } if (propertyHelper.Property.CanWrite && propertyHelper.Property.SetMethod?.IsPublic == true && !containerType.IsValueType) { propertyEntry.PropertySetter = propertyHelper.ValueSetter; } return(propertyEntry); }
public void GetAttributesForTestProperty_ModelOverridesMetadataAttributes() { // Arrange var modelType = typeof(BaseViewModel); var property = modelType.GetRuntimeProperties().FirstOrDefault(p => p.Name == nameof(BaseModel.TestProperty)); // Act var attributes = ModelAttributes.GetAttributesForProperty(modelType, property); // Assert var rangeAttributes = attributes.Attributes.OfType <RangeAttribute>().ToArray(); Assert.NotNull(rangeAttributes[0]); Assert.Equal(0, (int)rangeAttributes[0].Minimum); Assert.Equal(10, (int)rangeAttributes[0].Maximum); Assert.NotNull(rangeAttributes[1]); Assert.Equal(10, (int)rangeAttributes[1].Minimum); Assert.Equal(100, (int)rangeAttributes[1].Maximum); Assert.Single(attributes.Attributes.OfType <FromHeaderAttribute>()); rangeAttributes = attributes.PropertyAttributes.OfType <RangeAttribute>().ToArray(); Assert.NotNull(rangeAttributes[0]); Assert.Equal(0, (int)rangeAttributes[0].Minimum); Assert.Equal(10, (int)rangeAttributes[0].Maximum); Assert.NotNull(rangeAttributes[1]); Assert.Equal(10, (int)rangeAttributes[1].Minimum); Assert.Equal(100, (int)rangeAttributes[1].Maximum); Assert.Single(attributes.PropertyAttributes.OfType <FromHeaderAttribute>()); }
private static DefaultMetadataDetails CreateMetadataDetails(ParameterSpecification parameterSpecification, ActionExecutionModel containerModel) { if (parameterSpecification == null) { throw new ArgumentNullException(nameof(parameterSpecification)); } if (containerModel == null) { throw new ArgumentNullException(nameof(containerModel)); } #pragma warning disable CS0618 // Type or member is obsolete var parameterMetadataIdentity = ModelMetadataIdentity.ForProperty(parameterSpecification.Type, parameterSpecification.Key, containerModel.GetType()); #pragma warning restore CS0618 // Type or member is obsolete var parameterAttributes = ModelAttributes.GetAttributesForProperty(containerModel.GetType(), nullPropertyInfo, parameterSpecification.Type); var propertyMetadataDetails = new DefaultMetadataDetails( parameterMetadataIdentity, parameterAttributes) { PropertyGetter = thisModel => GetParameterValue((ActionExecutionModel)thisModel, parameterSpecification), PropertySetter = (thisModel, value) => SetParameterValue((ActionExecutionModel)thisModel, parameterSpecification, value), ValidationMetadata = new ValidationMetadata { IsRequired = parameterSpecification.IsRequired }, DisplayMetadata = new DisplayMetadata() }; var dataTypeAttribute = parameterSpecification.ValidationAttributes.OfType <DataTypeAttribute>().FirstOrDefault(); if (dataTypeAttribute != null) { propertyMetadataDetails.DisplayMetadata.DataTypeName = dataTypeAttribute.GetDataTypeName(); } foreach (var attribute in parameterSpecification.ValidationAttributes) { propertyMetadataDetails.ValidationMetadata.ValidatorMetadata.Add(attribute); } foreach (var attribute in parameterSpecification.Type.GetCustomAttributes <ValidationAttribute>(true)) { propertyMetadataDetails.ValidationMetadata.ValidatorMetadata.Add(attribute); } if (propertyMetadataDetails.ValidationMetadata.ValidatorMetadata.Count > 0 || typeof(IValidatableObject).IsAssignableFrom(parameterSpecification.Type)) { propertyMetadataDetails.ValidationMetadata.HasValidators = true; } return(propertyMetadataDetails); }
public void GetAttributesForBasePropertyFromDerivedModel_IncludesMetadataAttributes() { // Arrange var modelType = typeof(DerivedViewModel); var property = modelType.GetRuntimeProperties().FirstOrDefault(p => p.Name == nameof(BaseModel.BaseProperty)); // Act var attributes = ModelAttributes.GetAttributesForProperty(modelType, property); // Assert Assert.Single(attributes.Attributes.OfType <RequiredAttribute>()); Assert.Single(attributes.Attributes.OfType <StringLengthAttribute>()); Assert.Single(attributes.PropertyAttributes.OfType <RequiredAttribute>()); Assert.Single(attributes.PropertyAttributes.OfType <StringLengthAttribute>()); }
public void GetFromServiceAttributeFromBase_IncludesMetadataAttributes() { // Arrange var modelType = typeof(DerivedViewModel); var property = modelType.GetRuntimeProperties().FirstOrDefault(p => p.Name == nameof(BaseModel.RouteValue)); // Act var attributes = ModelAttributes.GetAttributesForProperty(modelType, property); // Assert Assert.Single(attributes.Attributes.OfType <RequiredAttribute>()); Assert.Single(attributes.Attributes.OfType <FromRouteAttribute>()); Assert.Single(attributes.PropertyAttributes.OfType <RequiredAttribute>()); Assert.Single(attributes.PropertyAttributes.OfType <FromRouteAttribute>()); }
private DefaultMetadataDetails CreateSinglePropertyDetails(ModelMetadataIdentity propertyKey, PropertyInfo property) { var containerType = propertyKey.ContainerType; var attributes = ModelAttributes.GetAttributesForProperty(containerType, property, propertyKey.ModelType); var propertyEntry = new DefaultMetadataDetails(propertyKey, attributes); if (property.CanRead && property.GetMethod?.IsPublic == true) { propertyEntry.PropertyGetter = property.MakeFastGetter <object, object>(); } if (property.CanWrite && property.SetMethod?.IsPublic == true && !containerType.IsValueType) { propertyEntry.PropertySetter = property.MakeFastSetter <object, object>(); } return(propertyEntry); }
public void GetAttributesForProperty_WithModelType_IncludesTypeAttributes() { // Arrange var property = typeof(MergedAttributes) .GetProperty(nameof(MergedAttributes.BaseModel)); // Act var attributes = ModelAttributes.GetAttributesForProperty(typeof(MergedAttributes), property, typeof(DerivedModelWithAttributes)); // Assert Assert.Collection( attributes.Attributes, attribute => Assert.IsType <BindRequiredAttribute>(attribute), attribute => Assert.IsType <ModelBinderAttribute>(attribute), attribute => Assert.IsType <ClassValidator>(attribute)); Assert.IsType <BindRequiredAttribute>(Assert.Single(attributes.PropertyAttributes)); Assert.Null(attributes.ParameterAttributes); Assert.Collection( attributes.TypeAttributes, attribute => Assert.IsType <ModelBinderAttribute>(attribute), attribute => Assert.IsType <ClassValidator>(attribute)); }
public void GetAttributesForProperty_MergedAttributes() { // Arrange var property = typeof(MergedAttributes).GetRuntimeProperty(nameof(MergedAttributes.Property)); // Act var attributes = ModelAttributes.GetAttributesForProperty(typeof(MergedAttributes), property); // Assert Assert.Equal(3, attributes.Attributes.Count); Assert.IsType <RequiredAttribute>(attributes.Attributes[0]); Assert.IsType <RangeAttribute>(attributes.Attributes[1]); Assert.IsType <ClassValidator>(attributes.Attributes[2]); Assert.Equal(2, attributes.PropertyAttributes.Count); Assert.IsType <RequiredAttribute>(attributes.PropertyAttributes[0]); Assert.IsType <RangeAttribute>(attributes.PropertyAttributes[1]); var attribute = Assert.Single(attributes.TypeAttributes); Assert.IsType <ClassValidator>(attribute); }
/// <summary> /// Creates the <see cref="DefaultMetadataDetails"/> entries for the properties of a model /// <see cref="Type"/>. /// </summary> /// <param name="key"> /// The <see cref="ModelMetadataIdentity"/> identifying the model <see cref="Type"/>. /// </param> /// <returns>A details object for each property of the model <see cref="Type"/>.</returns> /// <remarks> /// The results of this method will be cached and used to satisfy calls to /// <see cref="GetMetadataForProperties(Type)"/>. Override this method to provide a different /// set of property data. /// </remarks> protected virtual DefaultMetadataDetails[] CreatePropertyDetails(ModelMetadataIdentity key) { var propertyHelpers = PropertyHelper.GetVisibleProperties(key.ModelType); var propertyEntries = new List <DefaultMetadataDetails>(propertyHelpers.Length); for (var i = 0; i < propertyHelpers.Length; i++) { var propertyHelper = propertyHelpers[i]; var propertyKey = ModelMetadataIdentity.ForProperty( propertyHelper.Property.PropertyType, propertyHelper.Name, key.ModelType); var attributes = ModelAttributes.GetAttributesForProperty( key.ModelType, propertyHelper.Property); var propertyEntry = new DefaultMetadataDetails(propertyKey, attributes); if (propertyHelper.Property.CanRead && propertyHelper.Property.GetMethod?.IsPublic == true) { var getter = PropertyHelper.MakeNullSafeFastPropertyGetter(propertyHelper.Property); propertyEntry.PropertyGetter = getter; } if (propertyHelper.Property.CanWrite && propertyHelper.Property.SetMethod?.IsPublic == true && !key.ModelType.GetTypeInfo().IsValueType) { propertyEntry.PropertySetter = propertyHelper.ValueSetter; } propertyEntries.Add(propertyEntry); } return(propertyEntries.ToArray()); }
/// <summary> /// Creates the <see cref="DefaultMetadataDetailsCache"/> entries for the properties of a model /// <see cref="Type"/>. /// </summary> /// <param name="key"> /// The <see cref="ModelMetadataIdentity"/> identifying the model <see cref="Type"/>. /// </param> /// <returns>A cache object for each property of the model <see cref="Type"/>.</returns> /// <remarks> /// The results of this method will be cached and used to satisfy calls to /// <see cref="GetMetadataForProperties(Type)"/>. Override this method to provide a different /// set of property data. /// </remarks> protected virtual DefaultMetadataDetailsCache[] CreatePropertyCacheEntries([NotNull] ModelMetadataIdentity key) { var propertyHelpers = PropertyHelper.GetProperties(key.ModelType); var propertyEntries = new List <DefaultMetadataDetailsCache>(propertyHelpers.Length); for (var i = 0; i < propertyHelpers.Length; i++) { var propertyHelper = propertyHelpers[i]; if (propertyHelper.Property.DeclaringType != key.ModelType) { // If this property was declared on a base type then look for the definition closest to the // the model type to see if we should include it. var ignoreProperty = false; // Walk up the hierarchy until we find the type that actally declares this // PropertyInfo. var currentType = key.ModelType.GetTypeInfo(); while (currentType != propertyHelper.Property.DeclaringType.GetTypeInfo()) { // We've found a 'more proximal' public definition var declaredProperty = currentType.GetDeclaredProperty(propertyHelper.Name); if (declaredProperty != null) { ignoreProperty = true; break; } currentType = currentType.BaseType.GetTypeInfo(); } if (ignoreProperty) { // There's a better definition, ignore this. continue; } } var propertyKey = ModelMetadataIdentity.ForProperty( propertyHelper.Property.PropertyType, propertyHelper.Name, key.ModelType); var attributes = new List <object>(ModelAttributes.GetAttributesForProperty( key.ModelType, propertyHelper.Property)); var propertyEntry = new DefaultMetadataDetailsCache(propertyKey, attributes); if (propertyHelper.Property.CanRead && propertyHelper.Property.GetMethod?.IsPrivate == true) { propertyEntry.PropertyAccessor = PropertyHelper.MakeFastPropertyGetter(propertyHelper.Property); } if (propertyHelper.Property.CanWrite && propertyHelper.Property.SetMethod?.IsPrivate == true) { propertyEntry.PropertySetter = PropertyHelper.MakeFastPropertySetter(propertyHelper.Property); } propertyEntries.Add(propertyEntry); } return(propertyEntries.ToArray()); }