Removing ModelMetadata.Model

This commit is contained in:
Ryan Nowak 2015-02-18 08:45:13 -08:00
parent ed8e5716ea
commit 9d5364cf9b
110 changed files with 2313 additions and 1527 deletions

View File

@ -4,8 +4,8 @@
@functions {
private object FormattedValue {
get {
if (ViewData.TemplateInfo.FormattedModelValue == ViewData.ModelMetadata.Model) {
return String.Format(CultureInfo.CurrentCulture, "{0:0.00}", ViewData.ModelMetadata.Model);
if (ViewData.TemplateInfo.FormattedModelValue == ViewData.ModelExplorer.Model) {
return String.Format(CultureInfo.CurrentCulture, "{0:0.00}", ViewData.ModelExplorer.Model);
}
return ViewData.TemplateInfo.FormattedModelValue;
}

View File

@ -4,8 +4,8 @@
@functions {
private object FormattedValue {
get {
if (ViewData.TemplateInfo.FormattedModelValue == ViewData.ModelMetadata.Model) {
return string.Format(CultureInfo.CurrentCulture, "{0:0.00}", ViewData.ModelMetadata.Model);
if (ViewData.TemplateInfo.FormattedModelValue == ViewData.ModelExplorer.Model) {
return string.Format(CultureInfo.CurrentCulture, "{0:0.00}", ViewData.ModelExplorer.Model);
}
return ViewData.TemplateInfo.FormattedModelValue;
}

View File

@ -1209,17 +1209,14 @@ namespace Microsoft.AspNet.Mvc
throw new InvalidOperationException(message);
}
var modelMetadata = MetadataProvider.GetMetadataForType(
modelAccessor: () => model,
modelType: model.GetType());
var modelExplorer = MetadataProvider.GetModelExplorerForType(model.GetType(), model);
var modelName = prefix ?? string.Empty;
var validationContext = new ModelValidationContext(
modelName,
BindingContext.ValidatorProvider,
ModelState,
modelMetadata,
containerMetadata: null);
modelExplorer);
ObjectValidator.Validate(validationContext);
return ModelState.IsValid;

View File

@ -47,7 +47,6 @@ namespace Microsoft.AspNet.Mvc
foreach (var parameter in actionDescriptor.Parameters)
{
var metadata = _modelMetadataProvider.GetMetadataForParameter(
modelAccessor: null,
methodInfo: actionDescriptor.MethodInfo,
parameterName: parameter.Name);
@ -97,17 +96,19 @@ namespace Microsoft.AspNet.Mvc
foreach (var parameter in parameterMetadata)
{
var parameterType = parameter.ModelType;
var modelBindingContext = GetModelBindingContext(parameter, modelState, operationBindingContext);
var modelBindingResult = await bindingContext.ModelBinder.BindModelAsync(modelBindingContext);
if (modelBindingResult != null && modelBindingResult.IsModelSet)
{
var modelExplorer = new ModelExplorer(_modelMetadataProvider, parameter, modelBindingResult.Model);
arguments[parameter.PropertyName] = modelBindingResult.Model;
var validationContext = new ModelValidationContext(
modelBindingResult.Key,
bindingContext.ValidatorProvider,
actionContext.ModelState,
parameter,
containerMetadata: null);
modelBindingResult.Key,
bindingContext.ValidatorProvider,
actionContext.ModelState,
modelExplorer);
_validator.Validate(validationContext);
}
}

View File

@ -111,9 +111,7 @@ namespace Microsoft.AspNet.Mvc.Description
{
apiDescription.ResponseType = runtimeReturnType;
apiDescription.ResponseModelMetadata = _modelMetadataProvider.GetMetadataForType(
modelAccessor: null,
modelType: runtimeReturnType);
apiDescription.ResponseModelMetadata = _modelMetadataProvider.GetMetadataForType(runtimeReturnType);
var formats = GetResponseFormats(
action,
@ -436,7 +434,6 @@ namespace Microsoft.AspNet.Mvc.Description
public void WalkParameter()
{
var modelMetadata = Context.MetadataProvider.GetMetadataForParameter(
modelAccessor: null,
methodInfo: Context.ActionDescriptor.MethodInfo,
parameterName: Parameter.Name);

View File

@ -252,9 +252,7 @@ namespace Microsoft.AspNet.Mvc
throw new ArgumentException(message, nameof(modelType));
}
var modelMetadata = metadataProvider.GetMetadataForType(
modelAccessor: () => model,
modelType: modelType);
var modelMetadata = metadataProvider.GetMetadataForType(modelType);
var operationBindingContext = new OperationBindingContext
{
@ -266,6 +264,7 @@ namespace Microsoft.AspNet.Mvc
var modelBindingContext = new ModelBindingContext
{
Model = model,
ModelMetadata = modelMetadata,
ModelName = prefix,
ModelState = modelState,
@ -278,7 +277,8 @@ namespace Microsoft.AspNet.Mvc
var modelBindingResult = await modelBinder.BindModelAsync(modelBindingContext);
if (modelBindingResult != null)
{
var modelValidationContext = new ModelValidationContext(modelBindingContext, modelMetadata);
var modelExplorer = new ModelExplorer(metadataProvider, modelMetadata, modelBindingResult.Model);
var modelValidationContext = new ModelValidationContext(modelBindingContext, modelExplorer);
modelValidationContext.RootPrefix = prefix;
objectModelValidator.Validate(modelValidationContext);
return modelState.IsValid;
@ -382,4 +382,4 @@ namespace Microsoft.AspNet.Mvc
}
}
}
}
}

View File

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Globalization;
using System.Linq.Expressions;
using System.Reflection;
using Microsoft.AspNet.Mvc.Core;
@ -12,7 +13,7 @@ namespace Microsoft.AspNet.Mvc.Rendering.Expressions
{
public static class ExpressionMetadataProvider
{
public static ModelMetadata FromLambdaExpression<TModel, TResult>(
public static ModelExplorer FromLambdaExpression<TModel, TResult>(
[NotNull] Expression<Func<TModel, TResult>> expression,
[NotNull] ViewDataDictionary<TModel> viewData,
IModelMetadataProvider metadataProvider)
@ -55,12 +56,11 @@ namespace Microsoft.AspNet.Mvc.Rendering.Expressions
throw new InvalidOperationException(Resources.TemplateHelpers_TemplateLimitations);
}
var container = viewData.Model;
Func<object> modelAccessor = () =>
Func<object, object> modelAccessor = (container) =>
{
try
{
return CachedExpressionCompiler.Process(expression)(container);
return CachedExpressionCompiler.Process(expression)((TModel)container);
}
catch (NullReferenceException)
{
@ -68,18 +68,31 @@ namespace Microsoft.AspNet.Mvc.Rendering.Expressions
}
};
return GetMetadataFromProvider(
modelAccessor,
typeof(TResult),
propertyName,
container,
containerType,
metadataProvider);
ModelMetadata metadata;
if (propertyName == null)
{
// Ex:
// m => 5 (arbitrary expression)
// m => foo (arbitrary expression)
// m => m.Widgets[0] (expression ending with non-property-access)
metadata = metadataProvider.GetMetadataForType(typeof(TResult));
}
else
{
// Ex:
// m => m.Color (simple property access)
// m => m.Color.Red (nested property access)
// m => m.Widgets[0].Size (expression ending with property-access)
metadata = metadataProvider.GetMetadataForType(containerType).Properties[propertyName];
}
return viewData.ModelExplorer.GetExplorerForExpression(metadata, modelAccessor);
}
public static ModelMetadata FromStringExpression(string expression,
[NotNull] ViewDataDictionary viewData,
IModelMetadataProvider metadataProvider)
public static ModelExplorer FromStringExpression(
string expression,
[NotNull] ViewDataDictionary viewData,
IModelMetadataProvider metadataProvider)
{
if (string.IsNullOrEmpty(expression))
{
@ -88,93 +101,64 @@ namespace Microsoft.AspNet.Mvc.Rendering.Expressions
}
var viewDataInfo = ViewDataEvaluator.Eval(viewData, expression);
Type containerType = null;
Type modelType = null;
Func<object> modelAccessor = null;
string propertyName = null;
object container = null;
if (viewDataInfo == null)
{
// Try getting a property from ModelMetadata if we couldn't find an answer in ViewData
var propertyExplorer = viewData.ModelExplorer.GetExplorerForProperty(expression);
if (propertyExplorer != null)
{
return propertyExplorer;
}
}
if (viewDataInfo != null)
{
ModelExplorer containerExplorer = viewData.ModelExplorer;
if (viewDataInfo.Container != null)
{
containerType = viewDataInfo.Container.GetType();
container = viewDataInfo.Container;
containerExplorer = metadataProvider.GetModelExplorerForType(
viewDataInfo.Container.GetType(),
viewDataInfo.Container);
}
modelAccessor = () => viewDataInfo.Value;
if (viewDataInfo.PropertyInfo != null)
{
propertyName = viewDataInfo.PropertyInfo.Name;
modelType = viewDataInfo.PropertyInfo.PropertyType;
// We've identified a property access, which provides us with accurate metadata.
var containerType = viewDataInfo.Container?.GetType() ?? viewDataInfo.PropertyInfo.DeclaringType;
var containerMetadata = metadataProvider.GetMetadataForType(viewDataInfo.Container.GetType());
var propertyMetadata = containerMetadata.Properties[viewDataInfo.PropertyInfo.Name];
Func<object, object> modelAccessor = (ignore) => viewDataInfo.Value;
return containerExplorer.GetExplorerForExpression(propertyMetadata, modelAccessor);
}
else if (viewDataInfo.Value != null)
{
// We only need to delay accessing properties (for LINQ to SQL)
modelType = viewDataInfo.Value.GetType();
}
}
else
{
// Try getting a property from ModelMetadata if we couldn't find an answer in ViewData
var propertyMetadata = viewData.ModelMetadata.Properties[expression];
if (propertyMetadata != null)
{
return propertyMetadata;
// We have a value, even though we may not know where it came from.
var valueMetadata = metadataProvider.GetMetadataForType(viewDataInfo.Value.GetType());
return containerExplorer.GetExplorerForExpression(valueMetadata, viewDataInfo.Value);
}
}
return GetMetadataFromProvider(modelAccessor,
modelType ?? typeof(string),
propertyName,
container,
containerType,
metadataProvider);
// Treat the expression as string if we don't find anything better.
var stringMetadata = metadataProvider.GetMetadataForType(typeof(string));
return viewData.ModelExplorer.GetExplorerForExpression(stringMetadata, modelAccessor: null);
}
private static ModelMetadata FromModel([NotNull] ViewDataDictionary viewData,
IModelMetadataProvider metadataProvider)
private static ModelExplorer FromModel(
[NotNull] ViewDataDictionary viewData,
IModelMetadataProvider metadataProvider)
{
if (viewData.ModelMetadata.ModelType == typeof(object))
{
// Use common simple type rather than object so e.g. Editor() at least generates a TextBox.
return GetMetadataFromProvider(
modelAccessor: null,
modelType: typeof(string),
propertyName: null,
container: null,
containerType: null,
metadataProvider: metadataProvider);
var model = viewData.Model == null ? null : Convert.ToString(viewData.Model, CultureInfo.CurrentCulture);
return metadataProvider.GetModelExplorerForType(typeof(string), model);
}
else
{
return viewData.ModelMetadata;
return viewData.ModelExplorer;
}
}
// An IModelMetadataProvider is not required unless this method is called. Therefore other methods in this
// class lack [NotNull] attributes for their corresponding parameter.
private static ModelMetadata GetMetadataFromProvider(Func<object> modelAccessor,
Type modelType,
string propertyName,
object container,
Type containerType,
[NotNull] IModelMetadataProvider metadataProvider)
{
if (containerType != null && !string.IsNullOrEmpty(propertyName))
{
var metadata =
metadataProvider.GetMetadataForProperty(modelAccessor, containerType, propertyName);
if (metadata != null)
{
metadata.Container = container;
}
return metadata;
}
return metadataProvider.GetMetadataForType(modelAccessor, modelType);
}
}
}
}

View File

@ -91,7 +91,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
public static string CollectionTemplate(IHtmlHelper htmlHelper)
{
var model = htmlHelper.ViewData.ModelMetadata.Model;
var model = htmlHelper.ViewData.Model;
if (model == null)
{
return string.Empty;
@ -136,14 +136,14 @@ namespace Microsoft.AspNet.Mvc.Rendering
itemType = item.GetType();
}
var metadata = metadataProvider.GetMetadataForType(() => item, itemType);
var modelExplorer = metadataProvider.GetModelExplorerForType(itemType, item);
var fieldName = string.Format(CultureInfo.InvariantCulture, "{0}[{1}]", fieldNameBase, index++);
var templateBuilder = new TemplateBuilder(
viewEngine,
htmlHelper.ViewContext,
htmlHelper.ViewData,
metadata,
modelExplorer,
htmlFieldName: fieldName,
templateName: null,
readOnly: true,
@ -163,10 +163,10 @@ namespace Microsoft.AspNet.Mvc.Rendering
public static string DecimalTemplate(IHtmlHelper htmlHelper)
{
if (htmlHelper.ViewData.TemplateInfo.FormattedModelValue == htmlHelper.ViewData.ModelMetadata.Model)
if (htmlHelper.ViewData.TemplateInfo.FormattedModelValue == htmlHelper.ViewData.Model)
{
htmlHelper.ViewData.TemplateInfo.FormattedModelValue =
string.Format(CultureInfo.CurrentCulture, "{0:0.00}", htmlHelper.ViewData.ModelMetadata.Model);
string.Format(CultureInfo.CurrentCulture, "{0:0.00}", htmlHelper.ViewData.Model);
}
return StringTemplate(htmlHelper);
@ -203,18 +203,18 @@ namespace Microsoft.AspNet.Mvc.Rendering
{
var viewData = htmlHelper.ViewData;
var templateInfo = viewData.TemplateInfo;
var modelMetadata = viewData.ModelMetadata;
var modelExplorer = viewData.ModelExplorer;
var builder = new StringBuilder();
if (modelMetadata.Model == null)
if (modelExplorer.Model == null)
{
return modelMetadata.NullDisplayText;
return modelExplorer.Metadata.NullDisplayText;
}
if (templateInfo.TemplateDepth > 1)
{
var text = modelMetadata.SimpleDisplayText;
if (modelMetadata.HtmlEncode)
var text = modelExplorer.GetSimpleDisplayText();
if (modelExplorer.Metadata.HtmlEncode)
{
text = htmlHelper.Encode(text);
}
@ -224,9 +224,15 @@ namespace Microsoft.AspNet.Mvc.Rendering
var serviceProvider = htmlHelper.ViewContext.HttpContext.RequestServices;
var viewEngine = serviceProvider.GetRequiredService<ICompositeViewEngine>();
var properties = modelMetadata.Properties.Where(metadata => ShouldShow(metadata, templateInfo));
foreach (var propertyMetadata in properties)
foreach (var propertyExplorer in modelExplorer.Properties)
{
var propertyMetadata = propertyExplorer.Metadata;
if (!ShouldShow(propertyExplorer, templateInfo))
{
continue;
}
var divTag = new TagBuilder("div");
if (!propertyMetadata.HideSurroundingHtml)
@ -250,7 +256,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
viewEngine,
htmlHelper.ViewContext,
htmlHelper.ViewData,
propertyMetadata,
propertyExplorer,
htmlFieldName: propertyMetadata.PropertyName,
templateName: null,
readOnly: true,
@ -267,12 +273,12 @@ namespace Microsoft.AspNet.Mvc.Rendering
return builder.ToString();
}
private static bool ShouldShow(ModelMetadata metadata, TemplateInfo templateInfo)
private static bool ShouldShow(ModelExplorer modelExplorer, TemplateInfo templateInfo)
{
return
metadata.ShowForDisplay &&
!metadata.IsComplexType &&
!templateInfo.Visited(metadata);
modelExplorer.Metadata.ShowForDisplay &&
!modelExplorer.Metadata.IsComplexType &&
!templateInfo.Visited(modelExplorer);
}
public static string StringTemplate(IHtmlHelper htmlHelper)

View File

@ -52,7 +52,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
public static string CollectionTemplate(IHtmlHelper htmlHelper)
{
var viewData = htmlHelper.ViewData;
var model = viewData.ModelMetadata.Model;
var model = viewData.Model;
if (model == null)
{
return string.Empty;
@ -96,14 +96,14 @@ namespace Microsoft.AspNet.Mvc.Rendering
itemType = item.GetType();
}
var metadata = metadataProvider.GetMetadataForType(() => item, itemType);
var modelExplorer = metadataProvider.GetModelExplorerForType(itemType, item);
var fieldName = string.Format(CultureInfo.InvariantCulture, "{0}[{1}]", fieldNameBase, index++);
var templateBuilder = new TemplateBuilder(
viewEngine,
htmlHelper.ViewContext,
htmlHelper.ViewData,
metadata,
modelExplorer,
htmlFieldName: fieldName,
templateName: null,
readOnly: false,
@ -123,10 +123,10 @@ namespace Microsoft.AspNet.Mvc.Rendering
public static string DecimalTemplate(IHtmlHelper htmlHelper)
{
if (htmlHelper.ViewData.TemplateInfo.FormattedModelValue == htmlHelper.ViewData.ModelMetadata.Model)
if (htmlHelper.ViewData.TemplateInfo.FormattedModelValue == htmlHelper.ViewData.Model)
{
htmlHelper.ViewData.TemplateInfo.FormattedModelValue =
string.Format(CultureInfo.CurrentCulture, "{0:0.00}", htmlHelper.ViewData.ModelMetadata.Model);
string.Format(CultureInfo.CurrentCulture, "{0:0.00}", htmlHelper.ViewData.Model);
}
return StringTemplate(htmlHelper);
@ -227,18 +227,18 @@ namespace Microsoft.AspNet.Mvc.Rendering
{
var viewData = htmlHelper.ViewData;
var templateInfo = viewData.TemplateInfo;
var modelMetadata = viewData.ModelMetadata;
var modelExplorer = viewData.ModelExplorer;
var builder = new StringBuilder();
if (templateInfo.TemplateDepth > 1)
{
if (modelMetadata.Model == null)
if (modelExplorer.Model == null)
{
return modelMetadata.NullDisplayText;
return modelExplorer.Metadata.NullDisplayText;
}
var text = modelMetadata.SimpleDisplayText;
if (modelMetadata.HtmlEncode)
var text = modelExplorer.GetSimpleDisplayText();
if (modelExplorer.Metadata.HtmlEncode)
{
text = htmlHelper.Encode(text);
}
@ -248,9 +248,15 @@ namespace Microsoft.AspNet.Mvc.Rendering
var serviceProvider = htmlHelper.ViewContext.HttpContext.RequestServices;
var viewEngine = serviceProvider.GetRequiredService<ICompositeViewEngine>();
var properties = modelMetadata.Properties.Where(metadata => ShouldShow(metadata, templateInfo));
foreach (var propertyMetadata in properties)
foreach (var propertyExplorer in modelExplorer.Properties)
{
var propertyMetadata = propertyExplorer.Metadata;
if (!ShouldShow(propertyExplorer, templateInfo))
{
continue;
}
var divTag = new TagBuilder("div");
if (!propertyMetadata.HideSurroundingHtml)
@ -278,7 +284,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
viewEngine,
htmlHelper.ViewContext,
htmlHelper.ViewData,
propertyMetadata,
propertyExplorer,
htmlFieldName: propertyMetadata.PropertyName,
templateName: null,
readOnly: false,
@ -311,12 +317,12 @@ namespace Microsoft.AspNet.Mvc.Rendering
.ToString();
}
private static bool ShouldShow(ModelMetadata metadata, TemplateInfo templateInfo)
private static bool ShouldShow(ModelExplorer modelExplorer, TemplateInfo templateInfo)
{
return
metadata.ShowForEdit &&
!metadata.IsComplexType &&
!templateInfo.Visited(metadata);
modelExplorer.Metadata.ShowForEdit &&
!modelExplorer.Metadata.IsComplexType &&
!templateInfo.Visited(modelExplorer);
}
public static string StringTemplate(IHtmlHelper htmlHelper)
@ -376,7 +382,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
}
var metadata = htmlHelper.ViewData.ModelMetadata;
var value = metadata.Model;
var value = htmlHelper.ViewData.Model;
if (htmlHelper.ViewData.TemplateInfo.FormattedModelValue != value && metadata.HasNonDefaultEditFormat)
{
return;
@ -407,4 +413,4 @@ namespace Microsoft.AspNet.Mvc.Rendering
.ToString();
}
}
}
}

View File

@ -90,20 +90,20 @@ namespace Microsoft.AspNet.Mvc.Rendering
/// <inheritdoc />
public virtual TagBuilder GenerateCheckBox(
[NotNull] ViewContext viewContext,
ModelMetadata metadata,
ModelExplorer modelExplorer,
string expression,
bool? isChecked,
object htmlAttributes)
{
if (metadata != null)
if (modelExplorer != null)
{
// CheckBoxFor() case. That API does not support passing isChecked directly.
Debug.Assert(!isChecked.HasValue);
if (metadata.Model != null)
if (modelExplorer.Model != null)
{
bool modelChecked;
if (Boolean.TryParse(metadata.Model.ToString(), out modelChecked))
if (Boolean.TryParse(modelExplorer.Model.ToString(), out modelChecked))
{
isChecked = modelChecked;
}
@ -121,10 +121,10 @@ namespace Microsoft.AspNet.Mvc.Rendering
return GenerateInput(
viewContext,
InputType.CheckBox,
metadata,
modelExplorer,
expression,
value: "true",
useViewData: (metadata == null && !isChecked.HasValue),
useViewData: (modelExplorer == null && !isChecked.HasValue),
isChecked: isChecked ?? false,
setId: true,
isExplicitValue: false,
@ -135,7 +135,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
/// <inheritdoc />
public virtual TagBuilder GenerateHiddenForCheckbox(
[NotNull] ViewContext viewContext,
ModelMetadata metadata,
ModelExplorer modelExplorer,
string expression)
{
var tagBuilder = new TagBuilder("input");
@ -202,7 +202,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
/// <inheritdoc />
public virtual TagBuilder GenerateHidden(
[NotNull] ViewContext viewContext,
ModelMetadata metadata,
ModelExplorer modelExplorer,
string expression,
object value,
bool useViewData,
@ -219,7 +219,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
return GenerateInput(
viewContext,
InputType.Hidden,
metadata,
modelExplorer,
expression,
value,
useViewData,
@ -233,12 +233,14 @@ namespace Microsoft.AspNet.Mvc.Rendering
/// <inheritdoc />
public virtual TagBuilder GenerateLabel(
[NotNull] ViewContext viewContext,
[NotNull] ModelMetadata metadata,
[NotNull] ModelExplorer modelExplorer,
string expression,
string labelText,
object htmlAttributes)
{
var resolvedLabelText = labelText ?? metadata.DisplayName ?? metadata.PropertyName;
var resolvedLabelText = labelText ??
modelExplorer.Metadata.DisplayName ??
modelExplorer.Metadata.PropertyName;
if (resolvedLabelText == null)
{
resolvedLabelText =
@ -263,7 +265,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
/// <inheritdoc />
public virtual TagBuilder GeneratePassword(
[NotNull] ViewContext viewContext,
ModelMetadata metadata,
ModelExplorer modelExplorer,
string expression,
object value,
object htmlAttributes)
@ -272,7 +274,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
return GenerateInput(
viewContext,
InputType.Password,
metadata,
modelExplorer,
expression,
value,
useViewData: false,
@ -286,14 +288,14 @@ namespace Microsoft.AspNet.Mvc.Rendering
/// <inheritdoc />
public virtual TagBuilder GenerateRadioButton(
[NotNull] ViewContext viewContext,
ModelMetadata metadata,
ModelExplorer modelExplorer,
string expression,
object value,
bool? isChecked,
object htmlAttributes)
{
var htmlAttributeDictionary = GetHtmlAttributeDictionaryOrNull(htmlAttributes);
if (metadata == null)
if (modelExplorer == null)
{
// RadioButton() case. Do not override checked attribute if isChecked is implicit.
if (!isChecked.HasValue &&
@ -322,7 +324,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
// Need a value to determine isChecked.
Debug.Assert(value != null);
var model = metadata.Model;
var model = modelExplorer.Model;
var valueString = Convert.ToString(value, CultureInfo.CurrentCulture);
isChecked = model != null &&
string.Equals(model.ToString(), valueString, StringComparison.OrdinalIgnoreCase);
@ -337,7 +339,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
return GenerateInput(
viewContext,
InputType.Radio,
metadata,
modelExplorer,
expression,
value,
useViewData: false,
@ -365,7 +367,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
/// <inheritdoc />
public TagBuilder GenerateSelect(
[NotNull] ViewContext viewContext,
ModelMetadata metadata,
ModelExplorer modelExplorer,
string optionLabel,
string expression,
IEnumerable<SelectListItem> selectList,
@ -375,7 +377,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
ICollection<string> ignored;
return GenerateSelect(
viewContext,
metadata,
modelExplorer,
optionLabel,
expression,
selectList,
@ -387,7 +389,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
/// <inheritdoc />
public virtual TagBuilder GenerateSelect(
[NotNull] ViewContext viewContext,
ModelMetadata metadata,
ModelExplorer modelExplorer,
string optionLabel,
string expression,
IEnumerable<SelectListItem> selectList,
@ -428,9 +430,9 @@ namespace Microsoft.AspNet.Mvc.Rendering
{
defaultValue = viewContext.ViewData.Eval(expression);
}
else if (metadata != null)
else if (modelExplorer != null)
{
defaultValue = metadata.Model;
defaultValue = modelExplorer.Model;
}
}
@ -469,7 +471,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
}
}
tagBuilder.MergeAttributes(GetValidationAttributes(viewContext, metadata, expression));
tagBuilder.MergeAttributes(GetValidationAttributes(viewContext, modelExplorer, expression));
return tagBuilder;
}
@ -477,7 +479,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
/// <inheritdoc />
public virtual TagBuilder GenerateTextArea(
[NotNull] ViewContext viewContext,
ModelMetadata metadata,
ModelExplorer modelExplorer,
string expression,
int rows,
int columns,
@ -509,9 +511,9 @@ namespace Microsoft.AspNet.Mvc.Rendering
{
value = modelState.Value.AttemptedValue;
}
else if (metadata.Model != null)
else if (modelExplorer.Model != null)
{
value = metadata.Model.ToString();
value = modelExplorer.Model.ToString();
}
var tagBuilder = new TagBuilder("textarea");
@ -528,7 +530,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
}
tagBuilder.MergeAttribute("name", fullName, true);
tagBuilder.MergeAttributes(GetValidationAttributes(viewContext, metadata, expression));
tagBuilder.MergeAttributes(GetValidationAttributes(viewContext, modelExplorer, expression));
// If there are any errors for a named field, we add this CSS attribute.
if (modelState != null && modelState.Errors.Count > 0)
@ -546,7 +548,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
/// <inheritdoc />
public virtual TagBuilder GenerateTextBox(
[NotNull] ViewContext viewContext,
ModelMetadata metadata,
ModelExplorer modelExplorer,
string expression,
object value,
string format,
@ -556,10 +558,10 @@ namespace Microsoft.AspNet.Mvc.Rendering
return GenerateInput(
viewContext,
InputType.Text,
metadata,
modelExplorer,
expression,
value,
useViewData: (metadata == null && value == null),
useViewData: (modelExplorer == null && value == null),
isChecked: false,
setId: true,
isExplicitValue: true,
@ -726,17 +728,19 @@ namespace Microsoft.AspNet.Mvc.Rendering
/// <inheritdoc />
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(
[NotNull] ViewContext viewContext,
ModelMetadata metadata,
ModelExplorer modelExplorer,
string expression)
{
var validatorProvider = _bindingContextAccessor.Value.ValidatorProvider;
metadata = metadata ??
modelExplorer = modelExplorer ??
ExpressionMetadataProvider.FromStringExpression(expression, viewContext.ViewData, _metadataProvider);
var validationContext =
new ClientModelValidationContext(metadata, _metadataProvider, viewContext.HttpContext.RequestServices);
var validationContext = new ClientModelValidationContext(
modelExplorer.Metadata,
_metadataProvider,
viewContext.HttpContext.RequestServices);
return validatorProvider
.GetValidators(metadata)
.GetValidators(modelExplorer.Metadata)
.OfType<IClientModelValidator>()
.SelectMany(v => v.GetClientValidationRules(validationContext));
}
@ -832,7 +836,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
protected virtual TagBuilder GenerateInput(
[NotNull] ViewContext viewContext,
InputType inputType,
ModelMetadata metadata,
ModelExplorer modelExplorer,
string expression,
object value,
bool useViewData,
@ -926,7 +930,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
tagBuilder.AddCssClass(HtmlHelper.ValidationInputCssClassName);
}
tagBuilder.MergeAttributes(GetValidationAttributes(viewContext, metadata, expression));
tagBuilder.MergeAttributes(GetValidationAttributes(viewContext, modelExplorer, expression));
return tagBuilder;
}
@ -951,7 +955,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
// never rendered validation for a field with this name in this form.
protected virtual IDictionary<string, object> GetValidationAttributes(
ViewContext viewContext,
ModelMetadata metadata,
ModelExplorer modelExplorer,
string expression)
{
var formContext = viewContext.ClientValidationEnabled ? viewContext.FormContext : null;
@ -967,7 +971,8 @@ namespace Microsoft.AspNet.Mvc.Rendering
}
formContext.RenderedField(fullName, true);
var clientRules = GetClientValidationRules(viewContext, metadata, expression);
var clientRules = GetClientValidationRules(viewContext, modelExplorer, expression);
return UnobtrusiveValidationAttributesGenerator.GetValidationAttributes(clientRules);
}
@ -1153,4 +1158,4 @@ namespace Microsoft.AspNet.Mvc.Rendering
return tagBuilder.ToString(TagRenderMode.Normal);
}
}
}
}

View File

@ -235,7 +235,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
public HtmlString CheckBox(string expression, bool? isChecked, object htmlAttributes)
{
return GenerateCheckBox(
metadata: null,
modelExplorer: null,
expression: expression,
isChecked: isChecked,
htmlAttributes: htmlAttributes);
@ -282,15 +282,15 @@ namespace Microsoft.AspNet.Mvc.Rendering
/// <inheritdoc />
public string DisplayName(string expression)
{
var metadata = ExpressionMetadataProvider.FromStringExpression(expression, ViewData, MetadataProvider);
return GenerateDisplayName(metadata, expression);
var modelExplorer = ExpressionMetadataProvider.FromStringExpression(expression, ViewData, MetadataProvider);
return GenerateDisplayName(modelExplorer, expression);
}
/// <inheritdoc />
public string DisplayText(string expression)
{
var metadata = ExpressionMetadataProvider.FromStringExpression(expression, ViewData, MetadataProvider);
return GenerateDisplayText(metadata);
var modelExplorer = ExpressionMetadataProvider.FromStringExpression(expression, ViewData, MetadataProvider);
return GenerateDisplayText(modelExplorer);
}
/// <inheritdoc />
@ -301,7 +301,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
object htmlAttributes)
{
return GenerateDropDown(
metadata: null,
modelExplorer: null,
expression: expression,
selectList: selectList,
optionLabel: optionLabel,
@ -315,10 +315,10 @@ namespace Microsoft.AspNet.Mvc.Rendering
string htmlFieldName,
object additionalViewData)
{
var metadata = ExpressionMetadataProvider.FromStringExpression(expression, ViewData, MetadataProvider);
var modelExplorer = ExpressionMetadataProvider.FromStringExpression(expression, ViewData, MetadataProvider);
return GenerateEditor(
metadata,
modelExplorer,
htmlFieldName ?? ExpressionHelper.GetExpressionText(expression),
templateName,
additionalViewData);
@ -328,7 +328,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
public HtmlString Hidden(string expression, object value, object htmlAttributes)
{
return GenerateHidden(
metadata: null,
modelExplorer: null,
expression: expression,
value: value,
useViewData: (value == null),
@ -344,9 +344,9 @@ namespace Microsoft.AspNet.Mvc.Rendering
/// <inheritdoc />
public HtmlString Label(string expression, string labelText, object htmlAttributes)
{
var metadata = ExpressionMetadataProvider.FromStringExpression(expression, ViewData, MetadataProvider);
var modelExplorer = ExpressionMetadataProvider.FromStringExpression(expression, ViewData, MetadataProvider);
return GenerateLabel(
metadata,
modelExplorer,
expression,
labelText,
htmlAttributes);
@ -356,7 +356,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
public HtmlString ListBox(string expression, IEnumerable<SelectListItem> selectList, object htmlAttributes)
{
return GenerateListBox(
metadata: null,
modelExplorer: null,
expression: expression,
selectList: selectList,
htmlAttributes: htmlAttributes);
@ -388,7 +388,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
return RenderPartialCoreAsync(partialViewName, model, viewData, ViewContext.Writer);
}
protected virtual HtmlString GenerateDisplay(ModelMetadata metadata,
protected virtual HtmlString GenerateDisplay(ModelExplorer modelExplorer,
string htmlFieldName,
string templateName,
object additionalViewData)
@ -396,7 +396,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
var templateBuilder = new TemplateBuilder(_viewEngine,
ViewContext,
ViewData,
metadata,
modelExplorer,
htmlFieldName,
templateName,
readOnly: true,
@ -443,7 +443,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
public HtmlString Password(string expression, object value, object htmlAttributes)
{
return GeneratePassword(
metadata: null,
modelExplorer: null,
expression: expression,
value: value,
htmlAttributes: htmlAttributes);
@ -453,7 +453,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
public HtmlString RadioButton(string expression, object value, bool? isChecked, object htmlAttributes)
{
return GenerateRadioButton(
metadata: null,
modelExplorer: null,
expression: expression,
value: value,
isChecked: isChecked,
@ -535,19 +535,29 @@ namespace Microsoft.AspNet.Mvc.Rendering
/// <inheritdoc />
public HtmlString TextArea(string expression, string value, int rows, int columns, object htmlAttributes)
{
var metadata = ExpressionMetadataProvider.FromStringExpression(expression, ViewData, MetadataProvider);
var modelExplorer = ExpressionMetadataProvider.FromStringExpression(expression, ViewData, MetadataProvider);
if (value != null)
{
metadata.Model = value;
// As a special case we allow treating a string value as a model of arbitrary type.
// So pass through the string representation, even though the ModelMetadata might
// be for some other type.
//
// We do this because thought we're displaying something as a string, we want to have
// the right set of validation attributes.
modelExplorer = new ModelExplorer(
MetadataProvider,
modelExplorer.Container,
modelExplorer.Metadata,
value);
}
return GenerateTextArea(metadata, expression, rows, columns, htmlAttributes);
return GenerateTextArea(modelExplorer, expression, rows, columns, htmlAttributes);
}
/// <inheritdoc />
public HtmlString TextBox(string expression, object value, string format, object htmlAttributes)
{
return GenerateTextBox(metadata: null, expression: expression, value: value, format: format,
return GenerateTextBox(modelExplorer: null, expression: expression, value: value, format: format,
htmlAttributes: htmlAttributes);
}
@ -568,18 +578,19 @@ namespace Microsoft.AspNet.Mvc.Rendering
}
protected virtual HtmlString GenerateCheckBox(
ModelMetadata metadata,
ModelExplorer modelExplorer,
string expression,
bool? isChecked,
object htmlAttributes)
{
var checkbox = _htmlGenerator.GenerateCheckBox(
ViewContext,
metadata,
modelExplorer,
expression,
isChecked,
htmlAttributes);
var hidden = _htmlGenerator.GenerateHiddenForCheckbox(ViewContext, metadata, expression);
var hidden = _htmlGenerator.GenerateHiddenForCheckbox(ViewContext, modelExplorer, expression);
if (checkbox == null || hidden == null)
{
return HtmlString.Empty;
@ -590,12 +601,12 @@ namespace Microsoft.AspNet.Mvc.Rendering
return new HtmlString(elements);
}
protected virtual string GenerateDisplayName([NotNull] ModelMetadata metadata, string expression)
protected virtual string GenerateDisplayName([NotNull] ModelExplorer modelExplorer, string expression)
{
// We don't call ModelMetadata.GetDisplayName here because
// we want to fall back to the field name rather than the ModelType.
// This is similar to how the GenerateLabel get the text of a label.
var resolvedDisplayName = metadata.DisplayName ?? metadata.PropertyName;
var resolvedDisplayName = modelExplorer.Metadata.DisplayName ?? modelExplorer.Metadata.PropertyName;
if (resolvedDisplayName == null)
{
resolvedDisplayName =
@ -605,13 +616,13 @@ namespace Microsoft.AspNet.Mvc.Rendering
return resolvedDisplayName;
}
protected virtual string GenerateDisplayText(ModelMetadata metadata)
protected virtual string GenerateDisplayText(ModelExplorer modelExplorer)
{
return metadata.SimpleDisplayText ?? string.Empty;
return modelExplorer.GetSimpleDisplayText() ?? string.Empty;
}
protected HtmlString GenerateDropDown(
ModelMetadata metadata,
ModelExplorer modelExplorer,
string expression,
IEnumerable<SelectListItem> selectList,
string optionLabel,
@ -619,7 +630,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
{
var tagBuilder = _htmlGenerator.GenerateSelect(
ViewContext,
metadata,
modelExplorer,
optionLabel,
expression: expression,
selectList: selectList,
@ -634,7 +645,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
}
protected virtual HtmlString GenerateEditor(
ModelMetadata metadata,
ModelExplorer modelExplorer,
string htmlFieldName,
string templateName,
object additionalViewData)
@ -643,7 +654,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
_viewEngine,
ViewContext,
ViewData,
metadata,
modelExplorer,
htmlFieldName,
templateName,
readOnly: false,
@ -742,7 +753,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
}
protected virtual HtmlString GenerateHidden(
ModelMetadata metadata,
ModelExplorer modelExplorer,
string expression,
object value,
bool useViewData,
@ -751,7 +762,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
var tagBuilder =
_htmlGenerator.GenerateHidden(
ViewContext,
metadata,
modelExplorer,
expression,
value,
useViewData,
@ -773,14 +784,14 @@ namespace Microsoft.AspNet.Mvc.Rendering
}
protected virtual HtmlString GenerateLabel(
[NotNull] ModelMetadata metadata,
[NotNull] ModelExplorer modelExplorer,
string expression,
string labelText,
object htmlAttributes)
{
var tagBuilder = _htmlGenerator.GenerateLabel(
ViewContext,
metadata,
modelExplorer,
expression: expression,
labelText: labelText,
htmlAttributes: htmlAttributes);
@ -793,14 +804,14 @@ namespace Microsoft.AspNet.Mvc.Rendering
}
protected HtmlString GenerateListBox(
ModelMetadata metadata,
ModelExplorer modelExplorer,
string expression,
IEnumerable<SelectListItem> selectList,
object htmlAttributes)
{
var tagBuilder = _htmlGenerator.GenerateSelect(
ViewContext,
metadata,
modelExplorer,
optionLabel: null,
expression: expression,
selectList: selectList,
@ -821,14 +832,14 @@ namespace Microsoft.AspNet.Mvc.Rendering
}
protected virtual HtmlString GeneratePassword(
ModelMetadata metadata,
ModelExplorer modelExplorer,
string expression,
object value,
object htmlAttributes)
{
var tagBuilder = _htmlGenerator.GeneratePassword(
ViewContext,
metadata,
modelExplorer,
expression,
value,
htmlAttributes);
@ -841,7 +852,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
}
protected virtual HtmlString GenerateRadioButton(
ModelMetadata metadata,
ModelExplorer modelExplorer,
string expression,
object value,
bool? isChecked,
@ -849,7 +860,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
{
var tagBuilder = _htmlGenerator.GenerateRadioButton(
ViewContext,
metadata,
modelExplorer,
expression,
value,
isChecked,
@ -863,7 +874,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
}
protected virtual HtmlString GenerateTextArea(
ModelMetadata metadata,
ModelExplorer modelExplorer,
string expression,
int rows,
int columns,
@ -871,7 +882,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
{
var tagBuilder = _htmlGenerator.GenerateTextArea(
ViewContext,
metadata,
modelExplorer,
expression,
rows,
columns,
@ -885,7 +896,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
}
protected virtual HtmlString GenerateTextBox(
ModelMetadata metadata,
ModelExplorer modelExplorer,
string expression,
object value,
string format,
@ -893,7 +904,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
{
var tagBuilder = _htmlGenerator.GenerateTextBox(
ViewContext,
metadata,
modelExplorer,
expression,
value,
format,
@ -962,9 +973,8 @@ namespace Microsoft.AspNet.Mvc.Rendering
{
if (string.IsNullOrEmpty(expression))
{
// case 2(a): format the value from ModelMetadata for the current model
var metadata = ViewData.ModelMetadata;
resolvedValue = FormatValue(metadata.Model, format);
// case 2(a): format the value from ViewData for the current model
resolvedValue = FormatValue(ViewData.Model, format);
}
else
{
@ -983,10 +993,10 @@ namespace Microsoft.AspNet.Mvc.Rendering
/// <inheritdoc />
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(
ModelMetadata metadata,
ModelExplorer modelExplorer,
string expression)
{
return _htmlGenerator.GetClientValidationRules(ViewContext, metadata, expression);
return _htmlGenerator.GetClientValidationRules(ViewContext, modelExplorer, expression);
}
}
}

View File

@ -56,8 +56,8 @@ namespace Microsoft.AspNet.Mvc.Rendering
[NotNull] Expression<Func<TModel, bool>> expression,
object htmlAttributes)
{
var metadata = GetModelMetadata(expression);
return GenerateCheckBox(metadata, GetExpressionName(expression), isChecked: null,
var modelExplorer = GetModelExplorer(expression);
return GenerateCheckBox(modelExplorer, GetExpressionName(expression), isChecked: null,
htmlAttributes: htmlAttributes);
}
@ -68,9 +68,9 @@ namespace Microsoft.AspNet.Mvc.Rendering
string optionLabel,
object htmlAttributes)
{
var metadata = ExpressionMetadataProvider.FromLambdaExpression(expression, ViewData, MetadataProvider);
var modelExplorer = ExpressionMetadataProvider.FromLambdaExpression(expression, ViewData, MetadataProvider);
return GenerateDropDown(metadata, ExpressionHelper.GetExpressionText(expression), selectList,
return GenerateDropDown(modelExplorer, ExpressionHelper.GetExpressionText(expression), selectList,
optionLabel, htmlAttributes);
}
@ -81,11 +81,11 @@ namespace Microsoft.AspNet.Mvc.Rendering
string htmlFieldName,
object additionalViewData)
{
var metadata = ExpressionMetadataProvider.FromLambdaExpression(expression,
var modelExplorer = ExpressionMetadataProvider.FromLambdaExpression(expression,
ViewData,
MetadataProvider);
return GenerateDisplay(metadata,
return GenerateDisplay(modelExplorer,
htmlFieldName ?? ExpressionHelper.GetExpressionText(expression),
templateName,
additionalViewData);
@ -94,32 +94,32 @@ namespace Microsoft.AspNet.Mvc.Rendering
/// <inheritdoc />
public string DisplayNameFor<TResult>([NotNull] Expression<Func<TModel, TResult>> expression)
{
var metadata = GetModelMetadata(expression);
return GenerateDisplayName(metadata, ExpressionHelper.GetExpressionText(expression));
var modelExplorer = GetModelExplorer(expression);
return GenerateDisplayName(modelExplorer, ExpressionHelper.GetExpressionText(expression));
}
/// <inheritdoc />
public string DisplayNameForInnerType<TModelItem, TResult>(
[NotNull] Expression<Func<TModelItem, TResult>> expression)
{
var metadata = ExpressionMetadataProvider.FromLambdaExpression<TModelItem, TResult>(
var modelExplorer = ExpressionMetadataProvider.FromLambdaExpression<TModelItem, TResult>(
expression,
new ViewDataDictionary<TModelItem>(ViewData, model: null),
MetadataProvider);
var expressionText = ExpressionHelper.GetExpressionText(expression);
if (metadata == null)
if (modelExplorer == null)
{
throw new InvalidOperationException(Resources.FormatHtmlHelper_NullModelMetadata(expressionText));
}
return GenerateDisplayName(metadata, expressionText);
return GenerateDisplayName(modelExplorer, expressionText);
}
/// <inheritdoc />
public string DisplayTextFor<TResult>([NotNull] Expression<Func<TModel, TResult>> expression)
{
return GenerateDisplayText(GetModelMetadata(expression));
return GenerateDisplayText(GetModelExplorer(expression));
}
/// <inheritdoc />
@ -129,10 +129,10 @@ namespace Microsoft.AspNet.Mvc.Rendering
string htmlFieldName,
object additionalViewData)
{
var metadata = ExpressionMetadataProvider.FromLambdaExpression(expression, ViewData, MetadataProvider);
var modelExplorer = ExpressionMetadataProvider.FromLambdaExpression(expression, ViewData, MetadataProvider);
return GenerateEditor(
metadata,
modelExplorer,
htmlFieldName ?? ExpressionHelper.GetExpressionText(expression),
templateName,
additionalViewData);
@ -143,7 +143,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
[NotNull] Expression<Func<TModel, TResult>> expression,
object htmlAttributes)
{
var metadata = GetModelMetadata(expression);
var metadata = GetModelExplorer(expression);
return GenerateHidden(metadata, GetExpressionName(expression), metadata.Model, useViewData: false,
htmlAttributes: htmlAttributes);
}
@ -160,7 +160,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
string labelText,
object htmlAttributes)
{
var metadata = GetModelMetadata(expression);
var metadata = GetModelExplorer(expression);
return GenerateLabel(metadata, ExpressionHelper.GetExpressionText(expression), labelText, htmlAttributes);
}
@ -170,7 +170,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
IEnumerable<SelectListItem> selectList,
object htmlAttributes)
{
var metadata = GetModelMetadata(expression);
var metadata = GetModelExplorer(expression);
var name = ExpressionHelper.GetExpressionText(expression);
return GenerateListBox(metadata, name, selectList, htmlAttributes);
@ -188,7 +188,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
[NotNull] Expression<Func<TModel, TResult>> expression,
object htmlAttributes)
{
var metadata = GetModelMetadata(expression);
var metadata = GetModelExplorer(expression);
return GeneratePassword(metadata, GetExpressionName(expression), value: null,
htmlAttributes: htmlAttributes);
}
@ -199,7 +199,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
[NotNull] object value,
object htmlAttributes)
{
var metadata = GetModelMetadata(expression);
var metadata = GetModelExplorer(expression);
return GenerateRadioButton(metadata, GetExpressionName(expression), value, isChecked: null,
htmlAttributes: htmlAttributes);
}
@ -211,7 +211,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
int columns,
object htmlAttributes)
{
var metadata = GetModelMetadata(expression);
var metadata = GetModelExplorer(expression);
return GenerateTextArea(metadata, GetExpressionName(expression), rows, columns, htmlAttributes);
}
@ -221,7 +221,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
string format,
object htmlAttributes)
{
var metadata = GetModelMetadata(expression);
var metadata = GetModelExplorer(expression);
return GenerateTextBox(metadata, GetExpressionName(expression), metadata.Model, format, htmlAttributes);
}
@ -230,16 +230,16 @@ namespace Microsoft.AspNet.Mvc.Rendering
return ExpressionHelper.GetExpressionText(expression);
}
protected ModelMetadata GetModelMetadata<TResult>([NotNull] Expression<Func<TModel, TResult>> expression)
protected ModelExplorer GetModelExplorer<TResult>([NotNull] Expression<Func<TModel, TResult>> expression)
{
var metadata = ExpressionMetadataProvider.FromLambdaExpression(expression, ViewData, MetadataProvider);
if (metadata == null)
var modelExplorer = ExpressionMetadataProvider.FromLambdaExpression(expression, ViewData, MetadataProvider);
if (modelExplorer == null)
{
var expressionName = GetExpressionName(expression);
throw new InvalidOperationException(Resources.FormatHtmlHelper_NullModelMetadata(expressionName));
}
return metadata;
return modelExplorer;
}
/// <inheritdoc />
@ -258,7 +258,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
/// <inheritdoc />
public string ValueFor<TResult>([NotNull] Expression<Func<TModel, TResult>> expression, string format)
{
var metadata = GetModelMetadata(expression);
var metadata = GetModelExplorer(expression);
return GenerateValue(ExpressionHelper.GetExpressionText(expression), metadata.Model, format,
useViewData: false);
}

View File

@ -32,9 +32,23 @@ namespace Microsoft.AspNet.Mvc.Rendering
TagBuilder GenerateAntiForgery([NotNull] ViewContext viewContext);
/// <summary>
/// Generate a &lt;input type="checkbox".../&gt; element.
/// </summary>
/// <param name="viewContext">The <see cref="ViewContext"/> instance for the current scope.</param>
/// <param name="modelExplorer">The <see cref="ModelExplorer"/> for the model.</param>
/// <param name="expression">The model expression.</param>
/// <param name="isChecked">The initial state of the checkbox element.</param>
/// <param name="htmlAttributes">
/// An <see cref="object"/> that contains the HTML attributes for the element. Alternatively, an
/// <see cref="IDictionary{string, object}"/> instance containing the HTML attributes.
/// </param>
/// <returns>
/// A <see cref="TagBuilder"/> instance for the &lt;input type="checkbox".../&gt; element.
/// </returns>
TagBuilder GenerateCheckBox(
[NotNull] ViewContext viewContext,
ModelMetadata metadata,
ModelExplorer modelExplorer,
string expression,
bool? isChecked,
object htmlAttributes);
@ -46,7 +60,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
/// </summary>
TagBuilder GenerateHiddenForCheckbox(
[NotNull] ViewContext viewContext,
ModelMetadata metadata,
ModelExplorer modelExplorer,
string expression);
/// <summary>
@ -107,7 +121,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
TagBuilder GenerateHidden(
[NotNull] ViewContext viewContext,
ModelMetadata metadata,
ModelExplorer modelExplorer,
string expression,
object value,
bool useViewData,
@ -115,21 +129,21 @@ namespace Microsoft.AspNet.Mvc.Rendering
TagBuilder GenerateLabel(
[NotNull] ViewContext viewContext,
[NotNull] ModelMetadata metadata,
[NotNull] ModelExplorer modelExplorer,
string expression,
string labelText,
object htmlAttributes);
TagBuilder GeneratePassword(
[NotNull] ViewContext viewContext,
ModelMetadata metadata,
ModelExplorer modelExplorer,
string expression,
object value,
object htmlAttributes);
TagBuilder GenerateRadioButton(
[NotNull] ViewContext viewContext,
ModelMetadata metadata,
ModelExplorer modelExplorer,
string expression,
object value,
bool? isChecked,
@ -146,7 +160,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
TagBuilder GenerateSelect(
[NotNull] ViewContext viewContext,
ModelMetadata metadata,
ModelExplorer modelExplorer,
string optionLabel,
string expression,
IEnumerable<SelectListItem> selectList,
@ -155,7 +169,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
TagBuilder GenerateSelect(
[NotNull] ViewContext viewContext,
ModelMetadata metadata,
ModelExplorer modelExplorer,
string optionLabel,
string expression,
IEnumerable<SelectListItem> selectList,
@ -165,7 +179,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
TagBuilder GenerateTextArea(
[NotNull] ViewContext viewContext,
ModelMetadata metadata,
ModelExplorer modelExplorer,
string expression,
int rows,
int columns,
@ -173,7 +187,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
TagBuilder GenerateTextBox(
[NotNull] ViewContext viewContext,
ModelMetadata metadata,
ModelExplorer modelExplorer,
string expression,
object value,
string format,
@ -199,7 +213,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
/// </remarks>
IEnumerable<ModelClientValidationRule> GetClientValidationRules(
[NotNull] ViewContext viewContext,
ModelMetadata metadata,
ModelExplorer modelExplorer,
string expression);
}
}
}

View File

@ -12,6 +12,8 @@ namespace Microsoft.AspNet.Mvc.Rendering
private IViewEngine _viewEngine;
private ViewContext _viewContext;
private ViewDataDictionary _viewData;
private ModelExplorer _modelExplorer;
private object _model;
private ModelMetadata _metadata;
private string _htmlFieldName;
private string _templateName;
@ -21,7 +23,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
public TemplateBuilder([NotNull] IViewEngine viewEngine,
[NotNull] ViewContext viewContext,
[NotNull] ViewDataDictionary viewData,
[NotNull] ModelMetadata metadata,
[NotNull] ModelExplorer modelExplorer,
string htmlFieldName,
string templateName,
bool readOnly,
@ -30,45 +32,51 @@ namespace Microsoft.AspNet.Mvc.Rendering
_viewEngine = viewEngine;
_viewContext = viewContext;
_viewData = viewData;
_metadata = metadata;
_modelExplorer = modelExplorer;
_htmlFieldName = htmlFieldName;
_templateName = templateName;
_readOnly = readOnly;
_additionalViewData = additionalViewData;
_model = modelExplorer.Model;
_metadata = modelExplorer.Metadata;
}
public string Build()
{
if (_metadata.ConvertEmptyStringToNull && string.Empty.Equals(_metadata.Model))
if (_metadata.ConvertEmptyStringToNull && string.Empty.Equals(_model))
{
_metadata.Model = null;
_model = null;
}
var formattedModelValue = _metadata.Model;
if (_metadata.Model == null && _readOnly)
var formattedModelValue = _model;
if (_model == null && _readOnly)
{
formattedModelValue = _metadata.NullDisplayText;
}
var formatString = _readOnly ? _metadata.DisplayFormatString : _metadata.EditFormatString;
if (_metadata.Model != null && !string.IsNullOrEmpty(formatString))
if (_model != null && !string.IsNullOrEmpty(formatString))
{
formattedModelValue = string.Format(CultureInfo.CurrentCulture, formatString, _metadata.Model);
formattedModelValue = string.Format(CultureInfo.CurrentCulture, formatString, _model);
}
// Normally this shouldn't happen, unless someone writes their own custom Object templates which
// don't check to make sure that the object hasn't already been displayed
if (_viewData.TemplateInfo.Visited(_metadata))
if (_viewData.TemplateInfo.Visited(_modelExplorer))
{
return string.Empty;
}
var viewData = new ViewDataDictionary(_viewData, model: null)
{
Model = _metadata.Model,
ModelMetadata = _metadata
};
// We need to copy the ModelExplorer to copy the model metadata. Otherwise we might
// lose track of the model type/property. Passing null here explicitly, because
// this might be a typed VDD, and the model value might not be compatible.
var viewData = new ViewDataDictionary(_viewData, model: null);
// We're setting ModelExplorer in order to preserve the model metadata of the original
// _viewData even though _model may be null.
viewData.ModelExplorer = _modelExplorer.GetExplorerForModel(_model);
viewData.TemplateInfo.FormattedModelValue = formattedModelValue;
viewData.TemplateInfo.HtmlFieldPrefix = _viewData.TemplateInfo.GetFullHtmlFieldName(_htmlFieldName);
@ -81,7 +89,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
}
}
var visitedObjectsKey = _metadata.Model ?? _metadata.RealModelType;
var visitedObjectsKey = _model ?? _modelExplorer.ModelType;
viewData.TemplateInfo.AddVisited(visitedObjectsKey);
var templateRenderer = new TemplateRenderer(_viewEngine, _viewContext, viewData, _templateName, _readOnly);

View File

@ -114,7 +114,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
}
throw new InvalidOperationException(
Resources.FormatTemplateHelpers_NoTemplate(_viewData.ModelMetadata.RealModelType.FullName));
Resources.FormatTemplateHelpers_NoTemplate(_viewData.ModelExplorer.ModelType.FullName));
}
private Dictionary<string, Func<IHtmlHelper, string>> GetDefaultActions()
@ -139,7 +139,8 @@ namespace Microsoft.AspNet.Mvc.Rendering
// We don't want to search for Nullable<T>, we want to search for T (which should handle both T and
// Nullable<T>).
var fieldType = Nullable.GetUnderlyingType(metadata.RealModelType) ?? metadata.RealModelType;
var modelType = _viewData.ModelExplorer.ModelType;
var fieldType = Nullable.GetUnderlyingType(modelType) ?? modelType;
yield return fieldType.Name;

View File

@ -352,7 +352,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
/// <paramref name="metadata"/> is <c>null</c>; ignored otherwise.
/// </param>
/// <returns>An <see cref="IEnumerable{ModelClientValidationRule}"/> containing the relevant rules.</returns>
IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, string expression);
IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelExplorer modelExplorer, string expression);
/// <summary>
/// Returns an &lt;input&gt; element of type "hidden" for the specified <paramref name="expression"/>.

View File

@ -1,8 +1,6 @@
// 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.Core;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.Framework.Internal;
@ -19,13 +17,13 @@ namespace Microsoft.AspNet.Mvc.Rendering
/// <param name="name">
/// String representation of the <see cref="System.Linq.Expressions.Expression"/> of interest.
/// </param>
/// <param name="metadata">
/// Metadata about the <see cref="System.Linq.Expressions.Expression"/> of interest.
/// <param name="modelExplorer">
/// Includes the model and metadata about the <see cref="System.Linq.Expressions.Expression"/> of interest.
/// </param>
public ModelExpression([NotNull] string name, [NotNull] ModelMetadata metadata)
public ModelExpression([NotNull] string name, [NotNull] ModelExplorer modelExplorer)
{
Name = name;
Metadata = metadata;
ModelExplorer = modelExplorer;
}
/// <summary>
@ -36,10 +34,36 @@ namespace Microsoft.AspNet.Mvc.Rendering
/// <summary>
/// Metadata about the <see cref="System.Linq.Expressions.Expression"/> of interest.
/// </summary>
public ModelMetadata Metadata
{
get
{
return ModelExplorer.Metadata;
}
}
/// <summary>
/// Gets the model object for the <see cref="System.Linq.Expressions.Expression"/> of interest.
/// </summary>
/// <remarks>
/// Getting <see cref="ModelMetadata.Model"/> will evaluate a compiled version of the original
/// Getting <see cref="Model"/> will evaluate a compiled version of the original
/// <see cref="System.Linq.Expressions.Expression"/>.
/// </remarks>
public ModelMetadata Metadata { get; private set; }
public object Model
{
get
{
return ModelExplorer.Model;
}
}
/// <summary>
/// Gets the model explorer for the <see cref="System.Linq.Expressions.Expression"/> of interest.
/// </summary>
/// <remarks>
/// Getting <see cref="ModelExplorer.Model"/> will evaluate a compiled version of the original
/// <see cref="System.Linq.Expressions.Expression"/>.
/// </remarks>
public ModelExplorer ModelExplorer { get; }
}
}

View File

@ -88,9 +88,9 @@ namespace Microsoft.AspNet.Mvc
}
}
public bool Visited(ModelMetadata metadata)
public bool Visited(ModelExplorer modelExplorer)
{
return _visitedObjects.Contains(metadata.Model ?? metadata.ModelType);
return _visitedObjects.Contains(modelExplorer.Model ?? modelExplorer.Metadata.ModelType);
}
}
}

View File

@ -18,9 +18,6 @@ namespace Microsoft.AspNet.Mvc
private readonly Type _declaredModelType;
private readonly IModelMetadataProvider _metadataProvider;
private object _model;
private ModelMetadata _modelMetadata;
/// <summary>
/// Initializes a new instance of the <see cref="ViewDataDictionary"/> class.
/// </summary>
@ -128,7 +125,7 @@ namespace Microsoft.AspNet.Mvc
templateInfo: new TemplateInfo())
{
// This is the core constructor called when Model is unknown. Base ModelMetadata on the declared type.
ModelMetadata = _metadataProvider.GetMetadataForType(() => null, _declaredModelType);
ModelExplorer = _metadataProvider.GetModelExplorerForType(declaredModelType, model: null);
}
/// <summary>
@ -188,16 +185,16 @@ namespace Microsoft.AspNet.Mvc
{
// This is the core constructor called when Model is known.
var modelType = GetModelType(model);
if (modelType == source.ModelMetadata.ModelType && model == source.ModelMetadata.Model)
if (modelType == source.ModelMetadata.ModelType && model == source.ModelExplorer.Model)
{
// Preserve any customizations made to source.ModelMetadata if the Type that will be calculated in
// SetModel() and source.Model match new instance's values.
ModelMetadata = source.ModelMetadata;
// Preserve any customizations made to source.ModelExplorer.ModelMetadata if the Type
// that will be calculated in SetModel() and source.Model match new instance's values.
ModelExplorer = source.ModelExplorer;
}
else if (model == null)
{
// Ensure ModelMetadata is never null though SetModel() isn't called.
ModelMetadata = _metadataProvider.GetMetadataForType(() => null, _declaredModelType);
// Ensure ModelMetadata is never null though SetModel() isn't called below.
ModelExplorer = _metadataProvider.GetModelExplorerForType(_declaredModelType, model: null);
}
// If we're constructing a ViewDataDictionary<TModel> where TModel is a non-Nullable value type,
@ -226,12 +223,11 @@ namespace Microsoft.AspNet.Mvc
{
get
{
return _model;
return ModelExplorer.Model;
}
set
{
// Reset ModelMetadata to ensure Model and ModelMetadata.Model remain equal.
_modelMetadata = null;
// Reset ModelExplorer to ensure Model and ModelMetadata.Model remain equal.
SetModel(value);
}
}
@ -250,21 +246,15 @@ namespace Microsoft.AspNet.Mvc
{
get
{
return _modelMetadata;
}
set
{
if (value == null)
{
throw new ArgumentNullException(Resources.FormatPropertyOfTypeCannotBeNull(
nameof(ViewDataDictionary.ModelMetadata),
nameof(ViewDataDictionary)));
}
_modelMetadata = value;
return ModelExplorer.Metadata;
}
}
/// <summary>
/// Gets or sets the <see cref="ModelExplorer"/> for the <see cref="Model"/>.
/// </summary>
public ModelExplorer ModelExplorer { get; set; }
public TemplateInfo TemplateInfo { get; }
#region IDictionary properties
@ -355,15 +345,24 @@ namespace Microsoft.AspNet.Mvc
protected virtual void SetModel(object value)
{
EnsureCompatible(value);
_model = value;
// Reset or override ModelMetadata based on runtime value type. Fall back to declared type if value is
// null. When called from the Model setter, ModelMetadata will (temporarily) be null. When called from
// a constructor, current ModelMetadata may already be set to preserve customizations made in parent scope.
var modelType = GetModelType(value);
if (ModelMetadata == null || ModelMetadata.ModelType != modelType)
if (ModelExplorer?.Metadata.ModelType != modelType)
{
ModelMetadata = _metadataProvider.GetMetadataForType(() => value, modelType);
ModelExplorer = _metadataProvider.GetModelExplorerForType(modelType, value);
}
else if (object.ReferenceEquals(value, Model))
{
// The metadata already matches, and the model is literally the same, nothing
// to do here. This will likely occur when using one of the copy constructors.
}
else
{
// The metadata matches, but it's a new value.
ModelExplorer = new ModelExplorer(_metadataProvider, ModelExplorer.Container, ModelMetadata, value);
}
}

View File

@ -44,14 +44,15 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var boundCollection = new List<TElement>();
var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
var elementMetadata = metadataProvider.GetMetadataForType(typeof(TElement));
var rawValueArray = RawValueToObjectArray(rawValue);
foreach (var rawValueElement in rawValueArray)
{
var innerModelMetadata =
bindingContext.OperationBindingContext.MetadataProvider.GetMetadataForType(null, typeof(TElement));
var innerBindingContext = new ModelBindingContext(bindingContext,
bindingContext.ModelName,
innerModelMetadata)
elementMetadata)
{
ValueProvider = new CompositeValueProvider
{
@ -97,13 +98,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
.Select(i => i.ToString(CultureInfo.InvariantCulture));
}
var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
var elementMetadata = metadataProvider.GetMetadataForType(typeof(TElement));
var boundCollection = new List<TElement>();
foreach (var indexName in indexNames)
{
var fullChildName = ModelBindingHelper.CreateIndexModelName(bindingContext.ModelName, indexName);
var childModelMetadata =
bindingContext.OperationBindingContext.MetadataProvider.GetMetadataForType(null, typeof(TElement));
var childBindingContext = new ModelBindingContext(bindingContext, fullChildName, childModelMetadata);
var childBindingContext = new ModelBindingContext(bindingContext, fullChildName, elementMetadata);
var didBind = false;
object boundValue = null;

View File

@ -19,7 +19,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
typeof(ComplexModelDto),
allowNullModel: false);
var dto = (ComplexModelDto)bindingContext.ModelMetadata.Model;
var dto = (ComplexModelDto)bindingContext.Model;
foreach (var propertyMetadata in dto.PropertyMetadata)
{
var propertyModelName = ModelBindingHelper.CreatePropertyModelName(

View File

@ -57,8 +57,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
if (modelBindingResult.IsModelSet)
{
bindingContext.ModelMetadata.Model = modelBindingResult.Model;
// Update the model state key if we are bound using an empty prefix and it is a complex type.
// This is needed as validation uses the model state key to log errors. The client validation expects
// the errors with property names rather than the full name.
@ -112,6 +110,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
var newBindingContext = new ModelBindingContext
{
Model = oldBindingContext.Model,
ModelMetadata = oldBindingContext.ModelMetadata,
ModelName = modelName,
ModelState = oldBindingContext.ModelState,

View File

@ -18,36 +18,40 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var keyResult = await TryBindStrongModel<TKey>(bindingContext, "Key");
var valueResult = await TryBindStrongModel<TValue>(bindingContext, "Value");
var model = bindingContext.ModelMetadata.Model;
var isModelSet = false;
if (keyResult.IsModelSet && valueResult.IsModelSet)
{
model = new KeyValuePair<TKey, TValue>(
var model = new KeyValuePair<TKey, TValue>(
ModelBindingHelper.CastOrDefault<TKey>(keyResult.Model),
ModelBindingHelper.CastOrDefault<TValue>(valueResult.Model));
isModelSet = true;
return new ModelBindingResult(model, bindingContext.ModelName, isModelSet: true);
}
else if (!keyResult.IsModelSet && valueResult.IsModelSet)
{
bindingContext.ModelState.TryAddModelError(keyResult.Key,
bindingContext.ModelState.TryAddModelError(
keyResult.Key,
Resources.KeyValuePair_BothKeyAndValueMustBePresent);
return new ModelBindingResult(model: null, key: bindingContext.ModelName, isModelSet: false);
}
else if (keyResult.IsModelSet && !valueResult.IsModelSet)
{
bindingContext.ModelState.TryAddModelError(valueResult.Key,
bindingContext.ModelState.TryAddModelError(
valueResult.Key,
Resources.KeyValuePair_BothKeyAndValueMustBePresent);
return new ModelBindingResult(model: null, key: bindingContext.ModelName, isModelSet: false);
}
else
{
return null;
}
var result = new ModelBindingResult(model, bindingContext.ModelName, isModelSet);
return (keyResult.IsModelSet || valueResult.IsModelSet) ? result : null;
}
internal async Task<ModelBindingResult> TryBindStrongModel<TModel>(ModelBindingContext parentBindingContext,
string propertyName)
{
var propertyModelMetadata =
parentBindingContext.OperationBindingContext.MetadataProvider.GetMetadataForType(modelAccessor: null,
modelType: typeof(TModel));
parentBindingContext.OperationBindingContext.MetadataProvider.GetMetadataForType(typeof(TModel));
var propertyModelName =
ModelBindingHelper.CreatePropertyModelName(parentBindingContext.ModelName, propertyName);
var propertyBindingContext =

View File

@ -38,8 +38,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// post-processing, e.g. property setters and hooking up validation
ProcessDto(bindingContext, (ComplexModelDto)result.Model);
return
new ModelBindingResult(bindingContext.ModelMetadata.Model, bindingContext.ModelName, isModelSet: true);
return new ModelBindingResult(
bindingContext.Model,
bindingContext.ModelName,
isModelSet: true);
}
protected virtual bool CanUpdateProperty(ModelMetadata propertyMetadata)
@ -236,18 +238,25 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
return true;
}
private async Task<ModelBindingResult> CreateAndPopulateDto(ModelBindingContext bindingContext,
IEnumerable<ModelMetadata> propertyMetadatas)
private async Task<ModelBindingResult> CreateAndPopulateDto(
ModelBindingContext bindingContext,
IEnumerable<ModelMetadata> propertyMetadatas)
{
// create a DTO and call into the DTO binder
var originalDto = new ComplexModelDto(bindingContext.ModelMetadata, propertyMetadatas);
var complexModelDtoMetadata =
bindingContext.OperationBindingContext.MetadataProvider.GetMetadataForType(() => originalDto,
typeof(ComplexModelDto));
var dtoBindingContext =
new ModelBindingContext(bindingContext, bindingContext.ModelName, complexModelDtoMetadata);
var dto = new ComplexModelDto(bindingContext.ModelMetadata, propertyMetadatas);
return await bindingContext.OperationBindingContext.ModelBinder.BindModelAsync(dtoBindingContext);
var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
var dtoMetadata = metadataProvider.GetMetadataForType(typeof(ComplexModelDto));
var childContext = new ModelBindingContext(
bindingContext,
bindingContext.ModelName,
dtoMetadata)
{
Model = dto,
};
return await bindingContext.OperationBindingContext.ModelBinder.BindModelAsync(childContext);
}
protected virtual object CreateModel(ModelBindingContext bindingContext)
@ -259,9 +268,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
protected virtual void EnsureModel(ModelBindingContext bindingContext)
{
if (bindingContext.ModelMetadata.Model == null)
if (bindingContext.Model == null)
{
bindingContext.ModelMetadata.Model = CreateModel(bindingContext);
bindingContext.Model = CreateModel(bindingContext);
}
}
@ -349,6 +358,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
internal void ProcessDto(ModelBindingContext bindingContext, ComplexModelDto dto)
{
var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
var modelExplorer = metadataProvider.GetModelExplorerForType(bindingContext.ModelType, bindingContext.Model);
var validationInfo = GetPropertyValidationInfo(bindingContext);
// Eliminate provided properties from requiredProperties; leaving just *missing* required properties.
@ -359,12 +371,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
var addedError = false;
// Update Model as SetProperty() would: Place null value where validator will check for non-null. This
// ensures a failure result from a required validator (if any) even for a non-nullable property.
// (Otherwise, propertyMetadata.Model is likely already null.)
var propertyMetadata = bindingContext.ModelMetadata.Properties[missingRequiredProperty];
propertyMetadata.Model = null;
var propertyName = propertyMetadata.BinderModelName ?? missingRequiredProperty;
// We want to provide the 'null' value, not the value of model.Property,
// so avoiding modelExplorer.GetProperty here which would call the actual getter on the
// model. This avoids issues with value types, or properties with pre-initialized values.
var propertyExplorer = modelExplorer.GetExplorerForProperty(missingRequiredProperty, model: null);
var propertyName = propertyExplorer.Metadata.BinderModelName ?? missingRequiredProperty;
var modelStateKey = ModelBindingHelper.CreatePropertyModelName(
bindingContext.ModelName,
propertyName);
@ -373,7 +385,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
IModelValidator validator;
if (validationInfo.RequiredValidators.TryGetValue(missingRequiredProperty, out validator))
{
addedError = RunValidator(validator, bindingContext, propertyMetadata, modelStateKey);
addedError = RunValidator(validator, bindingContext, propertyExplorer, modelStateKey);
}
// Fall back to default message if BindingBehaviorAttribute required this property or validator
@ -389,26 +401,31 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// for each property that was bound, call the setter, recording exceptions as necessary
foreach (var entry in dto.Results)
{
var propertyMetadata = entry.Key;
var dtoResult = entry.Value;
if (dtoResult != null)
{
var propertyMetadata = entry.Key;
IModelValidator requiredValidator;
validationInfo.RequiredValidators.TryGetValue(propertyMetadata.PropertyName,
out requiredValidator);
SetProperty(bindingContext, propertyMetadata, dtoResult, requiredValidator);
validationInfo.RequiredValidators.TryGetValue(
propertyMetadata.PropertyName,
out requiredValidator);
SetProperty(bindingContext, modelExplorer, propertyMetadata, dtoResult, requiredValidator);
}
}
}
protected virtual void SetProperty(ModelBindingContext bindingContext,
ModelMetadata propertyMetadata,
ModelBindingResult dtoResult,
IModelValidator requiredValidator)
protected virtual void SetProperty(
ModelBindingContext bindingContext,
ModelExplorer modelExplorer,
ModelMetadata propertyMetadata,
ModelBindingResult dtoResult,
IModelValidator requiredValidator)
{
var bindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase;
var property = bindingContext.ModelType
.GetProperty(propertyMetadata.PropertyName, bindingFlags);
var property = bindingContext.ModelType.GetProperty(
propertyMetadata.PropertyName,
bindingFlags);
if (property == null || !property.CanWrite)
{
@ -427,8 +444,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
hasDefaultValue = TryGetPropertyDefaultValue(property, out value);
}
propertyMetadata.Model = value;
// 'Required' validators need to run first so that we can provide useful error messages if
// the property setters throw, e.g. if we're setting entity keys to null.
if (value == null)
@ -439,7 +454,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
if (requiredValidator != null)
{
var validationContext = new ModelValidationContext(bindingContext, propertyMetadata);
var propertyExplorer = modelExplorer.GetExplorerForExpression(propertyMetadata, model: null);
var validationContext = new ModelValidationContext(bindingContext, propertyExplorer);
foreach (var validationResult in requiredValidator.Validate(validationContext))
{
bindingContext.ModelState.TryAddModelError(modelStateKey, validationResult.Message);
@ -459,7 +475,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
try
{
property.SetValue(bindingContext.ModelMetadata.Model, value);
property.SetValue(bindingContext.Model, value);
}
catch (Exception ex)
{
@ -492,12 +508,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
}
// Returns true if validator execution adds a model error.
private static bool RunValidator(IModelValidator validator,
ModelBindingContext bindingContext,
ModelMetadata propertyMetadata,
string modelStateKey)
private static bool RunValidator(
IModelValidator validator,
ModelBindingContext bindingContext,
ModelExplorer propertyExplorer,
string modelStateKey)
{
var validationContext = new ModelValidationContext(bindingContext, propertyMetadata);
var validationContext = new ModelValidationContext(bindingContext, propertyExplorer);
var addedError = false;
foreach (var validationResult in validator.Validate(validationContext))

View File

@ -9,7 +9,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public interface IObjectModelValidator
{
/// <summary>
/// Validates the given model in <see cref="ModelValidationContext.ModelMetadata"/>.
/// Validates the given model in <see cref="ModelValidationContext.ModelExplorer"/>.
/// </summary>
/// <param name="validationContext">The <see cref="ModelValidationContext"/> associated with the current call.
/// </param>

View File

@ -81,17 +81,17 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Internal
throw new ArgumentException(message, "bindingContext");
}
if (!allowNullModel && bindingContext.ModelMetadata.Model == null)
if (!allowNullModel && bindingContext.Model == null)
{
var message = Resources.FormatModelBinderUtil_ModelCannotBeNull(requiredType);
throw new ArgumentException(message, "bindingContext");
}
if (bindingContext.ModelMetadata.Model != null &&
if (bindingContext.Model != null &&
!bindingContext.ModelType.GetTypeInfo().IsAssignableFrom(requiredType.GetTypeInfo()))
{
var message = Resources.FormatModelBinderUtil_ModelInstanceIsWrong(
bindingContext.ModelMetadata.Model.GetType(),
bindingContext.Model.GetType(),
requiredType);
throw new ArgumentException(message, "bindingContext");
}

View File

@ -19,13 +19,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
private readonly ConcurrentDictionary<Type, Dictionary<string, PropertyInformation>> _typePropertyInfoCache =
new ConcurrentDictionary<Type, Dictionary<string, PropertyInformation>>();
public IEnumerable<ModelMetadata> GetMetadataForProperties(object container, [NotNull] Type containerType)
public IEnumerable<ModelMetadata> GetMetadataForProperties([NotNull] Type containerType)
{
return GetMetadataForPropertiesCore(container, containerType);
return GetMetadataForPropertiesCore(containerType);
}
public ModelMetadata GetMetadataForProperty(Func<object> modelAccessor,
[NotNull] Type containerType,
public ModelMetadata GetMetadataForProperty([NotNull] Type containerType,
[NotNull] string propertyName)
{
if (string.IsNullOrEmpty(propertyName))
@ -42,17 +41,16 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
throw new ArgumentException(message, nameof(propertyName));
}
return CreatePropertyMetadata(modelAccessor, propertyInfo);
return CreatePropertyMetadata(propertyInfo);
}
public ModelMetadata GetMetadataForType(Func<object> modelAccessor, [NotNull] Type modelType)
public ModelMetadata GetMetadataForType([NotNull] Type modelType)
{
var prototype = GetTypeInformation(modelType);
return CreateMetadataFromPrototype(prototype, modelAccessor);
return CreateMetadataFromPrototype(prototype);
}
public ModelMetadata GetMetadataForParameter(
Func<object> modelAccessor,
[NotNull] MethodInfo methodInfo,
[NotNull] string parameterName)
{
@ -69,7 +67,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
throw new ArgumentException(message, nameof(parameterName));
}
return GetMetadataForParameterCore(modelAccessor, parameterName, parameter);
return GetMetadataForParameterCore(parameterName, parameter);
}
// Override for creating the prototype metadata (without the model accessor).
@ -104,47 +102,36 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
/// <returns>
/// A new <typeparamref name="TModelMetadata"/> instance based on <paramref name="prototype"/>.
/// </returns>
protected abstract TModelMetadata CreateMetadataFromPrototype(TModelMetadata prototype,
Func<object> modelAccessor);
protected abstract TModelMetadata CreateMetadataFromPrototype(TModelMetadata prototype);
private ModelMetadata GetMetadataForParameterCore(Func<object> modelAccessor,
string parameterName,
ParameterInfo parameter)
private ModelMetadata GetMetadataForParameterCore(
string parameterName,
ParameterInfo parameter)
{
var parameterInfo =
CreateParameterInfo(parameter.ParameterType,
ModelAttributes.GetAttributesForParameter(parameter),
parameterName);
var metadata = CreateMetadataFromPrototype(parameterInfo.Prototype, modelAccessor);
var metadata = CreateMetadataFromPrototype(parameterInfo.Prototype);
return metadata;
}
private IEnumerable<ModelMetadata> GetMetadataForPropertiesCore(object container, Type containerType)
private IEnumerable<ModelMetadata> GetMetadataForPropertiesCore(Type containerType)
{
var typePropertyInfo = GetTypePropertyInformation(containerType);
foreach (var kvp in typePropertyInfo)
{
var propertyInfo = kvp.Value;
Func<object> modelAccessor = null;
if (container != null)
{
modelAccessor = () => propertyInfo.PropertyHelper.GetValue(container);
}
var propertyMetadata = CreatePropertyMetadata(modelAccessor, propertyInfo);
if (propertyMetadata != null)
{
propertyMetadata.Container = container;
}
var propertyMetadata = CreatePropertyMetadata(propertyInfo);
yield return propertyMetadata;
}
}
private TModelMetadata CreatePropertyMetadata(Func<object> modelAccessor, PropertyInformation propertyInfo)
private TModelMetadata CreatePropertyMetadata(PropertyInformation propertyInfo)
{
var metadata = CreateMetadataFromPrototype(propertyInfo.Prototype, modelAccessor);
var metadata = CreateMetadataFromPrototype(propertyInfo.Prototype);
if (propertyInfo.IsReadOnly)
{
metadata.IsReadOnly = true;

View File

@ -16,9 +16,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
private static readonly string HtmlName = DataType.Html.ToString();
private bool _isEditFormatStringFromCache;
public CachedDataAnnotationsModelMetadata(CachedDataAnnotationsModelMetadata prototype,
Func<object> modelAccessor)
: base(prototype, modelAccessor)
public CachedDataAnnotationsModelMetadata(CachedDataAnnotationsModelMetadata prototype)
: base(prototype)
{
}
@ -299,24 +298,19 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
return result ?? base.ComputeOrder();
}
protected override string ComputeSimpleDisplayText()
/// <inheritdoc />
protected override string ComputeSimpleDisplayProperty()
{
if (Model != null &&
PrototypeCache.DisplayColumn != null &&
!string.IsNullOrEmpty(PrototypeCache.DisplayColumn.DisplayColumn))
if (!string.IsNullOrEmpty(PrototypeCache.DisplayColumn?.DisplayColumn))
{
var displayColumnProperty = ModelType.GetTypeInfo().GetDeclaredProperty(
PrototypeCache.DisplayColumn.DisplayColumn);
ValidateDisplayColumnAttribute(PrototypeCache.DisplayColumn, displayColumnProperty, ModelType);
var simpleDisplayTextValue = displayColumnProperty.GetValue(Model, null);
if (simpleDisplayTextValue != null)
{
return simpleDisplayTextValue.ToString();
}
return displayColumnProperty.Name;
}
return base.ComputeSimpleDisplayText();
return base.ComputeSimpleDisplayProperty();
}
protected override bool ComputeShowForDisplay()

View File

@ -33,6 +33,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
private int _order;
private bool _showForDisplay;
private bool _showForEdit;
private string _simpleDisplayProperty;
private string _templateHint;
private IBinderMetadata _binderMetadata;
private string _binderModelName;
@ -56,6 +57,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
private bool _orderComputed;
private bool _showForDisplayComputed;
private bool _showForEditComputed;
private bool _simpleDisplayPropertyComputed;
private bool _templateHintComputed;
private bool _isBinderMetadataComputed;
private bool _isBinderModelNameComputed;
@ -63,10 +65,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
private bool _propertyBindingPredicateProviderComputed;
// Constructor for creating real instances of the metadata class based on a prototype
protected CachedModelMetadata(CachedModelMetadata<TPrototypeCache> prototype, Func<object> modelAccessor)
protected CachedModelMetadata(CachedModelMetadata<TPrototypeCache> prototype)
: base(prototype.Provider,
prototype.ContainerType,
modelAccessor,
prototype.ModelType,
prototype.PropertyName)
{
@ -82,7 +83,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
Type modelType,
string propertyName,
TPrototypeCache prototypeCache)
: base(provider, containerType, modelAccessor: null, modelType: modelType, propertyName: propertyName)
: base(provider, containerType, modelType: modelType, propertyName: propertyName)
{
PrototypeCache = prototypeCache;
}
@ -500,17 +501,21 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
}
/// <inheritdoc />
public sealed override string SimpleDisplayText
public sealed override string SimpleDisplayProperty
{
get
{
// Value already cached in ModelMetadata. That class also already exposes ComputeSimpleDisplayText()
// for overrides. Sealed here for consistency with other properties.
return base.SimpleDisplayText;
if (!_simpleDisplayPropertyComputed)
{
_simpleDisplayProperty = ComputeSimpleDisplayProperty();
_simpleDisplayPropertyComputed = true;
}
return _simpleDisplayProperty;
}
set
{
base.SimpleDisplayText = value;
_simpleDisplayProperty = value;
_simpleDisplayPropertyComputed = true;
}
}
@ -701,6 +706,15 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
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>

View File

@ -28,10 +28,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
/// Copies only a few values from the <paramref name="prototype"/>. Unlikely the rest have been computed.
/// </remarks>
protected override CachedDataAnnotationsModelMetadata CreateMetadataFromPrototype(
CachedDataAnnotationsModelMetadata prototype,
Func<object> modelAccessor)
CachedDataAnnotationsModelMetadata prototype)
{
var metadata = new CachedDataAnnotationsModelMetadata(prototype, modelAccessor);
var metadata = new CachedDataAnnotationsModelMetadata(prototype);
foreach (var keyValuePair in prototype.AdditionalValues)
{
metadata.AdditionalValues.Add(keyValuePair);

View File

@ -26,7 +26,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
return new ModelMetadata(
this,
containerType,
modelAccessor: null,
modelType: modelType,
propertyName: propertyName);
}
@ -36,13 +35,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
/// 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,
Func<object> modelAccessor)
protected override ModelMetadata CreateMetadataFromPrototype([NotNull] ModelMetadata prototype)
{
var metadata = new ModelMetadata(
this,
prototype.ContainerType,
modelAccessor,
prototype.ModelType,
prototype.PropertyName);
foreach (var keyValuePair in prototype.AdditionalValues)

View File

@ -10,18 +10,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
public interface IModelMetadataProvider
{
IEnumerable<ModelMetadata> GetMetadataForProperties(object container, [NotNull] Type containerType);
IEnumerable<ModelMetadata> GetMetadataForProperties([NotNull] Type containerType);
ModelMetadata GetMetadataForProperty(
Func<object> modelAccessor,
[NotNull] Type containerType,
[NotNull] string propertyName);
ModelMetadata GetMetadataForType([NotNull] Type modelType);
ModelMetadata GetMetadataForType(Func<object> modelAccessor, [NotNull] Type modelType);
ModelMetadata GetMetadataForParameter(
Func<object> modelAccessor,
[NotNull] MethodInfo methodInfo,
[NotNull] string parameterName);
ModelMetadata GetMetadataForParameter([NotNull] MethodInfo methodInfo, [NotNull] string parameterName);
}
}

View File

@ -0,0 +1,392 @@
// 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.Diagnostics;
using System.Linq;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ModelBinding
{
/// <summary>
/// Associates a model object with it's corresponding <see cref="ModelMetadata"/>.
/// </summary>
[DebuggerDisplay("DeclaredType={Metadata.ModelType.Name} PropertyName={Metadata.PropertyName}")]
public class ModelExplorer
{
private readonly IModelMetadataProvider _metadataProvider;
private object _model;
private Func<object, object> _modelAccessor;
private Type _modelType;
private List<ModelExplorer> _properties;
/// <summary>
/// Creates a new <see cref="ModelExplorer"/>.
/// </summary>
/// <param name="metadataProvider">The <see cref="IModelMetadataProvider"/>.</param>
/// <param name="metadata">The <see cref="ModelMetadata"/>.</param>
/// <param name="model">The model object. May be <c>null</c>.</param>
public ModelExplorer(
[NotNull] IModelMetadataProvider metadataProvider,
[NotNull] ModelMetadata metadata,
object model)
{
_metadataProvider = metadataProvider;
Metadata = metadata;
_model = model;
}
/// <summary>
/// Creates a new <see cref="ModelExplorer"/>.
/// </summary>
/// <param name="metadataProvider">The <see cref="IModelMetadataProvider"/>.</param>
/// <param name="container">The container <see cref="ModelExplorer"/>.</param>
/// <param name="metadata">The <see cref="ModelMetadata"/>.</param>
/// <param name="modelAccessor">A model accessor function. May be <c>null</c>.</param>
public ModelExplorer(
[NotNull] IModelMetadataProvider metadataProvider,
[NotNull] ModelExplorer container,
[NotNull] ModelMetadata metadata,
Func<object, object> modelAccessor)
{
_metadataProvider = metadataProvider;
Container = container;
Metadata = metadata;
_modelAccessor = modelAccessor;
}
/// <summary>
/// Creates a new <see cref="ModelExplorer"/>.
/// </summary>
/// <param name="metadataProvider">The <see cref="IModelMetadataProvider"/>.</param>
/// <param name="container">The container <see cref="ModelExplorer"/>.</param>
/// <param name="metadata">The <see cref="ModelMetadata"/>.</param>
/// <param name="model">The model object. May be <c>null</c>.</param>
public ModelExplorer(
[NotNull] IModelMetadataProvider metadataProvider,
[NotNull] ModelExplorer container,
[NotNull] ModelMetadata metadata,
object model)
{
_metadataProvider = metadataProvider;
Container = container;
Metadata = metadata;
_model = model;
}
/// <summary>
/// Gets the container <see cref="ModelExplorer"/>.
/// </summary>
/// <remarks>
/// <para>
/// The <see cref="Container"/> will most commonly be set as a result of calling
/// <see cref="GetExplorerForProperty(string)"/>. In this case, the returned <see cref="ModelExplorer"/> will
/// have it's <see cref="Container"/> set to the instance upon which <see cref="GetExplorerForProperty(string)"/>
/// was called.
/// </para>
/// <para>
/// This however is not a requirement. The <see cref="Container"/> is informational, and may not
/// represent a type that defines the property represented by <see cref="Metadata"/>. This can
/// occur when constructing a <see cref="ModelExplorer"/> based on evaluation of a complex
/// expression.
/// </para>
/// <para>
/// If calling code relies on a parent-child relationship between <see cref="ModelExplorer"/>
/// instances, then use <see cref="ModelMetadata.ContainerType"/> to validate this assumption.
/// </para>
/// </remarks>
public ModelExplorer Container { get; }
/// <summary>
/// Gets the <see cref="ModelMetadata"/>.
/// </summary>
public ModelMetadata Metadata { get; }
/// <summary>
/// Gets the model object.
/// </summary>
/// <remarks>
/// Retrieving the <see cref="Model"/> object will execute the model accessor function if this
/// <see cref="ModelExplorer"/> was provided with one.
/// </remarks>
public object Model
{
get
{
if (_model == null && _modelAccessor != null)
{
Debug.Assert(Container != null);
_model = _modelAccessor(Container.Model);
// Null-out the accessor so we don't invoke it repeatedly if it returns null.
_modelAccessor = null;
}
return _model;
}
}
/// <remarks>
/// Retrieving the <see cref="ModelType"/> will execute the model accessor function if this
/// <see cref="ModelExplorer"/> was provided with one.
/// </remarks>
public Type ModelType
{
get
{
if (_modelType == null)
{
if (Model == null)
{
// If the model is null, then use the declared model type;
_modelType = Metadata.ModelType;
}
else if (Metadata.IsNullableValueType)
{
// We have a model, but if it's a nullable value type, then Model.GetType() will return
// the non-nullable type (int? -> int). Since it's a value type, there's no subclassing,
// just go with the declared type.
_modelType = Metadata.ModelType;
}
else
{
// We have a model, and it's not a nullable so use the runtime type to handle
// cases where the model is a subclass of the declared type and has extra data.
_modelType = Model.GetType();
}
}
return _modelType;
}
}
/// <summary>
/// Gets the properties.
/// </summary>
/// <remarks>
/// Includes a <see cref="ModelExplorer"/> for each property of the <see cref="ModelMetadata"/>
/// for <see cref="ModelType"/>.
/// </remarks>
public IEnumerable<ModelExplorer> Properties
{
get
{
if (_properties == null)
{
_properties = new List<ModelExplorer>();
var metadata = GetMetadataForRuntimeType();
var properties = Enumerable.Join(
metadata.Properties,
PropertyHelper.GetProperties(ModelType),
m => m.PropertyName,
ph => ph.Property.Name,
(m, ph) => CreateExplorerForProperty(m, ph));
_properties.AddRange(properties);
}
return _properties;
}
}
/// <summary>
/// Gets a <see cref="ModelExplorer"/> for the given <paramref name="model"/> value.
/// </summary>
/// <param name="model">The model value.</param>
/// <returns>A <see cref="ModelExplorer"/>.</returns>
public ModelExplorer GetExplorerForModel(object model)
{
if (Container == null)
{
return new ModelExplorer(_metadataProvider, Metadata, model);
}
else
{
return new ModelExplorer(_metadataProvider, Container, Metadata, model);
}
}
/// <summary>
/// Gets a <see cref="ModelExplorer"/> for the property with given <paramref name="name"/>, or <c>null</c> if
/// the property cannot be found.
/// </summary>
/// <param name="name">The property name.</param>
/// <returns>A <see cref="ModelExplorer"/>, or <c>null</c>.</returns>
public ModelExplorer GetExplorerForProperty([NotNull] string name)
{
return Properties.FirstOrDefault(p => string.Equals(
p.Metadata.PropertyName,
name,
StringComparison.Ordinal));
}
/// <summary>
/// Gets a <see cref="ModelExplorer"/> for the property with given <paramref name="name"/>, or <c>null</c> if
/// the property cannot be found.
/// </summary>
/// <param name="name">The property name.</param>
/// <param name="modelAccessor">An accessor for the model value.</param>
/// <returns>A <see cref="ModelExplorer"/>, or <c>null</c>.</returns>
/// <remarks>
/// As this creates a model explorer with a specific model accessor function, the result is not cached.
/// </remarks>
public ModelExplorer GetExplorerForProperty([NotNull] string name, Func<object, object> modelAccessor)
{
var metadata = GetMetadataForRuntimeType();
var propertyMetadata = metadata.Properties[name];
if (propertyMetadata == null)
{
return null;
}
return new ModelExplorer(_metadataProvider, this, propertyMetadata, modelAccessor);
}
/// <summary>
/// Gets a <see cref="ModelExplorer"/> for the property with given <paramref name="name"/>, or <c>null</c> if
/// the property cannot be found.
/// </summary>
/// <param name="name">The property name.</param>
/// <param name="model">The model value.</param>
/// <returns>A <see cref="ModelExplorer"/>, or <c>null</c>.</returns>
/// <remarks>
/// As this creates a model explorer with a specific model value, the result is not cached.
/// </remarks>
public ModelExplorer GetExplorerForProperty([NotNull] string name, object model)
{
var metadata = GetMetadataForRuntimeType();
var propertyMetadata = metadata.Properties[name];
if (propertyMetadata == null)
{
return null;
}
return new ModelExplorer(_metadataProvider, this, propertyMetadata, model);
}
/// <summary>
/// Gets a <see cref="ModelExplorer"/> for the provided model value and model <see cref="Type"/>.
/// </summary>
/// <param name="modelType">The model <see cref="Type"/>.</param>
/// <param name="model">The model value.</param>
/// <returns>A <see cref="ModelExplorer"/>.</returns>
/// <remarks>
/// <para>
/// A <see cref="ModelExplorer"/> created by <see cref="GetExplorerForExpression(Type, object)"/>
/// represents the result of executing an arbitrary expression against the model contained
/// in the current <see cref="ModelExplorer"/> instance.
/// </para>
/// <para>
/// The returned <see cref="ModelExplorer"/> will have the current instance set as its <see cref="Container"/>.
/// </para>
/// </remarks>
public ModelExplorer GetExplorerForExpression([NotNull] Type modelType, object model)
{
var metadata = _metadataProvider.GetMetadataForType(modelType);
return GetExplorerForExpression(metadata, model);
}
/// <summary>
/// Gets a <see cref="ModelExplorer"/> for the provided model value and model <see cref="Type"/>.
/// </summary>
/// <param name="modelType">The model <see cref="Type"/>.</param>
/// <param name="model">The model value.</param>
/// <returns>A <see cref="ModelExplorer"/>.</returns>
/// <remarks>
/// <para>
/// A <see cref="ModelExplorer"/> created by
/// <see cref="GetExplorerForExpression(ModelMetadata, object)"/>
/// represents the result of executing an arbitrary expression against the model contained
/// in the current <see cref="ModelExplorer"/> instance.
/// </para>
/// <para>
/// The returned <see cref="ModelExplorer"/> will have the current instance set as its <see cref="Container"/>.
/// </para>
/// </remarks>
public ModelExplorer GetExplorerForExpression([NotNull] ModelMetadata metadata, object model)
{
return new ModelExplorer(_metadataProvider, this, metadata, model);
}
/// <summary>
/// Gets a <see cref="ModelExplorer"/> for the provided model value and model <see cref="Type"/>.
/// </summary>
/// <param name="modelType">The model <see cref="Type"/>.</param>
/// <param name="model">The model value.</param>
/// <returns>A <see cref="ModelExplorer"/>.</returns>
/// <remarks>
/// <para>
/// A <see cref="ModelExplorer"/> created by
/// <see cref="GetExplorerForExpression(Type, Func{object, object})"/>
/// represents the result of executing an arbitrary expression against the model contained
/// in the current <see cref="ModelExplorer"/> instance.
/// </para>
/// <para>
/// The returned <see cref="ModelExplorer"/> will have the current instance set as its <see cref="Container"/>.
/// </para>
/// </remarks>
public ModelExplorer GetExplorerForExpression([NotNull] Type modelType, Func<object, object> modelAccessor)
{
var metadata = _metadataProvider.GetMetadataForType(modelType);
return GetExplorerForExpression(metadata, modelAccessor);
}
/// <summary>
/// Gets a <see cref="ModelExplorer"/> for the provided model value and model <see cref="Type"/>.
/// </summary>
/// <param name="modelType">The model <see cref="Type"/>.</param>
/// <param name="model">The model value.</param>
/// <returns>A <see cref="ModelExplorer"/>.</returns>
/// <remarks>
/// <para>
/// A <see cref="ModelExplorer"/> created by
/// <see cref="GetExplorerForExpression(ModelMetadata, Func{object, object})"/>
/// represents the result of executing an arbitrary expression against the model contained
/// in the current <see cref="ModelExplorer"/> instance.
/// </para>
/// <para>
/// The returned <see cref="ModelExplorer"/> will have the current instance set as its <see cref="Container"/>.
/// </para>
/// </remarks>
public ModelExplorer GetExplorerForExpression([NotNull] ModelMetadata metadata, Func<object, object> modelAccessor)
{
return new ModelExplorer(_metadataProvider, this, metadata, modelAccessor);
}
private ModelMetadata GetMetadataForRuntimeType()
{
// We want to make sure we're looking at the runtime properties of the model, and for
// that we need the model metadata of the runtime type.
var metadata = Metadata;
if (Metadata.ModelType != ModelType)
{
metadata = _metadataProvider.GetMetadataForType(ModelType);
}
return metadata;
}
private ModelExplorer CreateExplorerForProperty(
ModelMetadata propertyMetadata,
PropertyHelper propertyHelper)
{
if (propertyHelper == null)
{
return new ModelExplorer(_metadataProvider, this, propertyMetadata, modelAccessor: null);
}
var modelAccessor = new Func<object, object>((c) =>
{
return c == null ? null : propertyHelper.GetValue(c);
});
return new ModelExplorer(_metadataProvider, this, propertyMetadata, modelAccessor);
}
}
}

View File

@ -0,0 +1,64 @@
// 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.Globalization;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ModelBinding
{
/// <summary>
/// Extension methods for <see cref="ModelExplorer"/>.
/// </summary>
public static class ModelExplorerExtensions
{
/// <summary>
/// Gets a simple display string for the <see cref="ModelExplorer.Model"/> property
/// of <paramref name="modelExplorer"/>.
/// </summary>
/// <param name="modelExplorer">The <see cref="ModelExplorer"/>.</param>
/// <returns>A simple display string for the model.</returns>
public static string GetSimpleDisplayText([NotNull] this ModelExplorer modelExplorer)
{
if (modelExplorer.Metadata.SimpleDisplayProperty != null)
{
var propertyExplorer = modelExplorer.GetExplorerForProperty(
modelExplorer.Metadata.SimpleDisplayProperty);
if (propertyExplorer?.Model != null)
{
return propertyExplorer.Model.ToString();
}
}
if (modelExplorer.Model == null)
{
return modelExplorer.Metadata.NullDisplayText;
}
var stringResult = Convert.ToString(modelExplorer.Model, CultureInfo.CurrentCulture);
if (stringResult == null)
{
return string.Empty;
}
if (!stringResult.Equals(modelExplorer.Model.GetType().FullName, StringComparison.Ordinal))
{
return stringResult;
}
var firstProperty = modelExplorer.Properties.FirstOrDefault();
if (firstProperty == null)
{
return string.Empty;
}
if (firstProperty.Model == null)
{
return firstProperty.Metadata.NullDisplayText;
}
return Convert.ToString(firstProperty.Model, CultureInfo.CurrentCulture);
}
}
}

View File

@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Microsoft.AspNet.Mvc.ModelBinding.Internal;
using Microsoft.Framework.Internal;
@ -25,24 +24,18 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
private bool _showForDisplay = true;
private bool _showForEdit = true;
private object _model;
private Func<object> _modelAccessor;
private int _order = DefaultOrder;
private bool _isRequired;
private ModelPropertyCollection _properties;
private Type _realModelType;
private string _simpleDisplayText;
public ModelMetadata([NotNull] IModelMetadataProvider provider,
Type containerType,
Func<object> modelAccessor,
[NotNull] Type modelType,
string propertyName)
{
Provider = provider;
_containerType = containerType;
_modelAccessor = modelAccessor;
_modelType = modelType;
_propertyName = propertyName;
_isRequired = !modelType.AllowsNullValue();
@ -70,12 +63,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
/// </summary>
public virtual IBinderMetadata BinderMetadata { get; set; }
/// <summary>
/// A reference to the model's container <see cref="object"/>.
/// Will be non-<c>null</c> if the model represents a property.
/// </summary>
public object Container { get; set; }
public Type ContainerType
{
get { return _containerType; }
@ -88,7 +75,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
}
/// <summary>
/// Gets or sets the name of the <see cref="Model"/>'s datatype. Overrides <see cref="ModelType"/> in some
/// 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>
@ -98,7 +85,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
/// <summary>
/// Gets or sets the composite format <see cref="string"/> (see
/// http://msdn.microsoft.com/en-us/library/txafckwd.aspx) used to display the <see cref="Model"/>.
/// http://msdn.microsoft.com/en-us/library/txafckwd.aspx) used to display the Model.
/// </summary>
public virtual string DisplayFormatString { get; set; }
@ -106,7 +93,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
/// <summary>
/// Gets or sets the composite format <see cref="string"/> (see
/// http://msdn.microsoft.com/en-us/library/txafckwd.aspx) used to edit the <see cref="Model"/>.
/// 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,
@ -195,26 +182,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
set { _order = value; }
}
public object Model
{
get
{
if (_modelAccessor != null)
{
_model = _modelAccessor();
_modelAccessor = null;
}
return _model;
}
set
{
_model = value;
_modelAccessor = null;
_properties = null;
_realModelType = null;
}
}
public Type ModelType
{
get { return _modelType; }
@ -231,7 +198,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
if (_properties == null)
{
var properties = Provider.GetMetadataForProperties(Model, RealModelType);
var properties = Provider.GetMetadataForProperties(ModelType);
_properties = new ModelPropertyCollection(properties.OrderBy(m => m.Order));
}
@ -250,46 +217,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
get { return _propertyName; }
}
protected IModelMetadataProvider Provider { get; set; }
protected IModelMetadataProvider Provider { get; }
/// <returns>
/// Gets runtime <see cref="Type"/> of <see cref="Model"/> if <see cref="Model"/> is non-<c>null</c> and
/// <see cref="ModelType"/> is not <see cref="Nullable{T}"/>; <see cref="ModelType"/> otherwise.
/// </returns>
public Type RealModelType
{
get
{
if (_realModelType == null)
{
_realModelType = ModelType;
// Don't call GetType() if the model is Nullable<T>, because it will
// turn Nullable<T> into T for non-null values
if (Model != null && !ModelType.IsNullableValueType())
{
_realModelType = Model.GetType();
}
}
return _realModelType;
}
}
public virtual string SimpleDisplayText
{
get
{
if (_simpleDisplayText == null)
{
_simpleDisplayText = ComputeSimpleDisplayText();
}
return _simpleDisplayText;
}
set { _simpleDisplayText = value; }
}
/// <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.
@ -338,38 +271,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
return DisplayName ?? PropertyName ?? ModelType.Name;
}
protected virtual string ComputeSimpleDisplayText()
{
if (Model == null)
{
return NullDisplayText;
}
var stringResult = Convert.ToString(Model, CultureInfo.CurrentCulture);
if (stringResult == null)
{
return string.Empty;
}
if (!stringResult.Equals(Model.GetType().FullName, StringComparison.Ordinal))
{
return stringResult;
}
var firstProperty = Properties.FirstOrDefault();
if (firstProperty == null)
{
return string.Empty;
}
if (firstProperty.Model == null)
{
return firstProperty.NullDisplayText;
}
return Convert.ToString(firstProperty.Model, CultureInfo.CurrentCulture);
}
private static EfficientTypePropertyKey<Type, string> CreateCacheKey(Type containerType,
Type modelType,
string propertyName)

View File

@ -0,0 +1,56 @@
// 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.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ModelBinding
{
/// <summary>
/// Extensions methods for <see cref="IModelMetadataProvider"/>.
/// </summary>
public static class ModelMetadataProviderExtensions
{
/// <summary>
/// Gets a <see cref="ModelExplorer"/> for the provided <paramref name="modelType"/> and
/// <paramref name="model"/>.
/// </summary>
/// <param name="provider">The <see cref="IModelMetadataProvider"/>.</param>
/// <param name="modelType">The declared <see cref="Type"/> of the model object.</param>
/// <param name="model">The model object.</param>
/// <returns></returns>
public static ModelExplorer GetModelExplorerForType(
[NotNull] this IModelMetadataProvider provider,
[NotNull] Type modelType,
object model)
{
var modelMetadata = provider.GetMetadataForType(modelType);
return new ModelExplorer(provider, modelMetadata, model);
}
/// <summary>
/// Gets a <see cref="ModelMetadata"/> for property identified by the provided
/// <paramref name="containerType"/> and <paramref name="propertyName"/>.
/// </summary>
/// <param name="provider">The <see cref="ModelMetadata"/>.</param>
/// <param name="containerType">The <see cref="Type"/> for which the property is defined.</param>
/// <param name="propertyName">The property name.</param>
/// <returns>A <see cref="ModelMetadata"/> for the property.</returns>
public static ModelMetadata GetMetadataForProperty(
[NotNull] this IModelMetadataProvider provider,
[NotNull] Type containerType,
[NotNull] string propertyName)
{
var containerMetadata = provider.GetMetadataForType(containerType);
var propertyMetadata = containerMetadata.Properties[propertyName];
if (propertyMetadata == null)
{
var message = Resources.FormatCommon_PropertyNotFound(containerType, propertyName);
throw new ArgumentException(message, nameof(propertyName));
}
return propertyMetadata;
}
}
}

View File

@ -55,6 +55,15 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
/// </summary>
public OperationBindingContext OperationBindingContext { get; set; }
/// <summary>
/// Gets or sets the model value for the current operation.
/// </summary>
/// <remarks>
/// The <see cref="Model"/> will typically be set for a binding operation that works
/// against a pre-existing model object to update certain properties.
/// </remarks>
public object Model { get; set; }
/// <summary>
/// Gets or sets the metadata for the model associated with this context.
/// </summary>

View File

@ -66,10 +66,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var otherPropertyDisplayName = OtherPropertyDisplayName;
if (otherPropertyDisplayName == null && metadata.ContainerType != null)
{
var otherProperty = context.MetadataProvider
.GetMetadataForProperty(() => metadata.Model,
metadata.ContainerType,
OtherProperty);
var otherProperty = context.MetadataProvider.GetMetadataForProperty(
metadata.ContainerType,
OtherProperty);
if (otherProperty != null)
{
return otherProperty.GetDisplayName();

View File

@ -25,17 +25,20 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public IEnumerable<ModelValidationResult> Validate(ModelValidationContext validationContext)
{
var metadata = validationContext.ModelMetadata;
var modelExplorer = validationContext.ModelExplorer;
var metadata = modelExplorer.Metadata;
var memberName = metadata.PropertyName ?? metadata.ModelType.Name;
var containerMetadata = validationContext.ContainerMetadata;
var container = containerMetadata != null ? containerMetadata.Model : null;
var context = new ValidationContext(container ?? metadata.Model)
var containerExplorer = modelExplorer.Container;
var container = containerExplorer?.Model;
var context = new ValidationContext(container ?? modelExplorer.Model)
{
DisplayName = metadata.GetDisplayName(),
MemberName = memberName
};
var result = Attribute.GetValidationResult(metadata.Model, context);
var result = Attribute.GetValidationResult(modelExplorer.Model, context);
if (result != ValidationResult.Success)
{
// ModelValidationResult.MemberName is used by invoking validators (such as ModelValidator) to

View File

@ -31,7 +31,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
/// <inheritdoc />
public void Validate([NotNull] ModelValidationContext modelValidationContext)
{
var metadata = modelValidationContext.ModelMetadata;
var modelExplorer = modelValidationContext.ModelExplorer;
var validationContext = new ValidationContext()
{
ModelValidationContext = modelValidationContext,
@ -39,17 +39,23 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
};
ValidateNonVisitedNodeAndChildren(
modelValidationContext.RootPrefix, metadata, validationContext, validators: null);
modelValidationContext.RootPrefix,
modelExplorer,
validationContext,
validators: null);
}
private bool ValidateNonVisitedNodeAndChildren(string modelKey,
ModelMetadata metadata, ValidationContext validationContext, IEnumerable<IModelValidator> validators)
private bool ValidateNonVisitedNodeAndChildren(
string modelKey,
ModelExplorer modelExplorer,
ValidationContext validationContext,
IEnumerable<IModelValidator> validators)
{
// Recursion guard to avoid stack overflows
RuntimeHelpers.EnsureSufficientExecutionStack();
var modelState = validationContext.ModelValidationContext.ModelState;
var bindingSourceMetadata = metadata.BinderMetadata as IBindingSourceMetadata;
var bindingSourceMetadata = modelExplorer.Metadata.BinderMetadata as IBindingSourceMetadata;
var bindingSource = bindingSourceMetadata?.BindingSource;
if (bindingSource != null && !bindingSource.IsFromRequest)
@ -78,37 +84,38 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// The validators are not null in the case of validating an array. Since the validators are
// the same for all the elements of the array, we do not do GetValidators for each element,
// instead we just pass them over. See ValidateElements function.
validators = validationContext.ModelValidationContext.ValidatorProvider.GetValidators(metadata);
var validatorProvider = validationContext.ModelValidationContext.ValidatorProvider;
validators = validatorProvider.GetValidators(modelExplorer.Metadata);
}
// We don't need to recursively traverse the graph for null values
if (metadata.Model == null)
if (modelExplorer.Model == null)
{
return ShallowValidate(modelKey, metadata, validationContext, validators);
return ShallowValidate(modelKey, modelExplorer, validationContext, validators);
}
// We don't need to recursively traverse the graph for types that shouldn't be validated
var modelType = metadata.Model.GetType();
var modelType = modelExplorer.Model.GetType();
if (IsTypeExcludedFromValidation(_excludeFilterProvider.ExcludeFilters, modelType))
{
var result = ShallowValidate(modelKey, metadata, validationContext, validators);
MarkPropertiesAsSkipped(modelKey, metadata, validationContext);
var result = ShallowValidate(modelKey, modelExplorer, validationContext, validators);
MarkPropertiesAsSkipped(modelKey, modelExplorer.Metadata, validationContext);
return result;
}
// Check to avoid infinite recursion. This can happen with cycles in an object graph.
if (validationContext.Visited.Contains(metadata.Model))
if (validationContext.Visited.Contains(modelExplorer.Model))
{
return true;
}
validationContext.Visited.Add(metadata.Model);
validationContext.Visited.Add(modelExplorer.Model);
// Validate the children first - depth-first traversal
var enumerableModel = metadata.Model as IEnumerable;
var enumerableModel = modelExplorer.Model as IEnumerable;
if (enumerableModel == null)
{
isValid = ValidateProperties(modelKey, metadata, validationContext);
isValid = ValidateProperties(modelKey, modelExplorer, validationContext);
}
else
{
@ -118,11 +125,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
if (isValid)
{
// Don't bother to validate this node if children failed.
isValid = ShallowValidate(modelKey, metadata, validationContext, validators);
isValid = ShallowValidate(modelKey, modelExplorer, validationContext, validators);
}
// Pop the object so that it can be validated again in a different path
validationContext.Visited.Remove(metadata.Model);
validationContext.Visited.Remove(modelExplorer.Model);
return isValid;
}
@ -151,15 +158,22 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
}
}
private bool ValidateProperties(string currentModelKey, ModelMetadata metadata, ValidationContext validationContext)
private bool ValidateProperties(string currentModelKey, ModelExplorer modelExplorer, ValidationContext validationContext)
{
var isValid = true;
foreach (var childMetadata in metadata.Properties)
foreach (var property in modelExplorer.Metadata.Properties)
{
var propertyName = childMetadata.BinderModelName ?? childMetadata.PropertyName;
var childKey = ModelBindingHelper.CreatePropertyModelName(currentModelKey, propertyName);
if (!ValidateNonVisitedNodeAndChildren(childKey, childMetadata, validationContext, validators: null))
var propertyExplorer = modelExplorer.GetExplorerForProperty(property.PropertyName);
var propertyMetadata = propertyExplorer.Metadata;
var propertyBindingName = propertyMetadata.BinderModelName ?? propertyMetadata.PropertyName;
var childKey = ModelBindingHelper.CreatePropertyModelName(currentModelKey, propertyBindingName);
if (!ValidateNonVisitedNodeAndChildren(
childKey,
propertyExplorer,
validationContext,
validators: null))
{
isValid = false;
}
@ -171,9 +185,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
private bool ValidateElements(string currentKey, IEnumerable model, ValidationContext validationContext)
{
var elementType = GetElementType(model.GetType());
var elementMetadata = _modelMetadataProvider.GetMetadataForType(
modelAccessor: null,
modelType: elementType);
var elementMetadata = _modelMetadataProvider.GetMetadataForType(elementType);
var validators = validationContext.ModelValidationContext.ValidatorProvider.GetValidators(elementMetadata);
@ -189,9 +201,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// If it's null, then a shallow validation will be performed.
if (element != null || anyValidatorsDefined)
{
elementMetadata.Model = element;
var elementExplorer = new ModelExplorer(_modelMetadataProvider, elementMetadata, element);
var elementKey = ModelBindingHelper.CreateIndexModelName(currentKey, index);
if (!ValidateNonVisitedNodeAndChildren(elementKey, elementMetadata, validationContext, validators))
if (!ValidateNonVisitedNodeAndChildren(elementKey, elementExplorer, validationContext, validators))
{
isValid = false;
}
@ -207,7 +219,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// Returns true if validation passes successfully
private static bool ShallowValidate(
string modelKey,
ModelMetadata metadata,
ModelExplorer modelExplorer,
ValidationContext validationContext,
IEnumerable<IModelValidator> validators)
{
@ -219,7 +231,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
if (validatorsAsCollection == null || validatorsAsCollection.Count > 0)
{
var modelValidationContext =
new ModelValidationContext(validationContext.ModelValidationContext, metadata);
new ModelValidationContext(validationContext.ModelValidationContext, modelExplorer);
var modelState = validationContext.ModelValidationContext.ModelState;
var modelValidationState = modelState.GetValidationState(modelKey);
var fieldValidationState = modelState.GetFieldValidationState(modelKey);
@ -298,4 +310,4 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public HashSet<object> Visited { get; set; }
}
}
}
}

View File

@ -8,42 +8,40 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
public class ModelValidationContext
{
public ModelValidationContext([NotNull] ModelBindingContext bindingContext,
[NotNull] ModelMetadata metadata)
public ModelValidationContext(
[NotNull] ModelBindingContext bindingContext,
[NotNull] ModelExplorer modelExplorer)
: this(bindingContext.ModelName,
bindingContext.OperationBindingContext.ValidatorProvider,
bindingContext.ModelState,
metadata,
bindingContext.ModelMetadata)
modelExplorer)
{
}
public ModelValidationContext(string rootPrefix,
[NotNull] IModelValidatorProvider validatorProvider,
[NotNull] ModelStateDictionary modelState,
[NotNull] ModelMetadata metadata,
ModelMetadata containerMetadata)
public ModelValidationContext(
string rootPrefix,
[NotNull] IModelValidatorProvider validatorProvider,
[NotNull] ModelStateDictionary modelState,
[NotNull] ModelExplorer modelExplorer)
{
ModelMetadata = metadata;
ModelState = modelState;
RootPrefix = rootPrefix;
ValidatorProvider = validatorProvider;
ContainerMetadata = containerMetadata;
ModelExplorer = modelExplorer;
}
public ModelValidationContext([NotNull] ModelValidationContext parentContext,
[NotNull] ModelMetadata metadata)
public ModelValidationContext(
[NotNull] ModelValidationContext parentContext,
[NotNull] ModelExplorer modelExplorer)
{
ModelMetadata = metadata;
ContainerMetadata = parentContext.ModelMetadata;
ModelExplorer = modelExplorer;
ModelState = parentContext.ModelState;
RootPrefix = parentContext.RootPrefix;
ValidatorProvider = parentContext.ValidatorProvider;
}
public ModelMetadata ModelMetadata { get; }
public ModelMetadata ContainerMetadata { get; }
public ModelExplorer ModelExplorer { get; }
public ModelStateDictionary ModelState { get; }

View File

@ -17,7 +17,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public IEnumerable<ModelValidationResult> Validate(ModelValidationContext context)
{
var model = context.ModelMetadata.Model;
var model = context.ModelExplorer.Model;
if (model == null)
{
return Enumerable.Empty<ModelValidationResult>();

View File

@ -48,14 +48,14 @@ namespace Microsoft.AspNet.Mvc.Razor
}
var name = ExpressionHelper.GetExpressionText(expression);
var metadata = ExpressionMetadataProvider.FromLambdaExpression(expression, ViewData, _provider);
if (metadata == null)
var modelExplorer = ExpressionMetadataProvider.FromLambdaExpression(expression, ViewData, _provider);
if (modelExplorer == null)
{
throw new InvalidOperationException(
Resources.FormatRazorPage_NullModelMetadata(nameof(IModelMetadataProvider), name));
}
return new ModelExpression(name, metadata);
return new ModelExpression(name, modelExplorer);
}
}
}

View File

@ -137,6 +137,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
// Note null or empty For.Name is allowed because TemplateInfo.HtmlFieldPrefix may be sufficient.
// IHtmlGenerator will enforce name requirements.
var metadata = For.Metadata;
var modelExplorer = For.ModelExplorer;
if (metadata == null)
{
throw new InvalidOperationException(Resources.FormatTagHelpers_NoProvidedMetadata(
@ -151,7 +152,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
if (string.IsNullOrEmpty(InputTypeName))
{
// Note GetInputType never returns null.
inputType = GetInputType(metadata, out inputTypeHint);
inputType = GetInputType(modelExplorer, out inputTypeHint);
}
else
{
@ -169,15 +170,15 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
switch (inputType)
{
case "checkbox":
GenerateCheckBox(metadata, output);
GenerateCheckBox(modelExplorer, output);
return;
case "hidden":
tagBuilder = Generator.GenerateHidden(
ViewContext,
metadata,
modelExplorer,
For.Name,
value: metadata.Model,
value: For.Model,
useViewData: false,
htmlAttributes: null);
break;
@ -185,18 +186,18 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
case "password":
tagBuilder = Generator.GeneratePassword(
ViewContext,
metadata,
modelExplorer,
For.Name,
value: null,
htmlAttributes: null);
break;
case "radio":
tagBuilder = GenerateRadio(metadata);
tagBuilder = GenerateRadio(modelExplorer);
break;
default:
tagBuilder = GenerateTextBox(metadata, inputTypeHint, inputType);
tagBuilder = GenerateTextBox(modelExplorer, inputTypeHint, inputType);
break;
}
@ -210,14 +211,14 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
}
}
private void GenerateCheckBox(ModelMetadata metadata, TagHelperOutput output)
private void GenerateCheckBox(ModelExplorer modelExplorer, TagHelperOutput output)
{
if (typeof(bool) != metadata.RealModelType)
if (typeof(bool) != modelExplorer.ModelType)
{
throw new InvalidOperationException(Resources.FormatInputTagHelper_InvalidExpressionResult(
"<input>",
ForAttributeName,
metadata.RealModelType.FullName,
modelExplorer.ModelType.FullName,
typeof(bool).FullName,
"type",
"checkbox"));
@ -230,7 +231,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var tagBuilder = Generator.GenerateCheckBox(
ViewContext,
metadata,
modelExplorer,
For.Name,
isChecked: null,
htmlAttributes: htmlAttributes);
@ -243,7 +244,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
output.Content += tagBuilder.ToString(TagRenderMode.SelfClosing);
tagBuilder = Generator.GenerateHiddenForCheckbox(ViewContext, metadata, For.Name);
tagBuilder = Generator.GenerateHiddenForCheckbox(ViewContext, modelExplorer, For.Name);
if (tagBuilder != null)
{
output.Content += tagBuilder.ToString(TagRenderMode.SelfClosing);
@ -251,7 +252,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
}
}
private TagBuilder GenerateRadio(ModelMetadata metadata)
private TagBuilder GenerateRadio(ModelExplorer modelExplorer)
{
// Note empty string is allowed.
if (Value == null)
@ -265,38 +266,38 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
return Generator.GenerateRadioButton(
ViewContext,
metadata,
modelExplorer,
For.Name,
Value,
isChecked: null,
htmlAttributes: null);
}
private TagBuilder GenerateTextBox(ModelMetadata metadata, string inputTypeHint, string inputType)
private TagBuilder GenerateTextBox(ModelExplorer modelExplorer, string inputTypeHint, string inputType)
{
var format = Format;
if (string.IsNullOrEmpty(format))
{
format = GetFormat(metadata, inputTypeHint, inputType);
format = GetFormat(modelExplorer, inputTypeHint, inputType);
}
return Generator.GenerateTextBox(
ViewContext,
metadata,
modelExplorer,
For.Name,
value: metadata.Model,
value: modelExplorer.Model,
format: Format,
htmlAttributes: null);
}
// Get a fall-back format based on the metadata.
private string GetFormat(ModelMetadata metadata, string inputTypeHint, string inputType)
private string GetFormat(ModelExplorer modelExplorer, string inputTypeHint, string inputType)
{
string format;
string rfc3339Format;
if (string.Equals("decimal", inputTypeHint, StringComparison.OrdinalIgnoreCase) &&
string.Equals("text", inputType, StringComparison.Ordinal) &&
string.IsNullOrEmpty(metadata.EditFormatString))
string.IsNullOrEmpty(modelExplorer.Metadata.EditFormatString))
{
// Decimal data is edited using an <input type="text"/> element, with a reasonable format.
// EditFormatString has precedence over this fall-back format.
@ -304,8 +305,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
}
else if (_rfc3339Formats.TryGetValue(inputType, out rfc3339Format) &&
ViewContext.Html5DateRenderingMode == Html5DateRenderingMode.Rfc3339 &&
!metadata.HasNonDefaultEditFormat &&
(typeof(DateTime) == metadata.RealModelType || typeof(DateTimeOffset) == metadata.RealModelType))
!modelExplorer.Metadata.HasNonDefaultEditFormat &&
(typeof(DateTime) == modelExplorer.ModelType || typeof(DateTimeOffset) == modelExplorer.ModelType))
{
// Rfc3339 mode _may_ override EditFormatString in a limited number of cases e.g. EditFormatString
// must be a default format (i.e. came from a built-in [DataType] attribute).
@ -314,15 +315,15 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
else
{
// Otherwise use EditFormatString, if any.
format = metadata.EditFormatString;
format = modelExplorer.Metadata.EditFormatString;
}
return format;
}
private string GetInputType(ModelMetadata metadata, out string inputTypeHint)
private string GetInputType(ModelExplorer modelExplorer, out string inputTypeHint)
{
foreach (var hint in GetInputTypeHints(metadata))
foreach (var hint in GetInputTypeHints(modelExplorer))
{
string inputType;
if (_defaultInputTypes.TryGetValue(hint, out inputType))
@ -337,12 +338,12 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
}
// A variant of TemplateRenderer.GetViewNames(). Main change relates to bool? handling.
private static IEnumerable<string> GetInputTypeHints(ModelMetadata metadata)
private static IEnumerable<string> GetInputTypeHints(ModelExplorer modelExplorer)
{
var inputTypeHints = new string[]
{
metadata.TemplateHint,
metadata.DataTypeName,
modelExplorer.Metadata.TemplateHint,
modelExplorer.Metadata.DataTypeName,
};
foreach (string inputTypeHint in inputTypeHints.Where(s => !string.IsNullOrEmpty(s)))
@ -352,7 +353,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
// In most cases, we don't want to search for Nullable<T>. We want to search for T, which should handle
// both T and Nullable<T>. However we special-case bool? to avoid turning an <input/> into a <select/>.
var fieldType = metadata.RealModelType;
var fieldType = modelExplorer.ModelType;
if (typeof(bool?) != fieldType)
{
var underlyingType = Nullable.GetUnderlyingType(fieldType);
@ -369,7 +370,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
// Nothing more to provide
yield break;
}
else if (!metadata.IsComplexType)
else if (!modelExplorer.Metadata.IsComplexType)
{
// IsEnum is false for the Enum class itself
if (fieldType.IsEnum())

View File

@ -35,7 +35,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
if (For != null)
{
var tagBuilder = Generator.GenerateLabel(ViewContext,
For.Metadata,
For.ModelExplorer,
For.Name,
labelText: null,
htmlAttributes: null);

View File

@ -86,7 +86,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
// Base allowMultiple on the instance or declared type of the expression to avoid a
// "SelectExpressionNotEnumerable" InvalidOperationException during generation.
// Metadata.IsCollectionType() is similar but does not take runtime type into account.
var realModelType = For.Metadata.RealModelType;
var realModelType = For.ModelExplorer.ModelType;
var allowMultiple =
typeof(string) != realModelType && typeof(IEnumerable).IsAssignableFrom(realModelType);
@ -96,7 +96,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
ICollection<string> selectedValues;
var tagBuilder = Generator.GenerateSelect(
ViewContext,
For.Metadata,
For.ModelExplorer,
optionLabel: null,
expression: For.Name,
selectList: items,

View File

@ -37,7 +37,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
{
var tagBuilder = Generator.GenerateTextArea(
ViewContext,
For.Metadata,
For.ModelExplorer,
For.Name,
rows: 0,
columns: 0,

View File

@ -420,14 +420,13 @@ namespace System.Web.Http
/// </param>
public void Validate<TEntity>(TEntity entity, string keyPrefix)
{
var modelMetadata = MetadataProvider.GetMetadataForType(() => entity, typeof(TEntity));
var modelExplorer = MetadataProvider.GetModelExplorerForType(typeof(TEntity), entity);
var modelValidationContext = new ModelValidationContext(
keyPrefix,
BindingContext.ValidatorProvider,
ModelState,
modelMetadata,
containerMetadata: null);
modelExplorer);
ObjectValidator.Validate(modelValidationContext);
}

View File

@ -128,7 +128,7 @@ namespace Microsoft.AspNet.Mvc
var bindingContext = new ModelBindingContext
{
ModelMetadata = metadataProvider.GetMetadataForType(null, modelType),
ModelMetadata = metadataProvider.GetMetadataForType(modelType),
ModelName = "someName",
ValueProvider = Mock.Of<IValueProvider>(),
ModelState = new ModelStateDictionary(),

View File

@ -98,7 +98,7 @@ namespace Microsoft.AspNet.Mvc
var contentBytes = Encoding.UTF8.GetBytes(content);
var actionContext = GetActionContext(contentBytes);
var metadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(User));
var metadata = new EmptyModelMetadataProvider().GetMetadataForType(typeof(User));
var context = new InputFormatterContext(actionContext, metadata.ModelType);
// Act
@ -119,7 +119,7 @@ namespace Microsoft.AspNet.Mvc
var contentBytes = Encoding.UTF8.GetBytes(content);
var httpContext = GetActionContext(contentBytes);
var metadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(User));
var metadata = new EmptyModelMetadataProvider().GetMetadataForType(typeof(User));
var context = new InputFormatterContext(httpContext, metadata.ModelType);
// Act and Assert
@ -135,7 +135,7 @@ namespace Microsoft.AspNet.Mvc
var contentBytes = Encoding.UTF8.GetBytes(content);
var actionContext = GetActionContext(contentBytes);
var metadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(User));
var metadata = new EmptyModelMetadataProvider().GetMetadataForType(typeof(User));
var context = new InputFormatterContext(actionContext, metadata.ModelType);
// Act
@ -155,7 +155,7 @@ namespace Microsoft.AspNet.Mvc
var contentBytes = Encoding.UTF8.GetBytes(content);
var actionContext = GetActionContext(contentBytes);
var metadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(User));
var metadata = new EmptyModelMetadataProvider().GetMetadataForType(typeof(User));
var context = new InputFormatterContext(actionContext, metadata.ModelType);
actionContext.ModelState.MaxAllowedErrors = 3;
actionContext.ModelState.AddModelError("key1", "error1");
@ -193,8 +193,7 @@ namespace Microsoft.AspNet.Mvc
jsonFormatter.SerializerSettings.MissingMemberHandling = MissingMemberHandling.Error;
var actionContext = GetActionContext(contentBytes, "application/json;charset=utf-8");
var metadata = new EmptyModelMetadataProvider().GetMetadataForType(modelAccessor: null,
modelType: typeof(UserLogin));
var metadata = new EmptyModelMetadataProvider().GetMetadataForType(typeof(UserLogin));
var inputFormatterContext = new InputFormatterContext(actionContext, metadata.ModelType);
// Act
@ -222,8 +221,7 @@ namespace Microsoft.AspNet.Mvc
};
var actionContext = GetActionContext(contentBytes, "application/json;charset=utf-8");
var metadata = new EmptyModelMetadataProvider().GetMetadataForType(modelAccessor: null,
modelType: typeof(UserLogin));
var metadata = new EmptyModelMetadataProvider().GetMetadataForType(typeof(UserLogin));
var inputFormatterContext = new InputFormatterContext(actionContext, metadata.ModelType);
// Act

View File

@ -53,8 +53,7 @@ namespace Microsoft.AspNet.Mvc.Core.Test
// Arrange
var metadataProvider = new DataAnnotationsModelMetadataProvider();
var modelMetadata = metadataProvider.GetMetadataForType(
modelAccessor: null,
modelType: typeof(TypeWithIncludedPropertiesUsingBindAttribute));
typeof(TypeWithIncludedPropertiesUsingBindAttribute));
// Act
var context = DefaultControllerActionArgumentBinder.GetModelBindingContext(
@ -75,9 +74,9 @@ namespace Microsoft.AspNet.Mvc.Core.Test
var methodInfo = type.GetMethod("ParameterWithNoBindAttribute");
var metadataProvider = new DataAnnotationsModelMetadataProvider();
var modelMetadata = metadataProvider.GetMetadataForParameter(modelAccessor: null,
methodInfo: methodInfo,
parameterName: "parameter");
var modelMetadata = metadataProvider.GetMetadataForParameter(
methodInfo: methodInfo,
parameterName: "parameter");
// Act
var context = DefaultControllerActionArgumentBinder.GetModelBindingContext(
@ -104,9 +103,9 @@ namespace Microsoft.AspNet.Mvc.Core.Test
var methodInfo = type.GetMethod(actionMethodName);
var metadataProvider = new DataAnnotationsModelMetadataProvider();
var modelMetadata = metadataProvider.GetMetadataForParameter(modelAccessor: null,
methodInfo: methodInfo,
parameterName: "parameter");
var modelMetadata = metadataProvider.GetMetadataForParameter(
methodInfo: methodInfo,
parameterName: "parameter");
// Act
var context = DefaultControllerActionArgumentBinder.GetModelBindingContext(
@ -133,9 +132,9 @@ namespace Microsoft.AspNet.Mvc.Core.Test
var methodInfo = type.GetMethod(actionMethodName);
var metadataProvider = new DataAnnotationsModelMetadataProvider();
var modelMetadata = metadataProvider.GetMetadataForParameter(modelAccessor: null,
methodInfo: methodInfo,
parameterName: "parameter1");
var modelMetadata = metadataProvider.GetMetadataForParameter(
methodInfo: methodInfo,
parameterName: "parameter1");
// Act
var context = DefaultControllerActionArgumentBinder.GetModelBindingContext(
@ -275,9 +274,7 @@ namespace Microsoft.AspNet.Mvc.Core.Test
.Setup(b => b.BindModelAsync(It.IsAny<ModelBindingContext>()))
.Callback((ModelBindingContext context) =>
{
context.ModelMetadata = metadataProvider.GetMetadataForType(
modelAccessor: null,
modelType: typeof(string));
context.ModelMetadata = metadataProvider.GetMetadataForType(typeof(string));
})
.Returns(Task.FromResult(result: new ModelBindingResult(value, "", true)));

View File

@ -24,8 +24,8 @@ namespace Microsoft.AspNet.Mvc.Core.Test
{
// Arrange
var metadataProvider = new Mock<IModelMetadataProvider>();
metadataProvider.Setup(m => m.GetMetadataForType(It.IsAny<Func<object>>(), It.IsAny<Type>()))
.Returns(new ModelMetadata(metadataProvider.Object, null, null, typeof(MyModel), null))
metadataProvider.Setup(m => m.GetMetadataForType(It.IsAny<Type>()))
.Returns(new ModelMetadata(metadataProvider.Object, null, typeof(MyModel), null))
.Verifiable();
var binder = new Mock<IModelBinder>();
@ -136,8 +136,8 @@ namespace Microsoft.AspNet.Mvc.Core.Test
{
// Arrange
var metadataProvider = new Mock<IModelMetadataProvider>();
metadataProvider.Setup(m => m.GetMetadataForType(It.IsAny<Func<object>>(), It.IsAny<Type>()))
.Returns(new ModelMetadata(metadataProvider.Object, null, null, typeof(MyModel), null))
metadataProvider.Setup(m => m.GetMetadataForType(It.IsAny<Type>()))
.Returns(new ModelMetadata(metadataProvider.Object, null, typeof(MyModel), null))
.Verifiable();
var binder = new Mock<IModelBinder>();
@ -227,8 +227,8 @@ namespace Microsoft.AspNet.Mvc.Core.Test
{
// Arrange
var metadataProvider = new Mock<IModelMetadataProvider>();
metadataProvider.Setup(m => m.GetMetadataForType(It.IsAny<Func<object>>(), It.IsAny<Type>()))
.Returns(new ModelMetadata(metadataProvider.Object, null, null, typeof(MyModel), null))
metadataProvider.Setup(m => m.GetMetadataForType(It.IsAny<Type>()))
.Returns(new ModelMetadata(metadataProvider.Object, null, typeof(MyModel), null))
.Verifiable();
var binder = new Mock<IModelBinder>();
@ -484,8 +484,8 @@ namespace Microsoft.AspNet.Mvc.Core.Test
{
// Arrange
var metadataProvider = new Mock<IModelMetadataProvider>();
metadataProvider.Setup(m => m.GetMetadataForType(It.IsAny<Func<object>>(), It.IsAny<Type>()))
.Returns(new ModelMetadata(metadataProvider.Object, null, null, typeof(MyModel), null))
metadataProvider.Setup(m => m.GetMetadataForType(It.IsAny<Type>()))
.Returns(new ModelMetadata(metadataProvider.Object, null, typeof(MyModel), null))
.Verifiable();
var binder = new Mock<IModelBinder>();
@ -580,8 +580,8 @@ namespace Microsoft.AspNet.Mvc.Core.Test
{
// Arrange
var metadataProvider = new Mock<IModelMetadataProvider>();
metadataProvider.Setup(m => m.GetMetadataForType(It.IsAny<Func<object>>(), It.IsAny<Type>()))
.Returns(new ModelMetadata(metadataProvider.Object, null, null, typeof(MyModel), null))
metadataProvider.Setup(m => m.GetMetadataForType(It.IsAny<Type>()))
.Returns(new ModelMetadata(metadataProvider.Object, null, typeof(MyModel), null))
.Verifiable();
var binder = new Mock<IModelBinder>();
@ -655,13 +655,14 @@ namespace Microsoft.AspNet.Mvc.Core.Test
{
// Arrange
var metadataProvider = new Mock<IModelMetadataProvider>();
metadataProvider.Setup(m => m.GetMetadataForType(null, It.IsAny<Type>()))
.Returns(new ModelMetadata(metadataProvider.Object,
containerType: null,
modelAccessor: null,
modelType: typeof(MyModel),
propertyName: null))
.Verifiable();
metadataProvider
.Setup(m => m.GetMetadataForType(It.IsAny<Type>()))
.Returns(new ModelMetadata(
metadataProvider.Object,
containerType: null,
modelType: typeof(MyModel),
propertyName: null))
.Verifiable();
var binder = new Mock<IModelBinder>();
binder.Setup(b => b.BindModelAsync(It.IsAny<ModelBindingContext>()))

View File

@ -20,9 +20,8 @@ namespace Microsoft.AspNet.Mvc
{
private static readonly IModelMetadataProvider _metadataProvider = new EmptyModelMetadataProvider();
private static readonly ModelMetadata _metadata = _metadataProvider.GetMetadataForProperty(
modelAccessor: null,
containerType: typeof(string),
propertyName: "Length");
typeof(string),
"Length");
public static TheoryData<string> SomeNames
{

View File

@ -71,17 +71,14 @@ namespace Microsoft.AspNet.Mvc.Core
{
// Arrange
var html = DefaultTemplatesUtilities.GetHtmlHelper();
var metadata =
new EmptyModelMetadataProvider()
.GetMetadataForType(null, typeof(DefaultTemplatesUtilities.ObjectTemplateModel));
metadata.NullDisplayText = "(null value)";
html.ViewData.ModelMetadata = metadata;
html.ViewData.ModelMetadata.NullDisplayText = "(null value)";
// Act
var result = DefaultDisplayTemplates.ObjectTemplate(html);
// Assert
Assert.Equal(metadata.NullDisplayText, result);
Assert.Equal("(null value)", result);
}
[Theory]
@ -93,13 +90,13 @@ namespace Microsoft.AspNet.Mvc.Core
{
// Arrange
var model = new DefaultTemplatesUtilities.ObjectTemplateModel();
model.Property1 = simpleDisplayText;
var html = DefaultTemplatesUtilities.GetHtmlHelper(model);
var metadata =
new EmptyModelMetadataProvider()
.GetMetadataForType(() => model, typeof(DefaultTemplatesUtilities.ObjectTemplateModel));
metadata.HtmlEncode = htmlEncode;
metadata.SimpleDisplayText = simpleDisplayText;
html.ViewData.ModelMetadata = metadata;
html.ViewData.ModelMetadata.HtmlEncode = htmlEncode;
html.ViewData.ModelMetadata.SimpleDisplayProperty = "Property1";
html.ViewData.TemplateInfo.AddVisited("foo");
html.ViewData.TemplateInfo.AddVisited("bar");

View File

@ -103,12 +103,10 @@ namespace Microsoft.AspNet.Mvc.Core
{
// Arrange
var html = DefaultTemplatesUtilities.GetHtmlHelper();
var metadata =
new EmptyModelMetadataProvider()
.GetMetadataForType(null, typeof(DefaultTemplatesUtilities.ObjectTemplateModel));
metadata.NullDisplayText = "Null Display Text";
metadata.SimpleDisplayText = "Simple Display Text";
html.ViewData.ModelMetadata = metadata;
html.ViewData.ModelMetadata.NullDisplayText = "Null Display Text";
html.ViewData.ModelMetadata.SimpleDisplayProperty = "Property1";
html.ViewData.TemplateInfo.AddVisited("foo");
html.ViewData.TemplateInfo.AddVisited("bar");
@ -116,7 +114,7 @@ namespace Microsoft.AspNet.Mvc.Core
var result = DefaultEditorTemplates.ObjectTemplate(html);
// Assert
Assert.Equal(metadata.NullDisplayText, result);
Assert.Equal(html.ViewData.ModelMetadata.NullDisplayText, result);
}
[Theory]
@ -127,15 +125,17 @@ namespace Microsoft.AspNet.Mvc.Core
string expectedResult)
{
// Arrange
var model = new DefaultTemplatesUtilities.ObjectTemplateModel();
var model = new DefaultTemplatesUtilities.ObjectTemplateModel()
{
Property1 = simpleDisplayText,
};
var html = DefaultTemplatesUtilities.GetHtmlHelper(model);
var metadata =
new EmptyModelMetadataProvider()
.GetMetadataForType(() => model, typeof(DefaultTemplatesUtilities.ObjectTemplateModel));
metadata.HtmlEncode = htmlEncode;
metadata.NullDisplayText = "Null Display Text";
metadata.SimpleDisplayText = simpleDisplayText;
html.ViewData.ModelMetadata = metadata;
html.ViewData.ModelMetadata.HtmlEncode = htmlEncode;
html.ViewData.ModelMetadata.NullDisplayText = "Null Display Text";
html.ViewData.ModelMetadata.SimpleDisplayProperty = "Property1";
html.ViewData.TemplateInfo.AddVisited("foo");
html.ViewData.TemplateInfo.AddVisited("bar");
@ -917,7 +917,9 @@ Environment.NewLine;
throw new NotImplementedException();
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, string name)
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(
ModelExplorer modelExplorer,
string name)
{
return Enumerable.Empty<ModelClientValidationRule>();
}

View File

@ -2,6 +2,7 @@
// 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.Globalization;
using System.IO;
@ -45,6 +46,11 @@ namespace Microsoft.AspNet.Mvc.Rendering
return GetHtmlHelper<ObjectTemplateModel>(model: null);
}
public static HtmlHelper<IEnumerable<ObjectTemplateModel>> GetHtmlHelperForEnumerable()
{
return GetHtmlHelper<IEnumerable<ObjectTemplateModel>>(model: null);
}
public static HtmlHelper<ObjectTemplateModel> GetHtmlHelper(IUrlHelper urlHelper)
{
return GetHtmlHelper<ObjectTemplateModel>(
@ -82,11 +88,27 @@ namespace Microsoft.AspNet.Mvc.Rendering
return GetHtmlHelper(model, CreateViewEngine());
}
public static HtmlHelper<IEnumerable<TModel>> GetHtmlHelperForEnumerable<TModel>(TModel model)
{
return GetHtmlHelper<IEnumerable<TModel>>(new TModel[] { model });
}
public static HtmlHelper<TModel> GetHtmlHelper<TModel>(IModelMetadataProvider provider)
{
return GetHtmlHelper<TModel>(model: default(TModel), provider: provider);
}
public static HtmlHelper<ObjectTemplateModel> GetHtmlHelper(IModelMetadataProvider provider)
{
return GetHtmlHelper<ObjectTemplateModel>(model: null, provider: provider);
}
public static HtmlHelper<IEnumerable<ObjectTemplateModel>> GetHtmlHelperForEnumerable(
IModelMetadataProvider provider)
{
return GetHtmlHelper<IEnumerable<ObjectTemplateModel>>(model: null, provider: provider);
}
public static HtmlHelper<TModel> GetHtmlHelper<TModel>(TModel model, IModelMetadataProvider provider)
{
return GetHtmlHelper(model, CreateUrlHelper(), CreateViewEngine(), provider);
@ -191,8 +213,8 @@ namespace Microsoft.AspNet.Mvc.Rendering
public static string FormatOutput(IHtmlHelper helper, object model)
{
var metadata = helper.MetadataProvider.GetMetadataForType(() => model, model.GetType());
return FormatOutput(metadata);
var modelExplorer = helper.MetadataProvider.GetModelExplorerForType(model.GetType(), model);
return FormatOutput(modelExplorer);
}
private static ICompositeViewEngine CreateViewEngine()
@ -203,7 +225,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
.Callback(async (ViewContext v) =>
{
view.ToString();
await v.Writer.WriteAsync(FormatOutput(v.ViewData.ModelMetadata));
await v.Writer.WriteAsync(FormatOutput(v.ViewData.ModelExplorer));
})
.Returns(Task.FromResult(0));
@ -228,14 +250,15 @@ namespace Microsoft.AspNet.Mvc.Rendering
optionsAccessor.Object);
}
private static string FormatOutput(ModelMetadata metadata)
private static string FormatOutput(ModelExplorer modelExplorer)
{
var metadata = modelExplorer.Metadata;
return string.Format(CultureInfo.InvariantCulture,
"Model = {0}, ModelType = {1}, PropertyName = {2}, SimpleDisplayText = {3}",
metadata.Model ?? "(null)",
modelExplorer.Model ?? "(null)",
metadata.ModelType == null ? "(null)" : metadata.ModelType.FullName,
metadata.PropertyName ?? "(null)",
metadata.SimpleDisplayText ?? "(null)");
modelExplorer.GetSimpleDisplayText() ?? "(null)");
}
private static IUrlHelper CreateUrlHelper()

View File

@ -24,7 +24,7 @@ namespace Microsoft.AspNet.Mvc.Rendering.Expressions
provider);
// Assert
Assert.Same(myModel, metadata.Container);
Assert.Same(myModel, metadata.Container.Model);
}
[Fact]
@ -42,7 +42,7 @@ namespace Microsoft.AspNet.Mvc.Rendering.Expressions
provider);
// Assert
Assert.Same(myModel, metadata.Container);
Assert.Same(myModel, metadata.Container.Model);
}
private class TestModel

View File

@ -5,7 +5,6 @@ using System;
using System.Collections.Generic;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.Rendering;
using Moq;
using Xunit;
namespace Microsoft.AspNet.Mvc.Core
@ -43,8 +42,7 @@ namespace Microsoft.AspNet.Mvc.Core
{
// Arrange
var helper = DefaultTemplatesUtilities.GetHtmlHelper();
var enumerableHelper =
DefaultTemplatesUtilities.GetHtmlHelper<IEnumerable<DefaultTemplatesUtilities.ObjectTemplateModel>>(model: null);
var enumerableHelper = DefaultTemplatesUtilities.GetHtmlHelperForEnumerable();
// Act
var displayNameResult = helper.DisplayName("Property1");
@ -57,13 +55,27 @@ namespace Microsoft.AspNet.Mvc.Core
Assert.Equal("Property1", displayNameForEnumerableResult);
}
// If the metadata is for a type (not property), then DisplayName(expression) will evaluate the expression
[Fact]
public void DisplayNameHelpers_DisplayName_Evaluates_Expression()
{
// Arrange
var helper = DefaultTemplatesUtilities.GetHtmlHelper();
helper.ViewData["value"] = "testvalue";
// Act
var displayNameResult = helper.DisplayName(expression: "value");
// Assert
Assert.Equal("value", displayNameResult);
}
[Fact]
public void DisplayNameHelpers_ReturnPropertyName_ForNestedProperty()
{
// Arrange
var helper = DefaultTemplatesUtilities.GetHtmlHelper<OuterClass>(model: null);
var enumerableHelper =
DefaultTemplatesUtilities.GetHtmlHelper<IEnumerable<OuterClass>>(model: null);
var enumerableHelper = DefaultTemplatesUtilities.GetHtmlHelperForEnumerable<OuterClass>(model: null);
// Act
var displayNameResult = helper.DisplayName("Inner.Id");
@ -76,86 +88,24 @@ namespace Microsoft.AspNet.Mvc.Core
Assert.Equal("Id", displayNameForEnumerableResult);
}
[Theory]
[InlineData("")]
[InlineData("Custom property name from metadata")]
public void DisplayNameHelpers_ReturnMetadataPropertyName_IfOverridden(string propertyName)
{
// Arrange
var metadata = new ModelMetadata(
new DataAnnotationsModelMetadataProvider(),
containerType: null,
modelAccessor: null,
modelType: typeof(string), // Ensure FromStringExpression() doesn't ignore the ModelMetadata.
propertyName: propertyName);
var helper = DefaultTemplatesUtilities.GetHtmlHelper();
helper.ViewData.ModelMetadata = metadata;
var enumerableHelper =
DefaultTemplatesUtilities.GetHtmlHelper<IEnumerable<DefaultTemplatesUtilities.ObjectTemplateModel>>(model: null);
enumerableHelper.ViewData.ModelMetadata = metadata;
// Act
var displayNameResult = helper.DisplayName(expression: string.Empty);
var displayNameForResult = helper.DisplayNameFor(m => m);
var displayNameForEnumerableResult = enumerableHelper.DisplayNameFor(m => m);
var displayNameForModelResult = helper.DisplayNameForModel();
// Assert
Assert.Equal(propertyName, displayNameResult);
Assert.Equal(propertyName, displayNameForResult);
Assert.Equal(propertyName, displayNameForEnumerableResult);
Assert.Equal(propertyName, displayNameForModelResult);
}
[Theory]
[InlineData("")]
[InlineData("Custom property name from metadata")]
public void DisplayNameHelpers_ReturnMetadataPropertyNameForProperty_IfOverridden(string propertyName)
{
// Arrange
var metadataHelper = new MetadataHelper();
var metadata = new ModelMetadata(
metadataHelper.MetadataProvider.Object,
containerType: null,
modelAccessor: null,
modelType: typeof(object),
propertyName: propertyName);
metadataHelper.MetadataProvider
.Setup(provider => provider.GetMetadataForProperty(It.IsAny<Func<object>>(), It.IsAny<Type>(), "Property1"))
.Returns(metadata);
var helper = DefaultTemplatesUtilities.GetHtmlHelper(metadataHelper.MetadataProvider.Object);
var enumerableHelper =
DefaultTemplatesUtilities.GetHtmlHelper<IEnumerable<DefaultTemplatesUtilities.ObjectTemplateModel>>(
model: null,
provider: metadataHelper.MetadataProvider.Object);
// Act
var displayNameForResult = helper.DisplayNameFor(m => m.Property1);
var displayNameForEnumerableResult = enumerableHelper.DisplayNameFor(m => m.Property1);
// Assert
Assert.Equal(propertyName, displayNameForResult);
Assert.Equal(propertyName, displayNameForEnumerableResult);
}
[Theory]
[InlineData("")] // Empty display name wins over non-empty property name.
[InlineData("Custom display name from metadata")]
public void DisplayNameHelpers_ReturnDisplayName_IfNonNull(string displayName)
{
// Arrange
var helper = DefaultTemplatesUtilities.GetHtmlHelper();
helper.ViewData.ModelMetadata.DisplayName = displayName;
var enumerableHelper =
DefaultTemplatesUtilities.GetHtmlHelper<IEnumerable<DefaultTemplatesUtilities.ObjectTemplateModel>>(model: null);
enumerableHelper.ViewData.ModelMetadata.DisplayName = displayName;
var provider = new TestModelMetadataProvider();
provider
.ForType<DefaultTemplatesUtilities.ObjectTemplateModel>()
.Then(mm => mm.DisplayName = displayName);
var helper = DefaultTemplatesUtilities.GetHtmlHelper(provider: provider);
var enumerableHelper = DefaultTemplatesUtilities.GetHtmlHelperForEnumerable(provider: provider);
// Act
var displayNameResult = helper.DisplayName(expression: string.Empty);
var displayNameForResult = helper.DisplayNameFor(m => m);
var displayNameForEnumerableResult = enumerableHelper.DisplayNameFor(m => m);
var displayNameForEnumerableResult = enumerableHelper.DisplayNameFor((DefaultTemplatesUtilities.ObjectTemplateModel m) => m);
var displayNameForModelResult = helper.DisplayNameForModel();
// Assert
@ -171,15 +121,13 @@ namespace Microsoft.AspNet.Mvc.Core
public void DisplayNameHelpers_ReturnDisplayNameForProperty_IfNonNull(string displayName)
{
// Arrange
var metadataHelper = new MetadataHelper(); // All properties will use the same metadata.
metadataHelper.Metadata
.Setup(metadata => metadata.DisplayName)
.Returns(displayName);
var helper = DefaultTemplatesUtilities.GetHtmlHelper(metadataHelper.MetadataProvider.Object);
var enumerableHelper =
DefaultTemplatesUtilities.GetHtmlHelper<IEnumerable<DefaultTemplatesUtilities.ObjectTemplateModel>>(
model: null,
provider: metadataHelper.MetadataProvider.Object);
var provider = new TestModelMetadataProvider();
provider
.ForProperty<DefaultTemplatesUtilities.ObjectTemplateModel>("Property1")
.Then(mm => mm.DisplayName = displayName);
var helper = DefaultTemplatesUtilities.GetHtmlHelper(provider: provider);
var enumerableHelper = DefaultTemplatesUtilities.GetHtmlHelperForEnumerable(provider: provider);
// Act
var displayNameResult = helper.DisplayName("Property1");
@ -202,8 +150,7 @@ namespace Microsoft.AspNet.Mvc.Core
string expectedResult)
{
// Arrange
var metadataHelper = new MetadataHelper();
var helper = DefaultTemplatesUtilities.GetHtmlHelper(metadataHelper.MetadataProvider.Object);
var helper = DefaultTemplatesUtilities.GetHtmlHelper();
// Act
var result = helper.DisplayName(expression);
@ -213,29 +160,6 @@ namespace Microsoft.AspNet.Mvc.Core
Assert.Equal(expectedResult, result);
}
[Fact]
public void DisplayNameFor_ConsultsMetadataProviderForMetadataAboutProperty()
{
// Arrange
var modelType = typeof(DefaultTemplatesUtilities.ObjectTemplateModel);
var metadataHelper = new MetadataHelper();
metadataHelper.MetadataProvider
.Setup(p => p.GetMetadataForProperty(It.IsAny<Func<object>>(), modelType, "Property1"))
.Returns(metadataHelper.Metadata.Object)
.Verifiable();
var helper = DefaultTemplatesUtilities.GetHtmlHelper(metadataHelper.MetadataProvider.Object);
// Act
var result = helper.DisplayNameFor(m => m.Property1);
// Assert
metadataHelper.MetadataProvider.Verify();
// DisplayNameFor() falls back to expression name when DisplayName and PropertyName are null.
Assert.Equal("Property1", result);
}
[Fact]
public void DisplayNameFor_ThrowsInvalidOperation_IfExpressionUnsupported()
{
@ -303,25 +227,5 @@ namespace Microsoft.AspNet.Mvc.Core
{
public InnerClass Inner { get; set; }
}
private sealed class MetadataHelper
{
public Mock<ModelMetadata> Metadata { get; set; }
public Mock<IModelMetadataProvider> MetadataProvider { get; set; }
public MetadataHelper()
{
MetadataProvider = new Mock<IModelMetadataProvider>();
Metadata = new Mock<ModelMetadata>(MetadataProvider.Object, null, null, typeof(object), null);
Metadata.SetupGet(m => m.Properties).CallBase();
MetadataProvider.Setup(p => p.GetMetadataForProperties(It.IsAny<object>(), It.IsAny<Type>()))
.Returns(new ModelMetadata[0]);
MetadataProvider.Setup(p => p.GetMetadataForProperty(It.IsAny<Func<object>>(), It.IsAny<Type>(), It.IsAny<string>()))
.Returns(Metadata.Object);
MetadataProvider.Setup(p => p.GetMetadataForType(It.IsAny<Func<object>>(), It.IsAny<Type>()))
.Returns(Metadata.Object);
}
}
}
}

View File

@ -115,9 +115,13 @@ namespace Microsoft.AspNet.Mvc.Rendering
public void DisplayText_ReturnsSimpleDisplayText_IfSetAndValueNonNull()
{
// Arrange
var model = new OverriddenToStringModel("Ignored text");
var model = new OverriddenToStringModel("Ignored text")
{
SimpleDisplay = "Simple display text",
};
var helper = DefaultTemplatesUtilities.GetHtmlHelper(model);
helper.ViewData.ModelMetadata.SimpleDisplayText = "Simple display text";
helper.ViewData.ModelMetadata.SimpleDisplayProperty = nameof(OverriddenToStringModel.SimpleDisplay);
// Act
var result = helper.DisplayText(expression: string.Empty);
@ -130,9 +134,13 @@ namespace Microsoft.AspNet.Mvc.Rendering
public void DisplayTextFor_ReturnsSimpleDisplayText_IfSetAndValueNonNull()
{
// Arrange
var model = new OverriddenToStringModel("Ignored text");
var model = new OverriddenToStringModel("Ignored text")
{
SimpleDisplay = "Simple display text",
};
var helper = DefaultTemplatesUtilities.GetHtmlHelper(model);
helper.ViewData.ModelMetadata.SimpleDisplayText = "Simple display text";
helper.ViewData.ModelMetadata.SimpleDisplayProperty = nameof(OverriddenToStringModel.SimpleDisplay);
// Act
var result = helper.DisplayTextFor(m => m);
@ -148,9 +156,11 @@ namespace Microsoft.AspNet.Mvc.Rendering
var model = new OverriddenToStringModel("Ignored text")
{
Name = "Property value",
SimpleDisplay = "Simple display text",
};
var helper = DefaultTemplatesUtilities.GetHtmlHelper(model);
helper.ViewData.ModelMetadata.SimpleDisplayText = "Simple display text";
helper.ViewData.ModelMetadata.SimpleDisplayProperty = nameof(OverriddenToStringModel.SimpleDisplay);
// Act
var result = helper.DisplayText("Name");
@ -276,6 +286,8 @@ namespace Microsoft.AspNet.Mvc.Rendering
_simpleDisplayText = simpleDisplayText;
}
public string SimpleDisplay { get; set; }
public string Name { get; set; }
public override string ToString()

View File

@ -4,7 +4,6 @@
using System;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.Rendering;
using Moq;
using Xunit;
namespace Microsoft.AspNet.Mvc.Core
@ -67,15 +66,15 @@ namespace Microsoft.AspNet.Mvc.Core
public void LabelHelpers_ReturnEmptyForModel_IfMetadataPropertyNameEmpty()
{
// Arrange
var provider = new DataAnnotationsModelMetadataProvider();
var metadata = new ModelMetadata(
new DataAnnotationsModelMetadataProvider(),
provider,
containerType: null,
modelAccessor: null,
modelType: typeof(object),
propertyName: string.Empty);
var helper = DefaultTemplatesUtilities.GetHtmlHelper();
helper.ViewData.ModelMetadata = metadata;
helper.ViewData.ModelExplorer = new ModelExplorer(provider, metadata, model: null);
// Act
var labelResult = helper.Label(expression: string.Empty);
@ -90,21 +89,20 @@ namespace Microsoft.AspNet.Mvc.Core
Assert.Empty(labelForModelResult.ToString());
}
[Theory]
[InlineData("MyProperty")]
[InlineData("Custom property name from metadata")]
public void LabelHelpers_DisplayMetadataPropertyName_IfOverridden(string propertyName)
[Fact]
public void LabelHelpers_DisplayMetadataPropertyNameForProperty()
{
// Arrange
var metadata = new ModelMetadata(
new DataAnnotationsModelMetadataProvider(),
containerType: null,
modelAccessor: null,
modelType: typeof(string), // Ensure FromStringExpression() doesn't ignore the ModelMetadata.
propertyName: propertyName);
var propertyName = "Property1";
var provider = new EmptyModelMetadataProvider();
var modelExplorer = provider
.GetModelExplorerForType(typeof(DefaultTemplatesUtilities.ObjectTemplateModel), model: null)
.GetExplorerForProperty(propertyName);
var helper = DefaultTemplatesUtilities.GetHtmlHelper();
helper.ViewData.ModelMetadata = metadata;
helper.ViewData.ModelExplorer = modelExplorer;
// Act
var labelResult = helper.Label(expression: string.Empty);
@ -117,30 +115,19 @@ namespace Microsoft.AspNet.Mvc.Core
Assert.Equal("<label for=\"\">" + propertyName + "</label>", labelForModelResult.ToString());
}
[Theory]
[InlineData("MyProperty")]
[InlineData("Custom property name from metadata")]
public void LabelHelpers_DisplayMetadataPropertyNameForProperty_IfOverridden(string propertyName)
// If the metadata is for a type (not property), then Label(expression) will evaluate the expression
[Fact]
public void LabelHelpers_Label_Evaluates_Expression()
{
// Arrange
var metadataHelper = new MetadataHelper();
var metadata = new ModelMetadata(
metadataHelper.MetadataProvider.Object,
containerType: null,
modelAccessor: null,
modelType: typeof(object),
propertyName: propertyName);
metadataHelper.MetadataProvider
.Setup(provider => provider.GetMetadataForProperty(It.IsAny<Func<object>>(), It.IsAny<Type>(), "Property1"))
.Returns(metadata);
var helper = DefaultTemplatesUtilities.GetHtmlHelper(metadataHelper.MetadataProvider.Object);
var helper = DefaultTemplatesUtilities.GetHtmlHelper();
helper.ViewData["value"] = "testvalue";
// Act
var labelForResult = helper.LabelFor(m => m.Property1);
var labelResult = helper.Label(expression: "value");
// Assert
Assert.Equal("<label for=\"Property1\">" + propertyName + "</label>", labelForResult.ToString());
Assert.Equal("<label for=\"value\">value</label>", labelResult.ToString());
}
[Fact]
@ -187,11 +174,15 @@ namespace Microsoft.AspNet.Mvc.Core
public void LabelHelpers_ReturnEmptyForProperty_IfDisplayNameEmpty()
{
// Arrange
var metadataHelper = new MetadataHelper(); // All properties will use the same metadata.
metadataHelper.Metadata
.Setup(metadata => metadata.DisplayName)
.Returns(string.Empty);
var helper = DefaultTemplatesUtilities.GetHtmlHelper(metadataHelper.MetadataProvider.Object);
var provider = new EmptyModelMetadataProvider();
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);
@ -212,11 +203,12 @@ namespace Microsoft.AspNet.Mvc.Core
public void LabelHelpers_DisplayDisplayNameForProperty_IfNonNull(string displayName)
{
// Arrange
var metadataHelper = new MetadataHelper(); // All properties will use the same metadata.
metadataHelper.Metadata
.Setup(metadata => metadata.DisplayName)
.Returns(displayName);
var helper = DefaultTemplatesUtilities.GetHtmlHelper(metadataHelper.MetadataProvider.Object);
var provider = new TestModelMetadataProvider();
provider
.ForProperty<DefaultTemplatesUtilities.ObjectTemplateModel>("Property1")
.Then(mm => mm.DisplayName = displayName);
var helper = DefaultTemplatesUtilities.GetHtmlHelper(provider: provider);
// Act
var labelResult = helper.Label("Property1");
@ -238,8 +230,7 @@ namespace Microsoft.AspNet.Mvc.Core
string expectedId)
{
// Arrange
var metadataHelper = new MetadataHelper();
var helper = DefaultTemplatesUtilities.GetHtmlHelper(metadataHelper.MetadataProvider.Object);
var helper = DefaultTemplatesUtilities.GetHtmlHelper();
// Act
var result = helper.Label(expression);
@ -249,29 +240,6 @@ namespace Microsoft.AspNet.Mvc.Core
Assert.Equal("<label for=\"" + expectedId + "\">" + expectedText + "</label>", result.ToString());
}
[Fact]
public void LabelFor_ConsultsMetadataProviderForMetadataAboutProperty()
{
// Arrange
var modelType = typeof(DefaultTemplatesUtilities.ObjectTemplateModel);
var metadataHelper = new MetadataHelper();
metadataHelper.MetadataProvider
.Setup(p => p.GetMetadataForProperty(It.IsAny<Func<object>>(), modelType, "Property1"))
.Returns(metadataHelper.Metadata.Object)
.Verifiable();
var helper = DefaultTemplatesUtilities.GetHtmlHelper(metadataHelper.MetadataProvider.Object);
// Act
var result = helper.LabelFor(m => m.Property1);
// Assert
metadataHelper.MetadataProvider.Verify();
// LabelFor() falls back to expression name when DisplayName and PropertyName are null.
Assert.Equal("<label for=\"Property1\">Property1</label>", result.ToString());
}
[Fact]
public void LabelFor_ThrowsInvalidOperation_IfExpressionUnsupported()
{
@ -309,25 +277,5 @@ namespace Microsoft.AspNet.Mvc.Core
{
public InnerClass Inner { get; set; }
}
private sealed class MetadataHelper
{
public Mock<ModelMetadata> Metadata { get; set; }
public Mock<IModelMetadataProvider> MetadataProvider { get; set; }
public MetadataHelper()
{
MetadataProvider = new Mock<IModelMetadataProvider>();
Metadata = new Mock<ModelMetadata>(MetadataProvider.Object, null, null, typeof(object), null);
Metadata.SetupGet(m => m.Properties).CallBase();
MetadataProvider.Setup(p => p.GetMetadataForProperties(It.IsAny<object>(), It.IsAny<Type>()))
.Returns(new ModelMetadata[0]);
MetadataProvider.Setup(p => p.GetMetadataForProperty(It.IsAny<Func<object>>(), It.IsAny<Type>(), It.IsAny<string>()))
.Returns(Metadata.Object);
MetadataProvider.Setup(p => p.GetMetadataForType(It.IsAny<Func<object>>(), It.IsAny<Type>()))
.Returns(Metadata.Object);
}
}
}
}

View File

@ -139,13 +139,16 @@ namespace Microsoft.AspNet.Mvc.Core
{
// Arrange
var provider = new Mock<IModelMetadataProvider>(MockBehavior.Strict);
var metadata =
new Mock<ModelMetadata>(MockBehavior.Strict, provider.Object, null, null, typeof(object), null);
var metadata = new Mock<ModelMetadata>(
MockBehavior.Strict,
provider.Object,
null,
typeof(object),
null);
provider
.Setup(m => m.GetMetadataForType(
It.IsAny<Func<object>>(),
typeof(DefaultTemplatesUtilities.ObjectTemplateModel)))
.Setup(m => m.GetMetadataForType(typeof(DefaultTemplatesUtilities.ObjectTemplateModel)))
.Returns(metadata.Object);
var helper = DefaultTemplatesUtilities.GetHtmlHelper(provider.Object);
// Act (do not throw)
@ -159,7 +162,7 @@ namespace Microsoft.AspNet.Mvc.Core
// Assert
// Only the ViewDataDictionary should do anything with metadata.
provider.Verify(
m => m.GetMetadataForType(It.IsAny<Func<object>>(), typeof(DefaultTemplatesUtilities.ObjectTemplateModel)),
m => m.GetMetadataForType(typeof(DefaultTemplatesUtilities.ObjectTemplateModel)),
Times.Exactly(2));
}
@ -168,12 +171,15 @@ namespace Microsoft.AspNet.Mvc.Core
{
// Arrange
var provider = new Mock<IModelMetadataProvider>(MockBehavior.Strict);
var metadata =
new Mock<ModelMetadata>(MockBehavior.Strict, provider.Object, null, null, typeof(object), null);
var metadata = new Mock<ModelMetadata>(
MockBehavior.Strict,
provider.Object,
null,
typeof(object),
null);
provider
.Setup(m => m.GetMetadataForType(
It.IsAny<Func<object>>(),
typeof(DefaultTemplatesUtilities.ObjectTemplateModel)))
.Setup(m => m.GetMetadataForType(typeof(DefaultTemplatesUtilities.ObjectTemplateModel)))
.Returns(metadata.Object);
var helper = DefaultTemplatesUtilities.GetHtmlHelper(provider.Object);
@ -186,7 +192,7 @@ namespace Microsoft.AspNet.Mvc.Core
// Assert
// Only the ViewDataDictionary should do anything with metadata.
provider.Verify(
m => m.GetMetadataForType(It.IsAny<Func<object>>(), typeof(DefaultTemplatesUtilities.ObjectTemplateModel)),
m => m.GetMetadataForType(typeof(DefaultTemplatesUtilities.ObjectTemplateModel)),
Times.Exactly(2));
}

View File

@ -0,0 +1,117 @@
// 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
{
public class TestModelMetadataProvider : EmptyModelMetadataProvider
{
private List<MetadataBuilder> _builders = new List<MetadataBuilder>();
protected override ModelMetadata CreateMetadataFromPrototype([NotNull] ModelMetadata prototype)
{
var metadata = base.CreateMetadataFromPrototype(prototype);
if (prototype.PropertyName == null)
{
foreach (var builder in _builders)
{
builder.Apply(prototype.ModelType, metadata);
}
}
else
{
foreach (var builder in _builders)
{
builder.Apply(prototype.ContainerType, prototype.PropertyName, metadata);
}
}
return metadata;
}
public IMetadataBuilder ForType(Type type)
{
var builder = new MetadataBuilder(type);
_builders.Add(builder);
return builder;
}
public IMetadataBuilder ForType<TModel>()
{
var builder = new MetadataBuilder(typeof(TModel));
_builders.Add(builder);
return builder;
}
public IMetadataBuilder ForProperty(Type containerType, string propertyName)
{
var builder = new MetadataBuilder(containerType, propertyName);
_builders.Add(builder);
return builder;
}
public IMetadataBuilder ForProperty<TContainer>(string propertyName)
{
var builder = new MetadataBuilder(typeof(TContainer), propertyName);
_builders.Add(builder);
return builder;
}
public interface IMetadataBuilder
{
IMetadataBuilder Then(Action<ModelMetadata> action);
}
private class MetadataBuilder : IMetadataBuilder
{
private List<Action<ModelMetadata>> _actions = new List<Action<ModelMetadata>>();
private readonly Type _type;
private readonly Type _containerType;
private readonly string _propertyName;
public MetadataBuilder(Type type)
{
_type = type;
}
public MetadataBuilder(Type containerType, string propertyName)
{
_containerType = containerType;
_propertyName = propertyName;
}
public IMetadataBuilder Then(Action<ModelMetadata> action)
{
_actions.Add(action);
return this;
}
public void Apply(Type type, ModelMetadata metadata)
{
if (type == _type)
{
foreach (var action in _actions)
{
action(metadata);
}
}
}
public void Apply(Type containerType, string propertyName, ModelMetadata metadata)
{
if (containerType == _containerType && propertyName == _propertyName)
{
foreach (var action in _actions)
{
action(metadata);
}
}
}
}
}
}

View File

@ -193,12 +193,14 @@ namespace Microsoft.AspNet.Mvc.Core
// Assert
Assert.NotNull(viewData1.ModelMetadata);
Assert.NotNull(viewData1.ModelExplorer);
Assert.Equal(expectedType, viewData1.ModelMetadata.ModelType);
Assert.Equal(expectedType, viewData1.ModelMetadata.RealModelType);
Assert.Equal(expectedType, viewData1.ModelExplorer.ModelType);
Assert.NotNull(viewData2.ModelMetadata);
Assert.NotNull(viewData2.ModelExplorer);
Assert.Equal(expectedType, viewData2.ModelMetadata.ModelType);
Assert.Equal(expectedType, viewData2.ModelMetadata.RealModelType);
Assert.Equal(expectedType, viewData2.ModelExplorer.ModelType);
}
[Fact]

View File

@ -56,12 +56,12 @@ namespace Microsoft.AspNet.Mvc.Core
// Arrange
var metadataProvider = new Mock<IModelMetadataProvider>();
metadataProvider
.Setup(m => m.GetMetadataForType(It.IsAny<Func<object>>(), typeof(object)))
.Returns(new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(object)))
.Setup(m => m.GetMetadataForType(typeof(object)))
.Returns(new EmptyModelMetadataProvider().GetMetadataForType(typeof(object)))
.Verifiable();
metadataProvider
.Setup(m => m.GetMetadataForType(It.IsAny<Func<object>>(), typeof(TestModel)))
.Returns(new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(TestModel)))
.Setup(m => m.GetMetadataForType(typeof(TestModel)))
.Returns(new EmptyModelMetadataProvider().GetMetadataForType(typeof(TestModel)))
.Verifiable();
var modelState = new ModelStateDictionary();
var viewData = new TestViewDataDictionary(metadataProvider.Object, modelState);
@ -82,12 +82,12 @@ namespace Microsoft.AspNet.Mvc.Core
// Arrange
var metadataProvider = new Mock<IModelMetadataProvider>(MockBehavior.Strict);
metadataProvider
.Setup(m => m.GetMetadataForType(It.IsAny<Func<object>>(), typeof(object)))
.Returns(new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(object)))
.Setup(m => m.GetMetadataForType(typeof(object)))
.Returns(new EmptyModelMetadataProvider().GetMetadataForType(typeof(object)))
.Verifiable();
metadataProvider
.Setup(m => m.GetMetadataForType(It.IsAny<Func<object>>(), typeof(TestModel)))
.Returns(new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(TestModel)))
.Setup(m => m.GetMetadataForType(typeof(TestModel)))
.Returns(new EmptyModelMetadataProvider().GetMetadataForType(typeof(TestModel)))
.Verifiable();
var modelState = new ModelStateDictionary();
var viewData = new TestViewDataDictionary(metadataProvider.Object, modelState);
@ -100,13 +100,10 @@ namespace Microsoft.AspNet.Mvc.Core
Assert.NotNull(viewData.ModelMetadata);
// Verifies if the GetMetadataForType is called only once.
metadataProvider.Verify(
m => m.GetMetadataForType(It.IsAny<Func<object>>(), typeof(object)), Times.Once());
m => m.GetMetadataForType(typeof(object)), Times.Once());
// Verifies if GetMetadataForProperties and GetMetadataForProperty is not called.
metadataProvider.Verify(
m => m.GetMetadataForProperties(It.IsAny<Func<object>>(), typeof(object)), Times.Never());
metadataProvider.Verify(
m => m.GetMetadataForProperty(
It.IsAny<Func<object>>(), typeof(object), It.IsAny<string>()), Times.Never());
m => m.GetMetadataForProperties(typeof(object)), Times.Never());
}
public static TheoryData<object> SetModelData
@ -299,19 +296,14 @@ namespace Microsoft.AspNet.Mvc.Core
{
// Arrange
var metadataProvider = new EmptyModelMetadataProvider();
var metadata = metadataProvider.GetMetadataForType(() => null, typeof(object));
var source = new ViewDataDictionary(metadataProvider)
{
ModelMetadata = metadata,
};
var source = new ViewDataDictionary(metadataProvider);
// Act
var viewData1 = new ViewDataDictionary(source);
var viewData2 = new ViewDataDictionary(source, model: null);
// Assert
Assert.Same(metadata, viewData1.ModelMetadata);
Assert.Same(metadata, viewData2.ModelMetadata);
Assert.Same(viewData1.ModelMetadata, viewData2.ModelMetadata);
}
[Theory]
@ -326,11 +318,7 @@ namespace Microsoft.AspNet.Mvc.Core
{
// Arrange
var metadataProvider = new EmptyModelMetadataProvider();
var metadata = metadataProvider.GetMetadataForType(() => null, sourceType);
var source = new ViewDataDictionary(metadataProvider)
{
ModelMetadata = metadata,
};
var source = new ViewDataDictionary(metadataProvider);
// Act
var viewData1 = new ViewDataDictionary(source)
@ -342,33 +330,84 @@ namespace Microsoft.AspNet.Mvc.Core
// Assert
Assert.NotNull(viewData1.ModelMetadata);
Assert.Equal(expectedType, viewData1.ModelMetadata.ModelType);
Assert.Equal(expectedType, viewData1.ModelMetadata.RealModelType);
Assert.NotNull(viewData2.ModelMetadata);
Assert.Equal(expectedType, viewData2.ModelMetadata.ModelType);
Assert.Equal(expectedType, viewData2.ModelMetadata.RealModelType);
}
[Fact]
public void ModelSetter_UpdatesModelMetadata()
public void ModelSetter_SameType_UpdatesModelExplorer()
{
// Arrange
var metadataProvider = new EmptyModelMetadataProvider();
var viewData = new ViewDataDictionary(metadataProvider)
{
Model = "same-string",
Model = 3,
};
var originalMetadata = viewData.ModelMetadata;
var originalExplorer = viewData.ModelExplorer;
// Act
viewData.Model = "same-string";
viewData.Model = 5;
// Assert
Assert.NotNull(viewData.ModelMetadata);
Assert.Equal("same-string", viewData.Model);
Assert.Equal("same-string", viewData.ModelMetadata.Model);
Assert.Same(originalMetadata.ModelType, viewData.ModelMetadata.ModelType);
Assert.NotSame(originalMetadata, viewData.ModelMetadata);
Assert.NotNull(viewData.ModelExplorer);
Assert.Equal(5, viewData.Model);
Assert.Equal(5, viewData.ModelExplorer.Model);
Assert.Same(originalMetadata, viewData.ModelMetadata);
Assert.NotSame(originalExplorer, viewData.ModelExplorer);
}
[Fact]
public void ModelSetter_SameType_BoxedValueTypeUpdatesModelExplorer()
{
// Arrange
var metadataProvider = new EmptyModelMetadataProvider();
var viewData = new ViewDataDictionary(metadataProvider)
{
Model = 3,
};
var originalMetadata = viewData.ModelMetadata;
var originalExplorer = viewData.ModelExplorer;
// Act
viewData.Model = 3; // This is the same value, but it's in a different box.
// Assert
Assert.NotNull(viewData.ModelMetadata);
Assert.NotNull(viewData.ModelExplorer);
Assert.Equal(3, viewData.Model);
Assert.Equal(3, viewData.ModelExplorer.Model);
Assert.Same(originalMetadata, viewData.ModelMetadata);
Assert.NotSame(originalExplorer, viewData.ModelExplorer);
}
[Fact]
public void ModelSetter_SameModel_NoChanges()
{
// Arrange
var model = "Hello";
var metadataProvider = new EmptyModelMetadataProvider();
var viewData = new ViewDataDictionary(metadataProvider)
{
Model = model,
};
var originalMetadata = viewData.ModelMetadata;
var originalExplorer = viewData.ModelExplorer;
// Act
viewData.Model = model;
// Assert
Assert.NotNull(viewData.ModelMetadata);
Assert.Equal("Hello", viewData.Model);
Assert.Same(originalMetadata, viewData.ModelMetadata);
Assert.Same(originalExplorer, viewData.ModelExplorer);
}
public static TheoryData<object, string, object> Eval_EvaluatesExpressionsData

View File

@ -1354,7 +1354,7 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
};
var url = "http://localhost/dealers/43/update-vehicle?dealer.name=TestCarDealer&dealer.location=NE";
// Act
var response = await client.PostAsJsonAsync(url, postedContent);

View File

@ -88,7 +88,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
var metadataProvider = new EmptyModelMetadataProvider();
ModelBindingContext bindingContext = new ModelBindingContext
{
ModelMetadata = metadataProvider.GetMetadataForType(null, typeof(int[])),
ModelMetadata = metadataProvider.GetMetadataForType(typeof(int[])),
ModelName = "someName",
ValueProvider = valueProvider,
OperationBindingContext = new OperationBindingContext

View File

@ -131,7 +131,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
var bindingContext = new ModelBindingContext
{
ModelMetadata = metadataProvider.GetMetadataForType(null, modelType),
ModelMetadata = metadataProvider.GetMetadataForType(modelType),
ModelName = "someName",
ValueProvider = Mock.Of<IValueProvider>(),
ModelState = new ModelStateDictionary(),

View File

@ -36,9 +36,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
// Arrange
var context = new ModelBindingContext();
context.ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(
modelAccessor: null,
modelType: typeof(string));
context.ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(typeof(string));
var binder = new TestableBindingSourceModelBinder(BindingSource.Body);
@ -55,9 +53,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
// Arrange
var context = new ModelBindingContext();
context.ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(
modelAccessor: null,
modelType: typeof(string));
context.ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(typeof(string));
context.ModelMetadata.BinderMetadata = new ModelBinderAttribute()
{
@ -79,9 +75,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
// Arrange
var context = new ModelBindingContext();
context.ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(
modelAccessor: null,
modelType: typeof(string));
context.ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(typeof(string));
context.ModelMetadata.BinderMetadata = new ModelBinderAttribute()
{

View File

@ -118,7 +118,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
var metadataProvider = new EmptyModelMetadataProvider();
var bindingContext = new ModelBindingContext
{
ModelMetadata = metadataProvider.GetMetadataForType(null, modelType),
ModelMetadata = metadataProvider.GetMetadataForType(modelType),
ModelName = "foo",
ValueProvider = valueProvider,
OperationBindingContext = new OperationBindingContext

View File

@ -48,7 +48,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
var metadataProvider = new EmptyModelMetadataProvider();
ModelBindingContext bindingContext = new ModelBindingContext
{
ModelMetadata = metadataProvider.GetMetadataForType(null, modelType),
ModelMetadata = metadataProvider.GetMetadataForType(modelType),
ModelName = "someName",
ValueProvider = new SimpleHttpValueProvider(),
OperationBindingContext = new OperationBindingContext

View File

@ -103,8 +103,16 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
// Arrange
var binder = new CollectionModelBinder<int>();
var context = new ModelBindingContext()
{
OperationBindingContext = new OperationBindingContext()
{
MetadataProvider = new DataAnnotationsModelMetadataProvider(),
},
};
// Act
var boundCollection = await binder.BindSimpleCollection(bindingContext: null, rawValue: new object[0], culture: null);
var boundCollection = await binder.BindSimpleCollection(context, rawValue: new object[0], culture: null);
// Assert
Assert.NotNull(boundCollection);
@ -153,7 +161,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
var metadataProvider = new EmptyModelMetadataProvider();
var bindingContext = new ModelBindingContext
{
ModelMetadata = metadataProvider.GetMetadataForType(null, typeof(int)),
ModelMetadata = metadataProvider.GetMetadataForType(typeof(int)),
ModelName = "someName",
ValueProvider = valueProvider,
OperationBindingContext = new OperationBindingContext

View File

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

View File

@ -21,7 +21,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
var bindingContext = new ModelBindingContext
{
FallbackToEmptyPrefix = true,
ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(int)),
ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(typeof(int)),
ModelName = "someName",
ModelState = new ModelStateDictionary(),
ValueProvider = new SimpleHttpValueProvider
@ -68,7 +68,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
var bindingContext = new ModelBindingContext
{
FallbackToEmptyPrefix = true,
ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(List<int>)),
ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(typeof(List<int>)),
ModelName = "someName",
ModelState = new ModelStateDictionary(),
ValueProvider = new SimpleHttpValueProvider
@ -119,7 +119,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
var bindingContext = new ModelBindingContext
{
FallbackToEmptyPrefix = true,
ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(List<int>)),
ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(typeof(List<int>)),
ModelName = "someName",
ModelState = new ModelStateDictionary(),
ValueProvider = new SimpleHttpValueProvider
@ -157,7 +157,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
var bindingContext = new ModelBindingContext
{
FallbackToEmptyPrefix = true,
ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(List<int>)),
ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(typeof(List<int>)),
ModelName = "someName",
ModelState = new ModelStateDictionary(),
ValueProvider = new SimpleHttpValueProvider
@ -201,7 +201,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
var bindingContext = new ModelBindingContext
{
FallbackToEmptyPrefix = false,
ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(List<int>)),
ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(typeof(List<int>)),
};
// Act
@ -223,7 +223,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
var bindingContext = new ModelBindingContext
{
FallbackToEmptyPrefix = true,
ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(int)),
ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(typeof(int)),
ModelState = new ModelStateDictionary(),
OperationBindingContext = Mock.Of<OperationBindingContext>(),
};
@ -275,6 +275,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
{ "friends[1].lastName", "name"},
{ "resume", "4+mFeTp3tPF=" }
};
var bindingContext = CreateBindingContext(binder, valueProvider, typeof(Person));
// Act
@ -305,7 +306,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
var bindingContext = new ModelBindingContext
{
FallbackToEmptyPrefix = true,
ModelMetadata = metadataProvider.GetMetadataForType(null, type),
ModelMetadata = metadataProvider.GetMetadataForType(type),
ModelState = new ModelStateDictionary(),
ValueProvider = valueProvider,
OperationBindingContext = new OperationBindingContext

View File

@ -18,7 +18,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
var metadataProvider = new EmptyModelMetadataProvider();
ModelBindingContext bindingContext = new ModelBindingContext
{
ModelMetadata = metadataProvider.GetMetadataForType(null, typeof(IDictionary<int, string>)),
ModelMetadata = metadataProvider.GetMetadataForType(typeof(IDictionary<int, string>)),
ModelName = "someName",
ValueProvider = new SimpleHttpValueProvider
{

View File

@ -116,7 +116,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var metadataProvider = new EmptyModelMetadataProvider();
var bindingContext = new ModelBindingContext
{
ModelMetadata = metadataProvider.GetMetadataForType(null, modelType),
ModelMetadata = metadataProvider.GetMetadataForType(modelType),
ModelName = "file",
OperationBindingContext = new OperationBindingContext
{

View File

@ -154,7 +154,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var metadataProvider = new EmptyModelMetadataProvider();
var bindingContext = new ModelBindingContext
{
ModelMetadata = metadataProvider.GetMetadataForType(null, modelType),
ModelMetadata = metadataProvider.GetMetadataForType(modelType),
ModelName = "file",
OperationBindingContext = new OperationBindingContext
{

View File

@ -77,7 +77,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
var metadataProvider = new EmptyModelMetadataProvider();
var bindingContext = new ModelBindingContext
{
ModelMetadata = metadataProvider.GetMetadataForType(null, modelType),
ModelMetadata = metadataProvider.GetMetadataForType(modelType),
ModelName = "modelName",
OperationBindingContext = new OperationBindingContext
{

View File

@ -149,7 +149,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
var metataProvider = new EmptyModelMetadataProvider();
var bindingContext = new ModelBindingContext
{
ModelMetadata = metataProvider.GetMetadataForType(null, keyValuePairType ?? typeof(KeyValuePair<int, string>)),
ModelMetadata = metataProvider.GetMetadataForType(keyValuePairType ?? typeof(KeyValuePair<int, string>)),
ModelName = "someName",
ValueProvider = valueProvider,
OperationBindingContext = new OperationBindingContext

View File

@ -1,8 +1,6 @@
// 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.Testing;
using Xunit;
namespace Microsoft.AspNet.Mvc.ModelBinding.Test
@ -15,13 +13,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
// Arrange
var originalBindingContext = new ModelBindingContext
{
ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(object)),
ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(typeof(object)),
ModelName = "theName",
ModelState = new ModelStateDictionary(),
ValueProvider = new SimpleHttpValueProvider()
};
var newModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(object));
var newModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(typeof(object));
// Act
var newBindingContext = new ModelBindingContext(originalBindingContext, string.Empty, newModelMetadata);
@ -39,7 +37,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
// Act
var bindingContext = new ModelBindingContext
{
ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(() => 42, typeof(int))
ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(typeof(int))
};
// Assert

View File

@ -343,7 +343,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var mockDtoBinder = new Mock<IModelBinder>();
var bindingContext = new ModelBindingContext
{
ModelMetadata = GetMetadataForObject(new Person()),
ModelMetadata = GetMetadataForType(typeof(Person)),
ModelName = "someName",
ValueProvider = mockValueProvider.Object,
OperationBindingContext = new OperationBindingContext
@ -359,12 +359,18 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
.Returns((ModelBindingContext mbc) =>
{
// just return the DTO unchanged
return Task.FromResult(new ModelBindingResult(mbc.ModelMetadata.Model, mbc.ModelName, true));
return Task.FromResult(new ModelBindingResult(mbc.Model, mbc.ModelName, true));
});
var model = new Person();
var testableBinder = new Mock<TestableMutableObjectModelBinder> { CallBase = true };
testableBinder.Setup(o => o.EnsureModelPublic(bindingContext)).Verifiable();
testableBinder.Setup(o => o.GetMetadataForProperties(bindingContext))
testableBinder
.Setup(o => o.EnsureModelPublic(bindingContext))
.Callback<ModelBindingContext>(c => c.Model = model)
.Verifiable();
testableBinder
.Setup(o => o.GetMetadataForProperties(bindingContext))
.Returns(new ModelMetadata[0]);
// Act
@ -373,7 +379,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// Assert
Assert.NotNull(retValue);
Assert.True(retValue.IsModelSet);
Assert.NotNull(retValue.Model);
Assert.Same(model, retValue.Model);
Assert.IsType<Person>(retValue.Model);
testableBinder.Verify();
}
@ -389,7 +395,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var mockDtoBinder = new Mock<IModelBinder>();
var bindingContext = new ModelBindingContext
{
ModelMetadata = GetMetadataForObject(new Person()),
ModelMetadata = GetMetadataForType(typeof(Person)),
ModelName = "",
ValueProvider = mockValueProvider.Object,
OperationBindingContext = new OperationBindingContext
@ -405,13 +411,20 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
.Returns((ModelBindingContext mbc) =>
{
// just return the DTO unchanged
return Task.FromResult(new ModelBindingResult(mbc.ModelMetadata.Model, mbc.ModelName, true));
return Task.FromResult(new ModelBindingResult(mbc.Model, mbc.ModelName, true));
});
var model = new Person();
var testableBinder = new Mock<TestableMutableObjectModelBinder> { CallBase = true };
testableBinder.Setup(o => o.EnsureModelPublic(bindingContext)).Verifiable();
testableBinder.Setup(o => o.GetMetadataForProperties(bindingContext))
.Returns(new ModelMetadata[0]);
testableBinder
.Setup(o => o.EnsureModelPublic(bindingContext))
.Callback<ModelBindingContext>(c => c.Model = model)
.Verifiable();
testableBinder
.Setup(o => o.GetMetadataForProperties(bindingContext))
.Returns(new ModelMetadata[0]);
// Act
var retValue = await testableBinder.Object.BindModelAsync(bindingContext);
@ -419,7 +432,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// Assert
Assert.NotNull(retValue);
Assert.True(retValue.IsModelSet);
Assert.NotNull(retValue.Model);
Assert.Same(model, retValue.Model);
Assert.IsType<Person>(retValue.Model);
testableBinder.Verify();
}
@ -513,18 +526,18 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// Arrange
var bindingContext = new ModelBindingContext
{
ModelMetadata = GetMetadataForObject(new Person())
Model = new Person(),
ModelMetadata = GetMetadataForType(typeof(Person))
};
var testableBinder = new Mock<TestableMutableObjectModelBinder> { CallBase = true };
// Act
var originalModel = bindingContext.ModelMetadata.Model;
var originalModel = bindingContext.Model;
testableBinder.Object.EnsureModelPublic(bindingContext);
var newModel = bindingContext.ModelMetadata.Model;
// Assert
Assert.Same(originalModel, newModel);
Assert.Same(originalModel, bindingContext.Model);
testableBinder.Verify(o => o.CreateModelPublic(bindingContext), Times.Never());
}
@ -542,9 +555,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
.Returns(new Person()).Verifiable();
// Act
var originalModel = bindingContext.ModelMetadata.Model;
var originalModel = bindingContext.Model;
testableBinder.Object.EnsureModelPublic(bindingContext);
var newModel = bindingContext.ModelMetadata.Model;
var newModel = bindingContext.Model;
// Assert
Assert.Null(originalModel);
@ -672,7 +685,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// Arrange
var bindingContext = new ModelBindingContext
{
ModelMetadata = GetMetadataForObject(new ModelWithMixedBindingBehaviors()),
ModelMetadata = GetMetadataForType(typeof(ModelWithMixedBindingBehaviors)),
OperationBindingContext = new OperationBindingContext
{
ValidatorProvider = Mock.Of<IModelValidatorProvider>()
@ -698,13 +711,15 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
Age = -20
};
var containerMetadata = GetMetadataForObject(model);
var containerMetadata = GetMetadataForType(model.GetType());
var bindingContext = new ModelBindingContext
{
Model = model,
ModelMetadata = containerMetadata,
ModelName = "theModel",
OperationBindingContext = new OperationBindingContext
{
MetadataProvider = new DataAnnotationsModelMetadataProvider(),
ValidatorProvider = Mock.Of<IModelValidatorProvider>()
}
};
@ -746,23 +761,19 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
Age = -20
};
var containerMetadata = GetMetadataForObject(model);
var containerMetadata = GetMetadataForType(model.GetType());
var bindingContext = new ModelBindingContext()
{
Model = model,
ModelMetadata = containerMetadata,
ModelName = "theModel",
ModelState = new ModelStateDictionary(),
OperationBindingContext = new OperationBindingContext
{
MetadataProvider = new DataAnnotationsModelMetadataProvider(),
ValidatorProvider = Mock.Of<IModelValidatorProvider>()
}
};
var validationContext = new ModelValidationContext("theModel",
bindingContext.OperationBindingContext
.ValidatorProvider,
bindingContext.ModelState,
containerMetadata,
null);
var dto = new ComplexModelDto(containerMetadata, containerMetadata.Properties);
var testableBinder = new TestableMutableObjectModelBinder();
@ -806,8 +817,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
// Arrange
var model = new ModelWithRequired();
var containerMetadata = GetMetadataForObject(model);
var bindingContext = CreateContext(containerMetadata);
var containerMetadata = GetMetadataForType(model.GetType());
var bindingContext = CreateContext(containerMetadata, model);
// Set no properties though Age (a non-Nullable struct) and City (a class) properties are required.
var dto = new ComplexModelDto(containerMetadata, containerMetadata.Properties);
@ -847,8 +859,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
// Arrange
var model = new ModelWithRequired();
var containerMetadata = GetMetadataForObject(model);
var bindingContext = CreateContext(containerMetadata);
var containerMetadata = GetMetadataForType(model.GetType());
var bindingContext = CreateContext(containerMetadata, model);
var dto = new ComplexModelDto(containerMetadata, containerMetadata.Properties);
var testableBinder = new TestableMutableObjectModelBinder();
@ -890,8 +902,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
// Arrange
var model = new Person();
var containerMetadata = GetMetadataForObject(model);
var bindingContext = CreateContext(containerMetadata);
var containerMetadata = GetMetadataForType(model.GetType());
var bindingContext = CreateContext(containerMetadata, model);
// Set no properties though ValueTypeRequired (a non-Nullable struct) property is required.
var dto = new ComplexModelDto(containerMetadata, containerMetadata.Properties);
@ -920,9 +932,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
// Arrange
var model = new Person();
var containerMetadata = GetMetadataForObject(model);
var containerMetadata = GetMetadataForType(model.GetType());
var bindingContext = CreateContext(containerMetadata);
var bindingContext = CreateContext(containerMetadata, model);
var dto = new ComplexModelDto(containerMetadata, containerMetadata.Properties);
var testableBinder = new TestableMutableObjectModelBinder();
@ -961,9 +973,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
DateOfBirth = dob
};
var containerMetadata = GetMetadataForObject(model);
var containerMetadata = GetMetadataForType(model.GetType());
var bindingContext = CreateContext(containerMetadata);
var bindingContext = CreateContext(containerMetadata, model);
var dto = new ComplexModelDto(containerMetadata, containerMetadata.Properties);
var firstNameProperty = dto.PropertyMetadata.Single(o => o.PropertyName == "FirstName");
@ -997,9 +1009,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public void SetProperty_PropertyHasDefaultValue_SetsDefaultValue()
{
// Arrange
var bindingContext = CreateContext(GetMetadataForObject(new Person()));
var model = new Person();
var bindingContext = CreateContext(GetMetadataForType(model.GetType()), model);
var propertyMetadata = bindingContext.ModelMetadata.Properties.First(o => o.PropertyName == "PropertyWithDefaultValue");
var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(Person), model);
var propertyMetadata = bindingContext.ModelMetadata.Properties["PropertyWithDefaultValue"];
var dtoResult = new ModelBindingResult(
model: null,
@ -1014,10 +1029,15 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var testableBinder = new TestableMutableObjectModelBinder();
// Act
testableBinder.SetProperty(bindingContext, propertyMetadata, dtoResult, requiredValidator);
testableBinder.SetProperty(
bindingContext,
modelExplorer,
propertyMetadata,
dtoResult,
requiredValidator);
// Assert
var person = Assert.IsType<Person>(bindingContext.ModelMetadata.Model);
var person = Assert.IsType<Person>(bindingContext.Model);
Assert.Equal(123.456m, person.PropertyWithDefaultValue);
Assert.True(bindingContext.ModelState.IsValid);
}
@ -1026,10 +1046,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public void SetProperty_PropertyIsPreinitialized_NoValue_DoesNothing()
{
// Arrange
var bindingContext = CreateContext(GetMetadataForObject(new Person()));
var model = new Person();
var bindingContext = CreateContext(GetMetadataForType(model.GetType()), model);
var propertyMetadata = bindingContext.ModelMetadata.Properties.Single(
o => o.PropertyName == "PropertyWithInitializedValue");
var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(Person), model);
var propertyMetadata = bindingContext.ModelMetadata.Properties["PropertyWithInitializedValue"];
// This value won't be used because IsModelBound = false.
var dtoResult = new ModelBindingResult(
@ -1040,10 +1062,15 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var testableBinder = new TestableMutableObjectModelBinder();
// Act
testableBinder.SetProperty(bindingContext, propertyMetadata, dtoResult, requiredValidator: null);
testableBinder.SetProperty(
bindingContext,
modelExplorer,
propertyMetadata,
dtoResult,
requiredValidator: null);
// Assert
var person = Assert.IsType<Person>(bindingContext.ModelMetadata.Model);
var person = Assert.IsType<Person>(bindingContext.Model);
Assert.Equal("preinitialized", person.PropertyWithInitializedValue);
Assert.True(bindingContext.ModelState.IsValid);
}
@ -1052,10 +1079,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public void SetProperty_PropertyIsPreinitialized_WithDefaultValue_NoValue_CallsSetter()
{
// Arrange
var bindingContext = CreateContext(GetMetadataForObject(new Person()));
var model = new Person();
var bindingContext = CreateContext(GetMetadataForType(model.GetType()), model);
var propertyMetadata = bindingContext.ModelMetadata.Properties.Single(
o => o.PropertyName == "PropertyWithInitializedValueAndDefault");
var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(Person), model);
var propertyMetadata = bindingContext.ModelMetadata.Properties["PropertyWithInitializedValueAndDefault"];
// This value won't be used because IsModelBound = false.
var dtoResult = new ModelBindingResult(
@ -1066,10 +1095,15 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var testableBinder = new TestableMutableObjectModelBinder();
// Act
testableBinder.SetProperty(bindingContext, propertyMetadata, dtoResult, requiredValidator: null);
testableBinder.SetProperty(
bindingContext,
modelExplorer,
propertyMetadata,
dtoResult,
requiredValidator: null);
// Assert
var person = Assert.IsType<Person>(bindingContext.ModelMetadata.Model);
var person = Assert.IsType<Person>(bindingContext.Model);
Assert.Equal("default", person.PropertyWithInitializedValueAndDefault);
Assert.True(bindingContext.ModelState.IsValid);
}
@ -1078,8 +1112,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public void SetProperty_PropertyIsReadOnly_DoesNothing()
{
// Arrange
var bindingContext = CreateContext(GetMetadataForObject(new Person()));
var propertyMetadata = bindingContext.ModelMetadata.Properties.Single(o => o.PropertyName == "NonUpdateableProperty");
var model = new Person();
var bindingContext = CreateContext(GetMetadataForType(model.GetType()), model);
var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(Person), model);
var propertyMetadata = bindingContext.ModelMetadata.Properties["NonUpdateableProperty"];
var dtoResult = new ModelBindingResult(
model: null,
@ -1089,7 +1127,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var testableBinder = new TestableMutableObjectModelBinder();
// Act
testableBinder.SetProperty(bindingContext, propertyMetadata, dtoResult, requiredValidator: null);
testableBinder.SetProperty(
bindingContext,
modelExplorer,
propertyMetadata,
dtoResult,
requiredValidator: null);
// Assert
// If didn't throw, success!
@ -1100,26 +1143,32 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
// Arrange
var model = new Person();
var bindingContext = CreateContext(GetMetadataForObject(model));
var bindingContext = CreateContext(GetMetadataForType(model.GetType()), model);
var propertyMetadata = bindingContext.ModelMetadata.Properties.Single(o => o.PropertyName == "DateOfBirth");
var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(Person), model);
var propertyMetadata = bindingContext.ModelMetadata.Properties["DateOfBirth"];
var dtoResult = new ModelBindingResult(
new DateTime(2001, 1, 1),
key: "foo",
isModelSet: true);
var requiredValidator = bindingContext.OperationBindingContext
.ValidatorProvider
.GetValidators(propertyMetadata)
.FirstOrDefault(v => v.IsRequired);
var validationContext = new ModelValidationContext(bindingContext, propertyMetadata);
var testableBinder = new TestableMutableObjectModelBinder();
// Act
testableBinder.SetProperty(bindingContext, propertyMetadata, dtoResult, requiredValidator);
testableBinder.SetProperty(
bindingContext,
modelExplorer,
propertyMetadata,
dtoResult,
requiredValidator);
// Assert
Assert.True(bindingContext.ModelState.IsValid);
@ -1135,9 +1184,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
DateOfBirth = new DateTime(1900, 1, 1)
};
var bindingContext = CreateContext(GetMetadataForObject(model));
var propertyMetadata = bindingContext.ModelMetadata.Properties.Single(o => o.PropertyName == "DateOfDeath");
var bindingContext = CreateContext(GetMetadataForType(model.GetType()), model);
var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(Person), model);
var propertyMetadata = bindingContext.ModelMetadata.Properties["DateOfDeath"];
var dtoResult = new ModelBindingResult(
new DateTime(1800, 1, 1),
isModelSet: true,
@ -1146,7 +1199,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var testableBinder = new TestableMutableObjectModelBinder();
// Act
testableBinder.SetProperty(bindingContext, propertyMetadata, dtoResult, requiredValidator: null);
testableBinder.SetProperty(
bindingContext,
modelExplorer,
propertyMetadata,
dtoResult,
requiredValidator: null);
// Assert
Assert.Equal("Date of death can't be before date of birth." + Environment.NewLine
@ -1158,21 +1216,29 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public void SetProperty_SettingNonNullableValueTypeToNull_RequiredValidatorNotPresent_RegistersValidationCallback()
{
// Arrange
var bindingContext = CreateContext(GetMetadataForObject(new Person()));
var model = new Person();
var bindingContext = CreateContext(GetMetadataForType(model.GetType()), model);
var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(Person), model);
var propertyMetadata = bindingContext.ModelMetadata.Properties["DateOfBirth"];
var propertyMetadata = bindingContext.ModelMetadata.Properties.Single(o => o.PropertyName == "DateOfBirth");
var dtoResult = new ModelBindingResult(
model: null,
isModelSet: true,
key: "foo");
var requiredValidator = GetRequiredValidator(bindingContext, propertyMetadata);
var validationContext = new ModelValidationContext(bindingContext, propertyMetadata);
var testableBinder = new TestableMutableObjectModelBinder();
// Act
testableBinder.SetProperty(bindingContext, propertyMetadata, dtoResult, requiredValidator);
testableBinder.SetProperty(
bindingContext,
modelExplorer,
propertyMetadata,
dtoResult,
requiredValidator);
// Assert
Assert.False(bindingContext.ModelState.IsValid);
@ -1182,10 +1248,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public void SetProperty_SettingNonNullableValueTypeToNull_RequiredValidatorPresent_AddsModelError()
{
// Arrange
var bindingContext = CreateContext(GetMetadataForObject(new Person()));
var model = new Person();
var bindingContext = CreateContext(GetMetadataForType(model.GetType()), model);
bindingContext.ModelName = " foo";
var propertyMetadata = bindingContext.ModelMetadata.Properties.Single(o => o.PropertyName == "ValueTypeRequired");
var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(Person), model);
var propertyMetadata = bindingContext.ModelMetadata.Properties["ValueTypeRequired"];
var dtoResult = new ModelBindingResult(
model: null,
isModelSet: true,
@ -1196,7 +1266,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var testableBinder = new TestableMutableObjectModelBinder();
// Act
testableBinder.SetProperty(bindingContext, propertyMetadata, dtoResult, requiredValidator);
testableBinder.SetProperty(
bindingContext,
modelExplorer,
propertyMetadata,
dtoResult,
requiredValidator);
// Assert
Assert.False(bindingContext.ModelState.IsValid);
@ -1208,10 +1283,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public void SetProperty_SettingNullableTypeToNull_RequiredValidatorNotPresent_PropertySetterThrows_AddsRequiredMessageString()
{
// Arrange
var bindingContext = CreateContext(GetMetadataForObject(new ModelWhosePropertySetterThrows()));
var model = new ModelWhosePropertySetterThrows();
var bindingContext = CreateContext(GetMetadataForType(model.GetType()), model);
bindingContext.ModelName = "foo";
var propertyMetadata = bindingContext.ModelMetadata.Properties.Single(o => o.PropertyName == "NameNoAttribute");
var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(Person), model);
var propertyMetadata = bindingContext.ModelMetadata.Properties["NameNoAttribute"];
var dtoResult = new ModelBindingResult(
model: null,
isModelSet: true,
@ -1222,7 +1301,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var testableBinder = new TestableMutableObjectModelBinder();
// Act
testableBinder.SetProperty(bindingContext, propertyMetadata, dtoResult, requiredValidator);
testableBinder.SetProperty(
bindingContext,
modelExplorer,
propertyMetadata,
dtoResult,
requiredValidator);
// Assert
Assert.False(bindingContext.ModelState.IsValid);
@ -1236,11 +1320,16 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public void SetProperty_SettingNullableTypeToNull_RequiredValidatorPresent_PropertySetterThrows_AddsRequiredMessageString()
{
// Arrange
var bindingContext = CreateContext(GetMetadataForObject(new ModelWhosePropertySetterThrows()));
var model = new ModelWhosePropertySetterThrows();
var bindingContext = CreateContext(GetMetadataForType(model.GetType()), model);
bindingContext.ModelName = "foo";
var propertyMetadata = bindingContext.ModelMetadata.Properties.Single(o => o.PropertyName == "Name");
var dtoResult = new ModelBindingResult(model: null,
var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(Person), model);
var propertyMetadata = bindingContext.ModelMetadata.Properties["Name"];
var dtoResult = new ModelBindingResult(
model: null,
isModelSet: true,
key: "foo.Name");
@ -1249,7 +1338,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var testableBinder = new TestableMutableObjectModelBinder();
// Act
testableBinder.SetProperty(bindingContext, propertyMetadata, dtoResult, requiredValidator);
testableBinder.SetProperty(
bindingContext,
modelExplorer,
propertyMetadata,
dtoResult,
requiredValidator);
// Assert
Assert.False(bindingContext.ModelState.IsValid);
@ -1257,7 +1351,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
Assert.Equal("This message comes from the [Required] attribute.", error.ErrorMessage);
}
private static ModelBindingContext CreateContext(ModelMetadata metadata)
private static ModelBindingContext CreateContext(ModelMetadata metadata, object model)
{
var providers = new IModelValidatorProvider[]
{
@ -1267,11 +1361,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
return new ModelBindingContext
{
Model = model,
ModelState = new ModelStateDictionary(),
ModelMetadata = metadata,
ModelName = "theModel",
OperationBindingContext = new OperationBindingContext
{
MetadataProvider = new DataAnnotationsModelMetadataProvider(),
ValidatorProvider = new CompositeModelValidatorProvider(providers)
}
};
@ -1288,26 +1384,19 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
private static ModelMetadata GetMetadataForCanUpdateProperty(string propertyName)
{
var metadataProvider = new DataAnnotationsModelMetadataProvider();
return metadataProvider.GetMetadataForProperty(null, typeof(MyModelTestingCanUpdateProperty), propertyName);
}
private static ModelMetadata GetMetadataForObject(object o)
{
var metadataProvider = new DataAnnotationsModelMetadataProvider();
return metadataProvider.GetMetadataForType(() => o, o.GetType());
return metadataProvider.GetMetadataForProperty(typeof(MyModelTestingCanUpdateProperty), propertyName);
}
private static ModelMetadata GetMetadataForType(Type t)
{
var metadataProvider = new DataAnnotationsModelMetadataProvider();
return metadataProvider.GetMetadataForType(null, t);
return metadataProvider.GetMetadataForType(t);
}
private static ModelMetadata GetMetadataForParameter(MethodInfo methodInfo, string parameterName)
{
var metadataProvider = new DataAnnotationsModelMetadataProvider();
return metadataProvider.GetMetadataForParameter(
modelAccessor: null,
methodInfo: methodInfo,
parameterName: parameterName);
}
@ -1548,12 +1637,19 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
return base.GetMetadataForProperties(bindingContext);
}
public new void SetProperty(ModelBindingContext bindingContext,
ModelMetadata propertyMetadata,
ModelBindingResult dtoResult,
IModelValidator requiredValidator)
public new void SetProperty(
ModelBindingContext bindingContext,
ModelExplorer modelExplorer,
ModelMetadata propertyMetadata,
ModelBindingResult dtoResult,
IModelValidator requiredValidator)
{
base.SetProperty(bindingContext, propertyMetadata, dtoResult, requiredValidator);
base.SetProperty(
bindingContext,
modelExplorer,
propertyMetadata,
dtoResult,
requiredValidator);
}
}
}

View File

@ -152,7 +152,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
{
return new ModelBindingContext
{
ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, modelType),
ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(modelType),
ModelName = "theModelName",
ValueProvider = new SimpleHttpValueProvider() // empty
};

View File

@ -26,7 +26,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// Act
// Call ToList() to force the lazy evaluation to evaluate
provider.GetMetadataForProperties(model, typeof(PropertyModel)).ToList();
provider.GetMetadataForProperties(typeof(PropertyModel)).ToList();
// Assert
var local = Assert.Single(
@ -53,18 +53,15 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public void GetMetadataForProperties_ExcludesIndexers()
{
// Arrange
var value = "some value";
var model = new ModelWithIndexer { Value = value };
var model = new ModelWithIndexer();
var provider = new TestableAssociatedMetadataProvider();
var modelType = model.GetType();
// Act
provider.GetMetadataForProperties(model, modelType).ToList();
provider.GetMetadataForProperties(modelType).ToList();
// Assert
Assert.Equal(2, provider.CreateMetadataFromPrototypeLog.Count);
Assert.Equal(value, provider.CreateMetadataFromPrototypeLog[0].Model);
Assert.Null(provider.CreateMetadataFromPrototypeLog[1].Model);
var valueMetadata = Assert.Single(
provider.CreateMetadataPrototypeLog,
@ -78,23 +75,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
Assert.Equal(typeof(string), testPropertyMetadata.ModelType);
}
[Fact]
public void GetMetadataForPropertyWithNullContainerReturnsMetadataWithNullValuesForProperties()
{
// Arrange
var provider = new TestableAssociatedMetadataProvider();
// Act
provider.GetMetadataForProperties(null, typeof(PropertyModel)).ToList(); // Call ToList() to force the lazy evaluation to evaluate
// Assert
Assert.NotEmpty(provider.CreateMetadataFromPrototypeLog);
foreach (var parms in provider.CreateMetadataFromPrototypeLog)
{
Assert.Null(parms.Model);
}
}
[Fact]
public void GetMetadataForParameterNullOrEmptyPropertyNameThrows()
{
@ -103,56 +83,30 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// Act & Assert
ExceptionAssert.ThrowsArgumentNullOrEmpty(
() => provider.GetMetadataForParameter(modelAccessor: null, methodInfo: null, parameterName: null),
() => provider.GetMetadataForParameter(methodInfo: null, parameterName: null),
"parameterName");
ExceptionAssert.ThrowsArgumentNullOrEmpty(
() => provider.GetMetadataForParameter(modelAccessor: null, methodInfo: null, parameterName: null),
() => provider.GetMetadataForParameter(methodInfo: null, parameterName: null),
"parameterName");
}
// GetMetadataForProperty
// GetMetadata and access metadata for a property
[Fact]
public void GetMetadataForPropertyNullOrEmptyPropertyNameThrows()
public void GetMetadataForProperty_WithLocalAttributes()
{
// Arrange
var provider = new TestableAssociatedMetadataProvider();
var metadata = new ModelMetadata(provider, null, typeof(PropertyModel), null);
// Act & Assert
ExceptionAssert.ThrowsArgumentNullOrEmpty(
() => provider.GetMetadataForProperty(modelAccessor: null, containerType: typeof(object), propertyName: null),
"propertyName");
ExceptionAssert.ThrowsArgumentNullOrEmpty(
() => provider.GetMetadataForProperty(modelAccessor: null, containerType: typeof(object), propertyName: String.Empty),
"propertyName");
}
[Fact]
public void GetMetadataForPropertyInvalidPropertyNameThrows()
{
// Arrange
var provider = new TestableAssociatedMetadataProvider();
// Act & Assert
ExceptionAssert.ThrowsArgument(
() => provider.GetMetadataForProperty(modelAccessor: null, containerType: typeof(object), propertyName: "BadPropertyName"),
"propertyName",
"The property System.Object.BadPropertyName could not be found.");
}
[Fact]
public void GetMetadataForPropertyWithLocalAttributes()
{
// Arrange
var provider = new TestableAssociatedMetadataProvider();
var metadata = new ModelMetadata(provider, typeof(PropertyModel), null, typeof(int), "LocalAttributes");
provider.CreateMetadataFromPrototypeReturnValue = metadata;
var propertyMetadata = new ModelMetadata(provider, typeof(PropertyModel), typeof(int), "LocalAttributes");
provider.CreateMetadataFromPrototypeReturnValue = propertyMetadata;
// Act
var result = provider.GetMetadataForProperty(null, typeof(PropertyModel), "LocalAttributes");
var result = provider.GetMetadataForProperty(typeof(PropertyModel), "LocalAttributes");
// Assert
Assert.Same(metadata, result);
Assert.Same(propertyMetadata, result);
var localAttributes = Assert.Single(
provider.CreateMetadataPrototypeLog,
parameters => parameters.PropertyName == "LocalAttributes");
@ -160,18 +114,20 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
}
[Fact]
public void GetMetadataForPropertyWithMetadataAttributes()
public void GetMetadataForProperty_WithMetadataAttributes()
{
// Arrange
var provider = new TestableAssociatedMetadataProvider();
var metadata = new ModelMetadata(provider, typeof(PropertyModel), null, typeof(string), "MetadataAttributes");
provider.CreateMetadataFromPrototypeReturnValue = metadata;
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 = provider.GetMetadataForProperty(null, typeof(PropertyModel), "MetadataAttributes");
var result = metadata.Properties["MetadataAttributes"];
// Assert
Assert.Same(metadata, result);
Assert.Same(propertyMetadata, result);
var parmaters = Assert.Single(
provider.CreateMetadataPrototypeLog,
p => p.PropertyName == "MetadataAttributes");
@ -179,18 +135,20 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
}
[Fact]
public void GetMetadataForPropertyWithMixedAttributes()
public void GetMetadataForProperty_WithMixedAttributes()
{
// Arrange
var provider = new TestableAssociatedMetadataProvider();
var metadata = new ModelMetadata(provider, typeof(PropertyModel), null, typeof(double), "MixedAttributes");
provider.CreateMetadataFromPrototypeReturnValue = metadata;
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 = provider.GetMetadataForProperty(null, typeof(PropertyModel), "MixedAttributes");
var result = metadata.Properties["MixedAttributes"];
// Assert
Assert.Same(metadata, result);
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);
@ -203,11 +161,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public void GetMetadataForTypeIncludesAttributesOnType()
{
var provider = new TestableAssociatedMetadataProvider();
var metadata = new ModelMetadata(provider, null, null, typeof(TypeModel), null);
var metadata = new ModelMetadata(provider, null, typeof(TypeModel), null);
provider.CreateMetadataFromPrototypeReturnValue = metadata;
// Act
var result = provider.GetMetadataForType(null, typeof(TypeModel));
var result = provider.GetMetadataForType(typeof(TypeModel));
// Assert
Assert.Same(metadata, result);
@ -216,23 +174,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
}
#endif
[Fact]
public void GetMetadataForProperties_SetsContainerAsExpected()
{
// Arrange
var model = new PropertyModel { LocalAttributes = 42, MetadataAttributes = "hello", MixedAttributes = 21.12 };
var provider = new EmptyModelMetadataProvider();
// Act
var metadata = provider.GetMetadataForProperties(model, typeof(PropertyModel)).ToList();
// Assert
Assert.Equal(3, metadata.Count);
Assert.Same(model, metadata[0].Container);
Assert.Same(model, metadata[1].Container);
Assert.Same(model, metadata[2].Container);
}
// Helpers
private class PropertyModel
@ -292,12 +233,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
return CreateMetadataPrototypeReturnValue;
}
protected override ModelMetadata CreateMetadataFromPrototype(ModelMetadata prototype, Func<object> modelAccessor)
protected override ModelMetadata CreateMetadataFromPrototype(ModelMetadata prototype)
{
CreateMetadataFromPrototypeLog.Add(new CreateMetadataFromPrototypeParams
{
Prototype = prototype,
Model = modelAccessor == null ? null : modelAccessor()
});
return CreateMetadataFromPrototypeReturnValue;
@ -315,7 +255,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
private class CreateMetadataFromPrototypeParams
{
public ModelMetadata Prototype { get; set; }
public object Model { get; set; }
}
}
}

View File

@ -24,7 +24,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var expected = new[] { "IsAdmin", "UserName" };
// Act
var metadata = provider.GetMetadataForType(null, type);
var metadata = provider.GetMetadataForType(type);
// Assert
var predicate = metadata.PropertyBindingPredicateProvider.PropertyFilter;
@ -59,7 +59,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// Act
var metadata = provider.GetMetadataForParameter(
modelAccessor: null,
methodInfo: methodInfo,
parameterName: "param");
@ -97,7 +96,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// Act
var metadata = provider.GetMetadataForParameter(
modelAccessor: null,
methodInfo: methodInfo,
parameterName: "param");
@ -130,7 +128,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// Act
var metadata = provider.GetMetadataForParameter(
modelAccessor: null,
methodInfo: methodInfo,
parameterName: "param");
@ -146,7 +143,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var provider = new DataAnnotationsModelMetadataProvider();
// Act
var metadata = provider.GetMetadataForType(null, type);
var metadata = provider.GetMetadataForType(type);
// Assert
Assert.Equal("TypePrefix", metadata.BinderModelName);
@ -161,9 +158,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var provider = new DataAnnotationsModelMetadataProvider();
// Act & Assert
Assert.True(provider.GetMetadataForProperty(null, type, "NoAttribute").ShowForDisplay);
Assert.True(provider.GetMetadataForProperty(null, type, "ScaffoldColumnTrue").ShowForDisplay);
Assert.False(provider.GetMetadataForProperty(null, type, "ScaffoldColumnFalse").ShowForDisplay);
Assert.True(provider.GetMetadataForProperty(type, "NoAttribute").ShowForDisplay);
Assert.True(provider.GetMetadataForProperty(type, "ScaffoldColumnTrue").ShowForDisplay);
Assert.False(provider.GetMetadataForProperty(type, "ScaffoldColumnFalse").ShowForDisplay);
}
[Fact]
@ -174,9 +171,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var provider = new DataAnnotationsModelMetadataProvider();
// Act & Assert
Assert.True(provider.GetMetadataForProperty(null, type, "NoAttribute").ShowForEdit);
Assert.True(provider.GetMetadataForProperty(null, type, "ScaffoldColumnTrue").ShowForEdit);
Assert.False(provider.GetMetadataForProperty(null, type, "ScaffoldColumnFalse").ShowForEdit);
Assert.True(provider.GetMetadataForProperty(type, "NoAttribute").ShowForEdit);
Assert.True(provider.GetMetadataForProperty(type, "ScaffoldColumnTrue").ShowForEdit);
Assert.False(provider.GetMetadataForProperty(type, "ScaffoldColumnFalse").ShowForEdit);
}
[Fact]
@ -184,7 +181,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
// Arrange
var provider = new DataAnnotationsModelMetadataProvider();
var metadata = provider.GetMetadataForType(modelAccessor: null, modelType: typeof(ClassWithHiddenProperties));
var metadata = provider.GetMetadataForType(modelType: typeof(ClassWithHiddenProperties));
var property = metadata.Properties["DirectlyHidden"];
// Act
@ -199,7 +196,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
// Arrange
var provider = new DataAnnotationsModelMetadataProvider();
var metadata = provider.GetMetadataForType(modelAccessor: null, modelType: typeof(ClassWithHiddenProperties));
var metadata = provider.GetMetadataForType(typeof(ClassWithHiddenProperties));
var property = metadata.Properties["OfHiddenType"];
// Act
@ -214,7 +211,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
// Arrange
var provider = new DataAnnotationsModelMetadataProvider();
var metadata = provider.GetMetadataForType(modelAccessor: null, modelType: typeof(ClassWithHiddenProperties));
var metadata = provider.GetMetadataForType(typeof(ClassWithHiddenProperties));
var property = metadata.Properties["DirectlyHidden"];
// Act
@ -229,7 +226,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
// Arrange
var provider = new DataAnnotationsModelMetadataProvider();
var metadata = provider.GetMetadataForType(modelAccessor: null, modelType: typeof(ClassWithHiddenProperties));
var metadata = provider.GetMetadataForType(typeof(ClassWithHiddenProperties));
var property = metadata.Properties["OfHiddenType"];
// Act
@ -246,7 +243,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var provider = new DataAnnotationsModelMetadataProvider();
// Act
var propertyMetadata = provider.GetMetadataForProperty(null, typeof(Person), nameof(Person.Parent));
var propertyMetadata = provider.GetMetadataForProperty(typeof(Person), nameof(Person.Parent));
// Assert
Assert.NotNull(propertyMetadata.BinderMetadata);
@ -261,7 +258,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var provider = new DataAnnotationsModelMetadataProvider();
// Act
var propertyMetadata = provider.GetMetadataForProperty(null, typeof(Person), nameof(Person.GrandParent));
var propertyMetadata = provider.GetMetadataForProperty(typeof(Person), nameof(Person.GrandParent));
// Assert
Assert.NotNull(propertyMetadata.BinderMetadata);
@ -277,7 +274,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// Act
var parameterMetadata = provider.GetMetadataForParameter(
null,
typeof(Person).GetMethod("Update"),
"person");
@ -295,7 +291,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// Act
var parameterMetadata = provider.GetMetadataForParameter(
null,
typeof(Person).GetMethod("Save"),
"person");

View File

@ -31,7 +31,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// Assert
Assert.NotNull(metadata.AdditionalValues);
Assert.Empty(metadata.AdditionalValues);
Assert.Null(metadata.Container);
Assert.Null(metadata.ContainerType);
Assert.True(metadata.ConvertEmptyStringToNull);
@ -52,12 +51,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
Assert.Null(metadata.DisplayName);
Assert.Null(metadata.EditFormatString);
Assert.Null(metadata.NullDisplayText);
Assert.Null(metadata.SimpleDisplayText);
Assert.Null(metadata.SimpleDisplayProperty);
Assert.Null(metadata.TemplateHint);
Assert.Null(metadata.Model);
Assert.Equal(typeof(object), metadata.ModelType);
Assert.Equal(typeof(object), metadata.RealModelType);
Assert.Null(metadata.PropertyName);
Assert.Equal(ModelMetadata.DefaultOrder, metadata.Order);
@ -89,9 +86,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
new DisplayAttribute { Name = "value" }, metadata => metadata.DisplayName
},
{
new DisplayColumnAttribute("Property"), metadata => metadata.SimpleDisplayText
},
{
new DisplayFormatAttribute { DataFormatString = "value" },
metadata => metadata.DisplayFormatString
@ -130,10 +124,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
containerType: null,
modelType: typeof(ClassWithDisplayableColumn),
propertyName: null,
attributes: attributes)
{
Model = new ClassWithDisplayableColumn { Property = "value" },
};
attributes: attributes);
// Act
var result = accessor(metadata);
@ -142,6 +133,27 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
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

View File

@ -0,0 +1,86 @@
// 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 Xunit;
namespace Microsoft.AspNet.Mvc.ModelBinding
{
public class ModelExplorerExtensionsTest
{
public static TheoryData<object, Type, string> SimpleDisplayTextData
{
get
{
return new TheoryData<object, Type, string>
{
{
new ComplexClass()
{
Prop1 = new Class1 { Prop1 = "Hello" }
},
typeof(ComplexClass),
"Class1"
},
{
new Class1(),
typeof(Class1),
"Class1"
},
{
new ClassWithNoProperties(),
typeof(ClassWithNoProperties),
string.Empty
},
{
null,
typeof(object),
null
},
};
}
}
[Theory]
[MemberData(nameof(SimpleDisplayTextData))]
public void GetSimpleDisplayText_WithoutSimpleDisplayProperty(
object model,
Type modelType,
string expectedResult)
{
// Arrange
var provider = new EmptyModelMetadataProvider();
var modelExplorer = provider.GetModelExplorerForType(modelType, model);
// Act
var result = modelExplorer.GetSimpleDisplayText();
// Assert
Assert.Equal(expectedResult, result);
}
private class ClassWithNoProperties
{
public override string ToString()
{
return null;
}
}
private class ComplexClass
{
public Class1 Prop1 { get; set; }
}
private class Class1
{
public string Prop1 { get; set; }
public override string ToString()
{
return "Class1";
}
}
}
}

View File

@ -0,0 +1,150 @@
// 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.Linq;
using Xunit;
namespace Microsoft.AspNet.Mvc.ModelBinding
{
public class ModelExplorerTest
{
[Fact]
public void ModelType_UsesRuntimeType()
{
// Arrange
var provider = new EmptyModelMetadataProvider();
var modelExplorer = provider.GetModelExplorerForType(typeof(BaseClass), new DerivedClass());
// Act
var modelType = modelExplorer.ModelType;
// Assert
Assert.Equal(typeof(DerivedClass), modelType);
}
[Fact]
public void ModelType_UsesDeclaredType_WhenModelIsNull()
{
// Arrange
var provider = new EmptyModelMetadataProvider();
var modelExplorer = provider.GetModelExplorerForType(typeof(BaseClass), model: null);
// Act
var modelType = modelExplorer.ModelType;
// Assert
Assert.Equal(typeof(BaseClass), modelType);
}
[Fact]
public void Properties_UsesRuntimeType()
{
// Arrange
var provider = new EmptyModelMetadataProvider();
var modelExplorer = provider.GetModelExplorerForType(typeof(BaseClass), new DerivedClass());
// Act
var properties = modelExplorer.Properties.ToArray();
// Assert
Assert.Equal(2, properties.Length);
var baseProperty = Assert.Single(properties, p => p.Metadata.PropertyName == "Base1");
Assert.Equal(typeof(int), baseProperty.Metadata.ModelType);
Assert.Equal(typeof(DerivedClass), baseProperty.Metadata.ContainerType);
Assert.Same(modelExplorer, baseProperty.Container);
var derivedProperty = Assert.Single(properties, p => p.Metadata.PropertyName == "Derived1");
Assert.Equal(typeof(string), derivedProperty.Metadata.ModelType);
Assert.Equal(typeof(DerivedClass), derivedProperty.Metadata.ContainerType);
Assert.Same(modelExplorer, derivedProperty.Container);
}
[Fact]
public void Properties_UsesDeclaredType_WhenModelIsNull()
{
// Arrange
var provider = new EmptyModelMetadataProvider();
var modelExplorer = provider.GetModelExplorerForType(typeof(BaseClass), model: null);
// Act
var properties = modelExplorer.Properties.ToArray();
// Assert
Assert.Equal(1, properties.Length);
var baseProperty = Assert.Single(properties, p => p.Metadata.PropertyName == "Base1");
Assert.Equal(typeof(int), baseProperty.Metadata.ModelType);
Assert.Equal(typeof(BaseClass), baseProperty.Metadata.ContainerType);
Assert.Same(modelExplorer, baseProperty.Container);
}
[Fact]
public void GetPropertyExplorer_DeferredModelAccess()
{
// Arrange
var model = new DerivedClass()
{
Base1 = 5,
};
var provider = new EmptyModelMetadataProvider();
var modelExplorer = provider.GetModelExplorerForType(typeof(BaseClass), model);
// Change the model value after creating the explorer
var propertyExplorer = modelExplorer.GetExplorerForProperty("Base1");
model.Base1 = 17;
// Act
var propertyValue = propertyExplorer.Model;
// Assert
Assert.Equal(17, propertyValue);
}
[Fact]
public void GetPropertyExplorer_DeferredModelAccess_ContainerModelIsNull()
{
// Arrange
var provider = new EmptyModelMetadataProvider();
var modelExplorer = provider.GetModelExplorerForType(typeof(BaseClass), model: null);
var propertyExplorer = modelExplorer.GetExplorerForProperty("Base1");
// Act
var propertyValue = propertyExplorer.Model;
// Assert
Assert.Null(propertyValue);
}
[Fact]
public void GetPropertyExplorer_ReturnsNull_ForPropertyNotFound()
{
// Arrange
var model = new DerivedClass()
{
Base1 = 5,
};
var provider = new EmptyModelMetadataProvider();
var modelExplorer = provider.GetModelExplorerForType(typeof(BaseClass), model);
// Act
var propertyExplorer = modelExplorer.GetExplorerForProperty("BadName");
// Assert
Assert.Null(propertyExplorer);
}
private class BaseClass
{
public int Base1 { get; set; }
}
private class DerivedClass : BaseClass
{
public string Derived1 { get; set; }
}
}
}

View File

@ -0,0 +1,24 @@
// 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.Testing;
using Xunit;
namespace Microsoft.AspNet.Mvc.ModelBinding
{
public class ModelMetadataProviderExtensionsTest
{
[Fact]
public void GetMetadataForPropertyInvalidPropertyNameThrows()
{
// Arrange
var provider = (IModelMetadataProvider)new EmptyModelMetadataProvider();
// Act & Assert
ExceptionAssert.ThrowsArgument(
() => provider.GetMetadataForProperty(typeof(object), propertyName: "BadPropertyName"),
"propertyName",
"The property System.Object.BadPropertyName could not be found.");
}
}
}

View File

@ -42,13 +42,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{ 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.SimpleDisplayText = "New simple display", m => m.SimpleDisplayText, "New simple 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.Container = null, m => m.Container, null },
{ m => m.Container = emptycontainerModel, m => m.Container, emptycontainerModel },
{ m => m.Container = nonEmptycontainerModel, m => m.Container, nonEmptycontainerModel },
{ m => m.BinderMetadata = null, m => m.BinderMetadata, null },
{ m => m.BinderMetadata = binderMetadata, m => m.BinderMetadata, binderMetadata },
@ -72,14 +69,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var provider = new EmptyModelMetadataProvider();
// Act
var metadata =
new ModelMetadata(provider, typeof(Exception), () => "model", typeof(string), "propertyName");
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.Null(metadata.Container);
Assert.True(metadata.ConvertEmptyStringToNull);
Assert.False(metadata.HasNonDefaultEditFormat);
@ -100,10 +95,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
Assert.Null(metadata.EditFormatString);
Assert.Null(metadata.NullDisplayText);
Assert.Null(metadata.TemplateHint);
Assert.Equal("model", metadata.Model);
Assert.Equal("model", metadata.SimpleDisplayText);
Assert.Equal(typeof(string), metadata.RealModelType);
Assert.Null(metadata.SimpleDisplayProperty);
Assert.Equal(typeof(string), metadata.ModelType);
Assert.Equal("propertyName", metadata.PropertyName);
@ -128,7 +121,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var metadata = new ModelMetadata(
provider,
containerType: null,
modelAccessor: () => null,
modelType: typeof(object),
propertyName: null);
@ -148,7 +140,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var metadata = new ModelMetadata(
provider,
containerType: null,
modelAccessor: () => null,
modelType: typeof(object),
propertyName: null);
var valuesDictionary = new Dictionary<object, object>
@ -187,7 +178,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var modelMetadata = new ModelMetadata(
provider,
containerType: null,
modelAccessor: null,
modelType: type,
propertyName: null);
@ -209,7 +199,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var modelMetadata = new ModelMetadata(
provider,
containerType: null,
modelAccessor: null,
modelType: type,
propertyName: null);
@ -231,7 +220,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var modelMetadata = new ModelMetadata(
provider,
containerType: null,
modelAccessor: null,
modelType: type,
propertyName: null);
@ -256,7 +244,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var modelMetadata = new ModelMetadata(
provider,
containerType: null,
modelAccessor: null,
modelType: type,
propertyName: null);
@ -281,10 +268,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var provider = new EmptyModelMetadataProvider();
// Act & Assert
Assert.False(new ModelMetadata(provider, null, null, typeof(string), null).IsNullableValueType);
Assert.False(new ModelMetadata(provider, null, null, typeof(IDisposable), null).IsNullableValueType);
Assert.True(new ModelMetadata(provider, null, null, typeof(Nullable<int>), null).IsNullableValueType);
Assert.False(new ModelMetadata(provider, null, null, typeof(int), null).IsNullableValueType);
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
@ -299,7 +286,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var provider = new EmptyModelMetadataProvider();
var metadata = new ModelMetadata(provider,
containerType: null,
modelAccessor: null,
modelType: modelType,
propertyName: null);
@ -319,7 +305,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var provider = new EmptyModelMetadataProvider();
var metadata = new ModelMetadata(provider,
containerType: null,
modelAccessor: null,
modelType: modelType,
propertyName: null);
@ -341,7 +326,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var metadata = new ModelMetadata(
provider,
containerType: null,
modelAccessor: null,
modelType: modelType,
propertyName: null);
@ -393,7 +377,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var metadata = new ModelMetadata(
provider,
containerType: null,
modelAccessor: null,
modelType: modelType,
propertyName: null);
@ -482,7 +465,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var metadata = new ModelMetadata(
provider,
containerType: null,
modelAccessor: null,
modelType: modelType,
propertyName: null);
@ -506,7 +488,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var metadata = new ModelMetadata(
provider,
containerType: null,
modelAccessor: () => new Class1(),
modelType: typeof(Class1),
propertyName: null);
@ -536,7 +517,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var metadata = new ModelMetadata(
provider,
containerType: null,
modelAccessor: () => new Class1(),
modelType: typeof(Class1),
propertyName: null);
@ -549,27 +529,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
Assert.Equal(expected, result);
}
[Fact]
public void PropertiesListGetsResetWhenModelGetsReset()
{
// Arrange
var provider = new EmptyModelMetadataProvider();
var metadata = new ModelMetadata(provider, null, () => new Class1(), typeof(Class1), null);
// Act
var originalProps = metadata.Properties.ToArray();
metadata.Model = new Class2();
var newProps = metadata.Properties.ToArray();
// Assert
var originalProp = Assert.Single(originalProps);
Assert.Equal(typeof(string), originalProp.ModelType);
Assert.Equal("Prop1", originalProp.PropertyName);
var newProp = Assert.Single(newProps);
Assert.Equal(typeof(int), newProp.ModelType);
Assert.Equal("Prop2", newProp.PropertyName);
}
[Fact]
public void PropertiesSetOnce()
{
@ -578,7 +537,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var metadata = new ModelMetadata(
provider,
containerType: null,
modelAccessor: () => new Class1(),
modelType: typeof(Class1),
propertyName: null);
@ -599,7 +557,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var metadata = new ModelMetadata(
provider,
containerType: null,
modelAccessor: () => new Class1(),
modelType: typeof(Class1),
propertyName: null);
@ -633,7 +590,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
// Arrange
var provider = new EmptyModelMetadataProvider();
var metadata = new ModelMetadata(provider, null, () => null, typeof(object), "unusedName")
var metadata = new ModelMetadata(provider, null, typeof(object), "unusedName")
{
DisplayName = "displayName",
};
@ -650,7 +607,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
// Arrange
var provider = new EmptyModelMetadataProvider();
var metadata = new ModelMetadata(provider, null, null, typeof(object), "PropertyName");
var metadata = new ModelMetadata(provider, null, typeof(object), "PropertyName");
// Act
var result = metadata.GetDisplayName();
@ -664,7 +621,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
// Arrange
var provider = new EmptyModelMetadataProvider();
var metadata = new ModelMetadata(provider, null, null, typeof(object), null);
var metadata = new ModelMetadata(provider, null, typeof(object), null);
// Act
var result = metadata.GetDisplayName();
@ -673,57 +630,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
Assert.Equal("Object", result);
}
// SimpleDisplayText
public static IEnumerable<object[]> SimpleDisplayTextData
{
get
{
yield return new object[]
{
new Func<object>(() => new ComplexClass()
{
Prop1 = new Class1 { Prop1 = "Hello" }
}),
typeof(ComplexClass),
"Class1"
};
yield return new object[]
{
new Func<object>(() => new Class1()),
typeof(Class1),
"Class1"
};
yield return new object[]
{
new Func<object>(() => new ClassWithNoProperties()),
typeof(ClassWithNoProperties),
string.Empty
};
yield return new object[]
{
null,
typeof(object),
null
};
}
}
[Theory]
[MemberData(nameof(SimpleDisplayTextData))]
public void TestSimpleDisplayText(Func<object> modelAccessor, Type modelType, string expectedResult)
{
// Arrange
var provider = new EmptyModelMetadataProvider();
var metadata = new ModelMetadata(provider, null, modelAccessor, modelType, null);
// Act
var result = metadata.SimpleDisplayText;
// Assert
Assert.Equal(expectedResult, result);
}
private class ClassWithNoProperties
{
public override string ToString()
@ -774,7 +680,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var metadata = new ModelMetadata(
this,
containerType: typeof(DummyContactModel),
modelAccessor: null,
modelType: typeof(string),
propertyName: propertyName);
@ -789,7 +694,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var metadata = new ModelMetadata(
this,
containerType: typeof(DummyContactModel),
modelAccessor: null,
modelType: typeof(string),
propertyName: keyValuePair.Key)
{
@ -803,31 +707,21 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public int GetMetadataForPropertiesCalls { get; private set; }
public ModelMetadata GetMetadataForParameter(
Func<object> modelAccessor,
[NotNull] MethodInfo methodInfo,
[NotNull] string parameterName)
{
throw new NotImplementedException();
}
public IEnumerable<ModelMetadata> GetMetadataForProperties(object container, [NotNull] Type containerType)
public IEnumerable<ModelMetadata> GetMetadataForProperties([NotNull] Type containerType)
{
Assert.Null(container);
Assert.Equal(typeof(object), containerType);
GetMetadataForPropertiesCalls++;
return _properties;
}
public ModelMetadata GetMetadataForProperty(
Func<object> modelAccessor,
[NotNull] Type containerType,
[NotNull] string propertyName)
{
throw new NotImplementedException();
}
public ModelMetadata GetMetadataForType(Func<object> modelAccessor, [NotNull] Type modelType)
public ModelMetadata GetMetadataForType([NotNull] Type modelType)
{
throw new NotImplementedException();
}

View File

@ -20,7 +20,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
// Arrange
IEnumerable<object> callbackAttributes = null;
var metadata = _metadataProvider.GetMetadataForProperty(null, typeof(PropertyModel), "LocalAttributes");
var metadata = _metadataProvider.GetMetadataForProperty(typeof(PropertyModel), "LocalAttributes");
var provider = new Mock<TestableAssociatedValidatorProvider> { CallBase = true };
provider.Setup(p => p.AbstractGetValidators(metadata, It.IsAny<IEnumerable<object>>()))
.Callback<ModelMetadata, IEnumerable<object>>((m, attributes) => callbackAttributes = attributes)
@ -40,7 +40,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
// Arrange
IEnumerable<object> callbackAttributes = null;
var metadata = _metadataProvider.GetMetadataForProperty(null, typeof(PropertyModel), "MetadataAttributes");
var metadata = _metadataProvider.GetMetadataForProperty(typeof(PropertyModel), "MetadataAttributes");
var provider = new Mock<TestableAssociatedValidatorProvider> { CallBase = true };
provider.Setup(p => p.AbstractGetValidators(metadata, It.IsAny<IEnumerable<object>>()))
.Callback<ModelMetadata, IEnumerable<object>>((m, attributes) => callbackAttributes = attributes)
@ -60,7 +60,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
// Arrange
IEnumerable<object> callbackAttributes = null;
var metadata = _metadataProvider.GetMetadataForProperty(null, typeof(PropertyModel), "MixedAttributes");
var metadata = _metadataProvider.GetMetadataForProperty(typeof(PropertyModel), "MixedAttributes");
var provider = new Mock<TestableAssociatedValidatorProvider> { CallBase = true };
provider.Setup(p => p.AbstractGetValidators(metadata, It.IsAny<IEnumerable<object>>()))
.Callback<ModelMetadata, IEnumerable<object>>((m, attributes) => callbackAttributes = attributes)

View File

@ -17,12 +17,15 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
// Arrange
var metadataProvider = new DataAnnotationsModelMetadataProvider();
var metadata = metadataProvider.GetMetadataForProperty(() => null, typeof(PropertyDisplayNameModel), "MyProperty");
var metadata = metadataProvider.GetMetadataForProperty(typeof(PropertyDisplayNameModel), "MyProperty");
var attribute = new CompareAttribute("OtherProperty");
var adapter = new CompareAttributeAdapter(attribute);
var serviceCollection = new ServiceCollection();
var requestServices = serviceCollection.BuildServiceProvider();
var context = new ClientModelValidationContext(metadata, metadataProvider, requestServices);
var adapter = new CompareAttributeAdapter(attribute);
// Act
var rules = adapter.GetClientValidationRules(context);
@ -38,7 +41,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
// Arrange
var metadataProvider = new DataAnnotationsModelMetadataProvider();
var metadata = metadataProvider.GetMetadataForProperty(() => null, typeof(PropertyNameModel), "MyProperty");
var metadata = metadataProvider.GetMetadataForProperty(typeof(PropertyNameModel), "MyProperty");
var attribute = new CompareAttribute("OtherProperty");
var serviceCollection = new ServiceCollection();
var requestServices = serviceCollection.BuildServiceProvider();
@ -58,7 +61,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
// Arrange
var metadataProvider = new DataAnnotationsModelMetadataProvider();
var metadata = metadataProvider.GetMetadataForProperty(() => null, typeof(PropertyNameModel), "MyProperty");
var metadata = metadataProvider.GetMetadataForProperty( typeof(PropertyNameModel), "MyProperty");
var attribute = new CompareAttribute("OtherProperty")
{
ErrorMessage = "Hello '{0}', goodbye '{1}'."
@ -87,7 +90,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// Arrange
var metadataProvider = new DataAnnotationsModelMetadataProvider();
var metadata = metadataProvider.GetMetadataForProperty(() => null, typeof(PropertyNameModel), "MyProperty");
var metadata = metadataProvider.GetMetadataForProperty(typeof(PropertyNameModel), "MyProperty");
var attribute = new CompareAttribute("OtherProperty")
{
ErrorMessageResourceName = "CompareAttributeTestResource",

View File

@ -23,9 +23,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
provider2.Setup(p => p.GetValidators(It.IsAny<ModelMetadata>()))
.Returns(new[] { validator3 });
var compositeModelValidator = new CompositeModelValidatorProvider(new[] { provider1.Object, provider2.Object });
var modelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(
modelAccessor: null,
modelType: typeof(string));
var modelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(typeof(string));
// Act
var result = compositeModelValidator.GetValidators(modelMetadata);

View File

@ -23,7 +23,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// Arrange
var provider = new DataAnnotationsModelValidatorProvider();
var mockValidatable = Mock.Of<IValidatableObject>();
var metadata = _metadataProvider.GetMetadataForType(() => null, mockValidatable.GetType());
var metadata = _metadataProvider.GetMetadataForType(mockValidatable.GetType());
// Act
var validators = provider.GetValidators(metadata);
@ -39,8 +39,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
// Arrange
var provider = new DataAnnotationsModelValidatorProvider();
var metadata = _metadataProvider.GetMetadataForProperty(() => null,
typeof(DummyRequiredAttributeHelperClass),
var metadata = _metadataProvider.GetMetadataForProperty(typeof(DummyRequiredAttributeHelperClass),
"WithAttribute");
// Act
@ -143,7 +142,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
// Arrange
var provider = new DataAnnotationsModelValidatorProvider();
var metadata = _metadataProvider.GetMetadataForType(() => null, typeof(DummyClassWithDummyValidationAttribute));
var metadata = _metadataProvider.GetMetadataForType(typeof(DummyClassWithDummyValidationAttribute));
// Act
IEnumerable<IModelValidator> validators = provider.GetValidators(metadata);
@ -171,7 +170,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// Arrange
var provider = new DataAnnotationsModelValidatorProvider();
var mockValidatable = new Mock<IValidatableObject>();
var metadata = _metadataProvider.GetMetadataForType(() => null, mockValidatable.Object.GetType());
var metadata = _metadataProvider.GetMetadataForType(mockValidatable.Object.GetType());
// Act
var validators = provider.GetValidators(metadata);
@ -189,11 +188,19 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// Arrange
var provider = new DataAnnotationsModelValidatorProvider();
var model = new ObservableModel();
var metadata = _metadataProvider.GetMetadataForProperty(() => model.TheProperty, typeof(ObservableModel), "TheProperty");
var context = new ModelValidationContext(null, null, null, metadata, null);
var modelExplorer = _metadataProvider
.GetModelExplorerForType(typeof(ObservableModel), model)
.GetExplorerForProperty("TheProperty");
var context = new ModelValidationContext(
rootPrefix: null,
validatorProvider: null,
modelState: null,
modelExplorer: modelExplorer);
// Act
var validators = provider.GetValidators(metadata).ToArray();
var validators = provider.GetValidators(modelExplorer.Metadata).ToArray();
var results = validators.SelectMany(o => o.Validate(context)).ToArray();
// Assert

View File

@ -26,7 +26,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public void ValuesSet()
{
// Arrange
var metadata = _metadataProvider.GetMetadataForProperty(() => 15, typeof(string), "Length");
var attribute = new RequiredAttribute();
// Act
@ -42,13 +41,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
yield return new object[]
{
_metadataProvider.GetMetadataForProperty(() => 15, typeof(string), "Length"),
_metadataProvider.GetModelExplorerForType(typeof(string), "Hello").GetExplorerForProperty("Length"),
"Length"
};
yield return new object[]
{
_metadataProvider.GetMetadataForType(() => new object(), typeof(SampleModel)),
_metadataProvider.GetModelExplorerForType(typeof(SampleModel), 15),
"SampleModel"
};
}
@ -57,8 +56,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
#if ASPNET50
[Theory]
[MemberData(nameof(ValidateSetsMemberNamePropertyDataSet))]
public void ValidateSetsMemberNamePropertyOfValidationContextForProperties(ModelMetadata metadata,
string expectedMemberName)
public void ValidateSetsMemberNamePropertyOfValidationContextForProperties(
ModelExplorer modelExplorer,
string expectedMemberName)
{
// Arrange
var attribute = new Mock<ValidationAttribute> { CallBase = true };
@ -71,7 +71,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
.Returns(ValidationResult.Success)
.Verifiable();
var validator = new DataAnnotationsModelValidator(attribute.Object);
var validationContext = CreateValidationContext(metadata);
var validationContext = CreateValidationContext(modelExplorer);
// Act
var results = validator.Validate(validationContext);
@ -85,11 +85,15 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public void ValidateWithIsValidTrue()
{
// Arrange
var metadata = _metadataProvider.GetMetadataForProperty(() => 15, typeof(string), "Length");
var modelExplorer = _metadataProvider
.GetModelExplorerForType(typeof(string), "Hello")
.GetExplorerForProperty("Length");
var attribute = new Mock<ValidationAttribute> { CallBase = true };
attribute.Setup(a => a.IsValid(metadata.Model)).Returns(true);
attribute.Setup(a => a.IsValid(modelExplorer.Model)).Returns(true);
var validator = new DataAnnotationsModelValidator(attribute.Object);
var validationContext = CreateValidationContext(metadata);
var validationContext = CreateValidationContext(modelExplorer);
// Act
var result = validator.Validate(validationContext);
@ -102,11 +106,15 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public void ValidateWithIsValidFalse()
{
// Arrange
var metadata = _metadataProvider.GetMetadataForProperty(() => 15, typeof(string), "Length");
var modelExplorer = _metadataProvider
.GetModelExplorerForType(typeof(string), "Hello")
.GetExplorerForProperty("Length");
var attribute = new Mock<ValidationAttribute> { CallBase = true };
attribute.Setup(a => a.IsValid(metadata.Model)).Returns(false);
attribute.Setup(a => a.IsValid(modelExplorer.Model)).Returns(false);
var validator = new DataAnnotationsModelValidator(attribute.Object);
var validationContext = CreateValidationContext(metadata);
var validationContext = CreateValidationContext(modelExplorer);
// Act
var result = validator.Validate(validationContext);
@ -121,13 +129,16 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public void ValidatateWithValidationResultSuccess()
{
// Arrange
var metadata = _metadataProvider.GetMetadataForProperty(() => 15, typeof(string), "Length");
var modelExplorer = _metadataProvider
.GetModelExplorerForType(typeof(string), "Hello")
.GetExplorerForProperty("Length");
var attribute = new Mock<ValidationAttribute> { CallBase = true };
attribute.Protected()
.Setup<ValidationResult>("IsValid", ItExpr.IsAny<object>(), ItExpr.IsAny<ValidationContext>())
.Returns(ValidationResult.Success);
var validator = new DataAnnotationsModelValidator(attribute.Object);
var validationContext = CreateValidationContext(metadata);
var validationContext = CreateValidationContext(modelExplorer);
// Act
var result = validator.Validate(validationContext);
@ -141,13 +152,18 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
// Arrange
const string errorMessage = "Some error message";
var metadata = _metadataProvider.GetMetadataForProperty(() => 15, typeof(string), "Length");
var modelExplorer = _metadataProvider
.GetModelExplorerForType(typeof(string), "Hello")
.GetExplorerForProperty("Length");
var attribute = new Mock<ValidationAttribute> { CallBase = true };
attribute.Protected()
.Setup<ValidationResult>("IsValid", ItExpr.IsAny<object>(), ItExpr.IsAny<ValidationContext>())
.Returns(new ValidationResult(errorMessage, memberNames: null));
var validator = new DataAnnotationsModelValidator(attribute.Object);
var validationContext = CreateValidationContext(metadata);
var validationContext = CreateValidationContext(modelExplorer);
// Act
var results = validator.Validate(validationContext);
@ -163,11 +179,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
// Arrange
const string errorMessage = "A different error message";
var metadata = _metadataProvider.GetMetadataForType(() => new object(), typeof(object));
var metadata = _metadataProvider.GetModelExplorerForType(typeof(object), new object());
var attribute = new Mock<ValidationAttribute> { CallBase = true };
attribute.Protected()
.Setup<ValidationResult>("IsValid", ItExpr.IsAny<object>(), ItExpr.IsAny<ValidationContext>())
.Returns(new ValidationResult(errorMessage, new[] { "FirstName" }));
var validator = new DataAnnotationsModelValidator(attribute.Object);
var validationContext = CreateValidationContext(metadata);
@ -184,11 +203,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public void ValidateReturnsMemberNameIfItIsDifferentFromDisplayName()
{
// Arrange
var metadata = _metadataProvider.GetMetadataForType(() => new SampleModel(), typeof(SampleModel));
var metadata = _metadataProvider.GetModelExplorerForType(typeof(SampleModel), new SampleModel());
var attribute = new Mock<ValidationAttribute> { CallBase = true };
attribute.Protected()
.Setup<ValidationResult>("IsValid", ItExpr.IsAny<object>(), ItExpr.IsAny<ValidationContext>())
.Returns(new ValidationResult("Name error", new[] { "Name" }));
var validator = new DataAnnotationsModelValidator(attribute.Object);
var validationContext = CreateValidationContext(metadata);
@ -207,12 +228,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// Arrange
var attribute = new FileExtensionsAttribute();
var validator = new DataAnnotationsModelValidator<FileExtensionsAttribute>(attribute);
var metadata = _metadataProvider.GetMetadataForProperty(
modelAccessor: null,
containerType: typeof(string),
propertyName: nameof(string.Length));
var serviceCollection = new ServiceCollection();
var requestServices = serviceCollection.BuildServiceProvider();
var context = new ClientModelValidationContext(metadata, _metadataProvider, requestServices);
// Act
@ -228,12 +251,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// Arrange
var attribute = new TestableAttribute();
var validator = new DataAnnotationsModelValidator<TestableAttribute>(attribute);
var metadata = _metadataProvider.GetMetadataForProperty(
modelAccessor: null,
containerType: typeof(string),
propertyName: nameof(string.Length));
var serviceCollection = new ServiceCollection();
var requestServices = serviceCollection.BuildServiceProvider();
var context = new ClientModelValidationContext(metadata, _metadataProvider, requestServices);
// Act
@ -249,18 +274,15 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
[Fact]
public void IsRequiredTests()
{
// Arrange
var metadata = _metadataProvider.GetMetadataForProperty(() => 15, typeof(string), "Length");
// Act & Assert
// Arrange & Act & Assert
Assert.False(new DataAnnotationsModelValidator(new RangeAttribute(10, 20)).IsRequired);
Assert.True(new DataAnnotationsModelValidator(new RequiredAttribute()).IsRequired);
Assert.True(new DataAnnotationsModelValidator(new DerivedRequiredAttribute()).IsRequired);
}
private static ModelValidationContext CreateValidationContext(ModelMetadata metadata)
private static ModelValidationContext CreateValidationContext(ModelExplorer modelExplorer)
{
return new ModelValidationContext(null, null, null, metadata, null);
return new ModelValidationContext(null, null, null, modelExplorer);
}
private class DerivedRequiredAttribute : RequiredAttribute

View File

@ -15,7 +15,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
// Arrange
var provider = new DataMemberModelValidatorProvider();
var metadata = _metadataProvider.GetMetadataForProperty(() => null, typeof(ClassWithoutAttributes), "TheProperty");
var metadata = _metadataProvider.GetMetadataForProperty(typeof(ClassWithoutAttributes), "TheProperty");
// Act
var validators = provider.GetValidators(metadata);
@ -34,7 +34,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
// Arrange
var provider = new DataMemberModelValidatorProvider();
var metadata = _metadataProvider.GetMetadataForProperty(() => null, typeof(ClassWithDataMemberIsRequiredTrue), "TheProperty");
var metadata = _metadataProvider.GetMetadataForProperty(typeof(ClassWithDataMemberIsRequiredTrue), "TheProperty");
// Act
var validators = provider.GetValidators(metadata);
@ -56,7 +56,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
// Arrange
var provider = new DataMemberModelValidatorProvider();
var metadata = _metadataProvider.GetMetadataForProperty(() => null, typeof(ClassWithDataMemberIsRequiredFalse), "TheProperty");
var metadata = _metadataProvider.GetMetadataForProperty(typeof(ClassWithDataMemberIsRequiredFalse), "TheProperty");
// Act
var validators = provider.GetValidators(metadata);
@ -77,7 +77,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
// Arrange
var provider = new DataMemberModelValidatorProvider();
var metadata = _metadataProvider.GetMetadataForProperty(() => null, typeof(ClassWithDataMemberIsRequiredTrueWithoutDataContract), "TheProperty");
var metadata = _metadataProvider.GetMetadataForProperty(typeof(ClassWithDataMemberIsRequiredTrueWithoutDataContract), "TheProperty");
// Act
var validators = provider.GetValidators(metadata);

View File

@ -213,17 +213,16 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public void ExpectedValidationErrorsRaised(object model, Type type, Dictionary<string, string> expectedErrors)
{
// Arrange
var testValidationContext = GetModelValidationContext(model, type);
var context = GetModelValidationContext(model, type);
// Act (does not throw)
new DefaultObjectValidator(
testValidationContext.ExcludeFiltersProvider,
testValidationContext.ModelMetadataProvider)
.Validate(testValidationContext.ModelValidationContext);
var validator = new DefaultObjectValidator(context.ExcludeFiltersProvider, context.ModelMetadataProvider);
// Act
validator.Validate(context.ModelValidationContext);
// Assert
var actualErrors = new Dictionary<string, string>();
foreach (var keyStatePair in testValidationContext.ModelValidationContext.ModelState)
foreach (var keyStatePair in context.ModelValidationContext.ModelState)
{
foreach (var error in keyStatePair.Value.Errors)
{
@ -483,44 +482,46 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
}
private TestModelValidationContext GetModelValidationContext(
object model, Type type, string key = "", List<Type> excludedTypes = null)
object model,
Type type,
string key = "",
List<Type> excludedTypes = null)
{
var modelStateDictionary = new ModelStateDictionary();
var providers = new IModelValidatorProvider[]
{
new DataAnnotationsModelValidatorProvider(),
new DataMemberModelValidatorProvider()
};
var modelMetadataProvider = new DataAnnotationsModelMetadataProvider();
var excludedValidationTypesPredicate =
new List<IExcludeTypeValidationFilter>();
var excludedValidationTypesPredicate = new List<IExcludeTypeValidationFilter>();
if (excludedTypes != null)
{
var mockExcludeTypeFilter = new Mock<IExcludeTypeValidationFilter>();
mockExcludeTypeFilter.Setup(o => o.IsTypeExcluded(It.IsAny<Type>()))
.Returns<Type>(excludedType =>
excludedTypes.Any(t => t.IsAssignableFrom(excludedType)));
mockExcludeTypeFilter
.Setup(o => o.IsTypeExcluded(It.IsAny<Type>()))
.Returns<Type>(excludedType => excludedTypes.Any(t => t.IsAssignableFrom(excludedType)));
excludedValidationTypesPredicate.Add(mockExcludeTypeFilter.Object);
}
var mockValidationExcludeFiltersProvider = new Mock<IValidationExcludeFiltersProvider>();
mockValidationExcludeFiltersProvider.SetupGet(o => o.ExcludeFilters)
.Returns(excludedValidationTypesPredicate);
mockValidationExcludeFiltersProvider
.SetupGet(o => o.ExcludeFilters)
.Returns(excludedValidationTypesPredicate);
var modelExplorer = modelMetadataProvider.GetModelExplorerForType(type, model);
return new TestModelValidationContext
{
ModelValidationContext = new ModelValidationContext(
key,
new CompositeModelValidatorProvider(providers),
modelStateDictionary,
new ModelMetadata(
provider: modelMetadataProvider,
containerType: typeof(object),
modelAccessor: () => model,
modelType: type,
propertyName: null),
containerMetadata: null),
modelExplorer),
ModelMetadataProvider = modelMetadataProvider,
ExcludeFiltersProvider = mockValidationExcludeFiltersProvider.Object
};

View File

@ -17,7 +17,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
// Arrange
var provider = new DataAnnotationsModelMetadataProvider();
var metadata = provider.GetMetadataForProperty(() => null, typeof(string), "Length");
var metadata = provider.GetMetadataForProperty(typeof(string), "Length");
var attribute = new MaxLengthAttribute(10);
var adapter = new MaxLengthAttributeAdapter(attribute);
var serviceCollection = new ServiceCollection();
@ -43,7 +43,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var propertyName = "Length";
var message = "{0} must be at most {1}";
var provider = new DataAnnotationsModelMetadataProvider();
var metadata = provider.GetMetadataForProperty(() => null, typeof(string), propertyName);
var metadata = provider.GetMetadataForProperty(typeof(string), propertyName);
var attribute = new MaxLengthAttribute(5) { ErrorMessage = message };
var adapter = new MaxLengthAttributeAdapter(attribute);
var serviceCollection = new ServiceCollection();

View File

@ -17,7 +17,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
// Arrange
var provider = new DataAnnotationsModelMetadataProvider();
var metadata = provider.GetMetadataForProperty(() => null, typeof(string), "Length");
var metadata = provider.GetMetadataForProperty(typeof(string), "Length");
var attribute = new MinLengthAttribute(6);
var adapter = new MinLengthAttributeAdapter(attribute);
var serviceCollection = new ServiceCollection();
@ -43,7 +43,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var propertyName = "Length";
var message = "Array must have at least {1} items.";
var provider = new DataAnnotationsModelMetadataProvider();
var metadata = provider.GetMetadataForProperty(() => null, typeof(string), propertyName);
var metadata = provider.GetMetadataForProperty(typeof(string), propertyName);
var attribute = new MinLengthAttribute(2) { ErrorMessage = message };
var adapter = new MinLengthAttributeAdapter(attribute);
var serviceCollection = new ServiceCollection();

View File

@ -17,7 +17,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
// Arrange
var provider = new DataAnnotationsModelMetadataProvider();
var metadata = provider.GetMetadataForProperty(() => null, typeof(string), "Length");
var metadata = provider.GetMetadataForProperty(typeof(string), "Length");
var attribute = new RangeAttribute(typeof(decimal), "0", "100");
var adapter = new RangeAttributeAdapter(attribute);
var serviceCollection = new ServiceCollection();

View File

@ -18,7 +18,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// Arrange
var expected = ValidationAttributeUtil.GetRequiredErrorMessage("Length");
var provider = new DataAnnotationsModelMetadataProvider();
var metadata = provider.GetMetadataForProperty(() => null, typeof(string), "Length");
var metadata = provider.GetMetadataForProperty(typeof(string), "Length");
var attribute = new RequiredAttribute();
var adapter = new RequiredAttributeAdapter(attribute);
var serviceCollection = new ServiceCollection();

View File

@ -17,7 +17,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
// Arrange
var provider = new DataAnnotationsModelMetadataProvider();
var metadata = provider.GetMetadataForProperty(() => null, typeof(string), "Length");
var metadata = provider.GetMetadataForProperty(typeof(string), "Length");
var attribute = new StringLengthAttribute(8);
var adapter = new StringLengthAttributeAdapter(attribute);
var serviceCollection = new ServiceCollection();
@ -41,7 +41,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
// Arrange
var provider = new DataAnnotationsModelMetadataProvider();
var metadata = provider.GetMetadataForProperty(() => null, typeof(string), "Length");
var metadata = provider.GetMetadataForProperty(typeof(string), "Length");
var attribute = new StringLengthAttribute(10) { MinimumLength = 3 };
var adapter = new StringLengthAttributeAdapter(attribute);
var serviceCollection = new ServiceCollection();

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