Noundry.Tuxedo.Cufflink.CLI
0.4.0
dotnet tool install --global Noundry.Tuxedo.Cufflink.CLI --version 0.4.0
dotnet new tool-manifest
dotnet tool install --local Noundry.Tuxedo.Cufflink.CLI --version 0.4.0
#tool dotnet:?package=Noundry.Tuxedo.Cufflink.CLI&version=0.4.0
nuke :add-package Noundry.Tuxedo.Cufflink.CLI --version 0.4.0
Noundry.Tuxedo.Cufflink
Cufflink is a fake data generation library and CLI tool for Tuxedo ORM. It uses Bogus to generate realistic test data based on your entity models, automatically honoring foreign key relationships and primary key constraints.
Features
- ๐ฒ Intelligent Fake Data Generation: Uses Bogus to generate realistic data based on property names and types
- ๐ FK/PK Relationship Awareness: Automatically detects and honors foreign key relationships
- ๐ Bulk Insert Performance: Uses Tuxedo's bulk operations for efficient data loading
- ๐๏ธ Multi-Database Support: Works with SQL Server, PostgreSQL, MySQL, and SQLite
- ๐ JSON/JSONB Column Support: Generate fake JSON data using JSON schemas or auto-generation
- ๐ ๏ธ Three Usage Modes:
- Wizard Mode: Interactive CLI for easy configuration (recommended for beginners)
- Command Mode: Direct CLI commands for automation
- Library: Use programmatically in your
Program.csor test setup
- ๐ฏ Topological Sort: Automatically determines correct insert order based on dependencies
- ๐ฅ๏ธ Spectre.Console UI: Beautiful CLI interface with progress indication
Installation
As a Library
dotnet add package Noundry.Tuxedo.Cufflink
As a Global Tool
dotnet tool install --global Noundry.Tuxedo.Cufflink.CLI
Quick Start
CLI Usage
Wizard Mode (Recommended)
# Launch interactive wizard - easiest way to get started!
cufflink wizard
# The wizard will guide you through:
# 1. Discovering your entity models
# 2. Selecting which models to generate data for
# 3. Configuring JSON/JSONB columns (if any)
# 4. Setting record counts and database options
Command Mode
# 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;..."
# Generate with JSON schema support
cufflink generate --records 1000 --json-schema-dir ./schemas
# Generate for a specific directory
cufflink generate --records 100 --directory ./bin/Debug/net9.0
# Show information about discovered models
cufflink info
Library Usage
Example 1: Generate Fake Data
using Noundry.Tuxedo.Cufflink;
using Noundry.Tuxedo.Cufflink.Core;
using Noundry.Tuxedo.Contrib;
// Define your models using Tuxedo attributes
[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)
// Supports int, long, and Guid FK types
public int CategoryId { get; set; }
}
// Generate fake data
var engine = new CufflinkEngine();
await engine.GenerateFakeAsync(
assembly: typeof(Program).Assembly,
recordCount: 1000,
connectionString: "Data Source=app.db",
provider: DatabaseProvider.SQLite);
Example 2: 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);
Example 3: Use in Program.cs for Testing
var builder = WebApplication.CreateBuilder(args);
// ... configure services ...
var app = builder.Build();
// Generate test data in development environment
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();
Example 4: Generate for Specific Model Types
using Noundry.Tuxedo.Cufflink;
using Noundry.Tuxedo.Cufflink.Core;
var engine = new CufflinkEngine();
// Generate fake data for only the specified model types
var modelTypes = new[] { typeof(Category), typeof(Product) };
await engine.GenerateFakeAsync(
modelTypes: modelTypes,
recordCount: 500,
connectionString: "Data Source=app.db",
provider: DatabaseProvider.SQLite);
This overload is useful when you want to generate data for a subset of your models rather than scanning an entire assembly or directory.
How It Works
1. Model Discovery
Cufflink scans your assemblies for classes decorated with [Table] attributes from Tuxedo:
using Noundry.Tuxedo.Contrib;
[Table("Users")]
public class User
{
[Key]
public int Id { get; set; }
public string Email { get; set; } = string.Empty;
public string FirstName { get; set; } = string.Empty;
}
The ModelScanner class also exposes two convenience methods for advanced scenarios:
ModelScanner.ScanType(Type)-- Scans a single type and returns anEntityModelif it has a[Table]attribute, ornullotherwise. Used internally by theGenerateFakeAsync(IEnumerable<Type>, ...)overload.ModelScanner.ScanCurrentDirectory()-- Scans all DLL assemblies in the current working directory for entity models. Equivalent to callingScanDirectory(Directory.GetCurrentDirectory()).
2. Relationship Analysis
Cufflink automatically detects foreign key relationships using naming conventions - no attributes required!
- Primary Method: Properties ending in "Id" (e.g.,
CategoryIdโCategory) - Supported types:
int,long, andGuidFK columns are detected by convention - Optional:
[ForeignKey("TableName")]attribute (requires Bowtie package)
Note: Nullable FK columns (
int?,long?,Guid?) are not detected by convention. If you need nullable FK relationships, use the[ForeignKey("TableName")]attribute from Bowtie.
[Table("Products")]
public class Product
{
[Key]
public int Id { get; set; }
// โ
Automatically detected - CategoryId (int) references Category.Id
public int CategoryId { get; set; }
// โ
Automatically detected - ManufacturerId (long) references Manufacturer.Id
public long ManufacturerId { get; set; }
// โ
Automatically detected - CreatedById (Guid) references CreatedBy.Id
public Guid CreatedById { get; set; }
// โ NOT detected - nullable types require [ForeignKey] attribute
public int? OptionalCategoryId { get; set; }
}
// Optional: If you have Bowtie installed, you can use explicit attributes:
// [ForeignKey("Categories")]
// public int CategoryId { get; set; }
3. Topological Sort
Cufflink performs a topological sort to determine the correct insertion order based on FK dependencies:
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 int Age { get; set; } // โ Random int (18-80 for age)
public DateTime CreatedAt { get; set; }// โ Recent past date
}
5. Bulk Insert
Uses Tuxedo's high-performance bulk operations to insert data efficiently:
// Inserts 10,000 records in batches of 1000
await engine.GenerateFakeAsync(
recordCount: 10000,
batchSize: 1000,
...);
JSON/JSONB Column Support
Cufflink provides comprehensive support for generating fake data for JSON and JSONB columns in PostgreSQL, MySQL, SQL Server, and SQLite.
Three Ways to Generate JSON Data
1. Auto-Generation (Easiest)
Cufflink automatically detects properties that are likely JSON columns based on naming conventions:
[Table("Products")]
public class Product
{
[Key]
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
// โ
Auto-detected as JSON column (ends with "Metadata")
public string? Metadata { get; set; }
// โ
Also auto-detected: *Data, *Json, *Config, *Settings, *Attributes, *Properties
// โ
Contains-based detection: *jsonb* (e.g., JsonbPayload, UserJsonbData)
public string? ConfigData { get; set; }
}
Cufflink will generate simple JSON objects based on the property name:
// Product.Metadata will contain:
{
"key": "some-value",
"value": "another-value"
}
2. JSON Schema Files (Recommended)
Use JSON Schema files for full control over the structure and data types:
using Noundry.Tuxedo.Cufflink.Schema;
[Table("Products")]
public class Product
{
[Key]
public int Id { get; set; }
// ๐ Specify JSON schema file (relative to project or schemas/ directory)
[JsonSchema("product-metadata.json")]
public string? Metadata { get; set; }
}
schemas/product-metadata.json:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"sku": {
"type": "string",
"faker": "random.alphaNumeric"
},
"weight": {
"type": "number",
"minimum": 0.1,
"maximum": 100
},
"dimensions": {
"type": "object",
"properties": {
"length": { "type": "number", "minimum": 1, "maximum": 100 },
"width": { "type": "number", "minimum": 1, "maximum": 100 },
"height": { "type": "number", "minimum": 1, "maximum": 100 }
}
},
"tags": {
"type": "array",
"minItems": 1,
"maxItems": 5,
"items": {
"type": "string",
"faker": "commerce.productAdjective"
}
},
"inStock": {
"type": "boolean"
}
}
}
Generated JSON output:
{
"sku": "A7X9K2",
"weight": 45.7,
"dimensions": {
"length": 23.4,
"width": 12.8,
"height": 8.5
},
"tags": ["premium", "durable", "lightweight"],
"inStock": true
}
3. Inline Schemas
For simple cases, use inline JSON schemas:
[Table("Users")]
public class User
{
[Key]
public int Id { get; set; }
[JsonSchema(InlineSchema = @"{
""type"": ""object"",
""properties"": {
""theme"": { ""type"": ""string"", ""enum"": [""light"", ""dark""] },
""notifications"": { ""type"": ""boolean"" }
}
}")]
public string? Settings { get; set; }
}
Bogus Integration in JSON Schemas
Use the faker extension property to leverage Bogus formatters:
{
"type": "object",
"properties": {
"email": {
"type": "string",
"faker": "internet.email"
},
"fullName": {
"type": "string",
"faker": "name.fullName"
},
"company": {
"type": "string",
"faker": "company.companyName"
},
"city": {
"type": "string",
"faker": "address.city"
}
}
}
Supported Bogus formatters:
name.firstName,name.lastName,name.fullName,name.jobTitleinternet.email,internet.username,internet.url,internet.domainName,internet.ipaddress.street,address.city,address.state,address.zipCode,address.countryphone.phoneNumbercompany.companyNamecommerce.product,commerce.color,commerce.department,commerce.productAdjectiverandom.word,random.words,random.sentence,random.alphaNumeric
JSON Schema Format Support
Cufflink recognizes standard JSON Schema formats:
{
"email": { "type": "string", "format": "email" },
"website": { "type": "string", "format": "uri" },
"created": { "type": "string", "format": "date-time" },
"ipAddress": { "type": "string", "format": "ipv4" },
"id": { "type": "string", "format": "uuid" }
}
CLI Usage with JSON Schemas
Wizard Mode
cufflink wizard
# The wizard will:
# 1. Detect JSON columns automatically
# 2. Ask how you want to generate JSON data
# 3. Allow you to specify a schema directory
Command Mode
# Specify directory containing JSON schema files
cufflink generate --records 1000 --json-schema-dir ./schemas
# Schemas are matched by filename:
# - Metadata property โ metadata.json or product-metadata.json
# - ConfigData property โ config-data.json or configdata.json
Database-Specific Notes
| Database | Column Type | Notes |
|---|---|---|
| PostgreSQL | JSONB, JSON |
Native JSON support, use JSONB for better performance |
| MySQL | JSON |
Native JSON type (MySQL 5.7+) |
| SQL Server | NVARCHAR(MAX) |
Store as text, use ISJSON() and JSON_VALUE() functions |
| SQLite | TEXT |
Store as text, use json() functions |
Complete Example
using Noundry.Tuxedo.Contrib;
using Noundry.Tuxedo.Cufflink.Schema;
[Table("Products")]
public class Product
{
[Key]
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public decimal Price { get; set; }
public int CategoryId { get; set; } // FK to Categories
// JSON column with schema
[JsonSchema("product-metadata.json")]
public string? Metadata { get; set; }
// Auto-detected JSON column
public string? CustomAttributes { get; set; }
}
// Generate data
var engine = new CufflinkEngine();
await engine.GenerateFakeAsync(
assembly: typeof(Product).Assembly,
recordCount: 1000,
connectionString: "Host=localhost;Database=mydb;...",
provider: DatabaseProvider.PostgreSQL);
// Result: Products table with realistic JSON in Metadata and CustomAttributes columns
CLI Commands
wizard
Interactive wizard for generating fake data (recommended for beginners).
cufflink wizard [options]
Options:
-d|--directory <path>: Directory to scan for models (default: current directory)
Features:
- Step-by-step guided setup
- Automatic JSON column detection
- Visual model selection
- Connection string auto-detection
- Configuration summary before execution
generate
Generate fake data for all discovered models (command mode).
cufflink generate [options]
Options:
-r|--records <number>: Number of records per table (default: 100)-c|--connection-string <string>: Database connection string (auto-detected from appsettings.json if omitted)-p|--provider <SqlServer|PostgreSQL|MySQL|SQLite>: Database provider (default: SQLite)-d|--directory <path>: Directory to scan for models (default: current directory)-b|--batch-size <number>: Batch size for bulk inserts (default: 1000)--json-schema-dir <path>: Directory containing JSON schema files for JSON/JSONB columns
Examples:
# Generate 1000 records with auto-detected connection string
cufflink generate --records 1000
# SQL Server with explicit connection
cufflink generate -r 500 -p SqlServer -c "Server=.;Database=TestDb;Integrated Security=true"
# PostgreSQL
cufflink generate -r 200 -p PostgreSQL -c "Host=localhost;Database=testdb;Username=postgres;Password=pass"
# MySQL with custom directory
cufflink generate -r 100 -p MySQL -d ./bin/Release/net9.0
info
Display information about discovered models and their relationships.
cufflink info [options]
Options:
-d|--directory <path>: Directory to scan (default: current directory)
Example Output:
The banner is rendered using Spectre.Console's
FigletTextand will display a stylized "Cufflink" heading in blue in terminals that support it.
[Cufflink banner rendered via Spectre.Console FigletText]
Model Information
Found 3 entity models:
โญโโโโโโโโโโโโฌโโโโโโโโโโโฌโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโฎ
โ Table โ Type โ Properties โ Foreign Keys โ
โโโโโโโโโโโโโผโโโโโโโโโโโผโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโค
โ Categoriesโ Category โ 3 โ 0 โ
โ Products โ Product โ 6 โ 1 โ
โ Orders โ Order โ 4 โ 1 โ
โฐโโโโโโโโโโโโดโโโโโโโโโโโดโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโฏ
Insert Order (respecting FK dependencies):
1. Categories
2. Products
3. Orders
Supported Databases
Cufflink supports all databases that Tuxedo supports:
| Provider | Connection String Example |
|---|---|
| SQL Server | Server=.;Database=TestDb;Integrated Security=true;TrustServerCertificate=true |
| PostgreSQL | Host=localhost;Database=testdb;Username=postgres;Password=pass |
| MySQL | Server=localhost;Database=testdb;Uid=root;Pwd=pass; |
| SQLite | Data Source=test.db |
Configuration
appsettings.json
Cufflink can automatically detect connection strings from appsettings.json:
{
"ConnectionStrings": {
"DefaultConnection": "Data Source=app.db",
"SqlServer": "Server=.;Database=TestDb;...",
"PostgreSQL": "Host=localhost;Database=testdb;..."
}
}
Cufflink will search for connection strings in this order:
- Direct connection string (if provided via
-cflag orconnectionStringparameter) DefaultConnectionDatabase,DbConnection,SqlConnection, etc.- First available connection string
Programmatic JSON Schema Directory
When using Cufflink as a library, you can set the JSON schema base directory programmatically
using SetJsonSchemaDirectory. Schema file paths (from [JsonSchema("file.json")] attributes)
will be resolved relative to this directory.
var engine = new CufflinkEngine();
engine.SetJsonSchemaDirectory("/path/to/schemas");
await engine.GenerateFakeAsync(
assembly: typeof(Product).Assembly,
recordCount: 1000,
connectionString: "Data Source=app.db",
provider: DatabaseProvider.SQLite);
IServiceProvider Extension Method
Cufflink provides a GenerateFakeDataAsync extension method on IServiceProvider for convenient integration with ASP.NET Core applications. This method creates a CufflinkEngine internally and generates fake data using models discovered in the current directory.
using Noundry.Tuxedo.Cufflink;
var builder = WebApplication.CreateBuilder(args);
// ... configure services ...
var app = builder.Build();
// ASP.NET Core integration
if (app.Environment.IsDevelopment())
{
await app.Services.GenerateFakeDataAsync(
recordCount: 100,
connectionString: "Data Source=app.db",
provider: DatabaseProvider.SQLite);
}
app.Run();
Parameters:
recordCount(int): Number of records to generate per tableconnectionString(string?, optional): Database connection string. If omitted, auto-detected fromappsettings.jsonprovider(DatabaseProvider, optional): Database provider (default:SQLite)
Advanced Usage
Custom Seed Data Providers
public class CustomSeeder
{
public static Dictionary<Type, List<object>> GetSeedData()
{
return new Dictionary<Type, List<object>>
{
{ typeof(Category), GetCategories() },
{ typeof(Product), GetProducts() }
};
}
private static List<object> GetCategories()
{
return new List<object>
{
new Category { Id = 1, Name = "Electronics" },
new Category { Id = 2, Name = "Books" }
};
}
private static List<object> GetProducts()
{
return new List<object>
{
new Product { Id = 1, Name = "Laptop", CategoryId = 1 },
new Product { Id = 2, Name = "Novel", CategoryId = 2 }
};
}
}
// Use in seeding
var engine = new CufflinkEngine();
await engine.SeedAsync(
CustomSeeder.GetSeedData(),
connectionString,
DatabaseProvider.SQLite);
Integration with xUnit/NUnit Tests
[TestFixture]
public class ProductTests
{
private CufflinkEngine _cufflink = null!;
private string _connectionString = null!;
[SetUp]
public async Task Setup()
{
_connectionString = "Data Source=:memory:";
_cufflink = new CufflinkEngine();
// Create schema (using Bowtie or manual SQL)
// ...
// Generate test data
await _cufflink.GenerateFakeAsync(
assembly: typeof(Product).Assembly,
recordCount: 50,
connectionString: _connectionString,
provider: DatabaseProvider.SQLite);
}
[Test]
public async Task GetProducts_ReturnsData()
{
using var connection = new SqliteConnection(_connectionString);
var products = await connection.GetAllAsync<Product>();
Assert.That(products.Count(), Is.EqualTo(50));
}
}
Performance
Cufflink uses Tuxedo's bulk operations for high-performance data insertion:
| Records | Traditional Insert | Bulk Insert | Speedup |
|---|---|---|---|
| 100 | ~500ms | ~50ms | 10x |
| 1,000 | ~5s | ~200ms | 25x |
| 10,000 | ~50s | ~1s | 50x |
| 100,000 | ~500s | ~8s | 62x |
Tips for best performance:
- Use appropriate batch sizes (500-5000 depending on record size)
- Disable indexes before bulk insert (re-enable after)
- Use transactions
- Consider using SQLite for test data (fastest)
Comparison with Other Tools
| Feature | Cufflink | EF Core Seed Data | Bogus Alone |
|---|---|---|---|
| Automatic FK Resolution | โ | โ | โ |
| Multi-Database Support | โ | โ | โ |
| Bulk Insert Performance | โ | โ | โ |
| JSON/JSONB Support | โ | โ | โ |
| Interactive Wizard | โ | โ | โ |
| CLI Tool | โ | โ | โ |
| Library Usage | โ | โ | โ |
| Intelligent Data Gen | โ | โ | โ |
Requirements
- .NET 8.0, 9.0, or 10.0
- Noundry.Tuxedo (automatically installed)
- Bogus 35.6+ (automatically installed)
- NJsonSchema 11.0+ (automatically installed - for JSON schema support)
- System.Text.Json 8.0+ (automatically installed - for JSON serialization)
- Database provider packages (install as needed):
Microsoft.Data.SqlClientfor SQL ServerNpgsqlfor PostgreSQLMySqlConnectorfor MySQLMicrosoft.Data.Sqlitefor SQLite
- Optional: Noundry.Tuxedo.Bowtie (for
[ForeignKey]attribute support)
Contributing
Contributions are welcome! Please see the main Tuxedo repository for contribution guidelines.
License
Cufflink is part of the Noundry.Tuxedo ecosystem and follows the same license.
See Also
- Noundry.Tuxedo - The ORM that Cufflink extends
- Noundry.Tuxedo.Bowtie - Database migration tool
- Bogus - The fake data generation library
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. 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 is compatible. 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. |
This package has no dependencies.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.4.0 | 221 | 3/4/2026 |