Skip to content

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 ResponseAttributes and 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 .dll files 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 Data object 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

dotnet build YourCompany.P21.Rules.csproj --configuration Release

Deploy

  1. 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.

  2. Register the rule in P21: navigate to Setup → Business Rules → Rule Registration and add a new entry pointing to your DLL and class name.

  3. Assign the trigger in P21: navigate to Setup → Business Rules → Rule Assignments and configure the window, field/event, and rule type for each rule.

  4. Restart P21 application services (or the IIS application pool hosting the uiserver) to reload the rule assemblies.

  5. 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:

Log.AddAndPersist($"[MyRule] Executed for customer {customerId}, result: {outcome}");

Avoid logging on every execution in high-frequency rules (e.g., quantity field changes) as this can generate excessive log volume.


See Also