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.3.2
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.

The same names in every language

The object a run hands back answers to the same names in all five packages, spelled each language's own way. The table is here, and the test suites check it rather than take it on trust.

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.