Building Business Rules Module

Discussions related to writing and managing custom business rules.

Moderator: Phil Barton

Post Reply
roteague
Posts: 292
Joined: Thu Sep 25, 2008 4:49 pm
Location: Honolulu, Hawaii

Building Business Rules Module

Post by roteague » Tue Jun 29, 2010 3:35 pm

What process do you need to follow to create a custom business rules module (dll)? Is there a specific Interface to implement? Does Select use straight reflection to load the module, or use a technolgy such as MEF?

Thanks,
Robert

jbright

Re: Building Business Rules Module

Post by jbright » Tue Jun 29, 2010 3:58 pm

A business rule class should implement SoftPro.BusinessRules.Base.IBusinessRule. The easiest way to do this is to have your class inherit from SoftPro.BusinessRules.Base.BusinessRuleBase. By doing so, you only need to implement IBusinessRule.ExecuteImpl.

The discovery technique is much simpler than you have guessed. To make your rules discoverable, create a text file with the extension .rules containing a carriage return delimited list of your assembly names. You will probably just have one assembly. Place this file in your Select client install directory along with the assembly containing your rules.

There is a bit more to it and I don't think there is much documentation available, so you may have a difficult road to hoe. You will also need an xml file which specifies the inputs to your rules. This allows the engine to know what to pass into your rule class. When any of the inputs to your rule has its value change, your rule is triggered.

roteague
Posts: 292
Joined: Thu Sep 25, 2008 4:49 pm
Location: Honolulu, Hawaii

Re: Building Business Rules Module

Post by roteague » Thu Jul 01, 2010 1:19 pm

Thanks Jason, I'll let you know if I run into any issues.
Robert

roteague
Posts: 292
Joined: Thu Sep 25, 2008 4:49 pm
Location: Honolulu, Hawaii

Re: Building Business Rules Module

Post by roteague » Mon Oct 25, 2010 2:45 pm

How do I setup the XML file for the business rule?

In your XML file for example:

Code: Select all

  <Instance>
    <Name>IsDueToday</Name>
    <BusinessRule>ChecklistTaskIsDueToday</BusinessRule>
    <BusinessObjectContext>ChecklistTask</BusinessObjectContext>
    <InputList>
      <Input>
        <Order>0</Order>
        <Source>Status</Source>
      </Input>
      <Input>
        <Order>1</Order>
        <Source>DueDate</Source>
      </Input>
    </InputList>
  </Instance>
I assume there is one entry per input. The <Order> node refers to the order the parameter is passed into the function - not the Order object itself - and the <Source> is the class or property from the order that is passed in. Naturally, the <BusinessObjectContext> is the context from the order.

So, if I wanted for example to build a Business Rule module to validate the TMK of a property, I would setup the XML as follows:

Code: Select all

<Instance>
    <Name>IsValidTMK</Name>
    <BusinessRule>ValidateTMK</BusinessRule>
    <BusinessObjectContext>Property</BusinessObjectContext>
    <InputList>
      <Input>
        <Order>0</Order>
        <Source>TaxMap</Source>
      </Input>
    </InputList>
  </Instance>
Would <BusinessRule> then be the name of the class that implements BusinessRuleBase (IBusinessRule)?

In this example case, I would simply implement ExecuteImpl in such a way as I need to check the inputs.
Robert

jbright

Re: Building Business Rules Module

Post by jbright » Mon Oct 25, 2010 3:29 pm

There are a few different uses of business rules.

1. Default values define data items by specifying what their default value should be. I'm not sure how this would be of much value to a third-party developer like yourself.
2. Save validation define validation rules required to save. These rules can cause errors and/or warnings to appear in the "Errors and Warnings" windows. If it returns any errors, the save is aborted.
3. Field validation define validation rules applied at data entry time. When the data item is altered, this rule can return errors which are displayed in a message box to the user and their initial edit is reverted.

I think field validation is what you want to do, based on your chosen names.
roteague wrote:I assume there is one entry per input.
Ideally, your rule will refer to specifically to each individual piece of data it is dependent upon. The business engine is optimized such that it will only call your rule if any of the inputs to the rule have changed. If you specify an entire object as an input, rather than the particular property of that object as an input, the business engine will call your rule more often than it needs to.
roteague wrote:The <Order> node refers to the order the parameter is passed into the function - not the Order object itself
Correct. The <Order> nodes specify the ordinal position of that parameter among the list of parameters. It make the most sense to number them 0..n, without skipping any positions.
roteague wrote:and the <Source> is the class or property from the order that is passed in.
The <Source> nodes refer to data item names relative to the object named in the BusinessObjectContext node. It can also be a literal constant, such as an error message.
roteague wrote:Naturally, the <BusinessObjectContext> is the context from the order.
The <BusinessObjectContext> node refers to the data item name of the parent object of the data item name specified in <Name>.
roteague wrote:So, if I wanted for example to build a Business Rule module to validate the TMK of a property, I would setup the XML as follows:

Code: Select all

<Instance>
    <Name>IsValidTMK</Name>
    <BusinessRule>ValidateTMK</BusinessRule>
    <BusinessObjectContext>Property</BusinessObjectContext>
    <InputList>
      <Input>
        <Order>0</Order>
        <Source>TaxMap</Source>
      </Input>
    </InputList>
  </Instance>
Would <BusinessRule> then be the name of the class that implements BusinessRuleBase (IBusinessRule)?
Yes, <BusinessRule> names a class that implements IBusinessRule, but I don't think your xml is right. I have more about that below.
roteague wrote:In this example case, I would simply implement ExecuteImpl in such a way as I need to check the inputs.
Yes.

Assuming you are trying to validate the data, you are missing something.

If you want the validation to occur when attempting to save, add an empty child <SaveValidationInstance /> node to your Instance node.

If you want the validation to occur as the data is entered, add an empty <FieldValidationInstance /> child node to your Instance node. Add a ValidatedField child to Instance with it's text specifying the data item name of the field you're validating. In your list of inputs, use [PotentialValue] to pull in the actual value entered by the user.

Code: Select all

  <Instance>
    <Name>IsValidTMK</Name>
    <BusinessRule>ValidateTMK</BusinessRule>
    <BusinessObjectContext>Property</BusinessObjectContext>
    <FieldValidationInstance />
    <ValidatedField>TaxMap</ValidatedField>
    <InputList>
      <Input>
        <Order>0</Order>
        <Source>[PotentialValue]</Source>
      </Input>
    </InputList>
  </Instance>
You can find examples of this by searching <select dir>\Data\RuleInstanceDefs\CoreBusinessRules.xml for any of these terms: PotentialValue, FieldValidationInstance, or ConstantValue.

roteague
Posts: 292
Joined: Thu Sep 25, 2008 4:49 pm
Location: Honolulu, Hawaii

Re: Building Business Rules Module

Post by roteague » Mon Oct 25, 2010 5:57 pm

Is the assembly marked with a custom attribute, similar to the way snap section assemblies are?
Robert

jbright

Re: Building Business Rules Module

Post by jbright » Mon Oct 25, 2010 6:01 pm

roteague wrote:Is the assembly marked with a custom attribute, similar to the way snap section assemblies are?
Yup. Mark the assembly with SoftPro.Common.BusinessRuleContainerAttribute.

Code: Select all

using SoftPro.Common;

[assembly: BusinessRuleContainer()]

roteague
Posts: 292
Joined: Thu Sep 25, 2008 4:49 pm
Location: Honolulu, Hawaii

Re: Building Business Rules Module

Post by roteague » Tue Oct 26, 2010 2:33 pm

I've got the Business Rules to load (I think). When I execute the save method, I get the following:
Could not find the data value requested: "TaxMap"
My XML is defined as follows:

Code: Select all

<Instance>
    <Name>IsValidTMK</Name>
    <BusinessRule>ValidateTMK</BusinessRule>
    <BusinessObjectContext>Property</BusinessObjectContext>
    <SaveValidationInstance />
    <InputList>
      <Input>
        <Order>0</Order>
        <Source>TaxMap</Source>
      </Input>
    </InputList>
  </Instance>
Robert

jbright

Re: Building Business Rules Module

Post by jbright » Tue Oct 26, 2010 4:50 pm

Here is how I would do it.

Code: Select all

<Instance>
	<Name>ValidatePropertyHasTaxMap</Name>
	<BusinessRule>ValidatePropertyHasTaxMap</BusinessRule>
	<BusinessObjectContext>Property</BusinessObjectContext>
	<Persist>False</Persist>
	<HideFromUser>True</HideFromUser>
	<SaveValidationInstance />
	<InputList>
		<Input>
			<Order>0</Order>
			<Source>TaxMaps\Identification</Source>
		</Input>
		<Input>
			<Order>1</Order>
			<Source>[ConstantValue]\Property must have a TaxMap.</Source>
		</Input>
		<Input>
			<Order>2</Order>
			<Source>This</Source>
		</Input>
		<Input>
			<Order>3</Order>
			<Source>[ConstantValue]\TaxMaps</Source>
		</Input>
	</InputList>
</Instance>

Code: Select all

/// <summary>
/// Display errorMessage if Property.TaxMaps has no entries.
/// </summary>
internal class ValidatePropertyHasTaxMap : BusinessRuleBase
{
	public ValidationError ExecuteImpl(
		InputParameterHandle<string[]> taxMapIdentifications,
		InputParameterHandle<string> errorMessage,
		InputParameterHandle<IBusinessObject> source, 
		InputParameterHandle<string> strDataItem)
	{
		if ( taxMapIdentifications == null || taxMapIdentifications.Value == null 
			 || taxMapIdentifications.Value.Length == 0 )
		{
			// Including the Guid of the parent object and a string referring to the 
			// data item (third and fourth arguments below), we can create a clickable 
			// hotspot on the error in the Warnings and Errors window.  When the user 
			// clicks on the error, it will navigate straight to the missing TaxMap.
			return new ValidationError(
				errorMessage.Value, ValidationErrorLevel.Error, source.Value, strDataItem.Value);
		}

		return null;
	}
}

Post Reply