localizationmodel-bindingasp.net-core-3.1

How do I set the locale in asp.net core that my ModelBinders will use?


I am trying to force my asp.net core 3.1 application to use the locale "en-US", no matter the default locale of the Windows server it runs on.

My main goal is to have ModelBinding correctly parse doubles entered by users. So far, I did not have any luck. The comma is interpreted as the decimal separator, whereas the point is interpreted as the thousands separator, which is consistent with the Windows default locale.

So my code looks (simplified) like this:

public class Model
{
    public double Percentage {get; set;}
}

[HttpPost]
public ActionResult Index(Model model)
{
    Debug.WriteLine(model.Percentage);
}

This results in data entered and posted as "12.34" to be cast to double 1234, etc.

Previously, in ASP.Net MVC 5, adding this to the web.config file solved this issue:

  <configuration>
    <system.web>
      <globalization culture="en-US" uiCulture="en-US" />

I have tried various methods described, which I will list below, but to no avail. These seem to be revolving around localization of the mark-up, but do not affect model binding.

Apply the same settings as listed above. Adding this to the Startup's ConfigureServices() method:

System.Threading.Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
System.Threading.Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US");

Adding this to the same method:

services.Configure<RequestLocalizationOptions>(
    options =>
    {
        options.DefaultRequestCulture = new RequestCulture(culture: "en-US", uiCulture: "en-US");
        options.SetDefaultCulture("en-US");
    });

The latter attempts being merely trial-and-error.

I tried running on IIS and on IIS Express.

I do realize I could write a custom model binder, but I think this adds unneeded complexity. Also, I could skip model binding and parse the post data manually, but the actual models are extensive and contain a lot of data. I do not want to go that way.


Addendum

I could reproduce this behavior with a small demo application. Steps to reproduce:

Create a new web application in Visual Studio .Net 2019 with:

Add a view model:

public class InputModel
{
    [Range(0, 1)]
    public double Fraction { get; set; }
}

Replace the index.cshtml with:

@model InputModel
@using (Html.BeginForm("Index", "Home", FormMethod.Post))
{
    @Html.LabelFor(m=>m.Fraction)
    @Html.TextBoxFor(m=>m.Fraction)
    @Html.ValidationMessageFor(m => m.Fraction)
    <input type="submit" value="Submit" />
}

Add an action to the conntroller:

[HttpPost]
public IActionResult Index(InputModel input)
{
    if (ModelState.IsValid)
    {
        return Content($"Received fraction: {input.Fraction:E3}");
    }
    return View(input);
}

On my development Pc:

Received fraction: 1,000E+000

Adding this to ConfigureServices in Startup.cs:

services.Configure<RequestLocalizationOptions>(
    options =>
    {
        options.SupportedCultures = new List<CultureInfo> { new CultureInfo("en-US") };
        options.SupportedUICultures = new List<CultureInfo> { new CultureInfo("en-US") };
        options.SetDefaultCulture("en-US");
    });

does not make a difference.

Only when I set change settings through Control Panel - Clock and Region - Change date time or number formats, can I make my web application accept the dot as the decimal separator. I do not like having to rely on that.


Solution

  • The answer by nzjoel put me on the right track.

    app.UseRequestLocalization();

    does not change anything for me. However, adding

    app.UseRequestLocalization(:fr-CA", "fr", "de", "es");

    does work, as can been seen by changing the request to add a query string: ?culture=es, or by adding a cookie .AspNetCore.Culture=c=de|uic=de.

    If no culture, or a culture not listed, is passed, the culture will default to the first in the list. However, a culture with a country code that is not specifically supported, will default to plain language culture, so ?culture=fr-FR or culture=es-MX will default to français and español, respectively.