9

The default model binder is returning errors for properties that are of type double when my application is being used in countries that use different number formatting for decimals (e.g. 1.2 = 1,2). The culture of the site is set conditionally in my BaseController.

I have tried adding a custom model binder and overriding the bindModel function but I can't see how to get around the error in there as the Culture has already been set back to the default of en-GB.

So I tried adding an action filter to my BaseController that sets the Culture there but unfortunately bindModel seems to get fired before my action filter.

How can I get around this? Either by getting the Culture to not reset itself or set it back before bindModel kicks in?

Controller where model comes in invalid:

public ActionResult Save(MyModel myModel)
{
    if (ModelState.IsValid)
    {
        // Save my model
    }
    else
    {
       // Raise error
    }
}

Filter where Culture is set:

public override void OnActionExecuting(ActionExecutingContext filterContext)
{
    CultureInfo culture = createCulture(filterContext);

    if (culture != null)
    {
         Thread.CurrentThread.CurrentCulture = culture;
         Thread.CurrentThread.CurrentUICulture = culture;
    }

    base.OnActionExecuting(filterContext);
}

Custom Model Binder:

public class InternationalDoubleModelBinder : DefaultModelBinder
{
   public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
   {
       ValueProviderResult valueResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
       if (valueResult != null)
       {
           if (bindingContext.ModelType == typeof(double) || bindingContext.ModelType == typeof(Nullable<double>))
           {
               double doubleAttempt;

               doubleAttempt = Convert.ToDouble(valueResult.AttemptedValue);

               return doubleAttempt;
           }
        }
        return null;
   }
}
Nick Reeve
  • 1,658
  • 2
  • 22
  • 32

2 Answers2

3

You want your application to use a single culture, right? If so, you can do this with the globalization tag of your web.config.

<configuration>
    <system.web>
        <globalization
           enableClientBasedCulture="true"        
           culture="en-GB"
           uiCulture="en-GB"/>
    </system.web>
</configuration>

And then you can forget those custom model binder and use the default.

UPDATE: Ok, it's a multi-language application. How do you get the culture you want? Can you call createCulture on the MvcApplication class? You could do this:

public class MvcApplication : HttpApplication
{
    //...
    public void Application_OnBeginRequest(object sender, EventArgs e)
    {
        CultureInfo culture = GetCulture();
        Thread.CurrentThread.CurrentCulture = culture;
        Thread.CurrentThread.CurrentUICulture = culture;
    }
    //...
}

This method is called before the model bind, so, again, you won't need the custom model binder. I hope it works for you :)

goenning
  • 6,514
  • 1
  • 35
  • 42
  • Hi, no it's a multi-language app and the culture gets set in the BaseController each time – Nick Reeve Feb 21 '11 at 11:07
  • Very useful. I set culture in filter and spent many hours trying to understand why it still parses incorrectly. Thank you. – yu_sha May 27 '11 at 21:08
  • 1
    You could also switch the action filter to implement the IAuthorizationFilter. Similiar issue was resolved in that way by a friend of mine. – macwier Jun 24 '13 at 06:51
-1

Take a look in this article but, for short, if you could try this:

public ActionResult Create(FormCollection values)
{
    Recipe recipe = new Recipe();
    recipe.Name = values["Name"];      

    // ...

    return View();
}

...or this, in case you have a Model:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(Recipe newRecipe)
{            
    // ...

    return View();
}

The article has complete references and other ways of doing this. I use these 2 and they were enough for me up to now.

Davidson Sousa
  • 1,353
  • 1
  • 14
  • 34
  • 1
    How does this answer the question? – Serge Wautier Feb 19 '11 at 16:29
  • Yes this doesn't answer the question at all? Your first suggestion doesn't use the default model binder at all and is actually declared as 'doing it all wrong' in the link and the second suggestion actually is using the model binder in it's default fashion and is the source of this problem? – Nick Reeve Feb 19 '11 at 20:36
  • Well... Can you provide a piece of your code which we will check and give a proper answer? – Davidson Sousa Feb 19 '11 at 20:49