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.
See Question&Answers more detail:
os