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) { ... }
}