Conditional Rule CSLA 4

Conditional Rule CSLA 4

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


xAvailx posted on Monday, October 18, 2010

CSLA 4

I am trying to create a condtional rule and having trouble trying to replicate behavior from previous Csla versions.

So say I have some properties that are only required based on the state / flag in an object and I would like to reuse the existing RequiredRule.

In previous versions of Csla I would do something like this (sorry, old vb code that I could find..):

Private Shared Function DetailDataRequiredRule(Of T As MyObject)(ByVal target As T, ByVal e As Validation.RuleArgs) As Boolean
            Dim isRuleValid = True

            If (target.RequireDetailProductData) Then
                isRuleValid = CommonRules.StringRequired(target, e)
            End If

            Return isRuleValid
        End Function

And then I can add ValidationRules.AddRule with my custom rule for only the properties that need to be validated when the condition is met.

So...if you are still with me. My problem is trying to execute the existing ValidationRule.

Thanks for pointers.

 

emadalsous replied on Monday, October 18, 2010

Hello, if i get what you say , i think that you have to Define new Class for every custom rule  inherit BusinessRule in

CSLA 4 so in your case this is what i can help in C# :

 private class SomeRule: Csla.Rules.BusinessRule
        {
            protected override void Execute(Csla.Rules.RuleContext context)
            {
                var target = (TheClassIncludeTheProperites)context.Target;
  If (target.RequireDetailProductData)

if(string.IsNullOrEmpty(PrimaryProperty);

context.AddErrorResult("Required Field");

}         

   }
        }

and you add it to the rules in this way

 BusinessRules.AddRule(new SomeRule{ PrimaryProperty = PropertyToValidate, AffectedProperties = { PropertyToBeAffected } });
           

ajj3085 replied on Monday, October 18, 2010

I haven't tried this, but I would probably subclass the StringRequired rule, and the in the override of Execute selectively call the base implementation.

xAvailx replied on Tuesday, October 19, 2010

Perfect! That did the trick. Here is my code for reference:

private class AdditionalDataRequired : Csla.Rules.CommonRules.Required {
            public AdditionalDataRequired(IPropertyInfo primaryProperty) : base(primaryProperty) { }

            protected override void Execute(Csla.Rules.RuleContext context) {
                var target = (MyObject)context.Target;

                if (target.IsAdditionalDataRequired) {
                    base.Execute(context);
                }
            }
        }

 

BusinessRules.AddRule(new AdditionalDataRequired (MyProperty1));
BusinessRules.AddRule(new AdditionalDataRequired (MyProperty2));

BusinessRules.AddRule(new Dependency(PropertyThatDeterminesIfAddtionalDataIsRequired, MyProperty1));
BusinessRules.AddRule(new Dependency(PropertyThatDeterminesIfAddtionalDataIsRequired, MyProperty2));

JonnyBee replied on Tuesday, October 19, 2010

There is 2 different approaches to this:

1. Chained Rule:
     Your rule has a StringRequired inner rule and you test for whether the inner rule should run or not.

2. Use Priority to control sequence and create a "flow control rule" that will ShortCircuit the rule if condition is not met to run first.

I like the concept of FlowControlRules whenever applicable because they can be combined with any other rul and it is easy to get into trouble with RuleChaining if you do not fully understand the underlying rules in terms of Async/Sync capabilities, InputProperties and does the underlying rule require the Target object.

Jonny

Code samples:

1. Chained Rule

''' <summary>
''' This class demonstrates how to utilize inner rules inside a business rule. 
''' 
''' This rule calls inner rule StringRequired on PrimaryProperty if countryCode is US
''' </summary>
Public Class StringRequiredIfUS
	Inherits Csla.Rules.BusinessRule
	Private _countryProperty As IPropertyInfo
	Private _innerRule As IBusinessRule
	' TODO: Add additional parameters to your rule to the constructor
	Public Sub New(primaryProperty As IPropertyInfo, countryProperty As IPropertyInfo)
 		MyBase.New(primaryProperty)
		_countryProperty = countryProperty
		_innerRule = DirectCast(New Csla.Rules.CommonRules.Required(primaryProperty), IBusinessRule)
		InputProperties = New List(Of IPropertyInfo)()

		' this rule needs the Country property
		InputProperties.Add(countryProperty)

		' add input properties required by inner rules
		Dim inputProps = _innerRule.InputProperties.Where(Function(inputProp) Not InputProperties.Contains(inputProp))
		If inputProps.Count() > 0 Then
			InputProperties.AddRange(inputProps)
		End If
	End Sub

	Protected Overrides Sub Execute(context As RuleContext)
		' TODO: Add actual rule code here. 
		Dim country = DirectCast(context.InputPropertyValues(_countryProperty), String)
		If country = CountryNVL.UnitedStates Then
			_innerRule.Execute(context.GetChainedContext(_innerRule))
		End If
	End Sub
End Class

2. "Flow control rule"
(this rule checks if user is allowed to edit a field)

Public Class StopIfNotCanWrite
Inherits BusinessRule Public Sub New([property] As IPropertyInfo) MyBase.New([property]) End Sub ''' <summary> ''' Rule indicating whether the user is authorized ''' to read the property value. ''' Will always be silent and never set rule to broken. ''' </summary> ''' <param name="context">Rule context object.</param> ''' <remarks> ''' Combine this Rule with short-circuiting to ''' prevent evaluation of other rules in the case ''' that the user isn't allowed to read the value. ''' </remarks> Protected Overrides Sub Execute(context As RuleContext) Dim isAuthorized = True Dim business = TryCast(context.Target, BusinessBase) If business IsNot Nothing Then isAuthorized = business.CanWriteProperty(context.Rule.PrimaryProperty) End If If Not isAuthorized Then    ' ShortCircuit rule   
                        context.AddInformationResult([String].Empty, True) End If End Sub End Class

And in AddBusinessRules method

      ' Rules with Priority=-1 will always be executed before default Priority 0
      BusinessRules.AddRule(new StopIfNotCanWrite(OtherAddress1Property) { Priority = -1 }); 
      BusinessRules.AddRule(new Required(OtherAddress1Property));

xAvailx replied on Tuesday, October 19, 2010

Thanks for the detail reply Jonny. See my reply to Andy for how I ended up handling this scenario.

Copyright (c) Marimer LLC