SchedulingChanges#

One set of edits to a live Project: tasks, links, resources and assignments to add, update or remove. Project.Apply commits it, Project.Preview tries it on a copy, and each Candidate passed to Project.Evaluate carries one. Project.SuggestAssignments returns one in SuggestResult.Changes.

Create one with new SchedulingChanges(), where every bucket is empty, and add records to the buckets the edit needs.

Declaration#

public sealed class SchedulingChanges : IDeepCloneable<SchedulingChanges>
{
    public string? BaselineId { get; set; }
    public Changes<TaskData, TaskUpdate> Tasks { get; set; } = new();
    public Changes<LinkAdd, LinkUpdate> Links { get; set; } = new();
    public Changes<ResourceData, ResourceUpdate> Resources { get; set; } = new();
    public Changes<AssignmentData, AssignmentUpdate> Assignments { get; set; } = new();

    public SchedulingChanges DeepClone();
    public override string ToString();
}

SchedulingChanges doesn't implement IEquatable<SchedulingChanges>, so Equals compares references.

Constructors#

SchedulingChanges()#

public SchedulingChanges()

Creates an empty change set: BaselineId is null and every bucket has empty Add, Update and Remove lists.

Properties#

Property Type Default Description
BaselineId string? null Baseline token the edits were made against. null skips the check.
Tasks Changes<TaskData, TaskUpdate> new() Tasks to add, update or remove by id.
Links Changes<LinkAdd, LinkUpdate> new() Links to add, update or remove by id.
Resources Changes<ResourceData, ResourceUpdate> new() Resources to add, update or remove by id.
Assignments Changes<AssignmentData, AssignmentUpdate> new() Assignments to add, update or remove by id.

BaselineId#

public string? BaselineId { get; set; }

The baseline the edits expect. Take the token from the latest ScheduleResult.BaselineId. When it isn't null and doesn't name the project's current baseline, Apply, Preview and Evaluate throw a ProjectException with Code == ProjectError.StaleBaseline. Tokens are opaque: don't compare them across runs.

public Changes<TaskData, TaskUpdate> Tasks { get; set; } = new();
public Changes<LinkAdd, LinkUpdate> Links { get; set; } = new();
public Changes<ResourceData, ResourceUpdate> Resources { get; set; } = new();
public Changes<AssignmentData, AssignmentUpdate> Assignments { get; set; } = new();

One bucket per record kind. Each bucket holds Add, Update and Remove lists; see Changes<TAdd, TUpdate>.

A change set has no calendar bucket. Calendars can't be added or edited through SchedulingChanges; that takes a full Solve(data, …).

Operation order#

Operations run in a fixed order, not in the order they were added. Kinds run in the order tasks, links, resources, assignments. Within a kind:

Kind Order
Tasks Remove, Add, Update
Links Remove, Update, Add
Resources Remove, Add, Update
Assignments Remove, Update, Add

Within a list, records run in list order. Each operation is identified by its bucket, list and index, such as tasks.update[0], in AppliedOperation.Path and RejectedOperation.Path.

Methods#

DeepClone#

public SchedulingChanges DeepClone()

Returns a copy that shares no list or nested record with this instance.

ToString#

public override string ToString()

Returns a debug dump of all five properties.

Example#

Move a task, add a link and check the edit against the current baseline.

using Plen;

var monday = Moment.FromYmdHm(2026, 8, 10, 8, 0) ?? throw new InvalidOperationException("invalid date");
var wednesday = Moment.FromYmdHm(2026, 8, 12, 8, 0) ?? throw new InvalidOperationException("invalid date");

var data = new ProjectData
{
    Tasks =
    [
        new TaskData(1) { StartDate = monday, Duration = 1.0 },
        new TaskData(2) { StartDate = monday, Duration = 2.0 },
    ],
};

var project = new Project();
var solved = project.Solve(data, new SolveOptions());

var changes = new SchedulingChanges { BaselineId = solved.BaselineId };
changes.Tasks.Update.Add(new TaskUpdate(1) { StartDate = wednesday });
changes.Links.Add.Add(new LinkAdd { Source = 1, Target = 2 });

var report = project.Apply(changes);
foreach (var op in report.Rejected)
    Console.WriteLine($"rejected {op.Path}: {op.Message}");