Added caching for validators
This commit is contained in:
parent
71a815be50
commit
8c4bcf14c7
|
|
@ -13,8 +13,9 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Validation
|
|||
/// </summary>
|
||||
/// <param name="context">The <see cref="ModelValidatorProviderContext"/>.</param>
|
||||
/// <remarks>
|
||||
/// Implementations should add <see cref="IModelValidator"/> instances to
|
||||
/// <see cref="ModelValidatorProviderContext.Validators"/>.
|
||||
/// Implementations should add the <see cref="IModelValidator"/> instances to the appropriate
|
||||
/// <see cref="ValidatorItem"/> instance which should be added to
|
||||
/// <see cref="ModelValidatorProviderContext.Results"/>.
|
||||
/// </remarks>
|
||||
void GetValidators(ModelValidatorProviderContext context);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,9 +14,11 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Validation
|
|||
/// Creates a new <see cref="ModelValidatorProviderContext"/>.
|
||||
/// </summary>
|
||||
/// <param name="modelMetadata">The <see cref="ModelBinding.ModelMetadata"/>.</param>
|
||||
public ModelValidatorProviderContext(ModelMetadata modelMetadata)
|
||||
/// <param name="items">The list of <see cref="ValidatorItem"/>s.</param>
|
||||
public ModelValidatorProviderContext(ModelMetadata modelMetadata, IList<ValidatorItem> items)
|
||||
{
|
||||
ModelMetadata = modelMetadata;
|
||||
Results = items;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -39,11 +41,11 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Validation
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of <see cref="IModelValidator"/> instances. <see cref="IModelValidatorProvider"/> instances
|
||||
/// should add validators to this list when
|
||||
/// Gets the list of <see cref="ValidatorItem"/> instances. <see cref="IModelValidatorProvider"/> instances
|
||||
/// should add the appropriate <see cref="ValidatorItem.Validator"/> properties when
|
||||
/// <see cref="IModelValidatorProvider.GetValidators(ModelValidatorProviderContext)"/>
|
||||
/// is called.
|
||||
/// </summary>
|
||||
public IList<IModelValidator> Validators { get; } = new List<IModelValidator>();
|
||||
public IList<ValidatorItem> Results { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
// 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.
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.ModelBinding.Validation
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to associate validators with <see cref="ValidatorMetadata"/> instances
|
||||
/// as part of <see cref="ModelValidatorProviderContext"/>. An <see cref="IModelValidator"/> should
|
||||
/// inspect <see cref="ModelValidatorProviderContext.Results"/> and set <see cref="Validator"/> and
|
||||
/// <see cref="IsReusable"/> as appropriate.
|
||||
/// </summary>
|
||||
public class ValidatorItem
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="ValidatorItem"/>.
|
||||
/// </summary>
|
||||
public ValidatorItem()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="ValidatorItem"/>.
|
||||
/// </summary>
|
||||
/// <param name="validatorMetadata">The <see cref="ValidatorMetadata"/>.</param>
|
||||
public ValidatorItem(object validatorMetadata)
|
||||
{
|
||||
ValidatorMetadata = validatorMetadata;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the metadata associated with the <see cref="Validator"/>.
|
||||
/// </summary>
|
||||
public object ValidatorMetadata { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="IModelValidator"/>.
|
||||
/// </summary>
|
||||
public IModelValidator Validator { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether or not <see cref="Validator"/> can be reused across requests.
|
||||
/// </summary>
|
||||
public bool IsReusable { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -143,6 +143,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
return new DefaultCompositeMetadataDetailsProvider(options.ModelMetadataDetailsProviders);
|
||||
}));
|
||||
services.TryAddSingleton<IObjectModelValidator, DefaultObjectValidator>();
|
||||
services.TryAddSingleton<ValidatorCache>();
|
||||
|
||||
//
|
||||
// Random Infrastructure
|
||||
|
|
|
|||
|
|
@ -153,12 +153,13 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
}
|
||||
|
||||
var actionConstraints = new IActionConstraint[count];
|
||||
for (int i = 0, j = 0; i < items.Count; i++)
|
||||
var actionConstraintIndex = 0;
|
||||
for (int i = 0; i < items.Count; i++)
|
||||
{
|
||||
var actionConstraint = items[i].Constraint;
|
||||
if (actionConstraint != null)
|
||||
{
|
||||
actionConstraints[j++] = actionConstraint;
|
||||
actionConstraints[actionConstraintIndex++] = actionConstraint;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,12 +18,21 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
public void GetValidators(ModelValidatorProviderContext context)
|
||||
{
|
||||
//Perf: Avoid allocations here
|
||||
for (var i = 0; i < context.ValidatorMetadata.Count; i++)
|
||||
for (var i = 0; i < context.Results.Count; i++)
|
||||
{
|
||||
var validator = context.ValidatorMetadata[i] as IModelValidator;
|
||||
var validatorItem = context.Results[i];
|
||||
|
||||
// Don't overwrite anything that was done by a previous provider.
|
||||
if (validatorItem.Validator != null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var validator = validatorItem.ValidatorMetadata as IModelValidator;
|
||||
if (validator != null)
|
||||
{
|
||||
context.Validators.Add(validator);
|
||||
validatorItem.Validator = validator;
|
||||
validatorItem.IsReusable = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,20 +13,28 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
public class DefaultObjectValidator : IObjectModelValidator
|
||||
{
|
||||
private readonly IModelMetadataProvider _modelMetadataProvider;
|
||||
private readonly ValidatorCache _validatorCache;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="DefaultObjectValidator"/>.
|
||||
/// </summary>
|
||||
/// <param name="modelMetadataProvider">The <see cref="IModelMetadataProvider"/>.</param>
|
||||
public DefaultObjectValidator(
|
||||
IModelMetadataProvider modelMetadataProvider)
|
||||
IModelMetadataProvider modelMetadataProvider,
|
||||
ValidatorCache validatorCache)
|
||||
{
|
||||
if (modelMetadataProvider == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(modelMetadataProvider));
|
||||
}
|
||||
|
||||
if (validatorCache == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(validatorCache));
|
||||
}
|
||||
|
||||
_modelMetadataProvider = modelMetadataProvider;
|
||||
_validatorCache = validatorCache;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
|
@ -50,6 +58,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
var visitor = new ValidationVisitor(
|
||||
actionContext,
|
||||
validatorProvider,
|
||||
_validatorCache,
|
||||
_modelMetadataProvider,
|
||||
validationState);
|
||||
|
||||
|
|
|
|||
|
|
@ -151,12 +151,13 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
else
|
||||
{
|
||||
var filters = new IFilterMetadata[count];
|
||||
for (int i = 0, j = 0; i < items.Count; i++)
|
||||
var filterIndex = 0;
|
||||
for (int i = 0; i < items.Count; i++)
|
||||
{
|
||||
var filter = items[i].Filter;
|
||||
if (filter != null)
|
||||
{
|
||||
filters[j++] = filter;
|
||||
filters[filterIndex++] = filter;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,145 @@
|
|||
// 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.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Internal
|
||||
{
|
||||
public class ValidatorCache
|
||||
{
|
||||
private readonly IReadOnlyList<IModelValidator> EmptyArray = new IModelValidator[0];
|
||||
|
||||
private readonly ConcurrentDictionary<ModelMetadata, CacheEntry> _cacheEntries = new ConcurrentDictionary<ModelMetadata, CacheEntry>();
|
||||
|
||||
public IReadOnlyList<IModelValidator> GetValidators(ModelMetadata metadata, IModelValidatorProvider validatorProvider)
|
||||
{
|
||||
CacheEntry entry;
|
||||
if (_cacheEntries.TryGetValue(metadata, out entry))
|
||||
{
|
||||
return GetValidatorsFromEntry(entry, metadata, validatorProvider);
|
||||
}
|
||||
|
||||
var items = new List<ValidatorItem>(metadata.ValidatorMetadata.Count);
|
||||
for (var i = 0; i < metadata.ValidatorMetadata.Count; i++)
|
||||
{
|
||||
items.Add(new ValidatorItem(metadata.ValidatorMetadata[i]));
|
||||
}
|
||||
|
||||
ExecuteProvider(validatorProvider, metadata, items);
|
||||
|
||||
var validators = ExtractValidators(items);
|
||||
|
||||
var allValidatorsCached = true;
|
||||
for (var i = 0; i < items.Count; i++)
|
||||
{
|
||||
var item = items[i];
|
||||
if (!item.IsReusable)
|
||||
{
|
||||
item.Validator = null;
|
||||
allValidatorsCached = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (allValidatorsCached)
|
||||
{
|
||||
entry = new CacheEntry(validators);
|
||||
}
|
||||
else
|
||||
{
|
||||
entry = new CacheEntry(items);
|
||||
}
|
||||
|
||||
_cacheEntries.TryAdd(metadata, entry);
|
||||
|
||||
return validators;
|
||||
}
|
||||
|
||||
private IReadOnlyList<IModelValidator> GetValidatorsFromEntry(CacheEntry entry, ModelMetadata metadata, IModelValidatorProvider validationProvider)
|
||||
{
|
||||
Debug.Assert(entry.Validators != null || entry.Items != null);
|
||||
|
||||
if (entry.Validators != null)
|
||||
{
|
||||
return entry.Validators;
|
||||
}
|
||||
|
||||
var items = new List<ValidatorItem>(entry.Items.Count);
|
||||
for (var i = 0; i < entry.Items.Count; i++)
|
||||
{
|
||||
var item = entry.Items[i];
|
||||
if (item.IsReusable)
|
||||
{
|
||||
items.Add(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
items.Add(new ValidatorItem(item.ValidatorMetadata));
|
||||
}
|
||||
}
|
||||
|
||||
ExecuteProvider(validationProvider, metadata, items);
|
||||
|
||||
return ExtractValidators(items);
|
||||
}
|
||||
|
||||
private void ExecuteProvider(IModelValidatorProvider validatorProvider, ModelMetadata metadata, List<ValidatorItem> items)
|
||||
{
|
||||
var context = new ModelValidatorProviderContext(metadata, items);
|
||||
validatorProvider.GetValidators(context);
|
||||
}
|
||||
|
||||
private IReadOnlyList<IModelValidator> ExtractValidators(List<ValidatorItem> items)
|
||||
{
|
||||
var count = 0;
|
||||
for (var i = 0; i < items.Count; i++)
|
||||
{
|
||||
if (items[i].Validator != null)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (count == 0)
|
||||
{
|
||||
return EmptyArray;
|
||||
}
|
||||
|
||||
var validators = new IModelValidator[count];
|
||||
|
||||
var validatorIndex = 0;
|
||||
for (int i = 0; i < items.Count; i++)
|
||||
{
|
||||
var validator = items[i].Validator;
|
||||
if (validator != null)
|
||||
{
|
||||
validators[validatorIndex++] = validator;
|
||||
}
|
||||
}
|
||||
|
||||
return validators;
|
||||
}
|
||||
|
||||
private struct CacheEntry
|
||||
{
|
||||
public CacheEntry(IReadOnlyList<IModelValidator> validators)
|
||||
{
|
||||
Validators = validators;
|
||||
Items = null;
|
||||
}
|
||||
|
||||
public CacheEntry(List<ValidatorItem> items)
|
||||
{
|
||||
Items = items;
|
||||
Validators = null;
|
||||
}
|
||||
|
||||
public IReadOnlyList<IModelValidator> Validators { get; }
|
||||
|
||||
public List<ValidatorItem> Items { get; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -16,6 +16,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Validation
|
|||
{
|
||||
private readonly IModelValidatorProvider _validatorProvider;
|
||||
private readonly IModelMetadataProvider _metadataProvider;
|
||||
private readonly ValidatorCache _validatorCache;
|
||||
private readonly ActionContext _actionContext;
|
||||
private readonly ModelStateDictionary _modelState;
|
||||
private readonly ValidationStateDictionary _validationState;
|
||||
|
|
@ -24,7 +25,6 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Validation
|
|||
private string _key;
|
||||
private object _model;
|
||||
private ModelMetadata _metadata;
|
||||
private ModelValidatorProviderContext _context;
|
||||
private IValidationStrategy _strategy;
|
||||
|
||||
private HashSet<object> _currentPath;
|
||||
|
|
@ -38,6 +38,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Validation
|
|||
public ValidationVisitor(
|
||||
ActionContext actionContext,
|
||||
IModelValidatorProvider validatorProvider,
|
||||
ValidatorCache validatorCache,
|
||||
IModelMetadataProvider metadataProvider,
|
||||
ValidationStateDictionary validationState)
|
||||
{
|
||||
|
|
@ -51,8 +52,15 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Validation
|
|||
throw new ArgumentNullException(nameof(validatorProvider));
|
||||
}
|
||||
|
||||
if (validatorCache == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(validatorCache));
|
||||
}
|
||||
|
||||
_actionContext = actionContext;
|
||||
_validatorProvider = validatorProvider;
|
||||
_validatorCache = validatorCache;
|
||||
|
||||
_metadataProvider = metadataProvider;
|
||||
_validationState = validationState;
|
||||
|
||||
|
|
@ -91,7 +99,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Validation
|
|||
var state = _modelState.GetValidationState(_key);
|
||||
if (state == ModelValidationState.Unvalidated)
|
||||
{
|
||||
var validators = GetValidators();
|
||||
var validators = _validatorCache.GetValidators(_metadata, _validatorProvider);
|
||||
|
||||
var count = validators.Count;
|
||||
if (count > 0)
|
||||
|
|
@ -261,25 +269,6 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Validation
|
|||
return isValid;
|
||||
}
|
||||
|
||||
private IList<IModelValidator> GetValidators()
|
||||
{
|
||||
if (_context == null)
|
||||
{
|
||||
_context = new ModelValidatorProviderContext(_metadata);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Reusing the context so we don't allocate a new context and list
|
||||
// for every property that gets validated.
|
||||
_context.ModelMetadata = _metadata;
|
||||
_context.Validators.Clear();
|
||||
}
|
||||
|
||||
_validatorProvider.GetValidators(_context);
|
||||
|
||||
return _context.Validators;
|
||||
}
|
||||
|
||||
private void SuppressValidation(string key)
|
||||
{
|
||||
var entries = _modelState.FindKeysWithPrefix(key);
|
||||
|
|
|
|||
|
|
@ -59,9 +59,15 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
_stringLocalizerFactory);
|
||||
}
|
||||
|
||||
for (var i = 0; i < context.ValidatorMetadata.Count; i++)
|
||||
for (var i = 0; i < context.Results.Count; i++)
|
||||
{
|
||||
var attribute = context.ValidatorMetadata[i] as ValidationAttribute;
|
||||
var validatorItem = context.Results[i];
|
||||
if (validatorItem.Validator != null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var attribute = validatorItem.ValidatorMetadata as ValidationAttribute;
|
||||
if (attribute == null)
|
||||
{
|
||||
continue;
|
||||
|
|
@ -72,22 +78,25 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
attribute,
|
||||
stringLocalizer);
|
||||
|
||||
validatorItem.Validator = validator;
|
||||
validatorItem.IsReusable = true;
|
||||
// Inserts validators based on whether or not they are 'required'. We want to run
|
||||
// 'required' validators first so that we get the best possible error message.
|
||||
if (attribute is RequiredAttribute)
|
||||
{
|
||||
context.Validators.Insert(0, validator);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Validators.Add(validator);
|
||||
context.Results.Remove(validatorItem);
|
||||
context.Results.Insert(0, validatorItem);
|
||||
}
|
||||
}
|
||||
|
||||
// Produce a validator if the type supports IValidatableObject
|
||||
if (typeof(IValidatableObject).IsAssignableFrom(context.ModelMetadata.ModelType))
|
||||
{
|
||||
context.Validators.Add(new ValidatableObjectAdapter());
|
||||
context.Results.Add(new ValidatorItem
|
||||
{
|
||||
Validator = new ValidatableObjectAdapter(),
|
||||
IsReusable = true
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1542,14 +1542,15 @@ namespace Microsoft.AspNetCore.Mvc.Core.Test
|
|||
new ModelValidationResult(string.Empty, "Out of range!")
|
||||
};
|
||||
|
||||
var validator1 = new Mock<IModelValidator>();
|
||||
|
||||
validator1.Setup(v => v.Validate(It.IsAny<ModelValidationContext>()))
|
||||
.Returns(validationResult);
|
||||
var validator = new Mock<IModelValidator>();
|
||||
validator.Setup(v => v.Validate(It.IsAny<ModelValidationContext>()))
|
||||
.Returns(validationResult);
|
||||
var validator1 = new ValidatorItem(validator.Object);
|
||||
validator1.Validator = validator.Object;
|
||||
|
||||
var provider = new Mock<IModelValidatorProvider>();
|
||||
provider.Setup(v => v.GetValidators(It.IsAny<ModelValidatorProviderContext>()))
|
||||
.Callback<ModelValidatorProviderContext>(c => c.Validators.Add(validator1.Object));
|
||||
.Callback<ModelValidatorProviderContext>(c => c.Results.Add(validator1));
|
||||
|
||||
var binder = new Mock<IModelBinder>();
|
||||
var controller = GetController(binder.Object, valueProvider: null);
|
||||
|
|
@ -1578,14 +1579,15 @@ namespace Microsoft.AspNetCore.Mvc.Core.Test
|
|||
new ModelValidationResult(string.Empty, "Out of range!")
|
||||
};
|
||||
|
||||
var validator1 = new Mock<IModelValidator>();
|
||||
|
||||
validator1.Setup(v => v.Validate(It.IsAny<ModelValidationContext>()))
|
||||
.Returns(validationResult);
|
||||
var validator = new Mock<IModelValidator>();
|
||||
validator.Setup(v => v.Validate(It.IsAny<ModelValidationContext>()))
|
||||
.Returns(validationResult);
|
||||
var validator1 = new ValidatorItem(validator.Object);
|
||||
validator1.Validator = validator.Object;
|
||||
|
||||
var provider = new Mock<IModelValidatorProvider>();
|
||||
provider.Setup(v => v.GetValidators(It.IsAny<ModelValidatorProviderContext>()))
|
||||
.Callback<ModelValidatorProviderContext>(c => c.Validators.Add(validator1.Object));
|
||||
.Callback<ModelValidatorProviderContext>(c => c.Results.Add(validator1));
|
||||
|
||||
var binder = new Mock<IModelBinder>();
|
||||
var controller = GetController(binder.Object, valueProvider: null);
|
||||
|
|
@ -1643,7 +1645,7 @@ namespace Microsoft.AspNetCore.Mvc.Core.Test
|
|||
{
|
||||
ControllerContext = controllerContext,
|
||||
MetadataProvider = metadataProvider,
|
||||
ObjectValidator = new DefaultObjectValidator(metadataProvider),
|
||||
ObjectValidator = new DefaultObjectValidator(metadataProvider, new ValidatorCache()),
|
||||
};
|
||||
|
||||
return controller;
|
||||
|
|
|
|||
|
|
@ -164,7 +164,7 @@ namespace Microsoft.AspNetCore.Mvc.Controllers
|
|||
services.Setup(s => s.GetService(typeof(IModelMetadataProvider)))
|
||||
.Returns(metadataProvider);
|
||||
services.Setup(s => s.GetService(typeof(IObjectModelValidator)))
|
||||
.Returns(new DefaultObjectValidator(metadataProvider));
|
||||
.Returns(new DefaultObjectValidator(metadataProvider, new ValidatorCache()));
|
||||
return services.Object;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -211,7 +211,7 @@ namespace Microsoft.AspNetCore.Mvc.Controllers
|
|||
services.Setup(s => s.GetService(typeof(IModelMetadataProvider)))
|
||||
.Returns(metadataProvider);
|
||||
services.Setup(s => s.GetService(typeof(IObjectModelValidator)))
|
||||
.Returns(new DefaultObjectValidator(metadataProvider));
|
||||
.Returns(new DefaultObjectValidator(metadataProvider, new ValidatorCache()));
|
||||
return services.Object;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ using System;
|
|||
using Microsoft.AspNetCore.Http.Internal;
|
||||
using Microsoft.AspNetCore.Mvc.ActionConstraints;
|
||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Xunit;
|
||||
|
||||
|
|
|
|||
|
|
@ -2114,7 +2114,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
new IInputFormatter[0],
|
||||
new ControllerArgumentBinder(
|
||||
metadataProvider,
|
||||
new DefaultObjectValidator(metadataProvider)),
|
||||
new DefaultObjectValidator(metadataProvider, new ValidatorCache())),
|
||||
new IModelBinder[] { binder.Object },
|
||||
new IModelValidatorProvider[0],
|
||||
new IValueProviderFactory[0],
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Mvc.DataAnnotations.Internal;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||
|
|
@ -22,16 +23,16 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
var validatorProvider = TestModelValidatorProvider.CreateDefaultProvider();
|
||||
|
||||
var metadata = metadataProvider.GetMetadataForType(typeof(ValidatableObject));
|
||||
var context = new ModelValidatorProviderContext(metadata);
|
||||
var context = new ModelValidatorProviderContext(metadata, GetValidatorItems(metadata));
|
||||
|
||||
// Act
|
||||
validatorProvider.GetValidators(context);
|
||||
|
||||
// Assert
|
||||
var validators = context.Validators;
|
||||
var validatorItems = context.Results;
|
||||
|
||||
var validator = Assert.Single(validators);
|
||||
Assert.IsType<ValidatableObjectAdapter>(validator);
|
||||
var validatorItem = Assert.Single(validatorItems);
|
||||
Assert.IsType<ValidatableObjectAdapter>(validatorItem.Validator);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -42,15 +43,15 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
var validatorProvider = TestModelValidatorProvider.CreateDefaultProvider();
|
||||
|
||||
var metadata = metadataProvider.GetMetadataForType(typeof(ModelValidatorAttributeOnClass));
|
||||
var context = new ModelValidatorProviderContext(metadata);
|
||||
var context = new ModelValidatorProviderContext(metadata, GetValidatorItems(metadata));
|
||||
|
||||
// Act
|
||||
validatorProvider.GetValidators(context);
|
||||
|
||||
// Assert
|
||||
var validators = context.Validators;
|
||||
var validatorItems = context.Results;
|
||||
|
||||
var validator = Assert.IsType<CustomModelValidatorAttribute>(Assert.Single(validators));
|
||||
var validator = Assert.IsType<CustomModelValidatorAttribute>(Assert.Single(validatorItems).Validator);
|
||||
Assert.Equal("Class", validator.Tag);
|
||||
}
|
||||
|
||||
|
|
@ -64,15 +65,15 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
var metadata = metadataProvider.GetMetadataForProperty(
|
||||
typeof(ModelValidatorAttributeOnProperty),
|
||||
nameof(ModelValidatorAttributeOnProperty.Property));
|
||||
var context = new ModelValidatorProviderContext(metadata);
|
||||
var context = new ModelValidatorProviderContext(metadata, GetValidatorItems(metadata));
|
||||
|
||||
// Act
|
||||
validatorProvider.GetValidators(context);
|
||||
|
||||
// Assert
|
||||
var validators = context.Validators;
|
||||
var validatorItems = context.Results;
|
||||
|
||||
var validator = Assert.IsType<CustomModelValidatorAttribute>(Assert.Single(validators));
|
||||
var validator = Assert.IsType<CustomModelValidatorAttribute>(Assert.Single(validatorItems).Validator);
|
||||
Assert.Equal("Property", validator.Tag);
|
||||
}
|
||||
|
||||
|
|
@ -86,17 +87,17 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
var metadata = metadataProvider.GetMetadataForProperty(
|
||||
typeof(ModelValidatorAttributeOnPropertyAndClass),
|
||||
nameof(ModelValidatorAttributeOnPropertyAndClass.Property));
|
||||
var context = new ModelValidatorProviderContext(metadata);
|
||||
var context = new ModelValidatorProviderContext(metadata, GetValidatorItems(metadata));
|
||||
|
||||
// Act
|
||||
validatorProvider.GetValidators(context);
|
||||
|
||||
// Assert
|
||||
var validators = context.Validators;
|
||||
var validatorItems = context.Results;
|
||||
|
||||
Assert.Equal(2, validators.Count);
|
||||
Assert.Single(validators, v => Assert.IsType<CustomModelValidatorAttribute>(v).Tag == "Class");
|
||||
Assert.Single(validators, v => Assert.IsType<CustomModelValidatorAttribute>(v).Tag == "Property");
|
||||
Assert.Equal(2, validatorItems.Count);
|
||||
Assert.Single(validatorItems, v => Assert.IsType<CustomModelValidatorAttribute>(v.Validator).Tag == "Class");
|
||||
Assert.Single(validatorItems, v => Assert.IsType<CustomModelValidatorAttribute>(v.Validator).Tag == "Property");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -109,15 +110,15 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
var metadata = metadataProvider.GetMetadataForProperty(
|
||||
typeof(ProductViewModel),
|
||||
nameof(ProductViewModel.Id));
|
||||
var context = new ModelValidatorProviderContext(metadata);
|
||||
var context = new ModelValidatorProviderContext(metadata, GetValidatorItems(metadata));
|
||||
|
||||
// Act
|
||||
validatorProvider.GetValidators(context);
|
||||
|
||||
// Assert
|
||||
var validators = context.Validators;
|
||||
var validatorItems = context.Results;
|
||||
|
||||
var adapter = Assert.IsType<DataAnnotationsModelValidator>(Assert.Single(validators));
|
||||
var adapter = Assert.IsType<DataAnnotationsModelValidator>(Assert.Single(validatorItems).Validator);
|
||||
Assert.IsType<RangeAttribute>(adapter.Attribute);
|
||||
}
|
||||
|
||||
|
|
@ -131,17 +132,22 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
var metadata = metadataProvider.GetMetadataForProperty(
|
||||
typeof(ProductViewModel),
|
||||
nameof(ProductViewModel.Name));
|
||||
var context = new ModelValidatorProviderContext(metadata);
|
||||
var context = new ModelValidatorProviderContext(metadata, GetValidatorItems(metadata));
|
||||
|
||||
// Act
|
||||
validatorProvider.GetValidators(context);
|
||||
|
||||
// Assert
|
||||
var validators = context.Validators;
|
||||
var validatorItems = context.Results;
|
||||
|
||||
Assert.Equal(2, validators.Count);
|
||||
Assert.Single(validators, v => ((DataAnnotationsModelValidator)v).Attribute is RegularExpressionAttribute);
|
||||
Assert.Single(validators, v => ((DataAnnotationsModelValidator)v).Attribute is StringLengthAttribute);
|
||||
Assert.Equal(2, validatorItems.Count);
|
||||
Assert.Single(validatorItems, v => ((DataAnnotationsModelValidator)v.Validator).Attribute is RegularExpressionAttribute);
|
||||
Assert.Single(validatorItems, v => ((DataAnnotationsModelValidator)v.Validator).Attribute is StringLengthAttribute);
|
||||
}
|
||||
|
||||
private static IList<ValidatorItem> GetValidatorItems(ModelMetadata metadata)
|
||||
{
|
||||
return metadata.ValidatorMetadata.Select(v => new ValidatorItem(v)).ToList();
|
||||
}
|
||||
|
||||
private class ValidatableObject : IValidatableObject
|
||||
|
|
@ -157,7 +163,6 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
{
|
||||
}
|
||||
|
||||
|
||||
private class ModelValidatorAttributeOnProperty
|
||||
{
|
||||
[CustomModelValidator(Tag = "Property")]
|
||||
|
|
|
|||
|
|
@ -1010,13 +1010,13 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
}
|
||||
|
||||
var provider = TestModelMetadataProvider.CreateDefaultProvider(excludeFilters.ToArray());
|
||||
return new DefaultObjectValidator(provider);
|
||||
return new DefaultObjectValidator(provider, new ValidatorCache());
|
||||
}
|
||||
|
||||
private static DefaultObjectValidator CreateValidator(params IMetadataDetailsProvider[] providers)
|
||||
{
|
||||
var provider = TestModelMetadataProvider.CreateDefaultProvider(providers);
|
||||
return new DefaultObjectValidator(provider);
|
||||
return new DefaultObjectValidator(provider, new ValidatorCache());
|
||||
}
|
||||
|
||||
private static void AssertKeysEqual(ModelStateDictionary modelState, params string[] keys)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,112 @@
|
|||
// 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.ComponentModel.DataAnnotations;
|
||||
using Microsoft.AspNetCore.Mvc.DataAnnotations.Internal;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Internal
|
||||
{
|
||||
public class ValidatorCacheTest
|
||||
{
|
||||
[Fact]
|
||||
public void GetValidators_CachesAllValidators()
|
||||
{
|
||||
// Arrange
|
||||
var cache = new ValidatorCache();
|
||||
var metadata = new TestModelMetadataProvider().GetMetadataForProperty(typeof(TypeWithProperty), "Property1");
|
||||
var validatorProvider = TestModelValidatorProvider.CreateDefaultProvider();
|
||||
|
||||
// Act - 1
|
||||
var validators1 = cache.GetValidators(metadata, validatorProvider);
|
||||
|
||||
// Assert - 1
|
||||
Assert.Collection(
|
||||
validators1,
|
||||
v => Assert.Same(metadata.ValidatorMetadata[0], Assert.IsType<DataAnnotationsModelValidator>(v).Attribute), // Copied by provider
|
||||
v => Assert.Same(metadata.ValidatorMetadata[1], Assert.IsType<DataAnnotationsModelValidator>(v).Attribute)); // Copied by provider
|
||||
|
||||
// Act - 2
|
||||
var validators2 = cache.GetValidators(metadata, validatorProvider);
|
||||
|
||||
// Assert - 2
|
||||
Assert.Same(validators1, validators2);
|
||||
|
||||
Assert.Collection(
|
||||
validators2,
|
||||
v => Assert.Same(validators1[0], v), // Cached
|
||||
v => Assert.Same(validators1[1], v)); // Cached
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetValidators_DoesNotCacheValidatorsWithIsReusableFalse()
|
||||
{
|
||||
// Arrange
|
||||
var cache = new ValidatorCache();
|
||||
var metadata = new TestModelMetadataProvider().GetMetadataForProperty(typeof(TypeWithProperty), "Property1");
|
||||
var validatorProvider = new ProviderWithNonReusableValidators();
|
||||
|
||||
// Act - 1
|
||||
var validators1 = cache.GetValidators(metadata, validatorProvider);
|
||||
|
||||
// Assert - 1
|
||||
Assert.Collection(
|
||||
validators1,
|
||||
v => Assert.Same(metadata.ValidatorMetadata[0], Assert.IsType<DataAnnotationsModelValidator>(v).Attribute), // Copied by provider
|
||||
v => Assert.Same(metadata.ValidatorMetadata[1], Assert.IsType<DataAnnotationsModelValidator>(v).Attribute)); // Copied by provider
|
||||
|
||||
// Act - 2
|
||||
var validators2 = cache.GetValidators(metadata, validatorProvider);
|
||||
|
||||
// Assert - 2
|
||||
Assert.NotSame(validators1, validators2);
|
||||
|
||||
Assert.Collection(
|
||||
validators2,
|
||||
v => Assert.Same(validators1[0], v), // Cached
|
||||
v => Assert.NotSame(validators1[1], v)); // Not cached
|
||||
}
|
||||
|
||||
private class TypeWithProperty
|
||||
{
|
||||
[Required]
|
||||
[StringLength(10)]
|
||||
public string Property1 { get; set; }
|
||||
}
|
||||
|
||||
private class ProviderWithNonReusableValidators : IModelValidatorProvider
|
||||
{
|
||||
public void GetValidators(ModelValidatorProviderContext context)
|
||||
{
|
||||
for (var i = 0; i < context.Results.Count; i++)
|
||||
{
|
||||
var validatorItem = context.Results[i];
|
||||
if (validatorItem.Validator != null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var attribute = validatorItem.ValidatorMetadata as ValidationAttribute;
|
||||
if (attribute == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var validator = new DataAnnotationsModelValidator(
|
||||
new ValidationAttributeAdapterProvider(),
|
||||
attribute,
|
||||
stringLocalizer: null);
|
||||
|
||||
validatorItem.Validator = validator;
|
||||
|
||||
if (attribute is RequiredAttribute)
|
||||
{
|
||||
validatorItem.IsReusable = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -100,7 +100,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
|
|||
GetCompositeBinder(binders),
|
||||
valueProvider,
|
||||
new List<IInputFormatter>(),
|
||||
new DefaultObjectValidator(modelMetadataProvider),
|
||||
new DefaultObjectValidator(modelMetadataProvider, new ValidatorCache()),
|
||||
validator);
|
||||
|
||||
// Assert
|
||||
|
|
@ -142,7 +142,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
|
|||
GetCompositeBinder(binders),
|
||||
valueProvider,
|
||||
new List<IInputFormatter>(),
|
||||
new DefaultObjectValidator(metadataProvider),
|
||||
new DefaultObjectValidator(metadataProvider, new ValidatorCache()),
|
||||
validator);
|
||||
|
||||
// Assert
|
||||
|
|
@ -229,7 +229,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
|
|||
GetCompositeBinder(binders),
|
||||
valueProvider,
|
||||
new List<IInputFormatter>(),
|
||||
new DefaultObjectValidator(metadataProvider),
|
||||
new DefaultObjectValidator(metadataProvider, new ValidatorCache()),
|
||||
validator,
|
||||
includePredicate);
|
||||
|
||||
|
|
@ -314,7 +314,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
|
|||
GetCompositeBinder(binders),
|
||||
valueProvider,
|
||||
new List<IInputFormatter>(),
|
||||
new DefaultObjectValidator(metadataProvider),
|
||||
new DefaultObjectValidator(metadataProvider, new ValidatorCache()),
|
||||
validator,
|
||||
m => m.IncludedProperty,
|
||||
m => m.MyProperty);
|
||||
|
|
@ -367,7 +367,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
|
|||
GetCompositeBinder(binders),
|
||||
valueProvider,
|
||||
new List<IInputFormatter>(),
|
||||
new DefaultObjectValidator(metadataProvider),
|
||||
new DefaultObjectValidator(metadataProvider, new ValidatorCache()),
|
||||
validator);
|
||||
|
||||
// Assert
|
||||
|
|
@ -579,7 +579,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
|
|||
GetCompositeBinder(binders),
|
||||
valueProvider,
|
||||
new List<IInputFormatter>(),
|
||||
new DefaultObjectValidator(metadataProvider),
|
||||
new DefaultObjectValidator(metadataProvider, new ValidatorCache()),
|
||||
validator,
|
||||
includePredicate);
|
||||
|
||||
|
|
@ -655,7 +655,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
|
|||
GetCompositeBinder(binders),
|
||||
valueProvider,
|
||||
new List<IInputFormatter>(),
|
||||
new DefaultObjectValidator(metadataProvider),
|
||||
new DefaultObjectValidator(metadataProvider, new ValidatorCache()),
|
||||
validator);
|
||||
|
||||
// Assert
|
||||
|
|
@ -687,7 +687,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
|
|||
GetCompositeBinder(binder.Object),
|
||||
Mock.Of<IValueProvider>(),
|
||||
new List<IInputFormatter>(),
|
||||
new DefaultObjectValidator(metadataProvider),
|
||||
new DefaultObjectValidator(metadataProvider, new ValidatorCache()),
|
||||
Mock.Of<IModelValidatorProvider>(),
|
||||
includePredicate));
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
// 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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
|
@ -13,36 +14,37 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Validation
|
|||
public void GetModelValidators_ReturnsValidatorsFromAllProviders()
|
||||
{
|
||||
// Arrange
|
||||
var validator1 = Mock.Of<IModelValidator>();
|
||||
var validator2 = Mock.Of<IModelValidator>();
|
||||
var validator3 = Mock.Of<IModelValidator>();
|
||||
var validatorMetadata = new object();
|
||||
var validator1 = new ValidatorItem(validatorMetadata);
|
||||
var validator2 = new ValidatorItem(validatorMetadata);
|
||||
var validator3 = new ValidatorItem(validatorMetadata);
|
||||
|
||||
var provider1 = new Mock<IModelValidatorProvider>();
|
||||
provider1.Setup(p => p.GetValidators(It.IsAny<ModelValidatorProviderContext>()))
|
||||
.Callback<ModelValidatorProviderContext>(c =>
|
||||
{
|
||||
c.Validators.Add(validator1);
|
||||
c.Validators.Add(validator2);
|
||||
c.Results.Add(validator1);
|
||||
c.Results.Add(validator2);
|
||||
});
|
||||
|
||||
var provider2 = new Mock<IModelValidatorProvider>();
|
||||
provider2.Setup(p => p.GetValidators(It.IsAny<ModelValidatorProviderContext>()))
|
||||
.Callback<ModelValidatorProviderContext>(c =>
|
||||
{
|
||||
c.Validators.Add(validator3);
|
||||
c.Results.Add(validator3);
|
||||
});
|
||||
|
||||
var compositeModelValidator = new CompositeModelValidatorProvider(new[] { provider1.Object, provider2.Object });
|
||||
var modelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(typeof(string));
|
||||
|
||||
// Act
|
||||
var validatorProviderContext = new ModelValidatorProviderContext(modelMetadata);
|
||||
var validatorProviderContext = new ModelValidatorProviderContext(modelMetadata, new List<ValidatorItem>());
|
||||
compositeModelValidator.GetValidators(validatorProviderContext);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(
|
||||
new[] { validator1, validator2, validator3 },
|
||||
validatorProviderContext.Validators.ToArray());
|
||||
validatorProviderContext.Results.ToArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
// 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.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
|
|
@ -25,14 +26,14 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
var mockValidatable = Mock.Of<IValidatableObject>();
|
||||
var metadata = _metadataProvider.GetMetadataForType(mockValidatable.GetType());
|
||||
|
||||
var providerContext = new ModelValidatorProviderContext(metadata);
|
||||
var providerContext = new ModelValidatorProviderContext(metadata, GetValidatorItems(metadata));
|
||||
|
||||
// Act
|
||||
provider.GetValidators(providerContext);
|
||||
|
||||
// Assert
|
||||
var validator = Assert.Single(providerContext.Validators);
|
||||
Assert.IsType<ValidatableObjectAdapter>(validator);
|
||||
var validatorItem = Assert.Single(providerContext.Results);
|
||||
Assert.IsType<ValidatableObjectAdapter>(validatorItem.Validator);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -46,15 +47,15 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
typeof(ClassWithProperty),
|
||||
"PropertyWithMultipleValidationAttributes");
|
||||
|
||||
var providerContext = new ModelValidatorProviderContext(metadata);
|
||||
var providerContext = new ModelValidatorProviderContext(metadata, GetValidatorItems(metadata));
|
||||
|
||||
// Act
|
||||
provider.GetValidators(providerContext);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(4, providerContext.Validators.Count);
|
||||
Assert.IsAssignableFrom<RequiredAttribute>(((DataAnnotationsModelValidator)providerContext.Validators[0]).Attribute);
|
||||
Assert.IsAssignableFrom<RequiredAttribute>(((DataAnnotationsModelValidator)providerContext.Validators[1]).Attribute);
|
||||
Assert.Equal(4, providerContext.Results.Count);
|
||||
Assert.IsAssignableFrom<RequiredAttribute>(((DataAnnotationsModelValidator)providerContext.Results[0].Validator).Attribute);
|
||||
Assert.IsAssignableFrom<RequiredAttribute>(((DataAnnotationsModelValidator)providerContext.Results[1].Validator).Attribute);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -67,14 +68,14 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
stringLocalizerFactory: null);
|
||||
var metadata = _metadataProvider.GetMetadataForType(typeof(DummyClassWithDummyValidationAttribute));
|
||||
|
||||
var providerContext = new ModelValidatorProviderContext(metadata);
|
||||
var providerContext = new ModelValidatorProviderContext(metadata, GetValidatorItems(metadata));
|
||||
|
||||
// Act
|
||||
provider.GetValidators(providerContext);
|
||||
|
||||
// Assert
|
||||
var validator = providerContext.Validators.Single();
|
||||
Assert.IsType<DataAnnotationsModelValidator>(validator);
|
||||
var validatorItem = providerContext.Results.Single();
|
||||
Assert.IsType<DataAnnotationsModelValidator>(validatorItem.Validator);
|
||||
}
|
||||
|
||||
private class DummyValidationAttribute : ValidationAttribute
|
||||
|
|
@ -99,13 +100,24 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
var mockValidatable = new Mock<IValidatableObject>();
|
||||
var metadata = _metadataProvider.GetMetadataForType(mockValidatable.Object.GetType());
|
||||
|
||||
var providerContext = new ModelValidatorProviderContext(metadata);
|
||||
var providerContext = new ModelValidatorProviderContext(metadata, GetValidatorItems(metadata));
|
||||
|
||||
// Act
|
||||
provider.GetValidators(providerContext);
|
||||
|
||||
// Assert
|
||||
Assert.Single(providerContext.Validators);
|
||||
Assert.Single(providerContext.Results);
|
||||
}
|
||||
|
||||
private IList<ValidatorItem> GetValidatorItems(ModelMetadata metadata)
|
||||
{
|
||||
var items = new List<ValidatorItem>(metadata.ValidatorMetadata.Count);
|
||||
for (var i = 0; i < metadata.ValidatorMetadata.Count; i++)
|
||||
{
|
||||
items.Add(new ValidatorItem(metadata.ValidatorMetadata[i]));
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
private class ObservableModel
|
||||
|
|
|
|||
|
|
@ -22,16 +22,16 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
var validatorProvider = TestModelValidatorProvider.CreateDefaultProvider();
|
||||
|
||||
var metadata = metadataProvider.GetMetadataForType(typeof(ValidatableObject));
|
||||
var context = new ModelValidatorProviderContext(metadata);
|
||||
var context = new ModelValidatorProviderContext(metadata, GetValidatorItems(metadata));
|
||||
|
||||
// Act
|
||||
validatorProvider.GetValidators(context);
|
||||
|
||||
// Assert
|
||||
var validators = context.Validators;
|
||||
var validatorItems = context.Results;
|
||||
|
||||
var validator = Assert.Single(validators);
|
||||
Assert.IsType<ValidatableObjectAdapter>(validator);
|
||||
var validatorItem = Assert.Single(validatorItems);
|
||||
Assert.IsType<ValidatableObjectAdapter>(validatorItem.Validator);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -42,16 +42,16 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
var validatorProvider = TestModelValidatorProvider.CreateDefaultProvider();
|
||||
|
||||
var metadata = metadataProvider.GetMetadataForType(typeof(ModelValidatorAttributeOnClass));
|
||||
var context = new ModelValidatorProviderContext(metadata);
|
||||
var context = new ModelValidatorProviderContext(metadata, GetValidatorItems(metadata));
|
||||
|
||||
// Act
|
||||
validatorProvider.GetValidators(context);
|
||||
|
||||
// Assert
|
||||
var validators = context.Validators;
|
||||
var validatorItems = context.Results;
|
||||
|
||||
var validator = Assert.Single(validators);
|
||||
var customModelValidator = Assert.IsType<CustomModelValidatorAttribute>(validator);
|
||||
var validatorItem = Assert.Single(validatorItems);
|
||||
var customModelValidator = Assert.IsType<CustomModelValidatorAttribute>(validatorItem.Validator);
|
||||
Assert.Equal("Class", customModelValidator.Tag);
|
||||
}
|
||||
|
||||
|
|
@ -65,16 +65,16 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
var metadata = metadataProvider.GetMetadataForProperty(
|
||||
typeof(ModelValidatorAttributeOnProperty),
|
||||
nameof(ModelValidatorAttributeOnProperty.Property));
|
||||
var context = new ModelValidatorProviderContext(metadata);
|
||||
var context = new ModelValidatorProviderContext(metadata, GetValidatorItems(metadata));
|
||||
|
||||
// Act
|
||||
validatorProvider.GetValidators(context);
|
||||
|
||||
// Assert
|
||||
var validators = context.Validators;
|
||||
var validatorItems = context.Results;
|
||||
|
||||
var validator = Assert.IsType<CustomModelValidatorAttribute>(Assert.Single(validators));
|
||||
Assert.Equal("Property", validator.Tag);
|
||||
var validatorItem = Assert.IsType<CustomModelValidatorAttribute>(Assert.Single(validatorItems).Validator);
|
||||
Assert.Equal("Property", validatorItem.Tag);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -87,17 +87,17 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
var metadata = metadataProvider.GetMetadataForProperty(
|
||||
typeof(ModelValidatorAttributeOnPropertyAndClass),
|
||||
nameof(ModelValidatorAttributeOnPropertyAndClass.Property));
|
||||
var context = new ModelValidatorProviderContext(metadata);
|
||||
var context = new ModelValidatorProviderContext(metadata, GetValidatorItems(metadata));
|
||||
|
||||
// Act
|
||||
validatorProvider.GetValidators(context);
|
||||
|
||||
// Assert
|
||||
var validators = context.Validators;
|
||||
var validatorItems = context.Results;
|
||||
|
||||
Assert.Equal(2, validators.Count);
|
||||
Assert.Single(validators, v => Assert.IsType<CustomModelValidatorAttribute>(v).Tag == "Class");
|
||||
Assert.Single(validators, v => Assert.IsType<CustomModelValidatorAttribute>(v).Tag == "Property");
|
||||
Assert.Equal(2, validatorItems.Count);
|
||||
Assert.Single(validatorItems, v => Assert.IsType<CustomModelValidatorAttribute>(v.Validator).Tag == "Class");
|
||||
Assert.Single(validatorItems, v => Assert.IsType<CustomModelValidatorAttribute>(v.Validator).Tag == "Property");
|
||||
}
|
||||
|
||||
// RangeAttribute is an example of a ValidationAttribute with it's own adapter type.
|
||||
|
|
@ -191,6 +191,17 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
Assert.Single(validators, v => v is StringLengthAttributeAdapter);
|
||||
}
|
||||
|
||||
private IList<ValidatorItem> GetValidatorItems(ModelMetadata metadata)
|
||||
{
|
||||
var items = new List<ValidatorItem>(metadata.ValidatorMetadata.Count);
|
||||
for (var i = 0; i < metadata.ValidatorMetadata.Count; i++)
|
||||
{
|
||||
items.Add(new ValidatorItem(metadata.ValidatorMetadata[i]));
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
private class ValidatableObject : IValidatableObject
|
||||
{
|
||||
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
|||
|
||||
public static IObjectModelValidator GetObjectValidator(IModelMetadataProvider metadataProvider)
|
||||
{
|
||||
return new DefaultObjectValidator(metadataProvider);
|
||||
return new DefaultObjectValidator(metadataProvider, new ValidatorCache());
|
||||
}
|
||||
|
||||
private static HttpContext GetHttpContext(
|
||||
|
|
|
|||
|
|
@ -302,7 +302,7 @@ namespace Microsoft.AspNetCore.Mvc.Test
|
|||
{
|
||||
ControllerContext = controllerContext,
|
||||
MetadataProvider = metadataProvider,
|
||||
ObjectValidator = new DefaultObjectValidator(metadataProvider),
|
||||
ObjectValidator = new DefaultObjectValidator(metadataProvider, new ValidatorCache()),
|
||||
TempData = tempData,
|
||||
ViewData = viewData,
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue