Steven
Steven

Reputation: 18859

Validate decimal value to 2 decimal places with data annotations?

I have this in my view model:

[Required(ErrorMessage = "Price is required")]
[Range(0.01, 999999999, ErrorMessage = "Price must be greater than 0.00")]
[DisplayName("Price ($)")]
public decimal Price { get; set; }

I'd like to validate that the user doesn't enter more than 2 decimal places. So I'd like to have

Valid values: 12, 12.3, 12.34

Invalid values: 12., 12.345

Is there a way to validate this with a data annotation?

Upvotes: 25

Views: 109076

Answers (8)

Franck Duhaupas
Franck Duhaupas

Reputation: 41

To make it works for languages with decimal separator other than period (.) :

using System;
using System.ComponentModel.DataAnnotations;
using System.Globalization;

/// <summary>
/// Decimal precision validator data annotation.
/// </summary>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)]
public sealed class DecimalPrecisionAttribute : ValidationAttribute
{
  private readonly uint _decimalPrecision;

  public DecimalPrecisionAttribute(uint decimalPrecision)
  {
    _decimalPrecision = decimalPrecision;
  }

  public override bool IsValid(object value)
  {
    return value is null || (value is decimal d && HasPrecision(d, _decimalPrecision));
  }

  private static bool HasPrecision(decimal value, uint precision)
  {
    string valueStr = value.ToString(CultureInfo.InvariantCulture);
    int indexOfDot = valueStr.IndexOf('.');
    if (indexOfDot == -1)
    {
      return true;
    }

    return valueStr.Length - indexOfDot - 1 <= precision;
  }
}

Usage:

[Required(ErrorMessage = "Price is required")]
[DecimalPrecision(2)]
[DisplayName("Price ($)")]
public decimal Price { get; set; }

Upvotes: 4

jlew
jlew

Reputation: 10591

You could use the RegularExpression attribute, with a regex that matches your criteria. There are a whole bunch of expressions here that involve numbers, I'm sure one will fit the bill. Here is the link.

This will get you started, though it may not be as inclusive as you want (requires at least one digit leading the decimal point):

[RegularExpression(@"\d+(\.\d{1,2})?", ErrorMessage = "Invalid price")]

Note that it is difficult to emit a precise error message because you don't know which part of the regex failed to match (the string "z.22" has the correct number of decimal places, for example, but is not a valid price).

Upvotes: 35

user13563226
user13563226

Reputation:

Similar to mattytommo. You need to escape '.' - otherwise ANY character will be accepted

[RegularExpression(@"^\d+(\.\d{1,2})?$")]

Upvotes: 3

Jonathan
Jonathan

Reputation: 633

[RegularExpression(@"^\d+.\d{0,2}$",ErrorMessage = "Price can't have more than 2 decimal places")]
public decimal Price { get; set; }

This will cater for 0 to 2 decimal places, or none at all.

Upvotes: 18

Mathew Thompson
Mathew Thompson

Reputation: 56429

I had the same scenario as the OP, yet the answers provided don't give a solution that works for all of the following cases:

12, 12.3 and 12.34

To do that, we use the following regular expression:

[RegularExpression(@"^\d+(.\d{1,2})?$")]

Upvotes: 3

domenu
domenu

Reputation: 455

You can also create your own Decimal validation attribute, inheriting from RegularExpressionAttribute:

 public class DecimalAttribute : RegularExpressionAttribute
 {
    public int DecimalPlaces { get; set; }
    public DecimalAttribute(int decimalPlaces)
        : base(string.Format(@"^\d*\.?\d{{0,{0}}}$", decimalPlaces))
    {
        DecimalPlaces = decimalPlaces;
    }

    public override string FormatErrorMessage(string name)
    {
        return string.Format("This number can have maximum {0} decimal places", DecimalPlaces);
    }
 }

and register it to enable client-side validation in Application_Start():

DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(DecimalAttribute), typeof(RegularExpressionAttributeAdapter));

Upvotes: 6

Gurarpan
Gurarpan

Reputation: 61

[RegularExpression(@"^\d+(\.\d)?$", ErrorMessage = "It cannot have more than one decimal point value")]
[Range( 0.1,100)]
public double xyz{get;set;}         

It works for me upto one decimal value

Upvotes: 4

Ion Sapoval
Ion Sapoval

Reputation: 635

You can make this validation by using a regular expression and apply it with RegularExpression attribute.

Upvotes: 2

Related Questions