Update 18th December 2012
Since this question seems to be getting quite a few views, I should point out that the accepted answer is not the solution I used, but it does provide the links and resources to build a solution, but, to my mind, not the ideal solution. My answer contains replacements for standard parts of the MVC framework; and you should only use those if you are comfortable checking that they still work for future versions (some private code was ripped out of the official sources, because there wasn't enough extensibility in the base classes).
I can confirm, however, that these two classes also work for Asp.Net MVC 4 as well as 3.
It is also possible to repeat a similar implementation for the Asp.Net Web API framework as well, which I have done recently.
End update
I have a type that has a lot of 'standard' validation (required etc) but also a bit of custom validation as well.
Some of this validation requires grabbing hold of a service object and looking up some lower-level (i.e. 'beneath' the Model layer) meta data using one of the other properties as a key. The meta data then controls whether one or more properties are required as well as valid formats for those properties.
To be more concrete - the type is a Card Payment object, simplified to two of the properties in question as follows:
public class CardDetails
{
public string CardTypeID { get; set; }
public string CardNumber { get; set; }
}
I then have a service:
public interface ICardTypeService
{
ICardType GetCardType(string cardTypeID);
}
ICardType
then contains different bits of information - the two here that are crucial being:
public interface ICardType
{
//different cards support one or more card lengths
IEnumerable<int> CardNumberLengths { get; set; }
//e.g. - implementation of the Luhn algorithm
Func<string, bool> CardNumberVerifier { get; set; }
}
My controllers all have the ability to resolve an ICardTypeService
using a standard pattern i.e.
var service = Resolve<ICardTypeService>();
(Although I should mention that the framework behind this call is proprietary)
Which they gain via the use of a common interface
public interface IDependant
{
IDependencyResolver Resolver { get; set; }
}
My framework then takes care of assigning the most-specific dependency resolver available for the controller instance when it is constructed (either by another resolver, or by the MVC standard controller factory). That Resolve
method in the last-but one code block is a simple wrapper around this Resolver
member.
So - if I can grab the selected ICardType
for the payment that is received from the browser, I can then perform initial checks on card number length etc. The issue is, how to resolve the service from within my override of IsValid(object, ValidationContext)
override of ValidationAttribute
?
I need to pass through the current controller's dependency resolver to the validation context. I see that ValidationContext
both implements IServiceProvider
and has an instance of IServiceContainer
- so clearly I should be able to create a wrapper for my service resolver that also implements one of those (probably IServiceProvider
).
I've already noted that in all places where a ValidationContext
is produced by the MVC framework, the service provider is always passed null.
So at what point in the MVC pipeline should I be looking to override the core behaviour and inject my service provider?
I should add that this will not be the only scenario in which I need to do something like this - so ideally I'd like something which I can apply to the pipeline so that all ValidationContext
s are configured with the current service provider for the current controller.
Have you thought about creating a model validator, using a modelValidatorProvider, instead of using validation attributes? This way you're not dependant on ValidationAttribute but can create your own validation implementation (this will work in addition the existing DataAnnotations validation).
http://msdn.microsoft.com/en-us/library/system.web.mvc.modelvalidatorprovider.aspx
http://dotnetslackers.com/articles/aspnet/Experience-ASP-NET-MVC-3-Beta-the-New-Dependency-Injection-Support-Part2.aspx#s10-new-support-for-validator-provider
http://dotnetslackers.com/articles/aspnet/Customizing-ASP-NET-MVC-2-Metadata-and-Validation.aspx#s2-validation
Update
In addition to the class shown below, I've done a similar thing for
IValidatableObject
implementations as well(short notes towards the end of the answer instead of a full code sample because then the answer just gets too long)- I've added the code for that class as well in response to a comment - it does make the answer very long, but at least you'll have all the code you need.Original
Since I'm targeting
ValidationAttribute
-based validation at the moment I researched where MVC creates theValidationContext
that gets fed to theGetValidationResult
method of that class.Turns out it's in the
DataAnnotationsModelValidator
'sValidate
method:(Copied and reformatted from MVC3 RTM Source)
So I figured some extensibility here would be in order:
So I check first for my
IDependant
interface from the controller, in which case I create an instance of a wrapper class that acts as an adapter between myIDependencyResolver
interface andSystem.IServiceProvider
.I thought I'd also handle cases where a controller itself is an
IServiceProvider
too (not that that applies in my case - but it's a more general solution).Then I make the
DataAnnotationsModelValidatorProvider
use this validator by default, instead of the original:Now 'normal'
ValidationAttribute
-based validators, can resolve services:This still leaves direct
ModelValidator
-derived needing to be reimplemented to support the same technique - although they already have access to theControllerContext
, so it's less of an issue.Update
A similar thing has to be done if you want
IValidatableObject
-implementing types to be able to resolve services during the implementation ofValidate
without having to keep deriving your own adapters for each type.ValidatableObjectAdapter
, I called itValidatableObjectAdapterEx
Validate
andConvertResults
private method of that class.ValidationContext
is constructedUpdate (in response to comment below)
Here's the code for the
ValidatableObjectAdapterEx
- and I'll point out hopefully more clearly thatIDependant
andResolverServiceProviderWrapper
used here and before are types that only apply to my environment - if you're using a global, statically-accessible DI container, however, then it should be trivial to re-implement these two classes'CreateServiceProvider
methods appropriately.End Code
With that class in place, you can register this as the default adapter for
IValidatableObject
instances with the line:On MVC 5.2, you can
leveragesteal @Andras's answer and the MVC source and:1. Derive a
DataAnnotationsModelValidatorEx
fromDataAnnotationsModelValidator
2. Clone the base impl of
public override IEnumerable<ModelValidationResult> Validate(object container)
3.
Apply the hackRender the elegant incision afterValidate
creates the context:-4. Tell MVC about the new
DataAnnotationsModelValidatorProvider
in townafter your Global.asax does
DependencyResolver.SetResolver(new AutofacDependencyResolver(container))
:-5. Use your imagination to
abuse your new Service Locatorconsume using ctor injection viaGetService
in yourValidationAttribute
, for example:Allows you to slap it on your model:-
which wires it to a:
The preceding two are connected by:
I'm open to corrections, but most of all, ASP.NET team, would you be open to a PR to add a constructor with this facility to
DataAnnotationsModelValidator
?