diff --git a/src/Nancy.Tests/Unit/Validation/CompositeValidatorFixture.cs b/src/Nancy.Tests/Unit/Validation/CompositeValidatorFixture.cs index 1641536ba0..58d8f3ee92 100644 --- a/src/Nancy.Tests/Unit/Validation/CompositeValidatorFixture.cs +++ b/src/Nancy.Tests/Unit/Validation/CompositeValidatorFixture.cs @@ -12,17 +12,17 @@ public void Should_yield_composite_description() { // Given var fakeValidators = A.CollectionOfFake(2); - A.CallTo(() => fakeValidators[0].Description).Returns(new ModelValidationDescriptor(new[] { new ModelValidationRule("Test1", s => s, new[] { "Member1" }) })); - A.CallTo(() => fakeValidators[1].Description).Returns(new ModelValidationDescriptor(new[] { new ModelValidationRule("Test2", s => s, new[] { "Member2" }) })); - var subject = new CompositeValidator(fakeValidators); + A.CallTo(() => fakeValidators[0].Description).Returns(new ModelValidationDescriptor(new[] { new ModelValidationRule("Test1", s => s, new[] { "Member1" }) }, typeof(object))); + A.CallTo(() => fakeValidators[1].Description).Returns(new ModelValidationDescriptor(new[] { new ModelValidationRule("Test2", s => s, new[] { "Member2" }) }, typeof(object))); + var subject = new CompositeValidator(fakeValidators, typeof(object)); // When var result = subject.Description; // Then result.Rules.ShouldHaveCount(2); - result.Rules.ShouldHave(r => r.RuleType == "Test1" && r.MemberNames.Contains("Member1")); - result.Rules.ShouldHave(r => r.RuleType == "Test2" && r.MemberNames.Contains("Member2")); + result.Rules.First().Value.ShouldHave(r => r.RuleType == "Test1" && r.MemberNames.Contains("Member1")); + result.Rules.Last().Value.ShouldHave(r => r.RuleType == "Test2" && r.MemberNames.Contains("Member2")); } [Fact] @@ -30,7 +30,7 @@ public void Should_invoke_each_validator() { // Given var fakeValidators = A.CollectionOfFake(2); - var subject = new CompositeValidator(fakeValidators); + var subject = new CompositeValidator(fakeValidators, typeof(object)); // When subject.Validate("blah", new NancyContext()); diff --git a/src/Nancy.Validation.DataAnnotatioins.Tests/DataAnnotationsValidatorFixture.cs b/src/Nancy.Validation.DataAnnotatioins.Tests/DataAnnotationsValidatorFixture.cs index b47549ed8e..c8f9385158 100644 --- a/src/Nancy.Validation.DataAnnotatioins.Tests/DataAnnotationsValidatorFixture.cs +++ b/src/Nancy.Validation.DataAnnotatioins.Tests/DataAnnotationsValidatorFixture.cs @@ -119,9 +119,9 @@ public void Should_contain_validation_result_from_validatable_object_adapter() public void Should_return_descriptor_with_rules_from_all_validators() { // Given - var rule1 = new ModelValidationRule(string.Empty, s => string.Empty); - var rule2 = new ModelValidationRule(string.Empty, s => string.Empty); - var rule3 = new ModelValidationRule(string.Empty, s => string.Empty); + var rule1 = new ModelValidationRule(string.Empty, s => string.Empty, new[] { "One" }); + var rule2 = new ModelValidationRule(string.Empty, s => string.Empty, new[] { "Two" }); + var rule3 = new ModelValidationRule(string.Empty, s => string.Empty, new[] { "Three" }); A.CallTo(() => this.propertyValidator1.GetRules()).Returns(new[] { rule1 }); A.CallTo(() => this.propertyValidator2.GetRules()).Returns(new[] { rule2, rule3 }); @@ -131,9 +131,6 @@ public void Should_return_descriptor_with_rules_from_all_validators() // Then descriptor.Rules.Count().ShouldEqual(3); - descriptor.Rules.Contains(rule1).ShouldBeTrue(); - descriptor.Rules.Contains(rule2).ShouldBeTrue(); - descriptor.Rules.Contains(rule3).ShouldBeTrue(); } public class ModelUnderTest diff --git a/src/Nancy.Validation.DataAnnotations/DataAnnotationsValidator.cs b/src/Nancy.Validation.DataAnnotations/DataAnnotationsValidator.cs index 919eb8beb2..074b6522ff 100644 --- a/src/Nancy.Validation.DataAnnotations/DataAnnotationsValidator.cs +++ b/src/Nancy.Validation.DataAnnotations/DataAnnotationsValidator.cs @@ -21,6 +21,7 @@ public class DataAnnotationsValidator : IModelValidator /// The instance that should be used by the validator. public DataAnnotationsValidator(Type typeForValidation, IPropertyValidatorFactory factory, IValidatableObjectAdapter validatableObjectAdapter) { + this.ModelType = typeForValidation; this.validatableObjectAdapter = validatableObjectAdapter; this.validators = factory.GetValidators(typeForValidation); } @@ -34,6 +35,11 @@ public ModelValidationDescriptor Description get { return this.descriptor ?? (this.descriptor = GetModelValidationDescriptor()); } } + /// + /// Gets the of the model that is being validated by the validator. + /// + public Type ModelType { get; private set; } + /// /// Validates the specified instance. /// @@ -63,7 +69,7 @@ private ModelValidationDescriptor GetModelValidationDescriptor() var rules = this.validators.SelectMany(x => x.GetRules()); - return new ModelValidationDescriptor(rules); + return new ModelValidationDescriptor(rules, this.ModelType); } } } \ No newline at end of file diff --git a/src/Nancy.Validation.FluentValidation/FluentValidationValidator.cs b/src/Nancy.Validation.FluentValidation/FluentValidationValidator.cs index 2b69a421f5..223d7d50b4 100644 --- a/src/Nancy.Validation.FluentValidation/FluentValidationValidator.cs +++ b/src/Nancy.Validation.FluentValidation/FluentValidationValidator.cs @@ -1,5 +1,6 @@ namespace Nancy.Validation.FluentValidation { + using System; using System.Collections.Generic; using System.Linq; using global::FluentValidation; @@ -20,9 +21,11 @@ public class FluentValidationValidator : IModelValidator /// specified . /// /// The Fluent Validation validator that should be used. - /// - public FluentValidationValidator(IValidator validator, IFluentAdapterFactory factory) - { + /// Factory for creating adapters for the type that is being validated. + /// The type of the model that is being validated. + public FluentValidationValidator(IValidator validator, IFluentAdapterFactory factory, Type modelType) + { + this.ModelType = modelType; this.validator = validator; this.factory = factory; } @@ -36,6 +39,11 @@ public ModelValidationDescriptor Description get { return CreateDescriptor(); } } + /// + /// Gets the of the model that is being validated by the validator. + /// + public Type ModelType { get; private set; } + /// /// Validates the specified instance. /// @@ -78,7 +86,7 @@ private ModelValidationDescriptor CreateDescriptor() } } - return new ModelValidationDescriptor(rules); + return new ModelValidationDescriptor(rules, this.ModelType); } private static IEnumerable GetErrors(ValidationResult results) diff --git a/src/Nancy.Validation.FluentValidation/FluentValidationValidatorFactory.cs b/src/Nancy.Validation.FluentValidation/FluentValidationValidatorFactory.cs index aae782c822..291cac2543 100644 --- a/src/Nancy.Validation.FluentValidation/FluentValidationValidatorFactory.cs +++ b/src/Nancy.Validation.FluentValidation/FluentValidationValidatorFactory.cs @@ -36,7 +36,7 @@ public IModelValidator Create(Type type) GetValidatorInstance(type); return (instance != null) ? - new FluentValidationValidator(instance, this.adapterFactory) : + new FluentValidationValidator(instance, this.adapterFactory, type) : null; } diff --git a/src/Nancy/Validation/CompositeValidator.cs b/src/Nancy/Validation/CompositeValidator.cs index 0b1052a18f..2be53fd48c 100644 --- a/src/Nancy/Validation/CompositeValidator.cs +++ b/src/Nancy/Validation/CompositeValidator.cs @@ -1,5 +1,6 @@ namespace Nancy.Validation { + using System; using System.Collections.Generic; using System.Linq; @@ -10,24 +11,31 @@ public class CompositeValidator : IModelValidator { private readonly IEnumerable validators; - /// - /// Gets the description of the validator. - /// - public ModelValidationDescriptor Description { get; private set; } - /// /// Initializes a new instance of the class. /// /// The validators. - public CompositeValidator(IEnumerable validators) + /// The type of the model that is being validated. + public CompositeValidator(IEnumerable validators, Type modelType) { var modelValidators = validators.ToArray(); - this.Description = CreateCompositeDescription(modelValidators); + this.ModelType = modelType; + this.Description = CreateCompositeDescription(modelValidators, modelType); this.validators = modelValidators; } + /// + /// Gets the description of the validator. + /// + public ModelValidationDescriptor Description { get; private set; } + + /// + /// The type of the model that is being validated by the validator. + /// + public Type ModelType { get; private set; } + /// /// Validates the specified instance. /// @@ -42,17 +50,18 @@ public ModelValidationResult Validate(object instance, NancyContext context) .SelectMany(r => r.Errors) .ToArray(); - return !errors.Any() ? + return (!errors.Any()) ? ModelValidationResult.Valid : new ModelValidationResult(errors); } - private static ModelValidationDescriptor CreateCompositeDescription(IEnumerable validators) + private static ModelValidationDescriptor CreateCompositeDescription(IEnumerable validators, Type modelType) { - var rules = - validators.SelectMany(v => v.Description.Rules); + var rules = validators + .SelectMany(v => v.Description.Rules) + .ToDictionary(x => x.Key, x => x.Value); - return new ModelValidationDescriptor(rules); + return new ModelValidationDescriptor(rules, modelType); } } } diff --git a/src/Nancy/Validation/DefaultValidatorLocator.cs b/src/Nancy/Validation/DefaultValidatorLocator.cs index 86d09aa7aa..9d2117a1f6 100644 --- a/src/Nancy/Validation/DefaultValidatorLocator.cs +++ b/src/Nancy/Validation/DefaultValidatorLocator.cs @@ -54,7 +54,7 @@ private IModelValidator CreateValidator(Type type) return (validators.Length == 1) ? validators[0] : - new CompositeValidator(validators); + new CompositeValidator(validators, type); } } } \ No newline at end of file diff --git a/src/Nancy/Validation/IModelValidator.cs b/src/Nancy/Validation/IModelValidator.cs index 71402fa815..9ac73dcd11 100644 --- a/src/Nancy/Validation/IModelValidator.cs +++ b/src/Nancy/Validation/IModelValidator.cs @@ -1,5 +1,7 @@ namespace Nancy.Validation { + using System; + /// /// Provides a way to validate a type as well as a description to use for client-side validation. /// @@ -10,6 +12,11 @@ public interface IModelValidator /// ModelValidationDescriptor Description { get; } + /// + /// Gets the of the model that is being validated by the validator. + /// + Type ModelType { get; } + /// /// Validates the specified instance. /// diff --git a/src/Nancy/Validation/ModelValidationDescriptor.cs b/src/Nancy/Validation/ModelValidationDescriptor.cs index 7af3810ebf..a34976d9ed 100644 --- a/src/Nancy/Validation/ModelValidationDescriptor.cs +++ b/src/Nancy/Validation/ModelValidationDescriptor.cs @@ -1,7 +1,7 @@ namespace Nancy.Validation { + using System; using System.Collections.Generic; - using System.Linq; /// /// A description of the rules a validator provides. @@ -11,18 +11,59 @@ public class ModelValidationDescriptor /// /// Initializes a new instance of the class. /// - /// The rules. - public ModelValidationDescriptor(IEnumerable rules) + /// The rules that describes the model. + /// The type of the model that the rules are defined for. + public ModelValidationDescriptor(IEnumerable rules, Type modelType) + : this(GetModelValidationRuleDictionary(rules), modelType) { - this.Rules = rules == null - ? new List().AsReadOnly() - : rules.ToList().AsReadOnly(); } + /// + /// Initializes a new instance of the class. + /// + /// The rules that describes the model, grouped by member name. + /// The type of the model that the rules are defined for. + public ModelValidationDescriptor(IDictionary> rules, Type modelType) + { + this.Rules = rules; + this.ModelType = modelType; + } + + /// + /// The type of the model that is being described. + /// + public Type ModelType { get; private set; } + /// /// Gets the rules. /// /// An of instances. - public IEnumerable Rules { get; private set; } + public IDictionary> Rules { get; private set; } + + private static IDictionary> GetModelValidationRuleDictionary(IEnumerable rules) + { + var results = + new Dictionary>(StringComparer.OrdinalIgnoreCase); + + if (rules == null) + { + return results; + } + + foreach (var rule in rules) + { + foreach (var name in rule.MemberNames) + { + if (!results.ContainsKey(name)) + { + results.Add(name, new List()); + } + + results[name].Add(rule); + } + } + + return results; + } } } \ No newline at end of file