Skip to content

C# script ​

Runs your own C# code for every message that reaches the component. Use it to transform data between plugins, implement logic no plugin covers, or call .NET libraries (NuGet packages referenced in the flow configuration are available).

Pins ​

PinKindTypesDescription
InputinputanyThe data from the previous component. Its type is the type of the input connection.
OutputoutputanyWhatever the script passes to Next / NextAsync. The generated template creates an object of the output connection's type.

Configuration ​

FieldTypeDefaultDescription
Scriptcode (C#)emptyThe class body: the Execute (or ExecuteAsync) method plus any fields, properties and helper methods you need. Empty means the generated template is used.
Singletoneboolean ("Singleton")truetrue: one script instance is reused for all calls, so fields keep their values between messages. false: a new instance per message.
Asynchronousboolean ("Async/Await")falsetrue: the script derives from the async base class and must implement ExecuteAsync and call NextAsync.

Behaviour ​

  • The script is compiled into a class Script_<componentId> in the flow namespace (for example viauser), deriving from AbstractCshapScriptBase (or AbstractCshapScriptBaseAsync when Asynchronous is true). You write only the class body.
  • using statements may be written anywhere in the script; they are moved to the top before compiling. Already imported: System, System.Linq, System.Text, System.Collections, System.Collections.Generic, System.Threading, System.Threading.Tasks, Viaducta.TypeSystem, Viaducta.TypeSystem.Metadata, Viaducta.Plugin.Design, Viaducta.Plugin.Design.Scripting, Viaducta.Plugin.Design.Debugger, Viaducta.Plugins.CsharpScript and the flow namespace.
  • Method signatures:
    • sync: public override void Execute(IMetaData metaDataInstance, IViaType inputDataInstance)
    • async: public override async Task ExecuteAsync(IMetaData metaDataInstance, IViaType inputDataInstance)
    • (public void Execute(IMetaData ... without override is accepted and fixed automatically, for old scripts.)
  • The generated template (created when Script is empty, from the current connections):
    • casts the metadata: var metaData = (<namespace>.FlowMetaData)metaDataInstance;
    • casts the input to the type of the input connection, e.g. var input = (Customer)inputDataInstance; (AnyData uses inputDataInstance.Cast<...>(), NoInputData a direct cast, imported types their wrapper type);
    • creates the output from the output connection's type, var output = new Invoice();, and calls Next(metaData, output); (without an output connection it passes the input on).
  • Next(metaData, output) / await NextAsync(metaData, output) sends data to the components connected to Output. Pass an object of the output connection's type. Other objects are accepted too, but anything that is not a Viaducta type is wrapped as Viaducta.TypeSystem.Metadata.AnyData. Call Next zero, one or many times per input (filter, pass-through, split).
  • Services gives access to the flow services: Services.Log.Information("...") / Verbose / Warning / Error for logging, Services.Messaging.UpdateStatusMessage("..."), Services.Messaging.UpdateProgress(0.5f), Services.Settings.Get("<package>", "<name>") for expression values, Services.Assets, Services.TypesFactory, Services.CancellationToken.
  • Exceptions thrown by the script go to the flow's normal error handling (the component has no error pin).
  • With an empty Script the component shows a warning indicator: the template does nothing useful.

Notes ​

  • Changing Asynchronous does not rewrite an existing script: switch the method to ExecuteAsync / NextAsync (or back) yourself, or clear the script to regenerate the template; otherwise compilation fails.
  • The template is generated from the connections that exist when it is created. Connect input and output first, then open the editor (or clear the script) to get the right casts.
  • With Singletone = true the same instance serves concurrent messages; fields are shared between parallel executions, so keep them thread-safe or use local variables.
  • Do not hard-code secrets in the script; read them from settings (Services.Settings) instead.
  • A script can group its output into a batch for batch consumers (Build array, Bulk insert, Write file): await using var batch = Services.Batches.Begin(metaData, "label"); then pass batch.MetaData to Next / NextAsync and call await batch.CompleteAsync() at the end; leaving the scope without completing fails the batch.

Example ​

Input connection type Customer, output connection type CustomerDto:

csharp
using System.Globalization;

public override void Execute(IMetaData metaDataInstance, IViaType inputDataInstance)
{
    var metaData = (viauser.FlowMetaData)metaDataInstance;
    var input = (Customer)inputDataInstance;
    if (String.IsNullOrEmpty(input.Email)) return; // filter: nothing is sent on
    var output = new CustomerDto();
    output.Name = CultureInfo.InvariantCulture.TextInfo.ToTitleCase(input.Name ?? "");
    output.Email = input.Email.ToLowerInvariant();
    Services.Log.Information($"Customer {output.Email} mapped");
    Next(metaData, output);
}

Asynchronous variant (Asynchronous = true):

csharp
public override async Task ExecuteAsync(IMetaData metaDataInstance, IViaType inputDataInstance)
{
    var metaData = (viauser.FlowMetaData)metaDataInstance;
    var input = (Customer)inputDataInstance;
    await Task.Delay(100, Services.CancellationToken);
    await NextAsync(metaData, input);
}

Http listener → Json deserializer → C# script → Database writer

Viaducta documentation