Category Archives: Business Rules

CSLA-Validation–the new NuGet package in CSLA .NET 4.5.40

Or how to use the Csla 3.6 to 3.8 style rules in Csla 4.5.40

Introduction

Many developers find the move from Csla .NET 3.x to Csla .NET 4.x to be a big hurdle. Especially when it comes to the new rule engine in Csla .NET 4.x. In this article I will take the ProjectTracker.Library project from the Csla .NET 3.6 samples and upgrade to .NET 4.5 with Csla 4.5.40 NuGet packages.

The 3.x rule engine used static methods and the new 4.x rule engine uses classes. The benefit of using classes is inheritance, testability and allow the framework to provide base classes that get to the inner properties and methods of the business objects. The new rule engine also provides the same base classes and signature for both sync and async rules.

Having done several migrations with different approach to how to do the migration (starting with having the “old” Csla in a private renamed assembly and namespace) I finally came up with the idea of how to support most of the older style rules within the new rule engine. As it turns out this was quite simple to do and only required 1 to 3 days of work to get the application up and running on the new version and then the entire team could start to work on migration of the rules.

In order to do a full migration there is several breaking changes that you must handle. These will be covered in a separate blog posts, so for now let us focus on getting the business library with old style rules to work. And keep in mind – your target platform may be . NET 4, .NET 4.5, Silverlight 5, Windows Store or MonoAndroid.

Important breaking changes for rules:

  • There can be only one Authorization rule per AuthorizationAction and IMethodInfo (or property) where the old rule engine allowed multiple rules and both Allow/Deny combinations.
  • AddAuthorizationRules() is now deprecated and these authorization rules should be moved to AddBusinesRules().
  • Rewrite authorization rules to use the new IsInRole() or IsNotInRole() rules.
  • Rewrite asynchronous rules to use the new rule classes.

The changes the we must do is:

  1. Remove references to old Csla assembly and add Csla references from NuGet
  2. Add intermediate base classes if not already present in solution
  3. Add usings to business objects
  4. Change the signature of CanReadProperty, CanWriteProperty and CanExecuteMethod
  5. pdate AuthorizationRules to new IsInRole/IsNotInRole rules
  6. Add usings to business objects
  7. Change the signature of CanReadProperty, CanWriteProperty and CanExecuteMethod pdate AuthorizationRules to new IsInRole/IsNotInRole rules

Step 1: Remove references to old Csla assembly and add Csla references from NuGet

Remove the references to Csla.dll and update target framework for this sample to .NET 4.5.

Then install nuget package csla-updateValitation in ProjectTracker.Library

See: https://www.nuget.org/packages/CSLA-UpdateValidation

For my own preference I changed the solution so that only

  • ProjectTracker.DalEf
  • ProjectTracker.DalLinq
  • ProjectTracker.Library

will be compiled.

Step 2: Add intermediate base classes for BusinessBase and ReadOnlyBase

The first step you must do is to add your own intermediate base classes for Csla business objects, if not already present in you solution. Make sure that these classes inherit from Csla.Validation base classes for BusinessBase and ReadOnlyBase. When your app is fully migrated to Csla 4.x you should change the inheritance to use the standard Csla base classes and remove the reference to Csla.Validation.

[Serializable()]
public class MyBusinessBase<T> : Csla.Validation.BusinessBase<T> where T:MyBusinessBase<T>
  {
    protected override void AddBusinessRules()
    {
      AddAuthorizationRules();
      base.AddBusinessRules();
    }

    [Obsolete("Move code to AddBusinessRules for CSLA 4.x")]
    protected virtual void AddAuthorizationRules()
    {
    }
  }
[Serializable()]
public class MyReadOnlyBase<T> : Csla.Validation.ReadOnlyBase<T> where T:MyReadOnlyBase<T>
  {
    protected override void AddBusinessRules()
    {
      AddAuthorizationRules();
      base.AddBusinessRules();
    }

    [Obsolete("Move code to AddBusinessRules for CSLA 4.x")]
    protected virtual void AddAuthorizationRules()
    {
    }
  }

And make sure that all you business objects and readonly objects inherit from these base classes.

Also take not of how you can add support for AddAuthorizationRules in your own intermediate base class.

Step 3 Add usings to business objects

You should now take the opportunity to add these usings to business objects

using Csla.Validation;
using Csla.Rules;

Step 4: Change the signature of CanReadProperty, CanWriteProperty and CanExecuteMethod

to use the overload that accepts a PropertyInfo or MethodInfo as parameter

Here shown for CanExecuteMethod()

From:

public Resource GetResource()
    {
      CanExecuteMethod("GetResource", true);
      return Resource.GetResource(GetProperty(ResourceIdProperty));
    }

To:

private static MethodInfo GetResourceMethod = RegisterMethod(p => p.GetResource());
    public Resource GetResource()
    {
      CanExecuteMethod(GetResourceMethod, true);
      return Resource.GetResource(GetProperty(ResourceIdProperty));
    }

and take note of how you can get rid of the string constant.

Step 5: Update AuthorizationRules to new IsInRole/IsNotInRole rules

and keep in mind that you can only have one rule per AuthorizationAction and methodinfo.

From:

protected static void AddObjectAuthorizationRules()
    {
      AuthorizationRules.AllowCreate(typeof(Project), "ProjectManager");
      AuthorizationRules.AllowEdit(typeof(Project), "ProjectManager");
      AuthorizationRules.AllowDelete(typeof(Project), "ProjectManager");
      AuthorizationRules.AllowDelete(typeof(Project), "Administrator");
    }

To:

protected static void AddObjectAuthorizationRules()
    {
      BusinessRules.AddRule(typeof(Project), new IsInRole(AuthorizationActions.CreateObject, "ProjectManager"));
      BusinessRules.AddRule(typeof(Project), new IsInRole(AuthorizationActions.EditObject, "ProjectManager"));
      BusinessRules.AddRule(typeof(Project), new IsInRole(AuthorizationActions.CreateObject,"ProjectManager", "Administrator"));
    }

and take note of how we can supply multiple rows on the last rule.

Then do the same updates for Resources.cs

Step 6: Update async rules to use new style classes

See this blog post by Rocky Lhotka on how to write an async business rule

http://www.lhotka.net/weblog/ImplementingAnAsyncRuleInCSLA4Version45.aspx

Step 7: Misc changes for Csla 4.5

Next there is a couple of changes that must be done.

CriteriaBase now has a generic constraint so code must be updated to:

    [Serializable()]
    private class ExistsCommand : CommandBase<ExistsCommand>
And the Windows Forms (BindingList) based classes has been renamed to BusinessBindingListBase and ReadOnlyBindingListBase.
The Csla 4.5 list classes now inherit from ObservableCollection and has slightly different signature on AddNew and events. 
So change from:
BusinssListBase  ==> BusinessBindingListBase

ReadOnlyListBase ==> ReadOnlyBindingListBase

Summary

So this showed how to make the old ProjectTracker.Library compile with Csla .NET 4.5 in .NET 4.5 and use (most) the old style rules. The tecnhique behind this is a set of extension methods that provide the “old” methods and wraps the static methods with a lamda rules. If you want to look at the actual code, the source code can be downloaded from github: https://github.com/MarimerLLC/csla

There is still a number of changes to be done in order to make the entire solution run and this will be a subject for more blog posts.

Hope this encourages you to start migration of your application to Csla .NET 4.5

Advertisements

CSLA 4.5 RuleEngine update

The rule engine for CSLA 4.5.10 has been updated with a new advanced mode.

By default BusinessRules is run as follows when a property is edited by a user:

  1. Call CheckRulesForPropertymethod with parameters Property and Cascade=true
    1. Clear all BrukenRules from the Property
    1. Get list of BusinessRules for Property
    2. Call RunRuleswith parameters RulesForProperty and Cascade=true to run rules for this property.
      1. ForEach rule in RulesForProperty
        1. Call Rule.Execute method
      2. Update property values from context.OuputProperties collection
        1. if value was changed add Property to DirtyProperties
      3. Return RuleResult –  a list of AffectedProperties and lst of DirtyProperties
    3. If  (cascade)
      1. For the aggreate of (context.AffectedProperties from the rules checked) and
        (properties that have BusinessRules with PrimaryProperty as InputProperty)
        take distinct PropertyInfo

        1. call CheckRulesForProperty with parameters Property and Cascade=false.

But then the rule engine stops is rechecking. So the rule engine will only cascade down to the next level and then stop rechecking rules.
The consequence of having properties changed by f.ex. OutputProperties in the second run when Cacade=false is that the Property and RuleResults may get out of sync and a field that has a valid value may be invalid and vice versa.

So this may be a serious problem unless you have taken care of this in designing you business rules. In some financial applications you may also have a lot of calculations that should cascade for all “dirty” properties to rerun rule for as long as properties is changed – much like Excel does in a spreadsheet for calculating formulas  and cascade calculation for as long as cells is updated.

So to provide this level of functionality the RuleEngine for CSLA 4.5.10 has been updated with a new configuration flag:
<bo>.BusinessRules.CascadeOnDirtyProperties.
This is an instance level flag that must be set in the Create/Fetch method of the <bo>.

When CascadeOnDirtyProperties is true the rule engine run rules as follows:

  1. Call CheckRulesForPropertymethod with parameters Property and Cascade=true
    1. Clear all BrukenRules from the Property
    1. Get list of BusinessRules for Property
    2. Call RunRuleswith parameters RulesForProperty and Cascade=true to run rules for this property.
      1. ForEach rule in RulesForProperty
        1. Call Rule.Execute method
      2. Update property values from context.OuputProperties collection
        1. if value was changed add Property to DirtyProperties
      3. Return RuleResult –  a list of AffectedProperties and lst of DirtyProperties
    3. If (cascade)
      1. For the aggreate of (context.AffectedProperties from the rules checked) and
        (properties that have BusinessRules with PrimaryProperty as InputProperty)
        take distinct PropertyInfo

        1. call CheckRulesForProperty with parameters Property and Cascade=true if property is in DirtyProperties list .

Of course – unless your BusinessRules is carefully crafted you may end up with an infinite loop and eventually a StackOverflow exception.
But if you really need to make sure that BusinessRules is actually rerun for as long as property values is updated from context.OutputPropertyValues then this will be a nice extension to the rule engine.

The above sequence diagrams is simplified when it comes to asynchronous rules but the end result is the same behavior for both synchronous and asynchronous rules.

Unit Test CSLA 4 BusinessRules

Unit tests in CSLA 4 is now classes and most developers want to create unit tests to verify the correct behavior and result.
In this article I will show you a suggested way to create these tests.

Basic rule knowledge

Let’s start with how rules is registered in CSLA:

  1. <bo>.AddBusinessRules is only called once and register ALL rules for that object type.
  2. A rule instance should be viewed as a Singelton or Shared object. You cannot store any value elated to the execution (data values) in member variables.

The execution of the rule follow these steps:

  1. The RuleEngine will create an instance of a RuleContext with the actual rule
  2. and if InputProperties have entries copy values from BO to <context>.InputPropertyValues
  3. set <context>.Target property to the <bo> instance (with exception if IsAsync = true and ProvideTargetWhenAsync = false).
  4. call the <rule>.Execute method with <context> as parameter.
  5. When rule has completed call a CompleteHandler for both synchronous and async rules.

The rule engine will call the completed handler automatically for syncronous rules but for async rules your code is responsible for calling <context>.Complete in ALL execution paths.
The <rule>.Execute method is always called inline and when IsAsync = true the assumption is that your code in Execute will do an asyncronous call an in order to notify the rule engine when the rule is completed your code must call <rule>.Complete.

Then take a look at the <rule>.Execute method.

A: Your rule does NOT use <context>.Target and has no relationship to the actual BO.
These rules only interact with the RuleContext and use <context>.InputPropertyValues for input values and may call <context>.AddOutValue or context.AddXYZResult.

The output from these rules is in

  • <context>.Results
  • <context>.OuputPropertyValues

B: Your uses uses <context>.Target and the support methods in BusinessRule base class for ReadProperty or LoadProperty.
These rules interact with the BO and uses methods in BusinessRule base class that expects an actual CSLA BO to be present (implements Csla.Core.IManageProperties).

The output of a rule can be

The output from these rules is in

  • <context>.Results
  • <context>.OuputPropertyValues
  • directly updated properties in the context.Target or child object from calling <rule>.LoadProperty

RuleContext support for result

context.Results is a list of RuleResult that yon can add test for. <context>.AddXYZResult will all add a RuleResult to context.Results.
context.OutputPropertyValues is a dictionary of IPropertyInfo and values to be set in the bo.

So we should be able to create unit tests for both

  • synchreonous rules and async rules
  • rules that only interacts with the rule context.
  • rules that interacts with the context.Target

Support methods in CSLA

RuleContext has a constructor specifically designed for use in unit tests:

public RuleContext(Action<RuleContext> completeHandler, IBusinessRule rule, object target, Dictionary<Csla.Core.IPropertyInfo, object> inputPropertyValues)

that allows the test code to specify the

  • completeHandler for callback when rule has completed.
  • the actual business ruleyou want to test
  • the target object (may be null if your rule does not use the context.Target)
  • a dictionary of input property values

Support class BusinessRuleTest.cs in RuleTutorial samples download

adds more support for tests and will allow you to write the exact same test code whether the rule is syncronous or not.
This is done by adding a CompleteEeventHandler that will wait for up to 3 seconds on an async rule before calling <context>.Complete.

public void InitializeTest(IBusinessRule rule, object target)
public void ExecuteRule()
public void ExecuteRule(Dictionary<IPropertyInfo, object> inputPropertyValues)
public T GetOutputPropertyValue<T>(PropertyInfo<T> propertyInfo)

The ExeuteRule method will copy values from target object to input property values and then call rule.Execute
The ExecuteRule with InputPropertyValues dictionary will just set thes into the context and call rule.Execute.

Then to the unit tests

My proposed solution for creating unit tests uses the constructor in RuleContext, the BusinessRuleTest base class and creating fake business objects for the tests.

I like the testing approach from Phil Haack and Brad Wilson so these sample tests use a separate class for each method.
Read more here: http://haacked.com/archive/2012/01/01/structuring-unit-tests.aspx

Testing a ToUpper transformation rule that makes sure a property is always uppercase.

This rule only interacts with the <context>.InputPropertyValues and <context>.OuputPropertyValues

My fake root object:

  [Serializable]
  public class RootFake : BusinessBase<RootFake>
  {
    public static readonly PropertyInfo<int> CustomerIdProperty = RegisterProperty<int>(c => c.CustomerId);
    public int CustomerId
    {
      get { return GetProperty(CustomerIdProperty); }
      set { SetProperty(CustomerIdProperty, value); }
    }

    public static readonly PropertyInfo<string> NameProperty = RegisterProperty<string>(c => c.Name);
    public string Name
    {
      get { return GetProperty(NameProperty); }
      set { SetProperty(NameProperty, value); }
    }
  }

The the test class for constructor:

    [TestClass()]
    public class TheCtor
    {
      private IBusinessRule Rule;
      [TestInitialize]
      public void InitTests()
      {
        Rule = new ToUpper(RootFake.NameProperty);
      }

      [TestMethod]
      public void IsSync()
      {
        Assert.IsFalse(Rule.IsAsync);
      }

      [TestMethod]
      public void HasPrimaryProperty()
      {
        Assert.IsNotNull(Rule.PrimaryProperty);
        Assert.AreEqual(RootFake.NameProperty, Rule.PrimaryProperty);
      }

      [TestMethod]
      public void HasInputProperties()
      {
        Assert.IsTrue(Rule.InputProperties.Contains(RootFake.NameProperty));
      }

      [TestMethod]
      public void HasAffectedProperties()
      {
        Assert.IsTrue(Rule.AffectedProperties.Contains(RootFake.NameProperty));
      }
    }

Then testing the Execute method with supplied InputPropertyValues:

    [TestClass()]
    public class TheExecuteMethod : BusinessRuleTest
    {
      [TestInitialize]
      public void InitTests()
      {
        var rule = new ToUpper(RootFake.NameProperty);
        InitializeTest(rule, null);
      }

      [TestMethod]
      public void MustSetOutputPropertyToUpper()
      {
        var expected = "CSLA ROCKS";
        ExecuteRule(new Dictionary<IPropertyInfo, object>() {{RootFake.NameProperty, "csla rocks"}});
        Assert.IsTrue(GetOutputPropertyValue(RootFake.NameProperty) == expected);
      }
    }

Then testing the Execute method with a fake object to supply the property value:

    [TestClass()]
    public class TheExecuteMethodAlt : BusinessRuleTest
    {
      [TestInitialize]
      public void InitTests()
      {
        var rule = new ToUpper(RootFake.NameProperty);
        var root = new RootFake();
        root.Name = "csla rocks";
        InitializeTest(rule, root);
      }

      [TestMethod]
      public void MustSetOutputPropertyToUpper()
      {
        var expected = "CSLA ROCKS";
        ExecuteRule();
        Assert.IsTrue(GetOutputPropertyValue(RootFake.NameProperty) == expected);
      }
    }

Summary

I hope this gave you a better understanding of how Business Rules work and what to test.

You will find more unit test samples in the Net\cs\RuleTutorial sample available at http://www.lhotka.net/cslanet/download.aspx and if you have questions please post on the CSLA,NET Forum at http://forums.lhotka.net