// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Reflection;
using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;
using Microsoft.Extensions.Internal;
namespace Microsoft.AspNetCore.Mvc.ModelBinding
{
///
/// A metadata representation of a model type, property or parameter.
///
[DebuggerDisplay("{DebuggerToString(),nq}")]
public abstract class ModelMetadata
{
///
/// The default value of .
///
public static readonly int DefaultOrder = 10000;
///
/// Creates a new .
///
/// The .
protected ModelMetadata(ModelMetadataIdentity identity)
{
Identity = identity;
InitializeTypeInformation();
}
///
/// Gets the container type of this metadata if it represents a property, otherwise null .
///
public Type ContainerType { get { return Identity.ContainerType; } }
///
/// Gets a value indicating the kind of metadata element represented by the current instance.
///
public ModelMetadataKind MetadataKind { get { return Identity.MetadataKind; } }
///
/// Gets the model type represented by the current instance.
///
public Type ModelType { get { return Identity.ModelType; } }
///
/// Gets the property name represented by the current instance.
///
public string PropertyName
{
get
{
return Identity.Name;
}
}
///
/// Gets the key for the current instance.
///
protected ModelMetadataIdentity Identity { get; }
///
/// Gets a collection of additional information about the model.
///
public abstract IReadOnlyDictionary AdditionalValues { get; }
///
/// Gets the collection of instances for the model's properties.
///
public abstract ModelPropertyCollection Properties { get; }
///
/// Gets the name of a model if specified explicitly using .
///
public abstract string BinderModelName { get; }
///
/// Gets the of an of a model if specified explicitly using
/// .
///
public abstract Type BinderType { get; }
///
/// Gets a binder metadata for this model.
///
public abstract BindingSource BindingSource { get; }
///
/// Gets a value indicating whether or not to convert an empty string value to null when
/// representing a model as text.
///
public abstract bool ConvertEmptyStringToNull { get; }
///
/// Gets the name of the model's datatype. Overrides in some
/// display scenarios.
///
/// null unless set manually or through additional metadata e.g. attributes.
public abstract string DataTypeName { get; }
///
/// Gets the description of the model.
///
public abstract string Description { get; }
///
/// Gets the composite format (see
/// http://msdn.microsoft.com/en-us/library/txafckwd.aspx) used to display the model.
///
public abstract string DisplayFormatString { get; }
///
/// Gets the display name of the model.
///
public abstract string DisplayName { get; }
///
/// Gets the composite format (see
/// http://msdn.microsoft.com/en-us/library/txafckwd.aspx) used to edit the model.
///
public abstract string EditFormatString { get; }
///
/// Gets the for elements of if that
/// implements .
///
///
/// for T if implements
/// . for object if
/// implements but not . null otherwise i.e. when
/// is false .
///
public abstract ModelMetadata ElementMetadata { get; }
///
/// Gets the ordered and grouped display names and values of all values in
/// .
///
///
/// An of of mappings between
/// field groups, names and values. null if is false .
///
public abstract IEnumerable> EnumGroupedDisplayNamesAndValues { get; }
///
/// Gets the names and values of all values in .
///
///
/// An of mappings between field names
/// and values. null if is false .
///
public abstract IReadOnlyDictionary EnumNamesAndValues { get; }
///
/// Gets a value indicating whether has a non-null , non-empty
/// value different from the default for the datatype.
///
public abstract bool HasNonDefaultEditFormat { get; }
///
/// Gets a value indicating whether the value should be HTML-encoded.
///
/// If true , value should be HTML-encoded. Default is true .
public abstract bool HtmlEncode { get; }
///
/// Gets a value indicating whether the "HiddenInput" display template should return
/// string.Empty (not the expression value) and whether the "HiddenInput" editor template should not
/// also return the expression value (together with the hidden <input> element).
///
///
/// If true , also causes the default display and editor templates to return HTML
/// lacking the usual per-property <div> wrapper around the associated property. Thus the default
/// display template effectively skips the property and the default
/// editor template returns only the hidden <input> element for the property.
///
public abstract bool HideSurroundingHtml { get; }
///
/// Gets a value indicating whether or not the model value can be bound by model binding. This is only
/// applicable when the current instance represents a property.
///
///
/// If true then the model value is considered supported by model binding and can be set
/// based on provided input in the request.
///
public abstract bool IsBindingAllowed { get; }
///
/// Gets a value indicating whether or not the model value is required by model binding. This is only
/// applicable when the current instance represents a property.
///
///
/// If true then the model value is considered required by model binding and must have a value
/// supplied in the request to be considered valid.
///
public abstract bool IsBindingRequired { get; }
///
/// Gets a value indicating whether is for an .
///
///
/// true if type.IsEnum (type.GetTypeInfo().IsEnum for DNX Core 5.0) is true for
/// ; false otherwise.
///
public abstract bool IsEnum { get; }
///
/// Gets a value indicating whether is for an with an
/// associated .
///
///
/// true if is true and has an
/// associated ; false otherwise.
///
public abstract bool IsFlagsEnum { get; }
///
/// Gets a value indicating whether or not the model value is read-only. This is only applicable when
/// the current instance represents a property.
///
public abstract bool IsReadOnly { get; }
///
/// Gets a value indicating whether or not the model value is required. This is only applicable when
/// the current instance represents a property.
///
///
///
/// If true then the model value is considered required by validators.
///
///
/// By default an implicit System.ComponentModel.DataAnnotations.RequiredAttribute will be added
/// if not present when true. .
///
///
public abstract bool IsRequired { get; }
///
/// Gets the instance.
///
public abstract IModelBindingMessageProvider ModelBindingMessageProvider { get; }
///
/// Gets a value indicating where the current metadata should be ordered relative to other properties
/// in its containing type.
///
///
/// For example this property is used to order items in .
/// The default order is 10000 .
///
/// The order value of the current metadata.
public abstract int Order { get; }
///
/// Gets the text to display as a placeholder value for an editor.
///
public abstract string Placeholder { get; }
///
/// Gets the text to display when the model is null .
///
public abstract string NullDisplayText { get; }
///
/// Gets the , which can determine which properties
/// should be model bound.
///
public abstract IPropertyBindingPredicateProvider PropertyBindingPredicateProvider { get; }
///
/// Gets a value that indicates whether the property should be displayed in read-only views.
///
public abstract bool ShowForDisplay { get; }
///
/// Gets a value that indicates whether the property should be displayed in editable views.
///
public abstract bool ShowForEdit { get; }
///
/// Gets a value which is the name of the property used to display the model.
///
public abstract string SimpleDisplayProperty { get; }
///
/// Gets a string used by the templating system to discover display-templates and editor-templates.
///
public abstract string TemplateHint { get; }
///
/// Gets a value that indicates whether properties or elements of the model should be validated.
///
public abstract bool ValidateChildren { get; }
///
/// Gets a collection of metadata items for validators.
///
public abstract IReadOnlyList ValidatorMetadata { get; }
///
/// Gets the for elements of if that
/// implements .
///
public Type ElementType { get; private set; }
///
/// Gets a value indicating whether is a simple type.
///
///
/// A simple type is defined as a which has a
/// that can convert from .
///
public bool IsComplexType { get; private set; }
///
/// Gets a value indicating whether or not is a .
///
public bool IsNullableValueType { get; private set; }
///
/// Gets a value indicating whether or not is a collection type.
///
///
/// A collection type is defined as a which is assignable to .
///
public bool IsCollectionType { get; private set; }
///
/// Gets a value indicating whether or not is an enumerable type.
///
///
/// An enumerable type is defined as a which is assignable to
/// , and is not a .
///
public bool IsEnumerableType { get; private set; }
///
/// Gets a value indicating whether or not allows null values.
///
public bool IsReferenceOrNullableType { get; private set; }
///
/// Gets the underlying type argument if inherits from .
/// Otherwise gets .
///
///
/// Identical to unless is true .
///
public Type UnderlyingOrModelType { get; private set; }
///
/// Gets a property getter delegate to get the property value from a model object.
///
public abstract Func PropertyGetter { get; }
///
/// Gets a property setter delegate to set the property value on a model object.
///
public abstract Action PropertySetter { get; }
///
/// Gets a display name for the model.
///
///
/// will return the first of the following expressions which has a
/// non-null value: DisplayName , PropertyName , ModelType.Name .
///
/// The display name.
public string GetDisplayName()
{
return DisplayName ?? PropertyName ?? ModelType.Name;
}
private void InitializeTypeInformation()
{
Debug.Assert(ModelType != null);
IsComplexType = !TypeDescriptor.GetConverter(ModelType).CanConvertFrom(typeof(string));
IsNullableValueType = Nullable.GetUnderlyingType(ModelType) != null;
IsReferenceOrNullableType = !ModelType.GetTypeInfo().IsValueType || IsNullableValueType;
UnderlyingOrModelType = Nullable.GetUnderlyingType(ModelType) ?? ModelType;
var collectionType = ClosedGenericMatcher.ExtractGenericInterface(ModelType, typeof(ICollection<>));
IsCollectionType = collectionType != null;
if (ModelType == typeof(string) || !typeof(IEnumerable).IsAssignableFrom(ModelType))
{
// Do nothing, not Enumerable.
}
else if (ModelType.IsArray)
{
IsEnumerableType = true;
ElementType = ModelType.GetElementType();
}
else
{
IsEnumerableType = true;
var enumerableType = ClosedGenericMatcher.ExtractGenericInterface(ModelType, typeof(IEnumerable<>));
ElementType = enumerableType?.GenericTypeArguments[0];
if (ElementType == null && typeof(IEnumerable).IsAssignableFrom(ModelType))
{
// ModelType implements IEnumerable but not IEnumerable.
ElementType = typeof(object);
}
Debug.Assert(
ElementType != null,
$"Unable to find element type for '{ModelType.FullName}' though IsEnumerableType is true.");
}
}
private string DebuggerToString()
{
if (Identity.MetadataKind == ModelMetadataKind.Type)
{
return $"ModelMetadata (Type: '{ModelType.Name}')";
}
else
{
return $"ModelMetadata (Property: '{ContainerType.Name}.{PropertyName}' Type: '{ModelType.Name}')";
}
}
}
}