Create and Connect Child Instances Pattern
Overview
This pattern creates one or more new instances of a related type and links them to the triggering instance using RelationService.CreateRelation. It is used to automatically generate child or related records as a side effect of creating or updating a parent instance.
When to Use
- Auto-create child records on parent creation (e.g., create sub-tasks when a work order is created, create related records from a template).
- Create supporting records automatically (e.g., create a status tracking record for every new project).
- Fan out: create multiple child instances in a loop from a list of names or templates.
Applicable Program Types
| Program Type | Interface | Execution |
|---|---|---|
| PostCreate Trigger | IPostCreateTriggerProgramAsync | Async (after parent is committed) |
| PostConnect Trigger | IPostConnectTriggerProgramAsync | Async (after relation is committed) |
| PromoteActionCommand | IPromoteActionCommandProgramAsync | Synchronous |
Use PostCreate (not PreCreate) for this pattern. The parent instance must be committed to the database before child instances can be linked to it.
Pattern Structure
- Fetch the parent instance to read required attribute values.
- Build one or more
Instanceobjects withInstanceTypeNameandAttributes. - Call
InstanceService.CreateInstanceAsync(instance)(orCreateInstancesAsyncfor bulk) to create the child. - Use
RelationService.CreateRelationAsync(new Relation { ... })to link the new instance to the parent. Alternatively, set a Reference attribute on the child at creation time to create the link implicitly.
Code Example — Single Child via CreateInstance + CreateRelation
Creates a status tracking record and links it to a newly created project.
using Prorigo.Protrak.API.Contracts;
using Prorigo.Protrak.API.Contracts.Builders;
using Prorigo.Protrak.API.Contracts.Enum;
using Prorigo.Protrak.API.Services;
using Prorigo.Protrak.Programs;
using System;
using System.Threading.Tasks;
namespace YourNamespace.Programs
{
/// <summary>
/// Type: Post Create Trigger
/// Configured for: Type Project
/// Trigger: After project instance is created
/// Summary: Creates a StatusRecord and links it to the new project.
/// </summary>
public class CreateStatusRecordForProject : IPostCreateTriggerProgramAsync
{
public IInstanceService InstanceService { get; set; }
public IRelationService RelationService { get; set; }
public IHomeService HomeService { get; set; }
private readonly string TYPE_STATUS_RECORD = "StatusRecord";
private readonly string RELATION_PROJECT_TO_STATUS = "ProjectToStatusRecord";
private readonly string ATTRIBUTE_NAME = "Name";
private readonly string ATTRIBUTE_STATUS = "StatusValue";
private readonly string ATTRIBUTE_RECORD_DATE = "RecordDate";
private readonly string PICKLIST_ACTIVE = "Active";
public async Task RunAsync(Guid instanceId)
{
var project = await InstanceService.GetInstanceAsync(instanceId, new[] { ATTRIBUTE_NAME });
// Get tenant local time for the record date
var tenantSettings = HomeService.GetTenantSettings();
var tz = TimeZoneInfo.FindSystemTimeZoneById(tenantSettings.DateTimeFormat.TimeZoneId);
var now = TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, tz);
// 1. Build the child instance
var statusRecord = InstanceBuilder.ForCreate(TYPE_STATUS_RECORD)
.SetTextAttributeValue(ATTRIBUTE_NAME, project.Name)
.SetPicklistAttributeValue(ATTRIBUTE_STATUS, PICKLIST_ACTIVE)
.SetDateAttributeValue(ATTRIBUTE_RECORD_DATE, now)
.Build();
// 2. Create the child
var created = await InstanceService.CreateInstanceAsync(statusRecord);
// 3. Link child to parent
await RelationService.CreateRelationAsync(new Relation
{
RelationTypeName = RELATION_PROJECT_TO_STATUS,
SourceInstanceId = instanceId,
DestinationInstanceId = created.Id,
Direction = RelationDirection.To
});
}
}
}
Code Example — Bulk Create via Reference Attribute
Parses a comma-separated text field from the parent and creates multiple child instances with a parent reference already set.
/// <summary>
/// Type: Post Create Trigger
/// Configured for: Type Project
/// Trigger: After project instance is created
/// Summary: Parses comma-separated sub-item names and bulk-creates linked child instances.
/// </summary>
public class AutoCreateSubItemsOnProjectCreate : IPostCreateTriggerProgramAsync
{
public IInstanceService InstanceService { get; set; }
public ILoggingService LoggingService { get; set; }
private readonly string TYPE_PROJECT = "Project";
private readonly string ATTRIBUTE_CHILD_NAMES = "ChildItemNames";
private readonly string ATTRIBUTE_NAME = "Name";
private readonly string ATTRIBUTE_PARENT_REF = "ParentItemRef";
public async Task RunAsync(Guid instanceId)
{
try
{
var project = await InstanceService.GetInstanceAsync(instanceId, new[] { ATTRIBUTE_CHILD_NAMES });
var childNames = project.GetTextAttributeValue(ATTRIBUTE_CHILD_NAMES);
if (childNames == null) return;
// Parse comma-separated names
var names = childNames
.Split(',', StringSplitOptions.RemoveEmptyEntries)
.Select(n => n.Trim())
.Where(n => !string.IsNullOrWhiteSpace(n))
.ToArray();
if (names.Length == 0) return;
// Build child instances with parent reference (creates the relation implicitly)
var children = names.Select(name => (Instance)InstanceBuilder.ForCreate(TYPE_PROJECT)
.SetTextAttributeValue(ATTRIBUTE_NAME, name)
.SetReferenceAttributeValue(ATTRIBUTE_PARENT_REF, new ReferenceValue { Id = instanceId })
).ToArray();
// Bulk create
await InstanceService.CreateInstancesAsync(TYPE_PROJECT, children);
}
catch (Exception ex)
{
LoggingService.Error($"Error creating child items for project {instanceId}: {ex.Message}");
throw;
}
}
}
Choosing: RelationService vs Reference Attribute
| Approach | When to Use |
|---|---|
| Set a Reference attribute on the child at creation time | When the relation type is defined as a Reference attribute on the child type — the platform creates the relation automatically. |
RelationService.CreateRelationAsync explicitly | When the relation type only exists as a structural relation (not exposed as a Reference attribute). |
Key Services
| Service | Method | Purpose |
|---|---|---|
IInstanceService | CreateInstanceAsync | Create a single new instance |
IInstanceService | CreateInstancesAsync | Bulk create multiple instances of the same type |
IRelationService | CreateRelationAsync | Create a named relation between two instances |
Relation Object Properties
new Relation
{
RelationTypeName = "ParentTypeToChildType", // schema relation type name
SourceInstanceId = parentInstanceId,
DestinationInstanceId = childInstanceId,
Direction = RelationDirection.To // "To" = source→destination per schema
}