RejectedOperation#
One operation of a change set that Project.Apply didn't apply: where it sits in the change set, and why it was rejected. You get instances from ApplyReport.Rejected, and as CandidateResult.Error when Project.Evaluate can't score a candidate.
Declaration#
namespace Plen;
public sealed class RejectedOperation : IDeepCloneable<RejectedOperation>, IEquatable<RejectedOperation>
Both properties are required, so new RejectedOperation() without Path and Message doesn't compile.
Properties#
| Property | Type | Description |
|---|---|---|
Path |
string |
Position of the operation in the change set, as {group}.{verb}[{index}]. |
Message |
string |
Why the operation was rejected. |
Path#
public required string Path { get; set; }
group is tasks, links, resources or assignments; verb is add, update or remove; index is the zero-based position in that list of SchedulingChanges. For example, links.add[0] is the first entry of changes.Links.Add. AppliedOperation.Path uses the same form, so every operation of a change set shows up exactly once in either Applied or Rejected.
In CandidateResult.Error, a failure while solving or leveling the candidate carries the input path of that error instead, such as a field path. When that error has no path, Path is an empty string.
Message#
public required string Message { get; set; }
The validation message, without the path, e.g. duplicate link id or lag must be finite. Each change-set type lists the messages its operations can produce; see LinkAdd for an example. The text is meant for logs and diagnostics, not for matching in code.
Methods#
DeepClone#
public RejectedOperation DeepClone()
Returns a copy with the same Path and Message.
Equals#
public bool Equals(RejectedOperation? other)
public override bool Equals(object? obj)
Value equality: two instances are equal when Path and Message are equal. GetHashCode is consistent with it.
ToString#
public override string ToString()
Returns a readable rendering of both properties, for logging.
Example#
Add a link whose id already exists, and read the rejection:
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 },
],
Links = [new LinkData { Id = "L1", Source = 1, Target = 2 }],
};
var project = new Project();
project.Solve(data, new SolveOptions());
var changes = new SchedulingChanges();
changes.Links.Add.Add(new LinkAdd { Id = "L1", Source = 2, Target = 1 });
var report = project.Apply(changes);
foreach (var rejected in report.Rejected)
Console.WriteLine($"{rejected.Path}: {rejected.Message}");
// links.add[0]: duplicate link id
A rejection sets ApplyReport.NeedsResync; to continue, see Recover from rejected changes.
Related#
ApplyReport- the report that lists rejectionsProject.Apply- the call that rejects operationsSchedulingChanges- operation order and pathsCandidateResult- carries a rejection as its error- Recover from rejected changes - getting the project working again