CSV
Read and map CSV data in Elsa workflows with the 3.8.0 CSV extension.
The Elsa.Data.Csv extension adds the ReadCsv activity. Use it when a workflow needs to turn delimited text or an uploaded file into rows that later activities can inspect, validate, transform, or dispatch.
The activity runs on the Elsa Server. Studio can design a workflow containing it when the connected server registers the extension, but Studio does not parse CSV data itself.
Install and register the extension
Install the package in the server project that executes the workflow:
dotnet add package Elsa.Data.Csv --version 3.8.0Register the feature during Elsa startup:
using Elsa.Extensions;
builder.Services.AddElsa(elsa =>
{
elsa.UseCsv();
});UseCsv registers the extension feature, which discovers the ReadCsv activity in the Elsa / Data category. There is no CSV connection, database, or provider to configure.
Configure ReadCsv
The activity has one data input, three parsing inputs, and one output:
CsvData
object?
string, UTF-8 byte[], Stream, or IFormFile; other values use ToString().
HasHeaderRecord
bool
Treats the first row as column names. Defaults to true.
RecordType
Type?
Optional CLR type used for strongly typed row mapping through CsvHelper.
Delimiter
string?
Separates fields. Defaults to ,; empty, \\t, and tab have defined fallbacks.
Records
IList<object>
Contains dictionaries when RecordType is empty, or mapped objects when it is set.
Choose the input form based on where the data already lives:
Use a
stringwhen a previous activity or expression already produced CSV text.Use a UTF-8
byte[]when the workflow receives encoded file content.Use a
StreamorIFormFilefor server-side file or upload handling.
The activity does not download URLs. A URL supplied as a string is parsed as the literal CSV text of that string. This is also true even though the release package README lists URLs among the supported string inputs; the implementation does not contain a URL-fetch path. If the workflow must retrieve a remote file, download it with an HTTP or storage integration first, then pass the resulting bytes or stream to ReadCsv.
Example: parse header-based rows
Given this input:
configure these activity inputs:
CsvData
The CSV text above, or an expression that returns it
HasHeaderRecord
true
Delimiter
,
RecordType
Leave empty
Records contains dictionaries similar to:
Without RecordType, field values are read as strings. With headers enabled, the dictionary keys are the header values returned by CsvHelper. If the input has no header row, set HasHeaderRecord to false; rows then use keys such as Field1, Field2, and so on.
Example: map rows to a CLR type
Set RecordType to a type whose members match the CSV columns:
When RecordType is typeof(OrderRow), the activity asks CsvHelper to map each row to OrderRow and returns the mapped objects in Records. Use a CsvHelper class map or supported mapping attributes when column names do not match the type members. Conversion and mapping errors are not converted into an alternate activity outcome; they fault the activity and should be handled through the workflow's normal fault and incident strategy.
Typed mapping is useful when later activities need numeric, date, or domain properties. Keep the input as dictionaries when the columns are dynamic or when the workflow only needs to route or validate raw values.
Delimiters and headers
The release passes the normalized delimiter and header setting to CsvHelper:
Comma-separated data works with the default
,.Semicolon-separated data can use
;.Tab-separated data can use either the two-character value
\\tortab.Any non-empty delimiter string is passed through to CsvHelper.
Set
HasHeaderRecordtofalsefor positional rows. Do not leave it at its default when the first row is data rather than column names.
The parser uses CultureInfo.InvariantCulture. Test typed numeric and date columns with representative input from the producing system, especially when that system formats values using locale-specific conventions.
Runtime and memory behavior
ReadCsv reads the complete input into text, parses every row, and stores the complete result in a list before completing. It is therefore best suited to bounded files and batch-sized workflow inputs, not unbounded data streams.
The stream lifetime is an important server-side detail: the activity wraps a supplied Stream in a StreamReader and disposes the reader after reading, which also closes the supplied stream. Pass a stream whose ownership can end at this activity, or create a separate stream when the caller must keep it open. IFormFile streams are opened and disposed by the activity.
There is no built-in size limit, pagination, or incremental output. Enforce upload and request limits at the host, validate the expected encoding and delimiter before processing, and reject files that are too large for the workflow worker's memory budget.
Studio and security boundaries
Register
Elsa.Data.Csvon the server that executes workflows. Registering the package only in a Studio host does not make the activity executable.In Studio, search the Data category for
ReadCsvafter the server has registered the feature and exposed its activity descriptors.The activity does not fetch URLs, but it can process bytes, streams, and uploaded files supplied by the server. Apply authentication and authorization before accepting untrusted uploads or workflow inputs.
Treat CSV content as untrusted data. Validate size, encoding, expected columns, and values before using them to construct requests, file paths, or database commands.
If parsed values are later exported to a spreadsheet, apply your export layer's protections against formula injection;
ReadCsvitself only parses the input.
Troubleshooting checklist
Confirm
Elsa.Data.Csvis installed in the workflow execution host and.UseCsv()runs during startup.If Studio cannot find the activity, verify it is connected to that server and that the server exposes the registered activity descriptors.
Check
HasHeaderRecordandDelimiterbefore changing the input data.If rows contain
Field1keys, the activity is running without a header row; setHasHeaderRecordtotruewhen the first row contains names.For typed mapping failures, verify column names, member types, and the source system's number/date formatting.
For empty output, check that
CsvDatais not null or an unintended URL string, and confirm that the input contains at least one data row.
Release source
This page is validated against the release/3.8.0 Extensions source:
Last updated