Decimal numbers in ASP.NET MVC 5 app - c#

I have a problem with decimal numbers.
If I use .(dot) instead of ,(comma) in the textbox it comes null in controller.
I know its a language issue because in spanish we use comma instead of dot for decimals but I need to use dot.
It is possible to change this?
It is strange because in controller I have to use .(dot) for decimals i.e:
I can do float x = 3.14 but I can not do float x = 3,14 so I do not understand this... In some cases I have to use dot... In others I have to use comma...
This is my code:
In model:
[Display(Name = "Total")]
public double Total { get; set; }
In view:
#Html.EditorFor(model => model.Total, new { id = "Total", htmlAttributes = new {#class = "form-control" } })
In controller:
public ActionResult Create([Bind(Include = "ID,Codigo,Fecha,Trabajo,Notas,BaseImponible,Iva,Total,Verificado,FormaDePagoID,ClienteID")] Presupuesto presupuesto)
{

Thanks everybody. I found this code from Phil Haack that works pretty well.
Create a class in any folder of your project
public class ModelBinder
{
public class DecimalModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext,
ModelBindingContext bindingContext)
{
object result = null;
// Don't do this here!
// It might do bindingContext.ModelState.AddModelError
// and there is no RemoveModelError!
//
// result = base.BindModel(controllerContext, bindingContext);
string modelName = bindingContext.ModelName;
string attemptedValue =
bindingContext.ValueProvider.GetValue(modelName).AttemptedValue;
// Depending on CultureInfo, the NumberDecimalSeparator can be "," or "."
// Both "." and "," should be accepted, but aren't.
string wantedSeperator = NumberFormatInfo.CurrentInfo.NumberDecimalSeparator;
string alternateSeperator = (wantedSeperator == "," ? "." : ",");
if (attemptedValue.IndexOf(wantedSeperator) == -1
&& attemptedValue.IndexOf(alternateSeperator) != -1)
{
attemptedValue =
attemptedValue.Replace(alternateSeperator, wantedSeperator);
}
try
{
if (bindingContext.ModelMetadata.IsNullableValueType
&& string.IsNullOrWhiteSpace(attemptedValue))
{
return null;
}
result = decimal.Parse(attemptedValue, NumberStyles.Any);
}
catch (FormatException e)
{
bindingContext.ModelState.AddModelError(modelName, e);
}
return result;
}
}
}
Add this to Application_Start() method in Global.asax
ModelBinders.Binders.Add(typeof(decimal), new ModelBinder.DecimalModelBinder());
ModelBinders.Binders.Add(typeof(decimal?), new ModelBinder.DecimalModelBinder());
Now use decimal type instead of float or double and everything will go fine !!
Thank you mates see you around !.

Your controller uses C#. The language specific states that . is the decimal separator. Period. It's not language specific, that's just it.
Your database or UI (which uses the server's language settings) might use another decimal separator than the default (US) language setting C# uses. That's why you have to use , as separator there.

you would need to use a custom model binder.
See this blog post http://haacked.com/archive/2011/03/19/fixing-binding-to-decimals.aspx/

If you want your comma(,) separated decimal input in UI as per UI culture, to be converted to dot(.) to bind to C# decimal number, you can go for Asp.Net MVC's custom model binder, where take the comma separated decimal string and replace the comma with a dot and then assign to the C# decimal property.
The advantage is, its reusable across the application, where you might be having recurring scenarios for decimal conversions.
Hope following links could help you:
ASP.Net MVC Custom Model Binding explanation
http://odetocode.com/blogs/scott/archive/2009/04/27/6-tips-for-asp-net-mvc-model-binding.aspx
http://haacked.com/archive/2011/03/19/fixing-binding-to-decimals.aspx/

Related

passing decimals to REST API from multilingual frontend

I have a REST API in .Net Core 3 and front end in Angular 8. My front is a multilingual admin panel where I need to configure price for a product. The issue is I am not able receive price with decimal values.
My default culture of .NET Core API is "en-US" but my client is using "nl-NL" from front end. As you know Netherland they use "," instead of "." for decimal points therefore I am not getting price in my submitted model. Here are code snippets;
REST API
Entity
public class Product{
public Guid Id {get;set;}
public string Name {get;set;}
public decimal Price {get;set;}
}
Controller Method
[HttpPost]
public Task<IActionResult> SaveProduct([FromForm]Product model){
....code to save the product....
}
Stratup.cs
services.Configure<RequestLocalizationOptions>(options =>
{
var supportedCultures = new List<CultureInfo>
{
new CultureInfo("en"),
new CultureInfo("de"),
new CultureInfo("fr"),
new CultureInfo("sv")
};
options.RequestCultureProviders = new List<IRequestCultureProvider>()
{
new AcceptLanguageHeaderRequestCultureProvider()
};
options.FallBackToParentCultures = true;
options.SupportedCultures = supportedCultures;
options.SupportedUICultures = supportedCultures;
});
I tried setting Requestdefault culture to "nl" but then "en" values doesn't work. Can any one please help me how to pass decimal points from multilingual frontend to a REST API.
Thanks.
If you use [FromForm],you could create your own custom mobel binder for the Price property:
1.Create a DecimalModelBinder
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;
}
// Replace commas and remove spaces
value = value.Replace(",", ".").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;
}
}
2.Use it on Price property
public class Product
{
public Guid Id { get; set; }
public string Name { get; set; }
[BindProperty(BinderType = typeof(DecimalModelBinder))]
public decimal Price { get; set; }
}
3.Action
[HttpPost]
public Task<IActionResult> SaveProduct([FromForm]Product model){
....code to save the product....
}
Based on your question, it appears that your problem is that your front-end code is submitting the price with a comma on it. This can only be possible if you are sending a string representation of the price to the server, rather than a numeric one.
You should modify your front end code to always store and transmit the price value as a number. You can control how a price is displayed to a user in strings using Intl.NumberFormat().
As the user has to enter a price, you're probably using a <input type=text /> tag to capture the cost of the product. Since we want users to be able to enter commas here, we can't use <input type=number /> - instead, we can simply modify the value we get from this input tag on the client side by replacing occurences of commas in the string with a period - amount.replace(',', '.') - and then attempting to parse the number from the string - parseInt(amount, 10).
If parseInt returns NaN, you should display a validation error to the user.
Getting Decimal values for a price from the client is a terrible idea. I remember stories where a haphazard developer had put the shopping cart into the cookie, including prices. It took not that long for this mistake to be found and exploited. The company had the bill, because they had made their shop faulty.
Never trust input from the user. Especially if that user is on the Internet.
As for the specific problem: You basically have the issue that frontend and backend culture varies. I got 3 items of advice for transmitting numbers between processes:
never transmit them as String. String is arguably the 2nd worst format for processing. The only thing worse is raw binary
if you have to transmit them as string, make certain you pick fixed culture and encoding at both endpoints. Stuff like XML and JSON tends to take care of that for you. But you may have to pick something like the Invariant Culture.
If you are transmitting, storing or retrieving a DateTime, always do so as UTC. And hope you do not have that rare application that has to store the original Timezone (like a calendar).

How can i convert string value to rial money unit?

I'm beginner in c# and have this value of string:
123456
but want convert that string to my country money, want convert that string value to this:
123,456
always split three numbers with comma for example, if string number is this:
1234567890
Show to user this:
1,234,567,890
How can i write code that purpose?
I would suggest convert it to int (or long) first and then use ToString() and supply required format.
int number = int.Parse(numberString); //ex..
number.ToString("N0"); // 1,000,000
If you're asking about culture-specific formatting, then you could do this.
number.ToString("N0", CultureInfo.CreateSpecificCulture("es-US"));
You can explore more on standard numeric formats
Example code
Use the standard formatters and the CultureInfo for the desired country.
e.g
int i = int.Parse("123456");
string money = i.ToString("C", CultureInfo.CreateSpecificCulture("fr-Ir"));
Or if the system culture is fr-Ir
string money = i.ToString("C");
Which is the same as
string money = i.ToString("C", CultureInfo.CurrentCulture);
Or if you want to use the UI culture (the culture of the requesting browser)
string money = i.ToString("C", CultureInfo.CurrentUICulture);
Since you want to convert your value to currency, I would suggest using "C" of string formats provided by .NET.
123456.125M.ToString("C"); // $123,456.13
Sign infront of the string will be defined by the culture of your machine. More information here.
On the other hand, there is another solution to add your own custom format:
123456.125M.ToString("#,0.################"); // 123,456.125
It is not the clean way, but I have not since found a correct way of actually formating this in generic way.
Side note: for currency handling it is generally considered a good practise to use decimal. Since it does not have a floating point issue.
Please try this one hope will help
Just whats inside the void method
using System.Linq;
public class Program
{
public void ABC()
{
var data = "123456789";
const int separateOnLength = N;
var separated = new string(
data.Select((x,i) => i > 0 && i % separateOnLength == 0 ? new [] { ',', x } : new [] { x })
.SelectMany(x => x)
.ToArray()
);
}
}

Code not converting text

I have a textbox that allows users to input a decimal values that then gets stored in the a table in the database, this piece of code works in the development environment. I have now published the my project to the server and now is not longer taking the values with the decimal places.
decimal ReceiptAmount;
decimal AmountDue;
decimal Change;
if (!string.IsNullOrEmpty(((TextBox)dl_Item.FindControl("tb_ReceiptAmount")).Text))
{
if (((TextBox)dl_Item.FindControl("tb_ReceiptAmount")).Text.Contains(".") == true)
{
ReceiptAmount = Convert.ToDecimal(((TextBox)dl_Item.FindControl("tb_ReceiptAmount")).Text.Replace(".", ","));
}
else
{
ReceiptAmount = Convert.ToDecimal(((TextBox)dl_Item.FindControl("tb_ReceiptAmount")).Text);
}
}
else
{
ReceiptAmount = 0;
}
if (!string.IsNullOrEmpty(((TextBox)dl_Item.FindControl("tb_AmountDue")).Text))
{
if (((TextBox)dl_Item.FindControl("tb_AmountDue")).Text.Contains(".") == true)
{
AmountDue = Convert.ToDecimal(((TextBox)dl_Item.FindControl("tb_AmountDue")).Text.Replace(".", ","));
}
else
{
AmountDue = Convert.ToDecimal(((TextBox)dl_Item.FindControl("tb_AmountDue")).Text);
}
}
else
{
AmountDue = 0;
}
if (!string.IsNullOrEmpty(((TextBox)dl_Item.FindControl("tb_Change")).Text))
{
if (((TextBox)dl_Item.FindControl("tb_Change")).Text.Contains(".") == true)
{
Change = Convert.ToDecimal(((TextBox)dl_Item.FindControl("tb_Change")).Text.Replace(".", ","));
}
else
{
Change = Convert.ToDecimal(((TextBox)dl_Item.FindControl("tb_Change")).Text);
}
}
else
{
Change = 0;
}
I am not to sure what seems to be the problem with this piece of code. The Textbox are found in a datalist that I loop through to get all of the values.
The Convert.ToDecimal overload that takes a string as input will parse the string using the CultureInfo.CurrentCulture. Probably your server has different regional settings. Depending on regional settings, a comma or point may be either interpreted as a thousand separator (and thus ignored) or as the decimal separator.
Instead, you should use Decimal.Parse directly, providing either a specific culture or the invariant culture, depending on your use case.
Ideally, you'd set the culture of the user somewhere. To achieve this there are multiple approaches, e.g. for ASP.Net Web forms: https://msdn.microsoft.com/en-us/library/bz9tc508.aspx
If you parse the string using the correct culture, you can get rid of the string manipulation for replacing . with ,.
First of all, lines like
if (!string.IsNullOrEmpty(((TextBox)dl_Item.FindControl("tb_ReceiptAmount")).Text))
look very ugly; let's extract a method (copy/paste is very, very bad practice):
private String FindDLText(String controlName) {
var box = dl_Item.FindControl(controlName) as TextBox;
return box == null ? null : box.Text;
}
Then you don't need checking Text.Contains(".") == true, just Replace if you really need it:
private Decimal FindDLValue(String controlName) {
String text = FindDLText(controlName);
if (String.IsNullOrEmpty(text))
return 0.0M;
//TODO: check if you really need this
//text = text.Replace(".", ",");
// you have to specify Culture either InvariantCulture or some predefined one;
// say, new CultureInfo("ru-RU") // <- use Russian Culture to parse this
return Decimal.Parse(text, CultureInfo.InvariantCulture);
}
Finally, you can get
decimal ReceiptAmount = FindDLValue("tb_ReceiptAmount");
decimal AmountDue = FindDLValue("tb_AmountDue");
decimal Change = FindDLValue("tb_Change");
feel the difference: three evident lines and two simple methods.

formatting ajax.actionlink text to display

I have a decimal data type in my model and an annotation to format it so it adds commas after 3 digits:
[DisplayFormat(DataFormatString = "{0:#,###0.00}" + " (USD)")]
public decimal PaidAmount { get; set; }
when I have any DisplayFor(m => m.PaidAmount) the formatting displays correctly (1,200.00 USD). However, in Ajax.ActionLink the first argument takes a string for the text to display so I can't use a lambda expression (m => m.PaidAmount). When I do:
Ajax.ActionLink(Model.PaidAmount.ToString(), //rest of link params)
the formatting doesn't apply to the link text, it shows just a bunch of numbers without commas (1200.00 USD, note there is no comma after the 1)
my guess is that using the capital 'M'odel version of model loses its annotation properties, is there a way to go around this and apply the formatting to the ajax.actionlink?
You can use
#Ajax.ActionLink(string.Format("{0:#,##0.00 USD}", Model.PaidAmount), ...)
Note I think you mean 0:#,##0.00 not 0:#,###0.00 (i.e. 10,200.00 USD, not 1,0200.00 USD)
Data annotations will not work in this case as they are checked in HtmlHelpers and won't affect regular ToString.
You can create an extension method to format your number an call it in your view:
public static class Extensions
{
public static string ToCurrency(this decimal number)
{
return number.ToString("{0:#,###0.00}") + " (USD)";
}
}
In you view: (don't forget to reference the Extensions class either directly in the view or in web.config under views folder)
Ajax.ActionLink(Model.PaidAmount.ToCurrency(), //rest of link params)

ViewData.ModelState.IsValid is false because Mvc can't parse value which has separated hundreds and thousands with commas

I have fields of type int?.
On view i use jquery plugin which separate textBox value with commas like this : 3,463,436 = 3463436 - it's must be int value.
But on Form subbmit i get error "The value '3,463,436' is not valid for Maximum Contiguous."
Any advice? Thanks.
A better solution than stripping out commas is to use .NET's built in NumberStyles parsing.
public class MyBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
string key = bindingContext.ModelName;
var v = ((string[])bindingContext.ValueProvider.GetValue(key).RawValue)[0];
int outPut;
if (int.TryParse(v, NumberStyles.AllowThousands, new CultureInfo("en-US"), out outPut))
return outPut;
return base.BindModel(controllerContext, bindingContext);
}
}
ModelBinders.Binders.Add(typeof(int), new MyBinder());
In ASP.NET MVC one option is to implement a custom ModelBinder that strips out commas from the submitted values. I.e. implement a subclass of DefaultModelBinder
Here is how to do it for the decimal separator for the decimal data type:
How to set decimal separators in ASP.NET MVC controllers?
You will have to something similar for the thousands separator for the int data type.

Categories