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);
        }
Esempio n. 2
0
    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>());
    }
Esempio n. 3
0
        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);
        }
Esempio n. 4
0
    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>());
    }
Esempio n. 5
0
    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>());
    }
Esempio n. 6
0
        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);
        }
Esempio n. 7
0
    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));
    }
Esempio n. 8
0
    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);
    }
Esempio n. 9
0
        /// <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());
        }
Esempio n. 10
0
        /// <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());
        }