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:
parent
ccb9f70545
commit
90cef3b9ca
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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]))
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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 <input> 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 <div> 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 <input> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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 <input> 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 <div> 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 <input> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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-*"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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>();
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 },
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
{
|
||||
OperationBindingContext = new OperationBindingContext()
|
||||
{
|
||||
MetadataProvider = new DataAnnotationsModelMetadataProvider(),
|
||||
MetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(),
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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])
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Reference in New Issue