Refactor of the model metadata provider

Separates the MMP into two phases:
1). Creation of the ModelMetadata, discovery of properties and attributes
(reflection) is part of the MMP
2). Lookup of details based on attributes is now part of another phase,
and has its results cached.

Users can now implements and register an IFooMetadataProvider to customize
a single aspect of metadata (see how data annotations does it).
This commit is contained in:
Ryan Nowak 2015-02-27 15:09:44 -08:00
parent ccb9f70545
commit 90cef3b9ca
110 changed files with 5074 additions and 4348 deletions

View File

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc.Core;
using Microsoft.AspNet.Mvc.ModelBinding;
@ -43,18 +44,17 @@ namespace Microsoft.AspNet.Mvc
nameof(actionContext));
}
var methodParameters = actionDescriptor.MethodInfo.GetParameters();
var parameterMetadata = new List<ModelMetadata>();
foreach (var parameter in actionDescriptor.Parameters)
{
var parameterInfo = methodParameters.Where(p => p.Name == parameter.Name).Single();
var metadata = _modelMetadataProvider.GetMetadataForParameter(
methodInfo: actionDescriptor.MethodInfo,
parameterName: parameter.Name);
parameterInfo,
attributes: new object[] { parameter.BinderMetadata });
if (metadata != null)
{
UpdateParameterMetadata(metadata, parameter.BinderMetadata);
parameterMetadata.Add(metadata);
}
parameterMetadata.Add(metadata);
}
var actionArguments = new Dictionary<string, object>(StringComparer.Ordinal);
@ -62,20 +62,6 @@ namespace Microsoft.AspNet.Mvc
return actionArguments;
}
private void UpdateParameterMetadata(ModelMetadata metadata, IBinderMetadata binderMetadata)
{
if (binderMetadata != null)
{
metadata.BinderMetadata = binderMetadata;
}
var nameProvider = binderMetadata as IModelNameProvider;
if (nameProvider != null && nameProvider.Name != null)
{
metadata.BinderModelName = nameProvider.Name;
}
}
private async Task PopulateArgumentAsync(
ActionContext actionContext,
ActionBindingContext bindingContext,

View File

@ -433,21 +433,14 @@ namespace Microsoft.AspNet.Mvc.Description
public void WalkParameter()
{
var parameterInfo =
Context.ActionDescriptor.MethodInfo.GetParameters()
.Where(p => p.Name == Parameter.Name)
.Single();
var modelMetadata = Context.MetadataProvider.GetMetadataForParameter(
methodInfo: Context.ActionDescriptor.MethodInfo,
parameterName: Parameter.Name);
var binderMetadata = Parameter.BinderMetadata;
if (binderMetadata != null)
{
modelMetadata.BinderMetadata = binderMetadata;
}
var nameProvider = binderMetadata as IModelNameProvider;
if (nameProvider != null && nameProvider.Name != null)
{
modelMetadata.BinderModelName = nameProvider.Name;
}
parameterInfo,
attributes: new object[] { Parameter.BinderMetadata });
// Attempt to find a binding source for the parameter
//
@ -482,7 +475,7 @@ namespace Microsoft.AspNet.Mvc.Description
/// </remarks>
private bool Visit(ModelMetadata modelMetadata, BindingSource ambientSource, string containerName)
{
var source = BindingSource.GetBindingSource(modelMetadata.BinderMetadata);
var source = modelMetadata.BindingSource;
if (source != null && source.IsGreedy)
{
// We have a definite answer for this model. This is a greedy source like

View File

@ -7,7 +7,7 @@ using Microsoft.AspNet.Mvc.ApplicationModels;
using Microsoft.AspNet.Mvc.Core;
using Microsoft.AspNet.Mvc.OptionDescriptors;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.Net.Http.Headers;
using Microsoft.AspNet.Mvc.ModelBinding.Metadata;
namespace Microsoft.AspNet.Mvc
{
@ -30,6 +30,7 @@ namespace Microsoft.AspNet.Mvc
Filters = new List<IFilter>();
FormatterMappings = new FormatterMappings();
ValidationExcludeFilters = new List<ExcludeValidationDescriptor>();
ModelMetadataDetailsProviders = new List<IMetadataDetailsProvider>();
ModelValidatorProviders = new List<ModelValidatorProviderDescriptor>();
CacheProfiles = new Dictionary<string, CacheProfile>(StringComparer.OrdinalIgnoreCase);
}
@ -146,5 +147,20 @@ namespace Microsoft.AspNet.Mvc
/// <see cref="ResponseCacheFilter"/>.
/// </summary>
public IDictionary<string, CacheProfile> CacheProfiles { get; }
/// <summary>
/// Gets a list of <see cref="IMetadataDetailsProvider"/> instances that will be used to
/// create <see cref="ModelMetadata"/> instances.
/// </summary>
/// <remarks>
/// A provider should implement one or more of the following interfaces, depending on what
/// kind of details are provided:
/// <ul>
/// <li><see cref="IBindingMetadataProvider"/></li>
/// <li><see cref="IDisplayMetadataProvider"/></li>
/// <li><see cref="IValidationMetadataProvider"/></li>
/// </ul>
/// </remarks>
public IList<IMetadataDetailsProvider> ModelMetadataDetailsProviders { get; }
}
}

View File

@ -1707,7 +1707,15 @@ namespace Microsoft.AspNet.Mvc.Core
}
/// <summary>
/// The model type '{0}' does not match the '{1}' type parameter.
/// The model's runtime type '{0}' is not assignable to the type '{1}'.
/// </summary>
internal static string ModelType_WrongType
{
get { return GetString("ModelType_WrongType"); }
}
/// <summary>
/// The model's runtime type '{0}' is not assignable to the type '{1}'.
/// </summary>
internal static string FormatModelType_WrongType(object p0, object p1)
{

View File

@ -204,11 +204,5 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
return !(s1 == s2);
}
// THIS IS TEMP CODE, this will be moved to model metadata
public static BindingSource GetBindingSource(IBinderMetadata metadata)
{
return (metadata as IBindingSourceMetadata)?.BindingSource;
}
}
}

View File

@ -64,9 +64,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
/// <inheritdoc />
public async Task<ModelBindingResult> BindModelAsync(ModelBindingContext context)
{
var bindingSourceMetadata = context.ModelMetadata.BinderMetadata as IBindingSourceMetadata;
var allowedBindingSource = bindingSourceMetadata?.BindingSource;
var allowedBindingSource = context.ModelMetadata.BindingSource;
if (allowedBindingSource == null || !allowedBindingSource.CanAcceptDataFrom(BindingSource))
{
// Binding Sources are opt-in. This model either didn't specify one or specified something

View File

@ -139,7 +139,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// public IActionResult UpdatePerson([FromForm] Person person) { }
//
// In this example, [FromQuery] overrides the ambient data source (form).
var bindingSource = BindingSource.GetBindingSource(oldBindingContext.ModelMetadata.BinderMetadata);
var bindingSource = oldBindingContext.ModelMetadata.BindingSource;
if (bindingSource != null && !bindingSource.IsGreedy)
{
var valueProvider =
@ -155,7 +155,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
private static BodyBindingState GetBodyBindingState(ModelBindingContext oldBindingContext)
{
var bindingSource = BindingSource.GetBindingSource(oldBindingContext.ModelMetadata.BinderMetadata);
var bindingSource = oldBindingContext.ModelMetadata.BindingSource;
var willReadBodyWithFormatter = bindingSource == BindingSource.Body;
var willReadBodyAsFormData = bindingSource == BindingSource.Form;

View File

@ -67,7 +67,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
//
// We skip this check if it is a top level object because we want to always evaluate
// the creation of top level object (this is also required for ModelBinderAttribute to work.)
var bindingSource = BindingSource.GetBindingSource(bindingContext.ModelMetadata.BinderMetadata);
var bindingSource = bindingContext.ModelMetadata.BindingSource;
if (!isTopLevelObject &&
bindingSource != null &&
bindingSource.IsGreedy)
@ -139,7 +139,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
foreach (var propertyMetadata in context.PropertyMetadata)
{
// This check will skip properties which are marked explicitly using a non value binder.
var bindingSource = BindingSource.GetBindingSource(propertyMetadata.BinderMetadata);
var bindingSource = propertyMetadata.BindingSource;
if (bindingSource == null || !bindingSource.IsGreedy)
{
isAnyPropertyEnabledForValueProviderBasedBinding = true;
@ -168,7 +168,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
var valueProvider = bindingContext.ValueProvider;
var bindingSource = BindingSource.GetBindingSource(metadata.BinderMetadata);
var bindingSource = metadata.BindingSource;
if (bindingSource != null && !bindingSource.IsGreedy)
{
var rootValueProvider = bindingContext.OperationBindingContext.ValueProvider as IBindingSourceValueProvider;

View File

@ -0,0 +1,14 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Mvc.ModelBinding.Metadata;
namespace Microsoft.AspNet.Mvc.ModelBinding
{
public class EmptyModelMetadataProvider : DefaultModelMetadataProvider
{
public EmptyModelMetadataProvider()
: base(new DefaultCompositeMetadataDetailsProvider(new IMetadataDetailsProvider[0]))
{
}
}
}

View File

@ -1,4 +1,4 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
@ -10,10 +10,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
public interface IModelMetadataProvider
{
IEnumerable<ModelMetadata> GetMetadataForProperties([NotNull] Type containerType);
ModelMetadata GetMetadataForType([NotNull] Type modelType);
ModelMetadata GetMetadataForParameter([NotNull] MethodInfo methodInfo, [NotNull] string parameterName);
IEnumerable<ModelMetadata> GetMetadataForProperties([NotNull] Type modelType);
ModelMetadata GetMetadataForParameter([NotNull] ParameterInfo parameter, IEnumerable<object> attributes);
}
}
}

View File

@ -1,241 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ModelBinding
{
public abstract class AssociatedMetadataProvider<TModelMetadata> : IModelMetadataProvider
where TModelMetadata : ModelMetadata
{
private readonly ConcurrentDictionary<Type, TModelMetadata> _typeInfoCache =
new ConcurrentDictionary<Type, TModelMetadata>();
private readonly ConcurrentDictionary<Type, Dictionary<string, PropertyInformation>> _typePropertyInfoCache =
new ConcurrentDictionary<Type, Dictionary<string, PropertyInformation>>();
public IEnumerable<ModelMetadata> GetMetadataForProperties([NotNull] Type containerType)
{
return GetMetadataForPropertiesCore(containerType);
}
public ModelMetadata GetMetadataForProperty([NotNull] Type containerType,
[NotNull] string propertyName)
{
if (string.IsNullOrEmpty(propertyName))
{
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(propertyName));
}
var typePropertyInfo = GetTypePropertyInformation(containerType);
PropertyInformation propertyInfo;
if (!typePropertyInfo.TryGetValue(propertyName, out propertyInfo))
{
var message = Resources.FormatCommon_PropertyNotFound(containerType, propertyName);
throw new ArgumentException(message, nameof(propertyName));
}
return CreatePropertyMetadata(propertyInfo);
}
public ModelMetadata GetMetadataForType([NotNull] Type modelType)
{
var prototype = GetTypeInformation(modelType);
return CreateMetadataFromPrototype(prototype);
}
public ModelMetadata GetMetadataForParameter(
[NotNull] MethodInfo methodInfo,
[NotNull] string parameterName)
{
if (string.IsNullOrEmpty(parameterName))
{
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(parameterName));
}
var parameter = methodInfo.GetParameters().FirstOrDefault(
param => StringComparer.Ordinal.Equals(param.Name, parameterName));
if (parameter == null)
{
var message = Resources.FormatCommon_ParameterNotFound(parameterName);
throw new ArgumentException(message, nameof(parameterName));
}
return GetMetadataForParameterCore(parameterName, parameter);
}
// Override for creating the prototype metadata (without the model accessor).
/// <summary>
/// Creates a new <typeparamref name="TModelMetadata"/> instance.
/// </summary>
/// <param name="attributes">The set of attributes relevant for the new instance.</param>
/// <param name="containerType">
/// <see cref="Type"/> containing this property. <c>null</c> unless this <typeparamref name="TModelMetadata"/>
/// describes a property.
/// </param>
/// <param name="modelType"><see cref="Type"/> this <typeparamref name="TModelMetadata"/> describes.</param>
/// <param name="propertyName">
/// Name of the property (in <paramref name="containerType"/>) or parameter this
/// <typeparamref name="TModelMetadata"/> describes. <c>null</c> or empty if this
/// <typeparamref name="TModelMetadata"/> describes a <see cref="Type"/>.
/// </param>
/// <returns>A new <typeparamref name="TModelMetadata"/> instance.</returns>
protected abstract TModelMetadata CreateMetadataPrototype(IEnumerable<object> attributes,
Type containerType,
Type modelType,
string propertyName);
// Override for applying the prototype + model accessor to yield the final metadata.
/// <summary>
/// Creates a new <typeparamref name="TModelMetadata"/> instance based on a <paramref name="prototype"/>.
/// </summary>
/// <param name="prototype">
/// <typeparamref name="TModelMetadata"/> that provides the basis for new instance.
/// </param>
/// <param name="modelAccessor">Accessor for model value of new instance.</param>
/// <returns>
/// A new <typeparamref name="TModelMetadata"/> instance based on <paramref name="prototype"/>.
/// </returns>
protected abstract TModelMetadata CreateMetadataFromPrototype(TModelMetadata prototype);
private ModelMetadata GetMetadataForParameterCore(
string parameterName,
ParameterInfo parameter)
{
var parameterInfo =
CreateParameterInfo(parameter.ParameterType,
ModelAttributes.GetAttributesForParameter(parameter),
parameterName);
var metadata = CreateMetadataFromPrototype(parameterInfo.Prototype);
return metadata;
}
private IEnumerable<ModelMetadata> GetMetadataForPropertiesCore(Type containerType)
{
var typePropertyInfo = GetTypePropertyInformation(containerType);
foreach (var kvp in typePropertyInfo)
{
var propertyInfo = kvp.Value;
var propertyMetadata = CreatePropertyMetadata(propertyInfo);
yield return propertyMetadata;
}
}
private TModelMetadata CreatePropertyMetadata(PropertyInformation propertyInfo)
{
var metadata = CreateMetadataFromPrototype(propertyInfo.Prototype);
if (propertyInfo.IsReadOnly)
{
metadata.IsReadOnly = true;
}
return metadata;
}
private TModelMetadata GetTypeInformation(Type type, IEnumerable<Attribute> associatedAttributes = null)
{
// This retrieval is implemented as a TryGetValue/TryAdd instead of a GetOrAdd
// to avoid the performance cost of creating instance delegates
TModelMetadata typeInfo;
if (!_typeInfoCache.TryGetValue(type, out typeInfo))
{
typeInfo = CreateTypeInformation(type, associatedAttributes);
_typeInfoCache.TryAdd(type, typeInfo);
}
return typeInfo;
}
private Dictionary<string, PropertyInformation> GetTypePropertyInformation(Type type)
{
// This retrieval is implemented as a TryGetValue/TryAdd instead of a GetOrAdd
// to avoid the performance cost of creating instance delegates
Dictionary<string, PropertyInformation> typePropertyInfo;
if (!_typePropertyInfoCache.TryGetValue(type, out typePropertyInfo))
{
typePropertyInfo = GetPropertiesLookup(type);
_typePropertyInfoCache.TryAdd(type, typePropertyInfo);
}
return typePropertyInfo;
}
private TModelMetadata CreateTypeInformation(Type type, IEnumerable<Attribute> associatedAttributes)
{
var attributes = ModelAttributes.GetAttributesForType(type);
if (associatedAttributes != null)
{
attributes = attributes.Concat(associatedAttributes);
}
return CreateMetadataPrototype(attributes, containerType: null, modelType: type, propertyName: null);
}
private PropertyInformation CreatePropertyInformation(Type containerType, PropertyHelper helper)
{
var property = helper.Property;
var attributes = ModelAttributes.GetAttributesForProperty(containerType, property);
return new PropertyInformation
{
PropertyHelper = helper,
Prototype = CreateMetadataPrototype(attributes,
containerType,
property.PropertyType,
property.Name),
IsReadOnly = !property.CanWrite || property.SetMethod.IsPrivate
};
}
private Dictionary<string, PropertyInformation> GetPropertiesLookup(Type containerType)
{
var properties = new Dictionary<string, PropertyInformation>(StringComparer.Ordinal);
foreach (var propertyHelper in PropertyHelper.GetProperties(containerType))
{
// Avoid re-generating a property descriptor if one has already been generated for the property name
if (!properties.ContainsKey(propertyHelper.Name))
{
properties.Add(propertyHelper.Name, CreatePropertyInformation(containerType, propertyHelper));
}
}
return properties;
}
private ParameterInformation CreateParameterInfo(
Type parameterType,
IEnumerable<object> attributes,
string parameterName)
{
var metadataProtoType = CreateMetadataPrototype(attributes: attributes,
containerType: null,
modelType: parameterType,
propertyName: parameterName);
return new ParameterInformation
{
Prototype = metadataProtoType
};
}
private sealed class ParameterInformation
{
public TModelMetadata Prototype { get; set; }
}
private sealed class PropertyInformation
{
public PropertyHelper PropertyHelper { get; set; }
public TModelMetadata Prototype { get; set; }
public bool IsReadOnly { get; set; }
}
}
}

View File

@ -0,0 +1,53 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
{
/// <summary>
/// Binding metadata details for a <see cref="ModelMetadata"/>.
/// </summary>
public class BindingMetadata
{
/// <summary>
/// Gets or sets the <see cref="ModelBinding.BindingSource"/>.
/// See <see cref="ModelMetadata.BindingSource"/>.
/// </summary>
public BindingSource BindingSource { get; set; }
/// <summary>
/// Gets or sets the binder model name. If <c>null</c> the property or parameter name will be used.
/// See <see cref="ModelMetadata.BinderModelName"/>.
/// </summary>
public string BinderModelName { get; set; }
/// <summary>
/// Gets or sets the <see cref="Type"/> of the model binder used to bind the model.
/// See <see cref="ModelMetadata.BinderType"/>.
/// </summary>
public Type BinderType { get; set; }
/// <summary>
/// Gets or sets a value indicating whether or not the model is read-only. Will be ignored
/// if the model metadata being created is not a property. If <c>null</c> then
/// <see cref="ModelMetadata.IsReadOnly"/> will be computed based on the accessibility
/// of the property accessor and model <see cref="Type"/>. See <see cref="ModelMetadata.IsReadOnly"/>.
/// </summary>
public bool? IsReadOnly { get; set; }
/// <summary>
/// Gets or sets a value indicating whether or not the model is a required value. Will be ignored
/// if the model metadata being created is not a property. If <c>null</c> then
/// <see cref="ModelMetadata.IsRequired"/> will be computed based on the model <see cref="Type"/>.
/// See <see cref="ModelMetadata.IsRequired"/>.
/// </summary>
public bool? IsRequired { get; set; }
/// <summary>
/// Gets or sets the <see cref="ModelBinding.IPropertyBindingPredicateProvider"/>.
/// See <see cref="ModelMetadata.PropertyBindingPredicateProvider"/>.
/// </summary>
public IPropertyBindingPredicateProvider PropertyBindingPredicateProvider { get; set; }
}
}

View File

@ -0,0 +1,43 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
{
/// <summary>
/// A context for an <see cref="IBindingMetadataProvider"/>.
/// </summary>
public class BindingMetadataProviderContext
{
/// <summary>
/// Creates a new <see cref="BindingMetadataProviderContext"/>.
/// </summary>
/// <param name="key">The <see cref="ModelMetadataIdentity"/> for the <see cref="ModelMetadata"/>.</param>
/// <param name="attributes">The attributes for the <see cref="ModelMetadata"/>.</param>
public BindingMetadataProviderContext(
[NotNull] ModelMetadataIdentity key,
[NotNull] IReadOnlyList<object> attributes)
{
Key = key;
Attributes = attributes;
BindingMetadata = new BindingMetadata();
}
/// <summary>
/// Gets the attributes.
/// </summary>
public IReadOnlyList<object> Attributes { get; }
/// <summary>
/// Gets the <see cref="ModelMetadataIdentity"/>.
/// </summary>
public ModelMetadataIdentity Key { get; }
/// <summary>
/// Gets the <see cref="Metadata.BindingMetadata"/>.
/// </summary>
public BindingMetadata BindingMetadata { get; }
}
}

View File

@ -1,102 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
namespace Microsoft.AspNet.Mvc.ModelBinding
{
public class CachedDataAnnotationsMetadataAttributes
{
public CachedDataAnnotationsMetadataAttributes(IEnumerable<object> attributes)
{
DataType = attributes.OfType<DataTypeAttribute>().FirstOrDefault();
Display = attributes.OfType<DisplayAttribute>().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();
UIHint = attributes.OfType<UIHintAttribute>().FirstOrDefault();
BinderMetadata = attributes.OfType<IBinderMetadata>().FirstOrDefault();
PropertyBindingPredicateProviders = attributes.OfType<IPropertyBindingPredicateProvider>();
BinderModelNameProvider = attributes.OfType<IModelNameProvider>().FirstOrDefault();
BinderTypeProviders = attributes.OfType<IBinderTypeProviderMetadata>();
// 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="IEnumerable{IBinderTypeProviderMetadata}"/> found in collection
/// passed to the <see cref="CachedDataAnnotationsMetadataAttributes(IEnumerable{object})"/> constructor,
/// if any.
/// </summary>
public IEnumerable<IBinderTypeProviderMetadata> BinderTypeProviders { get; set; }
/// <summary>
/// Gets (or sets in subclasses) <see cref="IBinderMetadata"/> found in collection passed to the
/// <see cref="CachedDataAnnotationsMetadataAttributes(IEnumerable{object})"/> constructor, if any.
/// </summary>
public IBinderMetadata BinderMetadata { get; protected set; }
/// <summary>
/// Gets (or sets in subclasses) <see cref="IModelNameProvider"/> found in collection passed to the
/// <see cref="CachedDataAnnotationsMetadataAttributes(IEnumerable{object})"/> constructor, if any.
/// </summary>
public IModelNameProvider BinderModelNameProvider { get; protected set; }
/// <summary>
/// Gets (or sets in subclasses) <see cref="DataTypeAttribute"/> found in collection passed to the
/// <see cref="CachedDataAnnotationsMetadataAttributes(IEnumerable{object})"/> constructor, if any.
/// </summary>
public DataTypeAttribute DataType { get; protected set; }
public DisplayAttribute Display { 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{object})"/> 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>
/// Gets (or sets in subclasses) <see cref="HiddenInputAttribute"/> found in collection passed to the
/// <see cref="CachedDataAnnotationsMetadataAttributes(IEnumerable{object})"/> constructor, if any.
/// </summary>
public HiddenInputAttribute HiddenInput { get; protected set; }
/// <summary>
/// Gets (or sets in subclasses) <see cref="IEnumerable{IPropertyBindingPredicateProvider}"/> found in
/// collection passed to the <see cref="CachedDataAnnotationsMetadataAttributes(IEnumerable{object})"/>
/// constructor, if any.
/// </summary>
public IEnumerable<IPropertyBindingPredicateProvider> PropertyBindingPredicateProviders { get; protected set; }
public RequiredAttribute Required { get; protected set; }
public ScaffoldColumnAttribute ScaffoldColumn { get; protected set; }
/// <summary>
/// Gets (or sets in subclasses) <see cref="UIHintAttribute"/> found in collection passed to the
/// <see cref="CachedDataAnnotationsMetadataAttributes(IEnumerable{object})"/> constructor, if any.
/// </summary>
public UIHintAttribute UIHint { get; protected set; }
}
}

View File

@ -1,412 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
namespace Microsoft.AspNet.Mvc.ModelBinding
{
// Class does not override ComputeIsCollectionType() or ComputeIsComplexType() because values calculated in
// ModelMetadata's base implementation are correct. No data annotations override those calculations.
public class CachedDataAnnotationsModelMetadata : CachedModelMetadata<CachedDataAnnotationsMetadataAttributes>
{
private static readonly string HtmlName = DataType.Html.ToString();
private bool _isEditFormatStringFromCache;
public CachedDataAnnotationsModelMetadata(CachedDataAnnotationsModelMetadata prototype)
: base(prototype)
{
}
public CachedDataAnnotationsModelMetadata(DataAnnotationsModelMetadataProvider provider,
Type containerType,
Type modelType,
string propertyName,
IEnumerable<object> attributes)
: base(provider,
containerType,
modelType,
propertyName,
new CachedDataAnnotationsMetadataAttributes(attributes))
{
}
protected override Type ComputeBinderType()
{
if (PrototypeCache.BinderTypeProviders != null)
{
// We want to respect the value set by the parameter (if any), and use the value specifed
// on the type as a fallback.
//
// We generalize this process, in case someone adds ordered providers (with count > 2) through
// extensibility.
foreach (var provider in PrototypeCache.BinderTypeProviders)
{
if (provider.BinderType != null)
{
return provider.BinderType;
}
}
}
return base.ComputeBinderType();
}
protected override IBinderMetadata ComputeBinderMetadata()
{
return PrototypeCache.BinderMetadata != null
? PrototypeCache.BinderMetadata
: base.ComputeBinderMetadata();
}
protected override string ComputeBinderModelNamePrefix()
{
return PrototypeCache.BinderModelNameProvider != null
? PrototypeCache.BinderModelNameProvider.Name
: base.ComputeBinderModelNamePrefix();
}
protected override IPropertyBindingPredicateProvider ComputePropertyBindingPredicateProvider()
{
return PrototypeCache.PropertyBindingPredicateProviders.Any()
? new CompositePredicateProvider(PrototypeCache.PropertyBindingPredicateProviders.ToArray())
: null;
}
protected override bool ComputeConvertEmptyStringToNull()
{
return PrototypeCache.DisplayFormat != null
? PrototypeCache.DisplayFormat.ConvertEmptyStringToNull
: base.ComputeConvertEmptyStringToNull();
}
/// <summary>
/// Calculate <see cref="ModelMetadata.DataTypeName"/> based on presence of a <see cref="DataTypeAttribute"/>
/// and its <see cref="DataTypeAttribute.GetDataTypeName()"/> method.
/// </summary>
/// <returns>
/// Calculated <see cref="ModelMetadata.DataTypeName"/> value.
/// <see cref="DataTypeAttribute.GetDataTypeName()"/> value if a <see cref="DataTypeAttribute"/> exists.
/// <c>"Html"</c> if a <see cref="DisplayFormatAttribute"/> exists with its
/// <see cref="DisplayFormatAttribute.HtmlEncode"/> value <c>false</c>. <c>null</c> otherwise.
/// </returns>
protected override string ComputeDataTypeName()
{
if (PrototypeCache.DataType != null)
{
return PrototypeCache.DataType.GetDataTypeName();
}
if (PrototypeCache.DisplayFormat != null && !PrototypeCache.DisplayFormat.HtmlEncode)
{
return HtmlName;
}
return base.ComputeDataTypeName();
}
protected override string ComputeDescription()
{
return PrototypeCache.Display != null
? PrototypeCache.Display.GetDescription()
: base.ComputeDescription();
}
/// <summary>
/// Calculate <see cref="ModelMetadata.DisplayFormatString"/> based on presence of a
/// <see cref="DisplayFormatAttribute"/> and its <see cref="DisplayFormatAttribute.DataFormatString"/> value.
/// </summary>
/// <returns>
/// Calculated <see cref="ModelMetadata.DisplayFormatString"/> value.
/// <see cref="DisplayFormatAttribute.DataFormatString"/> if a <see cref="DisplayFormatAttribute"/> exists.
/// <c>null</c> otherwise.
/// </returns>
protected override string ComputeDisplayFormatString()
{
return PrototypeCache.DisplayFormat != null
? PrototypeCache.DisplayFormat.DataFormatString
: base.ComputeDisplayFormatString();
}
protected override string ComputeDisplayName()
{
// DisplayName may be provided by DisplayAttribute.
// If that does not supply a name, then we fall back to the property name (in base.GetDisplayName()).
if (PrototypeCache.Display != null)
{
// DisplayAttribute doesn't require you to set a name, so this could be null.
var name = PrototypeCache.Display.GetName();
if (name != null)
{
return name;
}
}
return base.ComputeDisplayName();
}
/// <summary>
/// Calculate <see cref="ModelMetadata.EditFormatString"/> based on presence of a
/// <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 a <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.
/// </summary>
/// <returns>Calculated <see cref="ModelMetadata.HideSurroundingHtml"/> value. <c>true</c> if an
/// <see cref="HiddenInputAttribute"/> exists and its <see cref="HiddenInputAttribute.DisplayValue"/> value is
/// <c>false</c>; <c>false</c> otherwise.</returns>
protected override bool ComputeHideSurroundingHtml()
{
if (PrototypeCache.HiddenInput != null)
{
return !PrototypeCache.HiddenInput.DisplayValue;
}
return base.ComputeHideSurroundingHtml();
}
/// <summary>
/// Calculate <see cref="ModelMetadata.HtmlEncode"/> based on presence of a
/// <see cref="DisplayFormatAttribute"/> and its <see cref="DisplayFormatAttribute.HtmlEncode"/> value.
/// </summary>
/// <returns>
/// Calculated <see cref="ModelMetadata.HtmlEncode"/> value. <c>false</c> if a
/// <see cref="DisplayFormatAttribute"/> exists and its <see cref="DisplayFormatAttribute.HtmlEncode"/> value
/// is <c>false</c>. <c>true</c> otherwise.
/// </returns>
protected override bool ComputeHtmlEncode()
{
if (PrototypeCache.DisplayFormat != null)
{
return PrototypeCache.DisplayFormat.HtmlEncode;
}
return base.ComputeHtmlEncode();
}
protected override bool ComputeIsReadOnly()
{
if (PrototypeCache.Editable != null)
{
return !PrototypeCache.Editable.AllowEdit;
}
return base.ComputeIsReadOnly();
}
protected override bool ComputeIsRequired()
{
return (PrototypeCache.Required != null) || base.ComputeIsRequired();
}
/// <summary>
/// Calculate the <see cref="ModelMetadata.NullDisplayText"/> value based on the presence of a
/// <see cref="DisplayFormatAttribute"/> and its <see cref="DisplayFormatAttribute.NullDisplayText"/> value.
/// </summary>
/// <returns>
/// Calculated <see cref="ModelMetadata.NullDisplayText"/> value.
/// <see cref="DisplayFormatAttribute.NullDisplayText"/> if a <see cref="DisplayFormatAttribute"/> exists;
/// <c>null</c> otherwise.
/// </returns>
protected override string ComputeNullDisplayText()
{
return PrototypeCache.DisplayFormat != null
? PrototypeCache.DisplayFormat.NullDisplayText
: base.ComputeNullDisplayText();
}
/// <summary>
/// Calculate the <see cref="ModelMetadata.Order"/> value based on presence of a <see cref="DisplayAttribute"/>
/// and its <see cref="DisplayAttribute.Order"/> value.
/// </summary>
/// <returns>
/// Calculated <see cref="ModelMetadata.Order"/> value. <see cref="DisplayAttribute.GetOrder"/> if a
/// <see cref="DisplayAttribute"/> exists and its <see cref="DisplayAttribute.Order"/> has been set;
/// <c>10000</c> otherwise.
/// </returns>
protected override int ComputeOrder()
{
var result = PrototypeCache.Display?.GetOrder();
return result ?? base.ComputeOrder();
}
/// <inheritdoc />
protected override string ComputeSimpleDisplayProperty()
{
if (!string.IsNullOrEmpty(PrototypeCache.DisplayColumn?.DisplayColumn))
{
var displayColumnProperty = ModelType.GetTypeInfo().GetDeclaredProperty(
PrototypeCache.DisplayColumn.DisplayColumn);
ValidateDisplayColumnAttribute(PrototypeCache.DisplayColumn, displayColumnProperty, ModelType);
return displayColumnProperty.Name;
}
return base.ComputeSimpleDisplayProperty();
}
protected override bool ComputeShowForDisplay()
{
return PrototypeCache.ScaffoldColumn != null
? PrototypeCache.ScaffoldColumn.Scaffold
: base.ComputeShowForDisplay();
}
protected override bool ComputeShowForEdit()
{
return PrototypeCache.ScaffoldColumn != null
? PrototypeCache.ScaffoldColumn.Scaffold
: base.ComputeShowForEdit();
}
/// <summary>
/// Calculate the <see cref="ModelMetadata.TemplateHint"/> value based on presence of a
/// <see cref="UIHintAttribute"/> and its <see cref="UIHintAttribute.UIHint"/> value or presence of a
/// <see cref="HiddenInputAttribute"/> when no <see cref="UIHintAttribute"/> exists.
/// </summary>
/// <returns>
/// Calculated <see cref="ModelMetadata.TemplateHint"/> value. <see cref="UIHintAttribute.UIHint"/> if a
/// <see cref="UIHintAttribute"/> exists. <c>"HiddenInput"</c> if a <see cref="HiddenInputAttribute"/> exists
/// and no <see cref="UIHintAttribute"/> exists. <c>null</c> otherwise.
/// </returns>
protected override string ComputeTemplateHint()
{
if (PrototypeCache.UIHint != null)
{
return PrototypeCache.UIHint.UIHint;
}
if (PrototypeCache.HiddenInput != null)
{
return "HiddenInput";
}
return base.ComputeTemplateHint();
}
private static void ValidateDisplayColumnAttribute(DisplayColumnAttribute displayColumnAttribute,
PropertyInfo displayColumnProperty, Type modelType)
{
if (displayColumnProperty == null)
{
throw new InvalidOperationException(
Resources.FormatDataAnnotationsModelMetadataProvider_UnknownProperty(
modelType.FullName, displayColumnAttribute.DisplayColumn));
}
if (displayColumnProperty.GetGetMethod() == null)
{
throw new InvalidOperationException(
Resources.FormatDataAnnotationsModelMetadataProvider_UnreadableProperty(
modelType.FullName, displayColumnAttribute.DisplayColumn));
}
}
private class CompositePredicateProvider : IPropertyBindingPredicateProvider
{
private readonly IPropertyBindingPredicateProvider[] _providers;
public CompositePredicateProvider(IPropertyBindingPredicateProvider[] providers)
{
_providers = providers;
}
public Func<ModelBindingContext, string, bool> PropertyFilter
{
get
{
return CreatePredicate();
}
}
private Func<ModelBindingContext, string, bool> CreatePredicate()
{
var predicates = _providers
.Select(p => p.PropertyFilter)
.Where(p => p != null)
.ToArray();
return (context, propertyName) =>
{
foreach (var predicate in predicates)
{
if (!predicate(context, propertyName))
{
return false;
}
}
return true;
};
}
}
}
}

View File

@ -1,727 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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.Generic;
namespace Microsoft.AspNet.Mvc.ModelBinding
{
// This class assumes that model metadata is expensive to create, and allows the user to
// stash a cache object that can be copied around as a prototype to make creation and
// computation quicker. It delegates the retrieval of values to getter methods, the results
// of which are cached on a per-metadata-instance basis.
//
// This allows flexible caching strategies: either caching the source of information across
// instances or caching of the actual information itself, depending on what the developer
// decides to put into the prototype cache.
public abstract class CachedModelMetadata<TPrototypeCache> : ModelMetadata
{
private bool _convertEmptyStringToNull;
private string _dataTypeName;
private string _description;
private string _displayFormatString;
private string _displayName;
private string _editFormatString;
private bool _hasNonDefaultEditFormat;
private bool _hideSurroundingHtml;
private bool _htmlEncode;
private bool _isCollectionType;
private bool _isComplexType;
private bool _isReadOnly;
private bool _isRequired;
private string _nullDisplayText;
private int _order;
private bool _showForDisplay;
private bool _showForEdit;
private string _simpleDisplayProperty;
private string _templateHint;
private IBinderMetadata _binderMetadata;
private string _binderModelName;
private IPropertyBindingPredicateProvider _propertyBindingPredicateProvider;
private Type _binderType;
private bool _convertEmptyStringToNullComputed;
private bool _dataTypeNameComputed;
private bool _descriptionComputed;
private bool _displayFormatStringComputed;
private bool _displayNameComputed;
private bool _editFormatStringComputed;
private bool _hasNonDefaultEditFormatComputed;
private bool _hideSurroundingHtmlComputed;
private bool _htmlEncodeComputed;
private bool _isCollectionTypeComputed;
private bool _isComplexTypeComputed;
private bool _isReadOnlyComputed;
private bool _isRequiredComputed;
private bool _nullDisplayTextComputed;
private bool _orderComputed;
private bool _showForDisplayComputed;
private bool _showForEditComputed;
private bool _simpleDisplayPropertyComputed;
private bool _templateHintComputed;
private bool _isBinderMetadataComputed;
private bool _isBinderModelNameComputed;
private bool _isBinderTypeComputed;
private bool _propertyBindingPredicateProviderComputed;
// Constructor for creating real instances of the metadata class based on a prototype
protected CachedModelMetadata(CachedModelMetadata<TPrototypeCache> prototype)
: base(prototype.Provider,
prototype.ContainerType,
prototype.ModelType,
prototype.PropertyName)
{
CacheKey = prototype.CacheKey;
PrototypeCache = prototype.PrototypeCache;
_isComplexType = prototype.IsComplexType;
_isComplexTypeComputed = true;
}
// Constructor for creating the prototype instances of the metadata class
protected CachedModelMetadata(DataAnnotationsModelMetadataProvider provider,
Type containerType,
Type modelType,
string propertyName,
TPrototypeCache prototypeCache)
: base(provider, containerType, modelType: modelType, propertyName: propertyName)
{
PrototypeCache = prototypeCache;
}
// Sealing for consistency with other properties.
// ModelMetadata already caches the collection and we have no use case for a ComputeAdditionalValues() method.
/// <inheritdoc />
public sealed override IDictionary<object, object> AdditionalValues
{
get
{
return base.AdditionalValues;
}
}
/// <inheritdoc />
public sealed override IBinderMetadata BinderMetadata
{
get
{
if (!_isBinderMetadataComputed)
{
_binderMetadata = ComputeBinderMetadata();
_isBinderMetadataComputed = true;
}
return _binderMetadata;
}
set
{
_binderMetadata = value;
_isBinderMetadataComputed = true;
}
}
/// <inheritdoc />
public sealed override string BinderModelName
{
get
{
if (!_isBinderModelNameComputed)
{
_binderModelName = ComputeBinderModelNamePrefix();
_isBinderModelNameComputed = true;
}
return _binderModelName;
}
set
{
_binderModelName = value;
_isBinderModelNameComputed = true;
}
}
/// <inheritdoc />
public sealed override bool ConvertEmptyStringToNull
{
get
{
if (!_convertEmptyStringToNullComputed)
{
_convertEmptyStringToNull = ComputeConvertEmptyStringToNull();
_convertEmptyStringToNullComputed = true;
}
return _convertEmptyStringToNull;
}
set
{
_convertEmptyStringToNull = value;
_convertEmptyStringToNullComputed = true;
}
}
/// <inheritdoc />
public sealed override string DataTypeName
{
get
{
if (!_dataTypeNameComputed)
{
_dataTypeName = ComputeDataTypeName();
_dataTypeNameComputed = true;
}
return _dataTypeName;
}
set
{
_dataTypeName = value;
_dataTypeNameComputed = true;
}
}
/// <inheritdoc />
public sealed override string Description
{
get
{
if (!_descriptionComputed)
{
_description = ComputeDescription();
_descriptionComputed = true;
}
return _description;
}
set
{
_description = value;
_descriptionComputed = true;
}
}
/// <inheritdoc />
public sealed override string DisplayFormatString
{
get
{
if (!_displayFormatStringComputed)
{
_displayFormatString = ComputeDisplayFormatString();
_displayFormatStringComputed = true;
}
return _displayFormatString;
}
set
{
_displayFormatString = value;
_displayFormatStringComputed = true;
}
}
/// <inheritdoc />
public sealed override string DisplayName
{
get
{
if (!_displayNameComputed)
{
_displayName = ComputeDisplayName();
_displayNameComputed = true;
}
return _displayName;
}
set
{
_displayName = value;
_displayNameComputed = true;
}
}
/// <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
{
get
{
if (!_hideSurroundingHtmlComputed)
{
_hideSurroundingHtml = ComputeHideSurroundingHtml();
_hideSurroundingHtmlComputed = true;
}
return _hideSurroundingHtml;
}
set
{
_hideSurroundingHtml = value;
_hideSurroundingHtmlComputed = true;
}
}
/// <inheritdoc />
public sealed override bool HtmlEncode
{
get
{
if (!_htmlEncodeComputed)
{
_htmlEncode = ComputeHtmlEncode();
_htmlEncodeComputed = true;
}
return _htmlEncode;
}
set
{
_htmlEncode = value;
_htmlEncodeComputed = true;
}
}
/// <inheritdoc />
public sealed override bool IsCollectionType
{
get
{
if (!_isCollectionTypeComputed)
{
_isCollectionType = ComputeIsCollectionType();
_isCollectionTypeComputed = true;
}
return _isCollectionType;
}
}
/// <inheritdoc />
public sealed override bool IsComplexType
{
get
{
if (!_isComplexTypeComputed)
{
_isComplexType = ComputeIsComplexType();
_isComplexTypeComputed = true;
}
return _isComplexType;
}
}
/// <inheritdoc />
public sealed override bool IsReadOnly
{
get
{
if (!_isReadOnlyComputed)
{
_isReadOnly = ComputeIsReadOnly();
_isReadOnlyComputed = true;
}
return _isReadOnly;
}
set
{
_isReadOnly = value;
_isReadOnlyComputed = true;
}
}
/// <inheritdoc />
public sealed override bool IsRequired
{
get
{
if (!_isRequiredComputed)
{
_isRequired = ComputeIsRequired();
_isRequiredComputed = true;
}
return _isRequired;
}
set
{
_isRequired = value;
_isRequiredComputed = true;
}
}
/// <inheritdoc />
public sealed override string NullDisplayText
{
get
{
if (!_nullDisplayTextComputed)
{
_nullDisplayText = ComputeNullDisplayText();
_nullDisplayTextComputed = true;
}
return _nullDisplayText;
}
set
{
_nullDisplayText = value;
_nullDisplayTextComputed = true;
}
}
/// <inheritdoc />
public sealed override int Order
{
get
{
if (!_orderComputed)
{
_order = ComputeOrder();
_orderComputed = true;
}
return _order;
}
set
{
_order = value;
_orderComputed = true;
}
}
public sealed override IPropertyBindingPredicateProvider PropertyBindingPredicateProvider
{
get
{
if (!_propertyBindingPredicateProviderComputed)
{
_propertyBindingPredicateProvider = ComputePropertyBindingPredicateProvider();
_propertyBindingPredicateProviderComputed = true;
}
return _propertyBindingPredicateProvider;
}
set
{
_propertyBindingPredicateProvider = value;
_propertyBindingPredicateProviderComputed = true;
}
}
// Sealing for consistency with other properties.
// ModelMetadata already caches the collection and we have no use case for a ComputeProperties() method.
/// <inheritdoc />
public sealed override ModelPropertyCollection Properties
{
get
{
return base.Properties;
}
}
/// <inheritdoc />
public sealed override bool ShowForDisplay
{
get
{
if (!_showForDisplayComputed)
{
_showForDisplay = ComputeShowForDisplay();
_showForDisplayComputed = true;
}
return _showForDisplay;
}
set
{
_showForDisplay = value;
_showForDisplayComputed = true;
}
}
/// <inheritdoc />
public sealed override bool ShowForEdit
{
get
{
if (!_showForEditComputed)
{
_showForEdit = ComputeShowForEdit();
_showForEditComputed = true;
}
return _showForEdit;
}
set
{
_showForEdit = value;
_showForEditComputed = true;
}
}
/// <inheritdoc />
public sealed override string SimpleDisplayProperty
{
get
{
if (!_simpleDisplayPropertyComputed)
{
_simpleDisplayProperty = ComputeSimpleDisplayProperty();
_simpleDisplayPropertyComputed = true;
}
return _simpleDisplayProperty;
}
set
{
_simpleDisplayProperty = value;
_simpleDisplayPropertyComputed = true;
}
}
/// <inheritdoc />
public sealed override string TemplateHint
{
get
{
if (!_templateHintComputed)
{
_templateHint = ComputeTemplateHint();
_templateHintComputed = true;
}
return _templateHint;
}
set
{
_templateHint = value;
_templateHintComputed = true;
}
}
/// <inheritdoc />
public sealed override Type BinderType
{
get
{
if (!_isBinderTypeComputed)
{
_binderType = ComputeBinderType();
_isBinderTypeComputed = true;
}
return _binderType;
}
set
{
_binderType = value;
_isBinderTypeComputed = true;
}
}
protected TPrototypeCache PrototypeCache { get; set; }
protected virtual Type ComputeBinderType()
{
return base.BinderType;
}
protected virtual IBinderMetadata ComputeBinderMetadata()
{
return base.BinderMetadata;
}
protected virtual IPropertyBindingPredicateProvider ComputePropertyBindingPredicateProvider()
{
return base.PropertyBindingPredicateProvider;
}
protected virtual string ComputeBinderModelNamePrefix()
{
return base.BinderModelName;
}
protected virtual bool ComputeConvertEmptyStringToNull()
{
return base.ConvertEmptyStringToNull;
}
/// <summary>
/// Calculate the <see cref="DataTypeName"/> value.
/// </summary>
/// <returns>Calculated <see cref="DataTypeName"/> value.</returns>
protected virtual string ComputeDataTypeName()
{
return base.DataTypeName;
}
protected virtual string ComputeDescription()
{
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>
/// <returns>Calculated <see cref="HideSurroundingHtml"/> value.</returns>
protected virtual bool ComputeHideSurroundingHtml()
{
return base.HideSurroundingHtml;
}
/// <summary>
/// Calculate the <see cref="HtmlEncode"/> value.
/// </summary>
/// <returns>Calculated <see cref="HtmlEncode"/> value.</returns>
protected virtual bool ComputeHtmlEncode()
{
return base.HtmlEncode;
}
/// <summary>
/// Calculate the <see cref="IsCollectionType"/> value.
/// </summary>
/// <returns>Calculated <see cref="IsCollectionType"/> value.</returns>
protected virtual bool ComputeIsCollectionType()
{
return base.IsCollectionType;
}
/// <summary>
/// Calculate the <see cref="IsComplexType"/> value.
/// </summary>
/// <returns>Calculated <see cref="IsComplexType"/> value.</returns>
protected virtual bool ComputeIsComplexType()
{
return base.IsComplexType;
}
protected virtual bool ComputeIsReadOnly()
{
return base.IsReadOnly;
}
protected virtual bool ComputeIsRequired()
{
return base.IsRequired;
}
/// <summary>
/// Calculate the <see cref="NullDisplayText"/> value.
/// </summary>
/// <returns>Calculated <see cref="NullDisplayText"/> value.</returns>
protected virtual string ComputeNullDisplayText()
{
return base.NullDisplayText;
}
/// <summary>
/// Calculate the <see cref="Order"/> value.
/// </summary>
/// <returns>Calculated <see cref="Order"/> value.</returns>
protected virtual int ComputeOrder()
{
return base.Order;
}
protected virtual bool ComputeShowForDisplay()
{
return base.ShowForDisplay;
}
protected virtual bool ComputeShowForEdit()
{
return base.ShowForEdit;
}
/// <summary>
/// Calculate the <see cref="SimpleDisplayProperty"/> value.
/// </summary>
/// <returns>Calculated <see cref="SimpleDisplayProperty"/> value.</returns>
protected virtual string ComputeSimpleDisplayProperty()
{
return base.SimpleDisplayProperty;
}
/// <summary>
/// Calculate the <see cref="TemplateHint"/> value.
/// </summary>
/// <returns>Calculated <see cref="TemplateHint"/> value.</returns>
protected virtual string ComputeTemplateHint()
{
return base.TemplateHint;
}
}
}

View File

@ -0,0 +1,171 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
{
/// <summary>
/// An implementation of <see cref="IBindingMetadataProvider"/> and <see cref="IDisplayMetadataProvider"/> for
/// the System.ComponentModel.DataAnnotations attribute classes.
/// </summary>
public class DataAnnotationsMetadataDetailsProvider :
IBindingMetadataProvider,
IDisplayMetadataProvider
{
/// <inheritdoc />
public void GetBindingMetadata([NotNull] BindingMetadataProviderContext context)
{
context.BindingMetadata.IsRequired = context.Attributes.OfType<RequiredAttribute>().Any();
var editableAttribute = context.Attributes.OfType<EditableAttribute>().FirstOrDefault();
if (editableAttribute != null)
{
context.BindingMetadata.IsReadOnly = !editableAttribute.AllowEdit;
}
}
/// <inheritdoc />
public void GetDisplayMetadata([NotNull] DisplayMetadataProviderContext context)
{
var attributes = context.Attributes;
var dataTypeAttribute = attributes.OfType<DataTypeAttribute>().FirstOrDefault();
var displayAttribute = attributes.OfType<DisplayAttribute>().FirstOrDefault();
var displayColumnAttribute = attributes.OfType<DisplayColumnAttribute>().FirstOrDefault();
var displayFormatAttribute = attributes.OfType<DisplayFormatAttribute>().FirstOrDefault();
var hiddenInputAttribute = attributes.OfType<HiddenInputAttribute>().FirstOrDefault();
var scaffoldColumnAttribute = attributes.OfType<ScaffoldColumnAttribute>().FirstOrDefault();
var uiHintAttribute = attributes.OfType<UIHintAttribute>().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 (displayFormatAttribute == null && dataTypeAttribute != null)
{
displayFormatAttribute = dataTypeAttribute.DisplayFormat;
}
var displayMetadata = context.DisplayMetadata;
// ConvertEmptyStringToNull
if (displayFormatAttribute != null)
{
displayMetadata.ConvertEmptyStringToNull = displayFormatAttribute.ConvertEmptyStringToNull;
}
// DataTypeName
if (dataTypeAttribute != null)
{
displayMetadata.DataTypeName = dataTypeAttribute.GetDataTypeName();
}
else if (displayFormatAttribute != null && !displayFormatAttribute.HtmlEncode)
{
displayMetadata.DataTypeName = DataType.Html.ToString();
}
// Description
if (displayAttribute != null)
{
displayMetadata.Description = displayAttribute.GetDescription();
}
// DisplayFormat
if (displayFormatAttribute != null)
{
displayMetadata.DisplayFormatString = displayFormatAttribute.DataFormatString;
}
// DisplayName
if (displayAttribute != null)
{
displayMetadata.DisplayName = displayAttribute.GetName();
}
if (displayFormatAttribute != null && displayFormatAttribute.ApplyFormatInEditMode)
{
displayMetadata.EditFormatString = displayFormatAttribute.DataFormatString;
}
// HasNonDefaultEditFormat
if (!string.IsNullOrEmpty(displayFormatAttribute?.DataFormatString) &&
displayFormatAttribute?.ApplyFormatInEditMode == true)
{
// Have a non-empty EditFormatString based on [DisplayFormat] from our cache.
if (dataTypeAttribute == null)
{
// Attributes include no [DataType]; [DisplayFormat] was applied directly.
displayMetadata.HasNonDefaultEditFormat = true;
}
else if (dataTypeAttribute.DisplayFormat != displayFormatAttribute)
{
// Attributes include separate [DataType] and [DisplayFormat]; [DisplayFormat] provided override.
displayMetadata.HasNonDefaultEditFormat = true;
}
else if (dataTypeAttribute.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.
displayMetadata.HasNonDefaultEditFormat = true;
}
}
// HideSurroundingHtml
if (hiddenInputAttribute != null)
{
displayMetadata.HideSurroundingHtml = !hiddenInputAttribute.DisplayValue;
}
// HtmlEncode
if (displayFormatAttribute != null)
{
displayMetadata.HtmlEncode = displayFormatAttribute.HtmlEncode;
}
// NullDisplayText
if (displayFormatAttribute != null)
{
displayMetadata.NullDisplayText = displayFormatAttribute.NullDisplayText;
}
// Order
if (displayAttribute?.GetOrder() != null)
{
displayMetadata.Order = displayAttribute.GetOrder().Value;
}
// ShowForDisplay
if (scaffoldColumnAttribute != null)
{
displayMetadata.ShowForDisplay = scaffoldColumnAttribute.Scaffold;
}
// ShowForEdit
if (scaffoldColumnAttribute != null)
{
displayMetadata.ShowForEdit = scaffoldColumnAttribute.Scaffold;
}
// SimpleDisplayProperty
if (displayColumnAttribute != null)
{
displayMetadata.SimpleDisplayProperty = displayColumnAttribute.DisplayColumn;
}
// TemplateHinte
if (uiHintAttribute != null)
{
displayMetadata.TemplateHint = uiHintAttribute.UIHint;
}
else if (hiddenInputAttribute != null)
{
displayMetadata.TemplateHint = "HiddenInput";
}
}
}
}

View File

@ -1,42 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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.Generic;
namespace Microsoft.AspNet.Mvc.ModelBinding
{
/// <summary>
/// An <see cref="IModelMetadataProvider"/> implementation that provides
/// <see cref="CachedDataAnnotationsModelMetadata"/> instances. Those instances primarily calculate property values
/// using attributes from the <see cref="System.ComponentModel.DataAnnotations"/> namespace.
/// </summary>
public class DataAnnotationsModelMetadataProvider : AssociatedMetadataProvider<CachedDataAnnotationsModelMetadata>
{
/// <inheritdoc />
protected override CachedDataAnnotationsModelMetadata CreateMetadataPrototype(
IEnumerable<object> attributes,
Type containerType,
Type modelType,
string propertyName)
{
return new CachedDataAnnotationsModelMetadata(this, containerType, modelType, propertyName, attributes);
}
/// <inheritdoc />
/// <remarks>
/// Copies only a few values from the <paramref name="prototype"/>. Unlikely the rest have been computed.
/// </remarks>
protected override CachedDataAnnotationsModelMetadata CreateMetadataFromPrototype(
CachedDataAnnotationsModelMetadata prototype)
{
var metadata = new CachedDataAnnotationsModelMetadata(prototype);
foreach (var keyValuePair in prototype.AdditionalValues)
{
metadata.AdditionalValues.Add(keyValuePair);
}
return metadata;
}
}
}

View File

@ -0,0 +1,99 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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.Generic;
using System.Linq;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
{
/// <summary>
/// A default implementation of <see cref="IBindingMetadataProvider"/>.
/// </summary>
public class DefaultBindingMetadataProvider : IBindingMetadataProvider
{
/// <inheritdoc />
public void GetBindingMetadata([NotNull] BindingMetadataProviderContext context)
{
// For Model Name - we only use the first attribute we find. An attribute on the parameter
// is considered an override of an attribute on the type. This is for compatibility with [Bind]
// from MVC 5.
//
// BinderType and BindingSource fall back to the first attribute to provide a value.
// BinderModelName
var binderModelNameAttribute = context.Attributes.OfType<IModelNameProvider>().FirstOrDefault();
if (binderModelNameAttribute?.Name != null)
{
context.BindingMetadata.BinderModelName = binderModelNameAttribute.Name;
}
// BinderType
foreach (var binderTypeAttribute in context.Attributes.OfType<IBinderTypeProviderMetadata>())
{
if (binderTypeAttribute.BinderType != null)
{
context.BindingMetadata.BinderType = binderTypeAttribute.BinderType;
break;
}
}
// BindingSource
foreach (var bindingSourceAttribute in context.Attributes.OfType<IBindingSourceMetadata>())
{
if (bindingSourceAttribute.BindingSource != null)
{
context.BindingMetadata.BindingSource = bindingSourceAttribute.BindingSource;
break;
}
}
// PropertyBindingPredicateProvider
var predicateProviders = context.Attributes.OfType<IPropertyBindingPredicateProvider>().ToArray();
if (predicateProviders.Length > 0)
{
context.BindingMetadata.PropertyBindingPredicateProvider = new CompositePredicateProvider(
predicateProviders);
}
}
private class CompositePredicateProvider : IPropertyBindingPredicateProvider
{
private readonly IEnumerable<IPropertyBindingPredicateProvider> _providers;
public CompositePredicateProvider(IEnumerable<IPropertyBindingPredicateProvider> providers)
{
_providers = providers;
}
public Func<ModelBindingContext, string, bool> PropertyFilter
{
get
{
return CreatePredicate();
}
}
private Func<ModelBindingContext, string, bool> CreatePredicate()
{
var predicates = _providers
.Select(p => p.PropertyFilter)
.Where(p => p != null);
return (context, propertyName) =>
{
foreach (var predicate in predicates)
{
if (!predicate(context, propertyName))
{
return false;
}
}
return true;
};
}
}
}
}

View File

@ -0,0 +1,53 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Linq;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
{
/// <summary>
/// A default implementation of <see cref="ICompositeMetadataDetailsProvider"/>.
/// </summary>
public class DefaultCompositeMetadataDetailsProvider : ICompositeMetadataDetailsProvider
{
private readonly IEnumerable<IMetadataDetailsProvider> _providers;
/// <summary>
/// Creates a new <see cref="DefaultCompositeMetadataDetailsProvider"/>.
/// </summary>
/// <param name="providers">The set of <see cref="IMetadataDetailsProvider"/> instances.</param>
public DefaultCompositeMetadataDetailsProvider(IEnumerable<IMetadataDetailsProvider> providers)
{
_providers = providers;
}
/// <inheritdoc />
public virtual void GetBindingMetadata([NotNull] BindingMetadataProviderContext context)
{
foreach (var provider in _providers.OfType<IBindingMetadataProvider>())
{
provider.GetBindingMetadata(context);
}
}
/// <inheritdoc />
public virtual void GetDisplayMetadata([NotNull] DisplayMetadataProviderContext context)
{
foreach (var provider in _providers.OfType<IDisplayMetadataProvider>())
{
provider.GetDisplayMetadata(context);
}
}
/// <inheritdoc />
public virtual void GetValidationMetadata([NotNull] ValidationMetadataProviderContext context)
{
foreach (var provider in _providers.OfType<IValidationMetadataProvider>())
{
provider.GetValidationMetadata(context);
}
}
}
}

View File

@ -0,0 +1,65 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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.Generic;
namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
{
/// <summary>
/// A cache of metadata objects for a <see cref="DefaultModelMetadata"/>.
/// </summary>
/// <remarks>
/// These instances are shared by all <see cref="DefaultModelMetadata"/> instances representing
/// the same <see cref="Type"/>, property, or parameter. Any modifications to the data must be
/// thread-safe for multiple readers and writers.
/// </remarks>
public class DefaultMetadataDetailsCache
{
/// <summary>
/// Creates a new <see cref="DefaultMetadataDetailsCache"/>.
/// </summary>
/// <param name="key">The <see cref="ModelMetadataIdentity"/>.</param>
/// <param name="attributes">The set of model attributes.</param>
public DefaultMetadataDetailsCache(ModelMetadataIdentity key, IReadOnlyList<object> attributes)
{
Key = key;
Attributes = attributes;
}
/// <summary>
/// Gets or sets the set of model attributes.
/// </summary>
public IReadOnlyList<object> Attributes { get; }
/// <summary>
/// Gets or sets the <see cref="Metadata.BindingMetadata"/>
/// </summary>
public BindingMetadata BindingMetadata { get; set; }
/// <summary>
/// Gets or sets the <see cref="Metadata.DisplayMetadata"/>
/// </summary>
public DisplayMetadata DisplayMetadata { get; set; }
/// <summary>
/// Gets or sets the <see cref="ModelMetadataIdentity"/>
/// </summary>
public ModelMetadataIdentity Key { get; }
/// <summary>
/// Gets or sets a property accessor delegate to get the property value from a model object.
/// </summary>
public Func<object, object> PropertyAccessor { get; set; }
/// <summary>
/// Gets or sets a property setter delegate to set the property value on a model object.
/// </summary>
public Action<object, object> PropertySetter { get; set; }
/// <summary>
/// Gets or sets the <see cref="Metadata.ValidationMetadata"/>
/// </summary>
public ValidationMetadata ValidationMetadata { get; set; }
}
}

View File

@ -0,0 +1,360 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
{
/// <summary>
/// A default <see cref="ModelMetadata"/> implementation.
/// </summary>
public class DefaultModelMetadata : ModelMetadata
{
private readonly IModelMetadataProvider _provider;
private readonly ICompositeMetadataDetailsProvider _detailsProvider;
private readonly DefaultMetadataDetailsCache _cache;
private ReadOnlyDictionary<object, object> _additionalValues;
private bool? _isReadOnly;
private bool? _isRequired;
private ModelPropertyCollection _properties;
/// <summary>
/// Creates a new <see cref="DefaultModelMetadata"/>.
/// </summary>
/// <param name="provider">The <see cref="IModelMetadataProvider"/>.</param>
/// <param name="detailsProvider">The <see cref="ICompositeMetadataDetailsProvider"/>.</param>
/// <param name="cache">The <see cref="DefaultMetadataDetailsCache"/>.</param>
public DefaultModelMetadata(
[NotNull] IModelMetadataProvider provider,
[NotNull] ICompositeMetadataDetailsProvider detailsProvider,
[NotNull] DefaultMetadataDetailsCache cache)
: base(cache.Key)
{
_provider = provider;
_detailsProvider = detailsProvider;
_cache = cache;
}
/// <summary>
/// Gets the set of attributes for the current instance.
/// </summary>
public IReadOnlyList<object> Attributes
{
get
{
return _cache.Attributes;
}
}
/// <summary>
/// Gets the <see cref="Metadata.BindingMetadata"/> for the current instance.
/// </summary>
/// <remarks>
/// Accessing this property will populate the <see cref="Metadata.BindingMetadata"/> if necessary.
/// </remarks>
public BindingMetadata BindingMetadata
{
get
{
if (_cache.BindingMetadata == null)
{
var context = new BindingMetadataProviderContext(Identity, _cache.Attributes);
_detailsProvider.GetBindingMetadata(context);
_cache.BindingMetadata = context.BindingMetadata;
}
return _cache.BindingMetadata;
}
}
/// <summary>
/// Gets the <see cref="Metadata.DisplayMetadata"/> for the current instance.
/// </summary>
/// <remarks>
/// Accessing this property will populate the <see cref="Metadata.DisplayMetadata"/> if necessary.
/// </remarks>
public DisplayMetadata DisplayMetadata
{
get
{
if (_cache.DisplayMetadata == null)
{
var context = new DisplayMetadataProviderContext(Identity, _cache.Attributes);
_detailsProvider.GetDisplayMetadata(context);
_cache.DisplayMetadata = context.DisplayMetadata;
}
return _cache.DisplayMetadata;
}
}
/// <summary>
/// Gets the <see cref="Metadata.ValidationMetadata"/> for the current instance.
/// </summary>
/// <remarks>
/// Accessing this property will populate the <see cref="Metadata.ValidationMetadata"/> if necessary.
/// </remarks>
public ValidationMetadata ValidationMetadata
{
get
{
if (_cache.ValidationMetadata == null)
{
var context = new ValidationMetadataProviderContext(Identity, _cache.Attributes);
_detailsProvider.GetValidationMetadata(context);
_cache.ValidationMetadata = context.ValidationMetadata;
}
return _cache.ValidationMetadata;
}
}
/// <inheritdoc />
public override IReadOnlyDictionary<object, object> AdditionalValues
{
get
{
if (_additionalValues == null)
{
_additionalValues = new ReadOnlyDictionary<object, object>(DisplayMetadata.AdditionalValues);
}
return _additionalValues;
}
}
/// <inheritdoc />
public override BindingSource BindingSource
{
get
{
return BindingMetadata.BindingSource;
}
}
/// <inheritdoc />
public override string BinderModelName
{
get
{
return BindingMetadata.BinderModelName;
}
}
/// <inheritdoc />
public override Type BinderType
{
get
{
return BindingMetadata.BinderType;
}
}
/// <inheritdoc />
public override bool ConvertEmptyStringToNull
{
get
{
return DisplayMetadata.ConvertEmptyStringToNull;
}
}
/// <inheritdoc />
public override string DataTypeName
{
get
{
return DisplayMetadata.DataTypeName;
}
}
/// <inheritdoc />
public override string Description
{
get
{
return DisplayMetadata.Description;
}
}
/// <inheritdoc />
public override string DisplayFormatString
{
get
{
return DisplayMetadata.DisplayFormatString;
}
}
/// <inheritdoc />
public override string DisplayName
{
get
{
return DisplayMetadata.DisplayName;
}
}
/// <inheritdoc />
public override string EditFormatString
{
get
{
return DisplayMetadata.EditFormatString;
}
}
/// <inheritdoc />
public override bool HasNonDefaultEditFormat
{
get
{
return DisplayMetadata.HasNonDefaultEditFormat;
}
}
/// <inheritdoc />
public override bool HideSurroundingHtml
{
get
{
return DisplayMetadata.HideSurroundingHtml;
}
}
/// <inheritdoc />
public override bool HtmlEncode
{
get
{
return DisplayMetadata.HtmlEncode;
}
}
/// <inheritdoc />
public override bool IsReadOnly
{
get
{
if (!_isReadOnly.HasValue)
{
if (BindingMetadata.IsReadOnly.HasValue)
{
_isReadOnly = BindingMetadata.IsReadOnly;
}
else
{
_isReadOnly = _cache.PropertySetter != null;
}
}
return _isReadOnly.Value;
}
}
/// <inheritdoc />
public override bool IsRequired
{
get
{
if (!_isRequired.HasValue)
{
if (BindingMetadata.IsRequired.HasValue)
{
_isRequired = BindingMetadata.IsRequired;
}
else
{
_isRequired = !ModelType.AllowsNullValue();
}
}
return _isRequired.Value;
}
}
/// <inheritdoc />
public override string NullDisplayText
{
get
{
return DisplayMetadata.NullDisplayText;
}
}
/// <inheritdoc />
public override int Order
{
get
{
return DisplayMetadata.Order;
}
}
/// <inheritdoc />
public override ModelPropertyCollection Properties
{
get
{
if (_properties == null)
{
var properties = _provider.GetMetadataForProperties(ModelType);
properties = properties.OrderBy(p => p.Order);
_properties = new ModelPropertyCollection(properties);
}
return _properties;
}
}
/// <inheritdoc />
public override IPropertyBindingPredicateProvider PropertyBindingPredicateProvider
{
get
{
return BindingMetadata.PropertyBindingPredicateProvider;
}
}
/// <inheritdoc />
public override bool ShowForDisplay
{
get
{
return DisplayMetadata.ShowForDisplay;
}
}
/// <inheritdoc />
public override bool ShowForEdit
{
get
{
return DisplayMetadata.ShowForEdit;
}
}
/// <inheritdoc />
public override string SimpleDisplayProperty
{
get
{
return DisplayMetadata.SimpleDisplayProperty;
}
}
/// <inheritdoc />
public override string TemplateHint
{
get
{
return DisplayMetadata.TemplateHint;
}
}
}
}

View File

@ -0,0 +1,198 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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.Concurrent;
using System.Collections.Generic;
using System.Reflection;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
{
/// <summary>
/// A default implementation of <see cref="IModelMetadataProvider"/> based on reflection.
/// </summary>
public class DefaultModelMetadataProvider : IModelMetadataProvider
{
private readonly TypeCache _typeCache = new TypeCache();
private readonly ParameterCache _parameterCache = new ParameterCache();
private readonly PropertiesCache _propertiesCache = new PropertiesCache();
/// <summary>
/// Creates a new <see cref="DefaultModelMetadataProvider"/>.
/// </summary>
/// <param name="detailsProvider">The <see cref="ICompositeMetadataDetailsProvider"/>.</param>
public DefaultModelMetadataProvider(ICompositeMetadataDetailsProvider detailsProvider)
{
DetailsProvider = detailsProvider;
}
/// <summary>
/// Gets the <see cref="ICompositeMetadataDetailsProvider"/>.
/// </summary>
protected ICompositeMetadataDetailsProvider DetailsProvider { get; }
/// <inheritdoc />
public virtual ModelMetadata GetMetadataForParameter(
[NotNull] ParameterInfo parameterInfo,
[NotNull] IEnumerable<object> attributes)
{
var key = ModelMetadataIdentity.ForParameter(parameterInfo);
DefaultMetadataDetailsCache entry;
if (!_parameterCache.TryGetValue(key, out entry))
{
entry = CreateParameterCacheEntry(key, attributes);
entry = _parameterCache.GetOrAdd(key, entry);
}
return CreateModelMetadata(entry);
}
/// <inheritdoc />
public virtual IEnumerable<ModelMetadata> GetMetadataForProperties([NotNull]Type modelType)
{
var key = ModelMetadataIdentity.ForType(modelType);
var propertyEntries = _propertiesCache.GetOrAdd(key, CreatePropertyCacheEntries);
var properties = new ModelMetadata[propertyEntries.Length];
for (var i = 0; i < properties.Length; i++)
{
properties[i] = CreateModelMetadata(propertyEntries[i]);
}
return properties;
}
/// <inheritdoc />
public virtual ModelMetadata GetMetadataForType([NotNull] Type modelType)
{
var key = ModelMetadataIdentity.ForType(modelType);
var entry = _typeCache.GetOrAdd(key, CreateTypeCacheEntry);
return CreateModelMetadata(entry);
}
/// <summary>
/// Creates a new <see cref="ModelMetadata"/> from a <see cref="DefaultMetadataDetailsCache"/>.
/// </summary>
/// <param name="entry">The <see cref="DefaultMetadataDetailsCache"/> entry with cached data.</param>
/// <returns>A new <see cref="ModelMetadata"/> instance.</returns>
/// <remarks>
/// <see cref="DefaultModelMetadataProvider"/> will always create instances of
/// <see cref="DefaultModelMetadata"/> .Override this method to create a <see cref="ModelMetadata"/>
/// of a different concrete type.
/// </remarks>
protected virtual ModelMetadata CreateModelMetadata(DefaultMetadataDetailsCache entry)
{
return new DefaultModelMetadata(this, DetailsProvider, entry);
}
/// <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 DefaultMetadataDetailsCache[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 = new List<object>(ModelAttributes.GetAttributesForProperty(
key.ModelType,
propertyHelper.Property));
propertyEntries[i] = new DefaultMetadataDetailsCache(propertyKey, attributes);
if (propertyHelper.Property.CanRead && propertyHelper.Property.GetMethod?.IsPrivate == true)
{
propertyEntries[i].PropertyAccessor = PropertyHelper.MakeFastPropertyGetter(propertyHelper.Property);
}
if (propertyHelper.Property.CanWrite && propertyHelper.Property.SetMethod?.IsPrivate == true)
{
propertyEntries[i].PropertySetter = PropertyHelper.MakeFastPropertySetter(propertyHelper.Property);
}
}
return propertyEntries;
}
/// <summary>
/// Creates the <see cref="DefaultMetadataDetailsCache"/> entry for 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 the model <see cref="Type"/>.</returns>
/// <remarks>
/// The results of this method will be cached and used to satisfy calls to
/// <see cref="GetMetadataForType(Type)"/>. Override this method to provide a different
/// set of attributes.
/// </remarks>
protected virtual DefaultMetadataDetailsCache CreateTypeCacheEntry([NotNull] ModelMetadataIdentity key)
{
var attributes = new List<object>(ModelAttributes.GetAttributesForType(key.ModelType));
return new DefaultMetadataDetailsCache(key, attributes);
}
/// <summary>
/// Creates the <see cref="DefaultMetadataDetailsCache"/> entry for a parameter.
/// </summary>
/// <param name="key">
/// The <see cref="ModelMetadataIdentity"/> identifying the model parameter.
/// </param>
/// <param name="attributes">
/// The set of model attributes applied to the parameter.
/// </param>
/// <returns>A cache object for the model parameter.</returns>
/// <remarks>
/// The results of this method will be cached and used to satisfy calls to
/// <see cref="GetMetadataForParameter(ParameterInfo, IEnumerable{object}))"/>.
/// Override this method to provide a different set of attributes.
/// </remarks>
protected virtual DefaultMetadataDetailsCache CreateParameterCacheEntry(
[NotNull] ModelMetadataIdentity key,
[NotNull] IEnumerable<object> attributes)
{
var allAttributes = new List<object>();
if (attributes != null)
{
allAttributes.AddRange(attributes);
}
allAttributes.AddRange(ModelAttributes.GetAttributesForParameter(key.ParameterInfo));
return new DefaultMetadataDetailsCache(key, allAttributes);
}
private class TypeCache : ConcurrentDictionary<ModelMetadataIdentity, DefaultMetadataDetailsCache>
{
}
private class PropertiesCache : ConcurrentDictionary<ModelMetadataIdentity, DefaultMetadataDetailsCache[]>
{
}
private class ParameterCache : ConcurrentDictionary<ModelMetadataIdentity, DefaultMetadataDetailsCache>
{
}
}
}

View File

@ -0,0 +1,112 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
{
/// <summary>
/// Display metadata details for a <see cref="ModelMetadata"/>.
/// </summary>
public class DisplayMetadata
{
/// <summary>
/// Gets a set of additional values. See <see cref="ModelMetadata.AdditionalValues"/>
/// </summary>
public IDictionary<object, object> AdditionalValues { get; } = new Dictionary<object, object>();
/// <summary>
/// Gets or sets a value indicating whether or not empty strings should be treated as <c>null</c>.
/// See <see cref="ModelMetadata.ConvertEmptyStringToNull"/>
/// </summary>
public bool ConvertEmptyStringToNull { get; set; } = true;
/// <summary>
/// Gets or sets the name of the data type.
/// See <see cref="ModelMetadata.DataTypeName"/>
/// </summary>
public string DataTypeName { get; set; }
/// <summary>
/// Gets or sets a model description.
/// See <see cref="ModelMetadata.Description"/>
/// </summary>
public string Description { get; set; }
/// <summary>
/// Gets or sets a display format string for the model.
/// See <see cref="ModelMetadata.DisplayFormatString"/>
/// </summary>
public string DisplayFormatString { get; set; }
/// <summary>
/// Gets or sets a display name for the model.
/// See <see cref="ModelMetadata.DisplayName"/>
/// </summary>
public string DisplayName { get; set; }
/// <summary>
/// Gets or sets an edit format string for the model.
/// See <see cref="ModelMetadata.EditFormatString"/>
/// </summary>
/// <remarks>
/// <see cref="IDisplayMetadataProvider"/> 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 string EditFormatString { get; set; }
/// <summary>
/// Gets or sets a value indicating whether or not the model has a non-default edit format.
/// See <see cref="ModelMetadata.HasNonDefaultEditFormat"/>
/// </summary>
public bool HasNonDefaultEditFormat { get; set; }
/// <summary>
/// Gets or sets a value indicating if the surrounding HTML should be hidden.
/// See <see cref="ModelMetadata.HideSurroundingHtml"/>
/// </summary>
public bool HideSurroundingHtml { get; set; }
/// <summary>
/// Gets or sets a value indicating if the model value should be HTML encoded.
/// See <see cref="ModelMetadata.HtmlEncode"/>
/// </summary>
public bool HtmlEncode { get; set; } = true;
/// <summary>
/// Gets or sets the text to display when the model value is null.
/// See <see cref="ModelMetadata.NullDisplayText"/>
/// </summary>
public string NullDisplayText { get; set; }
/// <summary>
/// Gets or sets the order.
/// See <see cref="ModelMetadata.Order"/>
/// </summary>
public int Order { get; set; } = 10000;
/// <summary>
/// Gets or sets a value indicating whether or not to include in the model value in display.
/// See <see cref="ModelMetadata.ShowForDisplay"/>
/// </summary>
public bool ShowForDisplay { get; set; } = true;
/// <summary>
/// Gets or sets a value indicating whether or not to include in the model value in an editor.
/// See <see cref="ModelMetadata.ShowForEdit"/>
/// </summary>
public bool ShowForEdit { get; set; } = true;
/// <summary>
/// Gets or sets a the property name of a model property to use for display.
/// See <see cref="ModelMetadata.SimpleDisplayProperty"/>
/// </summary>
public string SimpleDisplayProperty { get; set; }
/// <summary>
/// Gets or sets a hint for location of a display or editor template.
/// See <see cref="ModelMetadata.TemplateHint"/>
/// </summary>
public string TemplateHint { get; set; }
}
}

View File

@ -0,0 +1,43 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
{
/// <summary>
/// A context for and <see cref="IDisplayMetadataProvider"/>.
/// </summary>
public class DisplayMetadataProviderContext
{
/// <summary>
/// Creates a new <see cref="DisplayMetadataProviderContext"/>.
/// </summary>
/// <param name="key">The <see cref="ModelMetadataIdentity"/> for the <see cref="ModelMetadata"/>.</param>
/// <param name="attributes">The attributes for the <see cref="ModelMetadata"/>.</param>
public DisplayMetadataProviderContext(
[NotNull] ModelMetadataIdentity key,
[NotNull] IReadOnlyList<object> attributes)
{
Key = key;
Attributes = attributes;
DisplayMetadata = new DisplayMetadata();
}
/// <summary>
/// Gets the attributes.
/// </summary>
public IReadOnlyList<object> Attributes { get; }
/// <summary>
/// Gets the <see cref="ModelMetadataIdentity"/>.
/// </summary>
public ModelMetadataIdentity Key { get; }
/// <summary>
/// Gets the <see cref="Metadata.DisplayMetadata"/>.
/// </summary>
public DisplayMetadata DisplayMetadata { get; }
}
}

View File

@ -1,53 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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.Generic;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ModelBinding
{
/// <summary>
/// An <see cref="IModelMetadataProvider"/> that provides base <see cref="ModelMetadata"/> instances and does not
/// set most <see cref="ModelMetadata"/> properties. For example this provider does not use data annotations.
/// </summary>
/// <remarks>
/// Provided for efficiency in scenarios that require minimal <see cref="ModelMetadata"/> information.
/// </remarks>
public class EmptyModelMetadataProvider : AssociatedMetadataProvider<ModelMetadata>
{
/// <inheritdoc />
/// <remarks>Ignores <paramref name="attributes"/>.</remarks>
protected override ModelMetadata CreateMetadataPrototype(IEnumerable<object> attributes,
Type containerType,
[NotNull] Type modelType,
string propertyName)
{
return new ModelMetadata(
this,
containerType,
modelType: modelType,
propertyName: propertyName);
}
/// <inheritdoc />
/// <remarks>
/// Copies very few values from the <paramref name="prototype"/>. Likely <paramref name="prototype"/> has not
/// been modified except to add <see cref="ModelMetadata.AdditionalValues"/> entries.
/// </remarks>
protected override ModelMetadata CreateMetadataFromPrototype([NotNull] ModelMetadata prototype)
{
var metadata = new ModelMetadata(
this,
prototype.ContainerType,
prototype.ModelType,
prototype.PropertyName);
foreach (var keyValuePair in prototype.AdditionalValues)
{
metadata.AdditionalValues.Add(keyValuePair);
}
return metadata;
}
}
}

View File

@ -0,0 +1,19 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
{
/// <summary>
/// Provides <see cref="BindingMetadata"/> for a <see cref="DefaultModelMetadata"/>.
/// </summary>
public interface IBindingMetadataProvider : IMetadataDetailsProvider
{
/// <summary>
/// Gets the values for properties of <see cref="DisplayMetadata"/>.
/// </summary>
/// <param name="context">The <see cref="BindingMetadataProviderContext"/>.</param>
void GetBindingMetadata([NotNull] BindingMetadataProviderContext context);
}
}

View File

@ -0,0 +1,15 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
{
/// <summary>
/// A composite <see cref="IMetadataDetailsProvider"/>.
/// </summary>
public interface ICompositeMetadataDetailsProvider :
IBindingMetadataProvider,
IDisplayMetadataProvider,
IValidationMetadataProvider
{
}
}

View File

@ -0,0 +1,19 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
{
/// <summary>
/// Provides <see cref="DisplayMetadata"/> for a <see cref="DefaultModelMetadata"/>.
/// </summary>
public interface IDisplayMetadataProvider : IMetadataDetailsProvider
{
/// <summary>
/// Gets the values for properties of <see cref="DisplayMetadata"/>.
/// </summary>
/// <param name="context">The <see cref="DisplayMetadataProviderContext"/>.</param>
void GetDisplayMetadata([NotNull] DisplayMetadataProviderContext context);
}
}

View File

@ -0,0 +1,14 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
{
/// <summary>
/// Marker interface for a provider of metadata details about model objects. Implementations should
/// implement one or more of <see cref="IBindingMetadataProvider"/>, <see cref="IDisplayMetadataProvider"/>,
/// and <see cref="IModelValidatorProvider"/>.
/// </summary>
public interface IMetadataDetailsProvider
{
}
}

View File

@ -0,0 +1,19 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
{
/// <summary>
/// Provides <see cref="ValidationMetadata"/> for a <see cref="DefaultModelMetadata"/>.
/// </summary>
public interface IValidationMetadataProvider : IMetadataDetailsProvider
{
/// <summary>
/// Gets the values for properties of <see cref="ValidationMetadata"/>.
/// </summary>
/// <param name="context">The <see cref="ValidationMetadataProviderContext"/>.</param>
void GetValidationMetadata([NotNull] ValidationMetadataProviderContext context);
}
}

View File

@ -1,285 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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.Generic;
using System.Linq;
using Microsoft.AspNet.Mvc.ModelBinding.Internal;
using Microsoft.Framework.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<Type, string> _cacheKey;
// Backing fields for virtual properties with default values.
private bool _convertEmptyStringToNull = true;
private bool _htmlEncode = true;
private bool _showForDisplay = true;
private bool _showForEdit = true;
private int _order = DefaultOrder;
private bool _isRequired;
private ModelPropertyCollection _properties;
public ModelMetadata([NotNull] IModelMetadataProvider provider,
Type containerType,
[NotNull] Type modelType,
string propertyName)
{
Provider = provider;
_containerType = containerType;
_modelType = modelType;
_propertyName = propertyName;
_isRequired = !modelType.AllowsNullValue();
}
/// <summary>
/// Gets a collection of additional information about the model.
/// </summary>
public virtual IDictionary<object, object> AdditionalValues { get; } = new Dictionary<object, object>();
/// <summary>
/// Gets or sets the name of a model if specified explicitly using <see cref="IModelNameProvider"/>.
/// </summary>
public virtual string BinderModelName { get; set; }
/// <summary>
/// Gets or sets the <see cref="Type"/> of an <see cref="IModelBinder"/> or an
/// <see cref="IModelBinderProvider"/> of a model if specified explicitly using
/// <see cref="IBinderTypeProviderMetadata"/>.
/// </summary>
public virtual Type BinderType { get; set; }
/// <summary>
/// Gets or sets a binder metadata for this model.
/// </summary>
public virtual IBinderMetadata BinderMetadata { get; set; }
public Type ContainerType
{
get { return _containerType; }
}
public virtual bool ConvertEmptyStringToNull
{
get { return _convertEmptyStringToNull; }
set { _convertEmptyStringToNull = value; }
}
/// <summary>
/// Gets or sets the name of the Model's datatype. Overrides <see cref="ModelType"/> in some
/// display scenarios.
/// </summary>
/// <value><c>null</c> unless set manually or through additional metadata e.g. attributes.</value>
public virtual string DataTypeName { get; set; }
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 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 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 value should be HTML-encoded.
/// </summary>
/// <value>If <c>true</c>, value should be HTML-encoded. Default is <c>true</c>.</value>
public virtual bool HtmlEncode
{
get { return _htmlEncode; }
set { _htmlEncode = value; }
}
/// <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
/// also return the expression value (together with the hidden &lt;input&gt; element).
/// </summary>
/// <remarks>
/// If <c>true</c>, also causes the default <see cref="object"/> display and editor templates to return HTML
/// lacking the usual per-property &lt;div&gt; wrapper around the associated property. Thus the default
/// <see cref="object"/> display template effectively skips the property and the default <see cref="object"/>
/// editor template returns only the hidden &lt;input&gt; element for the property.
/// </remarks>
public virtual bool HideSurroundingHtml { get; set; }
/// <summary>
/// Gets a value indicating whether the <see cref="ModelType"/> is a collection <see cref="Type"/>.
/// </summary>
/// <remarks>
/// <c>true</c> if the <see cref="ModelType"/> is not <see cref="string"/> and is assignable to
/// <see cref="System.Collections.IEnumerable"/>; <c>false</c> otherwise.
/// </remarks>
public virtual bool IsCollectionType
{
get { return TypeHelper.IsCollectionType(ModelType); }
}
/// <summary>
/// Gets a value indicating whether the <see cref="ModelType"/> is a complex type.
/// </summary>
/// <remarks>
/// <c>false</c> if the <see cref="ModelType"/> has a direct conversion to <see cref="string"/>; <c>true</c>
/// otherwise.
/// </remarks>
public virtual bool IsComplexType
{
get { return !TypeHelper.HasStringConverter(ModelType); }
}
public bool IsNullableValueType
{
get { return ModelType.IsNullableValueType(); }
}
public virtual bool IsReadOnly { get; set; }
public virtual bool IsRequired
{
get { return _isRequired; }
set { _isRequired = value; }
}
/// <summary>
/// Gets or sets a value indicating where the current metadata should be ordered relative to other properties
/// in its containing type.
/// </summary>
/// <remarks>
/// <para>For example this property is used to order items in <see cref="Properties"/>.</para>
/// <para>The default order is <c>10000</c>.</para>
/// </remarks>
/// <value>The order value of the current metadata.</value>
public virtual int Order
{
get { return _order; }
set { _order = value; }
}
public Type ModelType
{
get { return _modelType; }
}
public virtual string NullDisplayText { get; set; }
/// <summary>
/// Gets the collection of <see cref="ModelMetadata"/> instances for the model's properties.
/// </summary>
public virtual ModelPropertyCollection Properties
{
get
{
if (_properties == null)
{
var properties = Provider.GetMetadataForProperties(ModelType);
_properties = new ModelPropertyCollection(properties.OrderBy(m => m.Order));
}
return _properties;
}
}
/// <summary>
/// Gets or sets the <see cref="IPropertyBindingPredicateProvider"/>, which can determine which properties
/// should be model bound.
/// </summary>
public virtual IPropertyBindingPredicateProvider PropertyBindingPredicateProvider { get; set; }
public string PropertyName
{
get { return _propertyName; }
}
protected IModelMetadataProvider Provider { get; }
/// <summary>
/// Gets or sets a value which is the name of the property used to display the model.
/// </summary>
public virtual string SimpleDisplayProperty { get; set; }
/// <summary>
/// Gets or sets a value that indicates whether the property should be displayed in read-only views.
/// </summary>
public virtual bool ShowForDisplay
{
get { return _showForDisplay; }
set { _showForDisplay = value; }
}
/// <summary>
/// Gets or sets a value that indicates whether the property should be displayed in editable views.
/// </summary>
public virtual bool ShowForEdit
{
get { return _showForEdit; }
set { _showForEdit = value; }
}
/// <summary>
/// Gets or sets a hint that suggests what template to use for this model. Overrides <see cref="DataTypeName"/>
/// in that context but, unlike <see cref="DataTypeName"/>, this value is not used elsewhere.
/// </summary>
/// <value><c>null</c> unless set manually or through additional metadata e.g. attributes.</value>
public virtual string TemplateHint { get; set; }
internal EfficientTypePropertyKey<Type, string> CacheKey
{
get
{
if (_cacheKey == null)
{
_cacheKey = CreateCacheKey(ContainerType, ModelType, PropertyName);
}
return _cacheKey;
}
set
{
_cacheKey = value;
}
}
public string GetDisplayName()
{
return DisplayName ?? PropertyName ?? ModelType.Name;
}
private static EfficientTypePropertyKey<Type, string> 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<Type, string>(containerType ?? modelType, propertyName);
}
}
}

View File

@ -0,0 +1,113 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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.Reflection;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
{
/// <summary>
/// A key type which identifies a <see cref="ModelMetadata"/>.
/// </summary>
public struct ModelMetadataIdentity
{
/// <summary>
/// Creates a <see cref="ModelMetadataIdentity"/> for the provided model <see cref="Type"/>.
/// </summary>
/// <param name="modelType">The model <see cref="Type"/>.</param>
/// <returns>A <see cref="ModelMetadataIdentity"/>.</returns>
public static ModelMetadataIdentity ForType([NotNull] Type modelType)
{
return new ModelMetadataIdentity()
{
ModelType = modelType,
};
}
/// <summary>
/// Creates a <see cref="ModelMetadataIdentity"/> for the provided <see cref="ParameterInfo"/>.
/// </summary>
/// <param name="parameterInfo">The model parameter.</param>
/// <returns>A <see cref="ModelMetadataIdentity"/>.</returns>
public static ModelMetadataIdentity ForParameter([NotNull] ParameterInfo parameterInfo)
{
return new ModelMetadataIdentity()
{
ParameterInfo = parameterInfo,
Name = parameterInfo.Name,
ModelType = parameterInfo.ParameterType,
};
}
/// <summary>
/// Creates a <see cref="ModelMetadataIdentity"/> for the provided property.
/// </summary>
/// <param name="modelType">The model type.</param>
/// <param name="name">The name of the property.</param>
/// <param name="containerType">The container type of the model property.</param>
/// <returns>A <see cref="ModelMetadataIdentity"/>.</returns>
public static ModelMetadataIdentity ForProperty(
[NotNull] Type modelType,
string name,
[NotNull] Type containerType)
{
if (string.IsNullOrEmpty(name))
{
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(name));
}
return new ModelMetadataIdentity()
{
ModelType = modelType,
Name = name,
ContainerType = containerType,
};
}
/// <summary>
/// Gets the <see cref="Type"/> defining the model property respresented by the current
/// instance, or <c>null</c> if the current instance does not represent a property.
/// </summary>
public Type ContainerType { get; private set; }
/// <summary>
/// Gets the <see cref="ParameterInfo"/> represented by the current instance, or <c>null</c>
/// if the current instance does not represent a parameter.
/// </summary>
public ParameterInfo ParameterInfo { get; private set; }
/// <summary>
/// Gets the <see cref="Type"/> represented by the current instance.
/// </summary>
public Type ModelType { get; private set; }
/// <summary>
/// Gets a value indicating the kind of metadata represented by the current instance.
/// </summary>
public ModelMetadataKind MetadataKind
{
get
{
if (ParameterInfo != null)
{
return ModelMetadataKind.Parameter;
}
else if (ContainerType != null && Name != null)
{
return ModelMetadataKind.Property;
}
else
{
return ModelMetadataKind.Type;
}
}
}
/// <summary>
/// Gets the name of the current instance if it represents a parameter or property, or <c>null</c> if
/// the current instance represents a type.
/// </summary>
public string Name { get; private set; }
}
}

View File

@ -0,0 +1,26 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
{
/// <summary>
/// Enumeration for the kinds of <see cref="ModelMetadata"/>
/// </summary>
public enum ModelMetadataKind
{
/// <summary>
/// Used for <see cref="ModelMetadata"/> for a <see cref="System.Type"/>.
/// </summary>
Type,
/// <summary>
/// Used for <see cref="ModelMetadata"/> for a property.
/// </summary>
Property,
/// <summary>
/// Used for <see cref="ModelMetadata"/> for a parameter.
/// </summary>
Parameter,
}
}

View File

@ -0,0 +1,12 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
{
/// <summary>
/// Validation metadata details for a <see cref="ModelMetadata"/>.
/// </summary>
public class ValidationMetadata
{
}
}

View File

@ -0,0 +1,43 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
{
/// <summary>
/// A context for an <see cref="IValidationMetadataProvider"/>.
/// </summary>
public class ValidationMetadataProviderContext
{
/// <summary>
/// Creates a new <see cref="ValidationMetadataProviderContext"/>.
/// </summary>
/// <param name="key">The <see cref="ModelMetadataIdentity"/> for the <see cref="ModelMetadata"/>.</param>
/// <param name="attributes">The attributes for the <see cref="ModelMetadata"/>.</param>
public ValidationMetadataProviderContext(
[NotNull] ModelMetadataIdentity key,
[NotNull] IReadOnlyList<object> attributes)
{
Key = key;
Attributes = attributes;
ValidationMetadata = new ValidationMetadata();
}
/// <summary>
/// Gets the attributes.
/// </summary>
public IReadOnlyList<object> Attributes { get; }
/// <summary>
/// Gets the <see cref="ModelMetadataIdentity"/>.
/// </summary>
public ModelMetadataIdentity Key { get; }
/// <summary>
/// Gets the <see cref="Metadata.ValidationMetadata"/>.
/// </summary>
public ValidationMetadata ValidationMetadata { get; }
}
}

View File

@ -0,0 +1,247 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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.Generic;
using Microsoft.AspNet.Mvc.ModelBinding.Metadata;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ModelBinding
{
/// <summary>
/// A metadata representation of a model type, property or parameter.
/// </summary>
public abstract class ModelMetadata
{
/// <summary>
/// The default value of <see cref="ModelMetadata.Order"/>.
/// </summary>
public static readonly int DefaultOrder = 10000;
/// <summary>
/// Creates a new <see cref="ModelMetadata"/>.
/// </summary>
/// <param name="identity">The <see cref="ModelMetadataIdentity"/>.</param>
protected ModelMetadata([NotNull] ModelMetadataIdentity identity)
{
Identity = identity;
}
/// <summary>
/// Gets the container type of this metadata if it represents a property, otherwise <c>null</c>.
/// </summary>
public Type ContainerType { get { return Identity.ContainerType; } }
/// <summary>
/// Gets a value indicating the kind of metadata element represented by the current instance.
/// </summary>
public ModelMetadataKind MetadataKind { get { return Identity.MetadataKind; } }
/// <summary>
/// Gets the model type represented by the current instance.
/// </summary>
public Type ModelType { get { return Identity.ModelType; } }
/// <summary>
/// Gets the property name represented by the current instance.
/// </summary>
public string PropertyName
{
get
{
return Identity.Name;
}
}
/// <summary>
/// Gets the key for the current instance.
/// </summary>
protected ModelMetadataIdentity Identity { get; }
/// <summary>
/// Gets a collection of additional information about the model.
/// </summary>
public abstract IReadOnlyDictionary<object, object> AdditionalValues { get; }
/// <summary>
/// Gets the collection of <see cref="ModelMetadata"/> instances for the model's properties.
/// </summary>
public abstract ModelPropertyCollection Properties { get; }
/// <summary>
/// Gets the name of a model if specified explicitly using <see cref="IModelNameProvider"/>.
/// </summary>
public abstract string BinderModelName { get; }
/// <summary>
/// Gets the <see cref="Type"/> of an <see cref="IModelBinder"/> or an
/// <see cref="IModelBinderProvider"/> of a model if specified explicitly using
/// <see cref="IBinderTypeProviderMetadata"/>.
/// </summary>
public abstract Type BinderType { get; }
/// <summary>
/// Gets a binder metadata for this model.
/// </summary>
public abstract BindingSource BindingSource { get; }
/// <summary>
/// Gets a value indicating whether or not to convert an empty string value to <c>null</c> when
/// representing a model as text.
/// </summary>
public abstract bool ConvertEmptyStringToNull { get; }
/// <summary>
/// Gets the name of the <see cref="Model"/>'s datatype. Overrides <see cref="ModelType"/> in some
/// display scenarios.
/// </summary>
/// <value><c>null</c> unless set manually or through additional metadata e.g. attributes.</value>
public abstract string DataTypeName { get; }
/// <summary>
/// Gets the description of the model.
/// </summary>
public abstract string Description { get; }
/// <summary>
/// Gets 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 abstract string DisplayFormatString { get; }
/// <summary>
/// Gets the display name of the model.
/// </summary>
public abstract string DisplayName { get; }
/// <summary>
/// Gets the composite format <see cref="string"/> (see
/// http://msdn.microsoft.com/en-us/library/txafckwd.aspx) used to edit the <see cref="Model"/>.
/// </summary>
public abstract string EditFormatString { get; }
/// <summary>
/// Gets 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 abstract bool HasNonDefaultEditFormat { get; }
/// <summary>
/// Gets a value indicating whether the value should be HTML-encoded.
/// </summary>
/// <value>If <c>true</c>, value should be HTML-encoded. Default is <c>true</c>.</value>
public abstract bool HtmlEncode { get; }
/// <summary>
/// Gets 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
/// also return the expression value (together with the hidden &lt;input&gt; element).
/// </summary>
/// <remarks>
/// If <c>true</c>, also causes the default <see cref="object"/> display and editor templates to return HTML
/// lacking the usual per-property &lt;div&gt; wrapper around the associated property. Thus the default
/// <see cref="object"/> display template effectively skips the property and the default <see cref="object"/>
/// editor template returns only the hidden &lt;input&gt; element for the property.
/// </remarks>
public abstract bool HideSurroundingHtml { get; }
/// <summary>
/// Gets a value indicating whether or not the model value is read-only. This is only applicable when
/// the current instance represents a property.
/// </summary>
public abstract bool IsReadOnly { get; }
/// <summary>
/// Gets a value indicating whether or not the model value is required. This is only applicable when
/// the current instance represents a property.
/// </summary>
public abstract bool IsRequired { get; }
/// <summary>
/// Gets a value indicating where the current metadata should be ordered relative to other properties
/// in its containing type.
/// </summary>
/// <remarks>
/// <para>For example this property is used to order items in <see cref="Properties"/>.</para>
/// <para>The default order is <c>10000</c>.</para>
/// </remarks>
/// <value>The order value of the current metadata.</value>
public abstract int Order { get; }
/// <summary>
/// Gets the text to display when the model is <c>null</c>.
/// </summary>
public abstract string NullDisplayText { get; }
/// <summary>
/// Gets the <see cref="IPropertyBindingPredicateProvider"/>, which can determine which properties
/// should be model bound.
/// </summary>
public abstract IPropertyBindingPredicateProvider PropertyBindingPredicateProvider { get; }
/// <summary>
/// Gets a value that indicates whether the property should be displayed in read-only views.
/// </summary>
public abstract bool ShowForDisplay { get; }
/// <summary>
/// Gets a value that indicates whether the property should be displayed in editable views.
/// </summary>
public abstract bool ShowForEdit { get; }
/// <summary>
/// Gets a value which is the name of the property used to display the model.
/// </summary>
public abstract string SimpleDisplayProperty { get; }
/// <summary>
/// Gets a string used by the templating system to discover display-templates and editor-templates.
/// </summary>
public abstract string TemplateHint { get; }
/// <summary>
/// Gets a value indicating whether <see cref="ModelType"/> is a simple type.
/// </summary>
/// <remarks>
/// A simple type is defined as a <see cref="Type"/> which has a
/// <see cref="System.ComponentModel.TypeConverter"/> that can convert from <see cref="string"/>.
/// </remarks>
public bool IsComplexType
{
get { return !TypeHelper.HasStringConverter(ModelType); }
}
/// <summary>
/// Gets a value indicating whether or not <see cref="ModelType"/> is a <see cref="Nullable{T}"/>.
/// </summary>
public bool IsNullableValueType
{
get { return ModelType.IsNullableValueType(); }
}
/// <summary>
/// Gets a value indicating whether or not <see cref="ModelType"/> is a collection type.
/// </summary>
/// <remarks>
/// A collection type is defined as a <see cref="Type"/> which is assignable to
/// <see cref="System.Collections.IEnumerable"/>, and is not a <see cref="string"/>.
/// </remarks>
public bool IsCollectionType
{
get { return TypeHelper.IsCollectionType(ModelType); }
}
/// <summary>
/// Gets a display name for the model.
/// </summary>
/// <remarks>
/// <see cref="GetDisplayName()"/> will return the first of the following expressions which has a
/// non-<c>null</c> value: <c>DisplayName</c>, <c>PropertyName</c>, <c>ModelType.Name</c>.
/// </remarks>
/// <returns>The display name.</returns>
public string GetDisplayName()
{
return DisplayName ?? PropertyName ?? ModelType.Name;
}
}
}

View File

@ -55,9 +55,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
RuntimeHelpers.EnsureSufficientExecutionStack();
var modelState = validationContext.ModelValidationContext.ModelState;
var bindingSourceMetadata = modelExplorer.Metadata.BinderMetadata as IBindingSourceMetadata;
var bindingSource = bindingSourceMetadata?.BindingSource;
var bindingSource = modelExplorer.Metadata.BindingSource;
if (bindingSource != null && !bindingSource.IsFromRequest)
{
// Short circuit if the metadata represents something that was not bound using request data.

View File

@ -27,6 +27,7 @@
"System.Collections.Concurrent": "4.0.10-beta-*",
"System.ComponentModel.Annotations": "4.0.10-beta-*",
"System.ComponentModel.TypeConverter": "4.0.0-beta-*",
"System.ObjectModel": "4.0.10-beta-*",
"System.Reflection.TypeExtensions": "4.0.0-beta-*",
"System.Runtime.Serialization.Primitives": "4.0.0-beta-*"
}

View File

@ -91,7 +91,8 @@ namespace Microsoft.AspNet.Mvc.WebApiCompatShim
foreach (var parameter in candidate.Action.Parameters)
{
// We only consider parameters that are marked as bound from the URL.
var source = BindingSource.GetBindingSource(parameter.BinderMetadata);
var bindingSourceMetadata = parameter?.BinderMetadata as IBindingSourceMetadata;
var source = bindingSourceMetadata?.BindingSource;
if (source == null)
{
continue;

View File

@ -4,6 +4,7 @@
using System;
using System.Xml.Linq;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.ModelBinding.Metadata;
using Microsoft.AspNet.Mvc.Razor;
using Microsoft.Framework.OptionsModel;
using Microsoft.Net.Http.Headers;
@ -59,6 +60,10 @@ namespace Microsoft.AspNet.Mvc
options.ValueProviderFactories.Add(new QueryStringValueProviderFactory());
options.ValueProviderFactories.Add(new FormValueProviderFactory());
// Set up metadata providers
options.ModelMetadataDetailsProviders.Add(new DefaultBindingMetadataProvider());
options.ModelMetadataDetailsProviders.Add(new DataAnnotationsMetadataDetailsProvider());
// Set up validators
options.ModelValidatorProviders.Add(new DataAnnotationsModelValidatorProvider());
options.ModelValidatorProviders.Add(new DataMemberModelValidatorProvider());

View File

@ -9,6 +9,7 @@ using Microsoft.AspNet.Mvc.Description;
using Microsoft.AspNet.Mvc.Filters;
using Microsoft.AspNet.Mvc.Internal;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.ModelBinding.Metadata;
using Microsoft.AspNet.Mvc.OptionDescriptors;
using Microsoft.AspNet.Mvc.Razor;
using Microsoft.AspNet.Mvc.Razor.Directives;
@ -81,10 +82,16 @@ namespace Microsoft.AspNet.Mvc
yield return describe.Transient<IFilterProvider, DefaultFilterProvider>();
yield return describe.Transient<FormatFilter, FormatFilter>();
// Dataflow - ModelBinding, Validation and Formatting
// The DataAnnotationsModelMetadataProvider does significant caching of reflection/attributes
// and thus needs to be singleton.
yield return describe.Singleton<IModelMetadataProvider, DataAnnotationsModelMetadataProvider>();
//
// The DefaultModelMetadataProvider does significant caching and should be a singleton.
yield return describe.Singleton<ModelBinding.IModelMetadataProvider, DefaultModelMetadataProvider>();
yield return describe.Transient<ModelBinding.Metadata.ICompositeMetadataDetailsProvider>(services =>
{
var options = services.GetRequiredService<IOptions<MvcOptions>>().Options;
return new DefaultCompositeMetadataDetailsProvider(options.ModelMetadataDetailsProviders);
});
yield return describe.Transient<IInputFormatterSelector, DefaultInputFormatterSelector>();
yield return describe.Scoped<IInputFormattersProvider, DefaultInputFormattersProvider>();

View File

@ -29,8 +29,10 @@ namespace Microsoft.AspNet.Mvc
.Returns(Task.FromResult<object>(new Person()))
.Verifiable();
var bindingContext = GetBindingContext(typeof(Person), inputFormatter: mockInputFormatter.Object);
bindingContext.ModelMetadata.BinderMetadata = new FromBodyAttribute();
var provider = new TestModelMetadataProvider();
provider.ForType<Person>().BindingDetails(d => d.BindingSource = BindingSource.Body);
var bindingContext = GetBindingContext(typeof(Person), metadataProvider: provider);
var binder = GetBodyBinder(mockInputFormatter.Object);
@ -47,8 +49,10 @@ namespace Microsoft.AspNet.Mvc
public async Task BindModel_NoInputFormatterFound_SetsModelStateError()
{
// Arrange
var bindingContext = GetBindingContext(typeof(Person), inputFormatter: null);
bindingContext.ModelMetadata.BinderMetadata = new FromBodyAttribute();
var provider = new TestModelMetadataProvider();
provider.ForType<Person>().BindingDetails(d => d.BindingSource = BindingSource.Body);
var bindingContext = GetBindingContext(typeof(Person), metadataProvider: provider);
var binder = bindingContext.OperationBindingContext.ModelBinder;
@ -68,11 +72,10 @@ namespace Microsoft.AspNet.Mvc
public async Task BindModel_IsGreedy()
{
// Arrange
var metadata = new Mock<IBindingSourceMetadata>();
metadata.SetupGet(m => m.BindingSource).Returns(BindingSource.Body);
var provider = new TestModelMetadataProvider();
provider.ForType<Person>().BindingDetails(d => d.BindingSource = BindingSource.Body);
var bindingContext = GetBindingContext(typeof(Person), inputFormatter: null);
bindingContext.ModelMetadata.BinderMetadata = metadata.Object;
var bindingContext = GetBindingContext(typeof(Person), metadataProvider: provider);
var binder = bindingContext.OperationBindingContext.ModelBinder;
@ -88,11 +91,10 @@ namespace Microsoft.AspNet.Mvc
public async Task BindModel_IsGreedy_IgnoresWrongSource()
{
// Arrange
var metadata = new Mock<IBindingSourceMetadata>();
metadata.SetupGet(m => m.BindingSource).Returns(BindingSource.Header);
var provider = new TestModelMetadataProvider();
provider.ForType<Person>().BindingDetails(d => d.BindingSource = BindingSource.Header);
var bindingContext = GetBindingContext(typeof(Person), inputFormatter: null);
bindingContext.ModelMetadata.BinderMetadata = metadata.Object;
var bindingContext = GetBindingContext(typeof(Person), metadataProvider: provider);
var binder = bindingContext.OperationBindingContext.ModelBinder;
@ -107,11 +109,10 @@ namespace Microsoft.AspNet.Mvc
public async Task BindModel_IsGreedy_IgnoresMetadataWithNoSource()
{
// Arrange
var metadata = new Mock<IBindingSourceMetadata>();
metadata.SetupGet(m => m.BindingSource).Returns((BindingSource)null);
var provider = new TestModelMetadataProvider();
provider.ForType<Person>().BindingDetails(d => d.BindingSource = null);
var bindingContext = GetBindingContext(typeof(Person), inputFormatter: null);
bindingContext.ModelMetadata.BinderMetadata = metadata.Object;
var bindingContext = GetBindingContext(typeof(Person), metadataProvider: provider);
var binder = bindingContext.OperationBindingContext.ModelBinder;
@ -129,8 +130,15 @@ namespace Microsoft.AspNet.Mvc
var httpContext = new DefaultHttpContext();
httpContext.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes("Bad data!"));
httpContext.Request.ContentType = "text/xyz";
var bindingContext = GetBindingContext(httpContext, typeof(Person), inputFormatter: new XyzFormatter());
bindingContext.ModelMetadata.BinderMetadata = new FromBodyAttribute();
var provider = new TestModelMetadataProvider();
provider.ForType<Person>().BindingDetails(d => d.BindingSource = BindingSource.Body);
var bindingContext = GetBindingContext(
typeof(Person),
inputFormatter: new XyzFormatter(),
httpContext: httpContext,
metadataProvider: provider);
var binder = bindingContext.OperationBindingContext.ModelBinder;
@ -148,17 +156,22 @@ namespace Microsoft.AspNet.Mvc
Assert.Equal("Your input is bad!", errorMessage);
}
private static ModelBindingContext GetBindingContext(Type modelType, IInputFormatter inputFormatter)
{
return GetBindingContext(new DefaultHttpContext(), modelType, inputFormatter);
}
private static ModelBindingContext GetBindingContext(
HttpContext httpContext,
Type modelType,
IInputFormatter inputFormatter)
Type modelType,
IInputFormatter inputFormatter = null,
HttpContext httpContext = null,
IModelMetadataProvider metadataProvider = null)
{
var metadataProvider = new EmptyModelMetadataProvider();
if (httpContext == null)
{
httpContext = new DefaultHttpContext();
}
if (metadataProvider == null)
{
metadataProvider = new EmptyModelMetadataProvider();
}
var operationBindingContext = new OperationBindingContext
{
ModelBinder = GetBodyBinder(httpContext, inputFormatter),

View File

@ -14,7 +14,6 @@ using Microsoft.AspNet.Routing;
using Microsoft.AspNet.Testing;
using Microsoft.AspNet.WebUtilities;
using Microsoft.AspNet.Http.Core;
using Microsoft.Framework.DependencyInjection;
#if ASPNET50
using Moq;
#endif
@ -41,7 +40,7 @@ namespace Microsoft.AspNet.Mvc.Test
public void SettingViewData_AlsoUpdatesViewBag()
{
// Arrange
var metadataProvider = new DataAnnotationsModelMetadataProvider();
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var controller = new TestableController();
var originalViewData = controller.ViewData = new ViewDataDictionary<object>(metadataProvider);
var replacementViewData = new ViewDataDictionary<object>(metadataProvider);
@ -920,7 +919,7 @@ namespace Microsoft.AspNet.Mvc.Test
public async Task TryUpdateModel_FallsBackOnEmptyPrefix_IfNotSpecified()
{
// Arrange
var metadataProvider = new DataAnnotationsModelMetadataProvider();
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var valueProvider = Mock.Of<IValueProvider>();
var binder = new Mock<IModelBinder>();
binder.Setup(b => b.BindModelAsync(It.IsAny<ModelBindingContext>()))
@ -953,7 +952,7 @@ namespace Microsoft.AspNet.Mvc.Test
// Arrange
var modelName = "mymodel";
var metadataProvider = new DataAnnotationsModelMetadataProvider();
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var valueProvider = Mock.Of<IValueProvider>();
var binder = new Mock<IModelBinder>();
binder.Setup(b => b.BindModelAsync(It.IsAny<ModelBindingContext>()))
@ -1199,7 +1198,7 @@ namespace Microsoft.AspNet.Mvc.Test
// Arrange
var modelName = "mymodel";
var metadataProvider = new DataAnnotationsModelMetadataProvider();
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var valueProvider = Mock.Of<IValueProvider>();
var binder = new Mock<IModelBinder>();
binder.Setup(b => b.BindModelAsync(It.IsAny<ModelBindingContext>()))
@ -1232,7 +1231,7 @@ namespace Microsoft.AspNet.Mvc.Test
// Arrange
var modelName = "mymodel";
var metadataProvider = new DataAnnotationsModelMetadataProvider();
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var valueProvider = Mock.Of<IValueProvider>();
var binder = new Mock<IModelBinder>();
binder.Setup(b => b.BindModelAsync(It.IsAny<ModelBindingContext>()))
@ -1456,7 +1455,7 @@ namespace Microsoft.AspNet.Mvc.Test
private static Controller GetController(IModelBinder binder, IValueProvider provider)
{
var metadataProvider = new DataAnnotationsModelMetadataProvider();
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var httpContext = new DefaultHttpContext();
var actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor());

View File

@ -918,7 +918,7 @@ namespace Microsoft.AspNet.Mvc.Description
constraintResolver.Setup(c => c.ResolveConstraint("int"))
.Returns(new IntRouteConstraint());
var modelMetadataProvider = new DataAnnotationsModelMetadataProvider();
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var provider = new DefaultApiDescriptionProvider(
formattersProvider.Object,

View File

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Http.Core;
using Microsoft.AspNet.Mvc.ModelBinding;
@ -51,7 +52,7 @@ namespace Microsoft.AspNet.Mvc.Core.Test
public void GetModelBindingContext_ReturnsOnlyIncludedProperties_UsingBindAttributeInclude()
{
// Arrange
var metadataProvider = new DataAnnotationsModelMetadataProvider();
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var modelMetadata = metadataProvider.GetMetadataForType(
typeof(TypeWithIncludedPropertiesUsingBindAttribute));
@ -72,11 +73,12 @@ namespace Microsoft.AspNet.Mvc.Core.Test
// Arrange
var type = typeof(ControllerActionArgumentBinderTests);
var methodInfo = type.GetMethod("ParameterWithNoBindAttribute");
var parameterInfo = methodInfo.GetParameters().Where(p => p.Name == "parameter").Single();
var metadataProvider = new DataAnnotationsModelMetadataProvider();
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var modelMetadata = metadataProvider.GetMetadataForParameter(
methodInfo: methodInfo,
parameterName: "parameter");
parameterInfo,
attributes: null);
// Act
var context = DefaultControllerActionArgumentBinder.GetModelBindingContext(
@ -101,11 +103,12 @@ namespace Microsoft.AspNet.Mvc.Core.Test
// Arrange
var type = typeof(ControllerActionArgumentBinderTests);
var methodInfo = type.GetMethod(actionMethodName);
var parameterInfo = methodInfo.GetParameters().Where(p => p.Name == "parameter").Single();
var metadataProvider = new DataAnnotationsModelMetadataProvider();
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var modelMetadata = metadataProvider.GetMetadataForParameter(
methodInfo: methodInfo,
parameterName: "parameter");
parameterInfo,
attributes: null);
// Act
var context = DefaultControllerActionArgumentBinder.GetModelBindingContext(
@ -130,11 +133,12 @@ namespace Microsoft.AspNet.Mvc.Core.Test
// Arrange
var type = typeof(ControllerActionArgumentBinderTests);
var methodInfo = type.GetMethod(actionMethodName);
var parameterInfo = methodInfo.GetParameters().Where(p => p.Name == "parameter1").Single();
var metadataProvider = new DataAnnotationsModelMetadataProvider();
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var modelMetadata = metadataProvider.GetMetadataForParameter(
methodInfo: methodInfo,
parameterName: "parameter1");
parameterInfo,
attributes: null);
// Act
var context = DefaultControllerActionArgumentBinder.GetModelBindingContext(
@ -180,7 +184,7 @@ namespace Microsoft.AspNet.Mvc.Core.Test
ModelBinder = binder.Object,
};
var modelMetadataProvider = new DataAnnotationsModelMetadataProvider();
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var inputFormattersProvider = new Mock<IInputFormattersProvider>();
inputFormattersProvider
.SetupGet(o => o.InputFormatters)
@ -235,7 +239,7 @@ namespace Microsoft.AspNet.Mvc.Core.Test
.SetupGet(o => o.InputFormatters)
.Returns(new List<IInputFormatter>());
var modelMetadataProvider = new DataAnnotationsModelMetadataProvider();
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var invoker = new DefaultControllerActionArgumentBinder(
modelMetadataProvider,
new DefaultObjectValidator(Mock.Of<IValidationExcludeFiltersProvider>(), modelMetadataProvider),
@ -345,7 +349,7 @@ namespace Microsoft.AspNet.Mvc.Core.Test
.Setup(o => o.Validate(It.IsAny<ModelValidationContext>()))
.Verifiable();
var invoker = new DefaultControllerActionArgumentBinder(
new DataAnnotationsModelMetadataProvider(),
TestModelMetadataProvider.CreateDefaultProvider(),
mockValidatorProvider.Object,
new MockMvcOptionsAccessor());
@ -394,7 +398,7 @@ namespace Microsoft.AspNet.Mvc.Core.Test
mockValidatorProvider.Setup(o => o.Validate(It.IsAny<ModelValidationContext>()))
.Verifiable();
var invoker = new DefaultControllerActionArgumentBinder(
new DataAnnotationsModelMetadataProvider(),
TestModelMetadataProvider.CreateDefaultProvider(),
mockValidatorProvider.Object,
new MockMvcOptionsAccessor());
@ -449,7 +453,7 @@ namespace Microsoft.AspNet.Mvc.Core.Test
var mockValidatorProvider = new Mock<IObjectModelValidator>(MockBehavior.Strict);
mockValidatorProvider.Setup(o => o.Validate(It.IsAny<ModelValidationContext>()));
var invoker = new DefaultControllerActionArgumentBinder(
new DataAnnotationsModelMetadataProvider(),
TestModelMetadataProvider.CreateDefaultProvider(),
mockValidatorProvider.Object,
options);

View File

@ -23,10 +23,7 @@ namespace Microsoft.AspNet.Mvc.Core.Test
public async Task TryUpdateModel_ReturnsFalse_IfBinderReturnsFalse()
{
// Arrange
var metadataProvider = new Mock<IModelMetadataProvider>();
metadataProvider.Setup(m => m.GetMetadataForType(It.IsAny<Type>()))
.Returns(new ModelMetadata(metadataProvider.Object, null, typeof(MyModel), null))
.Verifiable();
var metadataProvider = new EmptyModelMetadataProvider();
var binder = new Mock<IModelBinder>();
binder.Setup(b => b.BindModelAsync(It.IsAny<ModelBindingContext>()))
@ -39,16 +36,15 @@ namespace Microsoft.AspNet.Mvc.Core.Test
null,
Mock.Of<HttpContext>(),
new ModelStateDictionary(),
metadataProvider.Object,
metadataProvider,
GetCompositeBinder(binder.Object),
Mock.Of<IValueProvider>(),
new DefaultObjectValidator(Mock.Of<IValidationExcludeFiltersProvider>(), metadataProvider.Object),
new DefaultObjectValidator(Mock.Of<IValidationExcludeFiltersProvider>(), metadataProvider),
Mock.Of<IModelValidatorProvider>());
// Assert
Assert.False(result);
Assert.Null(model.MyProperty);
metadataProvider.Verify();
}
[Fact]
@ -72,7 +68,7 @@ namespace Microsoft.AspNet.Mvc.Core.Test
{ "", null }
};
var valueProvider = new TestValueProvider(values);
var modelMetadataProvider = new DataAnnotationsModelMetadataProvider();
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
// Act
var result = await ModelBindingHelper.TryUpdateModelAsync(
@ -112,7 +108,7 @@ namespace Microsoft.AspNet.Mvc.Core.Test
{ "MyProperty", "MyPropertyValue" }
};
var valueProvider = new TestValueProvider(values);
var metadataProvider = new DataAnnotationsModelMetadataProvider();
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
// Act
var result = await ModelBindingHelper.TryUpdateModelAsync(
@ -135,10 +131,7 @@ namespace Microsoft.AspNet.Mvc.Core.Test
public async Task TryUpdateModel_UsingIncludePredicateOverload_ReturnsFalse_IfBinderReturnsFalse()
{
// Arrange
var metadataProvider = new Mock<IModelMetadataProvider>();
metadataProvider.Setup(m => m.GetMetadataForType(It.IsAny<Type>()))
.Returns(new ModelMetadata(metadataProvider.Object, null, typeof(MyModel), null))
.Verifiable();
var metadataProvider = new EmptyModelMetadataProvider();
var binder = new Mock<IModelBinder>();
binder.Setup(b => b.BindModelAsync(It.IsAny<ModelBindingContext>()))
@ -152,7 +145,7 @@ namespace Microsoft.AspNet.Mvc.Core.Test
null,
Mock.Of<HttpContext>(),
new ModelStateDictionary(),
metadataProvider.Object,
metadataProvider,
GetCompositeBinder(binder.Object),
Mock.Of<IValueProvider>(),
Mock.Of<IObjectModelValidator>(),
@ -164,7 +157,6 @@ namespace Microsoft.AspNet.Mvc.Core.Test
Assert.Null(model.MyProperty);
Assert.Null(model.IncludedProperty);
Assert.Null(model.ExcludedProperty);
metadataProvider.Verify();
}
[Fact]
@ -200,7 +192,7 @@ namespace Microsoft.AspNet.Mvc.Core.Test
string.Equals(propertyName, "MyProperty", StringComparison.OrdinalIgnoreCase);
var valueProvider = new TestValueProvider(values);
var metadataProvider = new DataAnnotationsModelMetadataProvider();
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
// Act
var result = await ModelBindingHelper.TryUpdateModelAsync(
@ -226,10 +218,7 @@ namespace Microsoft.AspNet.Mvc.Core.Test
public async Task TryUpdateModel_UsingIncludeExpressionOverload_ReturnsFalse_IfBinderReturnsFalse()
{
// Arrange
var metadataProvider = new Mock<IModelMetadataProvider>();
metadataProvider.Setup(m => m.GetMetadataForType(It.IsAny<Type>()))
.Returns(new ModelMetadata(metadataProvider.Object, null, typeof(MyModel), null))
.Verifiable();
var metadataProvider = new EmptyModelMetadataProvider();
var binder = new Mock<IModelBinder>();
binder.Setup(b => b.BindModelAsync(It.IsAny<ModelBindingContext>()))
@ -242,7 +231,7 @@ namespace Microsoft.AspNet.Mvc.Core.Test
null,
Mock.Of<HttpContext>(),
new ModelStateDictionary(),
metadataProvider.Object,
metadataProvider,
GetCompositeBinder(binder.Object),
Mock.Of<IValueProvider>(),
Mock.Of<IObjectModelValidator>(),
@ -254,7 +243,6 @@ namespace Microsoft.AspNet.Mvc.Core.Test
Assert.Null(model.MyProperty);
Assert.Null(model.IncludedProperty);
Assert.Null(model.ExcludedProperty);
metadataProvider.Verify();
}
[Fact]
@ -286,7 +274,7 @@ namespace Microsoft.AspNet.Mvc.Core.Test
};
var valueProvider = new TestValueProvider(values);
var metadataProvider = new DataAnnotationsModelMetadataProvider();
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
// Act
var result = await ModelBindingHelper.TryUpdateModelAsync(
@ -294,7 +282,7 @@ namespace Microsoft.AspNet.Mvc.Core.Test
"",
Mock.Of<HttpContext>(),
modelStateDictionary,
new DataAnnotationsModelMetadataProvider(),
TestModelMetadataProvider.CreateDefaultProvider(),
GetCompositeBinder(binders),
valueProvider,
new DefaultObjectValidator(Mock.Of<IValidationExcludeFiltersProvider>(), metadataProvider),
@ -338,7 +326,7 @@ namespace Microsoft.AspNet.Mvc.Core.Test
};
var valueProvider = new TestValueProvider(values);
var metadataProvider = new DataAnnotationsModelMetadataProvider();
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
// Act
var result = await ModelBindingHelper.TryUpdateModelAsync(
@ -483,11 +471,7 @@ namespace Microsoft.AspNet.Mvc.Core.Test
public async Task TryUpdateModelNonGeneric_PredicateOverload_ReturnsFalse_IfBinderReturnsFalse()
{
// Arrange
var metadataProvider = new Mock<IModelMetadataProvider>();
metadataProvider.Setup(m => m.GetMetadataForType(It.IsAny<Type>()))
.Returns(new ModelMetadata(metadataProvider.Object, null, typeof(MyModel), null))
.Verifiable();
var metadataProvider = new EmptyModelMetadataProvider();
var binder = new Mock<IModelBinder>();
binder.Setup(b => b.BindModelAsync(It.IsAny<ModelBindingContext>()))
.Returns(Task.FromResult<ModelBindingResult>(null));
@ -501,7 +485,7 @@ namespace Microsoft.AspNet.Mvc.Core.Test
prefix: null,
httpContext: Mock.Of<HttpContext>(),
modelState: new ModelStateDictionary(),
metadataProvider: metadataProvider.Object,
metadataProvider: metadataProvider,
modelBinder: GetCompositeBinder(binder.Object),
valueProvider: Mock.Of<IValueProvider>(),
objectModelValidator: Mock.Of<IObjectModelValidator>(),
@ -513,7 +497,6 @@ namespace Microsoft.AspNet.Mvc.Core.Test
Assert.Null(model.MyProperty);
Assert.Null(model.IncludedProperty);
Assert.Null(model.ExcludedProperty);
metadataProvider.Verify();
}
[Fact]
@ -550,7 +533,7 @@ namespace Microsoft.AspNet.Mvc.Core.Test
string.Equals(propertyName, "MyProperty", StringComparison.OrdinalIgnoreCase);
var valueProvider = new TestValueProvider(values);
var metadataProvider = new DataAnnotationsModelMetadataProvider();
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
// Act
var result = await ModelBindingHelper.TryUpdateModelAsync(
@ -579,10 +562,7 @@ namespace Microsoft.AspNet.Mvc.Core.Test
public async Task TryUpdateModelNonGeneric_ModelTypeOverload_ReturnsFalse_IfBinderReturnsFalse()
{
// Arrange
var metadataProvider = new Mock<IModelMetadataProvider>();
metadataProvider.Setup(m => m.GetMetadataForType(It.IsAny<Type>()))
.Returns(new ModelMetadata(metadataProvider.Object, null, typeof(MyModel), null))
.Verifiable();
var metadataProvider = new EmptyModelMetadataProvider();
var binder = new Mock<IModelBinder>();
binder.Setup(b => b.BindModelAsync(It.IsAny<ModelBindingContext>()))
@ -596,7 +576,7 @@ namespace Microsoft.AspNet.Mvc.Core.Test
prefix: null,
httpContext: Mock.Of<HttpContext>(),
modelState: new ModelStateDictionary(),
metadataProvider: metadataProvider.Object,
metadataProvider: metadataProvider,
modelBinder: GetCompositeBinder(binder.Object),
valueProvider: Mock.Of<IValueProvider>(),
objectModelValidator: Mock.Of<IObjectModelValidator>(),
@ -605,7 +585,6 @@ namespace Microsoft.AspNet.Mvc.Core.Test
// Assert
Assert.False(result);
Assert.Null(model.MyProperty);
metadataProvider.Verify();
}
[Fact]
@ -628,7 +607,7 @@ namespace Microsoft.AspNet.Mvc.Core.Test
{ "MyProperty", "MyPropertyValue" }
};
var valueProvider = new TestValueProvider(values);
var metadataProvider = new DataAnnotationsModelMetadataProvider();
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
// Act
var result = await ModelBindingHelper.TryUpdateModelAsync(
@ -637,7 +616,7 @@ namespace Microsoft.AspNet.Mvc.Core.Test
"",
Mock.Of<HttpContext>(),
modelStateDictionary,
new DataAnnotationsModelMetadataProvider(),
TestModelMetadataProvider.CreateDefaultProvider(),
GetCompositeBinder(binders),
valueProvider,
new DefaultObjectValidator(
@ -654,15 +633,7 @@ namespace Microsoft.AspNet.Mvc.Core.Test
public async Task TryUpdataModel_ModelTypeDifferentFromModel_Throws()
{
// Arrange
var metadataProvider = new Mock<IModelMetadataProvider>();
metadataProvider
.Setup(m => m.GetMetadataForType(It.IsAny<Type>()))
.Returns(new ModelMetadata(
metadataProvider.Object,
containerType: null,
modelType: typeof(MyModel),
propertyName: null))
.Verifiable();
var metadataProvider = new EmptyModelMetadataProvider();
var binder = new Mock<IModelBinder>();
binder.Setup(b => b.BindModelAsync(It.IsAny<ModelBindingContext>()))
@ -679,12 +650,12 @@ namespace Microsoft.AspNet.Mvc.Core.Test
null,
Mock.Of<HttpContext>(),
new ModelStateDictionary(),
metadataProvider.Object,
metadataProvider,
GetCompositeBinder(binder.Object),
Mock.Of<IValueProvider>(),
new DefaultObjectValidator(
Mock.Of<IValidationExcludeFiltersProvider>(),
metadataProvider.Object),
metadataProvider),
Mock.Of<IModelValidatorProvider>(),
includePredicate));

View File

@ -70,9 +70,13 @@ namespace Microsoft.AspNet.Mvc.Core
public void ObjectTemplateDisplaysNullDisplayTextWhenObjectIsNull()
{
// Arrange
var html = DefaultTemplatesUtilities.GetHtmlHelper();
var provider = new TestModelMetadataProvider();
provider.ForType<DefaultTemplatesUtilities.ObjectTemplateModel>().DisplayDetails(dd =>
{
dd.NullDisplayText = "(null value)";
});
html.ViewData.ModelMetadata.NullDisplayText = "(null value)";
var html = DefaultTemplatesUtilities.GetHtmlHelper(provider: provider);
// Act
var result = DefaultDisplayTemplates.ObjectTemplate(html);
@ -92,10 +96,14 @@ namespace Microsoft.AspNet.Mvc.Core
var model = new DefaultTemplatesUtilities.ObjectTemplateModel();
model.Property1 = simpleDisplayText;
var html = DefaultTemplatesUtilities.GetHtmlHelper(model);
var provider = new TestModelMetadataProvider();
provider.ForType<DefaultTemplatesUtilities.ObjectTemplateModel>().DisplayDetails(dd =>
{
dd.HtmlEncode = htmlEncode;
dd.SimpleDisplayProperty = "Property1";
});
html.ViewData.ModelMetadata.HtmlEncode = htmlEncode;
html.ViewData.ModelMetadata.SimpleDisplayProperty = "Property1";
var html = DefaultTemplatesUtilities.GetHtmlHelper(model, provider);
html.ViewData.TemplateInfo.AddVisited("foo");
html.ViewData.TemplateInfo.AddVisited("bar");
@ -141,10 +149,14 @@ namespace Microsoft.AspNet.Mvc.Core
" SimpleDisplayText = (null)</div>" + Environment.NewLine;
var model = new DefaultTemplatesUtilities.ObjectTemplateModel { Property1 = "p1", Property2 = null };
var html = DefaultTemplatesUtilities.GetHtmlHelper(model);
var metadata = html.ViewData.ModelMetadata.Properties["Property1"];
metadata.HideSurroundingHtml = true;
var provider = new TestModelMetadataProvider();
provider.ForProperty<DefaultTemplatesUtilities.ObjectTemplateModel>("Property1").DisplayDetails(dd =>
{
dd.HideSurroundingHtml = true;
});
var html = DefaultTemplatesUtilities.GetHtmlHelper(model, provider);
// Act
var result = DefaultDisplayTemplates.ObjectTemplate(html);
@ -219,9 +231,15 @@ namespace Microsoft.AspNet.Mvc.Core
{
// Arrange
var model = "Model string";
var html = DefaultTemplatesUtilities.GetHtmlHelper(model);
var provider = new TestModelMetadataProvider();
provider.ForType<string>().DisplayDetails(dd =>
{
dd.HideSurroundingHtml = true;
});
var html = DefaultTemplatesUtilities.GetHtmlHelper(model, provider: provider);
var viewData = html.ViewData;
viewData.ModelMetadata.HideSurroundingHtml = true;
var templateInfo = viewData.TemplateInfo;
templateInfo.HtmlFieldPrefix = "FieldPrefix";

View File

@ -103,10 +103,14 @@ namespace Microsoft.AspNet.Mvc.Core
public void ObjectTemplateDisplaysNullDisplayTextWithNullModelAndTemplateDepthGreaterThanOne()
{
// Arrange
var html = DefaultTemplatesUtilities.GetHtmlHelper();
var provider = new TestModelMetadataProvider();
provider.ForType<DefaultTemplatesUtilities.ObjectTemplateModel>().DisplayDetails(dd =>
{
dd.NullDisplayText = "Null Display Text";
dd.SimpleDisplayProperty = "Property1";
});
html.ViewData.ModelMetadata.NullDisplayText = "Null Display Text";
html.ViewData.ModelMetadata.SimpleDisplayProperty = "Property1";
var html = DefaultTemplatesUtilities.GetHtmlHelper(provider: provider);
html.ViewData.TemplateInfo.AddVisited("foo");
html.ViewData.TemplateInfo.AddVisited("bar");
@ -131,11 +135,15 @@ namespace Microsoft.AspNet.Mvc.Core
Property1 = simpleDisplayText,
};
var html = DefaultTemplatesUtilities.GetHtmlHelper(model);
var provider = new TestModelMetadataProvider();
provider.ForType<DefaultTemplatesUtilities.ObjectTemplateModel>().DisplayDetails(dd =>
{
dd.HtmlEncode = htmlEncode;
dd.NullDisplayText = "Null Display Text";
dd.SimpleDisplayProperty = "Property1";
});
html.ViewData.ModelMetadata.HtmlEncode = htmlEncode;
html.ViewData.ModelMetadata.NullDisplayText = "Null Display Text";
html.ViewData.ModelMetadata.SimpleDisplayProperty = "Property1";
var html = DefaultTemplatesUtilities.GetHtmlHelper(model, provider: provider);
html.ViewData.TemplateInfo.AddVisited("foo");
html.ViewData.TemplateInfo.AddVisited("bar");
@ -190,11 +198,14 @@ Environment.NewLine;
"</span></div>" +
Environment.NewLine;
var model = new DefaultTemplatesUtilities.ObjectTemplateModel { Property1 = "p1", Property2 = null };
var html = DefaultTemplatesUtilities.GetHtmlHelper(model);
var provider = new TestModelMetadataProvider();
provider.ForProperty<DefaultTemplatesUtilities.ObjectTemplateModel>("Property1").DisplayDetails(dd =>
{
dd.HideSurroundingHtml = true;
});
var metadata = html.ViewData.ModelMetadata.Properties["Property1"];
metadata.HideSurroundingHtml = true;
var model = new DefaultTemplatesUtilities.ObjectTemplateModel { Property1 = "p1", Property2 = null };
var html = DefaultTemplatesUtilities.GetHtmlHelper(model, provider: provider);
// Act
var result = DefaultEditorTemplates.ObjectTemplate(html);
@ -276,11 +287,16 @@ Environment.NewLine;
var expected = "<input id=\"FieldPrefix\" name=\"FieldPrefix\" type=\"hidden\" value=\"Model string\" />";
var model = "Model string";
var html = DefaultTemplatesUtilities.GetHtmlHelper(model);
var viewData = html.ViewData;
viewData.ModelMetadata.HideSurroundingHtml = true;
var templateInfo = viewData.TemplateInfo;
var provider = new TestModelMetadataProvider();
provider.ForType<string>().DisplayDetails(dd =>
{
dd.HideSurroundingHtml = true;
});
var html = DefaultTemplatesUtilities.GetHtmlHelper(model, provider: provider);
var templateInfo = html.ViewData.TemplateInfo;
templateInfo.HtmlFieldPrefix = "FieldPrefix";
templateInfo.FormattedModelValue = "Formatted string";
@ -384,15 +400,21 @@ Environment.NewLine;
viewEngine
.Setup(v => v.FindPartialView(It.IsAny<ActionContext>(), It.IsAny<string>()))
.Returns(ViewEngineResult.NotFound("", Enumerable.Empty<string>()));
var provider = new TestModelMetadataProvider();
provider.ForProperty<DefaultTemplatesUtilities.ObjectTemplateModel>("Property1").DisplayDetails(dd =>
{
dd.DataTypeName = templateName;
});
var helper = DefaultTemplatesUtilities.GetHtmlHelper(
model,
null,
viewEngine.Object,
provider,
innerHelper => new StubbyHtmlHelper(innerHelper));
helper.ViewData["Property1"] = "True";
var metadata = helper.ViewData.ModelMetadata.Properties["Property1"];
metadata.DataTypeName = templateName;
// TemplateBuilder sets FormattedModelValue before calling TemplateRenderer and it's used in most templates.
helper.ViewData.TemplateInfo.FormattedModelValue = "Formatted string";
@ -417,14 +439,20 @@ Environment.NewLine;
viewEngine
.Setup(v => v.FindPartialView(It.IsAny<ActionContext>(), It.IsAny<string>()))
.Returns(ViewEngineResult.NotFound("", Enumerable.Empty<string>()));
var provider = new TestModelMetadataProvider();
provider.ForProperty<DefaultTemplatesUtilities.ObjectTemplateModel>("Property1").DisplayDetails(dd =>
{
dd.DataTypeName = templateName;
});
var helper = DefaultTemplatesUtilities.GetHtmlHelper(
model,
null,
viewEngine.Object,
provider,
innerHelper => new StubbyHtmlHelper(innerHelper));
var metadata = helper.ViewData.ModelMetadata.Properties["Property1"];
metadata.DataTypeName = templateName;
// TemplateBuilder sets FormattedModelValue before calling TemplateRenderer and it's used in most templates.
helper.ViewData.TemplateInfo.FormattedModelValue = "Formatted string";
@ -449,15 +477,21 @@ Environment.NewLine;
viewEngine
.Setup(v => v.FindPartialView(It.IsAny<ActionContext>(), It.IsAny<string>()))
.Returns(ViewEngineResult.NotFound("", Enumerable.Empty<string>()));
var provider = new TestModelMetadataProvider();
provider.ForProperty<DefaultTemplatesUtilities.ObjectTemplateModel>("Property1").DisplayDetails(dd =>
{
dd.TemplateHint = templateName;
});
var helper = DefaultTemplatesUtilities.GetHtmlHelper(
model,
null,
viewEngine.Object,
provider,
innerHelper => new StubbyHtmlHelper(innerHelper));
helper.ViewData["Property1"] = "True";
var metadata = helper.ViewData.ModelMetadata.Properties["Property1"];
metadata.TemplateHint = templateName;
// TemplateBuilder sets FormattedModelValue before calling TemplateRenderer and it's used in most templates.
helper.ViewData.TemplateInfo.FormattedModelValue = "Formatted string";
@ -482,14 +516,20 @@ Environment.NewLine;
viewEngine
.Setup(v => v.FindPartialView(It.IsAny<ActionContext>(), It.IsAny<string>()))
.Returns(ViewEngineResult.NotFound("", Enumerable.Empty<string>()));
var provider = new TestModelMetadataProvider();
provider.ForProperty<DefaultTemplatesUtilities.ObjectTemplateModel>("Property1").DisplayDetails(dd =>
{
dd.TemplateHint = templateName;
});
var helper = DefaultTemplatesUtilities.GetHtmlHelper(
model,
null,
viewEngine.Object,
provider,
innerHelper => new StubbyHtmlHelper(innerHelper));
var metadata = helper.ViewData.ModelMetadata.Properties["Property1"];
metadata.TemplateHint = templateName;
// TemplateBuilder sets FormattedModelValue before calling TemplateRenderer and it's used in most templates.
helper.ViewData.TemplateInfo.FormattedModelValue = "Formatted string";
@ -551,9 +591,19 @@ Environment.NewLine;
viewEngine
.Setup(v => v.FindPartialView(It.IsAny<ActionContext>(), It.IsAny<string>()))
.Returns(ViewEngineResult.NotFound("", Enumerable.Empty<string>()));
var helper = DefaultTemplatesUtilities.GetHtmlHelper(model, viewEngine.Object);
helper.ViewData.ModelMetadata.DataTypeName = dataTypeName;
helper.ViewData.ModelMetadata.EditFormatString = editFormatString; // What [DataType] does for given type.
var provider = new TestModelMetadataProvider();
provider.ForType<DateTimeOffset>().DisplayDetails(dd =>
{
dd.DataTypeName = dataTypeName;
dd.EditFormatString = editFormatString; // What [DataType] does for given type.
});
var helper = DefaultTemplatesUtilities.GetHtmlHelper(
model,
null,
viewEngine.Object,
provider);
helper.ViewData.TemplateInfo.HtmlFieldPrefix = "FieldPrefix";
// Act
@ -590,10 +640,20 @@ Environment.NewLine;
viewEngine
.Setup(v => v.FindPartialView(It.IsAny<ActionContext>(), It.IsAny<string>()))
.Returns(ViewEngineResult.NotFound("", Enumerable.Empty<string>()));
var helper = DefaultTemplatesUtilities.GetHtmlHelper(model, viewEngine.Object);
var provider = new TestModelMetadataProvider();
provider.ForType<DateTimeOffset>().DisplayDetails(dd =>
{
dd.DataTypeName = dataTypeName;
dd.EditFormatString = editFormatString; // What [DataType] does for given type.
});
var helper = DefaultTemplatesUtilities.GetHtmlHelper(
model,
null,
viewEngine.Object,
provider);
helper.Html5DateRenderingMode = Html5DateRenderingMode.Rfc3339;
helper.ViewData.ModelMetadata.DataTypeName = dataTypeName;
helper.ViewData.ModelMetadata.EditFormatString = editFormatString; // What [DataType] does for given type.
helper.ViewData.TemplateInfo.HtmlFieldPrefix = "FieldPrefix";
// Act
@ -631,11 +691,23 @@ Environment.NewLine;
viewEngine
.Setup(v => v.FindPartialView(It.IsAny<ActionContext>(), It.IsAny<string>()))
.Returns(ViewEngineResult.NotFound("", Enumerable.Empty<string>()));
var helper = DefaultTemplatesUtilities.GetHtmlHelper(model, viewEngine.Object);
var provider = new TestModelMetadataProvider();
provider.ForType<DateTimeOffset>().DisplayDetails(dd =>
{
dd.DataTypeName = dataTypeName;
dd.EditFormatString = "Formatted as {0:O}"; // What [DataType] does for given type.
dd.HasNonDefaultEditFormat = true;
});
var helper = DefaultTemplatesUtilities.GetHtmlHelper(
model,
null,
viewEngine.Object,
provider);
helper.Html5DateRenderingMode = renderingMode; // Ignored due to HasNonDefaultEditFormat.
helper.ViewData.ModelMetadata.DataTypeName = dataTypeName;
helper.ViewData.ModelMetadata.EditFormatString = "Formatted as {0:O}";
helper.ViewData.ModelMetadata.HasNonDefaultEditFormat = true;
helper.ViewData.TemplateInfo.HtmlFieldPrefix = "FieldPrefix";
// Act

View File

@ -58,12 +58,12 @@ namespace Microsoft.AspNet.Mvc.Rendering
model: null,
urlHelper: urlHelper,
viewEngine: CreateViewEngine(),
provider: CreateModelMetadataProvider());
provider: TestModelMetadataProvider.CreateDefaultProvider());
}
public static HtmlHelper<ObjectTemplateModel> GetHtmlHelper(IHtmlGenerator htmlGenerator)
{
var metadataProvider = CreateModelMetadataProvider();
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
return GetHtmlHelper<ObjectTemplateModel>(
new ViewDataDictionary<ObjectTemplateModel>(metadataProvider),
CreateUrlHelper(),
@ -79,7 +79,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
viewData,
CreateUrlHelper(),
CreateViewEngine(),
CreateModelMetadataProvider(),
TestModelMetadataProvider.CreateDefaultProvider(),
innerHelperWrapper: null,
htmlGenerator: null);
}
@ -117,7 +117,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
public static HtmlHelper<TModel> GetHtmlHelper<TModel>(TModel model, ICompositeViewEngine viewEngine)
{
return GetHtmlHelper(model, CreateUrlHelper(), viewEngine, CreateModelMetadataProvider());
return GetHtmlHelper(model, CreateUrlHelper(), viewEngine, TestModelMetadataProvider.CreateDefaultProvider());
}
public static HtmlHelper<TModel> GetHtmlHelper<TModel>(
@ -129,7 +129,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
model,
CreateUrlHelper(),
viewEngine,
CreateModelMetadataProvider(),
TestModelMetadataProvider.CreateDefaultProvider(),
innerHelperWrapper);
}
@ -282,10 +282,5 @@ namespace Microsoft.AspNet.Mvc.Rendering
{
return Mock.Of<IUrlHelper>();
}
private static IModelMetadataProvider CreateModelMetadataProvider()
{
return new DataAnnotationsModelMetadataProvider();
}
}
}

View File

@ -278,7 +278,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
var expected = @"<input data-val=""true"" data-val-required=""The Name field is required."" id=""Name""" +
@" name=""Name"" type=""checkbox"" value=""true"" />" +
@"<input name=""Name"" type=""hidden"" value=""false"" />";
var metadataProvider = new DataAnnotationsModelMetadataProvider();
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var viewDataDictionary = new ViewDataDictionary<ModelWithValidation>(metadataProvider)
{
Model = new ModelWithValidation()
@ -395,7 +395,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
private static ViewDataDictionary<ModelWithValidation> GetModelWithValidationViewData()
{
var provider = new DataAnnotationsModelMetadataProvider();
var provider = TestModelMetadataProvider.CreateDefaultProvider();
var viewData = new ViewDataDictionary<ModelWithValidation>(provider)
{
{ "ComplexProperty.Property1", true },

View File

@ -97,7 +97,7 @@ namespace Microsoft.AspNet.Mvc.Core
var provider = new TestModelMetadataProvider();
provider
.ForType<DefaultTemplatesUtilities.ObjectTemplateModel>()
.Then(mm => mm.DisplayName = displayName);
.DisplayDetails(dd => dd.DisplayName = displayName);
var helper = DefaultTemplatesUtilities.GetHtmlHelper(provider: provider);
var enumerableHelper = DefaultTemplatesUtilities.GetHtmlHelperForEnumerable(provider: provider);
@ -124,7 +124,7 @@ namespace Microsoft.AspNet.Mvc.Core
var provider = new TestModelMetadataProvider();
provider
.ForProperty<DefaultTemplatesUtilities.ObjectTemplateModel>("Property1")
.Then(mm => mm.DisplayName = displayName);
.DisplayDetails(dd => dd.DisplayName = displayName);
var helper = DefaultTemplatesUtilities.GetHtmlHelper(provider: provider);
var enumerableHelper = DefaultTemplatesUtilities.GetHtmlHelperForEnumerable(provider: provider);

View File

@ -43,8 +43,13 @@ namespace Microsoft.AspNet.Mvc.Rendering
public void DisplayText_ReturnsNullDisplayText_IfSetAndValueNull()
{
// Arrange
var helper = DefaultTemplatesUtilities.GetHtmlHelper<OverriddenToStringModel>(model: null);
helper.ViewData.ModelMetadata.NullDisplayText = "Null display Text";
var provider = new TestModelMetadataProvider();
provider.ForType<OverriddenToStringModel>().DisplayDetails(dd =>
{
dd.NullDisplayText = "Null display Text";
});
var helper = DefaultTemplatesUtilities.GetHtmlHelper<OverriddenToStringModel>(model: null, provider: provider);
// Act
var result = helper.DisplayText(expression: string.Empty);
@ -57,8 +62,13 @@ namespace Microsoft.AspNet.Mvc.Rendering
public void DisplayTextFor_ReturnsNullDisplayText_IfSetAndValueNull()
{
// Arrange
var helper = DefaultTemplatesUtilities.GetHtmlHelper<OverriddenToStringModel>(model: null);
helper.ViewData.ModelMetadata.NullDisplayText = "Null display Text";
var provider = new TestModelMetadataProvider();
provider.ForType<OverriddenToStringModel>().DisplayDetails(dd =>
{
dd.NullDisplayText = "Null display Text";
});
var helper = DefaultTemplatesUtilities.GetHtmlHelper<OverriddenToStringModel>(model: null, provider: provider);
// Act
var result = helper.DisplayTextFor(m => m);
@ -120,8 +130,13 @@ namespace Microsoft.AspNet.Mvc.Rendering
SimpleDisplay = "Simple display text",
};
var helper = DefaultTemplatesUtilities.GetHtmlHelper(model);
helper.ViewData.ModelMetadata.SimpleDisplayProperty = nameof(OverriddenToStringModel.SimpleDisplay);
var provider = new TestModelMetadataProvider();
provider.ForType<OverriddenToStringModel>().DisplayDetails(dd =>
{
dd.SimpleDisplayProperty = nameof(OverriddenToStringModel.SimpleDisplay);
});
var helper = DefaultTemplatesUtilities.GetHtmlHelper<OverriddenToStringModel>(model: model, provider: provider);
// Act
var result = helper.DisplayText(expression: string.Empty);
@ -138,9 +153,14 @@ namespace Microsoft.AspNet.Mvc.Rendering
{
SimpleDisplay = "Simple display text",
};
var helper = DefaultTemplatesUtilities.GetHtmlHelper(model);
helper.ViewData.ModelMetadata.SimpleDisplayProperty = nameof(OverriddenToStringModel.SimpleDisplay);
var provider = new TestModelMetadataProvider();
provider.ForType<OverriddenToStringModel>().DisplayDetails(dd =>
{
dd.SimpleDisplayProperty = nameof(OverriddenToStringModel.SimpleDisplay);
});
var helper = DefaultTemplatesUtilities.GetHtmlHelper<OverriddenToStringModel>(model: model, provider: provider);
// Act
var result = helper.DisplayTextFor(m => m);
@ -159,8 +179,13 @@ namespace Microsoft.AspNet.Mvc.Rendering
SimpleDisplay = "Simple display text",
};
var helper = DefaultTemplatesUtilities.GetHtmlHelper(model);
helper.ViewData.ModelMetadata.SimpleDisplayProperty = nameof(OverriddenToStringModel.SimpleDisplay);
var provider = new TestModelMetadataProvider();
provider.ForType<OverriddenToStringModel>().DisplayDetails(dd =>
{
dd.SimpleDisplayProperty = nameof(OverriddenToStringModel.SimpleDisplay);
});
var helper = DefaultTemplatesUtilities.GetHtmlHelper<OverriddenToStringModel>(model: model, provider: provider);
// Act
var result = helper.DisplayText("Name");

View File

@ -62,33 +62,6 @@ namespace Microsoft.AspNet.Mvc.Core
Assert.Equal("<label for=\"Inner_Id\">Id</label>", labelForResult.ToString());
}
[Fact]
public void LabelHelpers_ReturnEmptyForModel_IfMetadataPropertyNameEmpty()
{
// Arrange
var provider = new DataAnnotationsModelMetadataProvider();
var metadata = new ModelMetadata(
provider,
containerType: null,
modelType: typeof(object),
propertyName: string.Empty);
var helper = DefaultTemplatesUtilities.GetHtmlHelper();
helper.ViewData.ModelExplorer = new ModelExplorer(provider, metadata, model: null);
// Act
var labelResult = helper.Label(expression: string.Empty);
var labelNullResult = helper.Label(expression: null); // null is another alias for current model
var labelForResult = helper.LabelFor(m => m);
var labelForModelResult = helper.LabelForModel();
// Assert
Assert.Empty(labelResult.ToString());
Assert.Empty(labelNullResult.ToString());
Assert.Empty(labelForResult.ToString());
Assert.Empty(labelForModelResult.ToString());
}
[Fact]
public void LabelHelpers_DisplayMetadataPropertyNameForProperty()
{
@ -134,8 +107,12 @@ namespace Microsoft.AspNet.Mvc.Core
public void LabelHelpers_ReturnEmptyForModel_IfDisplayNameEmpty()
{
// Arrange
var helper = DefaultTemplatesUtilities.GetHtmlHelper();
helper.ViewData.ModelMetadata.DisplayName = string.Empty;
var provider = new TestModelMetadataProvider();
provider
.ForType<DefaultTemplatesUtilities.ObjectTemplateModel>()
.DisplayDetails(dd => dd.DisplayName = string.Empty);
var helper = DefaultTemplatesUtilities.GetHtmlHelper(provider: provider);
// Act
var labelResult = helper.Label(expression: string.Empty);
@ -156,8 +133,12 @@ namespace Microsoft.AspNet.Mvc.Core
public void LabelHelpers_DisplayDisplayName_IfNonNull(string displayName)
{
// Arrange
var helper = DefaultTemplatesUtilities.GetHtmlHelper();
helper.ViewData.ModelMetadata.DisplayName = displayName;
var provider = new TestModelMetadataProvider();
provider
.ForType<DefaultTemplatesUtilities.ObjectTemplateModel>()
.DisplayDetails(dd => dd.DisplayName = displayName);
var helper = DefaultTemplatesUtilities.GetHtmlHelper(provider: provider);
// Act
var labelResult = helper.Label(expression: string.Empty);
@ -174,15 +155,16 @@ namespace Microsoft.AspNet.Mvc.Core
public void LabelHelpers_ReturnEmptyForProperty_IfDisplayNameEmpty()
{
// Arrange
var provider = new EmptyModelMetadataProvider();
var provider = new TestModelMetadataProvider();
provider
.ForProperty<DefaultTemplatesUtilities.ObjectTemplateModel>("Property1")
.DisplayDetails(dd => dd.DisplayName = string.Empty);
var modelExplorer = provider
.GetModelExplorerForType(typeof(DefaultTemplatesUtilities.ObjectTemplateModel), model: null)
.GetExplorerForProperty("Property1");
var helper = DefaultTemplatesUtilities.GetHtmlHelper();
helper.ViewData.ModelExplorer = modelExplorer;
helper.ViewData.ModelMetadata.DisplayName = string.Empty;
// Act
var labelResult = helper.Label(expression: string.Empty);
@ -206,7 +188,7 @@ namespace Microsoft.AspNet.Mvc.Core
var provider = new TestModelMetadataProvider();
provider
.ForProperty<DefaultTemplatesUtilities.ObjectTemplateModel>("Property1")
.Then(mm => mm.DisplayName = displayName);
.DisplayDetails(dd => dd.DisplayName = displayName);
var helper = DefaultTemplatesUtilities.GetHtmlHelper(provider: provider);

View File

@ -1,8 +1,8 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.ModelBinding.Metadata;
using Microsoft.AspNet.Mvc.Rendering;
using Moq;
using Xunit;
@ -140,11 +140,8 @@ namespace Microsoft.AspNet.Mvc.Core
// Arrange
var provider = new Mock<IModelMetadataProvider>(MockBehavior.Strict);
var metadata = new Mock<ModelMetadata>(
MockBehavior.Strict,
provider.Object,
null,
typeof(object),
null);
MockBehavior.Loose,
ModelMetadataIdentity.ForType(typeof(DefaultTemplatesUtilities.ObjectTemplateModel)));
provider
.Setup(m => m.GetMetadataForType(typeof(DefaultTemplatesUtilities.ObjectTemplateModel)))
.Returns(metadata.Object);
@ -163,7 +160,7 @@ namespace Microsoft.AspNet.Mvc.Core
// Only the ViewDataDictionary should do anything with metadata.
provider.Verify(
m => m.GetMetadataForType(typeof(DefaultTemplatesUtilities.ObjectTemplateModel)),
Times.Exactly(2));
Times.Exactly(1));
}
[Fact]
@ -172,15 +169,12 @@ namespace Microsoft.AspNet.Mvc.Core
// Arrange
var provider = new Mock<IModelMetadataProvider>(MockBehavior.Strict);
var metadata = new Mock<ModelMetadata>(
MockBehavior.Strict,
provider.Object,
null,
typeof(object),
null);
MockBehavior.Loose,
ModelMetadataIdentity.ForType(typeof(DefaultTemplatesUtilities.ObjectTemplateModel)));
provider
.Setup(m => m.GetMetadataForType(typeof(DefaultTemplatesUtilities.ObjectTemplateModel)))
.Returns(metadata.Object);
var helper = DefaultTemplatesUtilities.GetHtmlHelper(provider.Object);
// Act (do not throw)
@ -193,7 +187,7 @@ namespace Microsoft.AspNet.Mvc.Core
// Only the ViewDataDictionary should do anything with metadata.
provider.Verify(
m => m.GetMetadataForType(typeof(DefaultTemplatesUtilities.ObjectTemplateModel)),
Times.Exactly(2));
Times.Exactly(1));
}
[Theory]

View File

@ -13,7 +13,7 @@ namespace Microsoft.AspNet.Mvc
public void SettingModelThrowsIfTheModelIsNull()
{
// Arrange
var viewDataOfT = new ViewDataDictionary<int>(new DataAnnotationsModelMetadataProvider());
var viewDataOfT = new ViewDataDictionary<int>(new EmptyModelMetadataProvider());
ViewDataDictionary viewData = viewDataOfT;
// Act and Assert
@ -25,7 +25,7 @@ namespace Microsoft.AspNet.Mvc
public void SettingModelThrowsIfTheModelIsIncompatible()
{
// Arrange
var viewDataOfT = new ViewDataDictionary<string>(new DataAnnotationsModelMetadataProvider());
var viewDataOfT = new ViewDataDictionary<string>(new EmptyModelMetadataProvider());
ViewDataDictionary viewData = viewDataOfT;
// Act and Assert
@ -38,7 +38,7 @@ namespace Microsoft.AspNet.Mvc
{
// Arrange
var value = "some value";
var viewDataOfT = new ViewDataDictionary<object>(new DataAnnotationsModelMetadataProvider());
var viewDataOfT = new ViewDataDictionary<object>(new EmptyModelMetadataProvider());
ViewDataDictionary viewData = viewDataOfT;
// Act
@ -52,7 +52,7 @@ namespace Microsoft.AspNet.Mvc
public void PropertiesInitializedCorrectly()
{
// Arrange
var viewData = new ViewDataDictionary<string>(new DataAnnotationsModelMetadataProvider());
var viewData = new ViewDataDictionary<string>(new EmptyModelMetadataProvider());
// Act & Assert
Assert.Empty(viewData);
@ -79,7 +79,7 @@ namespace Microsoft.AspNet.Mvc
public void TemplateInfoPropertiesAreNeverNull()
{
// Arrange
var viewData = new ViewDataDictionary<string>(new DataAnnotationsModelMetadataProvider());
var viewData = new ViewDataDictionary<string>(new EmptyModelMetadataProvider());
// Act
viewData.TemplateInfo.FormattedModelValue = null;

View File

@ -3,114 +3,180 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using Microsoft.AspNet.Mvc.ModelBinding.Metadata;
using Microsoft.Framework.Internal;
using Xunit;
namespace Microsoft.AspNet.Mvc.ModelBinding
{
public class TestModelMetadataProvider : EmptyModelMetadataProvider
public class TestModelMetadataProvider : DefaultModelMetadataProvider
{
private List<MetadataBuilder> _builders = new List<MetadataBuilder>();
protected override ModelMetadata CreateMetadataFromPrototype([NotNull] ModelMetadata prototype)
// Creates a provider with all the defaults - includes data annotations
public static IModelMetadataProvider CreateDefaultProvider()
{
var metadata = base.CreateMetadataFromPrototype(prototype);
if (prototype.PropertyName == null)
var detailsProviders = new IMetadataDetailsProvider[]
{
foreach (var builder in _builders)
{
builder.Apply(prototype.ModelType, metadata);
}
}
else
{
foreach (var builder in _builders)
{
builder.Apply(prototype.ContainerType, prototype.PropertyName, metadata);
}
}
new DefaultBindingMetadataProvider(),
new DataAnnotationsMetadataDetailsProvider(),
};
return metadata;
var compositeDetailsProvider = new DefaultCompositeMetadataDetailsProvider(detailsProviders);
return new DefaultModelMetadataProvider(compositeDetailsProvider);
}
private readonly TestModelMetadataDetailsProvider _detailsProvider;
public TestModelMetadataProvider()
: this(new TestModelMetadataDetailsProvider())
{
}
private TestModelMetadataProvider(TestModelMetadataDetailsProvider detailsProvider)
: base(new DefaultCompositeMetadataDetailsProvider(new IMetadataDetailsProvider[]
{
new DefaultBindingMetadataProvider(),
new DataAnnotationsMetadataDetailsProvider(),
detailsProvider
}))
{
_detailsProvider = detailsProvider;
}
public IMetadataBuilder ForType(Type type)
{
var builder = new MetadataBuilder(type);
_builders.Add(builder);
var key = ModelMetadataIdentity.ForType(type);
var builder = new MetadataBuilder(key);
_detailsProvider.Builders.Add(builder);
return builder;
}
public IMetadataBuilder ForType<TModel>()
{
var builder = new MetadataBuilder(typeof(TModel));
_builders.Add(builder);
return builder;
return ForType(typeof(TModel));
}
public IMetadataBuilder ForProperty(Type containerType, string propertyName)
{
var builder = new MetadataBuilder(containerType, propertyName);
_builders.Add(builder);
var property = containerType.GetRuntimeProperty(propertyName);
Assert.NotNull(property);
var key = ModelMetadataIdentity.ForProperty(property.PropertyType, property.Name, containerType);
var builder = new MetadataBuilder(key);
_detailsProvider.Builders.Add(builder);
return builder;
}
public IMetadataBuilder ForProperty<TContainer>(string propertyName)
{
var builder = new MetadataBuilder(typeof(TContainer), propertyName);
_builders.Add(builder);
return builder;
return ForProperty(typeof(TContainer), propertyName);
}
private class TestModelMetadataDetailsProvider :
IBindingMetadataProvider,
IDisplayMetadataProvider,
IValidationMetadataProvider
{
public List<MetadataBuilder> Builders { get; } = new List<MetadataBuilder>();
public void GetBindingMetadata([NotNull] BindingMetadataProviderContext context)
{
foreach (var builder in Builders)
{
builder.Apply(context);
}
}
public void GetDisplayMetadata([NotNull] DisplayMetadataProviderContext context)
{
foreach (var builder in Builders)
{
builder.Apply(context);
}
}
public void GetValidationMetadata([NotNull] ValidationMetadataProviderContext context)
{
foreach (var builder in Builders)
{
builder.Apply(context);
}
}
}
public interface IMetadataBuilder
{
IMetadataBuilder Then(Action<ModelMetadata> action);
IMetadataBuilder BindingDetails(Action<BindingMetadata> action);
IMetadataBuilder DisplayDetails(Action<DisplayMetadata> action);
IMetadataBuilder ValidationDetails(Action<ValidationMetadata> action);
}
private class MetadataBuilder : IMetadataBuilder
{
private List<Action<ModelMetadata>> _actions = new List<Action<ModelMetadata>>();
private List<Action<BindingMetadata>> _bindingActions = new List<Action<BindingMetadata>>();
private List<Action<DisplayMetadata>> _displayActions = new List<Action<DisplayMetadata>>();
private List<Action<ValidationMetadata>> _valiationActions = new List<Action<ValidationMetadata>>();
private readonly Type _type;
private readonly Type _containerType;
private readonly string _propertyName;
private readonly ModelMetadataIdentity _key;
public MetadataBuilder(Type type)
public MetadataBuilder(ModelMetadataIdentity key)
{
_type = type;
_key = key;
}
public MetadataBuilder(Type containerType, string propertyName)
public void Apply(BindingMetadataProviderContext context)
{
_containerType = containerType;
_propertyName = propertyName;
if (_key.Equals(context.Key))
{
foreach (var action in _bindingActions)
{
action(context.BindingMetadata);
}
}
}
public IMetadataBuilder Then(Action<ModelMetadata> action)
public void Apply(DisplayMetadataProviderContext context)
{
_actions.Add(action);
if (_key.Equals(context.Key))
{
foreach (var action in _displayActions)
{
action(context.DisplayMetadata);
}
}
}
public void Apply(ValidationMetadataProviderContext context)
{
if (_key.Equals(context.Key))
{
foreach (var action in _valiationActions)
{
action(context.ValidationMetadata);
}
}
}
public IMetadataBuilder BindingDetails(Action<BindingMetadata> action)
{
_bindingActions.Add(action);
return this;
}
public void Apply(Type type, ModelMetadata metadata)
public IMetadataBuilder DisplayDetails(Action<DisplayMetadata> action)
{
if (type == _type)
{
foreach (var action in _actions)
{
action(metadata);
}
}
_displayActions.Add(action);
return this;
}
public void Apply(Type containerType, string propertyName, ModelMetadata metadata)
public IMetadataBuilder ValidationDetails(Action<ValidationMetadata> action)
{
if (containerType == _containerType && propertyName == _propertyName)
{
foreach (var action in _actions)
{
action(metadata);
}
}
_valiationActions.Add(action);
return this;
}
}
}

View File

@ -14,7 +14,7 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
{
public class RazorInstrumentationTests
{
private readonly IServiceProvider _services = TestHelper.CreateServices("RazorInstrumentationWebsite");
private readonly IServiceProvider _services = TestHelper.CreateServices("RazorInstrumentationWebSite");
private readonly Action<IApplicationBuilder> _app = new Startup().Configure;
public static IEnumerable<object[]> InstrumentationData

View File

@ -19,7 +19,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
{ "someName[0]", "42" },
{ "someName[1]", "84" }
};
ModelBindingContext bindingContext = GetBindingContext(valueProvider);
var bindingContext = GetBindingContext(valueProvider);
var binder = new ArrayModelBinder<int>();
// Act
@ -36,7 +36,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
public async Task GetBinder_ValueProviderDoesNotContainPrefix_ReturnsNull()
{
// Arrange
ModelBindingContext bindingContext = GetBindingContext(new SimpleHttpValueProvider());
var bindingContext = GetBindingContext(new SimpleHttpValueProvider());
var binder = new ArrayModelBinder<int>();
// Act
@ -54,8 +54,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
{
{ "foo[0]", "42" },
};
ModelBindingContext bindingContext = GetBindingContext(valueProvider);
bindingContext.ModelMetadata.IsReadOnly = true;
var bindingContext = GetBindingContext(valueProvider, isReadOnly: true);
var binder = new ArrayModelBinder<int>();
// Act
@ -83,10 +82,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
return mockIntBinder.Object;
}
private static ModelBindingContext GetBindingContext(IValueProvider valueProvider)
private static ModelBindingContext GetBindingContext(
IValueProvider valueProvider,
bool isReadOnly = false)
{
var metadataProvider = new EmptyModelMetadataProvider();
ModelBindingContext bindingContext = new ModelBindingContext
var metadataProvider = new TestModelMetadataProvider();
metadataProvider.ForType<int[]>().BindingDetails(bd => bd.IsReadOnly = isReadOnly);
var bindingContext = new ModelBindingContext
{
ModelMetadata = metadataProvider.GetMetadataForType(typeof(int[])),
ModelName = "someName",

View File

@ -33,8 +33,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
public async Task BindModel_ReturnsTrueEvenIfSelectedBinderReturnsFalse()
{
// Arrange
var bindingContext = GetBindingContext(typeof(Person));
bindingContext.ModelMetadata.BinderType = typeof(FalseModelBinder);
var bindingContext = GetBindingContext(typeof(Person), binderType: typeof(FalseModelBinder));
var binder = new BinderTypeBasedModelBinder();
@ -49,8 +48,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
public async Task BindModel_CallsBindAsync_OnProvidedModelBinder()
{
// Arrange
var bindingContext = GetBindingContext(typeof(Person));
bindingContext.ModelMetadata.BinderType = typeof(TrueModelBinder);
var bindingContext = GetBindingContext(typeof(Person), binderType: typeof(TrueModelBinder));
var model = new Person();
var innerModelBinder = new TrueModelBinder();
@ -75,8 +73,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
public async Task BindModel_CallsBindAsync_OnProvidedModelBinderProvider()
{
// Arrange
var bindingContext = GetBindingContext(typeof(Person));
bindingContext.ModelMetadata.BinderType = typeof(ModelBinderProvider);
var bindingContext = GetBindingContext(typeof(Person), binderType: typeof(ModelBinderProvider));
var model = new Person();
var provider = new ModelBinderProvider();
@ -102,8 +99,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
public async Task BindModel_ForNonModelBinderAndModelBinderProviderTypes_Throws()
{
// Arrange
var bindingContext = GetBindingContext(typeof(Person));
bindingContext.ModelMetadata.BinderType = typeof(Person);
var bindingContext = GetBindingContext(typeof(Person), binderType: typeof(Person));
var binder = new BinderTypeBasedModelBinder();
var expected = "The type '" + typeof(Person).FullName + "' must implement either " +
@ -118,9 +114,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
Assert.Equal(expected, ex.Message);
}
private static ModelBindingContext GetBindingContext(Type modelType)
private static ModelBindingContext GetBindingContext(Type modelType, Type binderType = null)
{
var metadataProvider = new DataAnnotationsModelMetadataProvider();
var metadataProvider = new TestModelMetadataProvider();
metadataProvider.ForType(modelType).BindingDetails(bd => bd.BinderType = binderType);
var operationBindingContext = new OperationBindingContext
{
MetadataProvider = metadataProvider,

View File

@ -52,13 +52,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public async Task BindingSourceModelBinder_ReturnsFalse_NonMatchingSource()
{
// Arrange
var context = new ModelBindingContext();
context.ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(typeof(string));
var provider = new TestModelMetadataProvider();
provider.ForType<string>().BindingDetails(d => d.BindingSource = BindingSource.Query);
context.ModelMetadata.BinderMetadata = new ModelBinderAttribute()
{
BindingSource = BindingSource.Query,
};
var context = new ModelBindingContext();
context.ModelMetadata = provider.GetMetadataForType(typeof(string));
var binder = new TestableBindingSourceModelBinder(BindingSource.Body);
@ -74,13 +72,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public async Task BindingSourceModelBinder_ReturnsTrue_MatchingSource()
{
// Arrange
var context = new ModelBindingContext();
context.ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(typeof(string));
var provider = new TestModelMetadataProvider();
provider.ForType<string>().BindingDetails(d => d.BindingSource = BindingSource.Body);
context.ModelMetadata.BinderMetadata = new ModelBinderAttribute()
{
BindingSource = BindingSource.Body,
};
var context = new ModelBindingContext();
context.ModelMetadata = provider.GetMetadataForType(typeof(string));
var binder = new TestableBindingSourceModelBinder(BindingSource.Body);

View File

@ -107,7 +107,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
{
OperationBindingContext = new OperationBindingContext()
{
MetadataProvider = new DataAnnotationsModelMetadataProvider(),
MetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(),
},
};

View File

@ -26,7 +26,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
private static ModelMetadata GetModelMetadata()
{
return new ModelMetadata(new EmptyModelMetadataProvider(), typeof(object), typeof(object), "PropertyName");
return new EmptyModelMetadataProvider().GetMetadataForProperty(typeof(string), "Length");
}
}
}

View File

@ -302,7 +302,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
IModelValidatorProvider validatorProvider = null)
{
validatorProvider = validatorProvider ?? GetValidatorProvider();
var metadataProvider = new DataAnnotationsModelMetadataProvider();
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var bindingContext = new ModelBindingContext
{
FallbackToEmptyPrefix = true,

View File

@ -16,7 +16,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
[InlineData(typeof(object))]
[InlineData(typeof(int))]
[InlineData(typeof(int[]))]
[InlineData(typeof(TestFromHeader))]
[InlineData(typeof(BindingSource))]
public async Task BindModelAsync_ReturnsTrue_ForAllTypes(Type type)
{
// Arrange
@ -74,7 +74,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
private static ModelBindingContext GetBindingContext(Type modelType)
{
var metadataProvider = new EmptyModelMetadataProvider();
var metadataProvider = new TestModelMetadataProvider();
metadataProvider.ForType(modelType).BindingDetails(d => d.BindingSource = BindingSource.Header);
var bindingContext = new ModelBindingContext
{
ModelMetadata = metadataProvider.GetMetadataForType(modelType),
@ -87,13 +89,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
}
};
bindingContext.ModelMetadata.BinderMetadata = new TestFromHeader();
return bindingContext;
}
public class TestFromHeader : IBindingSourceMetadata
{
public BindingSource BindingSource { get; } = BindingSource.Header;
}
}
}

View File

@ -23,24 +23,31 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
[InlineData(typeof(Person), false)]
[InlineData(typeof(EmptyModel), true)]
[InlineData(typeof(EmptyModel), false)]
public async Task
CanCreateModel_CreatesModel_ForTopLevelObjectIfThereIsExplicitPrefix(Type modelType, bool isPrefixProvided)
public async Task CanCreateModel_CreatesModel_ForTopLevelObjectIfThereIsExplicitPrefix(
Type modelType,
bool isPrefixProvided)
{
var mockValueProvider = new Mock<IValueProvider>();
mockValueProvider.Setup(o => o.ContainsPrefixAsync(It.IsAny<string>()))
.Returns(Task.FromResult(false));
var metadataProvider = new TestModelMetadataProvider();
if (isPrefixProvided)
{
metadataProvider.ForType<Person>().BindingDetails(bd => bd.BinderModelName = "prefix");
}
var bindingContext = new MutableObjectBinderContext
{
ModelBindingContext = new ModelBindingContext
{
// Random type.
ModelMetadata = GetMetadataForType(typeof(Person)),
ModelMetadata = metadataProvider.GetMetadataForType(typeof(Person)),
ValueProvider = mockValueProvider.Object,
OperationBindingContext = new OperationBindingContext
{
ValueProvider = mockValueProvider.Object,
MetadataProvider = new DataAnnotationsModelMetadataProvider(),
MetadataProvider = metadataProvider,
ValidatorProvider = Mock.Of<IModelValidatorProvider>(),
},
@ -49,7 +56,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
}
};
bindingContext.ModelBindingContext.ModelMetadata.BinderModelName = isPrefixProvided ? "prefix" : null;
var mutableBinder = new TestableMutableObjectModelBinder();
bindingContext.PropertyMetadata = mutableBinder.GetMetadataForProperties(
bindingContext.ModelBindingContext);
@ -84,7 +90,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
ValidatorProvider = Mock.Of<IModelValidatorProvider>(),
ValueProvider = mockValueProvider.Object,
MetadataProvider = new DataAnnotationsModelMetadataProvider()
MetadataProvider = TestModelMetadataProvider.CreateDefaultProvider()
}
}
};
@ -170,7 +176,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
ValidatorProvider = Mock.Of<IModelValidatorProvider>(),
ValueProvider = mockValueProvider.Object,
MetadataProvider = new DataAnnotationsModelMetadataProvider(),
MetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(),
},
// Setting it to empty ensures that model does not get created becasue of no model name.
@ -214,7 +220,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
ValidatorProvider = Mock.Of<IModelValidatorProvider>(),
ValueProvider = mockValueProvider.Object,
MetadataProvider = new DataAnnotationsModelMetadataProvider(),
MetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(),
},
// Setting it to empty ensures that model does not get created becasue of no model name.
ModelName = "dummyName"
@ -269,7 +275,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
OperationBindingContext = new OperationBindingContext
{
ValueProvider = mockOriginalValueProvider.Object,
MetadataProvider = new DataAnnotationsModelMetadataProvider(),
MetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(),
ValidatorProvider = Mock.Of<IModelValidatorProvider>(),
},
@ -314,7 +320,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
ValidatorProvider = Mock.Of<IModelValidatorProvider>(),
ValueProvider = mockOriginalValueProvider.Object,
MetadataProvider = new DataAnnotationsModelMetadataProvider(),
MetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(),
},
// Setting it to empty ensures that model does not get created becasue of no model name.
ModelName = "dummyName"
@ -349,7 +355,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
OperationBindingContext = new OperationBindingContext
{
ModelBinder = mockDtoBinder.Object,
MetadataProvider = new DataAnnotationsModelMetadataProvider(),
MetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(),
ValidatorProvider = Mock.Of<IModelValidatorProvider>()
}
};
@ -401,7 +407,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
OperationBindingContext = new OperationBindingContext
{
ModelBinder = mockDtoBinder.Object,
MetadataProvider = new DataAnnotationsModelMetadataProvider(),
MetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(),
ValidatorProvider = Mock.Of<IModelValidatorProvider>()
}
};
@ -576,7 +582,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
OperationBindingContext = new OperationBindingContext
{
ValidatorProvider = Mock.Of<IModelValidatorProvider>(),
MetadataProvider = new DataAnnotationsModelMetadataProvider()
MetadataProvider = TestModelMetadataProvider.CreateDefaultProvider()
}
};
@ -611,7 +617,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
OperationBindingContext = new OperationBindingContext
{
ValidatorProvider = Mock.Of<IModelValidatorProvider>(),
MetadataProvider = new DataAnnotationsModelMetadataProvider()
MetadataProvider = TestModelMetadataProvider.CreateDefaultProvider()
},
};
@ -640,7 +646,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
RequestServices = CreateServices()
},
ValidatorProvider = Mock.Of<IModelValidatorProvider>(),
MetadataProvider = new DataAnnotationsModelMetadataProvider(),
MetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(),
}
};
@ -665,7 +671,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
OperationBindingContext = new OperationBindingContext
{
ValidatorProvider = Mock.Of<IModelValidatorProvider>(),
MetadataProvider = new DataAnnotationsModelMetadataProvider(),
MetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(),
}
};
@ -719,7 +725,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
ModelName = "theModel",
OperationBindingContext = new OperationBindingContext
{
MetadataProvider = new DataAnnotationsModelMetadataProvider(),
MetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(),
ValidatorProvider = Mock.Of<IModelValidatorProvider>()
}
};
@ -770,7 +776,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
ModelState = new ModelStateDictionary(),
OperationBindingContext = new OperationBindingContext
{
MetadataProvider = new DataAnnotationsModelMetadataProvider(),
MetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(),
ValidatorProvider = Mock.Of<IModelValidatorProvider>()
}
};
@ -1367,7 +1373,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
ModelName = "theModel",
OperationBindingContext = new OperationBindingContext
{
MetadataProvider = new DataAnnotationsModelMetadataProvider(),
MetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(),
ValidatorProvider = new CompositeModelValidatorProvider(providers)
}
};
@ -1383,24 +1389,16 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
private static ModelMetadata GetMetadataForCanUpdateProperty(string propertyName)
{
var metadataProvider = new DataAnnotationsModelMetadataProvider();
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
return metadataProvider.GetMetadataForProperty(typeof(MyModelTestingCanUpdateProperty), propertyName);
}
private static ModelMetadata GetMetadataForType(Type t)
{
var metadataProvider = new DataAnnotationsModelMetadataProvider();
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
return metadataProvider.GetMetadataForType(t);
}
private static ModelMetadata GetMetadataForParameter(MethodInfo methodInfo, string parameterName)
{
var metadataProvider = new DataAnnotationsModelMetadataProvider();
return metadataProvider.GetMetadataForParameter(
methodInfo: methodInfo,
parameterName: parameterName);
}
private class EmptyModel
{
}

View File

@ -1,260 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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.Generic;
#if ASPNET50
using System.ComponentModel;
#endif
using System.ComponentModel.DataAnnotations;
using System.Linq;
using Microsoft.AspNet.Testing;
using Xunit;
namespace Microsoft.AspNet.Mvc.ModelBinding
{
public class AssociatedMetadataProviderTest
{
// GetMetadataForProperties
[Fact]
public void GetMetadataForPropertiesCreatesMetadataForAllPropertiesOnModelWithPropertyValues()
{
// Arrange
var model = new PropertyModel { LocalAttributes = 42, MetadataAttributes = "hello", MixedAttributes = 21.12 };
var provider = new TestableAssociatedMetadataProvider();
// Act
// Call ToList() to force the lazy evaluation to evaluate
provider.GetMetadataForProperties(typeof(PropertyModel)).ToList();
// Assert
var local = Assert.Single(
provider.CreateMetadataPrototypeLog,
m => m.ContainerType == typeof(PropertyModel) && m.PropertyName == "LocalAttributes");
Assert.Equal(typeof(int), local.ModelType);
Assert.True(local.Attributes.Any(a => a is RequiredAttribute));
var metadata = Assert.Single(
provider.CreateMetadataPrototypeLog,
m => m.ContainerType == typeof(PropertyModel) && m.PropertyName == "MetadataAttributes");
Assert.Equal(typeof(string), metadata.ModelType);
Assert.True(metadata.Attributes.Any(a => a is RangeAttribute));
var mixed = Assert.Single(
provider.CreateMetadataPrototypeLog,
m => m.ContainerType == typeof(PropertyModel) && m.PropertyName == "MixedAttributes");
Assert.Equal(typeof(double), mixed.ModelType);
Assert.True(mixed.Attributes.Any(a => a is RequiredAttribute));
Assert.True(mixed.Attributes.Any(a => a is RangeAttribute));
}
[Fact]
public void GetMetadataForProperties_ExcludesIndexers()
{
// Arrange
var model = new ModelWithIndexer();
var provider = new TestableAssociatedMetadataProvider();
var modelType = model.GetType();
// Act
provider.GetMetadataForProperties(modelType).ToList();
// Assert
Assert.Equal(2, provider.CreateMetadataFromPrototypeLog.Count);
var valueMetadata = Assert.Single(
provider.CreateMetadataPrototypeLog,
m => m.ContainerType == modelType && m.PropertyName == "Value");
Assert.Equal(typeof(string), valueMetadata.ModelType);
Assert.Single(valueMetadata.Attributes.OfType<MinLengthAttribute>());
var testPropertyMetadata = Assert.Single(
provider.CreateMetadataPrototypeLog,
m => m.ContainerType == modelType && m.PropertyName == "TestProperty");
Assert.Equal(typeof(string), testPropertyMetadata.ModelType);
}
[Fact]
public void GetMetadataForParameterNullOrEmptyPropertyNameThrows()
{
// Arrange
var provider = new TestableAssociatedMetadataProvider();
// Act & Assert
ExceptionAssert.ThrowsArgumentNullOrEmpty(
() => provider.GetMetadataForParameter(methodInfo: null, parameterName: null),
"parameterName");
ExceptionAssert.ThrowsArgumentNullOrEmpty(
() => provider.GetMetadataForParameter(methodInfo: null, parameterName: null),
"parameterName");
}
// GetMetadata and access metadata for a property
[Fact]
public void GetMetadataForProperty_WithLocalAttributes()
{
// Arrange
var provider = new TestableAssociatedMetadataProvider();
var metadata = new ModelMetadata(provider, null, typeof(PropertyModel), null);
var propertyMetadata = new ModelMetadata(provider, typeof(PropertyModel), typeof(int), "LocalAttributes");
provider.CreateMetadataFromPrototypeReturnValue = propertyMetadata;
// Act
var result = provider.GetMetadataForProperty(typeof(PropertyModel), "LocalAttributes");
// Assert
Assert.Same(propertyMetadata, result);
var localAttributes = Assert.Single(
provider.CreateMetadataPrototypeLog,
parameters => parameters.PropertyName == "LocalAttributes");
Assert.Single(localAttributes.Attributes, a => a is RequiredAttribute);
}
[Fact]
public void GetMetadataForProperty_WithMetadataAttributes()
{
// Arrange
var provider = new TestableAssociatedMetadataProvider();
var metadata = new ModelMetadata(provider, null, typeof(PropertyModel), null);
var propertyMetadata = new ModelMetadata(provider, typeof(PropertyModel), typeof(string), "MetadataAttributes");
provider.CreateMetadataFromPrototypeReturnValue = propertyMetadata;
// Act
var result = metadata.Properties["MetadataAttributes"];
// Assert
Assert.Same(propertyMetadata, result);
var parmaters = Assert.Single(
provider.CreateMetadataPrototypeLog,
p => p.PropertyName == "MetadataAttributes");
Assert.Single(parmaters.Attributes, a => a is RangeAttribute);
}
[Fact]
public void GetMetadataForProperty_WithMixedAttributes()
{
// Arrange
var provider = new TestableAssociatedMetadataProvider();
var metadata = new ModelMetadata(provider, null, typeof(PropertyModel), null);
var propertyMetadata = new ModelMetadata(provider, typeof(PropertyModel), typeof(double), "MixedAttributes");
provider.CreateMetadataFromPrototypeReturnValue = propertyMetadata;
// Act
var result = metadata.Properties["MixedAttributes"];
// Assert
Assert.Same(propertyMetadata, result);
var parms = Assert.Single(provider.CreateMetadataPrototypeLog, p => p.PropertyName == "MixedAttributes");
Assert.Single(parms.Attributes, a => a is RequiredAttribute);
Assert.Single(parms.Attributes, a => a is RangeAttribute);
}
// GetMetadataForType
#if ASPNET50 // No ReadOnlyAttribute in K
[Fact]
public void GetMetadataForTypeIncludesAttributesOnType()
{
var provider = new TestableAssociatedMetadataProvider();
var metadata = new ModelMetadata(provider, null, typeof(TypeModel), null);
provider.CreateMetadataFromPrototypeReturnValue = metadata;
// Act
var result = provider.GetMetadataForType(typeof(TypeModel));
// Assert
Assert.Same(metadata, result);
var parms = Assert.Single(provider.CreateMetadataPrototypeLog, p => p.ModelType == typeof(TypeModel));
Assert.Single(parms.Attributes, a => a is ReadOnlyAttribute);
}
#endif
// Helpers
private class PropertyModel
{
[Required]
public int LocalAttributes { get; set; }
[Range(10, 100)]
public string MetadataAttributes { get; set; }
[Required]
[Range(10, 100)]
public double MixedAttributes { get; set; }
}
private class BaseType
{
public string TestProperty { get; set; }
}
private class ModelWithIndexer : BaseType
{
public string this[string x]
{
get { return string.Empty; }
set { }
}
[MinLength(4)]
public string Value { get; set; }
}
#if ASPNET50 // No [ReadOnly] in K
[ReadOnly(true)]
private class TypeModel
{
}
#endif
private class TestableAssociatedMetadataProvider : AssociatedMetadataProvider<ModelMetadata>
{
public List<CreateMetadataPrototypeParams> CreateMetadataPrototypeLog = new List<CreateMetadataPrototypeParams>();
public List<CreateMetadataFromPrototypeParams> CreateMetadataFromPrototypeLog = new List<CreateMetadataFromPrototypeParams>();
public ModelMetadata CreateMetadataPrototypeReturnValue = null;
public ModelMetadata CreateMetadataFromPrototypeReturnValue = null;
protected override ModelMetadata CreateMetadataPrototype(IEnumerable<object> attributes, Type containerType, Type modelType, string propertyName)
{
CreateMetadataPrototypeLog.Add(new CreateMetadataPrototypeParams
{
Attributes = attributes,
ContainerType = containerType,
ModelType = modelType,
PropertyName = propertyName
});
return CreateMetadataPrototypeReturnValue;
}
protected override ModelMetadata CreateMetadataFromPrototype(ModelMetadata prototype)
{
CreateMetadataFromPrototypeLog.Add(new CreateMetadataFromPrototypeParams
{
Prototype = prototype,
});
return CreateMetadataFromPrototypeReturnValue;
}
}
private class CreateMetadataPrototypeParams
{
public IEnumerable<object> Attributes { get; set; }
public Type ContainerType { get; set; }
public Type ModelType { get; set; }
public string PropertyName { get; set; }
}
private class CreateMetadataFromPrototypeParams
{
public ModelMetadata Prototype { get; set; }
}
}
}

View File

@ -1,166 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using Xunit;
namespace Microsoft.AspNet.Mvc.ModelBinding
{
/// <summary>
/// Test the <see cref="CachedDataAnnotationsMetadataAttributes" /> class.
/// </summary>
public class CachedDataAnnotationsMetadataAttributesTest
{
[Fact]
public void Constructor_SetsDefaultValuesForAllProperties()
{
// Arrange
var attributes = Enumerable.Empty<Attribute>();
// Act
var cache = new CachedDataAnnotationsMetadataAttributes(attributes);
// Assert
Assert.Null(cache.DataType);
Assert.Null(cache.Display);
Assert.Null(cache.DisplayColumn);
Assert.Null(cache.DisplayFormat);
Assert.Null(cache.Editable);
Assert.Null(cache.HiddenInput);
Assert.Null(cache.Required);
Assert.Null(cache.ScaffoldColumn);
Assert.Null(cache.UIHint);
Assert.Null(cache.BinderMetadata);
Assert.Null(cache.BinderModelNameProvider);
Assert.Empty(cache.PropertyBindingPredicateProviders);
}
public static TheoryData<object, Func<CachedDataAnnotationsMetadataAttributes, object>>
ExpectedAttributeData
{
get
{
return new TheoryData<object, Func<CachedDataAnnotationsMetadataAttributes, object>>
{
{ new DataTypeAttribute(DataType.Duration), cache => cache.DataType },
{ new DisplayAttribute(), cache => cache.Display },
{ new DisplayColumnAttribute("Property"), cache => cache.DisplayColumn },
{ new DisplayFormatAttribute(), cache => cache.DisplayFormat },
{ new EditableAttribute(allowEdit: false), cache => cache.Editable },
{ new HiddenInputAttribute(), cache => cache.HiddenInput },
{ new RequiredAttribute(), cache => cache.Required },
{ new ScaffoldColumnAttribute(scaffold: true), cache => cache.ScaffoldColumn },
{ new UIHintAttribute("hintHint"), cache => cache.UIHint },
{ new TestBinderMetadata(), cache => cache.BinderMetadata },
{ new TestModelNameProvider(), cache => cache.BinderModelNameProvider },
};
}
}
[Theory]
[MemberData(nameof(ExpectedAttributeData))]
public void Constructor_FindsExpectedAttribute(
object attribute,
Func<CachedDataAnnotationsMetadataAttributes, object> accessor)
{
// Arrange
var attributes = new[] { attribute };
// Act
var cache = new CachedDataAnnotationsMetadataAttributes(attributes);
var result = accessor(cache);
// Assert
Assert.Same(attribute, result);
}
[Fact]
public void Constructor_FindsPropertyBindingInfo()
{
// Arrange
var providers = new[] { new TestPredicateProvider(), new TestPredicateProvider() };
// Act
var cache = new CachedDataAnnotationsMetadataAttributes(providers);
var result = cache.PropertyBindingPredicateProviders.ToArray();
// Assert
Assert.Equal(providers.Length, result.Length);
for (var index = 0; index < providers.Length; index++)
{
Assert.Same(providers[index], result[index]);
}
}
[Fact]
public void Constructor_FindsBinderTypeProviders()
{
// Arrange
var providers = new[] { new TestBinderTypeProvider(), new TestBinderTypeProvider() };
// Act
var cache = new CachedDataAnnotationsMetadataAttributes(providers);
var result = cache.BinderTypeProviders.ToArray();
// Assert
Assert.Equal(providers.Length, result.Length);
for (var index = 0; index < providers.Length; index++)
{
Assert.Same(providers[index], result[index]);
}
}
[Fact]
public void Constructor_FindsDisplayFormat_FromDataType()
{
// Arrange
var dataType = new DataTypeAttribute(DataType.Currency);
var displayFormat = dataType.DisplayFormat; // Non-null for DataType.Currency.
var attributes = new[] { dataType, };
// Act
var cache = new CachedDataAnnotationsMetadataAttributes(attributes);
var result = cache.DisplayFormat;
// Assert
Assert.Same(displayFormat, result);
}
[Fact]
public void Constructor_FindsDisplayFormat_OverridingDataType()
{
// Arrange
var dataType = new DataTypeAttribute(DataType.Time); // Has a non-null DisplayFormat.
var displayFormat = new DisplayFormatAttribute();
var attributes = new Attribute[] { dataType, displayFormat, };
// Act
var cache = new CachedDataAnnotationsMetadataAttributes(attributes);
var result = cache.DisplayFormat;
// Assert
Assert.Same(displayFormat, result);
}
private class TestBinderTypeProvider : IBinderTypeProviderMetadata
{
public Type BinderType { get; set; }
public BindingSource BindingSource { get; set; }
}
private class TestPredicateProvider : IPropertyBindingPredicateProvider
{
public Func<ModelBindingContext, string, bool> PropertyFilter
{
get
{
throw new NotImplementedException();
}
}
}
}
}

View File

@ -1,375 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
using Xunit;
namespace Microsoft.AspNet.Mvc.ModelBinding
{
public class CachedDataAnnotationsModelMetadataProviderTest
{
[Fact]
public void DataAnnotationsModelMetadataProvider_UsesPredicateOnType()
{
// Arrange
var type = typeof(User);
var provider = new DataAnnotationsModelMetadataProvider();
var context = new ModelBindingContext();
var expected = new[] { "IsAdmin", "UserName" };
// Act
var metadata = provider.GetMetadataForType(type);
// Assert
var predicate = metadata.PropertyBindingPredicateProvider.PropertyFilter;
var matched = new HashSet<string>();
foreach (var property in metadata.Properties)
{
if (predicate(context, property.PropertyName))
{
matched.Add(property.PropertyName);
}
}
Assert.Equal<string>(expected, matched);
}
[Fact]
public void DataAnnotationsModelMetadataProvider_UsesPredicateOnParameter()
{
// Arrange
var type = GetType();
var methodInfo = type.GetMethod(
"ActionWithoutBindAttribute",
BindingFlags.Instance | BindingFlags.NonPublic);
var provider = new DataAnnotationsModelMetadataProvider();
var context = new ModelBindingContext();
// Note it does an intersection for included -- only properties that
// pass both predicates will be bound.
var expected = new[] { "IsAdmin", "UserName" };
// Act
var metadata = provider.GetMetadataForParameter(
methodInfo: methodInfo,
parameterName: "param");
// Assert
var predicate = metadata.PropertyBindingPredicateProvider.PropertyFilter;
Assert.NotNull(predicate);
var matched = new HashSet<string>();
foreach (var property in metadata.Properties)
{
if (predicate(context, property.PropertyName))
{
matched.Add(property.PropertyName);
}
}
Assert.Equal<string>(expected, matched);
}
[Fact]
public void DataAnnotationsModelMetadataProvider_UsesPredicateOnParameter_Merge()
{
// Arrange
var type = GetType();
var methodInfo = type.GetMethod(
"ActionWithBindAttribute",
BindingFlags.Instance | BindingFlags.NonPublic);
var provider = new DataAnnotationsModelMetadataProvider();
var context = new ModelBindingContext();
// Note it does an intersection for included -- only properties that
// pass both predicates will be bound.
var expected = new[] { "IsAdmin" };
// Act
var metadata = provider.GetMetadataForParameter(
methodInfo: methodInfo,
parameterName: "param");
// Assert
var predicate = metadata.PropertyBindingPredicateProvider.PropertyFilter;
Assert.NotNull(predicate);
var matched = new HashSet<string>();
foreach (var property in metadata.Properties)
{
if (predicate(context, property.PropertyName))
{
matched.Add(property.PropertyName);
}
}
Assert.Equal<string>(expected, matched);
}
[Fact]
public void DataAnnotationsModelMetadataProvider_ReadsModelNameProperty_ForParameters()
{
// Arrange
var type = GetType();
var methodInfo = type.GetMethod(
"ActionWithBindAttribute",
BindingFlags.Instance | BindingFlags.NonPublic);
var provider = new DataAnnotationsModelMetadataProvider();
// Act
var metadata = provider.GetMetadataForParameter(
methodInfo: methodInfo,
parameterName: "param");
// Assert
Assert.Equal("ParameterPrefix", metadata.BinderModelName);
}
[Fact]
public void DataAnnotationsModelMetadataProvider_ReadsModelNameProperty_ForTypes()
{
// Arrange
var type = typeof(User);
var provider = new DataAnnotationsModelMetadataProvider();
// Act
var metadata = provider.GetMetadataForType(type);
// Assert
Assert.Equal("TypePrefix", metadata.BinderModelName);
}
[Fact]
public void DataAnnotationsModelMetadataProvider_ReadsScaffoldColumnAttribute_ForShowForDisplay()
{
// Arrange
var type = typeof(ScaffoldColumnModel);
var provider = new DataAnnotationsModelMetadataProvider();
// Act & Assert
Assert.True(provider.GetMetadataForProperty(type, "NoAttribute").ShowForDisplay);
Assert.True(provider.GetMetadataForProperty(type, "ScaffoldColumnTrue").ShowForDisplay);
Assert.False(provider.GetMetadataForProperty(type, "ScaffoldColumnFalse").ShowForDisplay);
}
[Fact]
public void DataAnnotationsModelMetadataProvider_ReadsScaffoldColumnAttribute_ForShowForEdit()
{
// Arrange
var type = typeof(ScaffoldColumnModel);
var provider = new DataAnnotationsModelMetadataProvider();
// Act & Assert
Assert.True(provider.GetMetadataForProperty(type, "NoAttribute").ShowForEdit);
Assert.True(provider.GetMetadataForProperty(type, "ScaffoldColumnTrue").ShowForEdit);
Assert.False(provider.GetMetadataForProperty(type, "ScaffoldColumnFalse").ShowForEdit);
}
[Fact]
public void HiddenInputWorksOnProperty_ForHideSurroundingHtml()
{
// Arrange
var provider = new DataAnnotationsModelMetadataProvider();
var metadata = provider.GetMetadataForType(modelType: typeof(ClassWithHiddenProperties));
var property = metadata.Properties["DirectlyHidden"];
// Act
var result = property.HideSurroundingHtml;
// Assert
Assert.True(result);
}
[Fact]
public void HiddenInputWorksOnPropertyType_ForHideSurroundingHtml()
{
// Arrange
var provider = new DataAnnotationsModelMetadataProvider();
var metadata = provider.GetMetadataForType(typeof(ClassWithHiddenProperties));
var property = metadata.Properties["OfHiddenType"];
// Act
var result = property.HideSurroundingHtml;
// Assert
Assert.True(result);
}
[Fact]
public void HiddenInputWorksOnProperty_ForTemplateHint()
{
// Arrange
var provider = new DataAnnotationsModelMetadataProvider();
var metadata = provider.GetMetadataForType(typeof(ClassWithHiddenProperties));
var property = metadata.Properties["DirectlyHidden"];
// Act
var result = property.TemplateHint;
// Assert
Assert.Equal("HiddenInput", result);
}
[Fact]
public void HiddenInputWorksOnPropertyType_ForTemplateHint()
{
// Arrange
var provider = new DataAnnotationsModelMetadataProvider();
var metadata = provider.GetMetadataForType(typeof(ClassWithHiddenProperties));
var property = metadata.Properties["OfHiddenType"];
// Act
var result = property.TemplateHint;
// Assert
Assert.Equal("HiddenInput", result);
}
[Fact]
public void GetMetadataForProperty_WithNoBinderMetadata_GetsItFromType()
{
// Arrange
var provider = new DataAnnotationsModelMetadataProvider();
// Act
var propertyMetadata = provider.GetMetadataForProperty(typeof(Person), nameof(Person.Parent));
// Assert
Assert.NotNull(propertyMetadata.BinderMetadata);
var attribute = Assert.IsType<TypeBasedBinderAttribute>(propertyMetadata.BinderMetadata);
Assert.Equal("PersonType", propertyMetadata.BinderModelName);
}
[Fact]
public void GetMetadataForProperty_WithBinderMetadataOnPropertyAndType_GetsMetadataFromProperty()
{
// Arrange
var provider = new DataAnnotationsModelMetadataProvider();
// Act
var propertyMetadata = provider.GetMetadataForProperty(typeof(Person), nameof(Person.GrandParent));
// Assert
Assert.NotNull(propertyMetadata.BinderMetadata);
var attribute = Assert.IsType<NonTypeBasedBinderAttribute>(propertyMetadata.BinderMetadata);
Assert.Equal("GrandParentProperty", propertyMetadata.BinderModelName);
}
[Fact]
public void GetMetadataForParameter_WithNoBinderMetadata_GetsItFromType()
{
// Arrange
var provider = new DataAnnotationsModelMetadataProvider();
// Act
var parameterMetadata = provider.GetMetadataForParameter(
typeof(Person).GetMethod("Update"),
"person");
// Assert
Assert.NotNull(parameterMetadata.BinderMetadata);
var attribute = Assert.IsType<TypeBasedBinderAttribute>(parameterMetadata.BinderMetadata);
Assert.Equal("PersonType", parameterMetadata.BinderModelName);
}
[Fact]
public void GetMetadataForParameter_WithBinderDataOnParameterAndType_GetsMetadataFromParameter()
{
// Arrange
var provider = new DataAnnotationsModelMetadataProvider();
// Act
var parameterMetadata = provider.GetMetadataForParameter(
typeof(Person).GetMethod("Save"),
"person");
// Assert
Assert.NotNull(parameterMetadata.BinderMetadata);
var attribute = Assert.IsType<NonTypeBasedBinderAttribute>(parameterMetadata.BinderMetadata);
Assert.Equal("PersonParameter", parameterMetadata.BinderModelName);
}
private void ActionWithoutBindAttribute(User param)
{
}
private void ActionWithBindAttribute([Bind(new string[] { "IsAdmin" }, Prefix = "ParameterPrefix")] User param)
{
}
public class TypeBasedBinderAttribute : Attribute, IBinderMetadata, IModelNameProvider
{
public string Name { get; set; }
}
public class NonTypeBasedBinderAttribute : Attribute, IBinderMetadata, IModelNameProvider
{
public string Name { get; set; }
}
[TypeBasedBinder(Name = "PersonType")]
public class Person
{
public Person Parent { get; set; }
[NonTypeBasedBinder(Name = "GrandParentProperty")]
public Person GrandParent { get; set; }
public void Update(Person person)
{
}
public void Save([NonTypeBasedBinder(Name = "PersonParameter")] Person person)
{
}
}
private class ScaffoldColumnModel
{
public int NoAttribute { get; set; }
[ScaffoldColumn(scaffold: true)]
public int ScaffoldColumnTrue { get; set; }
[ScaffoldColumn(scaffold: false)]
public int ScaffoldColumnFalse { get; set; }
}
[HiddenInput(DisplayValue = false)]
private class HiddenClass
{
public string Property { get; set; }
}
private class ClassWithHiddenProperties
{
[HiddenInput(DisplayValue = false)]
public string DirectlyHidden { get; set; }
public HiddenClass OfHiddenType { get; set; }
}
[Bind(new[] { nameof(IsAdmin), nameof(UserName) }, Prefix = "TypePrefix")]
private class User
{
public int Id { get; set; }
public bool IsAdmin { get; set; }
public int UserName { get; set; }
public int NotIncludedOrExcluded { get; set; }
}
}
}

View File

@ -1,614 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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.ComponentModel.DataAnnotations;
using System.Linq;
using Xunit;
namespace Microsoft.AspNet.Mvc.ModelBinding
{
/// <summary>
/// Test the <see cref="CachedDataAnnotationsModelMetadata" /> class.
/// </summary>
public class CachedDataAnnotationsModelMetadataTest
{
[Fact]
public void Constructor_DefersDefaultsToBaseModelMetadata()
{
// Arrange
var attributes = Enumerable.Empty<Attribute>();
var provider = new DataAnnotationsModelMetadataProvider();
// Act
var metadata = new CachedDataAnnotationsModelMetadata(
provider,
containerType: null,
modelType: typeof(object),
propertyName: null,
attributes: attributes);
// Assert
Assert.NotNull(metadata.AdditionalValues);
Assert.Empty(metadata.AdditionalValues);
Assert.Null(metadata.ContainerType);
Assert.True(metadata.ConvertEmptyStringToNull);
Assert.False(metadata.HasNonDefaultEditFormat);
Assert.False(metadata.HideSurroundingHtml);
Assert.True(metadata.HtmlEncode);
Assert.False(metadata.IsCollectionType);
Assert.True(metadata.IsComplexType);
Assert.False(metadata.IsNullableValueType);
Assert.False(metadata.IsReadOnly);
Assert.False(metadata.IsRequired);
Assert.True(metadata.ShowForDisplay);
Assert.True(metadata.ShowForEdit);
Assert.Null(metadata.DataTypeName);
Assert.Null(metadata.Description);
Assert.Null(metadata.DisplayFormatString);
Assert.Null(metadata.DisplayName);
Assert.Null(metadata.EditFormatString);
Assert.Null(metadata.NullDisplayText);
Assert.Null(metadata.SimpleDisplayProperty);
Assert.Null(metadata.TemplateHint);
Assert.Equal(typeof(object), metadata.ModelType);
Assert.Null(metadata.PropertyName);
Assert.Equal(ModelMetadata.DefaultOrder, metadata.Order);
Assert.Null(metadata.BinderModelName);
Assert.Null(metadata.BinderMetadata);
Assert.Null(metadata.PropertyBindingPredicateProvider);
Assert.Null(metadata.BinderType);
}
public static TheoryData<object, Func<ModelMetadata, string>> ExpectedAttributeDataStrings
{
get
{
return new TheoryData<object, Func<ModelMetadata, string>>
{
{
new DataTypeAttribute("value"), metadata => metadata.DataTypeName
},
{
new DataTypeWithCustomDisplayFormat(), metadata => metadata.DisplayFormatString
},
{
new DataTypeWithCustomEditFormat(), metadata => metadata.EditFormatString
},
{
new DisplayAttribute { Description = "value" }, metadata => metadata.Description
},
{
new DisplayAttribute { Name = "value" }, metadata => metadata.DisplayName
},
{
new DisplayFormatAttribute { DataFormatString = "value" },
metadata => metadata.DisplayFormatString
},
{
// DisplayFormatString does not ignore [DisplayFormat] if ApplyFormatInEditMode==true.
new DisplayFormatAttribute { ApplyFormatInEditMode = true, DataFormatString = "value" },
metadata => metadata.DisplayFormatString
},
{
new DisplayFormatAttribute { ApplyFormatInEditMode = true, DataFormatString = "value" },
metadata => metadata.EditFormatString
},
{
new DisplayFormatAttribute { NullDisplayText = "value" }, metadata => metadata.NullDisplayText
},
{
new TestModelNameProvider { Name = "value" }, metadata => metadata.BinderModelName
},
{
new UIHintAttribute("value"), metadata => metadata.TemplateHint
},
};
}
}
[Theory]
[MemberData(nameof(ExpectedAttributeDataStrings))]
public void AttributesOverrideMetadataStrings(object attribute, Func<ModelMetadata, string> accessor)
{
// Arrange
var attributes = new[] { attribute };
var provider = new DataAnnotationsModelMetadataProvider();
var metadata = new CachedDataAnnotationsModelMetadata(
provider,
containerType: null,
modelType: typeof(ClassWithDisplayableColumn),
propertyName: null,
attributes: attributes);
// Act
var result = accessor(metadata);
// Assert
Assert.Equal("value", result);
}
[Fact]
public void AttributesOverrideMetadataStrings_SimpleDisplayProperty()
{
// Arrange
var attributes = new[] { new DisplayColumnAttribute("Property") };
var provider = new DataAnnotationsModelMetadataProvider();
var metadata = new CachedDataAnnotationsModelMetadata(
provider,
containerType: null,
modelType: typeof(ClassWithDisplayableColumn),
propertyName: null,
attributes: attributes);
// Act
var result = metadata.SimpleDisplayProperty;
// Assert
Assert.Equal("Property", result);
}
public static TheoryData<Attribute, Func<ModelMetadata, bool>, bool> ExpectedAttributeDataBooleans
{
get
{
return new TheoryData<Attribute, Func<ModelMetadata, bool>, bool>
{
{
// Edit formats from [DataType] subclass affect HasNonDefaultEditFormat.
new DataTypeWithCustomEditFormat(),
metadata => metadata.HasNonDefaultEditFormat,
true
},
{
// Edit formats from [DataType] do not affect HasNonDefaultEditFormat.
new DataTypeAttribute(DataType.Date),
metadata => metadata.HasNonDefaultEditFormat,
false
},
{
new DisplayFormatAttribute { ConvertEmptyStringToNull = false },
metadata => metadata.ConvertEmptyStringToNull,
false
},
{
new DisplayFormatAttribute { ConvertEmptyStringToNull = true },
metadata => metadata.ConvertEmptyStringToNull,
true
},
{
// Changes only to DisplayFormatString do not affect HasNonDefaultEditFormat.
new DisplayFormatAttribute { DataFormatString = "value" },
metadata => metadata.HasNonDefaultEditFormat,
false
},
{
new DisplayFormatAttribute { ApplyFormatInEditMode = true, DataFormatString = "value" },
metadata => metadata.HasNonDefaultEditFormat,
true
},
{
new DisplayFormatAttribute { HtmlEncode = false },
metadata => metadata.HtmlEncode,
false
},
{
new DisplayFormatAttribute { HtmlEncode = true },
metadata => metadata.HtmlEncode,
true
},
{
new EditableAttribute(allowEdit: false),
metadata => metadata.IsReadOnly,
true
},
{
new EditableAttribute(allowEdit: true),
metadata => metadata.IsReadOnly,
false
},
{
new HiddenInputAttribute { DisplayValue = false },
metadata => metadata.HideSurroundingHtml,
true
},
{
new HiddenInputAttribute { DisplayValue = true },
metadata => metadata.HideSurroundingHtml,
false
},
{
new HiddenInputAttribute(),
metadata => string.Equals("HiddenInput", metadata.TemplateHint, StringComparison.Ordinal),
true
},
{
new RequiredAttribute(),
metadata => metadata.IsRequired,
true
},
};
}
}
[Theory]
[MemberData(nameof(ExpectedAttributeDataBooleans))]
public void AttributesOverrideMetadataBooleans(
Attribute attribute,
Func<ModelMetadata, bool> accessor,
bool expectedResult)
{
// Arrange
var attributes = new[] { attribute };
var provider = new DataAnnotationsModelMetadataProvider();
var metadata = new CachedDataAnnotationsModelMetadata(
provider,
containerType: null,
modelType: typeof(object),
propertyName: null,
attributes: attributes);
// Act
var result = accessor(metadata);
// Assert
Assert.Equal(expectedResult, result);
}
public static TheoryData<DisplayAttribute, int> DisplayAttribute_OverridesOrderData
{
get
{
return new TheoryData<DisplayAttribute, int>
{
{
new DisplayAttribute(), ModelMetadata.DefaultOrder
},
{
new DisplayAttribute { Order = int.MinValue }, int.MinValue
},
{
new DisplayAttribute { Order = -100 }, -100
},
{
new DisplayAttribute { Order = -1 }, -1
},
{
new DisplayAttribute { Order = 0 }, 0
},
{
new DisplayAttribute { Order = 1 }, 1
},
{
new DisplayAttribute { Order = 200 }, 200
},
{
new DisplayAttribute { Order = int.MaxValue }, int.MaxValue
},
};
}
}
[Theory]
[MemberData(nameof(DisplayAttribute_OverridesOrderData))]
public void DisplayAttribute_OverridesOrder(DisplayAttribute attribute, int expectedOrder)
{
// Arrange
var attributes = new[] { attribute };
var provider = new DataAnnotationsModelMetadataProvider();
var metadata = new CachedDataAnnotationsModelMetadata(
provider,
containerType: null,
modelType: typeof(object),
propertyName: null,
attributes: attributes);
// Act
var result = metadata.Order;
// Assert
Assert.Equal(expectedOrder, result);
}
[Fact]
public void BinderMetadataIfPresent_Overrides_DefaultBinderMetadata()
{
// Arrange
var firstBinderMetadata = new TestBinderMetadata();
var secondBinderMetadata = new TestBinderMetadata();
var provider = new DataAnnotationsModelMetadataProvider();
var metadata = new CachedDataAnnotationsModelMetadata(
provider,
containerType: null,
modelType: typeof(object),
propertyName: null,
attributes: new object[] { firstBinderMetadata, secondBinderMetadata });
// Act
var result = metadata.BinderMetadata;
// Assert
Assert.Same(firstBinderMetadata, result);
}
[Fact]
public void DataTypeName_Null_IfHtmlEncodeTrue()
{
// Arrange
var displayFormat = new DisplayFormatAttribute { HtmlEncode = true, };
var provider = new DataAnnotationsModelMetadataProvider();
var metadata = new CachedDataAnnotationsModelMetadata(
provider,
containerType: null,
modelType: typeof(object),
propertyName: null,
attributes: new Attribute[] { displayFormat });
// Act
var result = metadata.DataTypeName;
// Assert
Assert.Null(result);
}
[Fact]
public void DataTypeName_Html_IfHtmlEncodeFalse()
{
// Arrange
var expected = "Html";
var displayFormat = new DisplayFormatAttribute { HtmlEncode = false, };
var provider = new DataAnnotationsModelMetadataProvider();
var metadata = new CachedDataAnnotationsModelMetadata(
provider,
containerType: null,
modelType: typeof(object),
propertyName: null,
attributes: new Attribute[] { displayFormat });
// Act
var result = metadata.DataTypeName;
// Assert
Assert.Equal(expected, result);
}
[Fact]
public void DataTypeName_AttributesHaveExpectedPrecedence()
{
// Arrange
var expected = "MultilineText";
var dataType = new DataTypeAttribute(DataType.MultilineText);
var displayFormat = new DisplayFormatAttribute { HtmlEncode = false, };
var provider = new DataAnnotationsModelMetadataProvider();
var metadata = new CachedDataAnnotationsModelMetadata(
provider,
containerType: null,
modelType: typeof(object),
propertyName: null,
attributes: new Attribute[] { dataType, displayFormat });
// Act
var result = metadata.DataTypeName;
// Assert
Assert.Equal(expected, result);
}
[Fact]
public void DisplayFormatString_AttributesHaveExpectedPrecedence()
{
// Arrange
var expected = "custom format";
var dataType = new DataTypeAttribute(DataType.Currency);
var displayFormat = new DisplayFormatAttribute { DataFormatString = expected, };
var provider = new DataAnnotationsModelMetadataProvider();
var metadata = new CachedDataAnnotationsModelMetadata(
provider,
containerType: null,
modelType: typeof(object),
propertyName: null,
attributes: new Attribute[] { dataType, displayFormat });
// Act
var result = metadata.DisplayFormatString;
// Assert
Assert.Equal(expected, result);
}
[Fact]
public void EditFormatString_AttributesHaveExpectedPrecedence()
{
// Arrange
var expected = "custom format";
var dataType = new DataTypeAttribute(DataType.Currency);
var displayFormat = new DisplayFormatAttribute
{
ApplyFormatInEditMode = true,
DataFormatString = expected,
};
var provider = new DataAnnotationsModelMetadataProvider();
var metadata = new CachedDataAnnotationsModelMetadata(
provider,
containerType: null,
modelType: typeof(object),
propertyName: null,
attributes: new Attribute[] { dataType, displayFormat });
// Act
var result = metadata.EditFormatString;
// Assert
Assert.Equal(expected, result);
}
[Fact]
public void EditFormatString_DoesNotAffectDisplayFormat()
{
// Arrange
var provider = new DataAnnotationsModelMetadataProvider();
var metadata = new CachedDataAnnotationsModelMetadata(
provider,
containerType: null,
modelType: typeof(object),
propertyName: null,
attributes: Enumerable.Empty<Attribute>());
// Act
metadata.EditFormatString = "custom format";
// Assert
Assert.Null(metadata.DisplayFormatString);
}
[Fact]
public void DisplayFormatString_DoesNotAffectEditFormat()
{
// Arrange
var provider = new DataAnnotationsModelMetadataProvider();
var metadata = new CachedDataAnnotationsModelMetadata(
provider,
containerType: null,
modelType: typeof(object),
propertyName: null,
attributes: Enumerable.Empty<Attribute>());
// Act
metadata.DisplayFormatString = "custom format";
// Assert
Assert.Null(metadata.EditFormatString);
}
[Fact]
public void TemplateHint_AttributesHaveExpectedPrecedence()
{
// Arrange
var expected = "this is a hint";
var hidden = new HiddenInputAttribute();
var uiHint = new UIHintAttribute(expected);
var provider = new DataAnnotationsModelMetadataProvider();
var metadata = new CachedDataAnnotationsModelMetadata(
provider,
containerType: null,
modelType: typeof(object),
propertyName: null,
attributes: new Attribute[] { hidden, uiHint });
// Act
var result = metadata.TemplateHint;
// Assert
Assert.Equal(expected, result);
}
[Fact]
public void Constructor_FindsBinderTypeProviders_Null()
{
// Arrange
var provider = new DataAnnotationsModelMetadataProvider();
var binderProviders = new[] { new TestBinderTypeProvider(), new TestBinderTypeProvider() };
// Act
var metadata = new CachedDataAnnotationsModelMetadata(
provider,
containerType: null,
modelType: typeof(object),
propertyName: null,
attributes: binderProviders);
// Assert
Assert.Null(metadata.BinderType);
}
[Fact]
public void Constructor_FindsBinderTypeProviders_Fallback()
{
// Arrange
var provider = new DataAnnotationsModelMetadataProvider();
var binderProviders = new[]
{
new TestBinderTypeProvider(),
new TestBinderTypeProvider() { BinderType = typeof(string) }
};
// Act
var metadata = new CachedDataAnnotationsModelMetadata(
provider,
containerType: null,
modelType: typeof(object),
propertyName: null,
attributes: binderProviders);
// Assert
Assert.Same(typeof(string), metadata.BinderType);
}
[Fact]
public void Constructor_FindsBinderTypeProviders_FirstAttributeHasPrecedence()
{
// Arrange
var provider = new DataAnnotationsModelMetadataProvider();
var binderProviders = new[]
{
new TestBinderTypeProvider() { BinderType = typeof(int) },
new TestBinderTypeProvider() { BinderType = typeof(string) }
};
// Act
var metadata = new CachedDataAnnotationsModelMetadata(
provider,
containerType: null,
modelType: typeof(object),
propertyName: null,
attributes: binderProviders);
// Assert
Assert.Same(typeof(int), metadata.BinderType);
}
private class TestBinderTypeProvider : IBinderTypeProviderMetadata
{
public Type BinderType { get; set; }
public BindingSource BindingSource { get; set; }
}
private class DataTypeWithCustomDisplayFormat : DataTypeAttribute
{
public DataTypeWithCustomDisplayFormat() : base("Custom datatype")
{
DisplayFormat = new DisplayFormatAttribute
{
DataFormatString = "value",
};
}
}
private class DataTypeWithCustomEditFormat : DataTypeAttribute
{
public DataTypeWithCustomEditFormat() : base("Custom datatype")
{
DisplayFormat = new DisplayFormatAttribute
{
ApplyFormatInEditMode = true,
DataFormatString = "value",
};
}
}
private class ClassWithDisplayableColumn
{
public string Property { get; set; }
}
}
}

View File

@ -0,0 +1,197 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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.ComponentModel.DataAnnotations;
using Xunit;
namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
{
public class DataAnnotationsMetadataDetailsProviderTest
{
// Includes attributes with a 'simple' effect on display details.
public static TheoryData<object, Func<DisplayMetadata, object>, object> DisplayDetailsData
{
get
{
return new TheoryData<object, Func<DisplayMetadata, object>, object>
{
{ new DataTypeAttribute(DataType.Duration), d => d.DataTypeName, DataType.Duration.ToString() },
{ new DisplayAttribute() { Description = "d" }, d => d.Description, "d" },
{ new DisplayAttribute() { Name = "DN" }, d => d.DisplayName, "DN" },
{ new DisplayAttribute() { Order = 3 }, d => d.Order, 3 },
{ new DisplayColumnAttribute("Property"), d => d.SimpleDisplayProperty, "Property" },
{ new DisplayFormatAttribute() { ConvertEmptyStringToNull = true }, d => d.ConvertEmptyStringToNull, true },
{ new DisplayFormatAttribute() { DataFormatString = "{0:G}" }, d => d.DisplayFormatString, "{0:G}" },
{
new DisplayFormatAttribute() { DataFormatString = "{0:G}", ApplyFormatInEditMode = true },
d => d.EditFormatString,
"{0:G}"
},
{ new DisplayFormatAttribute() { HtmlEncode = false }, d => d.HtmlEncode, false },
{ new DisplayFormatAttribute() { NullDisplayText = "(null)" }, d => d.NullDisplayText, "(null)" },
{ new HiddenInputAttribute() { DisplayValue = false }, d => d.HideSurroundingHtml, true },
{ new ScaffoldColumnAttribute(scaffold: false), d => d.ShowForDisplay, false },
{ new ScaffoldColumnAttribute(scaffold: false), d => d.ShowForEdit, false },
{ new UIHintAttribute("hintHint"), d => d.TemplateHint, "hintHint" },
};
}
}
[Theory]
[MemberData(nameof(DisplayDetailsData))]
public void GetDisplayDetails_SimpleAttributes(
object attribute,
Func<DisplayMetadata, object> accessor,
object expected)
{
// Arrange
var provider = new DataAnnotationsMetadataDetailsProvider();
var key = ModelMetadataIdentity.ForType(typeof(string));
var context = new DisplayMetadataProviderContext(key, new object[] { attribute });
// Act
provider.GetDisplayMetadata(context);
// Assert
var value = accessor(context.DisplayMetadata);
Assert.Equal(expected, value);
}
[Fact]
public void GetDisplayDetails_FindsDisplayFormat_FromDataType()
{
// Arrange
var provider = new DataAnnotationsMetadataDetailsProvider();
var dataType = new DataTypeAttribute(DataType.Currency);
var displayFormat = dataType.DisplayFormat; // Non-null for DataType.Currency.
var attributes = new[] { dataType, };
var key = ModelMetadataIdentity.ForType(typeof(string));
var context = new DisplayMetadataProviderContext(key, attributes);
// Act
provider.GetDisplayMetadata(context);
// Assert
Assert.Same(displayFormat.DataFormatString, context.DisplayMetadata.DisplayFormatString);
}
[Fact]
public void GetDisplayDetails_FindsDisplayFormat_OverridingDataType()
{
// Arrange
var provider = new DataAnnotationsMetadataDetailsProvider();
var dataType = new DataTypeAttribute(DataType.Time); // Has a non-null DisplayFormat.
var displayFormat = new DisplayFormatAttribute() // But these values override the values from DataType
{
DataFormatString = "Cool {0}",
};
var attributes = new Attribute[] { dataType, displayFormat, };
var key = ModelMetadataIdentity.ForType(typeof(string));
var context = new DisplayMetadataProviderContext(key, attributes);
// Act
provider.GetDisplayMetadata(context);
// Assert
Assert.Same(displayFormat.DataFormatString, context.DisplayMetadata.DisplayFormatString);
}
[Fact]
public void GetDisplayDetails_EditableAttribute_SetsReadOnly()
{
// Arrange
var provider = new DataAnnotationsMetadataDetailsProvider();
var editable = new EditableAttribute(allowEdit: false);
var attributes = new Attribute[] { editable };
var key = ModelMetadataIdentity.ForType(typeof(string));
var context = new BindingMetadataProviderContext(key, attributes);
// Act
provider.GetBindingMetadata(context);
// Assert
Assert.Equal(true, context.BindingMetadata.IsReadOnly);
}
[Fact]
public void GetDisplayDetails_RequiredAttribute_SetsRequired()
{
// Arrange
var provider = new DataAnnotationsMetadataDetailsProvider();
var required = new RequiredAttribute();
var attributes = new Attribute[] { required };
var key = ModelMetadataIdentity.ForType(typeof(string));
var context = new BindingMetadataProviderContext(key, attributes);
// Act
provider.GetBindingMetadata(context);
// Assert
Assert.Equal(true, context.BindingMetadata.IsRequired);
}
// This is IMPORTANT. Product code needs to use GetName() instead of .Name. It's easy to regress.
[Fact]
public void GetDisplayDetails_DisplayAttribute_NameFromResources()
{
// Arrange
var provider = new DataAnnotationsMetadataDetailsProvider();
var display = new DisplayAttribute()
{
Name = "DisplayAttribute_Name",
ResourceType = typeof(Test.Resources),
};
var attributes = new Attribute[] { display };
var key = ModelMetadataIdentity.ForType(typeof(string));
var context = new DisplayMetadataProviderContext(key, attributes);
// Act
provider.GetDisplayMetadata(context);
// Assert
Assert.Equal("name from resources", context.DisplayMetadata.DisplayName);
}
// This is IMPORTANT. Product code needs to use GetDescription() instead of .Description. It's easy to regress.
[Fact]
public void GetDisplayDetails_DisplayAttribute_DescriptionFromResources()
{
// Arrange
var provider = new DataAnnotationsMetadataDetailsProvider();
var display = new DisplayAttribute()
{
Description = "DisplayAttribute_Description",
ResourceType = typeof(Test.Resources),
};
var attributes = new Attribute[] { display };
var key = ModelMetadataIdentity.ForType(typeof(string));
var context = new DisplayMetadataProviderContext(key, attributes);
// Act
provider.GetDisplayMetadata(context);
// Assert
Assert.Equal("description from resources", context.DisplayMetadata.Description);
}
}
}

View File

@ -0,0 +1,151 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Xunit;
namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
{
public class DefaultModelMetadataBindingDetailsProviderTest
{
[Fact]
public void GetBindingDetails_FindsBinderTypeProvider()
{
// Arrange
var attributes = new object[]
{
new ModelBinderAttribute() { BinderType = typeof(HeaderModelBinder) },
new ModelBinderAttribute() { BinderType = typeof(ArrayModelBinder<string>) },
};
var context = new BindingMetadataProviderContext(
ModelMetadataIdentity.ForType(typeof(string)),
attributes);
var provider = new DefaultBindingMetadataProvider();
// Act
provider.GetBindingMetadata(context);
// Assert
Assert.Equal(typeof(HeaderModelBinder), context.BindingMetadata.BinderType);
}
[Fact]
public void GetBindingDetails_FindsBinderTypeProvider_IfNullFallsBack()
{
// Arrange
var attributes = new object[]
{
new ModelBinderAttribute(),
new ModelBinderAttribute() { BinderType = typeof(HeaderModelBinder) },
new ModelBinderAttribute() { BinderType = typeof(ArrayModelBinder<string>) },
};
var context = new BindingMetadataProviderContext(
ModelMetadataIdentity.ForType(typeof(string)),
attributes);
var provider = new DefaultBindingMetadataProvider();
// Act
provider.GetBindingMetadata(context);
// Assert
Assert.Equal(typeof(HeaderModelBinder), context.BindingMetadata.BinderType);
}
[Fact]
public void GetBindingDetails_FindsModelName()
{
// Arrange
var attributes = new object[]
{
new ModelBinderAttribute() { Name = "Product" },
new ModelBinderAttribute() { Name = "Order" },
};
var context = new BindingMetadataProviderContext(
ModelMetadataIdentity.ForType(typeof(string)),
attributes);
var provider = new DefaultBindingMetadataProvider();
// Act
provider.GetBindingMetadata(context);
// Assert
Assert.Equal("Product", context.BindingMetadata.BinderModelName);
}
[Fact]
public void GetBindingDetails_FindsModelName_IfEmpty()
{
// Arrange
var attributes = new object[]
{
new ModelBinderAttribute(),
new ModelBinderAttribute() { Name = "Product" },
new ModelBinderAttribute() { Name = "Order" },
};
var context = new BindingMetadataProviderContext(
ModelMetadataIdentity.ForType(typeof(string)),
attributes);
var provider = new DefaultBindingMetadataProvider();
// Act
provider.GetBindingMetadata(context);
// Assert
Assert.Null(context.BindingMetadata.BinderModelName);
}
[Fact]
public void GetBindingDetails_FindsBindingSource()
{
// Arrange
var attributes = new object[]
{
new ModelBinderAttribute() { BindingSource = BindingSource.Body },
new ModelBinderAttribute() { BindingSource = BindingSource.Query },
};
var context = new BindingMetadataProviderContext(
ModelMetadataIdentity.ForType(typeof(string)),
attributes);
var provider = new DefaultBindingMetadataProvider();
// Act
provider.GetBindingMetadata(context);
// Assert
Assert.Equal(BindingSource.Body, context.BindingMetadata.BindingSource);
}
[Fact]
public void GetBindingDetails_FindsBindingSource_IfNullFallsBack()
{
// Arrange
var attributes = new object[]
{
new ModelBinderAttribute(),
new ModelBinderAttribute() { BindingSource = BindingSource.Body },
new ModelBinderAttribute() { BindingSource = BindingSource.Query },
};
var context = new BindingMetadataProviderContext(
ModelMetadataIdentity.ForType(typeof(string)),
attributes);
var provider = new DefaultBindingMetadataProvider();
// Act
provider.GetBindingMetadata(context);
// Assert
Assert.Equal(BindingSource.Body, context.BindingMetadata.BindingSource);
}
}
}

View File

@ -0,0 +1,213 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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.Linq;
using System.Reflection;
using Xunit;
namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
{
public class DefaultModelMetadataProviderTest
{
[Fact]
public void GetMetadataForType_IncludesAttributes()
{
// Arrange
var provider = CreateProvider();
// Act
var metadata = provider.GetMetadataForType(typeof(ModelType));
// Assert
var defaultMetadata = Assert.IsType<DefaultModelMetadata>(metadata);
var attribute = Assert.IsType<ModelAttribute>(Assert.Single(defaultMetadata.Attributes));
Assert.Equal("OnType", attribute.Value);
}
// The attributes and other 'details' are cached
[Fact]
public void GetMetadataForType_Cached()
{
// Arrange
var provider = CreateProvider();
// Act
var metadata1 = Assert.IsType<DefaultModelMetadata>(provider.GetMetadataForType(typeof(ModelType)));
var metadata2 = Assert.IsType<DefaultModelMetadata>(provider.GetMetadataForType(typeof(ModelType)));
// Assert
Assert.Same(metadata1.Attributes, metadata2.Attributes);
Assert.Same(metadata1.BindingMetadata, metadata2.BindingMetadata);
Assert.Same(metadata1.DisplayMetadata, metadata2.DisplayMetadata);
Assert.Same(metadata1.ValidationMetadata, metadata2.ValidationMetadata);
}
[Fact]
public void GetMetadataForProperties_IncludesAllProperties()
{
// Arrange
var provider = CreateProvider();
// Act
var metadata = provider.GetMetadataForProperties(typeof(ModelType)).ToArray();
// Assert
Assert.Equal(2, metadata.Length);
Assert.Single(metadata, m => m.PropertyName == "Property1");
Assert.Single(metadata, m => m.PropertyName == "Property2");
}
[Fact]
public void GetMetadataForProperties_IncludesAllProperties_ExceptIndexer()
{
// Arrange
var provider = CreateProvider();
// Act
var metadata = provider.GetMetadataForProperties(typeof(ModelTypeWithIndexer)).ToArray();
// Assert
Assert.Equal(1, metadata.Length);
Assert.Single(metadata, m => m.PropertyName == "Property1");
}
[Fact]
public void GetMetadataForProperties_Cached()
{
// Arrange
var provider = CreateProvider();
// Act
var metadata1 = provider.GetMetadataForProperties(typeof(ModelType)).Cast<DefaultModelMetadata>().ToArray();
var metadata2 = provider.GetMetadataForProperties(typeof(ModelType)).Cast<DefaultModelMetadata>().ToArray();
// Assert
for (var i = 0; i < metadata1.Length; i++)
{
Assert.Same(metadata1[i].Attributes, metadata2[i].Attributes);
Assert.Same(metadata1[i].BindingMetadata, metadata2[i].BindingMetadata);
Assert.Same(metadata1[i].DisplayMetadata, metadata2[i].DisplayMetadata);
Assert.Same(metadata1[i].ValidationMetadata, metadata2[i].ValidationMetadata);
}
}
[Fact]
public void GetMetadataForProperties_IncludesMergedAttributes()
{
// Arrange
var provider = CreateProvider();
// Act
var metadata = provider.GetMetadataForProperties(typeof(ModelType)).First();
// Assert
var defaultMetadata = Assert.IsType<DefaultModelMetadata>(metadata);
var attributes = defaultMetadata.Attributes.ToArray();
Assert.Equal("OnProperty", Assert.IsType<ModelAttribute>(attributes[0]).Value);
Assert.Equal("OnPropertyType", Assert.IsType<ModelAttribute>(attributes[1]).Value);
}
[Fact]
public void GetMetadataForParameter_IncludesMergedAttributes()
{
// Arrange
var provider = CreateProvider();
var methodInfo = GetType().GetMethod(
"GetMetadataForParameterTestMethod",
BindingFlags.Instance | BindingFlags.NonPublic);
var parameterInfo = methodInfo.GetParameters().Where(p => p.Name == "parameter").Single();
var additionalAttributes = new object[]
{
new ModelAttribute("Extra"),
};
// Act
var metadata = provider.GetMetadataForParameter(parameterInfo, additionalAttributes);
// Assert
var defaultMetadata = Assert.IsType<DefaultModelMetadata>(metadata);
var attributes = defaultMetadata.Attributes.ToArray();
Assert.Equal("Extra", Assert.IsType<ModelAttribute>(attributes[0]).Value);
Assert.Equal("OnParameter", Assert.IsType<ModelAttribute>(attributes[1]).Value);
Assert.Equal("OnType", Assert.IsType<ModelAttribute>(attributes[2]).Value);
}
// The 'attributes' are assumed to be the same every time. We can safely omit them after
// the first call.
[Fact]
public void GetMetadataForParameter_Cached()
{
// Arrange
var provider = CreateProvider();
var methodInfo = GetType().GetMethod(
"GetMetadataForParameterTestMethod",
BindingFlags.Instance | BindingFlags.NonPublic);
var additionalAttributes = new object[]
{
new ModelAttribute("Extra"),
};
// Act
var metadata1 = Assert.IsType<DefaultModelMetadata>(provider.GetMetadataForParameter(
methodInfo.GetParameters().Where(p => p.Name == "parameter").Single(),
additionalAttributes));
var metadata2 = Assert.IsType<DefaultModelMetadata>(provider.GetMetadataForParameter(
methodInfo.GetParameters().Where(p => p.Name == "parameter").Single(),
attributes: null));
// Assert
Assert.Same(metadata1.Attributes, metadata2.Attributes);
Assert.Same(metadata1.BindingMetadata, metadata2.BindingMetadata);
Assert.Same(metadata1.DisplayMetadata, metadata2.DisplayMetadata);
Assert.Same(metadata1.ValidationMetadata, metadata2.ValidationMetadata);
}
private static DefaultModelMetadataProvider CreateProvider()
{
return new DefaultModelMetadataProvider(new EmptyCompositeMetadataDetailsProvider());
}
[Model("OnType")]
private class ModelType
{
[Model("OnProperty")]
public PropertyType Property1 { get; }
public PropertyType Property2 { get; set; }
}
[Model("OnPropertyType")]
private class PropertyType
{
}
private class ModelAttribute : Attribute
{
public ModelAttribute(string value)
{
Value = value;
}
public string Value { get; }
}
private class ModelTypeWithIndexer
{
public PropertyType this[string key] { get { return null; } }
public PropertyType Property1 { get; set; }
}
private void GetMetadataForParameterTestMethod([Model("OnParameter")] ModelType parameter)
{
}
}
}

View File

@ -0,0 +1,438 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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.Generic;
using System.Linq;
using System.Reflection;
#if !ASPNETCORE50
using Moq;
#endif
using Xunit;
namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
{
public class DefaultModelMetadataTest
{
[Fact]
public void DefaultValues()
{
// Arrange
var provider = new EmptyModelMetadataProvider();
var detailsProvider = new DefaultCompositeMetadataDetailsProvider(
Enumerable.Empty<IMetadataDetailsProvider>());
var key = ModelMetadataIdentity.ForType(typeof(string));
var cache = new DefaultMetadataDetailsCache(key, new object[0]);
// Act
var metadata = new DefaultModelMetadata(provider, detailsProvider, cache);
// Assert
Assert.NotNull(metadata.AdditionalValues);
Assert.Empty(metadata.AdditionalValues);
Assert.Equal(typeof(string), metadata.ModelType);
Assert.True(metadata.ConvertEmptyStringToNull);
Assert.False(metadata.HasNonDefaultEditFormat);
Assert.False(metadata.HideSurroundingHtml);
Assert.True(metadata.HtmlEncode);
Assert.False(metadata.IsComplexType);
Assert.False(metadata.IsCollectionType);
Assert.False(metadata.IsNullableValueType);
Assert.False(metadata.IsReadOnly);
Assert.False(metadata.IsRequired);
Assert.True(metadata.ShowForDisplay);
Assert.True(metadata.ShowForEdit);
Assert.Null(metadata.DataTypeName);
Assert.Null(metadata.Description);
Assert.Null(metadata.DisplayFormatString);
Assert.Null(metadata.DisplayName);
Assert.Null(metadata.EditFormatString);
Assert.Null(metadata.NullDisplayText);
Assert.Null(metadata.TemplateHint);
Assert.Null(metadata.SimpleDisplayProperty);
Assert.Equal(10000, ModelMetadata.DefaultOrder);
Assert.Equal(ModelMetadata.DefaultOrder, metadata.Order);
Assert.Null(metadata.BinderModelName);
Assert.Null(metadata.BinderType);
Assert.Null(metadata.BindingSource);
Assert.Null(metadata.PropertyBindingPredicateProvider);
}
[Fact]
public void CreateMetadataForType()
{
// Arrange
var provider = new EmptyModelMetadataProvider();
var detailsProvider = new DefaultCompositeMetadataDetailsProvider(
Enumerable.Empty<IMetadataDetailsProvider>());
var key = ModelMetadataIdentity.ForType(typeof(Exception));
var cache = new DefaultMetadataDetailsCache(key, new object[0]);
// Act
var metadata = new DefaultModelMetadata(provider, detailsProvider, cache);
// Assert
Assert.Equal(typeof(Exception), metadata.ModelType);
Assert.Null(metadata.PropertyName);
Assert.Null(metadata.ContainerType);
}
[Fact]
public void CreateMetadataForProperty()
{
// Arrange
var provider = new EmptyModelMetadataProvider();
var detailsProvider = new EmptyCompositeMetadataDetailsProvider();
var key = ModelMetadataIdentity.ForProperty(typeof(string), "Message", typeof(Exception));
var cache = new DefaultMetadataDetailsCache(key, new object[0]);
// Act
var metadata = new DefaultModelMetadata(provider, detailsProvider, cache);
// Assert
Assert.Equal(typeof(string), metadata.ModelType);
Assert.Equal("Message", metadata.PropertyName);
Assert.Equal(typeof(Exception), metadata.ContainerType);
}
[Fact]
public void CreateMetadataForParameter()
{
// Arrange
var provider = new EmptyModelMetadataProvider();
var detailsProvider = new EmptyCompositeMetadataDetailsProvider();
var methodInfo = GetType().GetMethod(
"ActionMethod",
BindingFlags.Instance | BindingFlags.NonPublic);
var parameterInfo = methodInfo.GetParameters().Where(p => p.Name == "input").Single();
var key = ModelMetadataIdentity.ForParameter(parameterInfo);
var cache = new DefaultMetadataDetailsCache(key, new object[0]);
// Act
var metadata = new DefaultModelMetadata(provider, detailsProvider, cache);
Assert.Equal(typeof(string), metadata.ModelType);
Assert.Equal("input", metadata.PropertyName);
Assert.Null(metadata.ContainerType);
}
[Theory]
[InlineData(typeof(string))]
[InlineData(typeof(IDisposable))]
[InlineData(typeof(Nullable<int>))]
public void IsRequired_ReturnsFalse_ForNullableTypes(Type modelType)
{
// Arrange
var provider = new EmptyModelMetadataProvider();
var detailsProvider = new EmptyCompositeMetadataDetailsProvider();
var key = ModelMetadataIdentity.ForType(modelType);
var cache = new DefaultMetadataDetailsCache(key, new object[0]);
var metadata = new DefaultModelMetadata(provider, detailsProvider, cache);
// Act
var isRequired = metadata.IsRequired;
// Assert
Assert.False(isRequired);
}
[Theory]
[InlineData(typeof(int))]
[InlineData(typeof(DayOfWeek))]
public void IsRequired_ReturnsTrue_ForNonNullableTypes(Type modelType)
{
// Arrange
var provider = new EmptyModelMetadataProvider();
var detailsProvider = new EmptyCompositeMetadataDetailsProvider();
var key = ModelMetadataIdentity.ForType(modelType);
var cache = new DefaultMetadataDetailsCache(key, new object[0]);
var metadata = new DefaultModelMetadata(provider, detailsProvider, cache);
// Act
var isRequired = metadata.IsRequired;
// Assert
Assert.True(isRequired);
}
#if !ASPNETCORE50
[Fact]
public void PropertiesProperty_CallsProvider()
{
// Arrange
var provider = new Mock<IModelMetadataProvider>(MockBehavior.Strict);
var detailsProvider = new EmptyCompositeMetadataDetailsProvider();
var expectedProperties = new DefaultModelMetadata[]
{
new DefaultModelMetadata(
provider.Object,
detailsProvider,
new DefaultMetadataDetailsCache(
ModelMetadataIdentity.ForProperty(typeof(int), "Prop1", typeof(string)),
attributes: null)),
new DefaultModelMetadata(
provider.Object,
detailsProvider,
new DefaultMetadataDetailsCache(
ModelMetadataIdentity.ForProperty(typeof(int), "Prop2", typeof(string)),
attributes: null)),
};
provider
.Setup(p => p.GetMetadataForProperties(typeof(string)))
.Returns(expectedProperties);
var key = ModelMetadataIdentity.ForType(typeof(string));
var cache = new DefaultMetadataDetailsCache(key, new object[0]);
var metadata = new DefaultModelMetadata(provider.Object, detailsProvider, cache);
// Act
var properties = metadata.Properties;
// Assert
Assert.Equal(expectedProperties.Length, properties.Count);
for (var i = 0; i < expectedProperties.Length; i++)
{
Assert.Same(expectedProperties[i], properties[i]);
}
}
// Input (original) property names and expected (ordered) property names.
public static TheoryData<IEnumerable<string>, IEnumerable<string>> PropertyNamesTheoryData
{
get
{
// ModelMetadata does not reorder properties the provider returns without an Order override.
return new TheoryData<IEnumerable<string>, IEnumerable<string>>
{
{
new List<string> { "Property1", "Property2", "Property3", "Property4", },
new List<string> { "Property1", "Property2", "Property3", "Property4", }
},
{
new List<string> { "Property4", "Property3", "Property2", "Property1", },
new List<string> { "Property4", "Property3", "Property2", "Property1", }
},
{
new List<string> { "Delta", "Bravo", "Charlie", "Alpha", },
new List<string> { "Delta", "Bravo", "Charlie", "Alpha", }
},
{
new List<string> { "John", "Jonathan", "Jon", "Joan", },
new List<string> { "John", "Jonathan", "Jon", "Joan", }
},
};
}
}
[Theory]
[MemberData(nameof(PropertyNamesTheoryData))]
public void PropertiesProperty_WithDefaultOrder_OrdersPropertyNamesAsProvided(
IEnumerable<string> originalNames,
IEnumerable<string> expectedNames)
{
// Arrange
var provider = new Mock<IModelMetadataProvider>(MockBehavior.Strict);
var detailsProvider = new EmptyCompositeMetadataDetailsProvider();
var expectedProperties = new List<DefaultModelMetadata>();
foreach (var originalName in originalNames)
{
expectedProperties.Add(new DefaultModelMetadata(
provider.Object,
detailsProvider,
new DefaultMetadataDetailsCache(
ModelMetadataIdentity.ForProperty(typeof(int), originalName, typeof(string)),
attributes: null)));
}
provider
.Setup(p => p.GetMetadataForProperties(typeof(string)))
.Returns(expectedProperties);
var key = ModelMetadataIdentity.ForType(typeof(string));
var cache = new DefaultMetadataDetailsCache(key, new object[0]);
var metadata = new DefaultModelMetadata(provider.Object, detailsProvider, cache);
// Act
var properties = metadata.Properties;
// Assert
Assert.Equal(expectedNames.Count(), properties.Count);
Assert.Equal(expectedNames.ToArray(), properties.Select(p => p.PropertyName).ToArray());
}
// Input (original) property names, Order values, and expected (ordered) property names.
public static TheoryData<IEnumerable<KeyValuePair<string, int>>, IEnumerable<string>>
PropertyNamesAndOrdersTheoryData
{
get
{
return new TheoryData<IEnumerable<KeyValuePair<string, int>>, IEnumerable<string>>
{
{
new List<KeyValuePair<string, int>>
{
new KeyValuePair<string, int>("Property1", 23),
new KeyValuePair<string, int>("Property2", 23),
new KeyValuePair<string, int>("Property3", 23),
new KeyValuePair<string, int>("Property4", 23),
},
new List<string> { "Property1", "Property2", "Property3", "Property4", }
},
// Same order if already ordered using Order.
{
new List<KeyValuePair<string, int>>
{
new KeyValuePair<string, int>("Property4", 23),
new KeyValuePair<string, int>("Property3", 24),
new KeyValuePair<string, int>("Property2", 25),
new KeyValuePair<string, int>("Property1", 26),
},
new List<string> { "Property4", "Property3", "Property2", "Property1", }
},
// Rest of the orderings get updated within ModelMetadata.
{
new List<KeyValuePair<string, int>>
{
new KeyValuePair<string, int>("Property1", 26),
new KeyValuePair<string, int>("Property2", 25),
new KeyValuePair<string, int>("Property3", 24),
new KeyValuePair<string, int>("Property4", 23),
},
new List<string> { "Property4", "Property3", "Property2", "Property1", }
},
{
new List<KeyValuePair<string, int>>
{
new KeyValuePair<string, int>("Alpha", 26),
new KeyValuePair<string, int>("Bravo", 24),
new KeyValuePair<string, int>("Charlie", 23),
new KeyValuePair<string, int>("Delta", 25),
},
new List<string> { "Charlie", "Bravo", "Delta", "Alpha", }
},
// Jonathan and Jon will not be reordered.
{
new List<KeyValuePair<string, int>>
{
new KeyValuePair<string, int>("Joan", 1),
new KeyValuePair<string, int>("Jonathan", 0),
new KeyValuePair<string, int>("Jon", 0),
new KeyValuePair<string, int>("John", -1),
},
new List<string> { "John", "Jonathan", "Jon", "Joan", }
},
};
}
}
[Theory]
[MemberData(nameof(PropertyNamesAndOrdersTheoryData))]
public void PropertiesProperty_OrdersPropertyNamesUsingOrder_ThenAsProvided(
IEnumerable<KeyValuePair<string, int>> originalNamesAndOrders,
IEnumerable<string> expectedNames)
{
// Arrange
var provider = new Mock<IModelMetadataProvider>(MockBehavior.Strict);
var detailsProvider = new EmptyCompositeMetadataDetailsProvider();
var expectedProperties = new List<DefaultModelMetadata>();
foreach (var kvp in originalNamesAndOrders)
{
var propertyCache = new DefaultMetadataDetailsCache(
ModelMetadataIdentity.ForProperty(typeof(int), kvp.Key, typeof(string)),
attributes: null);
propertyCache.DisplayMetadata = new DisplayMetadata();
propertyCache.DisplayMetadata.Order = kvp.Value;
expectedProperties.Add(new DefaultModelMetadata(
provider.Object,
detailsProvider,
propertyCache));
}
provider
.Setup(p => p.GetMetadataForProperties(typeof(string)))
.Returns(expectedProperties);
var key = ModelMetadataIdentity.ForType(typeof(string));
var cache = new DefaultMetadataDetailsCache(key, new object[0]);
var metadata = new DefaultModelMetadata(provider.Object, detailsProvider, cache);
// Act
var properties = metadata.Properties;
// Assert
Assert.Equal(expectedNames.Count(), properties.Count);
Assert.Equal(expectedNames.ToArray(), properties.Select(p => p.PropertyName).ToArray());
}
#endif
[Fact]
public void PropertiesSetOnce()
{
// Arrange
var provider = new EmptyModelMetadataProvider();
var detailsProvider = new EmptyCompositeMetadataDetailsProvider();
var key = ModelMetadataIdentity.ForType(typeof(string));
var cache = new DefaultMetadataDetailsCache(key, new object[0]);
var metadata = new DefaultModelMetadata(provider, detailsProvider, cache);
// Act
var firstPropertiesEvaluation = metadata.Properties;
var secondPropertiesEvaluation = metadata.Properties;
// Assert
// Same IEnumerable<ModelMetadata> object.
Assert.Same(firstPropertiesEvaluation, secondPropertiesEvaluation);
}
[Fact]
public void PropertiesEnumerationEvaluatedOnce()
{
// Arrange
var provider = new EmptyModelMetadataProvider();
var detailsProvider = new EmptyCompositeMetadataDetailsProvider();
var key = ModelMetadataIdentity.ForType(typeof(string));
var cache = new DefaultMetadataDetailsCache(key, new object[0]);
var metadata = new DefaultModelMetadata(provider, detailsProvider, cache);
// Act
var firstPropertiesEvaluation = metadata.Properties.ToList();
var secondPropertiesEvaluation = metadata.Properties.ToList();
// Assert
// Identical ModelMetadata objects every time we run through the Properties collection.
Assert.Equal(firstPropertiesEvaluation, secondPropertiesEvaluation);
}
private void ActionMethod(string input)
{
}
}
}

View File

@ -0,0 +1,13 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
{
public class EmptyCompositeMetadataDetailsProvider : DefaultCompositeMetadataDetailsProvider
{
public EmptyCompositeMetadataDetailsProvider()
: base(new IMetadataDetailsProvider[0])
{
}
}
}

View File

@ -0,0 +1,868 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
using Microsoft.Framework.Internal;
using Xunit;
namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
{
// Integration tests for the default provider configuration.
public class ModelMetadataProviderTest
{
[Fact]
public void ModelMetadataProvider_UsesPredicateOnType()
{
// Arrange
var type = typeof(User);
var provider = CreateProvider();
var context = new ModelBindingContext();
var expected = new[] { "IsAdmin", "UserName" };
// Act
var metadata = provider.GetMetadataForType(type);
// Assert
var predicate = metadata.PropertyBindingPredicateProvider.PropertyFilter;
var matched = new HashSet<string>();
foreach (var property in metadata.Properties)
{
if (predicate(context, property.PropertyName))
{
matched.Add(property.PropertyName);
}
}
Assert.Equal<string>(expected, matched);
}
[Fact]
public void ModelMetadataProvider_UsesPredicateOnParameter()
{
// Arrange
var type = GetType();
var methodInfo = type.GetMethod(
"ActionWithoutBindAttribute",
BindingFlags.Instance | BindingFlags.NonPublic);
var parameterInfo = methodInfo.GetParameters().Where(p => p.Name == "param").Single();
var provider = CreateProvider();
var context = new ModelBindingContext();
// Note it does an intersection for included -- only properties that
// pass both predicates will be bound.
var expected = new[] { "IsAdmin", "UserName" };
// Act
var metadata = provider.GetMetadataForParameter(
parameterInfo,
attributes: null);
// Assert
var predicate = metadata.PropertyBindingPredicateProvider.PropertyFilter;
Assert.NotNull(predicate);
var matched = new HashSet<string>();
foreach (var property in metadata.Properties)
{
if (predicate(context, property.PropertyName))
{
matched.Add(property.PropertyName);
}
}
Assert.Equal<string>(expected, matched);
}
[Fact]
public void ModelMetadataProvider_UsesPredicateOnParameter_Merge()
{
// Arrange
var type = GetType();
var methodInfo = type.GetMethod(
"ActionWithBindAttribute",
BindingFlags.Instance | BindingFlags.NonPublic);
var parameterInfo = methodInfo.GetParameters().Where(p => p.Name == "param").Single();
var provider = CreateProvider();
var context = new ModelBindingContext();
// Note it does an intersection for included -- only properties that
// pass both predicates will be bound.
var expected = new[] { "IsAdmin" };
// Act
var metadata = provider.GetMetadataForParameter(
parameterInfo,
attributes: null);
// Assert
var predicate = metadata.PropertyBindingPredicateProvider.PropertyFilter;
Assert.NotNull(predicate);
var matched = new HashSet<string>();
foreach (var property in metadata.Properties)
{
if (predicate(context, property.PropertyName))
{
matched.Add(property.PropertyName);
}
}
Assert.Equal<string>(expected, matched);
}
[Fact]
public void ModelMetadataProvider_ReadsModelNameProperty_ForParameters()
{
// Arrange
var type = GetType();
var methodInfo = type.GetMethod(
"ActionWithBindAttribute",
BindingFlags.Instance | BindingFlags.NonPublic);
var parameterInfo = methodInfo.GetParameters().Where(p => p.Name == "param").Single();
var provider = CreateProvider();
// Act
var metadata = provider.GetMetadataForParameter(
parameterInfo,
attributes: null);
// Assert
Assert.Equal("ParameterPrefix", metadata.BinderModelName);
}
[Fact]
public void ModelMetadataProvider_ReadsModelNameProperty_ForTypes()
{
// Arrange
var type = typeof(User);
var provider = CreateProvider();
// Act
var metadata = provider.GetMetadataForType(type);
// Assert
Assert.Equal("TypePrefix", metadata.BinderModelName);
}
[Fact]
public void ModelMetadataProvider_ReadsScaffoldColumnAttribute_ForShowForDisplay()
{
// Arrange
var type = typeof(ScaffoldColumnModel);
var provider = CreateProvider();
// Act & Assert
Assert.True(provider.GetMetadataForProperty(type, "NoAttribute").ShowForDisplay);
Assert.True(provider.GetMetadataForProperty(type, "ScaffoldColumnTrue").ShowForDisplay);
Assert.False(provider.GetMetadataForProperty(type, "ScaffoldColumnFalse").ShowForDisplay);
}
[Fact]
public void ModelMetadataProvider_ReadsScaffoldColumnAttribute_ForShowForEdit()
{
// Arrange
var type = typeof(ScaffoldColumnModel);
var provider = CreateProvider();
// Act & Assert
Assert.True(provider.GetMetadataForProperty(type, "NoAttribute").ShowForEdit);
Assert.True(provider.GetMetadataForProperty(type, "ScaffoldColumnTrue").ShowForEdit);
Assert.False(provider.GetMetadataForProperty(type, "ScaffoldColumnFalse").ShowForEdit);
}
[Fact]
public void HiddenInputWorksOnProperty_ForHideSurroundingHtml()
{
// Arrange
var provider = CreateProvider();
var metadata = provider.GetMetadataForType(modelType: typeof(ClassWithHiddenProperties));
var property = metadata.Properties["DirectlyHidden"];
// Act
var result = property.HideSurroundingHtml;
// Assert
Assert.True(result);
}
[Fact]
public void HiddenInputWorksOnPropertyType_ForHideSurroundingHtml()
{
// Arrange
var provider = CreateProvider();
var metadata = provider.GetMetadataForType(typeof(ClassWithHiddenProperties));
var property = metadata.Properties["OfHiddenType"];
// Act
var result = property.HideSurroundingHtml;
// Assert
Assert.True(result);
}
[Fact]
public void HiddenInputWorksOnProperty_ForTemplateHint()
{
// Arrange
var provider = CreateProvider();
var metadata = provider.GetMetadataForType(typeof(ClassWithHiddenProperties));
var property = metadata.Properties["DirectlyHidden"];
// Act
var result = property.TemplateHint;
// Assert
Assert.Equal("HiddenInput", result);
}
[Fact]
public void HiddenInputWorksOnPropertyType_ForTemplateHint()
{
// Arrange
var provider = CreateProvider();
var metadata = provider.GetMetadataForType(typeof(ClassWithHiddenProperties));
var property = metadata.Properties["OfHiddenType"];
// Act
var result = property.TemplateHint;
// Assert
Assert.Equal("HiddenInput", result);
}
[Fact]
public void GetMetadataForProperty_WithNoBinderModelName_GetsItFromType()
{
// Arrange
var provider = CreateProvider();
// Act
var propertyMetadata = provider.GetMetadataForProperty(typeof(Person), nameof(Person.Parent));
// Assert
Assert.Equal("PersonType", propertyMetadata.BinderModelName);
}
[Fact]
public void GetMetadataForProperty_WithBinderMetadataOnPropertyAndType_GetsMetadataFromProperty()
{
// Arrange
var provider = CreateProvider();
// Act
var propertyMetadata = provider.GetMetadataForProperty(typeof(Person), nameof(Person.GrandParent));
// Assert
Assert.Equal("GrandParentProperty", propertyMetadata.BinderModelName);
}
[Fact]
public void GetMetadataForParameter_WithNoBinderMetadata_GetsItFromType()
{
// Arrange
var provider = CreateProvider();
var methodInfo = typeof(Person).GetMethod("Update");
var parameterInfo = methodInfo.GetParameters().Where(p => p.Name == "person").Single();
// Act
var parameterMetadata = provider.GetMetadataForParameter(
parameterInfo,
attributes: null);
// Assert
Assert.Equal("PersonType", parameterMetadata.BinderModelName);
}
[Fact]
public void GetMetadataForParameter_WithBinderDataOnParameterAndType_GetsMetadataFromParameter()
{
// Arrange
var provider = CreateProvider();
var methodInfo = typeof(Person).GetMethod("Save");
var parameterInfo = methodInfo.GetParameters().Where(p => p.Name == "person").Single();
// Act
var parameterMetadata = provider.GetMetadataForParameter(
parameterInfo,
attributes: null);
// Assert
Assert.Equal("PersonParameter", parameterMetadata.BinderModelName);
}
[Fact]
public void GetMetadataForParameter_BinderModelNameOverride()
{
// Arrange
var provider = CreateProvider();
var methodInfo = typeof(Person).GetMethod("Save");
var parameterInfo = methodInfo.GetParameters().Where(p => p.Name == "person").Single();
// Act
var parameterMetadata = provider.GetMetadataForParameter(
parameterInfo,
attributes: new object[] { new ModelBinderAttribute() { Name = "Override" } });
// Assert
Assert.Equal("Override", parameterMetadata.BinderModelName);
}
public static TheoryData<object, Func<ModelMetadata, string>> ExpectedAttributeDataStrings
{
get
{
return new TheoryData<object, Func<ModelMetadata, string>>
{
{
new DataTypeAttribute("value"), metadata => metadata.DataTypeName
},
{
new DataTypeWithCustomDisplayFormat(), metadata => metadata.DisplayFormatString
},
{
new DataTypeWithCustomEditFormat(), metadata => metadata.EditFormatString
},
{
new DisplayAttribute { Description = "value" }, metadata => metadata.Description
},
{
new DisplayAttribute { Name = "value" }, metadata => metadata.DisplayName
},
{
new DisplayFormatAttribute { DataFormatString = "value" },
metadata => metadata.DisplayFormatString
},
{
// DisplayFormatString does not ignore [DisplayFormat] if ApplyFormatInEditMode==true.
new DisplayFormatAttribute { ApplyFormatInEditMode = true, DataFormatString = "value" },
metadata => metadata.DisplayFormatString
},
{
new DisplayFormatAttribute { ApplyFormatInEditMode = true, DataFormatString = "value" },
metadata => metadata.EditFormatString
},
{
new DisplayFormatAttribute { NullDisplayText = "value" }, metadata => metadata.NullDisplayText
},
{
new TestModelNameProvider { Name = "value" }, metadata => metadata.BinderModelName
},
{
new UIHintAttribute("value"), metadata => metadata.TemplateHint
},
};
}
}
[Theory]
[MemberData(nameof(ExpectedAttributeDataStrings))]
public void AttributesOverrideMetadataStrings(object attribute, Func<ModelMetadata, string> accessor)
{
// Arrange
var attributes = new[] { attribute };
var provider = CreateProvider(attributes);
var metadata = provider.GetMetadataForType(typeof(string));
// Act
var result = accessor(metadata);
// Assert
Assert.Equal("value", result);
}
[Fact]
public void AttributesOverrideMetadataStrings_SimpleDisplayProperty()
{
// Arrange
var attributes = new[] { new DisplayColumnAttribute("Property") };
var provider = CreateProvider(attributes);
var metadata = provider.GetMetadataForType(typeof(string));
// Act
var result = metadata.SimpleDisplayProperty;
// Assert
Assert.Equal("Property", result);
}
public static TheoryData<Attribute, Func<ModelMetadata, bool>, bool> ExpectedAttributeDataBooleans
{
get
{
return new TheoryData<Attribute, Func<ModelMetadata, bool>, bool>
{
{
// Edit formats from [DataType] subclass affect HasNonDefaultEditFormat.
new DataTypeWithCustomEditFormat(),
metadata => metadata.HasNonDefaultEditFormat,
true
},
{
// Edit formats from [DataType] do not affect HasNonDefaultEditFormat.
new DataTypeAttribute(DataType.Date),
metadata => metadata.HasNonDefaultEditFormat,
false
},
{
new DisplayFormatAttribute { ConvertEmptyStringToNull = false },
metadata => metadata.ConvertEmptyStringToNull,
false
},
{
new DisplayFormatAttribute { ConvertEmptyStringToNull = true },
metadata => metadata.ConvertEmptyStringToNull,
true
},
{
// Changes only to DisplayFormatString do not affect HasNonDefaultEditFormat.
new DisplayFormatAttribute { DataFormatString = "value" },
metadata => metadata.HasNonDefaultEditFormat,
false
},
{
new DisplayFormatAttribute { ApplyFormatInEditMode = true, DataFormatString = "value" },
metadata => metadata.HasNonDefaultEditFormat,
true
},
{
new DisplayFormatAttribute { HtmlEncode = false },
metadata => metadata.HtmlEncode,
false
},
{
new DisplayFormatAttribute { HtmlEncode = true },
metadata => metadata.HtmlEncode,
true
},
{
new EditableAttribute(allowEdit: false),
metadata => metadata.IsReadOnly,
true
},
{
new EditableAttribute(allowEdit: true),
metadata => metadata.IsReadOnly,
false
},
{
new HiddenInputAttribute { DisplayValue = false },
metadata => metadata.HideSurroundingHtml,
true
},
{
new HiddenInputAttribute { DisplayValue = true },
metadata => metadata.HideSurroundingHtml,
false
},
{
new HiddenInputAttribute(),
metadata => string.Equals("HiddenInput", metadata.TemplateHint, StringComparison.Ordinal),
true
},
{
new RequiredAttribute(),
metadata => metadata.IsRequired,
true
},
};
}
}
[Theory]
[MemberData(nameof(ExpectedAttributeDataBooleans))]
public void AttributesOverrideMetadataBooleans(
Attribute attribute,
Func<ModelMetadata, bool> accessor,
bool expectedResult)
{
// Arrange
var attributes = new[] { attribute };
var provider = CreateProvider(attributes);
var metadata = provider.GetMetadataForType(typeof(string));
// Act
var result = accessor(metadata);
// Assert
Assert.Equal(expectedResult, result);
}
public static TheoryData<DisplayAttribute, int> DisplayAttribute_OverridesOrderData
{
get
{
return new TheoryData<DisplayAttribute, int>
{
{
new DisplayAttribute(), ModelMetadata.DefaultOrder
},
{
new DisplayAttribute { Order = int.MinValue }, int.MinValue
},
{
new DisplayAttribute { Order = -100 }, -100
},
{
new DisplayAttribute { Order = -1 }, -1
},
{
new DisplayAttribute { Order = 0 }, 0
},
{
new DisplayAttribute { Order = 1 }, 1
},
{
new DisplayAttribute { Order = 200 }, 200
},
{
new DisplayAttribute { Order = int.MaxValue }, int.MaxValue
},
};
}
}
[Theory]
[MemberData(nameof(DisplayAttribute_OverridesOrderData))]
public void DisplayAttribute_OverridesOrder(DisplayAttribute attribute, int expectedOrder)
{
// Arrange
var attributes = new[] { attribute };
var provider = CreateProvider(attributes);
var metadata = provider.GetMetadataForType(typeof(string));
// Act
var result = metadata.Order;
// Assert
Assert.Equal(expectedOrder, result);
}
[Fact]
public void DisplayAttribute_Description()
{
// Arrange
var display = new DisplayAttribute() { Description = "description" };
var provider = CreateProvider(new[] { display });
var metadata = provider.GetMetadataForType(typeof(string));
// Act
var result = metadata.Description;
// Assert
Assert.Equal("description", result);
}
[Fact]
public void DataTypeName_Null_IfHtmlEncodeTrue()
{
// Arrange
var displayFormat = new DisplayFormatAttribute { HtmlEncode = true, };
var provider = CreateProvider(new[] { displayFormat });
var metadata = provider.GetMetadataForType(typeof(string));
// Act
var result = metadata.DataTypeName;
// Assert
Assert.Null(result);
}
[Fact]
public void DataTypeName_Html_IfHtmlEncodeFalse()
{
// Arrange
var expected = "Html";
var displayFormat = new DisplayFormatAttribute { HtmlEncode = false, };
var provider = CreateProvider(new[] { displayFormat });
var metadata = provider.GetMetadataForType(typeof(string));
// Act
var result = metadata.DataTypeName;
// Assert
Assert.Equal(expected, result);
}
[Fact]
public void DataTypeName_AttributesHaveExpectedPrecedence()
{
// Arrange
var expected = "MultilineText";
var dataType = new DataTypeAttribute(DataType.MultilineText);
var displayFormat = new DisplayFormatAttribute { HtmlEncode = false, };
var provider = CreateProvider(new object[] { dataType, displayFormat });
var metadata = provider.GetMetadataForType(typeof(string));
// Act
var result = metadata.DataTypeName;
// Assert
Assert.Equal(expected, result);
}
[Fact]
public void DisplayFormatString_AttributesHaveExpectedPrecedence()
{
// Arrange
var expected = "custom format";
var dataType = new DataTypeAttribute(DataType.Currency);
var displayFormat = new DisplayFormatAttribute { DataFormatString = expected, };
var provider = CreateProvider(new object[] { displayFormat, dataType, });
var metadata = provider.GetMetadataForType(typeof(string));
// Act
var result = metadata.DisplayFormatString;
// Assert
Assert.Equal(expected, result);
}
[Fact]
public void EditFormatString_AttributesHaveExpectedPrecedence()
{
// Arrange
var expected = "custom format";
var dataType = new DataTypeAttribute(DataType.Currency);
var displayFormat = new DisplayFormatAttribute
{
ApplyFormatInEditMode = true,
DataFormatString = expected,
};
var provider = CreateProvider(new object[] { displayFormat, dataType, });
var metadata = provider.GetMetadataForType(typeof(string));
// Act
var result = metadata.EditFormatString;
// Assert
Assert.Equal(expected, result);
}
[Fact]
public void TemplateHint_AttributesHaveExpectedPrecedence()
{
// Arrange
var expected = "this is a hint";
var hidden = new HiddenInputAttribute();
var uiHint = new UIHintAttribute(expected);
var provider = CreateProvider(new object[] { hidden, uiHint, });
var metadata = provider.GetMetadataForType(typeof(string));
// Act
var result = metadata.TemplateHint;
// Assert
Assert.Equal(expected, result);
}
[Fact]
public void BinderTypeProviders_Null()
{
// Arrange
var binderProviders = new[]
{
new TestBinderTypeProvider(),
new TestBinderTypeProvider(),
};
var provider = CreateProvider(binderProviders);
// Act
var metadata = provider.GetMetadataForType(typeof(string));
// Assert
Assert.Null(metadata.BinderType);
}
[Fact]
public void BinderTypeProviders_Fallback()
{
// Arrange
var attributes = new[]
{
new TestBinderTypeProvider(),
new TestBinderTypeProvider() { BinderType = typeof(string) }
};
var provider = CreateProvider(attributes);
// Act
var metadata = provider.GetMetadataForType(typeof(string));
// Assert
Assert.Same(typeof(string), metadata.BinderType);
}
[Fact]
public void BinderTypeProviders_FirstAttributeHasPrecedence()
{
// Arrange
var attributes = new[]
{
new TestBinderTypeProvider() { BinderType = typeof(int) },
new TestBinderTypeProvider() { BinderType = typeof(string) }
};
var provider = CreateProvider(attributes);
// Act
var metadata = provider.GetMetadataForType(typeof(string));
// Assert
Assert.Same(typeof(int), metadata.BinderType);
}
private IModelMetadataProvider CreateProvider(params object[] attributes)
{
return new AttributeInjectModelMetadataProvider(attributes);
}
private class TestBinderTypeProvider : IBinderTypeProviderMetadata
{
public Type BinderType { get; set; }
public BindingSource BindingSource { get; set; }
}
private class DataTypeWithCustomDisplayFormat : DataTypeAttribute
{
public DataTypeWithCustomDisplayFormat() : base("Custom datatype")
{
DisplayFormat = new DisplayFormatAttribute
{
DataFormatString = "value",
};
}
}
private class DataTypeWithCustomEditFormat : DataTypeAttribute
{
public DataTypeWithCustomEditFormat() : base("Custom datatype")
{
DisplayFormat = new DisplayFormatAttribute
{
ApplyFormatInEditMode = true,
DataFormatString = "value",
};
}
}
private void ActionWithoutBindAttribute(User param)
{
}
private void ActionWithBindAttribute([Bind(new string[] { "IsAdmin" }, Prefix = "ParameterPrefix")] User param)
{
}
public class TypeBasedBinderAttribute : Attribute, IBinderMetadata, IModelNameProvider
{
public string Name { get; set; }
}
public class NonTypeBasedBinderAttribute : Attribute, IBinderMetadata, IModelNameProvider
{
public string Name { get; set; }
}
[TypeBasedBinder(Name = "PersonType")]
public class Person
{
public Person Parent { get; set; }
[NonTypeBasedBinder(Name = "GrandParentProperty")]
public Person GrandParent { get; set; }
public void Update(Person person)
{
}
public void Save([NonTypeBasedBinder(Name = "PersonParameter")] Person person)
{
}
}
private class ScaffoldColumnModel
{
public int NoAttribute { get; set; }
[ScaffoldColumn(scaffold: true)]
public int ScaffoldColumnTrue { get; set; }
[ScaffoldColumn(scaffold: false)]
public int ScaffoldColumnFalse { get; set; }
}
[HiddenInput(DisplayValue = false)]
private class HiddenClass
{
public string Property { get; set; }
}
private class ClassWithHiddenProperties
{
[HiddenInput(DisplayValue = false)]
public string DirectlyHidden { get; set; }
public HiddenClass OfHiddenType { get; set; }
}
[Bind(new[] { nameof(IsAdmin), nameof(UserName) }, Prefix = "TypePrefix")]
private class User
{
public int Id { get; set; }
public bool IsAdmin { get; set; }
public int UserName { get; set; }
public int NotIncludedOrExcluded { get; set; }
}
private class AttributeInjectModelMetadataProvider : DefaultModelMetadataProvider
{
private readonly object[] _attributes;
public AttributeInjectModelMetadataProvider(object[] attributes)
: base(new DefaultCompositeMetadataDetailsProvider(new IMetadataDetailsProvider[]
{
new DefaultBindingMetadataProvider(),
new DataAnnotationsMetadataDetailsProvider(),
}))
{
_attributes = attributes;
}
protected override DefaultMetadataDetailsCache CreateTypeCacheEntry(ModelMetadataIdentity key)
{
var entry = base.CreateTypeCacheEntry(key);
return new DefaultMetadataDetailsCache(key, _attributes.Concat(entry.Attributes).ToArray());
}
}
}
}

View File

@ -5,162 +5,14 @@ using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reflection;
using Microsoft.Framework.Internal;
using Microsoft.AspNet.Mvc.ModelBinding.Metadata;
using Xunit;
namespace Microsoft.AspNet.Mvc.ModelBinding
{
public class ModelMetadataTest
{
public static TheoryData<Action<ModelMetadata>, Func<ModelMetadata, object>, object> MetadataModifierData
{
get
{
var emptycontainerModel = new DummyModelContainer();
var contactModel = new DummyContactModel { FirstName = "test" };
var nonEmptycontainerModel = new DummyModelContainer { Model = contactModel };
var binderMetadata = new TestBinderMetadata();
var predicateProvider = new DummyPropertyBindingPredicateProvider();
return new TheoryData<Action<ModelMetadata>, Func<ModelMetadata, object>, object>
{
{ m => m.ConvertEmptyStringToNull = false, m => m.ConvertEmptyStringToNull, false },
{ m => m.HasNonDefaultEditFormat = true, m => m.HasNonDefaultEditFormat, true },
{ m => m.HideSurroundingHtml = true, m => m.HideSurroundingHtml, true },
{ m => m.HtmlEncode = false, m => m.HtmlEncode, false },
{ m => m.IsReadOnly = true, m => m.IsReadOnly, true },
{ m => m.IsRequired = true, m => m.IsRequired, true },
{ m => m.ShowForDisplay = false, m => m.ShowForDisplay, false },
{ m => m.ShowForEdit = false, m => m.ShowForEdit, false },
{ m => m.DataTypeName = "New data type name", m => m.DataTypeName, "New data type name" },
{ m => m.Description = "New description", m => m.Description, "New description" },
{ m => m.DisplayFormatString = "New display format", m => m.DisplayFormatString, "New display format" },
{ m => m.DisplayName = "New display name", m => m.DisplayName, "New display name" },
{ m => m.EditFormatString = "New edit format", m => m.EditFormatString, "New edit format" },
{ m => m.NullDisplayText = "New null display", m => m.NullDisplayText, "New null display" },
{ m => m.SimpleDisplayProperty = "NewSimpleDisplay", m => m.SimpleDisplayProperty, "NewSimpleDisplay" },
{ m => m.TemplateHint = "New template hint", m => m.TemplateHint, "New template hint" },
{ m => m.Order = 23, m => m.Order, 23 },
{ m => m.BinderMetadata = null, m => m.BinderMetadata, null },
{ m => m.BinderMetadata = binderMetadata, m => m.BinderMetadata, binderMetadata },
{ m => m.BinderModelName = null, m => m.BinderModelName, null },
{ m => m.BinderModelName = "newModelName", m => m.BinderModelName, "newModelName" },
{ m => m.BinderModelName = string.Empty, m => m.BinderModelName, string.Empty },
{ m => m.BinderType = null, m => m.BinderType, null },
{ m => m.BinderType = typeof(string), m => m.BinderType, typeof(string) },
{ m => m.PropertyBindingPredicateProvider = null, m => m.PropertyBindingPredicateProvider, null },
{ m => m.PropertyBindingPredicateProvider = predicateProvider, m => m.PropertyBindingPredicateProvider, predicateProvider },
};
}
}
// Constructor
[Fact]
public void DefaultValues()
{
// Arrange
var provider = new EmptyModelMetadataProvider();
// Act
var metadata = new ModelMetadata(provider, typeof(Exception), typeof(string), "propertyName");
// Assert
Assert.NotNull(metadata.AdditionalValues);
Assert.Empty(metadata.AdditionalValues);
Assert.Equal(typeof(Exception), metadata.ContainerType);
Assert.True(metadata.ConvertEmptyStringToNull);
Assert.False(metadata.HasNonDefaultEditFormat);
Assert.False(metadata.HideSurroundingHtml);
Assert.True(metadata.HtmlEncode);
Assert.False(metadata.IsComplexType);
Assert.False(metadata.IsCollectionType);
Assert.False(metadata.IsNullableValueType);
Assert.False(metadata.IsReadOnly);
Assert.False(metadata.IsRequired);
Assert.True(metadata.ShowForDisplay);
Assert.True(metadata.ShowForEdit);
Assert.Null(metadata.DataTypeName);
Assert.Null(metadata.Description);
Assert.Null(metadata.DisplayFormatString);
Assert.Null(metadata.DisplayName);
Assert.Null(metadata.EditFormatString);
Assert.Null(metadata.NullDisplayText);
Assert.Null(metadata.TemplateHint);
Assert.Null(metadata.SimpleDisplayProperty);
Assert.Equal(typeof(string), metadata.ModelType);
Assert.Equal("propertyName", metadata.PropertyName);
Assert.Equal(10000, ModelMetadata.DefaultOrder);
Assert.Equal(ModelMetadata.DefaultOrder, metadata.Order);
Assert.Null(metadata.BinderModelName);
Assert.Null(metadata.BinderType);
Assert.Null(metadata.BinderMetadata);
Assert.Null(metadata.PropertyBindingPredicateProvider);
}
// AdditionalValues
[Fact]
public void AdditionalValues_CreatedOnce()
{
// Arrange
var provider = new EmptyModelMetadataProvider();
var metadata = new ModelMetadata(
provider,
containerType: null,
modelType: typeof(object),
propertyName: null);
// Act
var result1 = metadata.AdditionalValues;
var result2 = metadata.AdditionalValues;
// Assert
Assert.Same(result1, result2);
}
[Fact]
public void AdditionalValues_ChangesPersist()
{
// Arrange
var provider = new EmptyModelMetadataProvider();
var metadata = new ModelMetadata(
provider,
containerType: null,
modelType: typeof(object),
propertyName: null);
var valuesDictionary = new Dictionary<object, object>
{
{ "key1", new object() },
{ "key2", "value2" },
{ "key3", new object() },
};
// Act
foreach (var keyValuePair in valuesDictionary)
{
metadata.AdditionalValues.Add(keyValuePair);
}
// Assert
Assert.Equal(valuesDictionary, metadata.AdditionalValues);
}
// IsComplexType
private struct IsComplexTypeModel
{
}
@ -175,11 +27,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var provider = new EmptyModelMetadataProvider();
// Act
var modelMetadata = new ModelMetadata(
provider,
containerType: null,
modelType: type,
propertyName: null);
var modelMetadata = new TestModelMetadata(type);
// Assert
Assert.False(modelMetadata.IsComplexType);
@ -196,11 +44,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var provider = new EmptyModelMetadataProvider();
// Act
var modelMetadata = new ModelMetadata(
provider,
containerType: null,
modelType: type,
propertyName: null);
var modelMetadata = new TestModelMetadata(type);
// Assert
Assert.True(modelMetadata.IsComplexType);
@ -217,11 +61,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var provider = new EmptyModelMetadataProvider();
// Act
var modelMetadata = new ModelMetadata(
provider,
containerType: null,
modelType: type,
propertyName: null);
var modelMetadata = new TestModelMetadata(type);
// Assert
Assert.False(modelMetadata.IsCollectionType);
@ -241,11 +81,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var provider = new EmptyModelMetadataProvider();
// Act
var modelMetadata = new ModelMetadata(
provider,
containerType: null,
modelType: type,
propertyName: null);
var modelMetadata = new TestModelMetadata(type);
// Assert
Assert.True(modelMetadata.IsCollectionType);
@ -261,312 +97,18 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// IsNullableValueType
[Fact]
public void IsNullableValueTypeTests()
[Theory]
[InlineData(typeof(string), false)]
[InlineData(typeof(IDisposable), false)]
[InlineData(typeof(Nullable<int>), true)]
[InlineData(typeof(int), false)]
public void IsNullableValueTypeTests(Type modelType, bool expected)
{
// Arrange
var provider = new EmptyModelMetadataProvider();
var modelMetadata = new TestModelMetadata(modelType);
// Act & Assert
Assert.False(new ModelMetadata(provider, null, typeof(string), null).IsNullableValueType);
Assert.False(new ModelMetadata(provider, null, typeof(IDisposable), null).IsNullableValueType);
Assert.True(new ModelMetadata(provider, null, typeof(Nullable<int>), null).IsNullableValueType);
Assert.False(new ModelMetadata(provider, null, typeof(int), null).IsNullableValueType);
}
// IsRequired
[Theory]
[InlineData(typeof(string))]
[InlineData(typeof(IDisposable))]
[InlineData(typeof(Nullable<int>))]
public void IsRequired_ReturnsFalse_ForNullableTypes(Type modelType)
{
// Arrange
var provider = new EmptyModelMetadataProvider();
var metadata = new ModelMetadata(provider,
containerType: null,
modelType: modelType,
propertyName: null);
// Act
var isRequired = metadata.IsRequired;
// Assert
Assert.False(isRequired);
}
[Theory]
[InlineData(typeof(int))]
[InlineData(typeof(DayOfWeek))]
public void IsRequired_ReturnsTrue_ForNonNullableTypes(Type modelType)
{
// Arrange
var provider = new EmptyModelMetadataProvider();
var metadata = new ModelMetadata(provider,
containerType: null,
modelType: modelType,
propertyName: null);
// Act
var isRequired = metadata.IsRequired;
// Assert
Assert.True(isRequired);
}
// Properties
[Fact]
public void PropertiesProperty_CallsProvider()
{
// Arrange
var modelType = typeof(object);
var provider = new PropertiesModelMetadataProvider(new List<string>());
var metadata = new ModelMetadata(
provider,
containerType: null,
modelType: modelType,
propertyName: null);
// Act
var result = metadata.Properties;
// Assert
Assert.Empty(result);
Assert.Equal(1, provider.GetMetadataForPropertiesCalls);
}
// Input (original) property names and expected (ordered) property names.
public static TheoryData<IEnumerable<string>, IEnumerable<string>> PropertyNamesTheoryData
{
get
{
// ModelMetadata does not reorder properties the provider returns without an Order override.
return new TheoryData<IEnumerable<string>, IEnumerable<string>>
{
{
new List<string> { "Property1", "Property2", "Property3", "Property4", },
new List<string> { "Property1", "Property2", "Property3", "Property4", }
},
{
new List<string> { "Property4", "Property3", "Property2", "Property1", },
new List<string> { "Property4", "Property3", "Property2", "Property1", }
},
{
new List<string> { "Delta", "Bravo", "Charlie", "Alpha", },
new List<string> { "Delta", "Bravo", "Charlie", "Alpha", }
},
{
new List<string> { "John", "Jonathan", "Jon", "Joan", },
new List<string> { "John", "Jonathan", "Jon", "Joan", }
},
};
}
}
[Theory]
[MemberData(nameof(PropertyNamesTheoryData))]
public void PropertiesProperty_WithDefaultOrder_OrdersPropertyNamesAsProvided(
IEnumerable<string> originalNames,
IEnumerable<string> expectedNames)
{
// Arrange
var modelType = typeof(object);
var provider = new PropertiesModelMetadataProvider(originalNames);
var metadata = new ModelMetadata(
provider,
containerType: null,
modelType: modelType,
propertyName: null);
// Act
var result = metadata.Properties;
// Assert
var propertyNames = result.Select(property => property.PropertyName);
Assert.Equal(expectedNames, propertyNames);
}
// Input (original) property names, Order values, and expected (ordered) property names.
public static TheoryData<IEnumerable<KeyValuePair<string, int>>, IEnumerable<string>>
PropertyNamesAndOrdersTheoryData
{
get
{
return new TheoryData<IEnumerable<KeyValuePair<string, int>>, IEnumerable<string>>
{
{
new List<KeyValuePair<string, int>>
{
new KeyValuePair<string, int>("Property1", 23),
new KeyValuePair<string, int>("Property2", 23),
new KeyValuePair<string, int>("Property3", 23),
new KeyValuePair<string, int>("Property4", 23),
},
new List<string> { "Property1", "Property2", "Property3", "Property4", }
},
// Same order if already ordered using Order.
{
new List<KeyValuePair<string, int>>
{
new KeyValuePair<string, int>("Property4", 23),
new KeyValuePair<string, int>("Property3", 24),
new KeyValuePair<string, int>("Property2", 25),
new KeyValuePair<string, int>("Property1", 26),
},
new List<string> { "Property4", "Property3", "Property2", "Property1", }
},
// Rest of the orderings get updated within ModelMetadata.
{
new List<KeyValuePair<string, int>>
{
new KeyValuePair<string, int>("Property1", 26),
new KeyValuePair<string, int>("Property2", 25),
new KeyValuePair<string, int>("Property3", 24),
new KeyValuePair<string, int>("Property4", 23),
},
new List<string> { "Property4", "Property3", "Property2", "Property1", }
},
{
new List<KeyValuePair<string, int>>
{
new KeyValuePair<string, int>("Alpha", 26),
new KeyValuePair<string, int>("Bravo", 24),
new KeyValuePair<string, int>("Charlie", 23),
new KeyValuePair<string, int>("Delta", 25),
},
new List<string> { "Charlie", "Bravo", "Delta", "Alpha", }
},
// Jonathan and Jon will not be reordered.
{
new List<KeyValuePair<string, int>>
{
new KeyValuePair<string, int>("Joan", 1),
new KeyValuePair<string, int>("Jonathan", 0),
new KeyValuePair<string, int>("Jon", 0),
new KeyValuePair<string, int>("John", -1),
},
new List<string> { "John", "Jonathan", "Jon", "Joan", }
},
};
}
}
[Theory]
[MemberData(nameof(PropertyNamesAndOrdersTheoryData))]
public void PropertiesProperty_OrdersPropertyNamesUsingOrder_ThenAsProvided(
IEnumerable<KeyValuePair<string, int>> originalNamesAndOrders,
IEnumerable<string> expectedNames)
{
// Arrange
var modelType = typeof(object);
var provider = new PropertiesModelMetadataProvider(originalNamesAndOrders);
var metadata = new ModelMetadata(
provider,
containerType: null,
modelType: modelType,
propertyName: null);
// Act
var result = metadata.Properties;
// Assert
var propertyNames = result.Select(property => property.PropertyName);
Assert.Equal(expectedNames, propertyNames);
}
[Theory]
[MemberData(nameof(MetadataModifierData))]
public void PropertiesPropertyChangesPersist(
Action<ModelMetadata> setter,
Func<ModelMetadata, object> getter,
object expected)
{
// Arrange
var provider = new EmptyModelMetadataProvider();
var metadata = new ModelMetadata(
provider,
containerType: null,
modelType: typeof(Class1),
propertyName: null);
// Act
foreach (var property in metadata.Properties)
{
setter(property);
}
// Assert
foreach (var property in metadata.Properties)
{
// Due to boxing of structs, can't Assert.Same().
Assert.Equal(expected, getter(property));
}
}
[Theory]
[MemberData(nameof(MetadataModifierData))]
public void PropertyChangesPersist(
Action<ModelMetadata> setter,
Func<ModelMetadata, object> getter,
object expected)
{
// Arrange
var provider = new EmptyModelMetadataProvider();
var metadata = new ModelMetadata(
provider,
containerType: null,
modelType: typeof(Class1),
propertyName: null);
// Act
setter(metadata);
var result = getter(metadata);
// Assert
// Due to boxing of structs, can't Assert.Same().
Assert.Equal(expected, result);
}
[Fact]
public void PropertiesSetOnce()
{
// Arrange
var provider = new EmptyModelMetadataProvider();
var metadata = new ModelMetadata(
provider,
containerType: null,
modelType: typeof(Class1),
propertyName: null);
// Act
var firstPropertiesEvaluation = metadata.Properties;
var secondPropertiesEvaluation = metadata.Properties;
// Assert
// Same IEnumerable<ModelMetadata> object.
Assert.Same(firstPropertiesEvaluation, secondPropertiesEvaluation);
}
[Fact]
public void PropertiesEnumerationEvaluatedOnce()
{
// Arrange
var provider = new EmptyModelMetadataProvider();
var metadata = new ModelMetadata(
provider,
containerType: null,
modelType: typeof(Class1),
propertyName: null);
// Act
var firstPropertiesEvaluation = metadata.Properties.ToList();
var secondPropertiesEvaluation = metadata.Properties.ToList();
// Assert
// Identical ModelMetadata objects every time we run through the Properties collection.
Assert.Equal(firstPropertiesEvaluation, secondPropertiesEvaluation);
Assert.Equal(expected, modelMetadata.IsNullableValueType);
}
private class Class1
@ -590,10 +132,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
// Arrange
var provider = new EmptyModelMetadataProvider();
var metadata = new ModelMetadata(provider, null, typeof(object), "unusedName")
{
DisplayName = "displayName",
};
var metadata = new TestModelMetadata(typeof(int), "Length", typeof(string));
metadata.SetDisplayName("displayName");
// Act
var result = metadata.GetDisplayName();
@ -607,13 +147,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
// Arrange
var provider = new EmptyModelMetadataProvider();
var metadata = new ModelMetadata(provider, null, typeof(object), "PropertyName");
var metadata = new TestModelMetadata(typeof(int), "Length", typeof(string));
// Act
var result = metadata.GetDisplayName();
// Assert
Assert.Equal("PropertyName", result);
Assert.Equal("Length", result);
}
[Fact]
@ -621,109 +161,216 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
// Arrange
var provider = new EmptyModelMetadataProvider();
var metadata = new ModelMetadata(provider, null, typeof(object), null);
var metadata = new TestModelMetadata(typeof(string));
// Act
var result = metadata.GetDisplayName();
// Assert
Assert.Equal("Object", result);
Assert.Equal("String", result);
}
private class ClassWithNoProperties
private class TestModelMetadata : ModelMetadata
{
public override string ToString()
private string _displayName;
public TestModelMetadata(Type modelType)
: base(ModelMetadataIdentity.ForType(modelType))
{
return null;
}
}
private class ComplexClass
{
public Class1 Prop1 { get; set; }
}
// Helpers
private class DummyContactModel
{
public int IntField = 0;
public string FirstName { get; set; }
public string LastName { get; set; }
public int? NullableIntValue { get; set; }
public int[] Array { get; set; }
public string this[int index]
public TestModelMetadata(Type modelType, string propertyName, Type containerType)
: base(ModelMetadataIdentity.ForProperty(modelType, propertyName, containerType))
{
get { return "Indexed into " + index; }
}
}
private class DummyModelContainer
{
public DummyContactModel Model { get; set; }
}
private class DummyPropertyBindingPredicateProvider : IPropertyBindingPredicateProvider
{
public Func<ModelBindingContext, string, bool> PropertyFilter { get; set; }
}
// Gives object type properties with provided names or names and Order values.
private class PropertiesModelMetadataProvider : IModelMetadataProvider
{
private List<ModelMetadata> _properties = new List<ModelMetadata>();
public PropertiesModelMetadataProvider(IEnumerable<string> propertyNames)
public override IReadOnlyDictionary<object, object> AdditionalValues
{
foreach (var propertyName in propertyNames)
get
{
var metadata = new ModelMetadata(
this,
containerType: typeof(DummyContactModel),
modelType: typeof(string),
propertyName: propertyName);
_properties.Add(metadata);
throw new NotImplementedException();
}
}
public PropertiesModelMetadataProvider(IEnumerable<KeyValuePair<string, int>> propertyNamesAndOrders)
public override string BinderModelName
{
foreach (var keyValuePair in propertyNamesAndOrders)
get
{
var metadata = new ModelMetadata(
this,
containerType: typeof(DummyContactModel),
modelType: typeof(string),
propertyName: keyValuePair.Key)
{
Order = keyValuePair.Value,
};
_properties.Add(metadata);
throw new NotImplementedException();
}
}
public int GetMetadataForPropertiesCalls { get; private set; }
public ModelMetadata GetMetadataForParameter(
[NotNull] MethodInfo methodInfo,
[NotNull] string parameterName)
public override Type BinderType
{
throw new NotImplementedException();
get
{
throw new NotImplementedException();
}
}
public IEnumerable<ModelMetadata> GetMetadataForProperties([NotNull] Type containerType)
public override BindingSource BindingSource
{
Assert.Equal(typeof(object), containerType);
GetMetadataForPropertiesCalls++;
return _properties;
get
{
throw new NotImplementedException();
}
}
public ModelMetadata GetMetadataForType([NotNull] Type modelType)
public override bool ConvertEmptyStringToNull
{
throw new NotImplementedException();
get
{
throw new NotImplementedException();
}
}
public override string DataTypeName
{
get
{
throw new NotImplementedException();
}
}
public override string Description
{
get
{
throw new NotImplementedException();
}
}
public override string DisplayFormatString
{
get
{
throw new NotImplementedException();
}
}
public override string DisplayName
{
get
{
return _displayName;
}
}
public void SetDisplayName(string displayName)
{
_displayName = displayName;
}
public override string EditFormatString
{
get
{
throw new NotImplementedException();
}
}
public override bool HasNonDefaultEditFormat
{
get
{
throw new NotImplementedException();
}
}
public override bool HideSurroundingHtml
{
get
{
throw new NotImplementedException();
}
}
public override bool HtmlEncode
{
get
{
throw new NotImplementedException();
}
}
public override bool IsReadOnly
{
get
{
throw new NotImplementedException();
}
}
public override bool IsRequired
{
get
{
throw new NotImplementedException();
}
}
public override string NullDisplayText
{
get
{
throw new NotImplementedException();
}
}
public override int Order
{
get
{
throw new NotImplementedException();
}
}
public override ModelPropertyCollection Properties
{
get
{
throw new NotImplementedException();
}
}
public override IPropertyBindingPredicateProvider PropertyBindingPredicateProvider
{
get
{
throw new NotImplementedException();
}
}
public override bool ShowForDisplay
{
get
{
throw new NotImplementedException();
}
}
public override bool ShowForEdit
{
get
{
throw new NotImplementedException();
}
}
public override string SimpleDisplayProperty
{
get
{
throw new NotImplementedException();
}
}
public override string TemplateHint
{
get
{
throw new NotImplementedException();
}
}
}
}

View File

@ -26,6 +26,38 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
return string.Format(CultureInfo.CurrentCulture, GetString("CompareAttributeTestResource"), p0, p1);
}
/// <summary>
/// description from resources
/// </summary>
internal static string DisplayAttribute_Description
{
get { return GetString("DisplayAttribute_Description"); }
}
/// <summary>
/// description from resources
/// </summary>
internal static string FormatDisplayAttribute_Description()
{
return GetString("DisplayAttribute_Description");
}
/// <summary>
/// name from resources
/// </summary>
internal static string DisplayAttribute_Name
{
get { return GetString("DisplayAttribute_Name"); }
}
/// <summary>
/// name from resources
/// </summary>
internal static string FormatDisplayAttribute_Name()
{
return GetString("DisplayAttribute_Name");
}
private static string GetString(string name, params string[] formatterNames)
{
var value = _resourceManager.GetString(name);

View File

@ -1,17 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
@ -26,36 +26,36 @@
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
@ -120,4 +120,10 @@
<data name="CompareAttributeTestResource" xml:space="preserve">
<value>Comparing {0} to {1}.</value>
</data>
<data name="DisplayAttribute_Description" xml:space="preserve">
<value>description from resources</value>
</data>
<data name="DisplayAttribute_Name" xml:space="preserve">
<value>name from resources</value>
</data>
</root>

View File

@ -0,0 +1,183 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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.Generic;
using System.Reflection;
using Microsoft.AspNet.Mvc.ModelBinding.Metadata;
using Microsoft.Framework.Internal;
using Xunit;
namespace Microsoft.AspNet.Mvc.ModelBinding
{
public class TestModelMetadataProvider : DefaultModelMetadataProvider
{
// Creates a provider with all the defaults - includes data annotations
public static IModelMetadataProvider CreateDefaultProvider()
{
var detailsProviders = new IMetadataDetailsProvider[]
{
new DefaultBindingMetadataProvider(),
new DataAnnotationsMetadataDetailsProvider(),
};
var compositeDetailsProvider = new DefaultCompositeMetadataDetailsProvider(detailsProviders);
return new DefaultModelMetadataProvider(compositeDetailsProvider);
}
private readonly TestModelMetadataDetailsProvider _detailsProvider;
public TestModelMetadataProvider()
: this(new TestModelMetadataDetailsProvider())
{
}
private TestModelMetadataProvider(TestModelMetadataDetailsProvider detailsProvider)
: base(new DefaultCompositeMetadataDetailsProvider(new IMetadataDetailsProvider[]
{
new DefaultBindingMetadataProvider(),
new DataAnnotationsMetadataDetailsProvider(),
detailsProvider
}))
{
_detailsProvider = detailsProvider;
}
public IMetadataBuilder ForType(Type type)
{
var key = ModelMetadataIdentity.ForType(type);
var builder = new MetadataBuilder(key);
_detailsProvider.Builders.Add(builder);
return builder;
}
public IMetadataBuilder ForType<TModel>()
{
return ForType(typeof(TModel));
}
public IMetadataBuilder ForProperty(Type containerType, string propertyName)
{
var property = containerType.GetRuntimeProperty(propertyName);
Assert.NotNull(property);
var key = ModelMetadataIdentity.ForProperty(property.PropertyType, property.Name, containerType);
var builder = new MetadataBuilder(key);
_detailsProvider.Builders.Add(builder);
return builder;
}
public IMetadataBuilder ForProperty<TContainer>(string propertyName)
{
return ForProperty(typeof(TContainer), propertyName);
}
private class TestModelMetadataDetailsProvider :
IBindingMetadataProvider,
IDisplayMetadataProvider,
IValidationMetadataProvider
{
public List<MetadataBuilder> Builders { get; } = new List<MetadataBuilder>();
public void GetBindingMetadata([NotNull] BindingMetadataProviderContext context)
{
foreach (var builder in Builders)
{
builder.Apply(context);
}
}
public void GetDisplayMetadata([NotNull] DisplayMetadataProviderContext context)
{
foreach (var builder in Builders)
{
builder.Apply(context);
}
}
public void GetValidationMetadata([NotNull] ValidationMetadataProviderContext context)
{
foreach (var builder in Builders)
{
builder.Apply(context);
}
}
}
public interface IMetadataBuilder
{
IMetadataBuilder BindingDetails(Action<BindingMetadata> action);
IMetadataBuilder DisplayDetails(Action<DisplayMetadata> action);
IMetadataBuilder ValidationDetails(Action<ValidationMetadata> action);
}
private class MetadataBuilder : IMetadataBuilder
{
private List<Action<BindingMetadata>> _bindingActions = new List<Action<BindingMetadata>>();
private List<Action<DisplayMetadata>> _displayActions = new List<Action<DisplayMetadata>>();
private List<Action<ValidationMetadata>> _valiationActions = new List<Action<ValidationMetadata>>();
private readonly ModelMetadataIdentity _key;
public MetadataBuilder(ModelMetadataIdentity key)
{
_key = key;
}
public void Apply(BindingMetadataProviderContext context)
{
if (_key.Equals(context.Key))
{
foreach (var action in _bindingActions)
{
action(context.BindingMetadata);
}
}
}
public void Apply(DisplayMetadataProviderContext context)
{
if (_key.Equals(context.Key))
{
foreach (var action in _displayActions)
{
action(context.DisplayMetadata);
}
}
}
public void Apply(ValidationMetadataProviderContext context)
{
if (_key.Equals(context.Key))
{
foreach (var action in _valiationActions)
{
action(context.ValidationMetadata);
}
}
}
public IMetadataBuilder BindingDetails(Action<BindingMetadata> action)
{
_bindingActions.Add(action);
return this;
}
public IMetadataBuilder DisplayDetails(Action<DisplayMetadata> action)
{
_displayActions.Add(action);
return this;
}
public IMetadataBuilder ValidationDetails(Action<ValidationMetadata> action)
{
_valiationActions.Add(action);
return this;
}
}
}
}

View File

@ -13,7 +13,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
public class AssociatedValidatorProviderTest
{
private readonly DataAnnotationsModelMetadataProvider _metadataProvider = new DataAnnotationsModelMetadataProvider();
private readonly IModelMetadataProvider _metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
[Fact]
public void GetValidatorsForPropertyWithLocalAttributes()

View File

@ -15,7 +15,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public void ClientRulesWithCompareAttribute_ErrorMessageUsesDisplayName()
{
// Arrange
var metadataProvider = new DataAnnotationsModelMetadataProvider();
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var metadata = metadataProvider.GetMetadataForProperty(typeof(PropertyDisplayNameModel), "MyProperty");
var attribute = new CompareAttribute("OtherProperty");
@ -39,7 +39,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public void ClientRulesWithCompareAttribute_ErrorMessageUsesPropertyName()
{
// Arrange
var metadataProvider = new DataAnnotationsModelMetadataProvider();
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var metadata = metadataProvider.GetMetadataForProperty(typeof(PropertyNameModel), "MyProperty");
var attribute = new CompareAttribute("OtherProperty");
var serviceCollection = new ServiceCollection();
@ -59,7 +59,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public void ClientRulesWithCompareAttribute_ErrorMessageUsesOverride()
{
// Arrange
var metadataProvider = new DataAnnotationsModelMetadataProvider();
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var metadata = metadataProvider.GetMetadataForProperty( typeof(PropertyNameModel), "MyProperty");
var attribute = new CompareAttribute("OtherProperty")
{
@ -88,7 +88,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
}
// Arrange
var metadataProvider = new DataAnnotationsModelMetadataProvider();
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var metadata = metadataProvider.GetMetadataForProperty(typeof(PropertyNameModel), "MyProperty");
var attribute = new CompareAttribute("OtherProperty")
{

View File

@ -14,7 +14,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
public class DataAnnotationsModelValidatorProviderTest
{
private readonly DataAnnotationsModelMetadataProvider _metadataProvider = new DataAnnotationsModelMetadataProvider();
private readonly IModelMetadataProvider _metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
#if ASPNET50
[Fact]

View File

@ -18,8 +18,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
public class DataAnnotationsModelValidatorTest
{
private static DataAnnotationsModelMetadataProvider _metadataProvider =
new DataAnnotationsModelMetadataProvider();
private static IModelMetadataProvider _metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
[Fact]
public void ValuesSet()

View File

@ -8,7 +8,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
public class DataMemberModelValidatorProviderTest
{
private readonly DataAnnotationsModelMetadataProvider _metadataProvider = new DataAnnotationsModelMetadataProvider();
private readonly IModelMetadataProvider _metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
[Fact]
public void ClassWithoutAttributes_NoValidator()

View File

@ -495,7 +495,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
new DataMemberModelValidatorProvider()
};
var modelMetadataProvider = new DataAnnotationsModelMetadataProvider();
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var excludedValidationTypesPredicate = new List<IExcludeTypeValidationFilter>();
if (excludedTypes != null)

View File

@ -15,7 +15,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public void ClientRulesWithMaxLengthAttribute()
{
// Arrange
var provider = new DataAnnotationsModelMetadataProvider();
var provider = TestModelMetadataProvider.CreateDefaultProvider();
var metadata = provider.GetMetadataForProperty(typeof(string), "Length");
var attribute = new MaxLengthAttribute(10);
var adapter = new MaxLengthAttributeAdapter(attribute);
@ -41,7 +41,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// Arrange
var propertyName = "Length";
var message = "{0} must be at most {1}";
var provider = new DataAnnotationsModelMetadataProvider();
var provider = TestModelMetadataProvider.CreateDefaultProvider();
var metadata = provider.GetMetadataForProperty(typeof(string), propertyName);
var attribute = new MaxLengthAttribute(5) { ErrorMessage = message };
var adapter = new MaxLengthAttributeAdapter(attribute);

View File

@ -15,7 +15,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public void ClientRulesWithMinLengthAttribute()
{
// Arrange
var provider = new DataAnnotationsModelMetadataProvider();
var provider = TestModelMetadataProvider.CreateDefaultProvider();
var metadata = provider.GetMetadataForProperty(typeof(string), "Length");
var attribute = new MinLengthAttribute(6);
var adapter = new MinLengthAttributeAdapter(attribute);
@ -41,7 +41,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// Arrange
var propertyName = "Length";
var message = "Array must have at least {1} items.";
var provider = new DataAnnotationsModelMetadataProvider();
var provider = TestModelMetadataProvider.CreateDefaultProvider();
var metadata = provider.GetMetadataForProperty(typeof(string), propertyName);
var attribute = new MinLengthAttribute(2) { ErrorMessage = message };
var adapter = new MinLengthAttributeAdapter(attribute);

View File

@ -15,7 +15,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public void GetClientValidationRules_ReturnsValidationParameters()
{
// Arrange
var provider = new DataAnnotationsModelMetadataProvider();
var provider = TestModelMetadataProvider.CreateDefaultProvider();
var metadata = provider.GetMetadataForProperty(typeof(string), "Length");
var attribute = new RangeAttribute(typeof(decimal), "0", "100");
var adapter = new RangeAttributeAdapter(attribute);

View File

@ -16,7 +16,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
// Arrange
var expected = ValidationAttributeUtil.GetRequiredErrorMessage("Length");
var provider = new DataAnnotationsModelMetadataProvider();
var provider = TestModelMetadataProvider.CreateDefaultProvider();
var metadata = provider.GetMetadataForProperty(typeof(string), "Length");
var attribute = new RequiredAttribute();
var adapter = new RequiredAttributeAdapter(attribute);

View File

@ -15,7 +15,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public void GetClientValidationRules_WithMaxLength_ReturnsValidationParameters()
{
// Arrange
var provider = new DataAnnotationsModelMetadataProvider();
var provider = TestModelMetadataProvider.CreateDefaultProvider();
var metadata = provider.GetMetadataForProperty(typeof(string), "Length");
var attribute = new StringLengthAttribute(8);
var adapter = new StringLengthAttributeAdapter(attribute);
@ -39,7 +39,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public void GetClientValidationRules_WithMinAndMaxLength_ReturnsValidationParameters()
{
// Arrange
var provider = new DataAnnotationsModelMetadataProvider();
var provider = TestModelMetadataProvider.CreateDefaultProvider();
var metadata = provider.GetMetadataForProperty(typeof(string), "Length");
var attribute = new StringLengthAttribute(10) { MinimumLength = 3 };
var adapter = new StringLengthAttributeAdapter(attribute);

View File

@ -97,7 +97,7 @@ namespace Microsoft.AspNet.Mvc.Razor
private static ViewContext CreateViewContext(RazorPageCreateModelExpressionModel model)
{
return CreateViewContext(model, new DataAnnotationsModelMetadataProvider());
return CreateViewContext(model, new TestModelMetadataProvider());
}
private static ViewContext CreateViewContext(

View File

@ -0,0 +1,182 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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.Generic;
using System.Reflection;
using Microsoft.AspNet.Mvc.ModelBinding.Metadata;
using Xunit;
namespace Microsoft.AspNet.Mvc.ModelBinding
{
public class TestModelMetadataProvider : DefaultModelMetadataProvider
{
// Creates a provider with all the defaults - includes data annotations
public static IModelMetadataProvider CreateDefaultProvider()
{
var detailsProviders = new IMetadataDetailsProvider[]
{
new DefaultBindingMetadataProvider(),
new DataAnnotationsMetadataDetailsProvider(),
};
var compositeDetailsProvider = new DefaultCompositeMetadataDetailsProvider(detailsProviders);
return new DefaultModelMetadataProvider(compositeDetailsProvider);
}
private readonly TestModelMetadataDetailsProvider _detailsProvider;
public TestModelMetadataProvider()
: this(new TestModelMetadataDetailsProvider())
{
}
private TestModelMetadataProvider(TestModelMetadataDetailsProvider detailsProvider)
: base(new DefaultCompositeMetadataDetailsProvider(new IMetadataDetailsProvider[]
{
new DefaultBindingMetadataProvider(),
new DataAnnotationsMetadataDetailsProvider(),
detailsProvider
}))
{
_detailsProvider = detailsProvider;
}
public IMetadataBuilder ForType(Type type)
{
var key = ModelMetadataIdentity.ForType(type);
var builder = new MetadataBuilder(key);
_detailsProvider.Builders.Add(builder);
return builder;
}
public IMetadataBuilder ForType<TModel>()
{
return ForType(typeof(TModel));
}
public IMetadataBuilder ForProperty(Type containerType, string propertyName)
{
var property = containerType.GetRuntimeProperty(propertyName);
Assert.NotNull(property);
var key = ModelMetadataIdentity.ForProperty(property.PropertyType, propertyName, containerType);
var builder = new MetadataBuilder(key);
_detailsProvider.Builders.Add(builder);
return builder;
}
public IMetadataBuilder ForProperty<TContainer>(string propertyName)
{
return ForProperty(typeof(TContainer), propertyName);
}
private class TestModelMetadataDetailsProvider :
IBindingMetadataProvider,
IDisplayMetadataProvider,
IValidationMetadataProvider
{
public List<MetadataBuilder> Builders { get; } = new List<MetadataBuilder>();
public void GetBindingMetadata(BindingMetadataProviderContext context)
{
foreach (var builder in Builders)
{
builder.Apply(context);
}
}
public void GetDisplayMetadata(DisplayMetadataProviderContext context)
{
foreach (var builder in Builders)
{
builder.Apply(context);
}
}
public void GetValidationMetadata(ValidationMetadataProviderContext context)
{
foreach (var builder in Builders)
{
builder.Apply(context);
}
}
}
public interface IMetadataBuilder
{
IMetadataBuilder BindingDetails(Action<BindingMetadata> action);
IMetadataBuilder DisplayDetails(Action<DisplayMetadata> action);
IMetadataBuilder ValidationDetails(Action<ValidationMetadata> action);
}
private class MetadataBuilder : IMetadataBuilder
{
private List<Action<BindingMetadata>> _bindingActions = new List<Action<BindingMetadata>>();
private List<Action<DisplayMetadata>> _displayActions = new List<Action<DisplayMetadata>>();
private List<Action<ValidationMetadata>> _valiationActions = new List<Action<ValidationMetadata>>();
private readonly ModelMetadataIdentity _key;
public MetadataBuilder(ModelMetadataIdentity key)
{
_key = key;
}
public void Apply(BindingMetadataProviderContext context)
{
if (_key.Equals(context.Key))
{
foreach (var action in _bindingActions)
{
action(context.BindingMetadata);
}
}
}
public void Apply(DisplayMetadataProviderContext context)
{
if (_key.Equals(context.Key))
{
foreach (var action in _displayActions)
{
action(context.DisplayMetadata);
}
}
}
public void Apply(ValidationMetadataProviderContext context)
{
if (_key.Equals(context.Key))
{
foreach (var action in _valiationActions)
{
action(context.ValidationMetadata);
}
}
}
public IMetadataBuilder BindingDetails(Action<BindingMetadata> action)
{
_bindingActions.Add(action);
return this;
}
public IMetadataBuilder DisplayDetails(Action<DisplayMetadata> action)
{
_displayActions.Add(action);
return this;
}
public IMetadataBuilder ValidationDetails(Action<ValidationMetadata> action)
{
_valiationActions.Add(action);
return this;
}
}
}
}

View File

@ -20,7 +20,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
{
// Arrange
var expectedTagName = "not-a";
var metadataProvider = new DataAnnotationsModelMetadataProvider();
var metadataProvider = new TestModelMetadataProvider();
var tagHelperContext = new TagHelperContext(
allAttributes: new Dictionary<string, object>

View File

@ -23,7 +23,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
{
// Arrange
var expectedTagName = "not-form";
var metadataProvider = new DataAnnotationsModelMetadataProvider();
var metadataProvider = new TestModelMetadataProvider();
var tagHelperContext = new TagHelperContext(
allAttributes: new Dictionary<string, object>
{
@ -408,8 +408,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
return new ViewContext(
actionContext,
Mock.Of<IView>(),
new ViewDataDictionary(
new DataAnnotationsModelMetadataProvider()),
new ViewDataDictionary(new TestModelMetadataProvider()),
TextWriter.Null);
}
}

View File

@ -132,7 +132,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
containerType,
model,
propertyName: nameof(Model.Text),
expressionName: nameAndId.Name);
expressionName: nameAndId.Name,
metadataProvider: null);
// Act
await tagHelper.ProcessAsync(context, output);
@ -274,9 +275,15 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
output.Content.SetContent(expectedContent);
output.PostContent.SetContent(expectedPostContent);
var metadataProvider = new TestModelMetadataProvider();
metadataProvider.ForProperty<Model>("Text").DisplayDetails(dd => dd.DataTypeName = dataTypeName);
var htmlGenerator = new Mock<IHtmlGenerator>(MockBehavior.Strict);
var tagHelper = GetTagHelper(htmlGenerator.Object, model, nameof(Model.Text));
tagHelper.For.Metadata.DataTypeName = dataTypeName;
var tagHelper = GetTagHelper(
htmlGenerator.Object,
model,
nameof(Model.Text),
metadataProvider: metadataProvider);
tagHelper.InputTypeName = inputTypeName;
var tagBuilder = new TagBuilder("input", new HtmlEncoder())
@ -367,9 +374,15 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
output.Content.SetContent(expectedContent);
output.PostContent.SetContent(expectedPostContent);
var metadataProvider = new TestModelMetadataProvider();
metadataProvider.ForProperty<Model>("Text").DisplayDetails(dd => dd.DataTypeName = dataTypeName);
var htmlGenerator = new Mock<IHtmlGenerator>(MockBehavior.Strict);
var tagHelper = GetTagHelper(htmlGenerator.Object, model, nameof(Model.Text));
tagHelper.For.Metadata.DataTypeName = dataTypeName;
var tagHelper = GetTagHelper(
htmlGenerator.Object,
model,
nameof(Model.Text),
metadataProvider: metadataProvider);
tagHelper.InputTypeName = inputTypeName;
var tagBuilder = new TagBuilder("input", new HtmlEncoder())
@ -562,9 +575,15 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
output.Content.SetContent(expectedContent);
output.PostContent.SetContent(expectedPostContent);
var metadataProvider = new TestModelMetadataProvider();
metadataProvider.ForProperty<Model>("Text").DisplayDetails(dd => dd.DataTypeName = dataTypeName);
var htmlGenerator = new Mock<IHtmlGenerator>(MockBehavior.Strict);
var tagHelper = GetTagHelper(htmlGenerator.Object, model, nameof(Model.Text));
tagHelper.For.Metadata.DataTypeName = dataTypeName;
var tagHelper = GetTagHelper(
htmlGenerator.Object,
model,
nameof(Model.Text),
metadataProvider: metadataProvider);
tagHelper.InputTypeName = inputTypeName;
var tagBuilder = new TagBuilder("input", new HtmlEncoder())
@ -655,15 +674,25 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
Assert.Equal(expectedTagName, output.TagName);
}
private static InputTagHelper GetTagHelper(IHtmlGenerator htmlGenerator, object model, string propertyName)
private static InputTagHelper GetTagHelper(
IHtmlGenerator htmlGenerator,
object model,
string propertyName,
IModelMetadataProvider metadataProvider = null)
{
if (metadataProvider == null)
{
metadataProvider = new TestModelMetadataProvider();
}
return GetTagHelper(
htmlGenerator,
container: new Model(),
containerType: typeof(Model),
model: model,
propertyName: propertyName,
expressionName: propertyName);
expressionName: propertyName,
metadataProvider: metadataProvider);
}
[Fact]
@ -704,10 +733,9 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
Type containerType,
object model,
string propertyName,
string expressionName)
string expressionName,
IModelMetadataProvider metadataProvider)
{
var metadataProvider = new EmptyModelMetadataProvider();
var containerMetadata = metadataProvider.GetMetadataForType(containerType);
var containerExplorer = metadataProvider.GetModelExplorerForType(containerType, container);

Some files were not shown because too many files have changed in this diff Show More