Skip to content
This repository has been archived by the owner on Dec 14, 2018. It is now read-only.

Commit

Permalink
Allow ValidationAttributes and IValidatableObjects to resolve service…
Browse files Browse the repository at this point in the history
…s from the RequestServices provider by injecting it into the ValidationContext.
  • Loading branch information
tuespetre authored and rynowak committed Jan 20, 2016
1 parent a229b20 commit 328f4d6
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,10 @@ public IEnumerable<ModelValidationResult> Validate(ModelValidationContext valida
var memberName = metadata.PropertyName ?? metadata.ModelType.Name;
var container = validationContext.Container;

var context = new ValidationContext(container ?? validationContext.Model)
var context = new ValidationContext(
instance: container ?? validationContext.Model,
serviceProvider: validationContext.ActionContext?.HttpContext?.RequestServices,
items: null)
{
DisplayName = metadata.GetDisplayName(),
MemberName = memberName
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@ public IEnumerable<ModelValidationResult> Validate(ModelValidationContext contex
throw new InvalidOperationException(message);
}

var validationContext = new ValidationContext(validatable, serviceProvider: null, items: null);
var validationContext = new ValidationContext(
instance: validatable,
serviceProvider: context.ActionContext?.HttpContext?.RequestServices,
items: null);

return ConvertResults(validatable.Validate(validationContext));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@
#if DNXCORE50
using System.Reflection;
#endif
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Mvc.ModelBinding.Metadata;
using Microsoft.AspNet.Testing;
using Microsoft.Extensions.DependencyInjection;
using Moq;
using Xunit;

Expand Down Expand Up @@ -431,6 +433,49 @@ public void Validate_ComplexType_IValidatableObject_Invalid()
Assert.Equal("Error3", error.ErrorMessage);
}

[Fact]
public void Validate_ComplexType_IValidatableObject_CanUseRequestServices()
{
// Arrange
var service = new Mock<IExampleService>();
service.Setup(x => x.DoSomething()).Verifiable();

var provider = new ServiceCollection().AddSingleton(service.Object).BuildServiceProvider();

var httpContext = new Mock<HttpContext>();
httpContext.SetupGet(x => x.RequestServices).Returns(provider);

var actionContext = new ActionContext { HttpContext = httpContext.Object };

var validatorProvider = CreateValidatorProvider();
var modelState = actionContext.ModelState;
var validationState = new ValidationStateDictionary();

var validator = CreateValidator();

var model = new Mock<IValidatableObject>();
model
.Setup(x => x.Validate(It.IsAny<ValidationContext>()))
.Callback((ValidationContext context) =>
{
var receivedService = context.GetService<IExampleService>();
Assert.Equal(service.Object, receivedService);
receivedService.DoSomething();
})
.Returns(new List<ValidationResult>());

// Act
validator.Validate(
actionContext,
validatorProvider,
validationState,
null,
model.Object);

// Assert
service.Verify();
}

[Fact]
[ReplaceCulture]
public void Validate_ComplexType_FieldsAreIgnored_Valid()
Expand Down Expand Up @@ -1071,5 +1116,10 @@ public IEnumerable<ValidationResult> Validate(ValidationContext validationContex
}
}
}

public interface IExampleService
{
void DoSomething();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Mvc.DataAnnotations;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Localization;
using Moq;
using Xunit;
Expand Down Expand Up @@ -152,7 +154,7 @@ public void Validate_Invalid()
}

[Fact]
public void Validatate_ValidationResultSuccess()
public void Validate_ValidationResultSuccess()
{
// Arrange
var metadata = _metadataProvider.GetMetadataForType(typeof(string));
Expand Down Expand Up @@ -315,6 +317,50 @@ public void Validate_IsValidFalse_StringLocalizerReturnsLocalizerErrorMessage()
Assert.Equal("Longueur est invalide : 4", validationResult.Message);
}

[Fact]
public void Validate_CanUseRequestServices_WithinValidationAttribute()
{
// Arrange
var service = new Mock<IExampleService>();
service.Setup(x => x.DoSomething()).Verifiable();

var provider = new ServiceCollection().AddSingleton(service.Object).BuildServiceProvider();

var httpContext = new Mock<HttpContext>();
httpContext.SetupGet(x => x.RequestServices).Returns(provider);

var attribute = new Mock<TestableValidationAttribute> { CallBase = true };
attribute
.Setup(p => p.IsValidPublic(It.IsAny<object>(), It.IsAny<ValidationContext>()))
.Callback((object o, ValidationContext context) =>
{
var receivedService = context.GetService<IExampleService>();
Assert.Equal(service.Object, receivedService);
receivedService.DoSomething();
});

var validator = new DataAnnotationsModelValidator(
new ValidationAttributeAdapterProvider(),
attribute.Object,
stringLocalizer: null);

var validationContext = new ModelValidationContext(
actionContext: new ActionContext
{
HttpContext = httpContext.Object
},
modelMetadata: _metadataProvider.GetMetadataForType(typeof(object)),
metadataProvider: _metadataProvider,
container: null,
model: new object());

// Act
var results = validator.Validate(validationContext);

// Assert
service.Verify();
}

private const string LocalizationKey = "LocalizeIt";

public static TheoryData Validate_AttributesIncludeValues
Expand Down Expand Up @@ -430,5 +476,10 @@ private class SampleModel
{
public string Name { get; set; }
}

public interface IExampleService
{
void DoSomething();
}
}
}

0 comments on commit 328f4d6

Please sign in to comment.