Lyo.Csv 1.0.1

dotnet add package Lyo.Csv --version 1.0.1
                    
NuGet\Install-Package Lyo.Csv -Version 1.0.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Lyo.Csv" Version="1.0.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Lyo.Csv" Version="1.0.1" />
                    
Directory.Packages.props
<PackageReference Include="Lyo.Csv" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Lyo.Csv --version 1.0.1
                    
#r "nuget: Lyo.Csv, 1.0.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Lyo.Csv@1.0.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Lyo.Csv&version=1.0.1
                    
Install as a Cake Addin
#tool nuget:?package=Lyo.Csv&version=1.0.1
                    
Install as a Cake Tool

Lyo.Csv

Owned implementation of Lyo.Csv.Models. CsvService composes a CsvWriter and CsvReader over an internal field tokenizer/writer and cached type binder. Multi-targets net10.0;netstandard2.0; async, streaming, and option-based overloads are only available on net10.0.

Features

  • Strongly-typed read/write via IEnumerable<T> / List<T> and IAsyncEnumerable<T> (net10).
  • Row/column dictionary (IReadOnlyDictionary<int, IReadOnlyDictionary<int, string>>) read and write.
  • Lyo.DataTable.Models.DataTable round-trip including Footer (ParseFileAsDataTable with hasFooterRow, ExportToCsvFromDataTable always appends footer when present, plus an HTML helper ExportToHtmlTable).
  • CSV → DataTable pooling via CsvOptions.Pooling / CsvParseOptions.Pooling (defaults PoolValues=false; estimate is cols × (rows+1) after the full CSV is buffered).
  • Selected-property export (IReadOnlyList<PropertyInfo>), custom-header export (IReadOnlyDictionary<string, PropertyInfo>), and formatter export (IReadOnlyDictionary<string, Func<T, string>>).
  • URL download helpers (ParseFromUrl*) that share an optional injected HttpClient.
  • Append, combine, and split file operations (async, net10.0 only).
  • Streaming reads (IAsyncEnumerable<T> and string-row streams), IAsyncEnumerable exports, chunked processing, statistics, schema validation, column-mapping parses, and CSV-to-CSV comparison.
  • Dialect via CsvOptions: delimiter, quote, escape, comments, trim, blank-line skip, column-count detection, culture, encoding.
  • Header rename via [CsvColumn]; encoding via SetEncoding / SetOptions; CodePagesEncodingProvider registered in the service constructor.
  • Bundled ICsvValueConverter implementations: DecimalCsvConverter, Int32CsvConverter, Int64CsvConverter, YesNoBoolCsvConverter.
  • CsvErrorCodes constants (CSV_EXPORT_FAILED, CSV_PARSE_FAILED, CSV_OPERATION_CANCELLED, CSV_FILE_OPERATION_FAILED, CSV_VALIDATION_FAILED) used when wrapping failures in Result<T>.

Examples

Register with DI

using Lyo.Csv;
using Lyo.Csv.Models;

services.AddCsvService();
services.AddCsvService(o => {
    o.Pooling.PoolValues = true; // opt in for high-duplication grids
    o.Pooling.PoolingCellThreshold = 512;
    o.Delimiter = ";";
    o.HasHeaderRecord = true;
    o.AllowComments = true;
});

services.AddCsvService(new CsvOptions { Delimiter = "|", Quote = '"', Escape = '"' });

services.AddCsvService((provider, options) => {
    var feature = provider.GetRequiredService<IFeatureFlags>();
    options.Delimiter = feature.UseSemicolons ? ";" : ",";
});

Quick start

public sealed class ReportingService(ICsvService csv)
{
    public async Task ExportAsync(IEnumerable<Person> rows, Stream output, CancellationToken ct)
        => await csv.ExportToCsvStreamAsync(rows, output, ct);

    public async Task<List<Person>> ImportAsync(string path, CancellationToken ct)
        => await csv.ParseFileAsync<Person>(path, ct);
}

public sealed record Person(int Id, string Name, int Age);

Options and column attributes

csv.SetEncoding(new UTF8Encoding(encoderShouldEmitUTF8Identifier: true));
csv.SetOptions(new CsvOptions {
    Delimiter = ";",
    TrimFields = true,
    IgnoreBlankLines = true,
    AllowComments = true,
    Comment = '#',
});

public sealed class Person
{
    [CsvColumn(Ignore = true)]
    public int Id { get; set; }

    [CsvColumn("Full Name")]
    public string Name { get; set; }

    [CsvColumn("Years Old")]
    public int Age { get; set; }
}

Column shaping

IReadOnlyList<PropertyInfo> selected = [
    typeof(Person).GetProperty(nameof(Person.Name))!,
    typeof(Person).GetProperty(nameof(Person.Age))!,
];
csv.ExportToCsv(rows, selected, "out.csv");

IReadOnlyDictionary<string, PropertyInfo> namedColumns = new Dictionary<string, PropertyInfo> {
    ["Full Name"] = typeof(Person).GetProperty(nameof(Person.Name))!,
    ["Years"] = typeof(Person).GetProperty(nameof(Person.Age))!,
};
await csv.ExportToCsvStreamAsync(rows, namedColumns, stream, ct);

IReadOnlyDictionary<string, Func<Person, string>> formatters = new Dictionary<string, Func<Person, string>> {
    ["Display"] = p => $"{p.Name} ({p.Age})",
    ["Id"] = p => p.Id.ToString("D6"),
};
await csv.ExportToCsvStreamAsync(rows, formatters, stream, ct);

DataTable, dictionary, and HTML helpers

Result<DataTable> parsed = csv.ParseFileAsDataTable("in.csv", hasHeaderRow: true, hasFooterRow: true);
csv.ExportToCsvFromDataTable(parsed.ValueOrThrow(), "out.csv"); // writes Footer as trailing row when present

var grid = csv.ParseFileAsDictionary("in.csv");
csv.ExportToCsvFromDictionary(grid, "out.csv", hasHeaderRow: true, hasFooterRow: true);

string html = csv.ExportToHtmlTable(File.ReadAllBytes("in.csv"), hasHeaderRow: true, hasFooterRow: true);

Streaming, options, statistics, validation, comparison

await foreach (var row in csv.ParseFileStreamingAsync<Person>("big.csv", new CsvParseOptions {
    ContinueOnError = true,
    OnError = err => log.LogWarning("Row {Row}: {Message}", err.RowNumber, err.Message),
    RowFilter = cells => cells["Status"] == "active",
    MaxRows = 100_000,
}, ct)) {
    // process row
}

await foreach (var cells in csv.ParseFileRowsStreamingAsync("big.csv", ct)) {
    // untyped string cells
}

await csv.ExportToCsvStreamAsync(asyncRows, stream, ct);

CsvStatistics stats = await csv.GetStatisticsAsync("big.csv", ct);

await csv.ProcessFileInChunksAsync<Person>(
    "big.csv",
    chunkSize: 1_000,
    processChunk: async batch => await sink.WriteAsync(batch),
    ct: ct);

ValidationResult validation = await csv.ValidateAsync("in.csv", new CsvSchema {
    RequireAllColumns = true,
    AllowExtraColumns = false,
    Columns = [
        new CsvColumn { Name = "Id", IsRequired = true },
        new CsvColumn { Name = "Name", IsRequired = true },
    ],
}, ct);

CsvComparisonResult diff = await csv.CompareFilesAsync("v1.csv", "v2.csv", keyColumn: "Id", ct);

Append / combine / split (net10.0)

await csv.AppendToCsvAsync(rows, "log.csv", includeHeaderIfMissing: true, ct);
await csv.CombineCsvFilesAsync(parts, "merged.csv", includeHeaders: true, ct);
await csv.SplitCsvFileAsync("merged.csv", rowsPerFile: 10_000, outputDirectory: "chunks", ct);

Benchmarks

UTF-8 export of 100,000 sample rows in tens of milliseconds.

Dependency injection

AddCsvService registers a singleton CsvService and routes ICsvService, ICsvWriter, and ICsvReader to the same instance. Overloads accept Action<CsvOptions>, an options instance, Action<IServiceProvider, CsvOptions>, and AddCsvServiceFromConfiguration (binds Csv and optional DataTablePooling sections). Default CSV pooling remains off.

Output targets

Each export path is available as file / stream / TextWriter / string / byte array overloads, in sync (all TFMs) and async (net10.0) flavors:

csv.ExportToCsv(rows, "out.csv");
csv.ExportToCsvStream(rows, stream);
csv.ExportToCsv(rows, textWriter);
string text = csv.ExportToCsvString(rows);
byte[] bytes = csv.ExportToCsvBytes(rows);

await csv.ExportToCsvAsync(rows, "out.csv", ct);
await csv.ExportToCsvStreamAsync(rows, stream, ct);
string textAsync = await csv.ExportToCsvStringAsync(rows, ct);
byte[] bytesAsync = await csv.ExportToCsvBytesAsync(rows, ct);

URL and batch

Result<DataTable> table = await csv.ParseFromUrlAsDataTableAsync(url, hasHeaderRow: true, hasFooterRow: true, ct);
List<Person> rows = await csv.ParseFromUrlAsync<Person>(url, ct);

IReadOnlyList<Result<DataTable>> results =
    await csv.BatchParseFilesAsDataTableAsync(paths, hasHeaderRow: true, hasFooterRow: true, ct);

If you do not supply an HttpClient to the constructor, a fresh one is created per URL call and disposed afterward; production callers should inject one via DI. Pass hasFooterRow: true when the last physical row should become DataTable.Footer (default false).

Dialect defaults

Default CsvOptions use comma delimiter, RFC doubled quotes, header row on, blank-line skip, field trim, and DetectColumnCountChanges (throws CsvBadDataException on uneven rows). Header names match properties case-insensitively after trim; rename with [CsvColumn]. Prefer typed/IAsyncEnumerable streams for large files — Parse*AsDataTable* materializes the full grid.

Public API (generated)

  • CsvErrorCodes
  • CsvService
  • DecimalCsvConverter
  • Extensions
  • Int32CsvConverter
  • Int64CsvConverter
  • IsExternalInit
  • YesNoBoolCsvConverter

License

Copyright © Lyo

Dependencies

Generated from ProjectReference / PackageReference (same model as docs/Lyo.ProjectGraph.html).

  • Lyo.Common — (direct, lyo)
  • Lyo.Csv.Models — (direct, lyo)
  • Lyo.Exceptions — (direct, lyo)
  • Lyo.Result — (direct, lyo)
  • Microsoft.Extensions.Configuration 10.0.5 — (direct, microsoft)
  • Microsoft.Extensions.Configuration.Binder 10.0.5 — (direct, microsoft)
  • Microsoft.Extensions.DependencyInjection.Abstractions 10.0.5 — (direct, microsoft)
  • Microsoft.Extensions.Logging.Abstractions 10.0.5 — (direct, microsoft)
  • Microsoft.Extensions.Options 10.0.5 — (direct, microsoft)
  • System.Text.Encoding.CodePages 10.0.5 — (direct, microsoft)
  • Lyo.DataTable.Models — (transitive, lyo)
  • System.Memory 4.6.3 — (transitive, microsoft, netstandard2.0)
  • System.Text.Json 10.0.5 — (transitive, microsoft, netstandard2.0)
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (2)

Showing the top 2 NuGet packages that depend on Lyo.Csv:

Package Downloads
Lyo.Reporting.Postgres

PostgreSQL reporting data layer with EF Core, optional auto-migrations, and report generation orchestration.

Lyo.Api.Export.Csv

CSV export format handler for Lyo.Api export service.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.1 31 8/18/2026
1.0.0 97 8/16/2026