using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using Microsoft.AspNet.Mvc.ModelBinding.Internal; namespace Microsoft.AspNet.Mvc.ModelBinding { public class ModelMetadata { public static readonly int DefaultOrder = 10000; private readonly Type _containerType; private readonly Type _modelType; private readonly string _propertyName; private EfficientTypePropertyKey _cacheKey; private bool _convertEmptyStringToNull = true; private object _model; private Func _modelAccessor; private int _order = DefaultOrder; private IEnumerable _properties; private Type _realModelType; public ModelMetadata([NotNull] IModelMetadataProvider provider, Type containerType, Func modelAccessor, [NotNull] Type modelType, string propertyName) { Provider = provider; _containerType = containerType; _modelAccessor = modelAccessor; _modelType = modelType; _propertyName = propertyName; } public Type ContainerType { get { return _containerType; } } public virtual bool ConvertEmptyStringToNull { get { return _convertEmptyStringToNull; } set { _convertEmptyStringToNull = value; } } public virtual string DataTypeName { get; set; } public virtual string Description { get; set; } public virtual string DisplayFormatString { get; set; } public virtual string EditFormatString { get; set; } public virtual bool IsComplexType { get { return !ModelType.HasStringConverter(); } } public bool IsNullableValueType { get { return ModelType.IsNullableValueType(); } } public virtual bool IsReadOnly { get; set; } public virtual int Order { get { return _order; } set { _order = value; } } public object Model { get { if (_modelAccessor != null) { _model = _modelAccessor(); _modelAccessor = null; } return _model; } set { _model = value; _modelAccessor = null; _properties = null; _realModelType = null; } } public Type ModelType { get { return _modelType; } } public virtual string NullDisplayText { get; set; } public virtual IEnumerable Properties { get { if (_properties == null) { _properties = Provider.GetMetadataForProperties(Model, RealModelType); } return _properties; } } public string PropertyName { get { return _propertyName; } } protected IModelMetadataProvider Provider { get; set; } /// /// Gets TModel if ModelType is Nullable{TModel}, ModelType otherwise. /// public Type RealModelType { get { if (_realModelType == null) { _realModelType = ModelType; // Don't call GetType() if the model is Nullable, because it will // turn Nullable into T for non-null values if (Model != null && !ModelType.IsNullableValueType()) { _realModelType = Model.GetType(); } } return _realModelType; } } public virtual string TemplateHint { get; set; } internal EfficientTypePropertyKey CacheKey { get { if (_cacheKey == null) { _cacheKey = CreateCacheKey(ContainerType, ModelType, PropertyName); } return _cacheKey; } set { _cacheKey = value; } } [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "The method is a delegating helper to choose among multiple property values")] public virtual string GetDisplayName() { return PropertyName ?? ModelType.Name; } // TODO: Revive ModelValidators //public virtual IEnumerable GetValidators(IEnumerable validatorProviders) //{ // if (validatorProviders == null) // { // throw Error.ArgumentNull("validatorProviders"); // } // return validatorProviders.SelectMany(provider => provider.GetValidators(this, validatorProviders)); //} private static EfficientTypePropertyKey CreateCacheKey(Type containerType, Type modelType, string propertyName) { // If metadata is for a property then containerType != null && propertyName != null // If metadata is for a type then containerType == null && propertyName == null, so we have to use modelType for the cache key. return new EfficientTypePropertyKey(containerType ?? modelType, propertyName); } } }