It turns out that MVC's DefaultModelBinder
uses different cultures in order to parse values (e.g. double
, DateTime
, etc.) for POST vs GET requests.
Here is more info.
I see that this is controlled by the Culture
property of ValueProviderResult
objects, which are returned from IValueProvider.GetValue()
.
My question is: How can I globally make sure that this value is always CultureInfo.InvariantCulture.
I know I can implement custom value providers and do it this way.
I know I can implement custom model binders and do it this way.
I know I can set the culture in the thread, but unfortunately this is not an option in my case.
What I am looking for is a way to set it so that even the default model binder and the existing value providers are able to parse in culture invariant way, irrespective of what the thread culture is set to.
As far as I know, there is no way that will match your criteria. You will have to do one of the things you know you can do (I'd say the most proper way would be a custom value provider).
Reason: All the default ValueProviders are hardcoded to use either CultureInfo.InvariantCulture
or CultureInfo.CurrentCulture
.
Here, specifically, is the way FormValueProvider
does it:
internal FormValueProvider(
ControllerContext controllerContext,
IUnvalidatedRequestValues unvalidatedValues
)
: base(
controllerContext.HttpContext.Request.Form,
unvalidatedValues.Form,
CultureInfo.CurrentCulture // <--- Grrr, argh
)
{
}
The culture isn't retrieved from anywhere else (i.e., the argument above isn't used as a default, but as the one culture to use).
Cultures of the different IValueProviders
For reference, these are the cultures for each of the default IValueProviders:
Replacing the CurrentCulture
IValueProviders
It isn't a huge task to replace FormValueProvider
, since, as seen above, it just calls its base class' (NameValueCollectionValueProvider
) constructor - which takes the desired culture as an argument.
The original implementation of FormValueProvider appears on the surface to be harder than it actually is, with references to internal classes and interfaces. But they're not needed in order to replace the provider - they're only there for unit testing.
You only need to call the base constructor (as mentioned above), passing two NameValueCollection
s that are easy to acquire: Request.Forms
and the Forms
property of Validation.Unvalidated(Request)
(a static method). And set the third argument to the culture you want.
The FormValueProviderFactory
is even more straightforward.
JsonValueProvider
is a bit more involved - basically you'd have to copy the source of JsonValueProviderFactory
to a new class and modify it - because although it allows overriding GetValueProvider()
, that method mainly consists of calls to other private static
methods.
EDIT (Petar Ivanov): This worked for me. In order to make it work it was not enough to add the custom factory to ValueProviderFactories.Factories
, because this way it's is added after the FormValueProviderFactory
. Instead, I had to replace the FormValueProviderFactory
with the custom one.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With