Understanding Business Rule Objects

Understanding Business Rules

Business rules are a set of criteria that must be true before a business entity can be persisted to the database. They can include, but are not limited to:

  • Required values
  • Minimum and maximum values
  • Unique values

In a best-case scenario, your application's database enforces these same rules. This is important because your database is the last line of defense in creating and maintaining valid application data. If someone adds a record to the database outside of your application (for example, using SQL Server Management Studio or Oracle SQL Developer), the database rules ensure the data is valid.

So, why duplicate these rules in the business layer? Because this greatly improves application performance. It ensures that only fully validated requests are sent to the database. This means your database server isn't constantly bombarded with invalid requests, which would greatly reduce its efficiency.

Understanding Business Rule Objects

The main responsibility of a business rule object is to enforce business rules.

mmBusinessRule is the base class for all business rule classes in MM .NET. You can auto generate your business rule class as described in the Help topic Using the MM .NET Business Layer Generator, but you can also manually create your own business rules as outlined in the section below Manually Coded Business Rules.

Here is an example of an InventoryRules object:

You will learn more about the methods shown in this class in the sections that follow.

The MM .NET business rule class tracks two types of errors:

  • Broken business rules
  • Application warnings

Broken business rules (such as required fields that are empty) prevent data from being saved. In contrast, a warning is simply a message that can be displayed to the user as a word of caution, but still allows data to be saved.

For more information on application warnings, see the section below, Understanding Application Warnings.

There are two main ways to display broken rule and warning messages - either in a simple dialog or using the .NET Error Provider. Based on this functionality, the mmBusinessRule class contains four main collections:

  • A collection of broken rules containing simple text
  • A collection of warnings containing simple text
  • A collection of broken rules containing Error Provider information
  • A collection of warnings containing Error Provider information

To add a simple text broken rule, use the rule object's AddBrokenRule() method. To add a simple text warning, use the rule object's AddWarning() method.

To add an error provider-style broken rule, use the rule object's AddErrorProviderBrokenRule() method. To add an error provider-style warning, use the rule object's AddErrorProviderWarning() method.

Auto-Generated Business Rules

The MM .NET Business Layer Generator automatically generates a business rule class and business rules validation code for you from an existing database.

Even if you are using a Code First methodology, if you have an existing database containing many constraints, you can save yourself a lot of time by at least initially generating the business rules from this database. Afterwards, if desired, you can use a Code First methodology and add rules to business entities and then generate database constraints from these rules.

The generated business rule code is placed in your rule class's CheckRulesHook() method.

Automatic Rule Checking When Saving Entities

When you save an entity or entities using the business object's SaveEntity() or SaveEntityList() methods, the business rules are automatically checked for you. If any rules are broken, the save does not continue.

The sequence of events is shown in this diagram:

Here are the events that are business rule related:

  • SaveEntity() is called on the business object
  • The business object calls its own CheckRules() method
  • A call is made to the business rule object's CheckRules() method
  • The CheckRules() method calls the ClearAll() method which clears the broken rules collection
  • The CheckRules() method calls the CheckRulesHook() method
  • The CheckRulesHook() method calls the CheckExtendedRulesHook() method.
  • The Business Rule object returns an mmSaveDataResult enumerated value. If any rules are broken, the value returned is .RulesBroken.
  • If any rules are broken, the Save is aborted.

Here is a list of the mmSaveDataResult enumeration values:

  • RulesBroken - Business rules were broken - data is NOT saved
  • RulesPassed - All business rules passed, data is saved
  • RuleWarnings - Warnings only, data is saved
  • SaveCanceled - The Save was canceled
  • Exception - An exception occurred when attempting to save

Retrieving Broken Rules

When working with MM .NET front ends such as WPF and ASP.NET, broken business rules are automatically retrieved from the business rule object and displayed in the user interface.

However, you can also manually retrieve broken rules by calling the business rule object's GetAllBrokenRules() method. Note you access a business object's associated rule object by means of its Rules property.

For example, in C#:

// Save the new entity
var saveResult = employee.SaveEntity(employeeEntity);
 
// If rules are broken get all broken rules
if (saveResult == mmSaveDataResult.RulesBroken)
{
	var brokenRules = employee.Rules.GetAllBrokenRules();
}

And in VB .NET:

'' Save the New entity
Dim saveResult = Employee.SaveEntity(EmployeeEntity)
 
'' If rules are broken, get all broken rules
If saveResult = mmSaveDataResult.RulesBroken Then
	Dim brokenRules = Employee.Rules.GetAllBrokenRules()
End If

Manually Coded Business Rules

In addition to the rules automatically generated by the Business Layer Generator, you may also want to add additional rules to your Rule objects.

When you initially generate business rules, if you specified in the Business Layer Generator to create partial classes (highly recommended), the BLG creates a .Partial code file into which you can place your custom rule code. This allows you to regenerate the rules at a later date and not overwrite your custom rule code.

For example, if the Business Layer Generator creates an OrderRule.cs and associated OrderRule.Partial.cs file, you would place your custom rule checking code in the OrderRule.Partial.cs file.

The Partial.cs Rule object's CheckExtendedRulesHook method is the primary method in which you can place custom code. As shown in the sequence diagram above, the other half of the rule object's CheckRulesHook() method is automatically called by the business object before saving data. In turn, the CheckRulesHook() method calls the CheckExtendedRulesHook() method in which you can place your custom rule-checking code.

Rather than placing all your validation code in CheckExtendedRulesHook, it's best to create a separate method for each business rule. You can then make a call to each of these methods from within CheckExtendedRulesHook.

For example, the following OrderRules object makes calls to two other methods on itself (note that in this example, the rule code is identical to what is normally generated for you automatically by the Business Layer Generator):

/// <summary>
/// Checks business rules against the specified entity
/// </summary>
/// <param name="entity">Entity</param>
public override void CheckExtendedRulesHook<EntityType>(EntityType entity)
{
	OrderEntity currentEntity = entity as OrderEntity;
 
	// Call Validation methods
	this.ValidateCustomerID(currentEntity.CustomerID);
	this.ValidateShipName(currentEntity.ShipName);
}
 
/// <summary>
/// Validates the Customer ID
/// </summary>
public string ValidateCustomerID(string customerID)
{
	string Msg = null;
	if (mmType.IsEmpty(customerID))
	{
		this.EntityPropertyDisplayName = "Customer ID";
 
		Msg = this.RequiredFieldMessagePrefix +
			this.EntityPropertyDisplayName +
			this.RequiredFieldMessageSuffix;
 
		AddErrorProviderBrokenRule("CustomerID", Msg);
	}
	return Msg;
}
 
/// <summary>
/// Validates the Ship Name
/// </summary>
public string ValidateShipName(string shipName)
{
	string Msg = null;
	if (mmType.IsEmpty(shipName))
	{
		this.EntityPropertyDisplayName = "Ship Name";
 
		Msg = this.RequiredFieldMessagePrefix +
			this.EntityPropertyDisplayName +
			this.RequiredFieldMessageSuffix;
 
		AddErrorProviderBrokenRule("ShipName", Msg);
	}
	return Msg;
}

And in VB .NET:

''' <summary>
''' Checks business rules against the specified entity
''' </summary>
''' <param name="entity">Entity</param>
Public Overrides Sub CheckExtendedRulesHook(Of EntityType)(ByVal entity As EntityType)
 
	Dim currentEntity As OrderEntity = TryCast(entity, OrderEntity)
 
	'' Call Validation methods
	'' Call validation methods
	Me.ValidateCustomerID(currentEntity.CustomerID)
	Me.ValidateShipName(currentEntity.ShipName)
 
End Sub
 
''' <summary>
''' Validates the Customer ID
''' </summary>
Public Function ValidateCustomerID(customerID As StringAs String
	Dim Msg As String = Nothing
	If mmType.IsEmpty(customerID) Then
		Me.EntityPropertyDisplayName = "Customer ID"
 
		Msg = Me.RequiredFieldMessagePrefix +
			Me.EntityPropertyDisplayName +
			Me.RequiredFieldMessageSuffix
 
		AddErrorProviderBrokenRule("CustomerID", Msg)
	End If
	Return Msg
End Function
 
''' <summary>
''' Validates the Ship Name
''' </summary>
Public Function ValidateShipName(shipName As StringAs String
	Dim Msg As String = Nothing
	If mmType.IsEmpty(shipName) Then
		Me.EntityPropertyDisplayName = "Ship Name"
 
		Msg = Me.RequiredFieldMessagePrefix +
			Me.EntityPropertyDisplayName +
			Me.RequiredFieldMessageSuffix
 
		AddErrorProviderBrokenRule("ShipName", Msg)
	End If
	Return Msg
End Function

Note the following:

  • The value being tested is passed as a parameter to each of these methods. This allows you to call each method directly from user interface controls. For example, you may want to check a particular rule in the Validating event handler of a text box. You can place code in the event handler to directly call a method of this business rule object passing the current value of the control.

  • Each rule method returns the message as a string. Although this is ignored by the CheckRules method, it's convenient for code in user interface controls that calls business rule methods directly to receive this error message back so it can be displayed to the user.

Adding Broken Rules to Parent Business Objects

By default, broken rules are automatically added to parent business objects. This allows broken rules to bubble up the relationship hierarchy to top-level business objects. If you want to turn off this behavior for a particular business object, set its AddBrokenRulesToParent property to false. If you want to turn off this behavior for all business objects, set this property in the ABusinessObject class.

Using the HostObject Property

Often you need to access the host object to retrieve important information. The HostObject property contains a reference to the host business object. The following example shows a business rule method that calls the host object's IsEntityChanged() method.

In C#:

if (((Order)this.HostObject).IsEntityChanged(currentEntity))
{
	// Do something
}

In VB . NET:

If (CType(Me.HostObject, Order)).IsEntityChanged(currentEntity) Then
 
	'' Do something
 
End If

Understanding Application Warnings

An application warning is simply a message that can be displayed to the user as a word of caution, but still allows data to be saved.

For example, you may want to warn the user that, although a specific piece of information is not required, if they don't enter this information then something may go wrong.

The MM .NET Business Layer Generator does not generate application warnings. You must manually add this code to your business rule class's CheckExtendedRulesHook() method.

When adding a plain text warning message, use the AddWarning() method. When adding an error provider style message, use the AddErrorProviderWarning() method.

To get a list of all application warnings after attempting to save an entity or entity list, just call the Rule object's GetAllWarnings() method.


© (c) 2026 Oak Leaf Enterprises, Inc., 1996-2026 • Updated: 07/11/18
Comment or report problem with topic