LinkAdd#

A new dependency between two tasks, added to a live project through SchedulingChanges.Links.Add. It has the same properties as LinkData, except that Id is nullable. Build one with an object initializer; Source and Target are required.

Declaration#

namespace Plen;

public sealed class LinkAdd : IDeepCloneable<LinkAdd>

LinkAdd has no constructor that sets the required properties, so new LinkAdd() without Source and Target doesn't compile.

Properties#

Property Type Default Description
Id Id? null Id of the new link. null makes Apply generate one.
Source Id required Id of the predecessor task.
Target Id required Id of the successor task.
LinkType LinkType LinkType.Fs Which end of Source drives which end of Target.
Lag double 0.0 Working time between the two ends, in LagUnit. Must be finite.
LagUnit TimeUnit? null Unit of Lag. null uses SchedulingConfig.DurationUnit.

Source, Target, LinkType, Lag and LagUnit mean the same as on LinkData, including what happens when an endpoint names no task. A string, int or double converts to Id implicitly.

Id#

public Id? Id { get; set; }

A non-null id stores the link under that id. It must not repeat the id of a link already in the project.

null makes Project.Apply pick the first free id of the form $generated_link_1, $generated_link_2, and so on. The generated id is listed in ApplyReport.Effects.GeneratedLinks, so you can store it for later updates or removals.

Methods#

DeepClone#

public LinkAdd DeepClone()

Returns a copy with the same property values.

ToString#

public override string ToString()

Returns a readable rendering of every property, for logging.

LinkAdd doesn't override Equals; two instances compare by reference.

Errors#

A LinkAdd is one operation, named links.add[i]. It's rejected, and listed as a RejectedOperation in ApplyReport.Rejected instead of applied, when:

  • Id is non-null and a link with that id already exists: duplicate link id
  • Lag is NaN or infinite: lag must be finite

A rejection isn't thrown. Apply reports it and keeps going.

An endpoint that names no task, or names an unscheduled task, doesn't reject the operation; see ChangeEffects.DroppedLinks.

A LinkAdd can reuse the id of a link removed in the same change set, because link removals run before link adds; see operation order.

Example#

Link task 1 to task 2 in a solved project, and read back the generated link id:

using Plen;

var start = Moment.FromYmdHm(2026, 4, 6, 8, 0)
    ?? throw new InvalidOperationException("invalid date");

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

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

var changes = new SchedulingChanges();
changes.Links.Add.Add(new LinkAdd { Source = 1, Target = 2, LinkType = LinkType.Fs });

var report = project.Apply(changes);
Console.WriteLine($"new link ids: {string.Join(", ", report.Effects.GeneratedLinks)}");

var result = project.Solve(null, options);
foreach (var update in result.Updates)
    Console.WriteLine($"task {update.Id}: {update.StartDate} .. {update.EndDate}");