В asp.net core mvc привязка модели для десятичных дробей не принимает тысячи разделителей - PullRequest
0 голосов
/ 13 января 2019

Для модели со свойством decimal, если значение из клиента содержит запятые в качестве разделителя тысяч, привязка модели не будет выполнена.

Как мы можем решить это? Любое решение (глобально, контроллер / действие локальное или модель / свойство локальное) является хорошим.

У меня есть обходной путь, который должен иметь свойство string, которое читает и записывает в decimal. Но я ищу более чистое решение.

Ответы [ 2 ]

0 голосов
/ 26 июля 2019

В случае, если привязка локализации и пользовательской модели не работает для вас, как это не для меня. Вы можете расширить параметры сериализации с помощью пользовательского JsonConverter, который будет сериализовать и десерализовать все десятичные значения.

private class CultureInvariantDecimalConverter : JsonConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        writer.WriteValue(value);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
        JsonSerializer serializer)
    {
        //your custom parsing goes here
    }

    public override bool CanConvert(Type objectType)
    {
        return (objectType == typeof(decimal) || objectType == typeof(decimal?));
    }
}

И применить его с этим методом расширения

public static IMvcBuilder AddInvariantDecimalSerializer(this IMvcBuilder builder)
{
    return builder.AddJsonOptions(options =>
        options.SerializerSettings.Converters.Add(new CultureInvariantDecimalConverter()));
}
0 голосов
/ 13 января 2019

Если ваше приложение должно поддерживать только определенный формат (или культуру), вы можете указать его в своем методе Configure следующим образом:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    var cultureInfo = new CultureInfo("en-US");
    cultureInfo.NumberFormat.NumberGroupSeparator = ",";

    CultureInfo.DefaultThreadCurrentCulture = cultureInfo;
    CultureInfo.DefaultThreadCurrentUICulture = cultureInfo;

   [...]
}

Если вы хотите поддерживать несколько культур и автоматически выбирать правильную для каждого запроса, вместо этого вы можете использовать промежуточное программное обеспечение для локализации, например ::100100

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    [...]  

    var supportedCultures = new[]
    {
        new CultureInfo("en-US"),
        new CultureInfo("es"),
    };

    app.UseRequestLocalization(new RequestLocalizationOptions
    {
        DefaultRequestCulture = new RequestCulture("en-US"),
        // Formatting numbers, dates, etc.
        SupportedCultures = supportedCultures,
        // Localized UI strings.
        SupportedUICultures = supportedCultures
    });

    app.UseHttpsRedirection();
    app.UseStaticFiles();
    app.UseCookiePolicy();

    app.UseMvc();
}

Подробнее здесь: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/localization?view=aspnetcore-2.2

Правка - Десятичный переплет

Если все вышеперечисленное не помогло, вы также можете свернуть свою собственную модель, например ::11015.

public class CustomBinderProvider : IModelBinderProvider
{
    public IModelBinder GetBinder(ModelBinderProviderContext context)
    {
        if (context == null)
        {
            throw new ArgumentNullException(nameof(context));
        }

        if (context.Metadata.ModelType == typeof(decimal))
        {
            return new DecimalModelBinder();
        }

        return null;
    }
}

public class DecimalModelBinder : IModelBinder
{
    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);

        if (valueProviderResult == null)
        {
            return Task.CompletedTask;
        }

        var value = valueProviderResult.FirstValue;

        if (string.IsNullOrEmpty(value))
        {
            return Task.CompletedTask;
        }

        // Remove unnecessary commas and spaces
        value = value.Replace(",", string.Empty).Trim();

        decimal myValue = 0;
        if (!decimal.TryParse(value, out myValue))
        {
            // Error
            bindingContext.ModelState.TryAddModelError(
                                    bindingContext.ModelName,
                                    "Could not parse MyValue.");
            return Task.CompletedTask;
        }

        bindingContext.Result = ModelBindingResult.Success(myValue);
        return Task.CompletedTask;
    }
}

Не забудьте зарегистрировать пользовательское связующее в вашем методе ConfigureServices:

 services.AddMvc((options) =>
 {
     options.ModelBinderProviders.Insert(0, new CustomBinderProvider());
 });

Теперь каждый раз, когда вы используете десятичный тип в любой из ваших моделей, он будет анализироваться вашим пользовательским механизмом связывания.

...