Validation Rule Question

Validation Rule Question

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


JoeFallon1 posted on Tuesday, May 29, 2007

When I code gen a base class I get a rule for password like this:

ValidationRules.AddRule(AddressOf StringMaxLength, New MaxLengthRuleArgs("Password", 256))

In my derived class I override AddBusinessRules and add additional rules. The question is what happens if I use the same rule as defined in the base class and change the maxLength?

Like this:

ValidationRules.AddRule(AddressOf StringMaxLength, New MaxLengthRuleArgs("Password", 128))

1. Will both rules exist and be called? (So the rule is broken if password is over 128 characters long).

2. Will only the Base rule exist? (256)

3. Will only the Derived rule exist? (128)

Joe

 

david.wendelken replied on Tuesday, May 29, 2007

Let us know when you test it and find out!

Curious minds want to know! :)

RockfordLhotka replied on Wednesday, May 30, 2007

I think both will be called, but the order of execution will be indeterminate unless you use priorities. And the order of execution matters, because both rules will resolve to the same rule name - which is the key used in BrokenRulesCollection to maintain the list.

So if the 128 runs first, the 256 rule would remove the broken entry. But if 256 runs first, the 128 rule could break and stay in the broken rules list.

If you use priorities, you could force the 128 rule to run at priority 1, so it would run after the 256 rule. If you do that, you'll probably want to change the stop processing threshold to 2 (it defaults to 1) to ensure that the 128 rule always runs.

JoeFallon1 replied on Wednesday, May 30, 2007

Rocky,

Makes sense. I plan to use Priority 1 rules for all rules that hit the DB.

I can set this rule the same way. My understanding of the processing is that all level 0 rules get run first and only if all of them pass do the level 1 rules get run. So I should not need to modify the stop processing threshold, correct?

Here is what the ebook said about it:

"In this case, I have explicitly set the ProcessThroughPriority to 0, which is the default.

This means that all priority 0 rule methods will be invoked, regardless of success or failure.

But rules at priority 1 or higher will only be invoked if no prior rule has returned Falsewith

a severity if Error."

david.wendelken replied on Wednesday, May 30, 2007

Is there a way to modify a rule after it has been assigned?
Because if there is, you could just change the rule in the base class to the correct setting.

RockfordLhotka replied on Wednesday, May 30, 2007

david.wendelken:
Is there a way to modify a rule after it has been assigned? Because if there is, you could just change the rule in the base class to the correct setting.

You can't modify the rules in CommonRules, nor can you ever modify a RuleArgs once it has been set up (except from within the rule method itself).

However, you could certainly create a rule method that relied on some external MaxLengthForPropertyX value, rather than a value provided through the RuleArgs - and then the value could change as often as you'd like.

david.wendelken replied on Wednesday, May 30, 2007

RockfordLhotka:

david.wendelken:
Is there a way to modify a rule after it has been assigned? Because if there is, you could just change the rule in the base class to the correct setting.

You can't modify the rules in CommonRules, nor can you ever modify a RuleArgs once it has been set up (except from within the rule method itself).

However, you could certainly create a rule method that relied on some external MaxLengthForPropertyX value, rather than a value provided through the RuleArgs - and then the value could change as often as you'd like.

That seems like a much cleaner and much more reliable way to do it.

RockfordLhotka replied on Monday, July 02, 2007

RockfordLhotka:

However, you could certainly create a rule method that relied on some external MaxLengthForPropertyX value, rather than a value provided through the RuleArgs - and then the value could change as often as you'd like.

I go back to this original suggestion, as it seems (by far) to be the simplest solution.

JoeFallon1 replied on Monday, July 02, 2007

Rocky,

I used your suggestion before for getting a list of Status values. I code gen one but if it needs to be changed I just override the function which is called by the rule.

So I did the same thing here:

In Gen:
    Protected Overrides Sub AddBusinessRules()
      'Id
      ValidationRules.AddRule(AddressOf StringMaxLength, New MaxLengthRuleArgs("Id", GetIdMaxLength))
    End Sub

    Public Overridable Function GetIdMaxLength() As Integer
      Return 30
    End Function

In final type:

    Public Overrides Function GetIdMaxLength() As Integer
      Return 40
    End Function

 

Joe

 

JoeFallon1 replied on Wednesday, June 13, 2007

RockfordLhotka:

I think both will be called, but the order of execution will be indeterminate unless you use priorities. And the order of execution matters, because both rules will resolve to the same rule name - which is the key used in BrokenRulesCollection to maintain the list.

So if the 128 runs first, the 256 rule would remove the broken entry. But if 256 runs first, the 128 rule could break and stay in the broken rules list.

If you use priorities, you could force the 128 rule to run at priority 1, so it would run after the 256 rule. If you do that, you'll probably want to change the stop processing threshold to 2 (it defaults to 1) to ensure that the 128 rule always runs.

 

Rocky,

I just ran into the reverse case and the setting of priority will not work.
e.g. Code gen rule is for 128 but the final type rule is for 256. So if the 128 rule breaks then 256 rule will never run if it is priority 1.

You stated: "the order of execution will be indeterminate ".

I agree with that statement when discussing normal Delegate functions. But in this case aren't you storing references to the delegates in a list which you loop over to execute them? So isn't the execution order in fact determined by when the rule is added to the list? If I know my code gened rule is added to the list first and then I write a looser rule in my final type (at the same priority level) then it will be added to the list after the code gened rule and should essentially "override" it. Do you agree with this or did I miss something?

Joe

 

 

RockfordLhotka replied on Wednesday, June 13, 2007

The order of execution within a priority is indeterminate. The items get sorted, and I don’t know how the sort algorithm works (it is Microsoft’s), so you can’t guarantee order of execution.

 

However, the answer  to your issue is to raise the stop processing threshold from 1 to 2 (or something >1 anyway). This is a static/Shared method on ValidationRules and can be set in AddBusinessRules().

 

Rocky

 

 

From: JoeFallon1 [mailto:cslanet@lhotka.net]
Sent: Wednesday, June 13, 2007 8:47 AM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] Validation Rule Question

 

RockfordLhotka:

I think both will be called, but the order of execution will be indeterminate unless you use priorities. And the order of execution matters, because both rules will resolve to the same rule name - which is the key used in BrokenRulesCollection to maintain the list.

So if the 128 runs first, the 256 rule would remove the broken entry. But if 256 runs first, the 128 rule could break and stay in the broken rules list.

If you use priorities, you could force the 128 rule to run at priority 1, so it would run after the 256 rule. If you do that, you'll probably want to change the stop processing threshold to 2 (it defaults to 1) to ensure that the 128 rule always runs.

 

Rocky,

I just ran into the reverse case and the setting of priority will not work.
e.g. Code gen rule is for 128 but the final type rule is for 256. So if the 128 rule breaks then 256 rule will never run if it is priority 1.

You stated: "the order of execution will be indeterminate ".

I agree with that statement when discussing normal Delegate functions. But in this case aren't you storing references to the delegates in a list which you loop over to execute them? So isn't the execution order in fact determined by when the rule is added to the list? If I know my code gened rule is added to the list first and then I write a looser rule in my final type (at the same priority level) then it will be added to the list after the code gened rule and should essentially "override" it. Do you agree with this or did I miss something?

Joe

 

 



JoeFallon1 replied on Thursday, June 14, 2007

Rocky,

I re-read the 2.1 doc on Rule Priorities and see what you mean now.

ValidationRules.ProcessThroughPriority = 1 will force all rules at level 0 and level 1 to be run even if there are broken rules in level 0. They normally stop at level 0 becasue that is the default value of this setting.

I also see where the sorting can make the rule execution order nondeterministic within a priority.

Thanks for clearing this up.

Joe

 

JoeFallon1 replied on Sunday, July 01, 2007

Rocky,

There is a problem with the strategy you mentioned above.

If my Gen code has this rule:

ValidationRules.AddRule(AddressOf StringMaxLength, New MaxLengthRuleArgs("Id", 30))

and my finale type has this code:

Protected Overrides Sub AddBusinessRules()
 
MyBase.AddBusinessRules()
  ValidationRules.ProcessThroughPriority = 1

 
'Id
 
ValidationRules.AddRule(AddressOf StringMaxLength, New MaxLengthRuleArgs("Id", 40), 1)
End Sub

The idea was that the rule would break when Id is > 30 but then when the priority 1 rule ran it would "unbreak the rule" and remove it from the Broken Rules collection.

The problem is in the line of code below form the Remove method:
  If Me(index).RuleName = rule.RuleName Then

Friend Overloads Sub Remove(ByVal rule As IRuleMethod)
 
IsReadOnly = False
 
For index As Integer = 0 To Count - 1
   
If Me(index).RuleName = rule.RuleName Then
     
DecrementCount(Me(index))
      RemoveAt(index)
     
Exit For
   
End If
 
Next
 
IsReadOnly = True
End Sub

The problem is this:

Me(index).RuleName = "rule://StringMaxLength/Id?maxLength=30"

rule.RuleName = "rule://StringMaxLength/Id?maxLength=40"

So the rule does not get unbroken because the names are different by the maxLength parameter.

I think I would prefer to not change the rule name itself so that I lose the extra information.
e.g. Do not change it to: "rule://StringMaxLength/Id"

Perhaps the Remove method should search the RuleName string for a ? and if it finds one then grab everything up to the ? and then compare it to the passed in value. This way the rule names are "the same" and we can ubreak the original rule.

Joe

 

Copyright (c) Marimer LLC