Skip to main content

Program Patterns

This section documents recurring code patterns found across Protrak customization programs. Each pattern explains the intent, applicable program types, and shows a working code example drawn from real implementations.

Use these patterns as building blocks when generating or reviewing program code. Most programs combine two or more patterns.


Reference (Read These First)

Before writing code that reads or writes attributes:

  • Instance Accessor Methods — why to use typed getters/setters instead of Attributes.FirstOrDefault(...), with before/after comparisons.
  • Attribute Types Reference — per-type code snippets for every attribute type (Text, Numeric, Date, Boolean, Picklist, User, Reference, Attachment) plus RelatedInstance and Relation attributes.
  • Notification Template Reference@Model variables per target type and Razor (@Model.*) syntax for authoring notification template .html/.json files.
// Reading
string text = instance.GetTextAttributeValue("Name");
DateTime? date = instance.GetDateAttributeValue("StartDate");
string[] opts = instance.GetPicklistAttributeValue("Status");
UserValue[] users = instance.GetUserAttributeValue("Assignee");

// Writing (creates or updates — no manual find-or-add needed)
instance.SetPicklistAttributeValue("Status", "Active");

Pattern Catalog

PatternDescriptionPrimary Program Types
ValidationBlock an operation when business rules are violatedPreCreate, PreUpdate, PromoteActionCommand
Auto-Populate Attribute ValueSet attribute values by reading from related instancesPreCreate, PostCreate, PreUpdate, PostConnect
Send NotificationSend email (To, CC, BCC) or post a message to an instance feedPostCreate, PromoteActionCommand, Scheduler
Create and Connect Child InstancesCreate related instances and link themPostCreate, PostConnect
Cascade PromotePropagate a lifecycle transition to related instances; aggregate-all-approvers checkPromoteActionCommand, PostCreate, Scheduler
Query and FilterQuery instances using InstanceQuery and RelatedInstanceQueryAll program types
Invoke Common ProgramCall reusable logic encapsulated in a Common ProgramAll program types
Manage Instance AccessGrant or revoke per-instance user access; create users; manage rolesPostConnect, PostCreate, PromoteActionCommand
Delete Related DataDelete or unlink child instances before parent deletionPreDelete
System EventDispatch async operations by creating a SystemEvent instancePostCreate, PromoteActionCommand
Sequence NumberAuto-generate unique IDs with ISequenceCodeServicePreCreate, PromoteActionCommand
Dynamic State SelectionChoose destination state at runtime with IPromoteActionProgramAsyncPromoteAction
Role-Based User LookupFind users by role with UserService.GetUsers, RoleService, GetInstanceUsersAsync; activity historyScheduler, PostCreate, PromoteActionCommand
Dynamic Form HelperRead, merge, and transform Dynamic Form template/response JSON using DynamicFormHelperAll program types

Pattern Combinations

Real programs often combine multiple patterns. Common combinations:

PostCreate: Create children + send notification + auto-promote

PostCreate
→ [Create and Connect Child Instances] — create RAG, financial records, deliverables
→ [Send Notification] — notify team members
→ [Cascade Promote] — auto-promote to initial state

PreCreate: Validate + sequence number + auto-populate

PreCreate
→ [Sequence Number] — generate unique name/ID
→ [Validation] — check mandatory fields, uniqueness, date ranges
→ [Auto-Populate Attribute Value] — copy values from referenced instances

PromoteActionCommand: Validate + cascade + notify

PromoteAction (IPromoteActionProgramAsync)
→ [Dynamic State Selection] — determine target state from attribute value

PromoteActionCommand (IPromoteActionCommandProgramAsync)
→ [Validation] — check pre-conditions for the transition
→ [Auto-Populate Attribute Value] — update attributes on transition
→ [Cascade Promote] — promote related instances
→ [Send Notification] — alert stakeholders (with BCC to admins)

Scheduler: Query + activity history + role lookup + notify

Scheduler
→ [Query and Filter] + [Activity History] — find instances with stale state
→ [Role-Based User Lookup] — find recipients by state→role mapping
→ [Send Notification] — reminder with BCC to monitoring role
→ [Cascade Promote] — promote when all approvers are done

Service Injection

Services are injected as public properties — Protrak's DI framework resolves them automatically:

public class MyProgram : IPostCreateTriggerProgramAsync
{
public IInstanceService InstanceService { get; set; }
public IRelationService RelationService { get; set; }
public INotificationService NotificationService { get; set; }
public IHomeService HomeService { get; set; }
public IProgramService ProgramService { get; set; }
public IUserService UserService { get; set; }
public IRoleService RoleService { get; set; }
public ISequenceCodeService SequenceCodeService { get; set; }
public IFileService FileService { get; set; }
public ILoggingService LoggingService { get; set; }
public IAttributeService AttributeService { get; set; }

public async Task RunAsync(Guid instanceId) { ... }
}

Always Prefer Async

Always implement the Async interface variants — the sync versions are deprecated:

// ✅ Correct
public class MyProgram : IPostCreateTriggerProgramAsync
{
public async Task RunAsync(Guid instanceId) { ... }
}

// ❌ Deprecated
public class MyProgram : IPostCreateTriggerProgram
{
public void Run(Guid instanceId) { ... }
}