Business Rules¶
P21 Business Rules are compiled .NET class libraries that the ERP loads and executes at specific trigger points in the application. They are the primary mechanism for extending P21 behavior without modifying P21 source code — enabling validation, automation, custom dialogs, downstream integration, and data manipulation that fires as part of normal P21 operation.
What Are Business Rules?¶
At their core, P21 Business Rules are C# classes that inherit from P21.Extensions.BusinessRule.Rule and are registered in P21 administration against specific trigger points. When a trigger fires — a user changing a field, saving a record, a form being printed, a system event occurring — P21 loads the registered rule's DLL and calls its Execute() method.
Rules can:
- Validate field values before a save is committed, blocking the save and showing an error message
- Auto-calculate derived fields when a dependent value changes (e.g., compute a freight charge when quantity changes)
- Enforce business-specific policies that are unique to your organization
- Display custom dialogs using
ResponseAttributesand gather structured input from the user - React to system events such as order saved, invoice created, or form printed, and trigger downstream processes
- Manipulate the current window's data before it is committed to the database
- Call external systems via HTTP, database queries, or any .NET API
Rules execute in-process within P21's application server. They have direct access to the current window's data through the Data object and can reach the P21 SQL Server database directly via SqlConnection.
Architecture¶
┌─────────────────────────────────────────────────────┐
│ P21 Application Server │
│ │
│ ┌─────────────┐ trigger ┌──────────────────┐ │
│ │ P21 Window │ ──────────── │ Rule Engine │ │
│ │ (PB/UI) │ │ │ │
│ └─────────────┘ │ Loads rule DLL │ │
│ │ │ Calls Execute() │ │
│ │ Data object │ Returns result │ │
│ └─────────────────────└──────────────────┘ │
│ │ │
│ ┌───────▼──────────┐ │
│ │ Your Rule DLL │ │
│ │ (C# class lib) │ │
│ └──────────────────┘ │
└─────────────────────────────────────────────────────┘
│
┌────────▼────────┐
│ SQL Server │
│ (P21 Database) │
└─────────────────┘
Key architectural facts:
- Rules are standard .NET class libraries targeting net48 (the .NET Framework 4.8 runtime P21 uses)
- They are compiled to
.dllfiles and deployed to a P21-configured rules directory - P21 loads rule assemblies on first use and caches them — a server restart is required to reload after redeployment
- The
Dataobject provides access to the current window's DataSet (all tables and rows), individual field values, focus control, cascade control, and the database connection string - Rules run on the same thread as the P21 application event that triggered them (synchronous rules block the UI until complete)
Rule Types at a Glance¶
| Type | Trigger | Sync/Async | Can Block Save | Can Show Dialog |
|---|---|---|---|---|
| Validator | Field change or save | Synchronous | Yes | Via ResponseAttributes |
| OnEvent | System event (order saved, form printed) | Asynchronous | No | No |
| On Demand | User presses a custom button | Synchronous | No | Via ResponseAttributes |
See Rule Types for full details on each type with code examples.
SDK Reference¶
Business rules are built against the P21 Extensions SDK, distributed as NuGet packages. The relevant packages are:
| Package | Purpose |
|---|---|
P21.Extensions.BusinessRule | Base class Rule, RuleResult, ResponseAttributes, and all rule-authoring interfaces |
P21.DomainObject | P21 data types used by the rule engine |
P21.Common | Common utilities shared across P21 extension points |
Current SDK Versions¶
<!-- Directory.Build.props — place in repository root -->
<Project>
<PropertyGroup>
<Company>Your Company Name</Company>
<P21ContractsVersion>25.2.45</P21ContractsVersion>
<P21ExtensionsVersion>25.2.25</P21ExtensionsVersion>
</PropertyGroup>
</Project>
Version Alignment
SDK package versions must match — or be compatible with — the version of P21 installed on the server. Using a SDK version significantly newer than your P21 server version may cause runtime failures. Check your P21 version in Help → About and align SDK packages accordingly.
Project Setup¶
Directory.Build.props¶
Place this file at the root of your rules repository to centralize version management across multiple rule projects:
<Project>
<PropertyGroup>
<Company>Your Company Name</Company>
<P21ContractsVersion>25.2.45</P21ContractsVersion>
<P21ExtensionsVersion>25.2.25</P21ExtensionsVersion>
<Copyright>Copyright © $(Company) $(Year)</Copyright>
<Nullable>enable</Nullable>
<LangVersion>latest</LangVersion>
</PropertyGroup>
</Project>
.csproj¶
Each rule assembly is a standard .NET 4.8 class library:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net48</TargetFramework>
<AssemblyName>YourCompany.P21.Rules</AssemblyName>
<RootNamespace>YourCompany.P21.Rules</RootNamespace>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="P21.Extensions.BusinessRule"
Version="$(P21ExtensionsVersion)" />
</ItemGroup>
</Project>
Multiple Rule Assemblies
You can place multiple rule classes in a single assembly, or split them across multiple projects. Each DLL is registered separately in P21 administration. A common pattern is one DLL per functional domain (e.g., YourCompany.P21.Rules.Sales, YourCompany.P21.Rules.Purchasing).
Rule Structure¶
Every rule class must implement three members:
using P21.Extensions.BusinessRule;
namespace YourCompany.P21.Rules;
public class MyRule : Rule
{
/// <summary>
/// The rule name as it will appear in P21 administration.
/// Must be unique across all registered rules.
/// </summary>
public override string GetName() => "MyRule";
/// <summary>
/// Human-readable description shown in P21 administration.
/// </summary>
public override string GetDescription() =>
"Describes what this rule does and when it fires.";
/// <summary>
/// The rule's logic. Called by P21 when the trigger fires.
/// </summary>
public override RuleResult Execute()
{
// Access current field values via Data.Fields
// Access all window tables via Data.Set.Tables
// Return success or error
return RuleResult.Success;
}
}
RuleResult Values¶
| Return Value | Effect |
|---|---|
RuleResult.Success | Rule completed successfully. Processing continues normally. |
RuleResult.Error("message") | Rule failed. For Validator rules, this blocks the save and displays the message. |
RuleResult.Warning("message") | Rule completed with a warning. Processing continues but the message is shown to the user. |
Deployment¶
Build¶
Deploy¶
-
Copy the compiled
YourCompany.P21.Rules.dll(and any dependency DLLs) to the P21 rules directory. The default path is configured in P21 administration under Setup → System Setup → Business Rule Settings. -
Register the rule in P21: navigate to Setup → Business Rules → Rule Registration and add a new entry pointing to your DLL and class name.
-
Assign the trigger in P21: navigate to Setup → Business Rules → Rule Assignments and configure the window, field/event, and rule type for each rule.
-
Restart P21 application services (or the IIS application pool hosting the uiserver) to reload the rule assemblies.
-
Test by exercising the trigger in the P21 UI.
Server Restart Required on Redeploy
P21 caches rule assemblies after first load. To pick up a new version of a DLL, the P21 application services must be restarted. Overwriting a DLL while the server is running will not take effect until restart.
Logging¶
Rules have access to a Log object for persisting runtime log entries. These entries appear in the P21 application log and can be invaluable for troubleshooting:
Avoid logging on every execution in high-frequency rules (e.g., quantity field changes) as this can generate excessive log volume.
See Also¶
- Rule Types — Validator, OnEvent, and On Demand rules with full code examples
- Data Access Patterns — How to read and write window data from within a rule
- Response Attributes — Building custom dialogs with user input
- Code Examples — Fully annotated, production-oriented rule examples