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 { @functions {
private object FormattedValue { private object FormattedValue {
get { get {
if (ViewData.TemplateInfo.FormattedModelValue == ViewData.ModelMetadata.Model) { if (ViewData.TemplateInfo.FormattedModelValue == ViewData.ModelExplorer.Model) {
return String.Format(CultureInfo.CurrentCulture, "{0:0.00}", ViewData.ModelMetadata.Model); return String.Format(CultureInfo.CurrentCulture, "{0:0.00}", ViewData.ModelExplorer.Model);
} }
return ViewData.TemplateInfo.FormattedModelValue; return ViewData.TemplateInfo.FormattedModelValue;
} }

View File

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

View File

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

View File

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

View File

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

View File

@ -252,9 +252,7 @@ namespace Microsoft.AspNet.Mvc
throw new ArgumentException(message, nameof(modelType)); throw new ArgumentException(message, nameof(modelType));
} }
var modelMetadata = metadataProvider.GetMetadataForType( var modelMetadata = metadataProvider.GetMetadataForType(modelType);
modelAccessor: () => model,
modelType: modelType);
var operationBindingContext = new OperationBindingContext var operationBindingContext = new OperationBindingContext
{ {
@ -266,6 +264,7 @@ namespace Microsoft.AspNet.Mvc
var modelBindingContext = new ModelBindingContext var modelBindingContext = new ModelBindingContext
{ {
Model = model,
ModelMetadata = modelMetadata, ModelMetadata = modelMetadata,
ModelName = prefix, ModelName = prefix,
ModelState = modelState, ModelState = modelState,
@ -278,7 +277,8 @@ namespace Microsoft.AspNet.Mvc
var modelBindingResult = await modelBinder.BindModelAsync(modelBindingContext); var modelBindingResult = await modelBinder.BindModelAsync(modelBindingContext);
if (modelBindingResult != null) 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; modelValidationContext.RootPrefix = prefix;
objectModelValidator.Validate(modelValidationContext); objectModelValidator.Validate(modelValidationContext);
return modelState.IsValid; 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. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.Globalization;
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Reflection; using System.Reflection;
using Microsoft.AspNet.Mvc.Core; using Microsoft.AspNet.Mvc.Core;
@ -12,7 +13,7 @@ namespace Microsoft.AspNet.Mvc.Rendering.Expressions
{ {
public static class ExpressionMetadataProvider public static class ExpressionMetadataProvider
{ {
public static ModelMetadata FromLambdaExpression<TModel, TResult>( public static ModelExplorer FromLambdaExpression<TModel, TResult>(
[NotNull] Expression<Func<TModel, TResult>> expression, [NotNull] Expression<Func<TModel, TResult>> expression,
[NotNull] ViewDataDictionary<TModel> viewData, [NotNull] ViewDataDictionary<TModel> viewData,
IModelMetadataProvider metadataProvider) IModelMetadataProvider metadataProvider)
@ -55,12 +56,11 @@ namespace Microsoft.AspNet.Mvc.Rendering.Expressions
throw new InvalidOperationException(Resources.TemplateHelpers_TemplateLimitations); throw new InvalidOperationException(Resources.TemplateHelpers_TemplateLimitations);
} }
var container = viewData.Model; Func<object, object> modelAccessor = (container) =>
Func<object> modelAccessor = () =>
{ {
try try
{ {
return CachedExpressionCompiler.Process(expression)(container); return CachedExpressionCompiler.Process(expression)((TModel)container);
} }
catch (NullReferenceException) catch (NullReferenceException)
{ {
@ -68,18 +68,31 @@ namespace Microsoft.AspNet.Mvc.Rendering.Expressions
} }
}; };
return GetMetadataFromProvider( ModelMetadata metadata;
modelAccessor, if (propertyName == null)
typeof(TResult), {
propertyName, // Ex:
container, // m => 5 (arbitrary expression)
containerType, // m => foo (arbitrary expression)
metadataProvider); // 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, public static ModelExplorer FromStringExpression(
[NotNull] ViewDataDictionary viewData, string expression,
IModelMetadataProvider metadataProvider) [NotNull] ViewDataDictionary viewData,
IModelMetadataProvider metadataProvider)
{ {
if (string.IsNullOrEmpty(expression)) if (string.IsNullOrEmpty(expression))
{ {
@ -88,93 +101,64 @@ namespace Microsoft.AspNet.Mvc.Rendering.Expressions
} }
var viewDataInfo = ViewDataEvaluator.Eval(viewData, expression); var viewDataInfo = ViewDataEvaluator.Eval(viewData, expression);
Type containerType = null;
Type modelType = null; if (viewDataInfo == null)
Func<object> modelAccessor = null; {
string propertyName = null; // Try getting a property from ModelMetadata if we couldn't find an answer in ViewData
object container = null; var propertyExplorer = viewData.ModelExplorer.GetExplorerForProperty(expression);
if (propertyExplorer != null)
{
return propertyExplorer;
}
}
if (viewDataInfo != null) if (viewDataInfo != null)
{ {
ModelExplorer containerExplorer = viewData.ModelExplorer;
if (viewDataInfo.Container != null) if (viewDataInfo.Container != null)
{ {
containerType = viewDataInfo.Container.GetType(); containerExplorer = metadataProvider.GetModelExplorerForType(
container = viewDataInfo.Container; viewDataInfo.Container.GetType(),
viewDataInfo.Container);
} }
modelAccessor = () => viewDataInfo.Value;
if (viewDataInfo.PropertyInfo != null) if (viewDataInfo.PropertyInfo != null)
{ {
propertyName = viewDataInfo.PropertyInfo.Name; // We've identified a property access, which provides us with accurate metadata.
modelType = viewDataInfo.PropertyInfo.PropertyType; 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) else if (viewDataInfo.Value != null)
{ {
// We only need to delay accessing properties (for LINQ to SQL) // We have a value, even though we may not know where it came from.
modelType = viewDataInfo.Value.GetType(); var valueMetadata = metadataProvider.GetMetadataForType(viewDataInfo.Value.GetType());
} return containerExplorer.GetExplorerForExpression(valueMetadata, viewDataInfo.Value);
}
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;
} }
} }
return GetMetadataFromProvider(modelAccessor, // Treat the expression as string if we don't find anything better.
modelType ?? typeof(string), var stringMetadata = metadataProvider.GetMetadataForType(typeof(string));
propertyName, return viewData.ModelExplorer.GetExplorerForExpression(stringMetadata, modelAccessor: null);
container,
containerType,
metadataProvider);
} }
private static ModelMetadata FromModel([NotNull] ViewDataDictionary viewData, private static ModelExplorer FromModel(
IModelMetadataProvider metadataProvider) [NotNull] ViewDataDictionary viewData,
IModelMetadataProvider metadataProvider)
{ {
if (viewData.ModelMetadata.ModelType == typeof(object)) if (viewData.ModelMetadata.ModelType == typeof(object))
{ {
// Use common simple type rather than object so e.g. Editor() at least generates a TextBox. // Use common simple type rather than object so e.g. Editor() at least generates a TextBox.
return GetMetadataFromProvider( var model = viewData.Model == null ? null : Convert.ToString(viewData.Model, CultureInfo.CurrentCulture);
modelAccessor: null, return metadataProvider.GetModelExplorerForType(typeof(string), model);
modelType: typeof(string),
propertyName: null,
container: null,
containerType: null,
metadataProvider: metadataProvider);
} }
else 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) public static string CollectionTemplate(IHtmlHelper htmlHelper)
{ {
var model = htmlHelper.ViewData.ModelMetadata.Model; var model = htmlHelper.ViewData.Model;
if (model == null) if (model == null)
{ {
return string.Empty; return string.Empty;
@ -136,14 +136,14 @@ namespace Microsoft.AspNet.Mvc.Rendering
itemType = item.GetType(); 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 fieldName = string.Format(CultureInfo.InvariantCulture, "{0}[{1}]", fieldNameBase, index++);
var templateBuilder = new TemplateBuilder( var templateBuilder = new TemplateBuilder(
viewEngine, viewEngine,
htmlHelper.ViewContext, htmlHelper.ViewContext,
htmlHelper.ViewData, htmlHelper.ViewData,
metadata, modelExplorer,
htmlFieldName: fieldName, htmlFieldName: fieldName,
templateName: null, templateName: null,
readOnly: true, readOnly: true,
@ -163,10 +163,10 @@ namespace Microsoft.AspNet.Mvc.Rendering
public static string DecimalTemplate(IHtmlHelper htmlHelper) 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 = 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); return StringTemplate(htmlHelper);
@ -203,18 +203,18 @@ namespace Microsoft.AspNet.Mvc.Rendering
{ {
var viewData = htmlHelper.ViewData; var viewData = htmlHelper.ViewData;
var templateInfo = viewData.TemplateInfo; var templateInfo = viewData.TemplateInfo;
var modelMetadata = viewData.ModelMetadata; var modelExplorer = viewData.ModelExplorer;
var builder = new StringBuilder(); var builder = new StringBuilder();
if (modelMetadata.Model == null) if (modelExplorer.Model == null)
{ {
return modelMetadata.NullDisplayText; return modelExplorer.Metadata.NullDisplayText;
} }
if (templateInfo.TemplateDepth > 1) if (templateInfo.TemplateDepth > 1)
{ {
var text = modelMetadata.SimpleDisplayText; var text = modelExplorer.GetSimpleDisplayText();
if (modelMetadata.HtmlEncode) if (modelExplorer.Metadata.HtmlEncode)
{ {
text = htmlHelper.Encode(text); text = htmlHelper.Encode(text);
} }
@ -224,9 +224,15 @@ namespace Microsoft.AspNet.Mvc.Rendering
var serviceProvider = htmlHelper.ViewContext.HttpContext.RequestServices; var serviceProvider = htmlHelper.ViewContext.HttpContext.RequestServices;
var viewEngine = serviceProvider.GetRequiredService<ICompositeViewEngine>(); 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"); var divTag = new TagBuilder("div");
if (!propertyMetadata.HideSurroundingHtml) if (!propertyMetadata.HideSurroundingHtml)
@ -250,7 +256,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
viewEngine, viewEngine,
htmlHelper.ViewContext, htmlHelper.ViewContext,
htmlHelper.ViewData, htmlHelper.ViewData,
propertyMetadata, propertyExplorer,
htmlFieldName: propertyMetadata.PropertyName, htmlFieldName: propertyMetadata.PropertyName,
templateName: null, templateName: null,
readOnly: true, readOnly: true,
@ -267,12 +273,12 @@ namespace Microsoft.AspNet.Mvc.Rendering
return builder.ToString(); return builder.ToString();
} }
private static bool ShouldShow(ModelMetadata metadata, TemplateInfo templateInfo) private static bool ShouldShow(ModelExplorer modelExplorer, TemplateInfo templateInfo)
{ {
return return
metadata.ShowForDisplay && modelExplorer.Metadata.ShowForDisplay &&
!metadata.IsComplexType && !modelExplorer.Metadata.IsComplexType &&
!templateInfo.Visited(metadata); !templateInfo.Visited(modelExplorer);
} }
public static string StringTemplate(IHtmlHelper htmlHelper) public static string StringTemplate(IHtmlHelper htmlHelper)

View File

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

View File

@ -90,20 +90,20 @@ namespace Microsoft.AspNet.Mvc.Rendering
/// <inheritdoc /> /// <inheritdoc />
public virtual TagBuilder GenerateCheckBox( public virtual TagBuilder GenerateCheckBox(
[NotNull] ViewContext viewContext, [NotNull] ViewContext viewContext,
ModelMetadata metadata, ModelExplorer modelExplorer,
string expression, string expression,
bool? isChecked, bool? isChecked,
object htmlAttributes) object htmlAttributes)
{ {
if (metadata != null) if (modelExplorer != null)
{ {
// CheckBoxFor() case. That API does not support passing isChecked directly. // CheckBoxFor() case. That API does not support passing isChecked directly.
Debug.Assert(!isChecked.HasValue); Debug.Assert(!isChecked.HasValue);
if (metadata.Model != null) if (modelExplorer.Model != null)
{ {
bool modelChecked; bool modelChecked;
if (Boolean.TryParse(metadata.Model.ToString(), out modelChecked)) if (Boolean.TryParse(modelExplorer.Model.ToString(), out modelChecked))
{ {
isChecked = modelChecked; isChecked = modelChecked;
} }
@ -121,10 +121,10 @@ namespace Microsoft.AspNet.Mvc.Rendering
return GenerateInput( return GenerateInput(
viewContext, viewContext,
InputType.CheckBox, InputType.CheckBox,
metadata, modelExplorer,
expression, expression,
value: "true", value: "true",
useViewData: (metadata == null && !isChecked.HasValue), useViewData: (modelExplorer == null && !isChecked.HasValue),
isChecked: isChecked ?? false, isChecked: isChecked ?? false,
setId: true, setId: true,
isExplicitValue: false, isExplicitValue: false,
@ -135,7 +135,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
/// <inheritdoc /> /// <inheritdoc />
public virtual TagBuilder GenerateHiddenForCheckbox( public virtual TagBuilder GenerateHiddenForCheckbox(
[NotNull] ViewContext viewContext, [NotNull] ViewContext viewContext,
ModelMetadata metadata, ModelExplorer modelExplorer,
string expression) string expression)
{ {
var tagBuilder = new TagBuilder("input"); var tagBuilder = new TagBuilder("input");
@ -202,7 +202,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
/// <inheritdoc /> /// <inheritdoc />
public virtual TagBuilder GenerateHidden( public virtual TagBuilder GenerateHidden(
[NotNull] ViewContext viewContext, [NotNull] ViewContext viewContext,
ModelMetadata metadata, ModelExplorer modelExplorer,
string expression, string expression,
object value, object value,
bool useViewData, bool useViewData,
@ -219,7 +219,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
return GenerateInput( return GenerateInput(
viewContext, viewContext,
InputType.Hidden, InputType.Hidden,
metadata, modelExplorer,
expression, expression,
value, value,
useViewData, useViewData,
@ -233,12 +233,14 @@ namespace Microsoft.AspNet.Mvc.Rendering
/// <inheritdoc /> /// <inheritdoc />
public virtual TagBuilder GenerateLabel( public virtual TagBuilder GenerateLabel(
[NotNull] ViewContext viewContext, [NotNull] ViewContext viewContext,
[NotNull] ModelMetadata metadata, [NotNull] ModelExplorer modelExplorer,
string expression, string expression,
string labelText, string labelText,
object htmlAttributes) object htmlAttributes)
{ {
var resolvedLabelText = labelText ?? metadata.DisplayName ?? metadata.PropertyName; var resolvedLabelText = labelText ??
modelExplorer.Metadata.DisplayName ??
modelExplorer.Metadata.PropertyName;
if (resolvedLabelText == null) if (resolvedLabelText == null)
{ {
resolvedLabelText = resolvedLabelText =
@ -263,7 +265,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
/// <inheritdoc /> /// <inheritdoc />
public virtual TagBuilder GeneratePassword( public virtual TagBuilder GeneratePassword(
[NotNull] ViewContext viewContext, [NotNull] ViewContext viewContext,
ModelMetadata metadata, ModelExplorer modelExplorer,
string expression, string expression,
object value, object value,
object htmlAttributes) object htmlAttributes)
@ -272,7 +274,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
return GenerateInput( return GenerateInput(
viewContext, viewContext,
InputType.Password, InputType.Password,
metadata, modelExplorer,
expression, expression,
value, value,
useViewData: false, useViewData: false,
@ -286,14 +288,14 @@ namespace Microsoft.AspNet.Mvc.Rendering
/// <inheritdoc /> /// <inheritdoc />
public virtual TagBuilder GenerateRadioButton( public virtual TagBuilder GenerateRadioButton(
[NotNull] ViewContext viewContext, [NotNull] ViewContext viewContext,
ModelMetadata metadata, ModelExplorer modelExplorer,
string expression, string expression,
object value, object value,
bool? isChecked, bool? isChecked,
object htmlAttributes) object htmlAttributes)
{ {
var htmlAttributeDictionary = GetHtmlAttributeDictionaryOrNull(htmlAttributes); var htmlAttributeDictionary = GetHtmlAttributeDictionaryOrNull(htmlAttributes);
if (metadata == null) if (modelExplorer == null)
{ {
// RadioButton() case. Do not override checked attribute if isChecked is implicit. // RadioButton() case. Do not override checked attribute if isChecked is implicit.
if (!isChecked.HasValue && if (!isChecked.HasValue &&
@ -322,7 +324,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
// Need a value to determine isChecked. // Need a value to determine isChecked.
Debug.Assert(value != null); Debug.Assert(value != null);
var model = metadata.Model; var model = modelExplorer.Model;
var valueString = Convert.ToString(value, CultureInfo.CurrentCulture); var valueString = Convert.ToString(value, CultureInfo.CurrentCulture);
isChecked = model != null && isChecked = model != null &&
string.Equals(model.ToString(), valueString, StringComparison.OrdinalIgnoreCase); string.Equals(model.ToString(), valueString, StringComparison.OrdinalIgnoreCase);
@ -337,7 +339,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
return GenerateInput( return GenerateInput(
viewContext, viewContext,
InputType.Radio, InputType.Radio,
metadata, modelExplorer,
expression, expression,
value, value,
useViewData: false, useViewData: false,
@ -365,7 +367,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
/// <inheritdoc /> /// <inheritdoc />
public TagBuilder GenerateSelect( public TagBuilder GenerateSelect(
[NotNull] ViewContext viewContext, [NotNull] ViewContext viewContext,
ModelMetadata metadata, ModelExplorer modelExplorer,
string optionLabel, string optionLabel,
string expression, string expression,
IEnumerable<SelectListItem> selectList, IEnumerable<SelectListItem> selectList,
@ -375,7 +377,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
ICollection<string> ignored; ICollection<string> ignored;
return GenerateSelect( return GenerateSelect(
viewContext, viewContext,
metadata, modelExplorer,
optionLabel, optionLabel,
expression, expression,
selectList, selectList,
@ -387,7 +389,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
/// <inheritdoc /> /// <inheritdoc />
public virtual TagBuilder GenerateSelect( public virtual TagBuilder GenerateSelect(
[NotNull] ViewContext viewContext, [NotNull] ViewContext viewContext,
ModelMetadata metadata, ModelExplorer modelExplorer,
string optionLabel, string optionLabel,
string expression, string expression,
IEnumerable<SelectListItem> selectList, IEnumerable<SelectListItem> selectList,
@ -428,9 +430,9 @@ namespace Microsoft.AspNet.Mvc.Rendering
{ {
defaultValue = viewContext.ViewData.Eval(expression); 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; return tagBuilder;
} }
@ -477,7 +479,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
/// <inheritdoc /> /// <inheritdoc />
public virtual TagBuilder GenerateTextArea( public virtual TagBuilder GenerateTextArea(
[NotNull] ViewContext viewContext, [NotNull] ViewContext viewContext,
ModelMetadata metadata, ModelExplorer modelExplorer,
string expression, string expression,
int rows, int rows,
int columns, int columns,
@ -509,9 +511,9 @@ namespace Microsoft.AspNet.Mvc.Rendering
{ {
value = modelState.Value.AttemptedValue; 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"); var tagBuilder = new TagBuilder("textarea");
@ -528,7 +530,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
} }
tagBuilder.MergeAttribute("name", fullName, true); 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 there are any errors for a named field, we add this CSS attribute.
if (modelState != null && modelState.Errors.Count > 0) if (modelState != null && modelState.Errors.Count > 0)
@ -546,7 +548,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
/// <inheritdoc /> /// <inheritdoc />
public virtual TagBuilder GenerateTextBox( public virtual TagBuilder GenerateTextBox(
[NotNull] ViewContext viewContext, [NotNull] ViewContext viewContext,
ModelMetadata metadata, ModelExplorer modelExplorer,
string expression, string expression,
object value, object value,
string format, string format,
@ -556,10 +558,10 @@ namespace Microsoft.AspNet.Mvc.Rendering
return GenerateInput( return GenerateInput(
viewContext, viewContext,
InputType.Text, InputType.Text,
metadata, modelExplorer,
expression, expression,
value, value,
useViewData: (metadata == null && value == null), useViewData: (modelExplorer == null && value == null),
isChecked: false, isChecked: false,
setId: true, setId: true,
isExplicitValue: true, isExplicitValue: true,
@ -726,17 +728,19 @@ namespace Microsoft.AspNet.Mvc.Rendering
/// <inheritdoc /> /// <inheritdoc />
public IEnumerable<ModelClientValidationRule> GetClientValidationRules( public IEnumerable<ModelClientValidationRule> GetClientValidationRules(
[NotNull] ViewContext viewContext, [NotNull] ViewContext viewContext,
ModelMetadata metadata, ModelExplorer modelExplorer,
string expression) string expression)
{ {
var validatorProvider = _bindingContextAccessor.Value.ValidatorProvider; var validatorProvider = _bindingContextAccessor.Value.ValidatorProvider;
metadata = metadata ?? modelExplorer = modelExplorer ??
ExpressionMetadataProvider.FromStringExpression(expression, viewContext.ViewData, _metadataProvider); ExpressionMetadataProvider.FromStringExpression(expression, viewContext.ViewData, _metadataProvider);
var validationContext = var validationContext = new ClientModelValidationContext(
new ClientModelValidationContext(metadata, _metadataProvider, viewContext.HttpContext.RequestServices); modelExplorer.Metadata,
_metadataProvider,
viewContext.HttpContext.RequestServices);
return validatorProvider return validatorProvider
.GetValidators(metadata) .GetValidators(modelExplorer.Metadata)
.OfType<IClientModelValidator>() .OfType<IClientModelValidator>()
.SelectMany(v => v.GetClientValidationRules(validationContext)); .SelectMany(v => v.GetClientValidationRules(validationContext));
} }
@ -832,7 +836,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
protected virtual TagBuilder GenerateInput( protected virtual TagBuilder GenerateInput(
[NotNull] ViewContext viewContext, [NotNull] ViewContext viewContext,
InputType inputType, InputType inputType,
ModelMetadata metadata, ModelExplorer modelExplorer,
string expression, string expression,
object value, object value,
bool useViewData, bool useViewData,
@ -926,7 +930,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
tagBuilder.AddCssClass(HtmlHelper.ValidationInputCssClassName); tagBuilder.AddCssClass(HtmlHelper.ValidationInputCssClassName);
} }
tagBuilder.MergeAttributes(GetValidationAttributes(viewContext, metadata, expression)); tagBuilder.MergeAttributes(GetValidationAttributes(viewContext, modelExplorer, expression));
return tagBuilder; return tagBuilder;
} }
@ -951,7 +955,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
// never rendered validation for a field with this name in this form. // never rendered validation for a field with this name in this form.
protected virtual IDictionary<string, object> GetValidationAttributes( protected virtual IDictionary<string, object> GetValidationAttributes(
ViewContext viewContext, ViewContext viewContext,
ModelMetadata metadata, ModelExplorer modelExplorer,
string expression) string expression)
{ {
var formContext = viewContext.ClientValidationEnabled ? viewContext.FormContext : null; var formContext = viewContext.ClientValidationEnabled ? viewContext.FormContext : null;
@ -967,7 +971,8 @@ namespace Microsoft.AspNet.Mvc.Rendering
} }
formContext.RenderedField(fullName, true); formContext.RenderedField(fullName, true);
var clientRules = GetClientValidationRules(viewContext, metadata, expression);
var clientRules = GetClientValidationRules(viewContext, modelExplorer, expression);
return UnobtrusiveValidationAttributesGenerator.GetValidationAttributes(clientRules); return UnobtrusiveValidationAttributesGenerator.GetValidationAttributes(clientRules);
} }
@ -1153,4 +1158,4 @@ namespace Microsoft.AspNet.Mvc.Rendering
return tagBuilder.ToString(TagRenderMode.Normal); 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) public HtmlString CheckBox(string expression, bool? isChecked, object htmlAttributes)
{ {
return GenerateCheckBox( return GenerateCheckBox(
metadata: null, modelExplorer: null,
expression: expression, expression: expression,
isChecked: isChecked, isChecked: isChecked,
htmlAttributes: htmlAttributes); htmlAttributes: htmlAttributes);
@ -282,15 +282,15 @@ namespace Microsoft.AspNet.Mvc.Rendering
/// <inheritdoc /> /// <inheritdoc />
public string DisplayName(string expression) public string DisplayName(string expression)
{ {
var metadata = ExpressionMetadataProvider.FromStringExpression(expression, ViewData, MetadataProvider); var modelExplorer = ExpressionMetadataProvider.FromStringExpression(expression, ViewData, MetadataProvider);
return GenerateDisplayName(metadata, expression); return GenerateDisplayName(modelExplorer, expression);
} }
/// <inheritdoc /> /// <inheritdoc />
public string DisplayText(string expression) public string DisplayText(string expression)
{ {
var metadata = ExpressionMetadataProvider.FromStringExpression(expression, ViewData, MetadataProvider); var modelExplorer = ExpressionMetadataProvider.FromStringExpression(expression, ViewData, MetadataProvider);
return GenerateDisplayText(metadata); return GenerateDisplayText(modelExplorer);
} }
/// <inheritdoc /> /// <inheritdoc />
@ -301,7 +301,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
object htmlAttributes) object htmlAttributes)
{ {
return GenerateDropDown( return GenerateDropDown(
metadata: null, modelExplorer: null,
expression: expression, expression: expression,
selectList: selectList, selectList: selectList,
optionLabel: optionLabel, optionLabel: optionLabel,
@ -315,10 +315,10 @@ namespace Microsoft.AspNet.Mvc.Rendering
string htmlFieldName, string htmlFieldName,
object additionalViewData) object additionalViewData)
{ {
var metadata = ExpressionMetadataProvider.FromStringExpression(expression, ViewData, MetadataProvider); var modelExplorer = ExpressionMetadataProvider.FromStringExpression(expression, ViewData, MetadataProvider);
return GenerateEditor( return GenerateEditor(
metadata, modelExplorer,
htmlFieldName ?? ExpressionHelper.GetExpressionText(expression), htmlFieldName ?? ExpressionHelper.GetExpressionText(expression),
templateName, templateName,
additionalViewData); additionalViewData);
@ -328,7 +328,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
public HtmlString Hidden(string expression, object value, object htmlAttributes) public HtmlString Hidden(string expression, object value, object htmlAttributes)
{ {
return GenerateHidden( return GenerateHidden(
metadata: null, modelExplorer: null,
expression: expression, expression: expression,
value: value, value: value,
useViewData: (value == null), useViewData: (value == null),
@ -344,9 +344,9 @@ namespace Microsoft.AspNet.Mvc.Rendering
/// <inheritdoc /> /// <inheritdoc />
public HtmlString Label(string expression, string labelText, object htmlAttributes) 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( return GenerateLabel(
metadata, modelExplorer,
expression, expression,
labelText, labelText,
htmlAttributes); htmlAttributes);
@ -356,7 +356,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
public HtmlString ListBox(string expression, IEnumerable<SelectListItem> selectList, object htmlAttributes) public HtmlString ListBox(string expression, IEnumerable<SelectListItem> selectList, object htmlAttributes)
{ {
return GenerateListBox( return GenerateListBox(
metadata: null, modelExplorer: null,
expression: expression, expression: expression,
selectList: selectList, selectList: selectList,
htmlAttributes: htmlAttributes); htmlAttributes: htmlAttributes);
@ -388,7 +388,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
return RenderPartialCoreAsync(partialViewName, model, viewData, ViewContext.Writer); return RenderPartialCoreAsync(partialViewName, model, viewData, ViewContext.Writer);
} }
protected virtual HtmlString GenerateDisplay(ModelMetadata metadata, protected virtual HtmlString GenerateDisplay(ModelExplorer modelExplorer,
string htmlFieldName, string htmlFieldName,
string templateName, string templateName,
object additionalViewData) object additionalViewData)
@ -396,7 +396,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
var templateBuilder = new TemplateBuilder(_viewEngine, var templateBuilder = new TemplateBuilder(_viewEngine,
ViewContext, ViewContext,
ViewData, ViewData,
metadata, modelExplorer,
htmlFieldName, htmlFieldName,
templateName, templateName,
readOnly: true, readOnly: true,
@ -443,7 +443,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
public HtmlString Password(string expression, object value, object htmlAttributes) public HtmlString Password(string expression, object value, object htmlAttributes)
{ {
return GeneratePassword( return GeneratePassword(
metadata: null, modelExplorer: null,
expression: expression, expression: expression,
value: value, value: value,
htmlAttributes: htmlAttributes); htmlAttributes: htmlAttributes);
@ -453,7 +453,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
public HtmlString RadioButton(string expression, object value, bool? isChecked, object htmlAttributes) public HtmlString RadioButton(string expression, object value, bool? isChecked, object htmlAttributes)
{ {
return GenerateRadioButton( return GenerateRadioButton(
metadata: null, modelExplorer: null,
expression: expression, expression: expression,
value: value, value: value,
isChecked: isChecked, isChecked: isChecked,
@ -535,19 +535,29 @@ namespace Microsoft.AspNet.Mvc.Rendering
/// <inheritdoc /> /// <inheritdoc />
public HtmlString TextArea(string expression, string value, int rows, int columns, object htmlAttributes) 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) 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 /> /// <inheritdoc />
public HtmlString TextBox(string expression, object value, string format, object htmlAttributes) 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); htmlAttributes: htmlAttributes);
} }
@ -568,18 +578,19 @@ namespace Microsoft.AspNet.Mvc.Rendering
} }
protected virtual HtmlString GenerateCheckBox( protected virtual HtmlString GenerateCheckBox(
ModelMetadata metadata, ModelExplorer modelExplorer,
string expression, string expression,
bool? isChecked, bool? isChecked,
object htmlAttributes) object htmlAttributes)
{ {
var checkbox = _htmlGenerator.GenerateCheckBox( var checkbox = _htmlGenerator.GenerateCheckBox(
ViewContext, ViewContext,
metadata, modelExplorer,
expression, expression,
isChecked, isChecked,
htmlAttributes); htmlAttributes);
var hidden = _htmlGenerator.GenerateHiddenForCheckbox(ViewContext, metadata, expression);
var hidden = _htmlGenerator.GenerateHiddenForCheckbox(ViewContext, modelExplorer, expression);
if (checkbox == null || hidden == null) if (checkbox == null || hidden == null)
{ {
return HtmlString.Empty; return HtmlString.Empty;
@ -590,12 +601,12 @@ namespace Microsoft.AspNet.Mvc.Rendering
return new HtmlString(elements); 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 don't call ModelMetadata.GetDisplayName here because
// we want to fall back to the field name rather than the ModelType. // 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. // 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) if (resolvedDisplayName == null)
{ {
resolvedDisplayName = resolvedDisplayName =
@ -605,13 +616,13 @@ namespace Microsoft.AspNet.Mvc.Rendering
return resolvedDisplayName; 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( protected HtmlString GenerateDropDown(
ModelMetadata metadata, ModelExplorer modelExplorer,
string expression, string expression,
IEnumerable<SelectListItem> selectList, IEnumerable<SelectListItem> selectList,
string optionLabel, string optionLabel,
@ -619,7 +630,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
{ {
var tagBuilder = _htmlGenerator.GenerateSelect( var tagBuilder = _htmlGenerator.GenerateSelect(
ViewContext, ViewContext,
metadata, modelExplorer,
optionLabel, optionLabel,
expression: expression, expression: expression,
selectList: selectList, selectList: selectList,
@ -634,7 +645,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
} }
protected virtual HtmlString GenerateEditor( protected virtual HtmlString GenerateEditor(
ModelMetadata metadata, ModelExplorer modelExplorer,
string htmlFieldName, string htmlFieldName,
string templateName, string templateName,
object additionalViewData) object additionalViewData)
@ -643,7 +654,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
_viewEngine, _viewEngine,
ViewContext, ViewContext,
ViewData, ViewData,
metadata, modelExplorer,
htmlFieldName, htmlFieldName,
templateName, templateName,
readOnly: false, readOnly: false,
@ -742,7 +753,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
} }
protected virtual HtmlString GenerateHidden( protected virtual HtmlString GenerateHidden(
ModelMetadata metadata, ModelExplorer modelExplorer,
string expression, string expression,
object value, object value,
bool useViewData, bool useViewData,
@ -751,7 +762,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
var tagBuilder = var tagBuilder =
_htmlGenerator.GenerateHidden( _htmlGenerator.GenerateHidden(
ViewContext, ViewContext,
metadata, modelExplorer,
expression, expression,
value, value,
useViewData, useViewData,
@ -773,14 +784,14 @@ namespace Microsoft.AspNet.Mvc.Rendering
} }
protected virtual HtmlString GenerateLabel( protected virtual HtmlString GenerateLabel(
[NotNull] ModelMetadata metadata, [NotNull] ModelExplorer modelExplorer,
string expression, string expression,
string labelText, string labelText,
object htmlAttributes) object htmlAttributes)
{ {
var tagBuilder = _htmlGenerator.GenerateLabel( var tagBuilder = _htmlGenerator.GenerateLabel(
ViewContext, ViewContext,
metadata, modelExplorer,
expression: expression, expression: expression,
labelText: labelText, labelText: labelText,
htmlAttributes: htmlAttributes); htmlAttributes: htmlAttributes);
@ -793,14 +804,14 @@ namespace Microsoft.AspNet.Mvc.Rendering
} }
protected HtmlString GenerateListBox( protected HtmlString GenerateListBox(
ModelMetadata metadata, ModelExplorer modelExplorer,
string expression, string expression,
IEnumerable<SelectListItem> selectList, IEnumerable<SelectListItem> selectList,
object htmlAttributes) object htmlAttributes)
{ {
var tagBuilder = _htmlGenerator.GenerateSelect( var tagBuilder = _htmlGenerator.GenerateSelect(
ViewContext, ViewContext,
metadata, modelExplorer,
optionLabel: null, optionLabel: null,
expression: expression, expression: expression,
selectList: selectList, selectList: selectList,
@ -821,14 +832,14 @@ namespace Microsoft.AspNet.Mvc.Rendering
} }
protected virtual HtmlString GeneratePassword( protected virtual HtmlString GeneratePassword(
ModelMetadata metadata, ModelExplorer modelExplorer,
string expression, string expression,
object value, object value,
object htmlAttributes) object htmlAttributes)
{ {
var tagBuilder = _htmlGenerator.GeneratePassword( var tagBuilder = _htmlGenerator.GeneratePassword(
ViewContext, ViewContext,
metadata, modelExplorer,
expression, expression,
value, value,
htmlAttributes); htmlAttributes);
@ -841,7 +852,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
} }
protected virtual HtmlString GenerateRadioButton( protected virtual HtmlString GenerateRadioButton(
ModelMetadata metadata, ModelExplorer modelExplorer,
string expression, string expression,
object value, object value,
bool? isChecked, bool? isChecked,
@ -849,7 +860,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
{ {
var tagBuilder = _htmlGenerator.GenerateRadioButton( var tagBuilder = _htmlGenerator.GenerateRadioButton(
ViewContext, ViewContext,
metadata, modelExplorer,
expression, expression,
value, value,
isChecked, isChecked,
@ -863,7 +874,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
} }
protected virtual HtmlString GenerateTextArea( protected virtual HtmlString GenerateTextArea(
ModelMetadata metadata, ModelExplorer modelExplorer,
string expression, string expression,
int rows, int rows,
int columns, int columns,
@ -871,7 +882,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
{ {
var tagBuilder = _htmlGenerator.GenerateTextArea( var tagBuilder = _htmlGenerator.GenerateTextArea(
ViewContext, ViewContext,
metadata, modelExplorer,
expression, expression,
rows, rows,
columns, columns,
@ -885,7 +896,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
} }
protected virtual HtmlString GenerateTextBox( protected virtual HtmlString GenerateTextBox(
ModelMetadata metadata, ModelExplorer modelExplorer,
string expression, string expression,
object value, object value,
string format, string format,
@ -893,7 +904,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
{ {
var tagBuilder = _htmlGenerator.GenerateTextBox( var tagBuilder = _htmlGenerator.GenerateTextBox(
ViewContext, ViewContext,
metadata, modelExplorer,
expression, expression,
value, value,
format, format,
@ -962,9 +973,8 @@ namespace Microsoft.AspNet.Mvc.Rendering
{ {
if (string.IsNullOrEmpty(expression)) if (string.IsNullOrEmpty(expression))
{ {
// case 2(a): format the value from ModelMetadata for the current model // case 2(a): format the value from ViewData for the current model
var metadata = ViewData.ModelMetadata; resolvedValue = FormatValue(ViewData.Model, format);
resolvedValue = FormatValue(metadata.Model, format);
} }
else else
{ {
@ -983,10 +993,10 @@ namespace Microsoft.AspNet.Mvc.Rendering
/// <inheritdoc /> /// <inheritdoc />
public IEnumerable<ModelClientValidationRule> GetClientValidationRules( public IEnumerable<ModelClientValidationRule> GetClientValidationRules(
ModelMetadata metadata, ModelExplorer modelExplorer,
string expression) 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, [NotNull] Expression<Func<TModel, bool>> expression,
object htmlAttributes) object htmlAttributes)
{ {
var metadata = GetModelMetadata(expression); var modelExplorer = GetModelExplorer(expression);
return GenerateCheckBox(metadata, GetExpressionName(expression), isChecked: null, return GenerateCheckBox(modelExplorer, GetExpressionName(expression), isChecked: null,
htmlAttributes: htmlAttributes); htmlAttributes: htmlAttributes);
} }
@ -68,9 +68,9 @@ namespace Microsoft.AspNet.Mvc.Rendering
string optionLabel, string optionLabel,
object htmlAttributes) 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); optionLabel, htmlAttributes);
} }
@ -81,11 +81,11 @@ namespace Microsoft.AspNet.Mvc.Rendering
string htmlFieldName, string htmlFieldName,
object additionalViewData) object additionalViewData)
{ {
var metadata = ExpressionMetadataProvider.FromLambdaExpression(expression, var modelExplorer = ExpressionMetadataProvider.FromLambdaExpression(expression,
ViewData, ViewData,
MetadataProvider); MetadataProvider);
return GenerateDisplay(metadata, return GenerateDisplay(modelExplorer,
htmlFieldName ?? ExpressionHelper.GetExpressionText(expression), htmlFieldName ?? ExpressionHelper.GetExpressionText(expression),
templateName, templateName,
additionalViewData); additionalViewData);
@ -94,32 +94,32 @@ namespace Microsoft.AspNet.Mvc.Rendering
/// <inheritdoc /> /// <inheritdoc />
public string DisplayNameFor<TResult>([NotNull] Expression<Func<TModel, TResult>> expression) public string DisplayNameFor<TResult>([NotNull] Expression<Func<TModel, TResult>> expression)
{ {
var metadata = GetModelMetadata(expression); var modelExplorer = GetModelExplorer(expression);
return GenerateDisplayName(metadata, ExpressionHelper.GetExpressionText(expression)); return GenerateDisplayName(modelExplorer, ExpressionHelper.GetExpressionText(expression));
} }
/// <inheritdoc /> /// <inheritdoc />
public string DisplayNameForInnerType<TModelItem, TResult>( public string DisplayNameForInnerType<TModelItem, TResult>(
[NotNull] Expression<Func<TModelItem, TResult>> expression) [NotNull] Expression<Func<TModelItem, TResult>> expression)
{ {
var metadata = ExpressionMetadataProvider.FromLambdaExpression<TModelItem, TResult>( var modelExplorer = ExpressionMetadataProvider.FromLambdaExpression<TModelItem, TResult>(
expression, expression,
new ViewDataDictionary<TModelItem>(ViewData, model: null), new ViewDataDictionary<TModelItem>(ViewData, model: null),
MetadataProvider); MetadataProvider);
var expressionText = ExpressionHelper.GetExpressionText(expression); var expressionText = ExpressionHelper.GetExpressionText(expression);
if (metadata == null) if (modelExplorer == null)
{ {
throw new InvalidOperationException(Resources.FormatHtmlHelper_NullModelMetadata(expressionText)); throw new InvalidOperationException(Resources.FormatHtmlHelper_NullModelMetadata(expressionText));
} }
return GenerateDisplayName(metadata, expressionText); return GenerateDisplayName(modelExplorer, expressionText);
} }
/// <inheritdoc /> /// <inheritdoc />
public string DisplayTextFor<TResult>([NotNull] Expression<Func<TModel, TResult>> expression) public string DisplayTextFor<TResult>([NotNull] Expression<Func<TModel, TResult>> expression)
{ {
return GenerateDisplayText(GetModelMetadata(expression)); return GenerateDisplayText(GetModelExplorer(expression));
} }
/// <inheritdoc /> /// <inheritdoc />
@ -129,10 +129,10 @@ namespace Microsoft.AspNet.Mvc.Rendering
string htmlFieldName, string htmlFieldName,
object additionalViewData) object additionalViewData)
{ {
var metadata = ExpressionMetadataProvider.FromLambdaExpression(expression, ViewData, MetadataProvider); var modelExplorer = ExpressionMetadataProvider.FromLambdaExpression(expression, ViewData, MetadataProvider);
return GenerateEditor( return GenerateEditor(
metadata, modelExplorer,
htmlFieldName ?? ExpressionHelper.GetExpressionText(expression), htmlFieldName ?? ExpressionHelper.GetExpressionText(expression),
templateName, templateName,
additionalViewData); additionalViewData);
@ -143,7 +143,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
[NotNull] Expression<Func<TModel, TResult>> expression, [NotNull] Expression<Func<TModel, TResult>> expression,
object htmlAttributes) object htmlAttributes)
{ {
var metadata = GetModelMetadata(expression); var metadata = GetModelExplorer(expression);
return GenerateHidden(metadata, GetExpressionName(expression), metadata.Model, useViewData: false, return GenerateHidden(metadata, GetExpressionName(expression), metadata.Model, useViewData: false,
htmlAttributes: htmlAttributes); htmlAttributes: htmlAttributes);
} }
@ -160,7 +160,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
string labelText, string labelText,
object htmlAttributes) object htmlAttributes)
{ {
var metadata = GetModelMetadata(expression); var metadata = GetModelExplorer(expression);
return GenerateLabel(metadata, ExpressionHelper.GetExpressionText(expression), labelText, htmlAttributes); return GenerateLabel(metadata, ExpressionHelper.GetExpressionText(expression), labelText, htmlAttributes);
} }
@ -170,7 +170,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
IEnumerable<SelectListItem> selectList, IEnumerable<SelectListItem> selectList,
object htmlAttributes) object htmlAttributes)
{ {
var metadata = GetModelMetadata(expression); var metadata = GetModelExplorer(expression);
var name = ExpressionHelper.GetExpressionText(expression); var name = ExpressionHelper.GetExpressionText(expression);
return GenerateListBox(metadata, name, selectList, htmlAttributes); return GenerateListBox(metadata, name, selectList, htmlAttributes);
@ -188,7 +188,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
[NotNull] Expression<Func<TModel, TResult>> expression, [NotNull] Expression<Func<TModel, TResult>> expression,
object htmlAttributes) object htmlAttributes)
{ {
var metadata = GetModelMetadata(expression); var metadata = GetModelExplorer(expression);
return GeneratePassword(metadata, GetExpressionName(expression), value: null, return GeneratePassword(metadata, GetExpressionName(expression), value: null,
htmlAttributes: htmlAttributes); htmlAttributes: htmlAttributes);
} }
@ -199,7 +199,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
[NotNull] object value, [NotNull] object value,
object htmlAttributes) object htmlAttributes)
{ {
var metadata = GetModelMetadata(expression); var metadata = GetModelExplorer(expression);
return GenerateRadioButton(metadata, GetExpressionName(expression), value, isChecked: null, return GenerateRadioButton(metadata, GetExpressionName(expression), value, isChecked: null,
htmlAttributes: htmlAttributes); htmlAttributes: htmlAttributes);
} }
@ -211,7 +211,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
int columns, int columns,
object htmlAttributes) object htmlAttributes)
{ {
var metadata = GetModelMetadata(expression); var metadata = GetModelExplorer(expression);
return GenerateTextArea(metadata, GetExpressionName(expression), rows, columns, htmlAttributes); return GenerateTextArea(metadata, GetExpressionName(expression), rows, columns, htmlAttributes);
} }
@ -221,7 +221,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
string format, string format,
object htmlAttributes) object htmlAttributes)
{ {
var metadata = GetModelMetadata(expression); var metadata = GetModelExplorer(expression);
return GenerateTextBox(metadata, GetExpressionName(expression), metadata.Model, format, htmlAttributes); return GenerateTextBox(metadata, GetExpressionName(expression), metadata.Model, format, htmlAttributes);
} }
@ -230,16 +230,16 @@ namespace Microsoft.AspNet.Mvc.Rendering
return ExpressionHelper.GetExpressionText(expression); 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); var modelExplorer = ExpressionMetadataProvider.FromLambdaExpression(expression, ViewData, MetadataProvider);
if (metadata == null) if (modelExplorer == null)
{ {
var expressionName = GetExpressionName(expression); var expressionName = GetExpressionName(expression);
throw new InvalidOperationException(Resources.FormatHtmlHelper_NullModelMetadata(expressionName)); throw new InvalidOperationException(Resources.FormatHtmlHelper_NullModelMetadata(expressionName));
} }
return metadata; return modelExplorer;
} }
/// <inheritdoc /> /// <inheritdoc />
@ -258,7 +258,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
/// <inheritdoc /> /// <inheritdoc />
public string ValueFor<TResult>([NotNull] Expression<Func<TModel, TResult>> expression, string format) 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, return GenerateValue(ExpressionHelper.GetExpressionText(expression), metadata.Model, format,
useViewData: false); useViewData: false);
} }

View File

@ -32,9 +32,23 @@ namespace Microsoft.AspNet.Mvc.Rendering
TagBuilder GenerateAntiForgery([NotNull] ViewContext viewContext); 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( TagBuilder GenerateCheckBox(
[NotNull] ViewContext viewContext, [NotNull] ViewContext viewContext,
ModelMetadata metadata, ModelExplorer modelExplorer,
string expression, string expression,
bool? isChecked, bool? isChecked,
object htmlAttributes); object htmlAttributes);
@ -46,7 +60,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
/// </summary> /// </summary>
TagBuilder GenerateHiddenForCheckbox( TagBuilder GenerateHiddenForCheckbox(
[NotNull] ViewContext viewContext, [NotNull] ViewContext viewContext,
ModelMetadata metadata, ModelExplorer modelExplorer,
string expression); string expression);
/// <summary> /// <summary>
@ -107,7 +121,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
TagBuilder GenerateHidden( TagBuilder GenerateHidden(
[NotNull] ViewContext viewContext, [NotNull] ViewContext viewContext,
ModelMetadata metadata, ModelExplorer modelExplorer,
string expression, string expression,
object value, object value,
bool useViewData, bool useViewData,
@ -115,21 +129,21 @@ namespace Microsoft.AspNet.Mvc.Rendering
TagBuilder GenerateLabel( TagBuilder GenerateLabel(
[NotNull] ViewContext viewContext, [NotNull] ViewContext viewContext,
[NotNull] ModelMetadata metadata, [NotNull] ModelExplorer modelExplorer,
string expression, string expression,
string labelText, string labelText,
object htmlAttributes); object htmlAttributes);
TagBuilder GeneratePassword( TagBuilder GeneratePassword(
[NotNull] ViewContext viewContext, [NotNull] ViewContext viewContext,
ModelMetadata metadata, ModelExplorer modelExplorer,
string expression, string expression,
object value, object value,
object htmlAttributes); object htmlAttributes);
TagBuilder GenerateRadioButton( TagBuilder GenerateRadioButton(
[NotNull] ViewContext viewContext, [NotNull] ViewContext viewContext,
ModelMetadata metadata, ModelExplorer modelExplorer,
string expression, string expression,
object value, object value,
bool? isChecked, bool? isChecked,
@ -146,7 +160,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
TagBuilder GenerateSelect( TagBuilder GenerateSelect(
[NotNull] ViewContext viewContext, [NotNull] ViewContext viewContext,
ModelMetadata metadata, ModelExplorer modelExplorer,
string optionLabel, string optionLabel,
string expression, string expression,
IEnumerable<SelectListItem> selectList, IEnumerable<SelectListItem> selectList,
@ -155,7 +169,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
TagBuilder GenerateSelect( TagBuilder GenerateSelect(
[NotNull] ViewContext viewContext, [NotNull] ViewContext viewContext,
ModelMetadata metadata, ModelExplorer modelExplorer,
string optionLabel, string optionLabel,
string expression, string expression,
IEnumerable<SelectListItem> selectList, IEnumerable<SelectListItem> selectList,
@ -165,7 +179,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
TagBuilder GenerateTextArea( TagBuilder GenerateTextArea(
[NotNull] ViewContext viewContext, [NotNull] ViewContext viewContext,
ModelMetadata metadata, ModelExplorer modelExplorer,
string expression, string expression,
int rows, int rows,
int columns, int columns,
@ -173,7 +187,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
TagBuilder GenerateTextBox( TagBuilder GenerateTextBox(
[NotNull] ViewContext viewContext, [NotNull] ViewContext viewContext,
ModelMetadata metadata, ModelExplorer modelExplorer,
string expression, string expression,
object value, object value,
string format, string format,
@ -199,7 +213,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
/// </remarks> /// </remarks>
IEnumerable<ModelClientValidationRule> GetClientValidationRules( IEnumerable<ModelClientValidationRule> GetClientValidationRules(
[NotNull] ViewContext viewContext, [NotNull] ViewContext viewContext,
ModelMetadata metadata, ModelExplorer modelExplorer,
string expression); string expression);
} }
} }

View File

@ -12,6 +12,8 @@ namespace Microsoft.AspNet.Mvc.Rendering
private IViewEngine _viewEngine; private IViewEngine _viewEngine;
private ViewContext _viewContext; private ViewContext _viewContext;
private ViewDataDictionary _viewData; private ViewDataDictionary _viewData;
private ModelExplorer _modelExplorer;
private object _model;
private ModelMetadata _metadata; private ModelMetadata _metadata;
private string _htmlFieldName; private string _htmlFieldName;
private string _templateName; private string _templateName;
@ -21,7 +23,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
public TemplateBuilder([NotNull] IViewEngine viewEngine, public TemplateBuilder([NotNull] IViewEngine viewEngine,
[NotNull] ViewContext viewContext, [NotNull] ViewContext viewContext,
[NotNull] ViewDataDictionary viewData, [NotNull] ViewDataDictionary viewData,
[NotNull] ModelMetadata metadata, [NotNull] ModelExplorer modelExplorer,
string htmlFieldName, string htmlFieldName,
string templateName, string templateName,
bool readOnly, bool readOnly,
@ -30,45 +32,51 @@ namespace Microsoft.AspNet.Mvc.Rendering
_viewEngine = viewEngine; _viewEngine = viewEngine;
_viewContext = viewContext; _viewContext = viewContext;
_viewData = viewData; _viewData = viewData;
_metadata = metadata; _modelExplorer = modelExplorer;
_htmlFieldName = htmlFieldName; _htmlFieldName = htmlFieldName;
_templateName = templateName; _templateName = templateName;
_readOnly = readOnly; _readOnly = readOnly;
_additionalViewData = additionalViewData; _additionalViewData = additionalViewData;
_model = modelExplorer.Model;
_metadata = modelExplorer.Metadata;
} }
public string Build() 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; var formattedModelValue = _model;
if (_metadata.Model == null && _readOnly) if (_model == null && _readOnly)
{ {
formattedModelValue = _metadata.NullDisplayText; formattedModelValue = _metadata.NullDisplayText;
} }
var formatString = _readOnly ? _metadata.DisplayFormatString : _metadata.EditFormatString; 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 // 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 // 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; return string.Empty;
} }
var viewData = new ViewDataDictionary(_viewData, model: null) // 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
Model = _metadata.Model, // this might be a typed VDD, and the model value might not be compatible.
ModelMetadata = _metadata 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.FormattedModelValue = formattedModelValue;
viewData.TemplateInfo.HtmlFieldPrefix = _viewData.TemplateInfo.GetFullHtmlFieldName(_htmlFieldName); 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); viewData.TemplateInfo.AddVisited(visitedObjectsKey);
var templateRenderer = new TemplateRenderer(_viewEngine, _viewContext, viewData, _templateName, _readOnly); var templateRenderer = new TemplateRenderer(_viewEngine, _viewContext, viewData, _templateName, _readOnly);

View File

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

View File

@ -352,7 +352,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
/// <paramref name="metadata"/> is <c>null</c>; ignored otherwise. /// <paramref name="metadata"/> is <c>null</c>; ignored otherwise.
/// </param> /// </param>
/// <returns>An <see cref="IEnumerable{ModelClientValidationRule}"/> containing the relevant rules.</returns> /// <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> /// <summary>
/// Returns an &lt;input&gt; element of type "hidden" for the specified <paramref name="expression"/>. /// 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. // 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. // 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.AspNet.Mvc.ModelBinding;
using Microsoft.Framework.Internal; using Microsoft.Framework.Internal;
@ -19,13 +17,13 @@ namespace Microsoft.AspNet.Mvc.Rendering
/// <param name="name"> /// <param name="name">
/// String representation of the <see cref="System.Linq.Expressions.Expression"/> of interest. /// String representation of the <see cref="System.Linq.Expressions.Expression"/> of interest.
/// </param> /// </param>
/// <param name="metadata"> /// <param name="modelExplorer">
/// Metadata about the <see cref="System.Linq.Expressions.Expression"/> of interest. /// Includes the model and metadata about the <see cref="System.Linq.Expressions.Expression"/> of interest.
/// </param> /// </param>
public ModelExpression([NotNull] string name, [NotNull] ModelMetadata metadata) public ModelExpression([NotNull] string name, [NotNull] ModelExplorer modelExplorer)
{ {
Name = name; Name = name;
Metadata = metadata; ModelExplorer = modelExplorer;
} }
/// <summary> /// <summary>
@ -36,10 +34,36 @@ namespace Microsoft.AspNet.Mvc.Rendering
/// <summary> /// <summary>
/// Metadata about the <see cref="System.Linq.Expressions.Expression"/> of interest. /// Metadata about the <see cref="System.Linq.Expressions.Expression"/> of interest.
/// </summary> /// </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> /// <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"/>. /// <see cref="System.Linq.Expressions.Expression"/>.
/// </remarks> /// </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 Type _declaredModelType;
private readonly IModelMetadataProvider _metadataProvider; private readonly IModelMetadataProvider _metadataProvider;
private object _model;
private ModelMetadata _modelMetadata;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ViewDataDictionary"/> class. /// Initializes a new instance of the <see cref="ViewDataDictionary"/> class.
/// </summary> /// </summary>
@ -128,7 +125,7 @@ namespace Microsoft.AspNet.Mvc
templateInfo: new TemplateInfo()) templateInfo: new TemplateInfo())
{ {
// This is the core constructor called when Model is unknown. Base ModelMetadata on the declared type. // 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> /// <summary>
@ -188,16 +185,16 @@ namespace Microsoft.AspNet.Mvc
{ {
// This is the core constructor called when Model is known. // This is the core constructor called when Model is known.
var modelType = GetModelType(model); 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 // Preserve any customizations made to source.ModelExplorer.ModelMetadata if the Type
// SetModel() and source.Model match new instance's values. // that will be calculated in SetModel() and source.Model match new instance's values.
ModelMetadata = source.ModelMetadata; ModelExplorer = source.ModelExplorer;
} }
else if (model == null) else if (model == null)
{ {
// Ensure ModelMetadata is never null though SetModel() isn't called. // Ensure ModelMetadata is never null though SetModel() isn't called below.
ModelMetadata = _metadataProvider.GetMetadataForType(() => null, _declaredModelType); ModelExplorer = _metadataProvider.GetModelExplorerForType(_declaredModelType, model: null);
} }
// If we're constructing a ViewDataDictionary<TModel> where TModel is a non-Nullable value type, // If we're constructing a ViewDataDictionary<TModel> where TModel is a non-Nullable value type,
@ -226,12 +223,11 @@ namespace Microsoft.AspNet.Mvc
{ {
get get
{ {
return _model; return ModelExplorer.Model;
} }
set set
{ {
// Reset ModelMetadata to ensure Model and ModelMetadata.Model remain equal. // Reset ModelExplorer to ensure Model and ModelMetadata.Model remain equal.
_modelMetadata = null;
SetModel(value); SetModel(value);
} }
} }
@ -250,21 +246,15 @@ namespace Microsoft.AspNet.Mvc
{ {
get get
{ {
return _modelMetadata; return ModelExplorer.Metadata;
}
set
{
if (value == null)
{
throw new ArgumentNullException(Resources.FormatPropertyOfTypeCannotBeNull(
nameof(ViewDataDictionary.ModelMetadata),
nameof(ViewDataDictionary)));
}
_modelMetadata = value;
} }
} }
/// <summary>
/// Gets or sets the <see cref="ModelExplorer"/> for the <see cref="Model"/>.
/// </summary>
public ModelExplorer ModelExplorer { get; set; }
public TemplateInfo TemplateInfo { get; } public TemplateInfo TemplateInfo { get; }
#region IDictionary properties #region IDictionary properties
@ -355,15 +345,24 @@ namespace Microsoft.AspNet.Mvc
protected virtual void SetModel(object value) protected virtual void SetModel(object value)
{ {
EnsureCompatible(value); EnsureCompatible(value);
_model = value;
// Reset or override ModelMetadata based on runtime value type. Fall back to declared type if value is // 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 // 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. // a constructor, current ModelMetadata may already be set to preserve customizations made in parent scope.
var modelType = GetModelType(value); 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 boundCollection = new List<TElement>();
var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
var elementMetadata = metadataProvider.GetMetadataForType(typeof(TElement));
var rawValueArray = RawValueToObjectArray(rawValue); var rawValueArray = RawValueToObjectArray(rawValue);
foreach (var rawValueElement in rawValueArray) foreach (var rawValueElement in rawValueArray)
{ {
var innerModelMetadata =
bindingContext.OperationBindingContext.MetadataProvider.GetMetadataForType(null, typeof(TElement));
var innerBindingContext = new ModelBindingContext(bindingContext, var innerBindingContext = new ModelBindingContext(bindingContext,
bindingContext.ModelName, bindingContext.ModelName,
innerModelMetadata) elementMetadata)
{ {
ValueProvider = new CompositeValueProvider ValueProvider = new CompositeValueProvider
{ {
@ -97,13 +98,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
.Select(i => i.ToString(CultureInfo.InvariantCulture)); .Select(i => i.ToString(CultureInfo.InvariantCulture));
} }
var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
var elementMetadata = metadataProvider.GetMetadataForType(typeof(TElement));
var boundCollection = new List<TElement>(); var boundCollection = new List<TElement>();
foreach (var indexName in indexNames) foreach (var indexName in indexNames)
{ {
var fullChildName = ModelBindingHelper.CreateIndexModelName(bindingContext.ModelName, indexName); var fullChildName = ModelBindingHelper.CreateIndexModelName(bindingContext.ModelName, indexName);
var childModelMetadata = var childBindingContext = new ModelBindingContext(bindingContext, fullChildName, elementMetadata);
bindingContext.OperationBindingContext.MetadataProvider.GetMetadataForType(null, typeof(TElement));
var childBindingContext = new ModelBindingContext(bindingContext, fullChildName, childModelMetadata);
var didBind = false; var didBind = false;
object boundValue = null; object boundValue = null;

View File

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

View File

@ -57,8 +57,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
if (modelBindingResult.IsModelSet) 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. // 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 // 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. // the errors with property names rather than the full name.
@ -112,6 +110,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
var newBindingContext = new ModelBindingContext var newBindingContext = new ModelBindingContext
{ {
Model = oldBindingContext.Model,
ModelMetadata = oldBindingContext.ModelMetadata, ModelMetadata = oldBindingContext.ModelMetadata,
ModelName = modelName, ModelName = modelName,
ModelState = oldBindingContext.ModelState, ModelState = oldBindingContext.ModelState,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -26,7 +26,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
return new ModelMetadata( return new ModelMetadata(
this, this,
containerType, containerType,
modelAccessor: null,
modelType: modelType, modelType: modelType,
propertyName: propertyName); 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 /// 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. /// been modified except to add <see cref="ModelMetadata.AdditionalValues"/> entries.
/// </remarks> /// </remarks>
protected override ModelMetadata CreateMetadataFromPrototype([NotNull] ModelMetadata prototype, protected override ModelMetadata CreateMetadataFromPrototype([NotNull] ModelMetadata prototype)
Func<object> modelAccessor)
{ {
var metadata = new ModelMetadata( var metadata = new ModelMetadata(
this, this,
prototype.ContainerType, prototype.ContainerType,
modelAccessor,
prototype.ModelType, prototype.ModelType,
prototype.PropertyName); prototype.PropertyName);
foreach (var keyValuePair in prototype.AdditionalValues) foreach (var keyValuePair in prototype.AdditionalValues)

View File

@ -10,18 +10,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
public interface IModelMetadataProvider public interface IModelMetadataProvider
{ {
IEnumerable<ModelMetadata> GetMetadataForProperties(object container, [NotNull] Type containerType); IEnumerable<ModelMetadata> GetMetadataForProperties([NotNull] Type containerType);
ModelMetadata GetMetadataForProperty( ModelMetadata GetMetadataForType([NotNull] Type modelType);
Func<object> modelAccessor,
[NotNull] Type containerType,
[NotNull] string propertyName);
ModelMetadata GetMetadataForType(Func<object> modelAccessor, [NotNull] Type modelType); ModelMetadata GetMetadataForParameter([NotNull] MethodInfo methodInfo, [NotNull] string parameterName);
ModelMetadata GetMetadataForParameter(
Func<object> modelAccessor,
[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;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.Linq; using System.Linq;
using Microsoft.AspNet.Mvc.ModelBinding.Internal; using Microsoft.AspNet.Mvc.ModelBinding.Internal;
using Microsoft.Framework.Internal; using Microsoft.Framework.Internal;
@ -25,24 +24,18 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
private bool _showForDisplay = true; private bool _showForDisplay = true;
private bool _showForEdit = true; private bool _showForEdit = true;
private object _model;
private Func<object> _modelAccessor;
private int _order = DefaultOrder; private int _order = DefaultOrder;
private bool _isRequired; private bool _isRequired;
private ModelPropertyCollection _properties; private ModelPropertyCollection _properties;
private Type _realModelType;
private string _simpleDisplayText;
public ModelMetadata([NotNull] IModelMetadataProvider provider, public ModelMetadata([NotNull] IModelMetadataProvider provider,
Type containerType, Type containerType,
Func<object> modelAccessor,
[NotNull] Type modelType, [NotNull] Type modelType,
string propertyName) string propertyName)
{ {
Provider = provider; Provider = provider;
_containerType = containerType; _containerType = containerType;
_modelAccessor = modelAccessor;
_modelType = modelType; _modelType = modelType;
_propertyName = propertyName; _propertyName = propertyName;
_isRequired = !modelType.AllowsNullValue(); _isRequired = !modelType.AllowsNullValue();
@ -70,12 +63,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
/// </summary> /// </summary>
public virtual IBinderMetadata BinderMetadata { get; set; } 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 public Type ContainerType
{ {
get { return _containerType; } get { return _containerType; }
@ -88,7 +75,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
} }
/// <summary> /// <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. /// display scenarios.
/// </summary> /// </summary>
/// <value><c>null</c> unless set manually or through additional metadata e.g. attributes.</value> /// <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> /// <summary>
/// Gets or sets the composite format <see cref="string"/> (see /// 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> /// </summary>
public virtual string DisplayFormatString { get; set; } public virtual string DisplayFormatString { get; set; }
@ -106,7 +93,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
/// <summary> /// <summary>
/// Gets or sets the composite format <see cref="string"/> (see /// 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> /// </summary>
/// <remarks> /// <remarks>
/// <see cref="IModelMetadataProvider"/> instances that set this property to a non-<c>null</c>, non-empty, /// <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; } 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 public Type ModelType
{ {
get { return _modelType; } get { return _modelType; }
@ -231,7 +198,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
if (_properties == null) if (_properties == null)
{ {
var properties = Provider.GetMetadataForProperties(Model, RealModelType); var properties = Provider.GetMetadataForProperties(ModelType);
_properties = new ModelPropertyCollection(properties.OrderBy(m => m.Order)); _properties = new ModelPropertyCollection(properties.OrderBy(m => m.Order));
} }
@ -250,46 +217,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
get { return _propertyName; } get { return _propertyName; }
} }
protected IModelMetadataProvider Provider { get; set; } protected IModelMetadataProvider Provider { get; }
/// <returns> /// <summary>
/// Gets runtime <see cref="Type"/> of <see cref="Model"/> if <see cref="Model"/> is non-<c>null</c> and /// Gets or sets a value which is the name of the property used to display the model.
/// <see cref="ModelType"/> is not <see cref="Nullable{T}"/>; <see cref="ModelType"/> otherwise. /// </summary>
/// </returns> public virtual string SimpleDisplayProperty { get; set; }
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> /// <summary>
/// Gets or sets a value that indicates whether the property should be displayed in read-only views. /// 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; 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, private static EfficientTypePropertyKey<Type, string> CreateCacheKey(Type containerType,
Type modelType, Type modelType,
string propertyName) 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> /// </summary>
public OperationBindingContext OperationBindingContext { get; set; } 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> /// <summary>
/// Gets or sets the metadata for the model associated with this context. /// Gets or sets the metadata for the model associated with this context.
/// </summary> /// </summary>

View File

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

View File

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

View File

@ -31,7 +31,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
/// <inheritdoc /> /// <inheritdoc />
public void Validate([NotNull] ModelValidationContext modelValidationContext) public void Validate([NotNull] ModelValidationContext modelValidationContext)
{ {
var metadata = modelValidationContext.ModelMetadata; var modelExplorer = modelValidationContext.ModelExplorer;
var validationContext = new ValidationContext() var validationContext = new ValidationContext()
{ {
ModelValidationContext = modelValidationContext, ModelValidationContext = modelValidationContext,
@ -39,17 +39,23 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
}; };
ValidateNonVisitedNodeAndChildren( ValidateNonVisitedNodeAndChildren(
modelValidationContext.RootPrefix, metadata, validationContext, validators: null); modelValidationContext.RootPrefix,
modelExplorer,
validationContext,
validators: null);
} }
private bool ValidateNonVisitedNodeAndChildren(string modelKey, private bool ValidateNonVisitedNodeAndChildren(
ModelMetadata metadata, ValidationContext validationContext, IEnumerable<IModelValidator> validators) string modelKey,
ModelExplorer modelExplorer,
ValidationContext validationContext,
IEnumerable<IModelValidator> validators)
{ {
// Recursion guard to avoid stack overflows // Recursion guard to avoid stack overflows
RuntimeHelpers.EnsureSufficientExecutionStack(); RuntimeHelpers.EnsureSufficientExecutionStack();
var modelState = validationContext.ModelValidationContext.ModelState; var modelState = validationContext.ModelValidationContext.ModelState;
var bindingSourceMetadata = metadata.BinderMetadata as IBindingSourceMetadata; var bindingSourceMetadata = modelExplorer.Metadata.BinderMetadata as IBindingSourceMetadata;
var bindingSource = bindingSourceMetadata?.BindingSource; var bindingSource = bindingSourceMetadata?.BindingSource;
if (bindingSource != null && !bindingSource.IsFromRequest) 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 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, // 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. // 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 // 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 // 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)) if (IsTypeExcludedFromValidation(_excludeFilterProvider.ExcludeFilters, modelType))
{ {
var result = ShallowValidate(modelKey, metadata, validationContext, validators); var result = ShallowValidate(modelKey, modelExplorer, validationContext, validators);
MarkPropertiesAsSkipped(modelKey, metadata, validationContext); MarkPropertiesAsSkipped(modelKey, modelExplorer.Metadata, validationContext);
return result; return result;
} }
// Check to avoid infinite recursion. This can happen with cycles in an object graph. // 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; return true;
} }
validationContext.Visited.Add(metadata.Model); validationContext.Visited.Add(modelExplorer.Model);
// Validate the children first - depth-first traversal // Validate the children first - depth-first traversal
var enumerableModel = metadata.Model as IEnumerable; var enumerableModel = modelExplorer.Model as IEnumerable;
if (enumerableModel == null) if (enumerableModel == null)
{ {
isValid = ValidateProperties(modelKey, metadata, validationContext); isValid = ValidateProperties(modelKey, modelExplorer, validationContext);
} }
else else
{ {
@ -118,11 +125,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
if (isValid) if (isValid)
{ {
// Don't bother to validate this node if children failed. // 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 // 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; 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; var isValid = true;
foreach (var childMetadata in metadata.Properties) foreach (var property in modelExplorer.Metadata.Properties)
{ {
var propertyName = childMetadata.BinderModelName ?? childMetadata.PropertyName; var propertyExplorer = modelExplorer.GetExplorerForProperty(property.PropertyName);
var childKey = ModelBindingHelper.CreatePropertyModelName(currentModelKey, propertyName); var propertyMetadata = propertyExplorer.Metadata;
if (!ValidateNonVisitedNodeAndChildren(childKey, childMetadata, validationContext, validators: null))
var propertyBindingName = propertyMetadata.BinderModelName ?? propertyMetadata.PropertyName;
var childKey = ModelBindingHelper.CreatePropertyModelName(currentModelKey, propertyBindingName);
if (!ValidateNonVisitedNodeAndChildren(
childKey,
propertyExplorer,
validationContext,
validators: null))
{ {
isValid = false; isValid = false;
} }
@ -171,9 +185,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
private bool ValidateElements(string currentKey, IEnumerable model, ValidationContext validationContext) private bool ValidateElements(string currentKey, IEnumerable model, ValidationContext validationContext)
{ {
var elementType = GetElementType(model.GetType()); var elementType = GetElementType(model.GetType());
var elementMetadata = _modelMetadataProvider.GetMetadataForType( var elementMetadata = _modelMetadataProvider.GetMetadataForType(elementType);
modelAccessor: null,
modelType: elementType);
var validators = validationContext.ModelValidationContext.ValidatorProvider.GetValidators(elementMetadata); 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 it's null, then a shallow validation will be performed.
if (element != null || anyValidatorsDefined) if (element != null || anyValidatorsDefined)
{ {
elementMetadata.Model = element; var elementExplorer = new ModelExplorer(_modelMetadataProvider, elementMetadata, element);
var elementKey = ModelBindingHelper.CreateIndexModelName(currentKey, index); var elementKey = ModelBindingHelper.CreateIndexModelName(currentKey, index);
if (!ValidateNonVisitedNodeAndChildren(elementKey, elementMetadata, validationContext, validators)) if (!ValidateNonVisitedNodeAndChildren(elementKey, elementExplorer, validationContext, validators))
{ {
isValid = false; isValid = false;
} }
@ -207,7 +219,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// Returns true if validation passes successfully // Returns true if validation passes successfully
private static bool ShallowValidate( private static bool ShallowValidate(
string modelKey, string modelKey,
ModelMetadata metadata, ModelExplorer modelExplorer,
ValidationContext validationContext, ValidationContext validationContext,
IEnumerable<IModelValidator> validators) IEnumerable<IModelValidator> validators)
{ {
@ -219,7 +231,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
if (validatorsAsCollection == null || validatorsAsCollection.Count > 0) if (validatorsAsCollection == null || validatorsAsCollection.Count > 0)
{ {
var modelValidationContext = var modelValidationContext =
new ModelValidationContext(validationContext.ModelValidationContext, metadata); new ModelValidationContext(validationContext.ModelValidationContext, modelExplorer);
var modelState = validationContext.ModelValidationContext.ModelState; var modelState = validationContext.ModelValidationContext.ModelState;
var modelValidationState = modelState.GetValidationState(modelKey); var modelValidationState = modelState.GetValidationState(modelKey);
var fieldValidationState = modelState.GetFieldValidationState(modelKey); var fieldValidationState = modelState.GetFieldValidationState(modelKey);
@ -298,4 +310,4 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public HashSet<object> Visited { get; set; } public HashSet<object> Visited { get; set; }
} }
} }
} }

View File

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

View File

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

View File

@ -48,14 +48,14 @@ namespace Microsoft.AspNet.Mvc.Razor
} }
var name = ExpressionHelper.GetExpressionText(expression); var name = ExpressionHelper.GetExpressionText(expression);
var metadata = ExpressionMetadataProvider.FromLambdaExpression(expression, ViewData, _provider); var modelExplorer = ExpressionMetadataProvider.FromLambdaExpression(expression, ViewData, _provider);
if (metadata == null) if (modelExplorer == null)
{ {
throw new InvalidOperationException( throw new InvalidOperationException(
Resources.FormatRazorPage_NullModelMetadata(nameof(IModelMetadataProvider), name)); 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. // Note null or empty For.Name is allowed because TemplateInfo.HtmlFieldPrefix may be sufficient.
// IHtmlGenerator will enforce name requirements. // IHtmlGenerator will enforce name requirements.
var metadata = For.Metadata; var metadata = For.Metadata;
var modelExplorer = For.ModelExplorer;
if (metadata == null) if (metadata == null)
{ {
throw new InvalidOperationException(Resources.FormatTagHelpers_NoProvidedMetadata( throw new InvalidOperationException(Resources.FormatTagHelpers_NoProvidedMetadata(
@ -151,7 +152,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
if (string.IsNullOrEmpty(InputTypeName)) if (string.IsNullOrEmpty(InputTypeName))
{ {
// Note GetInputType never returns null. // Note GetInputType never returns null.
inputType = GetInputType(metadata, out inputTypeHint); inputType = GetInputType(modelExplorer, out inputTypeHint);
} }
else else
{ {
@ -169,15 +170,15 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
switch (inputType) switch (inputType)
{ {
case "checkbox": case "checkbox":
GenerateCheckBox(metadata, output); GenerateCheckBox(modelExplorer, output);
return; return;
case "hidden": case "hidden":
tagBuilder = Generator.GenerateHidden( tagBuilder = Generator.GenerateHidden(
ViewContext, ViewContext,
metadata, modelExplorer,
For.Name, For.Name,
value: metadata.Model, value: For.Model,
useViewData: false, useViewData: false,
htmlAttributes: null); htmlAttributes: null);
break; break;
@ -185,18 +186,18 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
case "password": case "password":
tagBuilder = Generator.GeneratePassword( tagBuilder = Generator.GeneratePassword(
ViewContext, ViewContext,
metadata, modelExplorer,
For.Name, For.Name,
value: null, value: null,
htmlAttributes: null); htmlAttributes: null);
break; break;
case "radio": case "radio":
tagBuilder = GenerateRadio(metadata); tagBuilder = GenerateRadio(modelExplorer);
break; break;
default: default:
tagBuilder = GenerateTextBox(metadata, inputTypeHint, inputType); tagBuilder = GenerateTextBox(modelExplorer, inputTypeHint, inputType);
break; 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( throw new InvalidOperationException(Resources.FormatInputTagHelper_InvalidExpressionResult(
"<input>", "<input>",
ForAttributeName, ForAttributeName,
metadata.RealModelType.FullName, modelExplorer.ModelType.FullName,
typeof(bool).FullName, typeof(bool).FullName,
"type", "type",
"checkbox")); "checkbox"));
@ -230,7 +231,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var tagBuilder = Generator.GenerateCheckBox( var tagBuilder = Generator.GenerateCheckBox(
ViewContext, ViewContext,
metadata, modelExplorer,
For.Name, For.Name,
isChecked: null, isChecked: null,
htmlAttributes: htmlAttributes); htmlAttributes: htmlAttributes);
@ -243,7 +244,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
output.Content += tagBuilder.ToString(TagRenderMode.SelfClosing); output.Content += tagBuilder.ToString(TagRenderMode.SelfClosing);
tagBuilder = Generator.GenerateHiddenForCheckbox(ViewContext, metadata, For.Name); tagBuilder = Generator.GenerateHiddenForCheckbox(ViewContext, modelExplorer, For.Name);
if (tagBuilder != null) if (tagBuilder != null)
{ {
output.Content += tagBuilder.ToString(TagRenderMode.SelfClosing); 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. // Note empty string is allowed.
if (Value == null) if (Value == null)
@ -265,38 +266,38 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
return Generator.GenerateRadioButton( return Generator.GenerateRadioButton(
ViewContext, ViewContext,
metadata, modelExplorer,
For.Name, For.Name,
Value, Value,
isChecked: null, isChecked: null,
htmlAttributes: null); htmlAttributes: null);
} }
private TagBuilder GenerateTextBox(ModelMetadata metadata, string inputTypeHint, string inputType) private TagBuilder GenerateTextBox(ModelExplorer modelExplorer, string inputTypeHint, string inputType)
{ {
var format = Format; var format = Format;
if (string.IsNullOrEmpty(format)) if (string.IsNullOrEmpty(format))
{ {
format = GetFormat(metadata, inputTypeHint, inputType); format = GetFormat(modelExplorer, inputTypeHint, inputType);
} }
return Generator.GenerateTextBox( return Generator.GenerateTextBox(
ViewContext, ViewContext,
metadata, modelExplorer,
For.Name, For.Name,
value: metadata.Model, value: modelExplorer.Model,
format: Format, format: Format,
htmlAttributes: null); htmlAttributes: null);
} }
// Get a fall-back format based on the metadata. // 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 format;
string rfc3339Format; string rfc3339Format;
if (string.Equals("decimal", inputTypeHint, StringComparison.OrdinalIgnoreCase) && if (string.Equals("decimal", inputTypeHint, StringComparison.OrdinalIgnoreCase) &&
string.Equals("text", inputType, StringComparison.Ordinal) && 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. // Decimal data is edited using an <input type="text"/> element, with a reasonable format.
// EditFormatString has precedence over this fall-back 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) && else if (_rfc3339Formats.TryGetValue(inputType, out rfc3339Format) &&
ViewContext.Html5DateRenderingMode == Html5DateRenderingMode.Rfc3339 && ViewContext.Html5DateRenderingMode == Html5DateRenderingMode.Rfc3339 &&
!metadata.HasNonDefaultEditFormat && !modelExplorer.Metadata.HasNonDefaultEditFormat &&
(typeof(DateTime) == metadata.RealModelType || typeof(DateTimeOffset) == metadata.RealModelType)) (typeof(DateTime) == modelExplorer.ModelType || typeof(DateTimeOffset) == modelExplorer.ModelType))
{ {
// Rfc3339 mode _may_ override EditFormatString in a limited number of cases e.g. EditFormatString // 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). // must be a default format (i.e. came from a built-in [DataType] attribute).
@ -314,15 +315,15 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
else else
{ {
// Otherwise use EditFormatString, if any. // Otherwise use EditFormatString, if any.
format = metadata.EditFormatString; format = modelExplorer.Metadata.EditFormatString;
} }
return format; 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; string inputType;
if (_defaultInputTypes.TryGetValue(hint, out 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. // 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[] var inputTypeHints = new string[]
{ {
metadata.TemplateHint, modelExplorer.Metadata.TemplateHint,
metadata.DataTypeName, modelExplorer.Metadata.DataTypeName,
}; };
foreach (string inputTypeHint in inputTypeHints.Where(s => !string.IsNullOrEmpty(s))) 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 // 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/>. // 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) if (typeof(bool?) != fieldType)
{ {
var underlyingType = Nullable.GetUnderlyingType(fieldType); var underlyingType = Nullable.GetUnderlyingType(fieldType);
@ -369,7 +370,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
// Nothing more to provide // Nothing more to provide
yield break; yield break;
} }
else if (!metadata.IsComplexType) else if (!modelExplorer.Metadata.IsComplexType)
{ {
// IsEnum is false for the Enum class itself // IsEnum is false for the Enum class itself
if (fieldType.IsEnum()) if (fieldType.IsEnum())

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -24,8 +24,8 @@ namespace Microsoft.AspNet.Mvc.Core.Test
{ {
// Arrange // Arrange
var metadataProvider = new Mock<IModelMetadataProvider>(); var metadataProvider = new Mock<IModelMetadataProvider>();
metadataProvider.Setup(m => m.GetMetadataForType(It.IsAny<Func<object>>(), It.IsAny<Type>())) metadataProvider.Setup(m => m.GetMetadataForType(It.IsAny<Type>()))
.Returns(new ModelMetadata(metadataProvider.Object, null, null, typeof(MyModel), null)) .Returns(new ModelMetadata(metadataProvider.Object, null, typeof(MyModel), null))
.Verifiable(); .Verifiable();
var binder = new Mock<IModelBinder>(); var binder = new Mock<IModelBinder>();
@ -136,8 +136,8 @@ namespace Microsoft.AspNet.Mvc.Core.Test
{ {
// Arrange // Arrange
var metadataProvider = new Mock<IModelMetadataProvider>(); var metadataProvider = new Mock<IModelMetadataProvider>();
metadataProvider.Setup(m => m.GetMetadataForType(It.IsAny<Func<object>>(), It.IsAny<Type>())) metadataProvider.Setup(m => m.GetMetadataForType(It.IsAny<Type>()))
.Returns(new ModelMetadata(metadataProvider.Object, null, null, typeof(MyModel), null)) .Returns(new ModelMetadata(metadataProvider.Object, null, typeof(MyModel), null))
.Verifiable(); .Verifiable();
var binder = new Mock<IModelBinder>(); var binder = new Mock<IModelBinder>();
@ -227,8 +227,8 @@ namespace Microsoft.AspNet.Mvc.Core.Test
{ {
// Arrange // Arrange
var metadataProvider = new Mock<IModelMetadataProvider>(); var metadataProvider = new Mock<IModelMetadataProvider>();
metadataProvider.Setup(m => m.GetMetadataForType(It.IsAny<Func<object>>(), It.IsAny<Type>())) metadataProvider.Setup(m => m.GetMetadataForType(It.IsAny<Type>()))
.Returns(new ModelMetadata(metadataProvider.Object, null, null, typeof(MyModel), null)) .Returns(new ModelMetadata(metadataProvider.Object, null, typeof(MyModel), null))
.Verifiable(); .Verifiable();
var binder = new Mock<IModelBinder>(); var binder = new Mock<IModelBinder>();
@ -484,8 +484,8 @@ namespace Microsoft.AspNet.Mvc.Core.Test
{ {
// Arrange // Arrange
var metadataProvider = new Mock<IModelMetadataProvider>(); var metadataProvider = new Mock<IModelMetadataProvider>();
metadataProvider.Setup(m => m.GetMetadataForType(It.IsAny<Func<object>>(), It.IsAny<Type>())) metadataProvider.Setup(m => m.GetMetadataForType(It.IsAny<Type>()))
.Returns(new ModelMetadata(metadataProvider.Object, null, null, typeof(MyModel), null)) .Returns(new ModelMetadata(metadataProvider.Object, null, typeof(MyModel), null))
.Verifiable(); .Verifiable();
var binder = new Mock<IModelBinder>(); var binder = new Mock<IModelBinder>();
@ -580,8 +580,8 @@ namespace Microsoft.AspNet.Mvc.Core.Test
{ {
// Arrange // Arrange
var metadataProvider = new Mock<IModelMetadataProvider>(); var metadataProvider = new Mock<IModelMetadataProvider>();
metadataProvider.Setup(m => m.GetMetadataForType(It.IsAny<Func<object>>(), It.IsAny<Type>())) metadataProvider.Setup(m => m.GetMetadataForType(It.IsAny<Type>()))
.Returns(new ModelMetadata(metadataProvider.Object, null, null, typeof(MyModel), null)) .Returns(new ModelMetadata(metadataProvider.Object, null, typeof(MyModel), null))
.Verifiable(); .Verifiable();
var binder = new Mock<IModelBinder>(); var binder = new Mock<IModelBinder>();
@ -655,13 +655,14 @@ namespace Microsoft.AspNet.Mvc.Core.Test
{ {
// Arrange // Arrange
var metadataProvider = new Mock<IModelMetadataProvider>(); var metadataProvider = new Mock<IModelMetadataProvider>();
metadataProvider.Setup(m => m.GetMetadataForType(null, It.IsAny<Type>())) metadataProvider
.Returns(new ModelMetadata(metadataProvider.Object, .Setup(m => m.GetMetadataForType(It.IsAny<Type>()))
containerType: null, .Returns(new ModelMetadata(
modelAccessor: null, metadataProvider.Object,
modelType: typeof(MyModel), containerType: null,
propertyName: null)) modelType: typeof(MyModel),
.Verifiable(); propertyName: null))
.Verifiable();
var binder = new Mock<IModelBinder>(); var binder = new Mock<IModelBinder>();
binder.Setup(b => b.BindModelAsync(It.IsAny<ModelBindingContext>())) 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 IModelMetadataProvider _metadataProvider = new EmptyModelMetadataProvider();
private static readonly ModelMetadata _metadata = _metadataProvider.GetMetadataForProperty( private static readonly ModelMetadata _metadata = _metadataProvider.GetMetadataForProperty(
modelAccessor: null, typeof(string),
containerType: typeof(string), "Length");
propertyName: "Length");
public static TheoryData<string> SomeNames public static TheoryData<string> SomeNames
{ {

View File

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

View File

@ -103,12 +103,10 @@ namespace Microsoft.AspNet.Mvc.Core
{ {
// Arrange // Arrange
var html = DefaultTemplatesUtilities.GetHtmlHelper(); var html = DefaultTemplatesUtilities.GetHtmlHelper();
var metadata =
new EmptyModelMetadataProvider() html.ViewData.ModelMetadata.NullDisplayText = "Null Display Text";
.GetMetadataForType(null, typeof(DefaultTemplatesUtilities.ObjectTemplateModel)); html.ViewData.ModelMetadata.SimpleDisplayProperty = "Property1";
metadata.NullDisplayText = "Null Display Text";
metadata.SimpleDisplayText = "Simple Display Text";
html.ViewData.ModelMetadata = metadata;
html.ViewData.TemplateInfo.AddVisited("foo"); html.ViewData.TemplateInfo.AddVisited("foo");
html.ViewData.TemplateInfo.AddVisited("bar"); html.ViewData.TemplateInfo.AddVisited("bar");
@ -116,7 +114,7 @@ namespace Microsoft.AspNet.Mvc.Core
var result = DefaultEditorTemplates.ObjectTemplate(html); var result = DefaultEditorTemplates.ObjectTemplate(html);
// Assert // Assert
Assert.Equal(metadata.NullDisplayText, result); Assert.Equal(html.ViewData.ModelMetadata.NullDisplayText, result);
} }
[Theory] [Theory]
@ -127,15 +125,17 @@ namespace Microsoft.AspNet.Mvc.Core
string expectedResult) string expectedResult)
{ {
// Arrange // Arrange
var model = new DefaultTemplatesUtilities.ObjectTemplateModel(); var model = new DefaultTemplatesUtilities.ObjectTemplateModel()
{
Property1 = simpleDisplayText,
};
var html = DefaultTemplatesUtilities.GetHtmlHelper(model); var html = DefaultTemplatesUtilities.GetHtmlHelper(model);
var metadata =
new EmptyModelMetadataProvider() html.ViewData.ModelMetadata.HtmlEncode = htmlEncode;
.GetMetadataForType(() => model, typeof(DefaultTemplatesUtilities.ObjectTemplateModel)); html.ViewData.ModelMetadata.NullDisplayText = "Null Display Text";
metadata.HtmlEncode = htmlEncode; html.ViewData.ModelMetadata.SimpleDisplayProperty = "Property1";
metadata.NullDisplayText = "Null Display Text";
metadata.SimpleDisplayText = simpleDisplayText;
html.ViewData.ModelMetadata = metadata;
html.ViewData.TemplateInfo.AddVisited("foo"); html.ViewData.TemplateInfo.AddVisited("foo");
html.ViewData.TemplateInfo.AddVisited("bar"); html.ViewData.TemplateInfo.AddVisited("bar");
@ -917,7 +917,9 @@ Environment.NewLine;
throw new NotImplementedException(); throw new NotImplementedException();
} }
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, string name) public IEnumerable<ModelClientValidationRule> GetClientValidationRules(
ModelExplorer modelExplorer,
string name)
{ {
return Enumerable.Empty<ModelClientValidationRule>(); 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. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
@ -45,6 +46,11 @@ namespace Microsoft.AspNet.Mvc.Rendering
return GetHtmlHelper<ObjectTemplateModel>(model: null); return GetHtmlHelper<ObjectTemplateModel>(model: null);
} }
public static HtmlHelper<IEnumerable<ObjectTemplateModel>> GetHtmlHelperForEnumerable()
{
return GetHtmlHelper<IEnumerable<ObjectTemplateModel>>(model: null);
}
public static HtmlHelper<ObjectTemplateModel> GetHtmlHelper(IUrlHelper urlHelper) public static HtmlHelper<ObjectTemplateModel> GetHtmlHelper(IUrlHelper urlHelper)
{ {
return GetHtmlHelper<ObjectTemplateModel>( return GetHtmlHelper<ObjectTemplateModel>(
@ -82,11 +88,27 @@ namespace Microsoft.AspNet.Mvc.Rendering
return GetHtmlHelper(model, CreateViewEngine()); 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) public static HtmlHelper<ObjectTemplateModel> GetHtmlHelper(IModelMetadataProvider provider)
{ {
return GetHtmlHelper<ObjectTemplateModel>(model: null, provider: 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) public static HtmlHelper<TModel> GetHtmlHelper<TModel>(TModel model, IModelMetadataProvider provider)
{ {
return GetHtmlHelper(model, CreateUrlHelper(), CreateViewEngine(), provider); return GetHtmlHelper(model, CreateUrlHelper(), CreateViewEngine(), provider);
@ -191,8 +213,8 @@ namespace Microsoft.AspNet.Mvc.Rendering
public static string FormatOutput(IHtmlHelper helper, object model) public static string FormatOutput(IHtmlHelper helper, object model)
{ {
var metadata = helper.MetadataProvider.GetMetadataForType(() => model, model.GetType()); var modelExplorer = helper.MetadataProvider.GetModelExplorerForType(model.GetType(), model);
return FormatOutput(metadata); return FormatOutput(modelExplorer);
} }
private static ICompositeViewEngine CreateViewEngine() private static ICompositeViewEngine CreateViewEngine()
@ -203,7 +225,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
.Callback(async (ViewContext v) => .Callback(async (ViewContext v) =>
{ {
view.ToString(); view.ToString();
await v.Writer.WriteAsync(FormatOutput(v.ViewData.ModelMetadata)); await v.Writer.WriteAsync(FormatOutput(v.ViewData.ModelExplorer));
}) })
.Returns(Task.FromResult(0)); .Returns(Task.FromResult(0));
@ -228,14 +250,15 @@ namespace Microsoft.AspNet.Mvc.Rendering
optionsAccessor.Object); optionsAccessor.Object);
} }
private static string FormatOutput(ModelMetadata metadata) private static string FormatOutput(ModelExplorer modelExplorer)
{ {
var metadata = modelExplorer.Metadata;
return string.Format(CultureInfo.InvariantCulture, return string.Format(CultureInfo.InvariantCulture,
"Model = {0}, ModelType = {1}, PropertyName = {2}, SimpleDisplayText = {3}", "Model = {0}, ModelType = {1}, PropertyName = {2}, SimpleDisplayText = {3}",
metadata.Model ?? "(null)", modelExplorer.Model ?? "(null)",
metadata.ModelType == null ? "(null)" : metadata.ModelType.FullName, metadata.ModelType == null ? "(null)" : metadata.ModelType.FullName,
metadata.PropertyName ?? "(null)", metadata.PropertyName ?? "(null)",
metadata.SimpleDisplayText ?? "(null)"); modelExplorer.GetSimpleDisplayText() ?? "(null)");
} }
private static IUrlHelper CreateUrlHelper() private static IUrlHelper CreateUrlHelper()

View File

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

View File

@ -5,7 +5,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using Microsoft.AspNet.Mvc.ModelBinding; using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.Rendering; using Microsoft.AspNet.Mvc.Rendering;
using Moq;
using Xunit; using Xunit;
namespace Microsoft.AspNet.Mvc.Core namespace Microsoft.AspNet.Mvc.Core
@ -43,8 +42,7 @@ namespace Microsoft.AspNet.Mvc.Core
{ {
// Arrange // Arrange
var helper = DefaultTemplatesUtilities.GetHtmlHelper(); var helper = DefaultTemplatesUtilities.GetHtmlHelper();
var enumerableHelper = var enumerableHelper = DefaultTemplatesUtilities.GetHtmlHelperForEnumerable();
DefaultTemplatesUtilities.GetHtmlHelper<IEnumerable<DefaultTemplatesUtilities.ObjectTemplateModel>>(model: null);
// Act // Act
var displayNameResult = helper.DisplayName("Property1"); var displayNameResult = helper.DisplayName("Property1");
@ -57,13 +55,27 @@ namespace Microsoft.AspNet.Mvc.Core
Assert.Equal("Property1", displayNameForEnumerableResult); 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] [Fact]
public void DisplayNameHelpers_ReturnPropertyName_ForNestedProperty() public void DisplayNameHelpers_ReturnPropertyName_ForNestedProperty()
{ {
// Arrange // Arrange
var helper = DefaultTemplatesUtilities.GetHtmlHelper<OuterClass>(model: null); var helper = DefaultTemplatesUtilities.GetHtmlHelper<OuterClass>(model: null);
var enumerableHelper = var enumerableHelper = DefaultTemplatesUtilities.GetHtmlHelperForEnumerable<OuterClass>(model: null);
DefaultTemplatesUtilities.GetHtmlHelper<IEnumerable<OuterClass>>(model: null);
// Act // Act
var displayNameResult = helper.DisplayName("Inner.Id"); var displayNameResult = helper.DisplayName("Inner.Id");
@ -76,86 +88,24 @@ namespace Microsoft.AspNet.Mvc.Core
Assert.Equal("Id", displayNameForEnumerableResult); 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] [Theory]
[InlineData("")] // Empty display name wins over non-empty property name. [InlineData("")] // Empty display name wins over non-empty property name.
[InlineData("Custom display name from metadata")] [InlineData("Custom display name from metadata")]
public void DisplayNameHelpers_ReturnDisplayName_IfNonNull(string displayName) public void DisplayNameHelpers_ReturnDisplayName_IfNonNull(string displayName)
{ {
// Arrange // Arrange
var helper = DefaultTemplatesUtilities.GetHtmlHelper(); var provider = new TestModelMetadataProvider();
helper.ViewData.ModelMetadata.DisplayName = displayName; provider
var enumerableHelper = .ForType<DefaultTemplatesUtilities.ObjectTemplateModel>()
DefaultTemplatesUtilities.GetHtmlHelper<IEnumerable<DefaultTemplatesUtilities.ObjectTemplateModel>>(model: null); .Then(mm => mm.DisplayName = displayName);
enumerableHelper.ViewData.ModelMetadata.DisplayName = displayName;
var helper = DefaultTemplatesUtilities.GetHtmlHelper(provider: provider);
var enumerableHelper = DefaultTemplatesUtilities.GetHtmlHelperForEnumerable(provider: provider);
// Act // Act
var displayNameResult = helper.DisplayName(expression: string.Empty); var displayNameResult = helper.DisplayName(expression: string.Empty);
var displayNameForResult = helper.DisplayNameFor(m => m); var displayNameForResult = helper.DisplayNameFor(m => m);
var displayNameForEnumerableResult = enumerableHelper.DisplayNameFor(m => m); var displayNameForEnumerableResult = enumerableHelper.DisplayNameFor((DefaultTemplatesUtilities.ObjectTemplateModel m) => m);
var displayNameForModelResult = helper.DisplayNameForModel(); var displayNameForModelResult = helper.DisplayNameForModel();
// Assert // Assert
@ -171,15 +121,13 @@ namespace Microsoft.AspNet.Mvc.Core
public void DisplayNameHelpers_ReturnDisplayNameForProperty_IfNonNull(string displayName) public void DisplayNameHelpers_ReturnDisplayNameForProperty_IfNonNull(string displayName)
{ {
// Arrange // Arrange
var metadataHelper = new MetadataHelper(); // All properties will use the same metadata. var provider = new TestModelMetadataProvider();
metadataHelper.Metadata provider
.Setup(metadata => metadata.DisplayName) .ForProperty<DefaultTemplatesUtilities.ObjectTemplateModel>("Property1")
.Returns(displayName); .Then(mm => mm.DisplayName = displayName);
var helper = DefaultTemplatesUtilities.GetHtmlHelper(metadataHelper.MetadataProvider.Object);
var enumerableHelper = var helper = DefaultTemplatesUtilities.GetHtmlHelper(provider: provider);
DefaultTemplatesUtilities.GetHtmlHelper<IEnumerable<DefaultTemplatesUtilities.ObjectTemplateModel>>( var enumerableHelper = DefaultTemplatesUtilities.GetHtmlHelperForEnumerable(provider: provider);
model: null,
provider: metadataHelper.MetadataProvider.Object);
// Act // Act
var displayNameResult = helper.DisplayName("Property1"); var displayNameResult = helper.DisplayName("Property1");
@ -202,8 +150,7 @@ namespace Microsoft.AspNet.Mvc.Core
string expectedResult) string expectedResult)
{ {
// Arrange // Arrange
var metadataHelper = new MetadataHelper(); var helper = DefaultTemplatesUtilities.GetHtmlHelper();
var helper = DefaultTemplatesUtilities.GetHtmlHelper(metadataHelper.MetadataProvider.Object);
// Act // Act
var result = helper.DisplayName(expression); var result = helper.DisplayName(expression);
@ -213,29 +160,6 @@ namespace Microsoft.AspNet.Mvc.Core
Assert.Equal(expectedResult, result); 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] [Fact]
public void DisplayNameFor_ThrowsInvalidOperation_IfExpressionUnsupported() public void DisplayNameFor_ThrowsInvalidOperation_IfExpressionUnsupported()
{ {
@ -303,25 +227,5 @@ namespace Microsoft.AspNet.Mvc.Core
{ {
public InnerClass Inner { get; set; } 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() public void DisplayText_ReturnsSimpleDisplayText_IfSetAndValueNonNull()
{ {
// Arrange // Arrange
var model = new OverriddenToStringModel("Ignored text"); var model = new OverriddenToStringModel("Ignored text")
{
SimpleDisplay = "Simple display text",
};
var helper = DefaultTemplatesUtilities.GetHtmlHelper(model); var helper = DefaultTemplatesUtilities.GetHtmlHelper(model);
helper.ViewData.ModelMetadata.SimpleDisplayText = "Simple display text"; helper.ViewData.ModelMetadata.SimpleDisplayProperty = nameof(OverriddenToStringModel.SimpleDisplay);
// Act // Act
var result = helper.DisplayText(expression: string.Empty); var result = helper.DisplayText(expression: string.Empty);
@ -130,9 +134,13 @@ namespace Microsoft.AspNet.Mvc.Rendering
public void DisplayTextFor_ReturnsSimpleDisplayText_IfSetAndValueNonNull() public void DisplayTextFor_ReturnsSimpleDisplayText_IfSetAndValueNonNull()
{ {
// Arrange // Arrange
var model = new OverriddenToStringModel("Ignored text"); var model = new OverriddenToStringModel("Ignored text")
{
SimpleDisplay = "Simple display text",
};
var helper = DefaultTemplatesUtilities.GetHtmlHelper(model); var helper = DefaultTemplatesUtilities.GetHtmlHelper(model);
helper.ViewData.ModelMetadata.SimpleDisplayText = "Simple display text"; helper.ViewData.ModelMetadata.SimpleDisplayProperty = nameof(OverriddenToStringModel.SimpleDisplay);
// Act // Act
var result = helper.DisplayTextFor(m => m); var result = helper.DisplayTextFor(m => m);
@ -148,9 +156,11 @@ namespace Microsoft.AspNet.Mvc.Rendering
var model = new OverriddenToStringModel("Ignored text") var model = new OverriddenToStringModel("Ignored text")
{ {
Name = "Property value", Name = "Property value",
SimpleDisplay = "Simple display text",
}; };
var helper = DefaultTemplatesUtilities.GetHtmlHelper(model); var helper = DefaultTemplatesUtilities.GetHtmlHelper(model);
helper.ViewData.ModelMetadata.SimpleDisplayText = "Simple display text"; helper.ViewData.ModelMetadata.SimpleDisplayProperty = nameof(OverriddenToStringModel.SimpleDisplay);
// Act // Act
var result = helper.DisplayText("Name"); var result = helper.DisplayText("Name");
@ -276,6 +286,8 @@ namespace Microsoft.AspNet.Mvc.Rendering
_simpleDisplayText = simpleDisplayText; _simpleDisplayText = simpleDisplayText;
} }
public string SimpleDisplay { get; set; }
public string Name { get; set; } public string Name { get; set; }
public override string ToString() public override string ToString()

View File

@ -4,7 +4,6 @@
using System; using System;
using Microsoft.AspNet.Mvc.ModelBinding; using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.Rendering; using Microsoft.AspNet.Mvc.Rendering;
using Moq;
using Xunit; using Xunit;
namespace Microsoft.AspNet.Mvc.Core namespace Microsoft.AspNet.Mvc.Core
@ -67,15 +66,15 @@ namespace Microsoft.AspNet.Mvc.Core
public void LabelHelpers_ReturnEmptyForModel_IfMetadataPropertyNameEmpty() public void LabelHelpers_ReturnEmptyForModel_IfMetadataPropertyNameEmpty()
{ {
// Arrange // Arrange
var provider = new DataAnnotationsModelMetadataProvider();
var metadata = new ModelMetadata( var metadata = new ModelMetadata(
new DataAnnotationsModelMetadataProvider(), provider,
containerType: null, containerType: null,
modelAccessor: null,
modelType: typeof(object), modelType: typeof(object),
propertyName: string.Empty); propertyName: string.Empty);
var helper = DefaultTemplatesUtilities.GetHtmlHelper(); var helper = DefaultTemplatesUtilities.GetHtmlHelper();
helper.ViewData.ModelMetadata = metadata; helper.ViewData.ModelExplorer = new ModelExplorer(provider, metadata, model: null);
// Act // Act
var labelResult = helper.Label(expression: string.Empty); var labelResult = helper.Label(expression: string.Empty);
@ -90,21 +89,20 @@ namespace Microsoft.AspNet.Mvc.Core
Assert.Empty(labelForModelResult.ToString()); Assert.Empty(labelForModelResult.ToString());
} }
[Theory] [Fact]
[InlineData("MyProperty")] public void LabelHelpers_DisplayMetadataPropertyNameForProperty()
[InlineData("Custom property name from metadata")]
public void LabelHelpers_DisplayMetadataPropertyName_IfOverridden(string propertyName)
{ {
// Arrange // Arrange
var metadata = new ModelMetadata( var propertyName = "Property1";
new DataAnnotationsModelMetadataProvider(),
containerType: null, var provider = new EmptyModelMetadataProvider();
modelAccessor: null,
modelType: typeof(string), // Ensure FromStringExpression() doesn't ignore the ModelMetadata. var modelExplorer = provider
propertyName: propertyName); .GetModelExplorerForType(typeof(DefaultTemplatesUtilities.ObjectTemplateModel), model: null)
.GetExplorerForProperty(propertyName);
var helper = DefaultTemplatesUtilities.GetHtmlHelper(); var helper = DefaultTemplatesUtilities.GetHtmlHelper();
helper.ViewData.ModelMetadata = metadata; helper.ViewData.ModelExplorer = modelExplorer;
// Act // Act
var labelResult = helper.Label(expression: string.Empty); var labelResult = helper.Label(expression: string.Empty);
@ -117,30 +115,19 @@ namespace Microsoft.AspNet.Mvc.Core
Assert.Equal("<label for=\"\">" + propertyName + "</label>", labelForModelResult.ToString()); Assert.Equal("<label for=\"\">" + propertyName + "</label>", labelForModelResult.ToString());
} }
[Theory] // If the metadata is for a type (not property), then Label(expression) will evaluate the expression
[InlineData("MyProperty")] [Fact]
[InlineData("Custom property name from metadata")] public void LabelHelpers_Label_Evaluates_Expression()
public void LabelHelpers_DisplayMetadataPropertyNameForProperty_IfOverridden(string propertyName)
{ {
// Arrange // Arrange
var metadataHelper = new MetadataHelper(); var helper = DefaultTemplatesUtilities.GetHtmlHelper();
var metadata = new ModelMetadata( helper.ViewData["value"] = "testvalue";
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);
// Act // Act
var labelForResult = helper.LabelFor(m => m.Property1); var labelResult = helper.Label(expression: "value");
// Assert // Assert
Assert.Equal("<label for=\"Property1\">" + propertyName + "</label>", labelForResult.ToString()); Assert.Equal("<label for=\"value\">value</label>", labelResult.ToString());
} }
[Fact] [Fact]
@ -187,11 +174,15 @@ namespace Microsoft.AspNet.Mvc.Core
public void LabelHelpers_ReturnEmptyForProperty_IfDisplayNameEmpty() public void LabelHelpers_ReturnEmptyForProperty_IfDisplayNameEmpty()
{ {
// Arrange // Arrange
var metadataHelper = new MetadataHelper(); // All properties will use the same metadata. var provider = new EmptyModelMetadataProvider();
metadataHelper.Metadata
.Setup(metadata => metadata.DisplayName) var modelExplorer = provider
.Returns(string.Empty); .GetModelExplorerForType(typeof(DefaultTemplatesUtilities.ObjectTemplateModel), model: null)
var helper = DefaultTemplatesUtilities.GetHtmlHelper(metadataHelper.MetadataProvider.Object); .GetExplorerForProperty("Property1");
var helper = DefaultTemplatesUtilities.GetHtmlHelper();
helper.ViewData.ModelExplorer = modelExplorer;
helper.ViewData.ModelMetadata.DisplayName = string.Empty;
// Act // Act
var labelResult = helper.Label(expression: string.Empty); var labelResult = helper.Label(expression: string.Empty);
@ -212,11 +203,12 @@ namespace Microsoft.AspNet.Mvc.Core
public void LabelHelpers_DisplayDisplayNameForProperty_IfNonNull(string displayName) public void LabelHelpers_DisplayDisplayNameForProperty_IfNonNull(string displayName)
{ {
// Arrange // Arrange
var metadataHelper = new MetadataHelper(); // All properties will use the same metadata. var provider = new TestModelMetadataProvider();
metadataHelper.Metadata provider
.Setup(metadata => metadata.DisplayName) .ForProperty<DefaultTemplatesUtilities.ObjectTemplateModel>("Property1")
.Returns(displayName); .Then(mm => mm.DisplayName = displayName);
var helper = DefaultTemplatesUtilities.GetHtmlHelper(metadataHelper.MetadataProvider.Object);
var helper = DefaultTemplatesUtilities.GetHtmlHelper(provider: provider);
// Act // Act
var labelResult = helper.Label("Property1"); var labelResult = helper.Label("Property1");
@ -238,8 +230,7 @@ namespace Microsoft.AspNet.Mvc.Core
string expectedId) string expectedId)
{ {
// Arrange // Arrange
var metadataHelper = new MetadataHelper(); var helper = DefaultTemplatesUtilities.GetHtmlHelper();
var helper = DefaultTemplatesUtilities.GetHtmlHelper(metadataHelper.MetadataProvider.Object);
// Act // Act
var result = helper.Label(expression); var result = helper.Label(expression);
@ -249,29 +240,6 @@ namespace Microsoft.AspNet.Mvc.Core
Assert.Equal("<label for=\"" + expectedId + "\">" + expectedText + "</label>", result.ToString()); 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] [Fact]
public void LabelFor_ThrowsInvalidOperation_IfExpressionUnsupported() public void LabelFor_ThrowsInvalidOperation_IfExpressionUnsupported()
{ {
@ -309,25 +277,5 @@ namespace Microsoft.AspNet.Mvc.Core
{ {
public InnerClass Inner { get; set; } 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 // Arrange
var provider = new Mock<IModelMetadataProvider>(MockBehavior.Strict); var provider = new Mock<IModelMetadataProvider>(MockBehavior.Strict);
var metadata = var metadata = new Mock<ModelMetadata>(
new Mock<ModelMetadata>(MockBehavior.Strict, provider.Object, null, null, typeof(object), null); MockBehavior.Strict,
provider.Object,
null,
typeof(object),
null);
provider provider
.Setup(m => m.GetMetadataForType( .Setup(m => m.GetMetadataForType(typeof(DefaultTemplatesUtilities.ObjectTemplateModel)))
It.IsAny<Func<object>>(),
typeof(DefaultTemplatesUtilities.ObjectTemplateModel)))
.Returns(metadata.Object); .Returns(metadata.Object);
var helper = DefaultTemplatesUtilities.GetHtmlHelper(provider.Object); var helper = DefaultTemplatesUtilities.GetHtmlHelper(provider.Object);
// Act (do not throw) // Act (do not throw)
@ -159,7 +162,7 @@ namespace Microsoft.AspNet.Mvc.Core
// Assert // Assert
// Only the ViewDataDictionary should do anything with metadata. // Only the ViewDataDictionary should do anything with metadata.
provider.Verify( provider.Verify(
m => m.GetMetadataForType(It.IsAny<Func<object>>(), typeof(DefaultTemplatesUtilities.ObjectTemplateModel)), m => m.GetMetadataForType(typeof(DefaultTemplatesUtilities.ObjectTemplateModel)),
Times.Exactly(2)); Times.Exactly(2));
} }
@ -168,12 +171,15 @@ namespace Microsoft.AspNet.Mvc.Core
{ {
// Arrange // Arrange
var provider = new Mock<IModelMetadataProvider>(MockBehavior.Strict); var provider = new Mock<IModelMetadataProvider>(MockBehavior.Strict);
var metadata = var metadata = new Mock<ModelMetadata>(
new Mock<ModelMetadata>(MockBehavior.Strict, provider.Object, null, null, typeof(object), null); MockBehavior.Strict,
provider.Object,
null,
typeof(object),
null);
provider provider
.Setup(m => m.GetMetadataForType( .Setup(m => m.GetMetadataForType(typeof(DefaultTemplatesUtilities.ObjectTemplateModel)))
It.IsAny<Func<object>>(),
typeof(DefaultTemplatesUtilities.ObjectTemplateModel)))
.Returns(metadata.Object); .Returns(metadata.Object);
var helper = DefaultTemplatesUtilities.GetHtmlHelper(provider.Object); var helper = DefaultTemplatesUtilities.GetHtmlHelper(provider.Object);
@ -186,7 +192,7 @@ namespace Microsoft.AspNet.Mvc.Core
// Assert // Assert
// Only the ViewDataDictionary should do anything with metadata. // Only the ViewDataDictionary should do anything with metadata.
provider.Verify( provider.Verify(
m => m.GetMetadataForType(It.IsAny<Func<object>>(), typeof(DefaultTemplatesUtilities.ObjectTemplateModel)), m => m.GetMetadataForType(typeof(DefaultTemplatesUtilities.ObjectTemplateModel)),
Times.Exactly(2)); 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
Assert.NotNull(viewData1.ModelMetadata); Assert.NotNull(viewData1.ModelMetadata);
Assert.NotNull(viewData1.ModelExplorer);
Assert.Equal(expectedType, viewData1.ModelMetadata.ModelType); 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.ModelMetadata);
Assert.NotNull(viewData2.ModelExplorer);
Assert.Equal(expectedType, viewData2.ModelMetadata.ModelType); Assert.Equal(expectedType, viewData2.ModelMetadata.ModelType);
Assert.Equal(expectedType, viewData2.ModelMetadata.RealModelType); Assert.Equal(expectedType, viewData2.ModelExplorer.ModelType);
} }
[Fact] [Fact]

View File

@ -56,12 +56,12 @@ namespace Microsoft.AspNet.Mvc.Core
// Arrange // Arrange
var metadataProvider = new Mock<IModelMetadataProvider>(); var metadataProvider = new Mock<IModelMetadataProvider>();
metadataProvider metadataProvider
.Setup(m => m.GetMetadataForType(It.IsAny<Func<object>>(), typeof(object))) .Setup(m => m.GetMetadataForType(typeof(object)))
.Returns(new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(object))) .Returns(new EmptyModelMetadataProvider().GetMetadataForType(typeof(object)))
.Verifiable(); .Verifiable();
metadataProvider metadataProvider
.Setup(m => m.GetMetadataForType(It.IsAny<Func<object>>(), typeof(TestModel))) .Setup(m => m.GetMetadataForType(typeof(TestModel)))
.Returns(new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(TestModel))) .Returns(new EmptyModelMetadataProvider().GetMetadataForType(typeof(TestModel)))
.Verifiable(); .Verifiable();
var modelState = new ModelStateDictionary(); var modelState = new ModelStateDictionary();
var viewData = new TestViewDataDictionary(metadataProvider.Object, modelState); var viewData = new TestViewDataDictionary(metadataProvider.Object, modelState);
@ -82,12 +82,12 @@ namespace Microsoft.AspNet.Mvc.Core
// Arrange // Arrange
var metadataProvider = new Mock<IModelMetadataProvider>(MockBehavior.Strict); var metadataProvider = new Mock<IModelMetadataProvider>(MockBehavior.Strict);
metadataProvider metadataProvider
.Setup(m => m.GetMetadataForType(It.IsAny<Func<object>>(), typeof(object))) .Setup(m => m.GetMetadataForType(typeof(object)))
.Returns(new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(object))) .Returns(new EmptyModelMetadataProvider().GetMetadataForType(typeof(object)))
.Verifiable(); .Verifiable();
metadataProvider metadataProvider
.Setup(m => m.GetMetadataForType(It.IsAny<Func<object>>(), typeof(TestModel))) .Setup(m => m.GetMetadataForType(typeof(TestModel)))
.Returns(new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(TestModel))) .Returns(new EmptyModelMetadataProvider().GetMetadataForType(typeof(TestModel)))
.Verifiable(); .Verifiable();
var modelState = new ModelStateDictionary(); var modelState = new ModelStateDictionary();
var viewData = new TestViewDataDictionary(metadataProvider.Object, modelState); var viewData = new TestViewDataDictionary(metadataProvider.Object, modelState);
@ -100,13 +100,10 @@ namespace Microsoft.AspNet.Mvc.Core
Assert.NotNull(viewData.ModelMetadata); Assert.NotNull(viewData.ModelMetadata);
// Verifies if the GetMetadataForType is called only once. // Verifies if the GetMetadataForType is called only once.
metadataProvider.Verify( 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. // Verifies if GetMetadataForProperties and GetMetadataForProperty is not called.
metadataProvider.Verify( metadataProvider.Verify(
m => m.GetMetadataForProperties(It.IsAny<Func<object>>(), typeof(object)), Times.Never()); m => m.GetMetadataForProperties(typeof(object)), Times.Never());
metadataProvider.Verify(
m => m.GetMetadataForProperty(
It.IsAny<Func<object>>(), typeof(object), It.IsAny<string>()), Times.Never());
} }
public static TheoryData<object> SetModelData public static TheoryData<object> SetModelData
@ -299,19 +296,14 @@ namespace Microsoft.AspNet.Mvc.Core
{ {
// Arrange // Arrange
var metadataProvider = new EmptyModelMetadataProvider(); var metadataProvider = new EmptyModelMetadataProvider();
var metadata = metadataProvider.GetMetadataForType(() => null, typeof(object)); var source = new ViewDataDictionary(metadataProvider);
var source = new ViewDataDictionary(metadataProvider)
{
ModelMetadata = metadata,
};
// Act // Act
var viewData1 = new ViewDataDictionary(source); var viewData1 = new ViewDataDictionary(source);
var viewData2 = new ViewDataDictionary(source, model: null); var viewData2 = new ViewDataDictionary(source, model: null);
// Assert // Assert
Assert.Same(metadata, viewData1.ModelMetadata); Assert.Same(viewData1.ModelMetadata, viewData2.ModelMetadata);
Assert.Same(metadata, viewData2.ModelMetadata);
} }
[Theory] [Theory]
@ -326,11 +318,7 @@ namespace Microsoft.AspNet.Mvc.Core
{ {
// Arrange // Arrange
var metadataProvider = new EmptyModelMetadataProvider(); var metadataProvider = new EmptyModelMetadataProvider();
var metadata = metadataProvider.GetMetadataForType(() => null, sourceType); var source = new ViewDataDictionary(metadataProvider);
var source = new ViewDataDictionary(metadataProvider)
{
ModelMetadata = metadata,
};
// Act // Act
var viewData1 = new ViewDataDictionary(source) var viewData1 = new ViewDataDictionary(source)
@ -342,33 +330,84 @@ namespace Microsoft.AspNet.Mvc.Core
// Assert // Assert
Assert.NotNull(viewData1.ModelMetadata); Assert.NotNull(viewData1.ModelMetadata);
Assert.Equal(expectedType, viewData1.ModelMetadata.ModelType); Assert.Equal(expectedType, viewData1.ModelMetadata.ModelType);
Assert.Equal(expectedType, viewData1.ModelMetadata.RealModelType);
Assert.NotNull(viewData2.ModelMetadata); Assert.NotNull(viewData2.ModelMetadata);
Assert.Equal(expectedType, viewData2.ModelMetadata.ModelType); Assert.Equal(expectedType, viewData2.ModelMetadata.ModelType);
Assert.Equal(expectedType, viewData2.ModelMetadata.RealModelType);
} }
[Fact] [Fact]
public void ModelSetter_UpdatesModelMetadata() public void ModelSetter_SameType_UpdatesModelExplorer()
{ {
// Arrange // Arrange
var metadataProvider = new EmptyModelMetadataProvider(); var metadataProvider = new EmptyModelMetadataProvider();
var viewData = new ViewDataDictionary(metadataProvider) var viewData = new ViewDataDictionary(metadataProvider)
{ {
Model = "same-string", Model = 3,
}; };
var originalMetadata = viewData.ModelMetadata; var originalMetadata = viewData.ModelMetadata;
var originalExplorer = viewData.ModelExplorer;
// Act // Act
viewData.Model = "same-string"; viewData.Model = 5;
// Assert // Assert
Assert.NotNull(viewData.ModelMetadata); Assert.NotNull(viewData.ModelMetadata);
Assert.Equal("same-string", viewData.Model); Assert.NotNull(viewData.ModelExplorer);
Assert.Equal("same-string", viewData.ModelMetadata.Model); Assert.Equal(5, viewData.Model);
Assert.Same(originalMetadata.ModelType, viewData.ModelMetadata.ModelType); Assert.Equal(5, viewData.ModelExplorer.Model);
Assert.NotSame(originalMetadata, viewData.ModelMetadata); 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 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"; var url = "http://localhost/dealers/43/update-vehicle?dealer.name=TestCarDealer&dealer.location=NE";
// Act // Act
var response = await client.PostAsJsonAsync(url, postedContent); var response = await client.PostAsJsonAsync(url, postedContent);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,8 +1,6 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // 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; using Xunit;
namespace Microsoft.AspNet.Mvc.ModelBinding.Test namespace Microsoft.AspNet.Mvc.ModelBinding.Test
@ -15,13 +13,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
// Arrange // Arrange
var originalBindingContext = new ModelBindingContext var originalBindingContext = new ModelBindingContext
{ {
ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(object)), ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(typeof(object)),
ModelName = "theName", ModelName = "theName",
ModelState = new ModelStateDictionary(), ModelState = new ModelStateDictionary(),
ValueProvider = new SimpleHttpValueProvider() ValueProvider = new SimpleHttpValueProvider()
}; };
var newModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(object)); var newModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(typeof(object));
// Act // Act
var newBindingContext = new ModelBindingContext(originalBindingContext, string.Empty, newModelMetadata); var newBindingContext = new ModelBindingContext(originalBindingContext, string.Empty, newModelMetadata);
@ -39,7 +37,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
// Act // Act
var bindingContext = new ModelBindingContext var bindingContext = new ModelBindingContext
{ {
ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(() => 42, typeof(int)) ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(typeof(int))
}; };
// Assert // Assert

View File

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

View File

@ -26,7 +26,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// Act // Act
// Call ToList() to force the lazy evaluation to evaluate // Call ToList() to force the lazy evaluation to evaluate
provider.GetMetadataForProperties(model, typeof(PropertyModel)).ToList(); provider.GetMetadataForProperties(typeof(PropertyModel)).ToList();
// Assert // Assert
var local = Assert.Single( var local = Assert.Single(
@ -53,18 +53,15 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public void GetMetadataForProperties_ExcludesIndexers() public void GetMetadataForProperties_ExcludesIndexers()
{ {
// Arrange // Arrange
var value = "some value"; var model = new ModelWithIndexer();
var model = new ModelWithIndexer { Value = value };
var provider = new TestableAssociatedMetadataProvider(); var provider = new TestableAssociatedMetadataProvider();
var modelType = model.GetType(); var modelType = model.GetType();
// Act // Act
provider.GetMetadataForProperties(model, modelType).ToList(); provider.GetMetadataForProperties(modelType).ToList();
// Assert // Assert
Assert.Equal(2, provider.CreateMetadataFromPrototypeLog.Count); Assert.Equal(2, provider.CreateMetadataFromPrototypeLog.Count);
Assert.Equal(value, provider.CreateMetadataFromPrototypeLog[0].Model);
Assert.Null(provider.CreateMetadataFromPrototypeLog[1].Model);
var valueMetadata = Assert.Single( var valueMetadata = Assert.Single(
provider.CreateMetadataPrototypeLog, provider.CreateMetadataPrototypeLog,
@ -78,23 +75,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
Assert.Equal(typeof(string), testPropertyMetadata.ModelType); 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] [Fact]
public void GetMetadataForParameterNullOrEmptyPropertyNameThrows() public void GetMetadataForParameterNullOrEmptyPropertyNameThrows()
{ {
@ -103,56 +83,30 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// Act & Assert // Act & Assert
ExceptionAssert.ThrowsArgumentNullOrEmpty( ExceptionAssert.ThrowsArgumentNullOrEmpty(
() => provider.GetMetadataForParameter(modelAccessor: null, methodInfo: null, parameterName: null), () => provider.GetMetadataForParameter(methodInfo: null, parameterName: null),
"parameterName"); "parameterName");
ExceptionAssert.ThrowsArgumentNullOrEmpty( ExceptionAssert.ThrowsArgumentNullOrEmpty(
() => provider.GetMetadataForParameter(modelAccessor: null, methodInfo: null, parameterName: null), () => provider.GetMetadataForParameter(methodInfo: null, parameterName: null),
"parameterName"); "parameterName");
} }
// GetMetadataForProperty // GetMetadata and access metadata for a property
[Fact] [Fact]
public void GetMetadataForPropertyNullOrEmptyPropertyNameThrows() public void GetMetadataForProperty_WithLocalAttributes()
{ {
// Arrange // Arrange
var provider = new TestableAssociatedMetadataProvider(); var provider = new TestableAssociatedMetadataProvider();
var metadata = new ModelMetadata(provider, null, typeof(PropertyModel), null);
// Act & Assert var propertyMetadata = new ModelMetadata(provider, typeof(PropertyModel), typeof(int), "LocalAttributes");
ExceptionAssert.ThrowsArgumentNullOrEmpty( provider.CreateMetadataFromPrototypeReturnValue = propertyMetadata;
() => 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;
// Act // Act
var result = provider.GetMetadataForProperty(null, typeof(PropertyModel), "LocalAttributes"); var result = provider.GetMetadataForProperty(typeof(PropertyModel), "LocalAttributes");
// Assert // Assert
Assert.Same(metadata, result); Assert.Same(propertyMetadata, result);
var localAttributes = Assert.Single( var localAttributes = Assert.Single(
provider.CreateMetadataPrototypeLog, provider.CreateMetadataPrototypeLog,
parameters => parameters.PropertyName == "LocalAttributes"); parameters => parameters.PropertyName == "LocalAttributes");
@ -160,18 +114,20 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
} }
[Fact] [Fact]
public void GetMetadataForPropertyWithMetadataAttributes() public void GetMetadataForProperty_WithMetadataAttributes()
{ {
// Arrange // Arrange
var provider = new TestableAssociatedMetadataProvider(); var provider = new TestableAssociatedMetadataProvider();
var metadata = new ModelMetadata(provider, typeof(PropertyModel), null, typeof(string), "MetadataAttributes"); var metadata = new ModelMetadata(provider, null, typeof(PropertyModel), null);
provider.CreateMetadataFromPrototypeReturnValue = metadata;
var propertyMetadata = new ModelMetadata(provider, typeof(PropertyModel), typeof(string), "MetadataAttributes");
provider.CreateMetadataFromPrototypeReturnValue = propertyMetadata;
// Act // Act
var result = provider.GetMetadataForProperty(null, typeof(PropertyModel), "MetadataAttributes"); var result = metadata.Properties["MetadataAttributes"];
// Assert // Assert
Assert.Same(metadata, result); Assert.Same(propertyMetadata, result);
var parmaters = Assert.Single( var parmaters = Assert.Single(
provider.CreateMetadataPrototypeLog, provider.CreateMetadataPrototypeLog,
p => p.PropertyName == "MetadataAttributes"); p => p.PropertyName == "MetadataAttributes");
@ -179,18 +135,20 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
} }
[Fact] [Fact]
public void GetMetadataForPropertyWithMixedAttributes() public void GetMetadataForProperty_WithMixedAttributes()
{ {
// Arrange // Arrange
var provider = new TestableAssociatedMetadataProvider(); var provider = new TestableAssociatedMetadataProvider();
var metadata = new ModelMetadata(provider, typeof(PropertyModel), null, typeof(double), "MixedAttributes"); var metadata = new ModelMetadata(provider, null, typeof(PropertyModel), null);
provider.CreateMetadataFromPrototypeReturnValue = metadata;
var propertyMetadata = new ModelMetadata(provider, typeof(PropertyModel), typeof(double), "MixedAttributes");
provider.CreateMetadataFromPrototypeReturnValue = propertyMetadata;
// Act // Act
var result = provider.GetMetadataForProperty(null, typeof(PropertyModel), "MixedAttributes"); var result = metadata.Properties["MixedAttributes"];
// Assert // Assert
Assert.Same(metadata, result); Assert.Same(propertyMetadata, result);
var parms = Assert.Single(provider.CreateMetadataPrototypeLog, p => p.PropertyName == "MixedAttributes"); 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 RequiredAttribute);
Assert.Single(parms.Attributes, a => a is RangeAttribute); Assert.Single(parms.Attributes, a => a is RangeAttribute);
@ -203,11 +161,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public void GetMetadataForTypeIncludesAttributesOnType() public void GetMetadataForTypeIncludesAttributesOnType()
{ {
var provider = new TestableAssociatedMetadataProvider(); 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; provider.CreateMetadataFromPrototypeReturnValue = metadata;
// Act // Act
var result = provider.GetMetadataForType(null, typeof(TypeModel)); var result = provider.GetMetadataForType(typeof(TypeModel));
// Assert // Assert
Assert.Same(metadata, result); Assert.Same(metadata, result);
@ -216,23 +174,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
} }
#endif #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 // Helpers
private class PropertyModel private class PropertyModel
@ -292,12 +233,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
return CreateMetadataPrototypeReturnValue; return CreateMetadataPrototypeReturnValue;
} }
protected override ModelMetadata CreateMetadataFromPrototype(ModelMetadata prototype, Func<object> modelAccessor) protected override ModelMetadata CreateMetadataFromPrototype(ModelMetadata prototype)
{ {
CreateMetadataFromPrototypeLog.Add(new CreateMetadataFromPrototypeParams CreateMetadataFromPrototypeLog.Add(new CreateMetadataFromPrototypeParams
{ {
Prototype = prototype, Prototype = prototype,
Model = modelAccessor == null ? null : modelAccessor()
}); });
return CreateMetadataFromPrototypeReturnValue; return CreateMetadataFromPrototypeReturnValue;
@ -315,7 +255,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
private class CreateMetadataFromPrototypeParams private class CreateMetadataFromPrototypeParams
{ {
public ModelMetadata Prototype { get; set; } 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" }; var expected = new[] { "IsAdmin", "UserName" };
// Act // Act
var metadata = provider.GetMetadataForType(null, type); var metadata = provider.GetMetadataForType(type);
// Assert // Assert
var predicate = metadata.PropertyBindingPredicateProvider.PropertyFilter; var predicate = metadata.PropertyBindingPredicateProvider.PropertyFilter;
@ -59,7 +59,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// Act // Act
var metadata = provider.GetMetadataForParameter( var metadata = provider.GetMetadataForParameter(
modelAccessor: null,
methodInfo: methodInfo, methodInfo: methodInfo,
parameterName: "param"); parameterName: "param");
@ -97,7 +96,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// Act // Act
var metadata = provider.GetMetadataForParameter( var metadata = provider.GetMetadataForParameter(
modelAccessor: null,
methodInfo: methodInfo, methodInfo: methodInfo,
parameterName: "param"); parameterName: "param");
@ -130,7 +128,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// Act // Act
var metadata = provider.GetMetadataForParameter( var metadata = provider.GetMetadataForParameter(
modelAccessor: null,
methodInfo: methodInfo, methodInfo: methodInfo,
parameterName: "param"); parameterName: "param");
@ -146,7 +143,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var provider = new DataAnnotationsModelMetadataProvider(); var provider = new DataAnnotationsModelMetadataProvider();
// Act // Act
var metadata = provider.GetMetadataForType(null, type); var metadata = provider.GetMetadataForType(type);
// Assert // Assert
Assert.Equal("TypePrefix", metadata.BinderModelName); Assert.Equal("TypePrefix", metadata.BinderModelName);
@ -161,9 +158,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var provider = new DataAnnotationsModelMetadataProvider(); var provider = new DataAnnotationsModelMetadataProvider();
// Act & Assert // Act & Assert
Assert.True(provider.GetMetadataForProperty(null, type, "NoAttribute").ShowForDisplay); Assert.True(provider.GetMetadataForProperty(type, "NoAttribute").ShowForDisplay);
Assert.True(provider.GetMetadataForProperty(null, type, "ScaffoldColumnTrue").ShowForDisplay); Assert.True(provider.GetMetadataForProperty(type, "ScaffoldColumnTrue").ShowForDisplay);
Assert.False(provider.GetMetadataForProperty(null, type, "ScaffoldColumnFalse").ShowForDisplay); Assert.False(provider.GetMetadataForProperty(type, "ScaffoldColumnFalse").ShowForDisplay);
} }
[Fact] [Fact]
@ -174,9 +171,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var provider = new DataAnnotationsModelMetadataProvider(); var provider = new DataAnnotationsModelMetadataProvider();
// Act & Assert // Act & Assert
Assert.True(provider.GetMetadataForProperty(null, type, "NoAttribute").ShowForEdit); Assert.True(provider.GetMetadataForProperty(type, "NoAttribute").ShowForEdit);
Assert.True(provider.GetMetadataForProperty(null, type, "ScaffoldColumnTrue").ShowForEdit); Assert.True(provider.GetMetadataForProperty(type, "ScaffoldColumnTrue").ShowForEdit);
Assert.False(provider.GetMetadataForProperty(null, type, "ScaffoldColumnFalse").ShowForEdit); Assert.False(provider.GetMetadataForProperty(type, "ScaffoldColumnFalse").ShowForEdit);
} }
[Fact] [Fact]
@ -184,7 +181,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
// Arrange // Arrange
var provider = new DataAnnotationsModelMetadataProvider(); 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"]; var property = metadata.Properties["DirectlyHidden"];
// Act // Act
@ -199,7 +196,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
// Arrange // Arrange
var provider = new DataAnnotationsModelMetadataProvider(); var provider = new DataAnnotationsModelMetadataProvider();
var metadata = provider.GetMetadataForType(modelAccessor: null, modelType: typeof(ClassWithHiddenProperties)); var metadata = provider.GetMetadataForType(typeof(ClassWithHiddenProperties));
var property = metadata.Properties["OfHiddenType"]; var property = metadata.Properties["OfHiddenType"];
// Act // Act
@ -214,7 +211,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
// Arrange // Arrange
var provider = new DataAnnotationsModelMetadataProvider(); var provider = new DataAnnotationsModelMetadataProvider();
var metadata = provider.GetMetadataForType(modelAccessor: null, modelType: typeof(ClassWithHiddenProperties)); var metadata = provider.GetMetadataForType(typeof(ClassWithHiddenProperties));
var property = metadata.Properties["DirectlyHidden"]; var property = metadata.Properties["DirectlyHidden"];
// Act // Act
@ -229,7 +226,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
// Arrange // Arrange
var provider = new DataAnnotationsModelMetadataProvider(); var provider = new DataAnnotationsModelMetadataProvider();
var metadata = provider.GetMetadataForType(modelAccessor: null, modelType: typeof(ClassWithHiddenProperties)); var metadata = provider.GetMetadataForType(typeof(ClassWithHiddenProperties));
var property = metadata.Properties["OfHiddenType"]; var property = metadata.Properties["OfHiddenType"];
// Act // Act
@ -246,7 +243,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var provider = new DataAnnotationsModelMetadataProvider(); var provider = new DataAnnotationsModelMetadataProvider();
// Act // Act
var propertyMetadata = provider.GetMetadataForProperty(null, typeof(Person), nameof(Person.Parent)); var propertyMetadata = provider.GetMetadataForProperty(typeof(Person), nameof(Person.Parent));
// Assert // Assert
Assert.NotNull(propertyMetadata.BinderMetadata); Assert.NotNull(propertyMetadata.BinderMetadata);
@ -261,7 +258,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var provider = new DataAnnotationsModelMetadataProvider(); var provider = new DataAnnotationsModelMetadataProvider();
// Act // Act
var propertyMetadata = provider.GetMetadataForProperty(null, typeof(Person), nameof(Person.GrandParent)); var propertyMetadata = provider.GetMetadataForProperty(typeof(Person), nameof(Person.GrandParent));
// Assert // Assert
Assert.NotNull(propertyMetadata.BinderMetadata); Assert.NotNull(propertyMetadata.BinderMetadata);
@ -277,7 +274,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// Act // Act
var parameterMetadata = provider.GetMetadataForParameter( var parameterMetadata = provider.GetMetadataForParameter(
null,
typeof(Person).GetMethod("Update"), typeof(Person).GetMethod("Update"),
"person"); "person");
@ -295,7 +291,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// Act // Act
var parameterMetadata = provider.GetMetadataForParameter( var parameterMetadata = provider.GetMetadataForParameter(
null,
typeof(Person).GetMethod("Save"), typeof(Person).GetMethod("Save"),
"person"); "person");

View File

@ -31,7 +31,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// Assert // Assert
Assert.NotNull(metadata.AdditionalValues); Assert.NotNull(metadata.AdditionalValues);
Assert.Empty(metadata.AdditionalValues); Assert.Empty(metadata.AdditionalValues);
Assert.Null(metadata.Container);
Assert.Null(metadata.ContainerType); Assert.Null(metadata.ContainerType);
Assert.True(metadata.ConvertEmptyStringToNull); Assert.True(metadata.ConvertEmptyStringToNull);
@ -52,12 +51,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
Assert.Null(metadata.DisplayName); Assert.Null(metadata.DisplayName);
Assert.Null(metadata.EditFormatString); Assert.Null(metadata.EditFormatString);
Assert.Null(metadata.NullDisplayText); Assert.Null(metadata.NullDisplayText);
Assert.Null(metadata.SimpleDisplayText); Assert.Null(metadata.SimpleDisplayProperty);
Assert.Null(metadata.TemplateHint); Assert.Null(metadata.TemplateHint);
Assert.Null(metadata.Model);
Assert.Equal(typeof(object), metadata.ModelType); Assert.Equal(typeof(object), metadata.ModelType);
Assert.Equal(typeof(object), metadata.RealModelType);
Assert.Null(metadata.PropertyName); Assert.Null(metadata.PropertyName);
Assert.Equal(ModelMetadata.DefaultOrder, metadata.Order); Assert.Equal(ModelMetadata.DefaultOrder, metadata.Order);
@ -89,9 +86,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
new DisplayAttribute { Name = "value" }, metadata => metadata.DisplayName new DisplayAttribute { Name = "value" }, metadata => metadata.DisplayName
}, },
{
new DisplayColumnAttribute("Property"), metadata => metadata.SimpleDisplayText
},
{ {
new DisplayFormatAttribute { DataFormatString = "value" }, new DisplayFormatAttribute { DataFormatString = "value" },
metadata => metadata.DisplayFormatString metadata => metadata.DisplayFormatString
@ -130,10 +124,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
containerType: null, containerType: null,
modelType: typeof(ClassWithDisplayableColumn), modelType: typeof(ClassWithDisplayableColumn),
propertyName: null, propertyName: null,
attributes: attributes) attributes: attributes);
{
Model = new ClassWithDisplayableColumn { Property = "value" },
};
// Act // Act
var result = accessor(metadata); var result = accessor(metadata);
@ -142,6 +133,27 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
Assert.Equal("value", result); 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 public static TheoryData<Attribute, Func<ModelMetadata, bool>, bool> ExpectedAttributeDataBooleans
{ {
get 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.DisplayName = "New display name", m => m.DisplayName, "New display name" },
{ m => m.EditFormatString = "New edit format", m => m.EditFormatString, "New edit format" }, { 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.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.TemplateHint = "New template hint", m => m.TemplateHint, "New template hint" },
{ m => m.Order = 23, m => m.Order, 23 }, { 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 = null, m => m.BinderMetadata, null },
{ m => m.BinderMetadata = binderMetadata, m => m.BinderMetadata, binderMetadata }, { m => m.BinderMetadata = binderMetadata, m => m.BinderMetadata, binderMetadata },
@ -72,14 +69,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var provider = new EmptyModelMetadataProvider(); var provider = new EmptyModelMetadataProvider();
// Act // Act
var metadata = var metadata = new ModelMetadata(provider, typeof(Exception), typeof(string), "propertyName");
new ModelMetadata(provider, typeof(Exception), () => "model", typeof(string), "propertyName");
// Assert // Assert
Assert.NotNull(metadata.AdditionalValues); Assert.NotNull(metadata.AdditionalValues);
Assert.Empty(metadata.AdditionalValues); Assert.Empty(metadata.AdditionalValues);
Assert.Equal(typeof(Exception), metadata.ContainerType); Assert.Equal(typeof(Exception), metadata.ContainerType);
Assert.Null(metadata.Container);
Assert.True(metadata.ConvertEmptyStringToNull); Assert.True(metadata.ConvertEmptyStringToNull);
Assert.False(metadata.HasNonDefaultEditFormat); Assert.False(metadata.HasNonDefaultEditFormat);
@ -100,10 +95,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
Assert.Null(metadata.EditFormatString); Assert.Null(metadata.EditFormatString);
Assert.Null(metadata.NullDisplayText); Assert.Null(metadata.NullDisplayText);
Assert.Null(metadata.TemplateHint); Assert.Null(metadata.TemplateHint);
Assert.Null(metadata.SimpleDisplayProperty);
Assert.Equal("model", metadata.Model);
Assert.Equal("model", metadata.SimpleDisplayText);
Assert.Equal(typeof(string), metadata.RealModelType);
Assert.Equal(typeof(string), metadata.ModelType); Assert.Equal(typeof(string), metadata.ModelType);
Assert.Equal("propertyName", metadata.PropertyName); Assert.Equal("propertyName", metadata.PropertyName);
@ -128,7 +121,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var metadata = new ModelMetadata( var metadata = new ModelMetadata(
provider, provider,
containerType: null, containerType: null,
modelAccessor: () => null,
modelType: typeof(object), modelType: typeof(object),
propertyName: null); propertyName: null);
@ -148,7 +140,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var metadata = new ModelMetadata( var metadata = new ModelMetadata(
provider, provider,
containerType: null, containerType: null,
modelAccessor: () => null,
modelType: typeof(object), modelType: typeof(object),
propertyName: null); propertyName: null);
var valuesDictionary = new Dictionary<object, object> var valuesDictionary = new Dictionary<object, object>
@ -187,7 +178,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var modelMetadata = new ModelMetadata( var modelMetadata = new ModelMetadata(
provider, provider,
containerType: null, containerType: null,
modelAccessor: null,
modelType: type, modelType: type,
propertyName: null); propertyName: null);
@ -209,7 +199,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var modelMetadata = new ModelMetadata( var modelMetadata = new ModelMetadata(
provider, provider,
containerType: null, containerType: null,
modelAccessor: null,
modelType: type, modelType: type,
propertyName: null); propertyName: null);
@ -231,7 +220,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var modelMetadata = new ModelMetadata( var modelMetadata = new ModelMetadata(
provider, provider,
containerType: null, containerType: null,
modelAccessor: null,
modelType: type, modelType: type,
propertyName: null); propertyName: null);
@ -256,7 +244,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var modelMetadata = new ModelMetadata( var modelMetadata = new ModelMetadata(
provider, provider,
containerType: null, containerType: null,
modelAccessor: null,
modelType: type, modelType: type,
propertyName: null); propertyName: null);
@ -281,10 +268,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var provider = new EmptyModelMetadataProvider(); var provider = new EmptyModelMetadataProvider();
// Act & Assert // Act & Assert
Assert.False(new ModelMetadata(provider, null, null, typeof(string), null).IsNullableValueType); Assert.False(new ModelMetadata(provider, null, typeof(string), null).IsNullableValueType);
Assert.False(new ModelMetadata(provider, null, null, typeof(IDisposable), null).IsNullableValueType); Assert.False(new ModelMetadata(provider, null, typeof(IDisposable), null).IsNullableValueType);
Assert.True(new ModelMetadata(provider, null, null, typeof(Nullable<int>), null).IsNullableValueType); Assert.True(new ModelMetadata(provider, null, typeof(Nullable<int>), null).IsNullableValueType);
Assert.False(new ModelMetadata(provider, null, null, typeof(int), null).IsNullableValueType); Assert.False(new ModelMetadata(provider, null, typeof(int), null).IsNullableValueType);
} }
// IsRequired // IsRequired
@ -299,7 +286,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var provider = new EmptyModelMetadataProvider(); var provider = new EmptyModelMetadataProvider();
var metadata = new ModelMetadata(provider, var metadata = new ModelMetadata(provider,
containerType: null, containerType: null,
modelAccessor: null,
modelType: modelType, modelType: modelType,
propertyName: null); propertyName: null);
@ -319,7 +305,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var provider = new EmptyModelMetadataProvider(); var provider = new EmptyModelMetadataProvider();
var metadata = new ModelMetadata(provider, var metadata = new ModelMetadata(provider,
containerType: null, containerType: null,
modelAccessor: null,
modelType: modelType, modelType: modelType,
propertyName: null); propertyName: null);
@ -341,7 +326,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var metadata = new ModelMetadata( var metadata = new ModelMetadata(
provider, provider,
containerType: null, containerType: null,
modelAccessor: null,
modelType: modelType, modelType: modelType,
propertyName: null); propertyName: null);
@ -393,7 +377,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var metadata = new ModelMetadata( var metadata = new ModelMetadata(
provider, provider,
containerType: null, containerType: null,
modelAccessor: null,
modelType: modelType, modelType: modelType,
propertyName: null); propertyName: null);
@ -482,7 +465,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var metadata = new ModelMetadata( var metadata = new ModelMetadata(
provider, provider,
containerType: null, containerType: null,
modelAccessor: null,
modelType: modelType, modelType: modelType,
propertyName: null); propertyName: null);
@ -506,7 +488,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var metadata = new ModelMetadata( var metadata = new ModelMetadata(
provider, provider,
containerType: null, containerType: null,
modelAccessor: () => new Class1(),
modelType: typeof(Class1), modelType: typeof(Class1),
propertyName: null); propertyName: null);
@ -536,7 +517,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var metadata = new ModelMetadata( var metadata = new ModelMetadata(
provider, provider,
containerType: null, containerType: null,
modelAccessor: () => new Class1(),
modelType: typeof(Class1), modelType: typeof(Class1),
propertyName: null); propertyName: null);
@ -549,27 +529,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
Assert.Equal(expected, result); 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] [Fact]
public void PropertiesSetOnce() public void PropertiesSetOnce()
{ {
@ -578,7 +537,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var metadata = new ModelMetadata( var metadata = new ModelMetadata(
provider, provider,
containerType: null, containerType: null,
modelAccessor: () => new Class1(),
modelType: typeof(Class1), modelType: typeof(Class1),
propertyName: null); propertyName: null);
@ -599,7 +557,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var metadata = new ModelMetadata( var metadata = new ModelMetadata(
provider, provider,
containerType: null, containerType: null,
modelAccessor: () => new Class1(),
modelType: typeof(Class1), modelType: typeof(Class1),
propertyName: null); propertyName: null);
@ -633,7 +590,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
// Arrange // Arrange
var provider = new EmptyModelMetadataProvider(); 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", DisplayName = "displayName",
}; };
@ -650,7 +607,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
// Arrange // Arrange
var provider = new EmptyModelMetadataProvider(); var provider = new EmptyModelMetadataProvider();
var metadata = new ModelMetadata(provider, null, null, typeof(object), "PropertyName"); var metadata = new ModelMetadata(provider, null, typeof(object), "PropertyName");
// Act // Act
var result = metadata.GetDisplayName(); var result = metadata.GetDisplayName();
@ -664,7 +621,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
// Arrange // Arrange
var provider = new EmptyModelMetadataProvider(); var provider = new EmptyModelMetadataProvider();
var metadata = new ModelMetadata(provider, null, null, typeof(object), null); var metadata = new ModelMetadata(provider, null, typeof(object), null);
// Act // Act
var result = metadata.GetDisplayName(); var result = metadata.GetDisplayName();
@ -673,57 +630,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
Assert.Equal("Object", result); 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 private class ClassWithNoProperties
{ {
public override string ToString() public override string ToString()
@ -774,7 +680,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var metadata = new ModelMetadata( var metadata = new ModelMetadata(
this, this,
containerType: typeof(DummyContactModel), containerType: typeof(DummyContactModel),
modelAccessor: null,
modelType: typeof(string), modelType: typeof(string),
propertyName: propertyName); propertyName: propertyName);
@ -789,7 +694,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var metadata = new ModelMetadata( var metadata = new ModelMetadata(
this, this,
containerType: typeof(DummyContactModel), containerType: typeof(DummyContactModel),
modelAccessor: null,
modelType: typeof(string), modelType: typeof(string),
propertyName: keyValuePair.Key) propertyName: keyValuePair.Key)
{ {
@ -803,31 +707,21 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public int GetMetadataForPropertiesCalls { get; private set; } public int GetMetadataForPropertiesCalls { get; private set; }
public ModelMetadata GetMetadataForParameter( public ModelMetadata GetMetadataForParameter(
Func<object> modelAccessor,
[NotNull] MethodInfo methodInfo, [NotNull] MethodInfo methodInfo,
[NotNull] string parameterName) [NotNull] string parameterName)
{ {
throw new NotImplementedException(); 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); Assert.Equal(typeof(object), containerType);
GetMetadataForPropertiesCalls++; GetMetadataForPropertiesCalls++;
return _properties; return _properties;
} }
public ModelMetadata GetMetadataForProperty( public ModelMetadata GetMetadataForType([NotNull] Type modelType)
Func<object> modelAccessor,
[NotNull] Type containerType,
[NotNull] string propertyName)
{
throw new NotImplementedException();
}
public ModelMetadata GetMetadataForType(Func<object> modelAccessor, [NotNull] Type modelType)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }

View File

@ -20,7 +20,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
// Arrange // Arrange
IEnumerable<object> callbackAttributes = null; 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 }; var provider = new Mock<TestableAssociatedValidatorProvider> { CallBase = true };
provider.Setup(p => p.AbstractGetValidators(metadata, It.IsAny<IEnumerable<object>>())) provider.Setup(p => p.AbstractGetValidators(metadata, It.IsAny<IEnumerable<object>>()))
.Callback<ModelMetadata, IEnumerable<object>>((m, attributes) => callbackAttributes = attributes) .Callback<ModelMetadata, IEnumerable<object>>((m, attributes) => callbackAttributes = attributes)
@ -40,7 +40,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
// Arrange // Arrange
IEnumerable<object> callbackAttributes = null; 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 }; var provider = new Mock<TestableAssociatedValidatorProvider> { CallBase = true };
provider.Setup(p => p.AbstractGetValidators(metadata, It.IsAny<IEnumerable<object>>())) provider.Setup(p => p.AbstractGetValidators(metadata, It.IsAny<IEnumerable<object>>()))
.Callback<ModelMetadata, IEnumerable<object>>((m, attributes) => callbackAttributes = attributes) .Callback<ModelMetadata, IEnumerable<object>>((m, attributes) => callbackAttributes = attributes)
@ -60,7 +60,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
// Arrange // Arrange
IEnumerable<object> callbackAttributes = null; 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 }; var provider = new Mock<TestableAssociatedValidatorProvider> { CallBase = true };
provider.Setup(p => p.AbstractGetValidators(metadata, It.IsAny<IEnumerable<object>>())) provider.Setup(p => p.AbstractGetValidators(metadata, It.IsAny<IEnumerable<object>>()))
.Callback<ModelMetadata, IEnumerable<object>>((m, attributes) => callbackAttributes = attributes) .Callback<ModelMetadata, IEnumerable<object>>((m, attributes) => callbackAttributes = attributes)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -17,7 +17,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
// Arrange // Arrange
var provider = new DataAnnotationsModelMetadataProvider(); 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 attribute = new RangeAttribute(typeof(decimal), "0", "100");
var adapter = new RangeAttributeAdapter(attribute); var adapter = new RangeAttributeAdapter(attribute);
var serviceCollection = new ServiceCollection(); var serviceCollection = new ServiceCollection();

View File

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

View File

@ -17,7 +17,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
// Arrange // Arrange
var provider = new DataAnnotationsModelMetadataProvider(); 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 attribute = new StringLengthAttribute(8);
var adapter = new StringLengthAttributeAdapter(attribute); var adapter = new StringLengthAttributeAdapter(attribute);
var serviceCollection = new ServiceCollection(); var serviceCollection = new ServiceCollection();
@ -41,7 +41,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
// Arrange // Arrange
var provider = new DataAnnotationsModelMetadataProvider(); 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 attribute = new StringLengthAttribute(10) { MinimumLength = 3 };
var adapter = new StringLengthAttributeAdapter(attribute); var adapter = new StringLengthAttributeAdapter(attribute);
var serviceCollection = new ServiceCollection(); var serviceCollection = new ServiceCollection();

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