profileShare

rasmusjy / splitapp-backend-clean-onion

Read-only snapshot

No repository description.

main default branch 429 files Expires Sep 13, 2026, 9:06 AM
InvariantDecimalModelBinderProvider.cs 1,804 bytes
1 using System.Globalization;
2 using Microsoft.AspNetCore.Mvc.ModelBinding;
3
4 namespace WebApp;
5
6 /// <summary>
7 /// Ensures decimal values from HTML number inputs (which always send dot-separated values)
8 /// bind correctly regardless of the server's request culture (e.g. et-EE uses comma).
9 /// </summary>
10 public class InvariantDecimalModelBinderProvider : IModelBinderProvider
11 {
12 public IModelBinder? GetBinder(ModelBinderProviderContext context)
13 {
14 if (context.Metadata.ModelType == typeof(decimal) || context.Metadata.ModelType == typeof(decimal?))
15 {
16 return new InvariantDecimalModelBinder();
17 }
18
19 return null;
20 }
21 }
22
23 public class InvariantDecimalModelBinder : IModelBinder
24 {
25 public Task BindModelAsync(ModelBindingContext bindingContext)
26 {
27 var valueResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
28 if (valueResult == ValueProviderResult.None)
29 return Task.CompletedTask;
30
31 var value = valueResult.FirstValue;
32 if (string.IsNullOrWhiteSpace(value))
33 {
34 if (bindingContext.ModelType == typeof(decimal?))
35 {
36 bindingContext.Result = ModelBindingResult.Success(null);
37 }
38
39 return Task.CompletedTask;
40 }
41
42 // Normalize: replace comma with dot so InvariantCulture can parse both formats
43 value = value.Replace(',', '.');
44
45 if (decimal.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var result))
46 {
47 bindingContext.Result = ModelBindingResult.Success(result);
48 }
49 else
50 {
51 bindingContext.ModelState.TryAddModelError(bindingContext.ModelName, "Invalid number format.");
52 }
53
54 return Task.CompletedTask;
55 }
56 }
57