System.ComponentModel.DataAnnotations offers a RangeAttribute that can be used to validate that a value is within a given range.  Silverlight 3’s SDK includes this assembly and .NET RIA Services and ASP.NET Dynamic Data use this too.

Something that a few of us have stumbled on here internally is that the RangeAttribute doesn’t have a constructor that takes Decimal values.  In fact, a bug was submitted by another feature crew for this.  Unfortunately, even if RangeAttribute offered a constructor that accepted decimals, you still wouldn’t be able to pass them in.

Here’s what the code would look like:

   1: public class MyClass
   2: {
   3:     [Range(0.5m, 1.0m)]
   4:   public decimal MyValue { get; set; }
   5: }

But that results in compile errors in C#:

An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type.

And you get different compile errors in VB.NET:

Attribute constructor has a parameter of type 'Decimal', which is not an integral, floating-point or Enum type or one of Object, Char, String, Boolean, System.Type or 1-dimensional array of these types.

Decimal isn’t a primitive CLR type and attribute constructor parameters must be primitive CLR types representing constant values.  So we closed the bug as working as designed and RangeAttribute doesn’t have a constructor that accepts decimals.

The two workarounds that came to mind for this problem, but they both admittedly have their concessions.

  1. Use doubles.  Simply don’t suffix the value with “m” and it will be passed in as a double, and the RangeAttribute will be happy.
    • The concession here is that you lose some precision
    • I was able to get 0.49999999999999996 to pass itself off as within this range of 0.5 to 1.0
  2. Use strings.  You can supply the RangeAttribute with a Type and 2 strings, and those strings will be converted to the specified type.  So you could supply typeof(decimal) and “0.5” and “1.0” as your values.
    • This is subject to localization issues in Silverlight.  Even though you wrote the code in your language, the attribute value will be converted to decimal using the runtime culture.
    • There’s really no workaround for this and you can never guarantee that an end user won’t have their culture set to something you don’t expect.

Because of the localization issue with #2, I suggest using #1.  It looks cleaner too.  While it does run the risk of some precision loss, I would expect that the value is going to be rounded off before getting stored anyway.