How can I properly validate a price field when doing postback?
I have already checked: How can I validate a currency field? but that doesn't fulfil my expectations.
I've tried with (and some variations):
[Range(0, (double)decimal.MaxValue)]
[DataType(DataType.Currency, ErrorMessage = "...")]
public decimal? Price {get; set;}
Problems:
It never uses the [DataType...] attribute to perform validations.
When the value used is not of decimal type, the issued validation error will be a standard English message that I don't know how to customize and need to.
If I make it string instead of decimal?, then the range validation will be thrown for non-decimal or negative values, but it will still allow values like: 1.23456, which is not the currency format I'm expecting.
I'm looking for a built-in approach before I have to create my own custom validation or regular expression. Something like:
[EmailAddress()]
public string ContactEmail {get; set; }
This perfectly suits me!
You can use FluentValidation. You create your own validator class inheriting from : AbstractValidator and inside the constructor you can put all your logic.
MyCurrencyValidatorMyClass : AbstractValidator<MyClass>
{
MyCurrencyValidatorMyClass()
{
RuleFor(x => x.MyField).NotNull().GreatherThan(0);
//All your rules
}
}
var validator = new MyCurrencyValidatorMyClass();
var myClassVar = new MyClass();
validator.Validate(myClassVar);
Also you can integrate your validator with MVC, for reference please see
https://fluentvalidation.codeplex.com/wikipage?title=mvc
For decimal reference see
https://github.com/JeremySkinner/FluentValidation/blob/master/src/FluentValidation/Validators/ScalePrecisionValidator.cs
This is the nuget package https://www.nuget.org/packages/FluentValidation
You can try this RegEx on your Price model property. It will check for strings in the format of "XXXX.XX" where X is a numeric digit (0-9):
[RegularExpression(#"\d{1,20}(\.\d{1,2})?", ErrorMessage = "Invalid Price. Please use the format of XXXX.XX.")]
The first range d{1, 20} allows up to twenty digits before the decimal place. The second range d{1, 2} allows up to two decimal places.
Related
I have to change the decimal comma to a point without changing the culture settings.
This is the only thing I found, but it doesn't change anything:
[DisplayFormat(DataFormatString = "{0:$###.###}")]
public double Number { get; set; }
[DisplayFormat(DataFormatString = "{0:$###,###}")]
public double Number { get; set; }
I'm using .NET Core 3.1
As #juharr already mentioned this should be done by culture settings, but you already specified that culture shouldn't be used.
Your only bet is to manually replace the comma with a point after your number was converted to a string: number.Replace(",", ".").
You could also add another Property like "NumberAsCurrencyString" or anything like that, that returns a formatted string determined by culture just for this case. All other culture settings inside your project or server won't be changed.
//using required for CultureInfo
using System.Globalization;
public NumberAsCurrencyString => Number.ToString("C0", CultureInfo.GetCultureInfo("en-US"));
You can try it, this work for me:
string.Replace(".",",");
That it's the fastest you can try, it doesn't change the culture
I need a regex which allows only 4 digits and those four can contain 0 at any position.
Below is my code:
View :
<label asp-for="UserId"></label><br />
<input asp-for="UserId" class="form-control" maxlength="4" />
<span asp-validation-for="UserId" class="text-danger"></span>
Model :
[RegularExpression(#"^([0-9]{4})$", ErrorMessage = "Please enter last 4 digits of your user Id.")]
[Display(Name = "Last 4 digits of user Id")]
public int? UserId{ get; set; }
But if I type in 0645, it throws an error "Please enter last 4 digits of your user Id.".If I change it to say 4567, it works fine. So how should I fix my regex?
You do not have any problem with your regex. As was already said in the comments, your property is an integer and when you set its value to 0645 internally it is converted to int and become 645.
If you look into RegularExpressionAttibute class, line 59, on GitHub you will realize that the method IsValid receives and object and then parses it as a string.
So lets look at the complete flow of your data.
1) Your user types a value into a textbox. ("0645")
2) ModelBinder converts the string typed into an integer. (645)
3) Inside RegularExpressionAttibute.IsValid your integer is converted again into an string ("645")
4) Regular Expression is applied to the value ("645") not ("0645"). So it will not pass your validation.
This is the RegularExpressionAttibute.IsValid method.
override bool IsValid(object value) {
this.SetupRegex();
// Convert the value to a string
string stringValue = Convert.ToString(value, CultureInfo.CurrentCulture);
// Automatically pass if value is null or empty. RequiredAttribute should be used to assert a value is not empty.
if (String.IsNullOrEmpty(stringValue)) {
return true;
}
Match m = this.Regex.Match(stringValue);
// We are looking for an exact match, not just a search hit. This matches what
// the RegularExpressionValidator control does
return (m.Success && m.Index == 0 && m.Length == stringValue.Length);
}
Whats the solution / suggestion?
You are expecting 4 digits as an input, and until now you don't said anything about have to do any kind of calculation with this.
As you don't need to do any calculation you can keep it as an string without any harm. Just keep validating that your string contains 4 digits (you are already doing it).
And if you need to do any calc in the future just convert the string to integer when its needed.
So just change this line:
public int? UserId{ get; set; }
To this:
public string UserId{ get; set; }
I have a date that gets displayed on my View that shows the last date a user logged in. They may have never done so. So it's passed as a nullable DateTime.
On the UI, I display it like this:
<td>#(u.LastVisit != null ? u.LastVisit.Value.ToString("dd-MMM-yyyy") : "Not yet")</td>
But I have a few issues with this and think it might be bad practice.
Firstly, the view now has logic. (If null, show "Not Yet", else show the date). The View also dictates the date format. That format is already stored as a constant in a Constants file, accessible from my controller. And the text "Not Yet" should probably be a constant as well.
The only way I can see around this, is to return a String to the UI, and move that logic to the controller. But is that the right way to do this?
You can apply a [DisplayFormat] attribute to the property and set the DataFormatString and NullDisplayText properties, for example
[DisplayFormat(DataFormatString = "{0:dd-MMM-yyyy}", NullDisplayText = "Not yet")]
public DateTime? LastVisit { get; set; }
If you have already defined some constants for the format then you can use (for example) DataFormatString = yourAssembly.Constants.DateFormat where DateFormat is defined as
public const string DateFormat = "{0:dd-MMM-yyyy}";
and in the view use DisplayFor()
#foreach(var u in Model)
{
#Html.DisplayFor(m => u.LastVisit)
}
I have a double variable :
public double Width { set; get; }
I want to validate the number and display an error message so I added:
[Range(0.0, Double.MaxValue, ErrorMessage = "Width must be a valid number")]
It works great if I enter a negative number, but if I leave it empty or enter letters - the inner error message is
"Input string was not in a correct format."
I'm not usign #html.validationmessagefor , because I need to handle the ModelState's ErrorMessage or error.exception.InnerException.message manually.
How can I fix that?
it seems that this error message comes from a double.Parse
the Data Annotation attributes are not used by the double class, they are just used by some frameworks.
anyway it looks like your input string was no valid double, so a range validation has nothing to do with a valid double string.
I have this:
var pl = new CultureInfo("pl-PL");
decimal valsue = decimal.Parse("2,25 PLN", pl);
It works ok if i don't put "PLN" into my string. But PLN is currency in my country and i think that it should parse, so maybe i am doing something wrong? Is there any option to parse this into decimal with "PLN" attached to the string?
If you take a look at your CultureInfo's NumberFormat object, this will yield some clues about how it intends to parse values. In particular, NumberFormat.CurrencySymbol for the "pl-PL" culture is zł.
In other words, this expression would parse successfully: decimal.Parse("2,25 zł", pl);
If you prefer to use PLN as the currency symbol (technically, it's the currency code), it is possible to configure a custom NumberFormat, like so:
var pln = (NumberFormatInfo) pl.NumberFormat.Clone(); // Clone is needed to create a copy whose properties can be set.
pln.CurrencySymbol = "PLN";
decimal valsue = decimal.Parse("2,25 PLN", NumberStyles.Currency, pln);
But note the usage of NumberStyles.Currency in the Parse call: by default, decimal.Parse accepts only strings containing numeric values, without currency formatting.