Move ModelExplorer to ViewFeatures

This commit is contained in:
Ryan Nowak 2015-09-02 15:59:55 -07:00
parent ec18b35123
commit 0d6edf240a
14 changed files with 297 additions and 148 deletions

View File

@ -3,7 +3,6 @@
using System;
using Microsoft.AspNet.Mvc.Core;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ModelBinding
{
@ -12,25 +11,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
/// </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>
/// A <see cref="ModelExplorer"/> for the <paramref name="modelType"/> and <paramref name="model"/>.
/// </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"/>.
@ -40,10 +20,25 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
/// <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)
this IModelMetadataProvider provider,
Type containerType,
string propertyName)
{
if (provider == null)
{
throw new ArgumentNullException(nameof(provider));
}
if (containerType == null)
{
throw new ArgumentNullException(nameof(containerType));
}
if (propertyName == null)
{
throw new ArgumentNullException(nameof(propertyName));
}
var containerMetadata = provider.GetMetadataForType(containerType);
var propertyMetadata = containerMetadata.Properties[propertyName];

View File

@ -398,8 +398,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
IDictionary<ModelMetadata, ModelBindingResult> results)
{
var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
var modelExplorer =
metadataProvider.GetModelExplorerForType(bindingContext.ModelType, bindingContext.Model);
var metadata = metadataProvider.GetMetadataForType(bindingContext.ModelType);
var validationInfo = GetPropertyValidationInfo(bindingContext);
// Eliminate provided properties from RequiredProperties; leaving just *missing* required properties.
@ -408,8 +408,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
foreach (var missingRequiredProperty in validationInfo.RequiredProperties)
{
var propertyExplorer = modelExplorer.GetExplorerForProperty(missingRequiredProperty);
var propertyName = propertyExplorer.Metadata.BinderModelName ?? missingRequiredProperty;
var propertyMetadata = metadata.Properties[missingRequiredProperty];
var propertyName = propertyMetadata.BinderModelName ?? missingRequiredProperty;
var modelStateKey = ModelNames.CreatePropertyModelName(bindingContext.ModelName, propertyName);
bindingContext.ModelState.TryAddModelError(
@ -425,7 +425,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
var result = entry.Value;
var propertyMetadata = entry.Key;
SetProperty(bindingContext, modelExplorer, propertyMetadata, result);
SetProperty(bindingContext, metadata, propertyMetadata, result);
}
}
}
@ -434,15 +434,15 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
/// Updates a property in the current <see cref="ModelBindingContext.Model"/>.
/// </summary>
/// <param name="bindingContext">The <see cref="ModelBindingContext"/>.</param>
/// <param name="modelExplorer">
/// The <see cref="ModelExplorer"/> for the model containing property to set.
/// <param name="metadata">
/// The <see cref="ModelMetadata"/> for the model containing property to set.
/// </param>
/// <param name="propertyMetadata">The <see cref="ModelMetadata"/> for the property to set.</param>
/// <param name="result">The <see cref="ModelBindingResult"/> for the property's new value.</param>
/// <remarks>Should succeed in all cases that <see cref="CanUpdateProperty"/> returns <c>true</c>.</remarks>
protected virtual void SetProperty(
[NotNull] ModelBindingContext bindingContext,
[NotNull] ModelExplorer modelExplorer,
[NotNull] ModelMetadata metadata,
[NotNull] ModelMetadata propertyMetadata,
[NotNull] ModelBindingResult result)
{
@ -464,7 +464,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
if (!property.CanWrite)
{
// Try to handle as a collection if property exists but is not settable.
AddToProperty(bindingContext, modelExplorer, property, result);
AddToProperty(bindingContext, metadata, property, result);
return;
}
@ -481,13 +481,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
private void AddToProperty(
ModelBindingContext bindingContext,
ModelExplorer modelExplorer,
ModelMetadata modelMetadata,
PropertyInfo property,
ModelBindingResult result)
{
var propertyExplorer = modelExplorer.GetExplorerForProperty(property.Name);
var propertyMetadata = modelMetadata.Properties[property.Name];
var target = propertyExplorer.Model;
var target = propertyMetadata.PropertyGetter(bindingContext.Model);
var source = result.Model;
if (target == null || source == null)
{
@ -504,14 +504,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// Determine T if this is an ICollection<T> property. No need for a T[] case because CanUpdateProperty()
// ensures property is either settable or not an array. Underlying assumption is that CanUpdateProperty()
// and SetProperty() are overridden together.
if (!propertyExplorer.Metadata.IsCollectionType)
if (!propertyMetadata.IsCollectionType)
{
// Not a collection model.
return;
}
var propertyAddRange = CallPropertyAddRangeOpenGenericMethod.MakeGenericMethod(
propertyExplorer.Metadata.ElementMetadata.ModelType);
propertyMetadata.ElementMetadata.ModelType);
try
{
propertyAddRange.Invoke(obj: null, parameters: new[] { target, source });

View File

@ -5,9 +5,10 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ModelBinding
namespace Microsoft.AspNet.Mvc.ViewFeatures
{
/// <summary>
/// Associates a model object with it's corresponding <see cref="ModelMetadata"/>.
@ -29,10 +30,20 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
/// <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,
IModelMetadataProvider metadataProvider,
ModelMetadata metadata,
object model)
{
if (metadataProvider == null)
{
throw new ArgumentNullException(nameof(metadataProvider));
}
if (metadata == null)
{
throw new ArgumentNullException(nameof(metadata));
}
_metadataProvider = metadataProvider;
Metadata = metadata;
_model = model;
@ -46,11 +57,26 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
/// <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,
IModelMetadataProvider metadataProvider,
ModelExplorer container,
ModelMetadata metadata,
Func<object, object> modelAccessor)
{
if (metadataProvider == null)
{
throw new ArgumentNullException(nameof(metadataProvider));
}
if (container == null)
{
throw new ArgumentNullException(nameof(container));
}
if (metadata == null)
{
throw new ArgumentNullException(nameof(metadata));
}
_metadataProvider = metadataProvider;
Container = container;
Metadata = metadata;
@ -65,11 +91,21 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
/// <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,
IModelMetadataProvider metadataProvider,
ModelExplorer container,
ModelMetadata metadata,
object model)
{
if (metadataProvider == null)
{
throw new ArgumentNullException(nameof(metadataProvider));
}
if (metadata == null)
{
throw new ArgumentNullException(nameof(metadata));
}
_metadataProvider = metadataProvider;
Container = container;
Metadata = metadata;
@ -216,8 +252,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
/// </summary>
/// <param name="name">The property name.</param>
/// <returns>A <see cref="ModelExplorer"/>, or <c>null</c>.</returns>
public ModelExplorer GetExplorerForProperty([NotNull] string name)
public ModelExplorer GetExplorerForProperty(string name)
{
if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
return Properties.FirstOrDefault(p => string.Equals(
p.Metadata.PropertyName,
name,
@ -234,8 +275,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
/// <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)
public ModelExplorer GetExplorerForProperty(string name, Func<object, object> modelAccessor)
{
if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
var metadata = GetMetadataForRuntimeType();
var propertyMetadata = metadata.Properties[name];
@ -257,8 +303,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
/// <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)
public ModelExplorer GetExplorerForProperty(string name, object model)
{
if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
var metadata = GetMetadataForRuntimeType();
var propertyMetadata = metadata.Properties[name];
@ -286,8 +337,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
/// 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)
public ModelExplorer GetExplorerForExpression(Type modelType, object model)
{
if (modelType == null)
{
throw new ArgumentNullException(nameof(modelType));
}
var metadata = _metadataProvider.GetMetadataForType(modelType);
return GetExplorerForExpression(metadata, model);
}
@ -309,8 +365,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
/// 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)
public ModelExplorer GetExplorerForExpression(ModelMetadata metadata, object model)
{
if (metadata == null)
{
throw new ArgumentNullException(nameof(metadata));
}
return new ModelExplorer(_metadataProvider, this, metadata, model);
}
@ -331,8 +392,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
/// 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)
public ModelExplorer GetExplorerForExpression(Type modelType, Func<object, object> modelAccessor)
{
if (modelType == null)
{
throw new ArgumentNullException(nameof(modelType));
}
var metadata = _metadataProvider.GetMetadataForType(modelType);
return GetExplorerForExpression(metadata, modelAccessor);
}
@ -354,8 +420,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
/// 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)
public ModelExplorer GetExplorerForExpression(ModelMetadata metadata, Func<object, object> modelAccessor)
{
if (metadata == null)
{
throw new ArgumentNullException(nameof(metadata));
}
return new ModelExplorer(_metadataProvider, this, metadata, modelAccessor);
}

View File

@ -4,9 +4,9 @@
using System;
using System.Linq;
using System.Globalization;
using Microsoft.Framework.Internal;
using Microsoft.AspNet.Mvc.ModelBinding;
namespace Microsoft.AspNet.Mvc.ModelBinding
namespace Microsoft.AspNet.Mvc.ViewFeatures
{
/// <summary>
/// Extension methods for <see cref="ModelExplorer"/>.
@ -19,8 +19,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
/// </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)
public static string GetSimpleDisplayText(this ModelExplorer modelExplorer)
{
if (modelExplorer == null)
{
throw new ArgumentNullException(nameof(modelExplorer));
}
if (modelExplorer.Metadata.SimpleDisplayProperty != null)
{
var propertyExplorer = modelExplorer.GetExplorerForProperty(

View File

@ -3,6 +3,7 @@
using System;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.ViewFeatures;
namespace Microsoft.AspNet.Mvc.Rendering
{

View File

@ -0,0 +1,43 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNet.Mvc.ModelBinding;
namespace Microsoft.AspNet.Mvc.ViewFeatures
{
/// <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>
/// A <see cref="ModelExplorer"/> for the <paramref name="modelType"/> and <paramref name="model"/>.
/// </returns>
public static ModelExplorer GetModelExplorerForType(
this IModelMetadataProvider provider,
Type modelType,
object model)
{
if (provider == null)
{
throw new ArgumentNullException(nameof(provider));
}
if (modelType == null)
{
throw new ArgumentNullException(nameof(modelType));
}
var modelMetadata = provider.GetMetadataForType(modelType);
return new ModelExplorer(provider, modelMetadata, model);
}
}
}

View File

@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
using Microsoft.AspNet.Mvc.ModelBinding;
namespace Microsoft.AspNet.Mvc.ViewFeatures
{

View File

@ -415,10 +415,7 @@ namespace System.Web.Http
/// </param>
public void Validate<TEntity>(TEntity entity, string keyPrefix)
{
var modelExplorer = MetadataProvider.GetModelExplorerForType(typeof(TEntity), entity);
var validatidationState = new ValidationStateDictionary();
ObjectValidator.Validate(
BindingContext.ValidatorProvider,
ModelState,

View File

@ -1240,14 +1240,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var bindingContext = CreateContext(GetMetadataForType(model.GetType()), model);
var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(Person), model);
var propertyMetadata = bindingContext.ModelMetadata.Properties[nameof(model.PropertyWithDefaultValue)];
var metadata = metadataProvider.GetMetadataForType(typeof(Person));
var propertyMetadata = metadata.Properties[nameof(model.PropertyWithDefaultValue)];
var result = ModelBindingResult.Failed("foo");
var testableBinder = new TestableMutableObjectModelBinder();
// Act
testableBinder.SetProperty(bindingContext, modelExplorer, propertyMetadata, result);
testableBinder.SetProperty(bindingContext, metadata, propertyMetadata, result);
// Assert
var person = Assert.IsType<Person>(bindingContext.Model);
@ -1263,8 +1263,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var bindingContext = CreateContext(GetMetadataForType(model.GetType()), model);
var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(Person), model);
var propertyMetadata = bindingContext.ModelMetadata.Properties[nameof(model.PropertyWithInitializedValue)];
var metadata = metadataProvider.GetMetadataForType(typeof(Person));
var propertyMetadata = metadata.Properties[nameof(model.PropertyWithInitializedValue)];
// The null model value won't be used because IsModelBound = false.
var result = ModelBindingResult.Failed("foo");
@ -1272,7 +1272,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var testableBinder = new TestableMutableObjectModelBinder();
// Act
testableBinder.SetProperty(bindingContext, modelExplorer, propertyMetadata, result);
testableBinder.SetProperty(bindingContext, metadata, propertyMetadata, result);
// Assert
var person = Assert.IsType<Person>(bindingContext.Model);
@ -1288,9 +1288,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var bindingContext = CreateContext(GetMetadataForType(model.GetType()), model);
var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(Person), model);
var propertyMetadata =
bindingContext.ModelMetadata.Properties[nameof(model.PropertyWithInitializedValueAndDefault)];
var metadata = metadataProvider.GetMetadataForType(typeof(Person));
var propertyMetadata = metadata.Properties[nameof(model.PropertyWithInitializedValueAndDefault)];
// The null model value won't be used because IsModelBound = false.
var result = ModelBindingResult.Failed("foo");
@ -1298,7 +1297,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var testableBinder = new TestableMutableObjectModelBinder();
// Act
testableBinder.SetProperty(bindingContext, modelExplorer, propertyMetadata, result);
testableBinder.SetProperty(bindingContext, metadata, propertyMetadata, result);
// Assert
var person = Assert.IsType<Person>(bindingContext.Model);
@ -1314,14 +1313,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var bindingContext = CreateContext(GetMetadataForType(model.GetType()), model);
var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(Person), model);
var propertyMetadata = bindingContext.ModelMetadata.Properties[nameof(model.NonUpdateableProperty)];
var metadata = metadataProvider.GetMetadataForType(typeof(Person));
var propertyMetadata = metadata.Properties[nameof(model.NonUpdateableProperty)];
var result = ModelBindingResult.Failed("foo");
var testableBinder = new TestableMutableObjectModelBinder();
// Act
testableBinder.SetProperty(bindingContext, modelExplorer, propertyMetadata, result);
testableBinder.SetProperty(bindingContext, metadata, propertyMetadata, result);
// Assert
// If didn't throw, success!
@ -1351,7 +1350,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
[MemberData(nameof(MyCanUpdateButCannotSetPropertyData))]
public void SetProperty_ValueProvidedAndCanUpdatePropertyTrue_DoesNothing(
string propertyName,
Func<object, object> propertAccessor)
Func<object, object> propertyAccessor)
{
// Arrange
var model = new MyModelTestingCanUpdateProperty();
@ -1359,7 +1358,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var bindingContext = CreateContext(GetMetadataForType(type), model);
var modelState = bindingContext.ModelState;
var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
var modelExplorer = metadataProvider.GetModelExplorerForType(type, model);
var metadata = metadataProvider.GetMetadataForType(type);
var propertyMetadata = bindingContext.ModelMetadata.Properties[propertyName];
var result = ModelBindingResult.Success(
@ -1369,10 +1368,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var testableBinder = new TestableMutableObjectModelBinder();
// Act
testableBinder.SetProperty(bindingContext, modelExplorer, propertyMetadata, result);
testableBinder.SetProperty(bindingContext, metadata, propertyMetadata, result);
// Assert
Assert.Equal("Joe", propertAccessor(model));
Assert.Equal("Joe", propertyAccessor(model));
Assert.True(modelState.IsValid);
Assert.Empty(modelState);
}
@ -1436,14 +1435,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var bindingContext = CreateContext(GetMetadataForType(type), model);
var modelState = bindingContext.ModelState;
var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
var modelExplorer = metadataProvider.GetModelExplorerForType(type, model);
var metadata = metadataProvider.GetMetadataForType(type);
var propertyMetadata = bindingContext.ModelMetadata.Properties[propertyName];
var result = ModelBindingResult.Success(propertyName, collection);
var testableBinder = new TestableMutableObjectModelBinder();
// Act
testableBinder.SetProperty(bindingContext, modelExplorer, propertyMetadata, result);
testableBinder.SetProperty(bindingContext, metadata, propertyMetadata, result);
// Assert
Assert.Equal(collection, propertyAccessor(model));
@ -1459,14 +1458,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var bindingContext = CreateContext(GetMetadataForType(model.GetType()), model);
var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(Person), model);
var metadata = metadataProvider.GetMetadataForType(typeof(Person));
var propertyMetadata = bindingContext.ModelMetadata.Properties[nameof(model.DateOfBirth)];
var result = ModelBindingResult.Success("foo", new DateTime(2001, 1, 1));
var testableBinder = new TestableMutableObjectModelBinder();
// Act
testableBinder.SetProperty(bindingContext, modelExplorer, propertyMetadata, result);
testableBinder.SetProperty(bindingContext, metadata, propertyMetadata, result);
// Assert
Assert.True(bindingContext.ModelState.IsValid);
@ -1486,14 +1485,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var bindingContext = CreateContext(GetMetadataForType(model.GetType()), model);
var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(Person), model);
var metadata = metadataProvider.GetMetadataForType(typeof(Person));
var propertyMetadata = bindingContext.ModelMetadata.Properties[nameof(model.DateOfDeath)];
var result = ModelBindingResult.Success("foo", new DateTime(1800, 1, 1));
var testableBinder = new TestableMutableObjectModelBinder();
// Act
testableBinder.SetProperty(bindingContext, modelExplorer, propertyMetadata, result);
testableBinder.SetProperty(bindingContext, metadata, propertyMetadata, result);
// Assert
Assert.Equal("Date of death can't be before date of birth." + Environment.NewLine
@ -1511,14 +1510,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var bindingContext = CreateContext(GetMetadataForType(model.GetType()), model);
var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(Person), model);
var metadata = metadataProvider.GetMetadataForType(typeof(Person));
var propertyMetadata = bindingContext.ModelMetadata.Properties[nameof(model.DateOfBirth)];
var result = ModelBindingResult.Success("foo.DateOfBirth", model: null);
var testableBinder = new TestableMutableObjectModelBinder();
// Act
testableBinder.SetProperty(bindingContext, modelExplorer, propertyMetadata, result);
testableBinder.SetProperty(bindingContext, metadata, propertyMetadata, result);
// Assert
Assert.False(bindingContext.ModelState.IsValid);
@ -1539,14 +1538,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
bindingContext.ModelName = "foo";
var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(ModelWhosePropertySetterThrows), model);
var metadata = metadataProvider.GetMetadataForType(typeof(ModelWhosePropertySetterThrows));
var propertyMetadata = bindingContext.ModelMetadata.Properties[nameof(model.NameNoAttribute)];
var result = ModelBindingResult.Success("foo.NameNoAttribute", model: null);
var testableBinder = new TestableMutableObjectModelBinder();
// Act
testableBinder.SetProperty(bindingContext, modelExplorer, propertyMetadata, result);
testableBinder.SetProperty(bindingContext, metadata, propertyMetadata, result);
// Assert
Assert.False(bindingContext.ModelState.IsValid);
@ -1898,11 +1897,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public new void SetProperty(
ModelBindingContext bindingContext,
ModelExplorer modelExplorer,
ModelMetadata metadata,
ModelMetadata propertyMetadata,
ModelBindingResult result)
{
base.SetProperty(bindingContext, modelExplorer, propertyMetadata, result);
base.SetProperty(bindingContext, metadata, propertyMetadata, result);
}
}
}

View File

@ -21,7 +21,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
private static IModelMetadataProvider _metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
[Fact]
public void ValuesSet()
public void Constructor_SetsAttribute()
{
// Arrange
var attribute = new RequiredAttribute();
@ -33,29 +33,35 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
Assert.Same(attribute, validator.Attribute);
}
public static IEnumerable<object[]> ValidateSetsMemberNamePropertyDataSet
public static IEnumerable<object[]> Validate_SetsMemberName_OnValidationContext_ForProperties_Data
{
get
{
yield return new object[]
{
_metadataProvider.GetModelExplorerForType(typeof(string), "Hello").GetExplorerForProperty("Length"),
"Length"
_metadataProvider.GetMetadataForType(typeof(string)).Properties["Length"],
"Hello",
"Hello".Length,
"Length",
};
yield return new object[]
{
_metadataProvider.GetModelExplorerForType(typeof(SampleModel), 15),
"SampleModel"
_metadataProvider.GetMetadataForType(typeof(SampleModel)),
null,
15,
"SampleModel",
};
}
}
#if DNX451
[Theory]
[MemberData(nameof(ValidateSetsMemberNamePropertyDataSet))]
public void ValidateSetsMemberNamePropertyOfValidationContextForProperties(
ModelExplorer modelExplorer,
[MemberData(nameof(Validate_SetsMemberName_OnValidationContext_ForProperties_Data))]
public void Validate_SetsMemberName_OnValidationContext_ForProperties(
ModelMetadata metadata,
object container,
object model,
string expectedMemberName)
{
// Arrange
@ -69,7 +75,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
.Returns(ValidationResult.Success)
.Verifiable();
var validator = new DataAnnotationsModelValidator(attribute.Object, stringLocalizer: null);
var validationContext = CreateValidationContext(modelExplorer);
var validationContext = new ModelValidationContext()
{
Metadata = metadata,
Container = container,
Model = model,
};
// Act
var results = validator.Validate(validationContext);
@ -80,18 +91,23 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
}
[Fact]
public void ValidateWithIsValidTrue()
public void Validate_Valid()
{
// Arrange
var modelExplorer = _metadataProvider
.GetModelExplorerForType(typeof(string), "Hello")
.GetExplorerForProperty("Length");
var metadata = _metadataProvider.GetMetadataForType(typeof(string));
var container = "Hello";
var model = container.Length;
var attribute = new Mock<ValidationAttribute> { CallBase = true };
attribute.Setup(a => a.IsValid(modelExplorer.Model)).Returns(true);
attribute.Setup(a => a.IsValid(model)).Returns(true);
var validator = new DataAnnotationsModelValidator(attribute.Object, stringLocalizer: null);
var validationContext = CreateValidationContext(modelExplorer);
var validationContext = new ModelValidationContext()
{
Metadata = metadata,
Container = container,
Model = model,
};
// Act
var result = validator.Validate(validationContext);
@ -101,18 +117,23 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
}
[Fact]
public void ValidateWithIsValidFalse()
public void Validate_Invalid()
{
// Arrange
var modelExplorer = _metadataProvider
.GetModelExplorerForType(typeof(string), "Hello")
.GetExplorerForProperty("Length");
var metadata = _metadataProvider.GetMetadataForProperty(typeof(string), "Length");
var container = "Hello";
var model = container.Length;
var attribute = new Mock<ValidationAttribute> { CallBase = true };
attribute.Setup(a => a.IsValid(modelExplorer.Model)).Returns(false);
attribute.Setup(a => a.IsValid(model)).Returns(false);
var validator = new DataAnnotationsModelValidator(attribute.Object, stringLocalizer: null);
var validationContext = CreateValidationContext(modelExplorer);
var validationContext = new ModelValidationContext()
{
Metadata = metadata,
Container = container,
Model = model,
};
// Act
var result = validator.Validate(validationContext);
@ -124,19 +145,24 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
}
[Fact]
public void ValidatateWithValidationResultSuccess()
public void Validatate_ValidationResultSuccess()
{
// Arrange
var modelExplorer = _metadataProvider
.GetModelExplorerForType(typeof(string), "Hello")
.GetExplorerForProperty("Length");
var metadata = _metadataProvider.GetMetadataForType(typeof(string));
var container = "Hello";
var model = container.Length;
var attribute = new Mock<ValidationAttribute> { CallBase = true };
attribute.Protected()
.Setup<ValidationResult>("IsValid", ItExpr.IsAny<object>(), ItExpr.IsAny<ValidationContext>())
.Returns(ValidationResult.Success);
var validator = new DataAnnotationsModelValidator(attribute.Object, stringLocalizer: null);
var validationContext = CreateValidationContext(modelExplorer);
var validationContext = new ModelValidationContext()
{
Metadata = metadata,
Container = container,
Model = model,
};
// Act
var result = validator.Validate(validationContext);
@ -146,14 +172,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
}
[Fact]
public void ValidateReturnsSingleValidationResultIfMemberNameSequenceIsEmpty()
public void Validate_ReturnsSingleValidationResult_IfMemberNameSequenceIsEmpty()
{
// Arrange
const string errorMessage = "Some error message";
var modelExplorer = _metadataProvider
.GetModelExplorerForType(typeof(string), "Hello")
.GetExplorerForProperty("Length");
var metadata = _metadataProvider.GetMetadataForType(typeof(string));
var container = "Hello";
var model = container.Length;
var attribute = new Mock<ValidationAttribute> { CallBase = true };
attribute.Protected()
@ -161,24 +187,30 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
.Returns(new ValidationResult(errorMessage, memberNames: null));
var validator = new DataAnnotationsModelValidator(attribute.Object, stringLocalizer: null);
var validationContext = CreateValidationContext(modelExplorer);
var validationContext = new ModelValidationContext()
{
Metadata = metadata,
Container = container,
Model = model,
};
// Act
var results = validator.Validate(validationContext);
// Assert
ModelValidationResult validationResult = Assert.Single(results);
var validationResult = Assert.Single(results);
Assert.Equal(errorMessage, validationResult.Message);
Assert.Empty(validationResult.MemberName);
}
[Fact]
public void ValidateReturnsSingleValidationResultIfOneMemberNameIsSpecified()
public void Validate_ReturnsSingleValidationResult_IfOneMemberNameIsSpecified()
{
// Arrange
const string errorMessage = "A different error message";
var modelExplorer = _metadataProvider.GetModelExplorerForType(typeof(object), new object());
var metadata = _metadataProvider.GetMetadataForType(typeof(object));
var model = new object();
var attribute = new Mock<ValidationAttribute> { CallBase = true };
attribute.Protected()
@ -186,7 +218,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
.Returns(new ValidationResult(errorMessage, new[] { "FirstName" }));
var validator = new DataAnnotationsModelValidator(attribute.Object, stringLocalizer: null);
var validationContext = CreateValidationContext(modelExplorer);
var validationContext = new ModelValidationContext()
{
Metadata = metadata,
Model = model,
};
// Act
var results = validator.Validate(validationContext);
@ -198,10 +234,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
}
[Fact]
public void ValidateReturnsMemberNameIfItIsDifferentFromDisplayName()
public void Validate_ReturnsMemberName_IfItIsDifferentFromDisplayName()
{
// Arrange
var modelExplorer = _metadataProvider.GetModelExplorerForType(typeof(SampleModel), new SampleModel());
var metadata = _metadataProvider.GetMetadataForType(typeof(SampleModel));
var model = new SampleModel();
var attribute = new Mock<ValidationAttribute> { CallBase = true };
attribute.Protected()
@ -209,7 +246,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
.Returns(new ValidationResult("Name error", new[] { "Name" }));
var validator = new DataAnnotationsModelValidator(attribute.Object, stringLocalizer: null);
var validationContext = CreateValidationContext(modelExplorer);
var validationContext = new ModelValidationContext()
{
Metadata = metadata,
Model = model,
};
// Act
var results = validator.Validate(validationContext);
@ -220,15 +261,15 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
}
[Fact]
public void ValidateWithIsValidFalse_StringLocalizerReturnsLocalizerErrorMessage()
public void Validate_IsValidFalse_StringLocalizerReturnsLocalizerErrorMessage()
{
// Arrange
var modelExplorer = _metadataProvider
.GetModelExplorerForType(typeof(string), "Hello")
.GetExplorerForProperty("Length");
var metadata = _metadataProvider.GetMetadataForType(typeof(string));
var container = "Hello";
var model = container.Length;
var attribute = new Mock<ValidationAttribute> { CallBase = true };
attribute.Setup(a => a.IsValid(modelExplorer.Model)).Returns(false);
attribute.Setup(a => a.IsValid(model)).Returns(false);
attribute.Object.ErrorMessage = "Length";
@ -237,7 +278,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
stringLocalizer.Setup(s => s["Length"]).Returns(localizedString);
var validator = new DataAnnotationsModelValidator(attribute.Object, stringLocalizer.Object);
var validationContext = CreateValidationContext(modelExplorer);
var validationContext = new ModelValidationContext()
{
Metadata = metadata,
Container = container,
Model = model,
};
// Act
var result = validator.Validate(validationContext);
@ -260,16 +306,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
.IsRequired);
}
private static ModelValidationContext CreateValidationContext(ModelExplorer modelExplorer)
{
return new ModelValidationContext()
{
Container = modelExplorer.Container,
Metadata = modelExplorer.Metadata,
Model = modelExplorer.Model,
};
}
private class DerivedRequiredAttribute : RequiredAttribute
{
}

View File

@ -6,8 +6,9 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.TestCommon;
using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Mvc.TestCommon;
using Microsoft.AspNet.Mvc.ViewFeatures;
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
using Xunit;

View File

@ -5,6 +5,7 @@ using System;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Mvc.TestCommon;
using Microsoft.AspNet.Mvc.ViewFeatures;
using Xunit;
namespace Microsoft.AspNet.Mvc.Core

View File

@ -2,10 +2,10 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using Microsoft.AspNet.Mvc.ModelBinding;
using Xunit;
namespace Microsoft.AspNet.Mvc.ModelBinding
namespace Microsoft.AspNet.Mvc.ViewFeatures
{
public class ModelExplorerExtensionsTest
{

View File

@ -2,9 +2,10 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Linq;
using Microsoft.AspNet.Mvc.ModelBinding;
using Xunit;
namespace Microsoft.AspNet.Mvc.ModelBinding
namespace Microsoft.AspNet.Mvc.ViewFeatures
{
public class ModelExplorerTest
{