Download Reportworq
⬇ Guide PDF

Script API reference#

This is the reference for what a Reportworq .csx script can reach. For how to attach one to a job, see The Script Runner.

Script file format#

Scripts are plain C# statements. No class, namespace or Main method is required, top-level code runs directly, and await is fully supported.

// Minimal example. No using statements required for the pre-imported namespaces.
var sheet = Workbook.Worksheets["Summary"];
sheet.Cells["A1"].PutValue("Generated by Reportworq");
sheet.Cells["A2"].PutValue(DateTime.Now.ToString("yyyy-MM-dd HH:mm"));
sheet.AutoFitColumns();
Logger.LogInformation("Summary stamped for {OutputName}.", OutputName);

Compilation environment#

These namespaces are pre-imported, so you do not need using statements for them:

Anything else needs an explicit using at the top of the script, for example System.IO or System.Net.Http.

These assemblies are explicitly referenced: Aspose.Cells, Aspose.Slides, Microsoft.Extensions.Logging, the .NET base library, and System.Linq. Every other assembly already loaded by the Reportworq process is also reachable with a using.

NuGet packages cannot be added at run time.

Globals available in every script#

Name Type Notes
JobName string Name of the distribution job
OutputName string Tokenised output file name for the current runtime step
Parameters IReadOnlyDictionary<string, string> Resolved job parameter values
HookName string The hook currently firing, for example "AfterCalculation"
EntryName string This script's label, for example "#1 validate_totals.csx"
Logger ILogger Routes into the job history log

Hook-dependent objects#

Name Type Non-null in
Workbook Aspose.Cells.Workbook Before Calculation, After Calculation, After Packet Generation
Presentation Aspose.Slides.Presentation After PowerPoint
JobResult ScriptJobResult After Send

All three are null in every other hook. A script attached to more than one hook must check before use:

if (Workbook == null)
{
    AddWarning("No workbook at this hook, skipping.");
    return;
}

To branch on the current hook, read HookName:

if (HookName == "AfterCalculation")
{
    // workbook work
}
else if (HookName == "AfterSend")
{
    // check JobResult
}

Logging and control flow#

Member Effect
Logger.LogInformation / LogWarning / LogError Writes to the job history log with the [Script/Hook/#N file.csx] prefix
AddWarning(string message) Logs a warning. Execution continues.
AddError(string message) Logs an error and immediately halts the job. No further scripts and no further pipeline steps run.

AddError is the mechanism for script-controlled validation that must stop the pipeline. It throws, so nothing after it in your script runs either.

Calling AddError in After Send is valid but cannot undo a send. The files have already gone out. It records the failure in the job log; it does not recall anything.

Shared state across hooks#

One JobState dictionary is allocated per job and threaded into every script at every hook. A value written in Before Calculation on report 2 is readable in After Send.

Method Signature Behavior
SetValue void SetValue(string key, object value) Stores a value. A null or empty key is silently ignored. A null value is stored but reads as absent.
GetValue<T> T GetValue<T>(string key, T defaultValue) Returns the value if present and assignable to T, otherwise the default. Strict type match, no coercion: store an int, ask for a long, get the default. Never throws.
HasValue bool HasValue(string key) True only for a stored non-null value.
RemoveValue void RemoveValue(string key) Silent no-op when the key is missing.
// In a Before Calculation script:
SetValue("report_start", DateTime.UtcNow);

// In an After Send script, a different row in the same job:
var started = GetValue<DateTime>("report_start", DateTime.MinValue);
Logger.LogInformation("Job took {Elapsed}.", DateTime.UtcNow - started);

Thread-safe file append#

AppendLineWithRetry writes one line to a file, tolerating transient IOException. Use it instead of File.AppendAllLines whenever concurrent burst job steps might write to the same file.

void AppendLineWithRetry(
    string path,
    string line,
    int maxAttempts = 8,
    int initialDelayMs = 25)

It retries with jittered exponential backoff, per-attempt delays start at 25 ms and double, capped at one second each. It opens the file with read sharing so a tailing reader does not block it. The final attempt lets the exception escape, so a genuinely stuck file still surfaces as an error.

// After Send, safe for concurrent burst steps writing to a shared CSV
var line = $"{DateTime.UtcNow:O},{JobName},{OutputName},{JobResult.Success}";
AppendLineWithRetry(@"\\server\logs\reportworq_audit.csv", line);

The After Send job result#

JobResult is populated only in After Send. All properties are read-only.

Property Type Meaning
Success bool Every distribution step completed without error
HasCalcWarnings bool Any report sheet had calculation warnings
OutputFiles IReadOnlyList<string> Names of all output files prepared for distribution
Errors IReadOnlyList<string> Errors collected during the job, empty on success
StartTime DateTime? UTC job start
EndTime DateTime? UTC job end
Elapsed TimeSpan Total elapsed time
Status string For example Complete, Failed, Cancelled
StepName string Runtime step name. Includes the contact name on a burst job.
if (!JobResult.Success)
{
    var errors = string.Join("; ", JobResult.Errors);
    Logger.LogWarning("Job failed in {Elapsed:g}: {Errors}", JobResult.Elapsed, errors);
}
else
{
    Logger.LogInformation("Job succeeded in {Elapsed:g}. Files: {Files}",
        JobResult.Elapsed, string.Join(", ", JobResult.OutputFiles));
}

Error handling#

Scenario Behavior
AddError called in any hook Job halts immediately, remaining scripts and pipeline steps skipped, error recorded in job history
AddError called in After Send Same, but the sends already happened
Unhandled runtime exception Wrapped and propagated, halting the job
Compilation error Thrown before execution begins, job halts
Job canceled Cancellation propagates, job marked canceled, scripts stop at their next await
Any other exception in After Send Caught and logged, job result recording continues

Script C# is not quite file C##

A .csx is compiled as a script, not as a source file, and the grammar differs in one place that bites immediately.

You cannot use a top-level using declaration. At the start of a statement, the script parser reads using as a using directive, so the C# 8 form fails to compile:

using var http = new HttpClient();   // does NOT compile in a .csx

The parser binds using var as a directive importing a namespace called var, then expects ; and finds the variable name instead, reporting CS1002 at the identifier. Adding a semicolon does not help, because the semicolon is not what is missing.

Write it as a using statement, or drop the using entirely:

using (var http = new HttpClient())   // compiles
{
    // ...
}

var http = new HttpClient();          // also compiles

Using directives at the top of the file are fine, and are how you reach namespaces that are not pre-imported. It is only the inline using var x = ... declaration form that the script grammar rejects.

Cell is ambiguous#

Both Aspose.Cells and Aspose.Slides are pre-imported, and each defines a type called Cell. Using the bare name fails to compile:

foreach (Cell cell in sheet.Cells)              // does NOT compile: CS0104, ambiguous
foreach (Aspose.Cells.Cell cell in sheet.Cells) // compiles

Qualify it. The same applies to any other type name the two libraries share.

Cells is a non-generic collection#

Worksheet.Cells implements the non-generic IEnumerable, so var in a foreach over it binds as object and members like .IsFormula and .Value will not resolve. State the element type explicitly, qualified as above.

HTTP calls#

System.Net.Http.HttpClient is available. The hook blocks until the call completes, so always set a timeout.

using System.Net.Http;

using (var http = new HttpClient { Timeout = TimeSpan.FromSeconds(10) })
{
    var response = await http.PostAsync(url, content);
}

Note the using (...) statement form rather than using var, for the reason above.

Log format#

Each execution is bracketed by the runner:

[Script/AfterCalculation/#1 validate.csx] Executing script for job 'Monthly Sales', output 'Monthly Sales - East'.
[Script/AfterCalculation/#1 validate.csx] Script completed in 42 ms.

AddWarning and AddError use the same prefix:

[Script/AfterCalculation/#1 validate.csx] Summary!B2 is zero or blank, job halted.

Entries appear in the job history log for that job step.

Feedback on this page

Comments, questions, requests, or something missing or unclear? Email us - the page you are on is filled in for you.

Email feedback on this page

Or write to support@reportworq.com directly.