Skip to main content

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 TypeInterfaceExecution
PostCreate TriggerIPostCreateTriggerProgramAsyncAsync (after parent is committed)
PostConnect TriggerIPostConnectTriggerProgramAsyncAsync (after relation is committed)
PromoteActionCommandIPromoteActionCommandProgramAsyncSynchronous

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

  1. Fetch the parent instance to read required attribute values.
  2. Build one or more Instance objects with InstanceTypeName and Attributes.
  3. Call InstanceService.CreateInstanceAsync(instance) (or CreateInstancesAsync for bulk) to create the child.
  4. 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

ApproachWhen to Use
Set a Reference attribute on the child at creation timeWhen the relation type is defined as a Reference attribute on the child type — the platform creates the relation automatically.
RelationService.CreateRelationAsync explicitlyWhen the relation type only exists as a structural relation (not exposed as a Reference attribute).

Key Services

ServiceMethodPurpose
IInstanceServiceCreateInstanceAsyncCreate a single new instance
IInstanceServiceCreateInstancesAsyncBulk create multiple instances of the same type
IRelationServiceCreateRelationAsyncCreate 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
}