Skip to main content

C#

The .NET package reads the same .tdc config and, for the same seed, produces the same output as the TypeScript, Python, Java and Rust implementations — byte for byte, on all three engines and in Parquet.

Two packages: the library, and the command line as a dotnet tool.

Getting it

On NuGet — version 0.1.7
dotnet add package Tdcv2

The starter data packs are embedded in the assembly, so it works with nothing else installed. The command line is its own tool package — install it globally and tdcv2 is on your PATH:

dotnet tool install --global Tdcv2.Cli

Using it

using Tdcv2;

var data = new Tdc("users.tdc");
Console.WriteLine(data);

foreach (Tdc.Row row in data.Rows())
{
Console.WriteLine(row["Gender"]);
}

data.WriteFile("users.csv");

Rows, not strings

A row is the reason to use the library rather than the command line. A test that asserts on row["Gender"] says what it means; the same test parsing CSV back out of a string spends most of its lines on the parsing.

Text output and row output read the same generated values, so the two can never disagree. The row view ignores <block> and the text wrappers entirely — those describe a file format, and a row has no format.

var data = new Tdc(new Tdc.Options
{
ConfigFile = "users.tdc",
Count = 100, // overrides what <env> declared
SeedValue = "test", // pins the run
});

Tdc.Row first = data[0];
Console.WriteLine(first["Address.city"]); // a compound's field
Console.WriteLine(first.Nested()["Address"]); // or the whole address at once

A sequence that does not apply to a row returns null, never "". A column declared parent="Gender.Male" has no value on a female row, and a blank would claim it had one that happened to be empty.

Options

ConfigFile / ConfigStringExactly one of the two
Count, SeedValue, LocaleOverride what <env> declared
NowMillisPin the clock, so a test asserting on a date does not expire overnight
PacksDir, DataPathsWhere packs and @data/… sources are found
BaseDirWhat a relative src= is relative to

Diagnostics carries anything the config was warned about but not refused for; errors are thrown from the constructor, so whatever is left there is worth saying and not worth stopping for. SeedInfo reports whether the seed was generated — an unseeded run is not reproducible, which is almost never what was wanted.

One value, without a config

Quick.Tdc draws a single value from the same data packs a config reads — no file, no <env>, one call:

using Tdcv2.Quick;

dynamic tdc = Quick.Tdc;

tdc.person.lastName(); // Jones
tdc.country.usa.docs.ssn(); // 699209702, with its real check digits
tdc.person.lastName.many(5); // five of them
Quick.Seed("demo").locale("ru").person.lastName(); // pinned and in Russian

This is the one part of the library that is dynamic, and deliberately: an address is a path through data rather than a fixed set of members, and a class per pack folder would put a hundred thousand lines of nothing in the assembly. The cost is that a misspelled address is caught when it runs, so the message it throws names the nearest real address. One value at a time is the whole surface.

Requirements

.NET 6.0 or newer. See the C# README for the places where .NET needed care the JVM did not — overflow, shifts, endianness, case mapping and locale data — each of which would otherwise have changed the bytes.