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
RelatedInstanceand Relation attributes. - Notification Template Reference —
@Modelvariables per target type and Razor (@Model.*) syntax for authoring notification template.html/.jsonfiles.
// 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
| Pattern | Description | Primary Program Types |
|---|---|---|
| Validation | Block an operation when business rules are violated | PreCreate, PreUpdate, PromoteActionCommand |
| Auto-Populate Attribute Value | Set attribute values by reading from related instances | PreCreate, PostCreate, PreUpdate, PostConnect |
| Send Notification | Send email (To, CC, BCC) or post a message to an instance feed | PostCreate, PromoteActionCommand, Scheduler |
| Create and Connect Child Instances | Create related instances and link them | PostCreate, PostConnect |
| Cascade Promote | Propagate a lifecycle transition to related instances; aggregate-all-approvers check | PromoteActionCommand, PostCreate, Scheduler |
| Query and Filter | Query instances using InstanceQuery and RelatedInstanceQuery | All program types |
| Invoke Common Program | Call reusable logic encapsulated in a Common Program | All program types |
| Manage Instance Access | Grant or revoke per-instance user access; create users; manage roles | PostConnect, PostCreate, PromoteActionCommand |
| Delete Related Data | Delete or unlink child instances before parent deletion | PreDelete |
| System Event | Dispatch async operations by creating a SystemEvent instance | PostCreate, PromoteActionCommand |
| Sequence Number | Auto-generate unique IDs with ISequenceCodeService | PreCreate, PromoteActionCommand |
| Dynamic State Selection | Choose destination state at runtime with IPromoteActionProgramAsync | PromoteAction |
| Role-Based User Lookup | Find users by role with UserService.GetUsers, RoleService, GetInstanceUsersAsync; activity history | Scheduler, PostCreate, PromoteActionCommand |
| Dynamic Form Helper | Read, merge, and transform Dynamic Form template/response JSON using DynamicFormHelper | All 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) { ... }
}