One reason the validation rules are associated with properties in AddBusinessRules() - as opposed to using attributes or some other technique - is to allow programmatic association of rules with properties.
Remember that AddRule() takes two parameters: a delegate reference and a property name.
Using a bit of reflection, you can easily create a delegate reference to a method based on the full type and method name in string form.
In other words, if you are able to get metadata from some source (like your database) that specifies the full type path to a rule method (so the type name and method name), along with the property the rule applies to, you can dynamically call AddRule() based on that metadata.
CSLA3 includes a new capability to help with this: the DecoratedRuleArgs class. This is a subclass of RuleArgs that accepts a Dictionary of name/value pairs that are passed to the rule method - following the Decorator design pattern. If your rule method uses DecoratedRuleArgs to get its extra parameters, then you have a much simpler time storing the name/value data for a rule method as metadata as well - and that completes the whole picture.
So your metadata is:
Given that metadata, you can write code in AddBusinessRules() to dynamically associate rules with methods.
We are doing this for one of our objects where we have properties that are required, recommended, and visible for specific statuses. We use table based code generation to create our objects. To be able to do this in our generated classes, we created sql server extended properties for each of these behaviors. Our template in turn reads the extended property and creates them as attributes on our objects. Then our object iterates through the attributes and dynamically adds/removes items in the validationrules collection. The EVILAttributes project helped us with the attribute rules. As the user changes statuses, the object notifies the UI of these behaviors and updates the controls. It seems to work good. We had to make one CSLA.NET change that I can remember (I didn't do all of the implementation) for this to work. We had to make the add/remove methods of the validationrules public. If this sounds like what you are wanting to do, I can post some code.
Mike,
I took a quick look at the EViL attributes project just now.
I would like to see how you make it work with CSLA validation rules.
Please post some code samples. Thanks.
Joe
Here’s a small example of the different pieces. I can’t include the full class because my work doesn’t like me to post that much code. This should show you the main parts. The only change to CSLA.NET that I can remember is that we had to make the ValidationRules public so we could call the AddRule and DeleteRule from outside the business object.
In the Extended Property of the FullName column:
Name = IsRequiredForStatus
Value = Codes.Status.Complete
Property is generated like this:
<IsRequiredForStatus(Codes.Status.Complete)> _
Public Overridable Property FullName() As String
In the “set” of the nogen Status property (which is of type Codes.Status enumeration), it calls the function to check the attributes. This calls the EVIL attributes to process the rules:
RunValidationAttributes()
In the Validator, it uses reflection to get all of the properties of the object. It loops through each of the properties and checks to see if any of the attributes are of type VBEvilBaseAttribute. If so then it calls the ProcessRule of that attribute class such as “IsRequiredForStats”.
Public Class IsRequiredForStatus
Inherits VBEvilBaseAttribute
Public Overrides Function ProcessRule(ByVal pi As PropertyInfo,
ByVal entity As Object) As Boolean
If the property’s attribute enumeration value matches the enumeration of the Status property then it’s required. The logic below is done for each datatype.
If IsRequired Then
If pi.PropertyType Is GetType(String) Then
Dim args As New Csla2.Validation.RuleArgs(pi.Name)
args.Description = pi.Name & " is required for this status."
args.Severity = Csla2.Validation.RuleSeverity.FailsCustomRules
busObject.ValidationRules.AddRule(AddressOf
Csla2.Validation.CommonRules.StringRequired, args)
Else
If pi.PropertyType Is GetType(String) Then
busObject.ValidationRules.DeleteRule(pi.Name, "StringRequired")
If you have any questions, comments, or better ideas, please let me know. I always like to hear ideas for using CSLA.NET. I would like to thank Dave Cottle. He is the one that figured out a lot details to make this work.
I also blogged this same info here: http://codesmartnothard.com/PermaLink,guid,ea138e37-fb83-47b3-bbf8-b0ee5c27044f.aspx
Mike
CodeSmartNotHard:We had to make one CSLA.NET change that I can remember (I didn't do all of the implementation) for this to work. We had to make the add/remove methods of the validationrules public. If this sounds like what you are wanting to do, I can post some code.
It would be interesting to see.
Thanks,
Copyright (c) Marimer LLC