Removed use of LINQ and added some optimizations in ValidationVisitor

This commit is contained in:
Ajay Bhargav Baaskaran 2015-11-04 16:23:45 -08:00
parent bebd3dc9a0
commit f7a211c095
11 changed files with 84 additions and 84 deletions

View File

@ -10,12 +10,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
/// </summary>
public interface IModelValidator
{
/// <summary>
/// Gets a value indicating whether or not this validator validates that a required value
/// has been provided for the model.
/// </summary>
bool IsRequired { get; }
/// <summary>
/// Validates the model value.
/// </summary>

View File

@ -22,7 +22,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
/// <summary>
/// Gets the <see cref="ModelBinding.ModelMetadata"/>.
/// </summary>
public ModelMetadata ModelMetadata { get; }
public ModelMetadata ModelMetadata { get; set; }
/// <summary>
/// Gets the validator metadata.

View File

@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
@ -23,6 +22,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
private string _key;
private object _model;
private ModelMetadata _metadata;
private ModelValidatorProviderContext _context;
private IValidationStrategy _strategy;
private HashSet<object> _currentPath;
@ -94,7 +94,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
var state = _modelState.GetValidationState(_key);
if (state == ModelValidationState.Unvalidated)
{
var validators = GetValidators(_metadata);
var validators = GetValidators();
var count = validators.Count;
if (count > 0)
@ -255,11 +255,23 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
return isValid;
}
private IList<IModelValidator> GetValidators(ModelMetadata metadata)
private IList<IModelValidator> GetValidators()
{
var context = new ModelValidatorProviderContext(metadata);
_validatorProvider.GetValidators(context);
return context.Validators.OrderBy(v => v, ValidatorOrderComparer.Instance).ToList();
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)
@ -348,19 +360,5 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
_visitor._currentPath.Remove(_newModel);
}
}
// Sorts 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.
private class ValidatorOrderComparer : IComparer<IModelValidator>
{
public static readonly ValidatorOrderComparer Instance = new ValidatorOrderComparer();
public int Compare(IModelValidator x, IModelValidator y)
{
var xScore = x.IsRequired ? 0 : 1;
var yScore = y.IsRequired ? 0 : 1;
return xScore.CompareTo(yScore);
}
}
}
}

View File

@ -26,11 +26,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
public ValidationAttribute Attribute { get; }
public bool IsRequired
{
get { return Attribute is RequiredAttribute; }
}
public IEnumerable<ModelValidationResult> Validate(ModelValidationContext validationContext)
{
var metadata = validationContext.Metadata;

View File

@ -1,9 +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;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.OptionsModel;
@ -43,9 +41,26 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
_stringLocalizerFactory);
}
foreach (var attribute in context.ValidatorMetadata.OfType<ValidationAttribute>())
for (var i = 0; i < context.ValidatorMetadata.Count; i++)
{
context.Validators.Add(new DataAnnotationsModelValidator(attribute, stringLocalizer));
var attribute = context.ValidatorMetadata[i] as ValidationAttribute;
if (attribute == null)
{
continue;
}
var validator = new DataAnnotationsModelValidator(attribute, stringLocalizer);
// 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);
}
}
// Produce a validator if the type supports IValidatableObject

View File

@ -11,11 +11,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
{
public class ValidatableObjectAdapter : IModelValidator
{
public bool IsRequired
{
get { return false; }
}
public IEnumerable<ModelValidationResult> Validate(ModelValidationContext context)
{
var model = context.Model;

View File

@ -70,14 +70,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
private class TestModelValidationAttribute : Attribute, IModelValidator
{
public bool IsRequired
{
get
{
throw new NotImplementedException();
}
}
public IEnumerable<ModelValidationResult> Validate(ModelValidationContext context)
{
throw new NotImplementedException();
@ -94,14 +86,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
private class TestValidationAttribute : Attribute, IModelValidator, IClientModelValidator
{
public bool IsRequired
{
get
{
throw new NotImplementedException();
}
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ClientModelValidationContext context)
{
throw new NotImplementedException();

View File

@ -171,14 +171,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
{
public string Tag { get; set; }
public bool IsRequired
{
get
{
throw new NotImplementedException();
}
}
public IEnumerable<ModelValidationResult> Validate(ModelValidationContext context)
{
throw new NotImplementedException();

View File

@ -38,6 +38,27 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
}
#endif
[Fact]
public void GetValidators_InsertsRequiredValidatorsFirst()
{
var provider = new DataAnnotationsModelValidatorProvider(
new TestOptionsManager<MvcDataAnnotationsLocalizationOptions>(),
stringLocalizerFactory: null);
var metadata = _metadataProvider.GetMetadataForProperty(
typeof(ClassWithProperty),
"PropertyWithMultipleValidationAttributes");
var providerContext = new ModelValidatorProviderContext(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);
}
[Fact]
public void UnknownValidationAttributeGetsDefaultAdapter()
{
@ -130,5 +151,30 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
public int WithoutAttribute { get; set; }
}
private class ClassWithProperty
{
[CustomNonRequiredAttribute1]
[CustomNonRequiredAttribute2]
[CustomRequiredAttribute1]
[CustomRequiredAttribute2]
public string PropertyWithMultipleValidationAttributes { get; set; }
}
public class CustomRequiredAttribute1 : RequiredAttribute
{
}
public class CustomRequiredAttribute2 : RequiredAttribute
{
}
public class CustomNonRequiredAttribute1 : ValidationAttribute
{
}
public class CustomNonRequiredAttribute2 : ValidationAttribute
{
}
}
}

View File

@ -294,17 +294,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
}
#endif
[Fact]
public void IsRequiredTests()
{
// Arrange & Act & Assert
Assert.False(new DataAnnotationsModelValidator(new RangeAttribute(10, 20), stringLocalizer: null)
.IsRequired);
Assert.True(new DataAnnotationsModelValidator(new RequiredAttribute(), stringLocalizer: null).IsRequired);
Assert.True(new DataAnnotationsModelValidator(new DerivedRequiredAttribute(), stringLocalizer: null)
.IsRequired);
}
private class DerivedRequiredAttribute : RequiredAttribute
{
}

View File

@ -218,14 +218,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
{
public string Tag { get; set; }
public bool IsRequired
{
get
{
throw new NotImplementedException();
}
}
public IEnumerable<ModelValidationResult> Validate(ModelValidationContext context)
{
throw new NotImplementedException();