MVC3 Range attribute - doesnt admit value zero - c#

I have the following attribute on my field:
[Range(-1,200)]
public decimal MyValue{ get; set; }
If I enter any value that doesn't fall in the range I get:
The field must be between -1 and 200
This is fine.
Here's the problem, I'm getting "The field must be a number" validation message when I enter zero which is a valid value.
Any suggestions?
Thanks

I think this may be happening due to the decimal type. Try this:
[Range(typeof(Decimal),"-1", "200")]
public decimal MyValue{ get; set; }
Source.

Related

Why validation for Decimal not work for ASP.NET Core 3.1 Razor Pages?

Why validation for Decimal not work for ASP.NET Core 3.1 Razor Pages ?
Client side work, but after post data I get this server side validation message (ModelState.IsValid = false in my case).
The value '1.000000' is not valid for...
Model property:
[Column(TypeName = "decimal(16, 6)")]
public decimal ValueFactor { get; set; }
View:
<input class="form-control" asp-for="#Model.ValueFactor" />
<span asp-validation-for="ValueFactor" class="text-danger"></span>
I found a solution, the thing is in the regional settings of my computer, where the decimal symbol is a comma.
Server side wants dot for decimal separator, but my client side wants comma as you can see in picture, that's why there is inconsistency, for resolving this just you need to change Decimal symbol type as shown in picture.
Column attribute is Code First Data Annotation
For validation of decimal you need a combination of regex and range validations like below:
[RegularExpression(#"^\d+\.\d{6}$")]
[Range(0, 9999999999.999999)]
RegularExpression attribute above will accept ' any number of digits, follwed by dot(.) and then 6 digits'. So following numbers are accepted: 1.000000, 500000.000000 etx.
Range attribute accepts a number of type decimal(16,6) between 0 and 9999999999.999999
Try something like this on server side.
public class PersonModel
{
[Required(ErrorMessage = "Required Decimal Number")]
[RegularExpression(#"^[0-9]+(\.[0-9]{1,100})$", ErrorMessage = "Valid Decimal number with maximum decimal places.")]
public string Expenses { get; set; }
}
My model
[Column(TypeName = "decimal(18,2)")]
public Decimal Bonus { get; set; }
I created a view model.
[Display(Name="Ek Ücret")]
[RegularExpression(#"^(\d+),(\d{2})$", ErrorMessage = "Tutar 0,00 - 9999999999,99 aralığında olmalıdır. ")]
public string Bonus { get; set; }

Integer validation against non-required attributes in MVC

I've trying to validate a property on a model I have. This property is NOT required, and so if its invalid MVC seems to be ignoring it. I've even created a custom ValidationAttribute, but nothing works.
public class NumberWang : ValidationAttribute
{
public override bool IsValid(object value)
{
if (value == null)
return true;
int g;
if (int.TryParse(value.ToString(), out g))
{
if (g >= 0)
return true;
}
return false;
}
}
public class MyModel
{
[Range(0, 999999, ErrorMessage = "category_id must be a valid number")]
[NumberWang(ErrorMessage = "That's NumberWang!")]
public int? category_id { get; set; }
/* there are other properties of course, but I've omitted them for simplicity */
public void Validate()
{
Validator.TryValidateProperty(this.category_id,
new ValidationContext(this, null, null) { MemberName = "category_id" },
this.validation_results);
}
}
If I pass the value 'abc' as a category_id to this model, it validates just fine. What am I doing wrong?
I found an ugly workaround.
It seems that if category_id is a nullable int? and my value is not a valid number, a null value is passed, and the model doesn't see the invalid 'abc' value.
[Range(0, 999999, ErrorMessage = "category_id must be a valid number")]
public int? category_id { get; set; }
// when we pass a good number
MyAction?category_id=123
validation: successful
// when we pass a bad number
// validation ignores it. not what we want.
MyAction?category_id=abc
validation: successful
If I change category_id to a non-nullable int, it fails validation even when no value is passed.
[Range(0, 999999, ErrorMessage = "category_id must be a valid number")]
public int? category_id { get; set; }
// when we pass a good number
MyAction?category_id=123
validation: successful
// when we pass an bad number
MyAction?category_id=abc
validation: "category_id must be a valid number"
// BUT, when we don't pass any number at all ...
MyAction
validation: "category_id must be a valid number"
The Ugly Workaround
If I change category_id to a string, and then only convert it to an int when I need it, I can validate it properly, using only [Range]
[Range(0, 999999, ErrorMessage = "category_id must be a valid number")]
public string category_id { get; set; }
// when we pass a good number
MyAction?category_id=123
validation: successful
// when we pass a bad number
MyAction?category_id=abc
validation: "category_id must be a valid number"
// no number, no validation. hooray!
MyAction
validation: successful
It's ugly, but it works.
(Note: the custom attribute was not needed, so I removed it and just used [Range])
model:
// "(\s*[0-9]{0,6})" for 999999 max
// "(\s*[0-9]*)" for "infinite"
[RegularExpression(#"(\s*[0-9]{0,6})", ErrorMessage = "Field must be a natural number (max 999999)")]
public int? Quantity { get; set; }
view:
#Html.TextBoxFor(m => m.Quantity)
#Html.ValidationMessageFor(m => m.Quantity)
First your model should implement IValidatableObject, but the Validate method is going to be called only if the ModelState is valid. This link can help.
Are you receiving your model as a parameter in the action? Are you asking for the ModelState.IsValid? This should work fine with the Default model binder.
I found a workaround that I kind of like. The problem I ran into was a similar issue, where the value had to be greater than 0 and a number, so once I tried to cast the 9999999999 (invalid) string as a number it threw an exception and didn't show that the model state has an error message on the post. I hope this is on topic since it sounds like "Rules don't seem to apply correctly when I type in an invalid number"
Since my number had to be positive I intercepted the value into an anonymous type object (dynamic) and used it as an intercepting body between the model's user and the private int property. When I did that, the Data Annotations worked as expected.
public dynamic MyProperty
{
get { return _myProperty; }
set
{
try
{
/I have to convert the value to String because it comes in as String[], and if there is more than one value then it should be problematic, so you join it together with an invalid character and throw an error, or you take the first value/
_myProperty = = Convert.ToInt32(String.Join("a",value));
}
catch (Exception e)
{
_myProperty= -1;
}
}
}
private int _myProperty;

Problem getting the MVC3 model binder to bind to a decimal instead of an int

I have a AJAX call from my webpage sending the following data to my MVC3 action method.
{
"name":"Test",
"newitems":[
{"id":15,"amount":100,"unit":"gram"},
{"id":1,"amount":75,"unit":"gram"},
{"id":46,"amount":25,"unit":"gram"}
]
}
In my controller I have the following classes:
public class NewDataItem
{
public string name { get; set; }
public List<NewDataItemDetails> newitems { get; set; }
}
public class NewDataItemDetails
{
public int id { get; set; }
public int amount { get; set; }
public string unit { get; set; }
}
And the Action method that received the request have a NewDataItem as a parameter. This works perfect, however the amount property of NewDataItemDetails might not always contain an int. It might for example be 50.45. So because of that I changed the line public int amount { get; set; } to public decimal amount { get; set; }.
After this change amount is always shown as 0, and not the proper value that it did when it was an int.
Why does MVC fail to bind the value to the property when it is a decimal, when it worked just fine as an int?
See the answer here Default ASP.NET MVC 3 model binder doesn't bind decimal properties from ryudice about creating a DecimalModelBinder. It works and it also keeps all the model validation working, (some other solutions use deserialization which can stop validation from working.
I found the problem.
If I change the line "amount":100, to "amount":"100", it works fine. It seems that the MVC ModelBinder can manage the string to decimal conversion, but not the int to decimal.
Your type (class) NewDataItemDetails property amount is of type int.
You are making things difficult to manage. If you are expecting to have amount as a decimal eventually, it simplifies to actually use amount's type as decimal.
This will avoid having you to extend the default model binder and cater for your behaviour.
I think, it is wiser and it simplies to send the amount as decimal in the ajax call. it provides consistency.
I know I am a bit late with this but there is another solution which works without changing anything until MVC fixes it (I believe it is at their end).
In my javascript I add 0.00001 to my value if it is supposed to be a decimal but is a round number. For those I know should be currency values I know this has no effect and will round down. For those that I do not know their value and this could affect it I remove the 0.00001 from the value after binding in mvc. The binding works correctly as this is no longer an int in the eyes of mvc
If you don't want to change model binder then you can create json object and convert amount to string.It will work fine for me.
var test={"amount":""};
var test1={"amount":100}
test.amount=test1.amount.toString();

asp.net mvc - DTO not picking up incorrect input

I have a basic DTO object which I am trying to update, I noticed if I post some data from the UI to the controller and I enter string inside a decimal field the data annotations validation does not pick this up, in fact the string is converted into 0 for some reason...
How do I get my decimal values to remain decimal i.e. throw an error if a string is added, do I need to create a custom value provider for this DTO object?
My DTO:
public class FeesDTO
{
public int ID{ get; set; }
//[DataType( DataType.Currency)]
//[DisplayFormat(ApplyFormatInEditMode=true)]
public decimal ClientFee { get; set; }
public string VAT { get; set; }
public string GrossProfit { get; set; }
}
If I want to update my fees and I enter 'something' inside the ClientFee field this turns the string input into 0...
NOTE:
The commented out data annotations did not work... Is this the correct way to do this?
when you enter a string value e.g "xyz" for ClientFee which is a decimal variable, it can not be converted to decimal and you will get 0 as value of clientfee but when form is rendered again you will see validation message telling you that
xyz is not valid for field clientfee
this is perhaps due to the fact that when modelbinder fail to convert xyz to decimal it adds the error in Modelstate dictionary's error collection. you can make this value string in your viewmodel and validate it yourself using regex and when you are to store it to db you can convert it to your entity with AutoMapper to similar utility.
I think you need to be using the Data Annotation validators:
http://www.asp.net/mvc/tutorials/validation-with-the-data-annotation-validators-cs
Implement one on the Decimal field that throws a validation error if the Control is posted to with a non decimal entry.

asp.net mvc int property bind exception

I have a int property in my class and want to validate if the user has entered a string.
How can I do that using data annotations?
When I pass a non-integer value I get a excpetion like this:
The value 'asdasd' is not valid for property.
For example using this validation attribute:
[Range(0, Int32.MaxValue, ErrorMessage="Invalid Number")]
public int? Number { get; set; }
and entering 'aaa' in a field tha uses the model I've got an excepetion with this message:
The value 'aaa' is not valid for Number.
Instead of the "Invalid Number" message.
Any Ideas?
I've put
[Range(0, Int32.MaxValue, ErrorMessage="Invalid Number")]
public int? Number { get; set; }
but I've got this message from an excpetion
The value 'aaa' is not valid for Number.
There are two stages of validation at play here.
Before the validation set up in your attributes is called, the framework first attempts to parse the information.
So here are a couple of examples based on this code:
[Range(0, Int32.MaxValue, ErrorMessage="Invalid Number")]
public int? Number { get; set; }
I type nothing into the box...
"Invalid Number" (Framework will create a null integer, your validation rule fails)
I type "A" into the box...
"The value 'A' is not valid for Number." (Framework cannot convert "A" into a nullable int, so the framework validation rule fails and your validation rule it not checked.
** Solutions **
1 - Live with the default message until you are using MVC 3 / .NET 4, which makes it easier to override these messages
2 - Exclude the value from the binder, so it won't cause an error (but you will have to bind it and check it yourself)
[Bind(Exclude="MyNumber")]
3 - Make it a string on the model, then test it with a TryParse and add your own custom model error (This is a reasonable practice and reminds us all why View Models are used rather than Domain Objects!)
if (!Int32.TryParse("MyNumber", out myInteger)) {
ModelState.AddModelError("MyNumber", "That isn't a number!");
}
There are actually lots of solutions, but I would say go with option 3 for now.
You could put a Range-attribute on your model-field like so:
[Range(0, 9999)]
public int Something {get; set;}
Now whenever the user inputs a string it will not validate, and also the int must be between 0-9999
this should also work with
[Range(0, 9999)]
public string Something {get; set;}
and
[Range(0, 9999)]
public object Something {get; set;}

Categories