Entity Framework decimal problem on insert after publish - c#

I have a data model named Gold with a decimal property for the price of gold like this:
public class Gold
{
[key]
public int GoldId { get; set; }
public decimal RateValue { get; set; }
}
I know that EF decimal default precision and scale is (18,2) and this is enough for me. I don't want to change it and I do not have any problem on insert decimal values on localhost.
But after publishing my website on a host, when I try to insert a decimal like 1210.40 it does not insert or sometimes it inserts 1210.00 (with wrong scale) in db without any error!
I completely confused because I don't have this problem on local running of my code.
No problem with decimals like 1210 without the right of dot digits!!!
This is my code for insert a decimal:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include="RateValue")]Gold manualPrices)
{
Gold ons = new Gold() {RateValue=(decimal)manualPrices.RateValue,};
db.GoldRepository.Insert(ons);
db.Save();
}
Razor Code:
#Html.EditorFor(model => model.RateValue, new { htmlAttributes = new { #class = "form-control" } })
more info: I am using MVC .Net Framework 4.5.2 , Code first, Unit of Work, MS SQL 2016.

Thanks to #StephenMuecke and #heuristican
The culture have been changed to Persian culture in global. This affects the character used for the decimal separator from dot(.) to slash (/) so decimals with dot as separator don't even bind.
Nothing changed in code. Just typing decimals like 1210/40 instead of 1210.40 for insert. Problem solved.

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).

MVC format number EU style for both server and client side

I'm searching for days how to format my both double and double? inputs as
decimal = comma and thousand separator =dot
example:
123456.01 ==> 123.456,01
14,02 ==> 14,02
987654321,002 ==> 987.654.321,002
what i did so far:
I checked server/local region setting
add globalization in my web.config
globalization uiCulture="fr-BE" culture="fr-BE
In my model i used the following attribute (but i'm using TextBoxFor)
[DisplayFormat(DataFormatString = "{0:N3}", ApplyFormatInEditMode = true)]
#Html.TextBoxFor( model => model.size, new {tabindex=11, id = "SizTotText", #class = "form-control input-sm calc" })
but
How can I:
1- while user is typing, the number should be formatted ?
I found this solution: how to format input box text as I am typing it
but i cannot switch comma with Dot Fiddle
2- Created Custom helper following this post (changed decimal to double)Post
there are a lot of solution in stackoverflow but none seems to work for me.
I hope my question is clear and sorry for my bad english
EDIT:
I decided to ignore the client formatting, no more dot's and comma but i still have issue with the regional setting.
My application still treating the dot as decimal separator, how can i fix this?
and more funny, it accepts some fields and others not, although they all have the same attribute, settings ... etc see screen shot
The field that has an error is a calculated field
var url = '#Url.Action("CalculateDropPrec", "Drop")';
$('.calc').change(function () {
// get the values of the textboxes
var numUsed = $('#numUseText').val();
var totNumUsed = $('#totNumUseText').val();
$.post(url, { nUsed: numUsed, tUsed: totNumUsed }, function (response) {
$('#precText').val(response);
});
return false;
});
#Html.TextBoxFor(model => model.PerceDropTot, new {data_val = "false", id = "precText", name = "precText", #class = "form-control input-sm drop-read", placeholder = "0.0", #readonly = "readonly" })
public JsonResult CalculateDropPrec(double nUsed, double tUsed)
{
var result = CommonComputation.CalcPrecDrop(nUsed, tUsed);
return Json(result);
}
This is probably not going into the direction you want, but is "number formatting as you type" really desired? the downside is that the behaviour can become very tricky, and I mean also for the user. Assume that you type the number "1234" which becomes "1.234". When the input field is focused again and the caret is placed here: "1.|234", is it clear what happens when Backspace is hit? Is the point removed, or the 1? What happens when the user tries to type , or . manually? All the questions need to be considered. Also, when you POST the value in a FORM, it's not a valid numeric when formatted.
For those reason, even advanced Controls with Numeric Inputs like the Kendo UI NumericTextBox which we use heavily, choose to format a display-only value but not intercept user inputs (try the linked Demo).
But you can also check how cleave.js solves the issue, it does the realtime formatting, but probably uses the browsers culture settings. But personally don't like how it behaves when trying to correct already existing values.

DataGridView: How to allow comma and dot as a separators at the same time

I have a DataGridViewTextBoxColumn which is binded to a property. I want to allow user to input numbers no matter what he uses to separate decimals. Also I don't need spaces or commas to separate thousads.
It's simple:
1.908 = 1.908
1,908 = 1.908
And if there is no way to specify format string, can i Replace(",",".") before binding? Or any other way?
Thank you. (Sorry for my English)
Crete another property of String type which will be bounded to that column.
Then set/read value of original property through this
public class YourBindableItem
{
public decimal OriginalValue { get; set; }
public decimal ParsedValue
{
get { return this.OriginalValue.ToString(); }
set
{
string forParse =
value.Replace(",", Globalization.CultureInfo.InvariantCulture.NumberFormat.NumberDecimalSeparator);
decimal temp = 0;
if(decimal.TryParse(forParse,
out temp,
Globalization.CultureInfo.InvariantCulture) == true)
{
this.OriginalValue = temp;
}
//if value wasn't parsed succesfully, original value will be returned
this.RaiseOnPropertyChanged(nameOf(this.ParsedValue));
}
}
}
The DataGridView already formats according to the regional settings for the current user, at least if you data bind to an object data source and the property is numeric (i.e. not a string).
You can test this by opening Region and Language in Windows and switching between e.g. the English (United States) format and Swedish (Sweden). In the former case, the input 2.718 will parse correctly while in the second 2,718 will. You'll have to run without debugging in VS in order to load fresh settings.
(I would not suggest trying to parse both comma and dot as a decimal separator for the same user, if you're thinking of doing that. That's not the expected behavior for most users and it would lead to bugs if the user should happen to use the thousand separator too.)

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)

Decimal numbers in ASP.NET MVC 5 app

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/

Categories