Noundry.Tuxedo.Bowtie

Database schema synchronization tool for Noundry.Tuxedo. Define your database schema directly in POCO model classes with attributes, and Bowtie automatically keeps your database in perfect sync.

120/137
Tests Passing
87.6%
Code Coverage
Model-First
Approach
POCO-Based
Schema Sync

Why Choose Noundry.Tuxedo.Bowtie?

Model-first database tool designed for .NET developers who want to define their schema in code and automatically synchronize with any RDBMS.

Model-Driven Schema Sync

Automatically synchronize database schema with POCO model classes using attributes to define tables, indexes, and constraints.

Cross-Database Schema Sync

Automatically synchronize database schema across all environments based on your POCO model definitions with attribute-driven configuration.

Attribute-Driven Configuration

Use .NET attributes on your model classes to define tables, indexes, constraints, and relationships - all in your familiar C# code.

Get Started in 5 Minutes

Install Bowtie and start defining your database schema directly in your POCO model classes.

1. Installation

# Install Tuxedo + Bowtie
$ dotnet add package Noundry.Tuxedo
$ dotnet add package Noundry.Tuxedo.Bowtie

2. Configure Services

builder.Services.AddTuxedoSqlServer(connectionString);
builder.Services.AddNoundryTuxedoBowtie();

3. Synchronize Schema

await app.Services.SynchronizeDatabaseAsync();
Product.cs
using Noundry.Tuxedo.Bowtie.Attributes;
using System.ComponentModel.DataAnnotations;

// Define your database schema in the model
[Table("Products")]
public class Product
{
    [Key]
    public int Id { get; set; }

    [Column(MaxLength = 100)]
    [Index("IX_Products_Name")]
    public string Name { get; set; }

    [CheckConstraint("Price > 0")]
    public decimal Price { get; set; }

    [ForeignKey("Categories", "Id")]
    public int CategoryId { get; set; }
    
    [Column(TypeName = "datetime2")]
    [DefaultValue("GETUTCDATE()")]
    public DateTime CreatedAt { get; set; }
}

// Bowtie automatically creates this table structure:
// - Products table with all columns
// - IX_Products_Name index
// - Price > 0 check constraint
// - Foreign key to Categories table

POCO Model Attributes

Define your complete database schema using attributes on your model classes. Bowtie handles the rest.

Table & Column Definition

Basic Table Attributes

Use standard .NET attributes to define table names, column properties, and data types.

[Table("name")]

Specify custom table name

[Key]

Mark property as primary key

[Column(MaxLength = n)]

Set column length and properties

[Required]

Make column NOT NULL

[Table("Users")]
public class User
{
    [Key]
    public int Id { get; set; }

    [Required]
    [Column(MaxLength = 100)]
    public string Name { get; set; }

    [Required]
    [Column(MaxLength = 255)]
    public string Email { get; set; }

    [Column(TypeName = "decimal(10,2)")]
    public decimal? Balance { get; set; }

    [Column(TypeName = "datetime2")]
    [DefaultValue("GETUTCDATE()")]
    public DateTime CreatedAt { get; set; }
}

// Bowtie automatically creates:
// CREATE TABLE Users (
//     Id int IDENTITY(1,1) PRIMARY KEY,
//     Name nvarchar(100) NOT NULL,
//     Email nvarchar(255) NOT NULL,
//     Balance decimal(10,2) NULL,
//     CreatedAt datetime2 DEFAULT GETUTCDATE()
//)

Index Definition

Performance Indexes

Define indexes directly on model properties for optimal query performance across different database providers.

[Index("name")]

Create single-column index

[Index("name", Unique = true)]

Create unique index

[Index("name", Type = IndexType.Hash)]

Specify index type (B-Tree, Hash, GIN)

[Table("Products")]
public class Product
{
    [Key]
    public int Id { get; set; }

    // Single column index for fast lookups
    [Index("IX_Products_Name")]
    [Column(MaxLength = 100)]
    public string Name { get; set; }

    // Unique index for email addresses
    [Index("IX_Products_SKU", Unique = true)]
    [Column(MaxLength = 50)]
    public string SKU { get; set; }

    // Composite index for category + active filtering
    [Index("IX_Products_Category_Active", Order = 1)]
    public int CategoryId { get; set; }

    [Index("IX_Products_Category_Active", Order = 2)]
    public bool IsActive { get; set; }

    // Hash index for PostgreSQL (fast equality lookups)
    [Index("IX_Products_Hash", Type = IndexType.Hash)]
    public Guid ProductCode { get; set; }
}

Constraints & Validation

Database Constraints

Define check constraints, unique constraints, and default values directly in your model classes.

[CheckConstraint("expression")]

Add database check constraints

[DefaultValue("value")]

Set column default values

[UniqueConstraint("name")]

Create unique constraints

[Table("Orders")]
public class Order
{
    [Key]
    public int Id { get; set; }

    // Unique order number constraint
    [UniqueConstraint("UQ_Orders_OrderNumber")]
    [Column(MaxLength = 50)]
    public string OrderNumber { get; set; }

    // Check constraint for positive total
    [CheckConstraint("Total > 0")]
    [Column(TypeName = "decimal(10,2)")]
    public decimal Total { get; set; }

    // Default value for status
    [DefaultValue("'Pending'")]
    [Column(MaxLength = 20)]
    public string Status { get; set; }

    // Date range constraint
    [CheckConstraint("OrderDate >= '2024-01-01'")]
    [DefaultValue("GETUTCDATE()")]
    public DateTime OrderDate { get; set; }

    // Multiple constraints on single property
    [CheckConstraint("Quantity > 0 AND Quantity <= 1000")]
    [DefaultValue("1")]
    public int Quantity { get; set; }
}

Foreign Key Relationships

Relationship Attributes

Define foreign key relationships and referential integrity directly in your model classes.

[ForeignKey("table", "column")]

Define foreign key relationship

OnDelete = Rule.Cascade

Cascading delete behavior

OnUpdate = Rule.SetNull

Update behavior configuration

// Parent table
[Table("Categories")]
public class Category
{
    [Key]
    public int Id { get; set; }
    
    [Required]
    [Column(MaxLength = 100)]
    public string Name { get; set; }
}

// Child table with foreign key
[Table("Products")]
public class Product
{
    [Key]
    public int Id { get; set; }

    [Column(MaxLength = 100)]
    public string Name { get; set; }

    // Foreign key with cascade delete
    [ForeignKey("Categories", "Id", OnDelete = Rule.Cascade)]
    public int CategoryId { get; set; }
}

// Bowtie creates:
// ALTER TABLE Products ADD CONSTRAINT FK_Products_Categories
// FOREIGN KEY (CategoryId) REFERENCES Categories(Id)
// ON DELETE CASCADE

How Schema Synchronization Works

Bowtie analyzes your POCO models and automatically synchronizes your database schema to match.

1. Model Analysis

Bowtie scans your POCO classes and analyzes all attributes to understand the desired database schema.

2. Schema Comparison

Compares the current database schema with the desired schema defined in your models.

3. Automatic Sync

Generates and executes the necessary SQL commands to bring the database schema into sync with your models.

Live Schema Synchronization

// When you run SynchronizeDatabaseAsync(), Bowtie:

🔍 Analyzing POCO models...
   • Found Product model with [Table("Products")]
   • Found Category model with [Table("Categories")]
   • Found Index attributes: IX_Products_Name, IX_Products_SKU
   • Found ForeignKey: Products.CategoryId → Categories.Id

📊 Comparing with database schema...
   • Products table: missing IX_Products_SKU index
   • Categories table: exists and matches
   • Foreign key constraint: missing

🔧 Generating synchronization commands...
   • CREATE INDEX IX_Products_SKU ON Products(SKU)
   • ALTER TABLE Products ADD CONSTRAINT FK_Products_Categories...

✅ Database schema synchronized successfully!
   • 2 indexes created
   • 1 foreign key constraint added
   • 0 tables modified

Real-World Model Examples

See complete POCO models with Bowtie attributes for common application scenarios.

E-commerce Schema Models

Complete Product & Order Models

Full e-commerce schema with products, categories, orders, and customers defined entirely through POCO model attributes.

Multi-table relationships
Performance indexes
Business rule constraints
Audit trail columns
[Table("Products")]
public class Product
{
    [Key]
    public int Id { get; set; }

    [Required]
    [Column(MaxLength = 200)]
    [Index("IX_Products_Name")]
    public string Name { get; set; }

    [UniqueConstraint("UQ_Products_SKU")]
    [Column(MaxLength = 50)]
    public string SKU { get; set; }

    [CheckConstraint("Price > 0")]
    [Column(TypeName = "decimal(10,2)")]
    public decimal Price { get; set; }

    [ForeignKey("Categories", "Id")]
    [Index("IX_Products_Category_Active", Order = 1)]
    public int CategoryId { get; set; }

    [DefaultValue("1")]
    [Index("IX_Products_Category_Active", Order = 2)]
    public bool IsActive { get; set; }

    [DefaultValue("0")]
    [CheckConstraint("StockQuantity >= 0")]
    public int StockQuantity { get; set; }

    // Audit trail
    [DefaultValue("GETUTCDATE()")]
    public DateTime CreatedAt { get; set; }

    public DateTime? UpdatedAt { get; set; }
}

Bowtie Configuration & Usage

Advanced Configuration Options

Configure Bowtie for different environments and deployment scenarios.

// Program.cs - Advanced Bowtie configuration
builder.Services.AddNoundryTuxedoBowtie(options =>
{
    // Assembly containing your POCO models
    options.ModelAssembly = typeof(Product).Assembly;
    
    // Database provider and connection
    options.Provider = DatabaseProvider.SqlServer;
    options.ConnectionString = connectionString;
    
    // Synchronization behavior
    options.AutoCreateTables = true;
    options.AutoCreateIndexes = true;
    options.AutoCreateConstraints = true;
    
    // Safety settings
    options.AllowDropColumns = app.Environment.IsDevelopment();
    options.AllowDropTables = false; // Never drop tables
    
    // Dry run mode (generate SQL without execution)
    options.DryRun = false;
    options.LogGeneratedSQL = true;
});

// Manual synchronization
var syncService = app.Services.GetRequiredService<IBowtieService>();

// Check what changes would be made
var changes = await syncService.GetPendingChangesAsync();

if (changes.Any())
{
    // Apply changes
    await syncService.SynchronizeSchemaAsync();
}

CLI Tools & Deployment

Use Bowtie CLI tools for build processes, CI/CD integration, and production deployments.

CLI Commands

bowtie sync

Synchronize database schema with POCO models

bowtie validate

Validate model definitions and check for issues

bowtie generate-sql

Generate SQL scripts without executing

bowtie status

Check current database schema status

Install CLI Tool:
$ dotnet tool install -g Noundry.Tuxedo.Bowtie.Tool

CI/CD Integration

# GitHub Actions - Schema Sync
name: Deploy with Schema Sync

jobs:
  deploy:
    steps:
    - name: Validate Schema
      run: bowtie validate --connection "${{ secrets.DB_CONNECTION }}"
      
    - name: Generate Schema SQL
      run: bowtie generate-sql --output schema-changes.sql
      
    - name: Review Changes
      run: cat schema-changes.sql
      
    - name: Apply Schema Changes
      run: bowtie sync --connection "${{ secrets.DB_CONNECTION }}" --confirm

# Azure DevOps Pipeline
- task: DotNetCoreCLI@2
  displayName: 'Sync Database Schema'
  inputs:
    command: 'custom'
    custom: 'bowtie'
    arguments: 'sync --connection "$(ConnectionString)" --validate-first'

Cross-Database Compatibility

Same POCO models work across SQL Server, PostgreSQL, MySQL, and SQLite with database-specific optimizations.

SQL Server

Native SQL Server types, indexes, and constraints

PostgreSQL

GIN indexes, JSONB columns, and PostgreSQL-specific features

MySQL

MySQL engine optimization and charset handling

SQLite

Lightweight file-based database perfect for testing and development

Same Model, Different Databases

// This single model definition works across all supported databases:

[Table("Products")]
public class Product
{
    [Key]
    public int Id { get; set; }

    [Index("IX_Products_Search", Type = IndexType.GIN)] // PostgreSQL: GIN index
    [Index("IX_Products_Search", Type = IndexType.FullText)] // SQL Server: Full-text
    [Column(MaxLength = 100)]
    public string Name { get; set; }
}

// SQL Server generates:
// CREATE FULLTEXT INDEX IX_Products_Search ON Products(Name)

// PostgreSQL generates:
// CREATE INDEX IX_Products_Search ON Products USING GIN(to_tsvector('english', Name))

// MySQL generates:
// CREATE FULLTEXT INDEX IX_Products_Search ON Products(Name)

// SQLite generates:
// CREATE INDEX IX_Products_Search ON Products(Name)

Ready to Synchronize Your Schema?

Start using Noundry.Tuxedo.Bowtie today and define your database schema directly in your POCO model classes with automatic synchronization.