[Canonical page](<https://docs.dhtmlx.com/planning-engine/net/api/core/changes/scheduling-changes/>)
[Full documentation index](<https://docs.dhtmlx.com/planning-engine/llms.txt>)


# SchedulingChanges

One set of edits to a live [`Project`](<https://docs.dhtmlx.com/planning-engine/net/api/core/project/project.md>): tasks, links, resources and assignments to add, update or remove. [`Project.Apply`](<https://docs.dhtmlx.com/planning-engine/net/api/project-apply.md>) commits it, [`Project.Preview`](<https://docs.dhtmlx.com/planning-engine/net/api/project-preview.md>) tries it on a copy, and each [`Candidate`](<https://docs.dhtmlx.com/planning-engine/net/api/core/evaluate/candidate.md>) passed to [`Project.Evaluate`](<https://docs.dhtmlx.com/planning-engine/net/api/project-evaluate.md>) carries one. [`Project.SuggestAssignments`](<https://docs.dhtmlx.com/planning-engine/net/api/project-suggest-assignments.md>) returns one in [`SuggestResult.Changes`](<https://docs.dhtmlx.com/planning-engine/net/api/core/suggest/suggest-result.md>).

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

## Declaration

```csharp
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()

```csharp
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`](<https://docs.dhtmlx.com/planning-engine/net/api/core/changes/changes.md>)`<`[`TaskData`](<https://docs.dhtmlx.com/planning-engine/net/api/core/data/task-data.md>)`, `[`TaskUpdate`](<https://docs.dhtmlx.com/planning-engine/net/api/core/changes/task-update.md>)`>` | `new()` | Tasks to add, update or remove by id. |
| `Links` | [`Changes`](<https://docs.dhtmlx.com/planning-engine/net/api/core/changes/changes.md>)`<`[`LinkAdd`](<https://docs.dhtmlx.com/planning-engine/net/api/core/changes/link-add.md>)`, `[`LinkUpdate`](<https://docs.dhtmlx.com/planning-engine/net/api/core/changes/link-update.md>)`>` | `new()` | Links to add, update or remove by id. |
| `Resources` | [`Changes`](<https://docs.dhtmlx.com/planning-engine/net/api/core/changes/changes.md>)`<`[`ResourceData`](<https://docs.dhtmlx.com/planning-engine/net/api/core/data/resource-data.md>)`, `[`ResourceUpdate`](<https://docs.dhtmlx.com/planning-engine/net/api/core/changes/resource-update.md>)`>` | `new()` | Resources to add, update or remove by id. |
| `Assignments` | [`Changes`](<https://docs.dhtmlx.com/planning-engine/net/api/core/changes/changes.md>)`<`[`AssignmentData`](<https://docs.dhtmlx.com/planning-engine/net/api/core/data/assignment-data.md>)`, `[`AssignmentUpdate`](<https://docs.dhtmlx.com/planning-engine/net/api/core/changes/assignment-update.md>)`>` | `new()` | Assignments to add, update or remove by id. |

### BaselineId

```csharp
public string? BaselineId { get; set; }
```

The baseline the edits expect. Take the token from the latest [`ScheduleResult.BaselineId`](<https://docs.dhtmlx.com/planning-engine/net/api/core/results/schedule-result.md>). When it isn't `null` and doesn't name the project's current baseline, `Apply`, `Preview` and `Evaluate` throw a [`ProjectException`](<https://docs.dhtmlx.com/planning-engine/net/api/core/errors/project-exception.md>) with `Code == `[`ProjectError.StaleBaseline`](<https://docs.dhtmlx.com/planning-engine/net/api/core/errors/project-exception.md#projecterror>). Tokens are opaque: don't compare them across runs.

### Tasks, Links, Resources, Assignments

```csharp
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>`](<https://docs.dhtmlx.com/planning-engine/net/api/core/changes/changes.md>).

A change set has no calendar bucket. Calendars can't be added or edited through `SchedulingChanges`; that takes a full [`Solve(data, …)`](<https://docs.dhtmlx.com/planning-engine/net/api/project-solve.md>).

## 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`](<https://docs.dhtmlx.com/planning-engine/net/api/core/changes/apply-report.md#appliedoperation>) and [`RejectedOperation.Path`](<https://docs.dhtmlx.com/planning-engine/net/api/core/changes/rejected-operation.md>).

## Methods

### DeepClone

```csharp
public SchedulingChanges DeepClone()
```

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

### ToString

```csharp
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.

```csharp
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}");
```

## Related

- [`Project.Apply`](<https://docs.dhtmlx.com/planning-engine/net/api/project-apply.md>) - commits a change set
- [`Project.Preview`](<https://docs.dhtmlx.com/planning-engine/net/api/project-preview.md>) - tries a change set on a copy
- [`ApplyReport`](<https://docs.dhtmlx.com/planning-engine/net/api/core/changes/apply-report.md>) - the outcome of each operation
- [Editing a project](<https://docs.dhtmlx.com/planning-engine/net/live-projects/editing-a-project.md>) - building and applying change sets
- [Previewing changes](<https://docs.dhtmlx.com/planning-engine/net/live-projects/previewing-changes.md>) - seeing what an edit would do
