Noundry.Tuxedo.Cufflink

Fake Data Generation & Database Seeding for Tuxedo ORM

Cufflink is a fake data generation library and CLI tool that uses Bogus to generate realistic test data based on your Tuxedo entity models. It automatically detects foreign key relationships, performs topological sorting for correct insert order, and uses bulk operations for high-performance data loading across SQL Server, PostgreSQL, MySQL, and SQLite.

dotnet add package Noundry.Tuxedo.Cufflink
dotnet tool install --global Noundry.Tuxedo.Cufflink.CLI

Key Features

Intelligent Data Generation

Uses Bogus to generate realistic data based on property names: Email, FirstName, Phone, Address, Price, and more.

FK/PK Relationship Aware

Automatically detects foreign keys by naming convention (CategoryId -> Category) for int, long, and Guid types.

Topological Sort

Determines correct insert order based on FK dependencies. Parent tables are populated before child tables.

JSON/JSONB Column Support

Generate fake JSON data via auto-detection, JSON schemas, or custom generators.

Three Usage Modes

Interactive wizard CLI, command-line mode for automation, and programmatic library usage.

Bulk Insert Performance

Uses Tuxedo's high-performance bulk operations with configurable batch sizes.

CLI Quick Start

# Install the CLI tool
dotnet tool install --global Noundry.Tuxedo.Cufflink.CLI

# Launch interactive wizard (recommended for beginners)
cufflink wizard

# Generate 1000 fake records per table
cufflink generate --records 1000

# Specify database provider and connection string
cufflink generate --records 500 \
 --provider SqlServer \
 --connection-string "Server=.;Database=TestDb;Integrated Security=true"

# Generate with JSON schema support
cufflink generate --records 1000 --json-schema-dir ./schemas

# Show information about discovered models
cufflink info

Library Usage

Generate Fake Data

using Noundry.Tuxedo.Cufflink;
using Noundry.Tuxedo.Cufflink.Core;

var engine = new CufflinkEngine();

// Generate from assembly (scans for [Table] decorated classes)
await engine.GenerateFakeAsync(
 assembly: typeof(Program).Assembly,
 recordCount: 1000,
 connectionString: "Data Source=app.db",
 provider: DatabaseProvider.SQLite);

// Generate for specific model types
var types = new[] { typeof(Category), typeof(Product) };
await engine.GenerateFakeAsync(
 modelTypes: types,
 recordCount: 500,
 connectionString: "Data Source=app.db",
 provider: DatabaseProvider.SQLite);

Seed Predefined Data

var seedData = new Dictionary<Type, List<object>>
{
 {
 typeof(Category),
 new List<object>
 {
 new Category { Id = 1, Name = "Electronics" },
 new Category { Id = 2, Name = "Books" }
 }
 },
 {
 typeof(Product),
 new List<object>
 {
 new Product { Id = 1, Name = "Laptop", Price = 999.99m, CategoryId = 1 },
 new Product { Id = 2, Name = "Novel", Price = 14.99m, CategoryId = 2 }
 }
 }
};

await engine.SeedAsync(
 seedData: seedData,
 connectionString: "Data Source=app.db",
 provider: DatabaseProvider.SQLite);

Use in Program.cs

var builder = WebApplication.CreateBuilder(args);
// ... configure services ...
var app = builder.Build();

// Generate test data in development
if (app.Environment.IsDevelopment())
{
 var engine = new CufflinkEngine();
 await engine.GenerateFakeAsync(
 assembly: typeof(Program).Assembly,
 recordCount: 100,
 connectionString: builder.Configuration.GetConnectionString("DefaultConnection")!,
 provider: DatabaseProvider.SQLite);
}

app.Run();

How It Works

1. Model Discovery

Cufflink scans assemblies for classes decorated with [Table] attributes from Tuxedo. Use ModelScanner.ScanType() for single types or ModelScanner.ScanCurrentDirectory() for all DLLs.

2. Relationship Analysis

Foreign keys are detected automatically by naming convention -- properties ending in "Id" (e.g., CategoryId references Category). Supports int, long, and Guid FK types. Nullable FKs require the [ForeignKey] attribute from Bowtie.

3. Topological Sort

Tables are sorted by dependency order. Parent tables (no FKs) are inserted first, then child tables that reference them.

Categories (no dependencies)
 |
Products (depends on Categories)
 |
Orders (depends on Products)

4. Intelligent Data Generation

Bogus generates realistic data based on property names and types:

public class User
{
 public string Email { get; set; } // -> "john.doe@example.com"
 public string FirstName { get; set; } // -> "John"
 public string LastName { get; set; } // -> "Doe"
 public string Phone { get; set; } // -> "(555) 123-4567"
 public string Address { get; set; } // -> "123 Main Street, City, ST 12345"
 public decimal Price { get; set; } // -> Random decimal
 public DateTime CreatedAt { get; set; }// -> Recent past date
}

5. Bulk Insert

Uses Tuxedo's high-performance bulk operations to insert data efficiently in configurable batch sizes.

Model Examples

using Noundry.Tuxedo.Contrib;

[Table("Categories")]
public class Category
{
 [Key]
 public int Id { get; set; }
 public string Name { get; set; } = string.Empty;
}

[Table("Products")]
public class Product
{
 [Key]
 public int Id { get; set; }
 public string Name { get; set; } = string.Empty;
 public decimal Price { get; set; }

 // Automatically detected as FK to Categories.Id (by naming convention)
 public int CategoryId { get; set; }

 // Guid FK also detected automatically
 public Guid CreatedById { get; set; }

 // Nullable FK: NOT detected -- use [ForeignKey] attribute
 public int? OptionalCategoryId { get; set; }
}

JSON/JSONB Column Support

Cufflink generates fake JSON data for columns detected as JSON types. Three approaches:

Auto-Generation

Automatically detects JSON columns by naming convention (Metadata, Settings, Config, Data, etc.).

JSON Schemas

Provide JSON schema files in a directory for structured fake JSON generation.

Custom Generators

Use the [Column(TypeName = "jsonb")] attribute to explicitly mark JSON columns.

[Table("Products")]
public class Product
{
 [Key]
 public int Id { get; set; }
 public string Name { get; set; } = string.Empty;

 // Auto-detected as JSON by name
 public string Metadata { get; set; } = "{}";

 // Explicitly marked as JSONB
 [Column(TypeName = "jsonb")]
 public string Settings { get; set; } = "{}";
}

Extension Method

// Quick way to generate data from ServiceProvider
await services.BuildServiceProvider()
 .GenerateFakeDataAsync(
 recordCount: 1000,
 provider: DatabaseProvider.SQLite);