Add `ModelMetadata.HasNonDefaultEditFormat` property

- add `DataType` property to `CachedDataAnnotationsMetadataAttributes` in support of `ComputeHasNonDefaultEditFormat()`
- provide doc comments and flesh out the implementation of `DisplayFormatString` and `EditFormatString`

nits:
- add comment about _not_ overriding `ComputeIsComplexType()` in `CachedDataAnnotationsModelMetadata`
- restore alphabetic order in `CachedDataAnnotationsMetadataAttributes`
- seal `SimpleDisplayText` in `CachedModelMetadata<TPrototypeCache>`, matching MVC 5.2
This commit is contained in:
dougbu 2014-08-15 18:57:05 -07:00
parent a3ab95713e
commit 4351ddd092
4 changed files with 242 additions and 4 deletions

View File

@ -12,21 +12,44 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
public CachedDataAnnotationsMetadataAttributes(IEnumerable<Attribute> attributes)
{
DataType = attributes.OfType<DataTypeAttribute>().FirstOrDefault();
Display = attributes.OfType<DisplayAttribute>().FirstOrDefault();
DisplayFormat = attributes.OfType<DisplayFormatAttribute>().FirstOrDefault();
DisplayColumn = attributes.OfType<DisplayColumnAttribute>().FirstOrDefault();
DisplayFormat = attributes.OfType<DisplayFormatAttribute>().FirstOrDefault();
Editable = attributes.OfType<EditableAttribute>().FirstOrDefault();
HiddenInput = attributes.OfType<HiddenInputAttribute>().FirstOrDefault();
Required = attributes.OfType<RequiredAttribute>().FirstOrDefault();
ScaffoldColumn = attributes.OfType<ScaffoldColumnAttribute>().FirstOrDefault();
// Special case the [DisplayFormat] attribute hanging off an applied [DataType] attribute. This property is
// non-null for DataType.Currency, DataType.Date, DataType.Time, and potentially custom [DataType]
// subclasses. The DataType.Currency, DataType.Date, and DataType.Time [DisplayFormat] attributes have a
// non-null DataFormatString and the DataType.Date and DataType.Time [DisplayFormat] attributes have
// ApplyFormatInEditMode==true.
if (DisplayFormat == null && DataType != null)
{
DisplayFormat = DataType.DisplayFormat;
}
}
/// <summary>
/// Gets (or sets in subclasses) <see cref="DataTypeAttribute"/> found in collection passed to the
/// <see cref="CachedDataAnnotationsMetadataAttributes(IEnumerable{Attribute})"/> constructor, if any.
/// </summary>
public DataTypeAttribute DataType { get; protected set; }
public DisplayAttribute Display { get; protected set; }
public DisplayFormatAttribute DisplayFormat { get; protected set; }
public DisplayColumnAttribute DisplayColumn { get; protected set; }
/// <summary>
/// Gets (or sets in subclasses) <see cref="DisplayFormatAttribute"/> found in collection passed to the
/// <see cref="CachedDataAnnotationsMetadataAttributes(IEnumerable{Attribute})"/> constructor, if any.
/// If no such attribute was found but a <see cref="DataTypeAttribute"/> was, gets the
/// <see cref="DataTypeAttribute.DisplayFormat"/> value.
/// </summary>
public DisplayFormatAttribute DisplayFormat { get; protected set; }
public EditableAttribute Editable { get; protected set; }
/// <summary>

View File

@ -8,8 +8,12 @@ using System.Reflection;
namespace Microsoft.AspNet.Mvc.ModelBinding
{
// Class does not override ComputeIsComplexType() because value calculated in ModelMetadata's base implementation
// is correct.
public class CachedDataAnnotationsModelMetadata : CachedModelMetadata<CachedDataAnnotationsMetadataAttributes>
{
private bool _isEditFormatStringFromCache;
public CachedDataAnnotationsModelMetadata(CachedDataAnnotationsModelMetadata prototype,
Func<object> modelAccessor)
: base(prototype, modelAccessor)
@ -50,6 +54,22 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
: base.ComputeDescription();
}
/// <summary>
/// Calculate <see cref="ModelMetadata.DisplayFormatString"/> based on presence of an
/// <see cref="DisplayFormatAttribute"/> and its <see cref="DisplayFormatAttribute.DataFormatString"/> value.
/// </summary>
/// <returns>
/// Calculated <see cref="ModelMetadata.DisplayFormatString"/> value.
/// <see cref="DisplayFormatAttribute.DataFormatString"/> if an <see cref="DisplayFormatAttribute"/> exists.
/// <c>null</c> otherwise.
/// </returns>
protected override string ComputeDisplayFormatString()
{
return PrototypeCache.DisplayFormat != null
? PrototypeCache.DisplayFormat.DataFormatString
: base.ComputeEditFormatString();
}
protected override string ComputeDisplayName()
{
// DisplayName may be provided by DisplayAttribute.
@ -67,6 +87,73 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
return base.ComputeDisplayName();
}
/// <summary>
/// Calculate <see cref="ModelMetadata.EditFormatString"/> based on presence of an
/// <see cref="DisplayFormatAttribute"/> and its <see cref="DisplayFormatAttribute.ApplyFormatInEditMode"/> and
/// <see cref="DisplayFormatAttribute.DataFormatString"/> values.
/// </summary>
/// <returns>
/// Calculated <see cref="ModelMetadata.DisplayFormatString"/> value.
/// <see cref="DisplayFormatAttribute.DataFormatString"/> if an <see cref="DisplayFormatAttribute"/> exists and
/// its <see cref="DisplayFormatAttribute.ApplyFormatInEditMode"/> is <c>true</c>; <c>null</c> otherwise.
/// </returns>
/// <remarks>
/// Subclasses overriding this method should also override <see cref="ComputeHasNonDefaultEditFormat"/> to
/// ensure the two calculations remain consistent.
/// </remarks>
protected override string ComputeEditFormatString()
{
if (PrototypeCache.DisplayFormat != null && PrototypeCache.DisplayFormat.ApplyFormatInEditMode)
{
_isEditFormatStringFromCache = true;
return PrototypeCache.DisplayFormat.DataFormatString;
}
return base.ComputeEditFormatString();
}
/// <summary>
/// Calculate <see cref="ModelMetadata.HasNonDefaultEditFormat"/> based on
/// <see cref="ModelMetadata.EditFormatString"/> and presence of <see cref="DataTypeAttribute"/> and
/// <see cref="DisplayFormatAttribute"/>.
/// </summary>
/// <returns>
/// Calculated <see cref="ModelMetadata.HasNonDefaultEditFormat"/> value. <c>true</c> if
/// <see cref="ModelMetadata.EditFormatString"/> is non-<c>null</c>, non-empty, and came from the cache (was
/// not set directly). In addition the applied <see cref="DisplayFormatAttribute"/> must not have come from an
/// applied <see cref="DataTypeAttribute"/>. <c>false</c> otherwise.
/// </returns>
protected override bool ComputeHasNonDefaultEditFormat()
{
// Following calculation ignores possibility something (an IModelMetadataProvider) set EditFormatString
// directly.
if (!string.IsNullOrEmpty(EditFormatString) && _isEditFormatStringFromCache)
{
// Have a non-empty EditFormatString based on [DisplayFormat] from our cache.
if (PrototypeCache.DataType == null)
{
// Attributes include no [DataType]; [DisplayFormat] was applied directly.
return true;
}
if (PrototypeCache.DataType.DisplayFormat != PrototypeCache.DisplayFormat)
{
// Attributes include separate [DataType] and [DisplayFormat]; [DisplayFormat] provided override.
return true;
}
if (PrototypeCache.DataType.GetType() != typeof(DataTypeAttribute))
{
// Attributes include [DisplayFormat] copied from [DataType] and [DataType] was of a subclass.
// Assume the [DataType] constructor used the protected DisplayFormat setter to override its
// default. That is derived [DataType] provided override.
return true;
}
}
return base.ComputeHasNonDefaultEditFormat();
}
/// <summary>
/// Calculate <see cref="ModelMetadata.HideSurroundingHtml"/> based on presence of an
/// <see cref="HiddenInputAttribute"/> and its <see cref="HiddenInputAttribute.DisplayValue"/> value.

View File

@ -18,7 +18,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
private bool _convertEmptyStringToNull;
private string _nullDisplayText;
private string _description;
private string _displayFormatString;
private string _displayName;
private string _editFormatString;
private bool _hasNonDefaultEditFormat;
private bool _hideSurroundingHtml;
private bool _isReadOnly;
private bool _isComplexType;
@ -29,7 +32,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
private bool _convertEmptyStringToNullComputed;
private bool _nullDisplayTextComputed;
private bool _descriptionComputed;
private bool _displayFormatStringComputed;
private bool _displayNameComputed;
private bool _editFormatStringComputed;
private bool _hasNonDefaultEditFormatComputed;
private bool _hideSurroundingHtmlComputed;
private bool _isReadOnlyComputed;
private bool _isComplexTypeComputed;
@ -117,6 +123,27 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
}
}
/// <inheritdoc />
public sealed override string DisplayFormatString
{
get
{
if (!_displayFormatStringComputed)
{
_displayFormatString = ComputeDisplayFormatString();
_displayFormatStringComputed = true;
}
return _displayFormatString;
}
set
{
_displayFormatString = value;
_displayFormatStringComputed = true;
}
}
public sealed override string DisplayName
{
get
@ -136,6 +163,48 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
}
}
/// <inheritdoc />
public sealed override string EditFormatString
{
get
{
if (!_editFormatStringComputed)
{
_editFormatString = ComputeEditFormatString();
_editFormatStringComputed = true;
}
return _editFormatString;
}
set
{
_editFormatString = value;
_editFormatStringComputed = true;
}
}
/// <inheritdoc />
public sealed override bool HasNonDefaultEditFormat
{
get
{
if (!_hasNonDefaultEditFormatComputed)
{
_hasNonDefaultEditFormat = ComputeHasNonDefaultEditFormat();
_hasNonDefaultEditFormatComputed = true;
}
return _hasNonDefaultEditFormat;
}
set
{
_hasNonDefaultEditFormat = value;
_hasNonDefaultEditFormatComputed = true;
}
}
/// <inheritdoc />
public sealed override bool HideSurroundingHtml
{
@ -242,6 +311,21 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
}
}
/// <inheritdoc />
public sealed override string SimpleDisplayText
{
get
{
// Value already cached in ModelMetadata. That class also already exposes ComputeSimpleDisplayText()
// for overrides. Sealed here for consistency with other properties.
return base.SimpleDisplayText;
}
set
{
base.SimpleDisplayText = value;
}
}
protected TPrototypeCache PrototypeCache { get; set; }
protected virtual bool ComputeConvertEmptyStringToNull()
@ -259,11 +343,38 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
return base.Description;
}
/// <summary>
/// Calculate the <see cref="DisplayFormatString"/> value.
/// </summary>
/// <returns>Calculated <see cref="DisplayFormatString"/> value.</returns>
protected virtual string ComputeDisplayFormatString()
{
return base.DisplayFormatString;
}
protected virtual string ComputeDisplayName()
{
return base.DisplayName;
}
/// <summary>
/// Calculate the <see cref="EditFormatString"/> value.
/// </summary>
/// <returns>Calculated <see cref="EditFormatString"/> value.</returns>
protected virtual string ComputeEditFormatString()
{
return base.EditFormatString;
}
/// <summary>
/// Calculate the <see cref="HasNonDefaultEditFormat"/> value.
/// </summary>
/// <returns>Calculated <see cref="HasNonDefaultEditFormat"/> value.</returns>
protected virtual bool ComputeHasNonDefaultEditFormat()
{
return base.HasNonDefaultEditFormat;
}
/// <summary>
/// Calculate the <see cref="HideSurroundingHtml"/> value.
/// </summary>

View File

@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Linq;
using Microsoft.AspNet.Mvc.ModelBinding.Internal;
@ -64,12 +63,30 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public virtual string Description { get; set; }
/// <summary>
/// Gets or sets the composite format <see cref="string"/> (see
/// http://msdn.microsoft.com/en-us/library/txafckwd.aspx) used to display the <see cref="Model"/>.
/// </summary>
public virtual string DisplayFormatString { get; set; }
public virtual string DisplayName { get; set; }
/// <summary>
/// Gets or sets the composite format <see cref="string"/> (see
/// http://msdn.microsoft.com/en-us/library/txafckwd.aspx) used to edit the <see cref="Model"/>.
/// </summary>
/// <remarks>
/// <see cref="IModelMetadataProvider"/> instances that set this property to a non-<c>null</c>, non-empty,
/// non-default value should also set <see cref="HasNonDefaultEditFormat"/> to <c>true</c>.
/// </remarks>
public virtual string EditFormatString { get; set; }
/// <summary>
/// Gets or sets a value indicating whether <see cref="EditFormatString"/> has a non-<c>null</c>, non-empty
/// value different from the default for the datatype.
/// </summary>
public virtual bool HasNonDefaultEditFormat { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the "HiddenInput" display template should return
/// <c>string.Empty</c> (not the expression value) and whether the "HiddenInput" editor template should not