Csla 3.6.1 - SmartDate ValidationRule

Csla 3.6.1 - SmartDate ValidationRule

Old forum URL: forums.lhotka.net/forums/t/8412.aspx


xAvailx posted on Tuesday, January 26, 2010

Hi, I am trying (for the first time) to use smart date. I am trying to use the MaxRule common rule. Specifically, I am trying to validate my property is not greater than say today. However I am getting a type conversion error. This the code I have so far, hopefully someone can help me spot the problem...

private static PropertyInfo<SmartDate> AMaxDateProperty = RegisterProperty<SmartDate>(c => c.AMaxDate);
public string AMaxDate {
get { return GetPropertyConvert<SmartDate, string>(AMaxDateProperty); }
set { SetPropertyConvert<SmartDate, string>(AMaxDateProperty, value); }
}


#endregion

#region Validation Rules

protected override void AddBusinessRules() {
ValidationRules.AddRule(CommonRules.MaxValue<SmartDate>, new CommonRules.MaxValueRuleArgs<SmartDate>(AMaxDateProperty, DateTime.Now));
}
When I step through the code it breaks on this line: T value = (T)pi.GetValue(target, null); T is SmartDate. pi is {System.String AMaxDate}. I receive a specified cast is not valid exception.

rxelizondo replied on Tuesday, January 26, 2010


Its kind of hard to follow the code if its not formatted correctly. For example, I think you are missing all the stuff between the lower and greater symbols (the generics info).

I recommend looking at the following post:

http://forums.lhotka.net/forums/thread/39018.aspx

Go to the last post and follow the instructions and repost the code.

xAvailx replied on Wednesday, January 27, 2010

that was incredibly painful (formatting) ...but I think I got it now. Thanks

JonnyBee replied on Wednesday, January 27, 2010

Hi,

This is a case where the csla generic rules really do not work straight out of the box as there is no default TypeConverter between SmartDate and object and so you get a "specified cast not valid" exception.

The simplest solution is to create a custom validation rule:
public static bool MyMaxValue<T>(object target, RuleArgs e) where T : IComparable
{
  var args = (DecoratedRuleArgs)e;
  var fi = MethodCaller.CallMethodIfImplemented(target, "GetPropertyInfo", args.PropertyName);
  var pi = target.GetType().GetProperty(e.PropertyName);

  T max = (T)args["MaxValue"];
  T value;

  if (fi != null)
 {
    value = (T)MethodCaller.CallMethod(target, "ReadProperty", fi);
  }
  else
  {
    var tmpval = pi.GetValue(target, null);
    value = Csla.Utilities.CoerceValue<T>(typeof(T), null, tmpval);
  }

  var result = value.CompareTo(max);
 
if (result < 0)
 
{
  
  e.Description = string.Format("{0} must be less than {1}", e.PropertyFriendlyName, max);
  
  return false;
 
}
 
return true;
}

 
Usage:
ValidationRules.AddRule(MyCommonRules.MyMaxValue<SmartDate>, newCommonRules.MaxValueRuleArgs<SmartDate>(FoundedProperty, DateTime.Now));

The custom rule is generic and can accept most value types so long as they implement IComparable.  Property here may be f.ex. a public string property and a internal SmartDate property.


xAvailx replied on Wednesday, January 27, 2010

Thanks, I will give it a shot.

xAvailx replied on Wednesday, January 27, 2010

That didn't work either...but I know see what the problem is. The rule is getting called from the DataPortal_Create. At that point my date doesn't have a value, it is an empty string. So when this line runs, it throws the exception since "tmpval" is an empty string.

value = Csla.Utilities.CoerceValue(typeof(T), null, tmpval);

So I think I will need a custom rule that checks if the value is empty since in this case, my date is nullable and is the reason I am using SmartDate. Thanks for the help, it made it easier to find out what the problem was!

xAvailx replied on Wednesday, January 27, 2010

I made some changes, and I am still receiving an error on that line, even if tmpval is a date ("11/11/2001"). Does CoerceValue not work with SmartDate?

rxelizondo replied on Wednesday, January 27, 2010

First, lets understand the problem.

Here is the thing, "pi.GetValue(target, null)" is returning a String (casted as an *Object*). An although there is and explicit conversion from String to SmartDate, there is no explicit or implicit conversion from Object to SmartDate.

Although the object reference is really a string and you would think that the casting should work, operator overloading is not polymorphic so you are out of luck.

So that is why its crashing

xAvailx replied on Wednesday, January 27, 2010

I am not sure that is the behavior I am seeing. When I inspect:
var tmpval = pi.GetValue(target, null);
It is a string.
?tmpval.GetType()
{Name = "String" FullName = "System.String"}
    [System.RuntimeType]: {Name = "String" FullName = "System.String"}
From the code you posted, I changed the following line, and it works now:
value = Csla.Utilities.CoerceValue(tmpval.GetType(), null, tmpval);
vs
value = Csla.Utilities.CoerceValue(typeof(T), null, tmpval);
Now, it is trying to Coerce a string (tmpval) to a SmartDate (T). I've added some unit tests and tried another datatype > DateTime, and it seems to be working correctly. I've also changed how the rule is registered to this:
ValidationRules.AddRule(BaseCommonRules.MaxValue<SmartDate>, new CommonRules.MaxValueRuleArgs<SmartDate>(ASmartDateProperty, new SmartDate("01/01/2050")), 1);
and for reference my property is declared like this:
private static PropertyInfo<SmartDate> ASmartDateProperty = RegisterProperty<SmartDate>(c => c.ASmartDate);
public string ASmartDate {
get { return GetPropertyConvert<SmartDate, string>(ASmartDateProperty); }
set { SetPropertyConvert<SmartDate, string>(ASmartDateProperty, value); }
}

Copyright (c) Marimer LLC