Modern ORM combining the power of Dapper with additional functionality for rapid data access. High-performance, lightweight, and developer-friendly for .NET applications.
Built on proven Dapper performance with modern .NET features and developer productivity enhancements.
Built on Dapper's proven performance foundation with optimized query execution and minimal overhead.
Support for SQL Server, PostgreSQL, MySQL, and SQLite with consistent API across all providers.
Complete CRUD operations with conventions-based mapping, async support, and transaction management.
Install Noundry.Tuxedo and start building high-performance data access layers.
using Noundry.Tuxedo;
public class UserService
{
private readonly IDbConnection _connection;
public UserService(IDbConnection connection)
{
_connection = connection;
}
// Get by ID - Simple and fast
public async Task<User?> GetByIdAsync(int id)
{
return await _connection.GetAsync<User>(id);
}
// Create new user
public async Task<int> CreateAsync(User user)
{
return await _connection.InsertAsync(user);
}
// Update existing user
public async Task<bool> UpdateAsync(User user)
{
return await _connection.UpdateAsync(user);
}
// Delete user
public async Task<bool> DeleteAsync(int id)
{
return await _connection.DeleteAsync<User>(id);
}
}
Choose your database provider with consistent API across all supported platforms.
Full-featured SQL Server support with advanced features and optimizations.
Optimized connection management
Support for SQL Server specific data types
High-performance bulk insert and update
// Program.cs - SQL Server configuration
builder.Services.AddTuxedoSqlServer(connectionString);
// Or with options
builder.Services.AddTuxedoSqlServer(connectionString, options =>
{
options.CommandTimeout = 30;
options.EnableRetryOnFailure = true;
options.MaxRetryCount = 3;
});
// Connection string example
var connectionString = "Server=localhost;Database=MyApp;Integrated Security=true;TrustServerCertificate=true;";
Open-source PostgreSQL support with advanced features and JSON support.
Native JSON and JSONB column support
PostgreSQL-specific data types
Advanced search capabilities
// Program.cs - PostgreSQL configuration
builder.Services.AddTuxedoPostgreSQL(connectionString);
// Connection string example
var connectionString = "Host=localhost;Database=myapp;Username=postgres;Password=password";
// JSON column example
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
// JSON column for flexible data
[Column(TypeName = "jsonb")]
public Dictionary<string, object> Metadata { get; set; }
}
Popular open-source database with excellent performance and wide hosting support.
Works with MySQL 5.7+ and MariaDB
Amazon RDS, Azure Database, Google Cloud SQL
Excellent price-performance ratio
// Program.cs - MySQL configuration
builder.Services.AddTuxedoMySQL(connectionString);
// Connection string examples
var localConnection = "Server=localhost;Database=myapp;Uid=root;Pwd=password;";
var cloudConnection = "Server=mysql.aws.com;Database=prod;Uid=user;Pwd=secret;SslMode=Required;";
// Model example with MySQL-specific attributes
public class Order
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Column(TypeName = "VARCHAR(100)")]
public string OrderNumber { get; set; }
[Column(TypeName = "DECIMAL(10,2)")]
public decimal Total { get; set; }
}
Lightweight, file-based database perfect for development, testing, and small applications.
No server setup required
Fast in-memory database for unit tests
Works everywhere .NET runs
// Program.cs - SQLite configuration
builder.Services.AddTuxedoSQLite(connectionString);
// Connection string examples
var fileDb = "Data Source=myapp.db";
var memoryDb = "Data Source=:memory:";
// Perfect for testing
public class TestFixture
{
private IDbConnection _connection;
[SetUp]
public void Setup()
{
_connection = new TuxedoConnection("Data Source=:memory:");
}
[Test]
public async Task CanInsertAndRetrieveUser()
{
var user = new User { Name = "Test User" };
var id = await _connection.InsertAsync(user);
var retrieved = await _connection.GetAsync<User>(id);
retrieved.Assert().IsNotNull();
}
}
Learn all the essential data access patterns with Noundry.Tuxedo.
Simple Create, Read, Update, Delete operations with automatic SQL generation.
public class ProductService
{
private readonly IDbConnection _connection;
// CREATE - Insert new product
public async Task<int> CreateProductAsync(Product product)
{
return await _connection.InsertAsync(product);
}
// READ - Get by ID
public async Task<Product?> GetProductAsync(int id)
{
return await _connection.GetAsync<Product>(id);
}
// READ - Get all
public async Task<IEnumerable<Product>> GetAllProductsAsync()
{
return await _connection.GetAllAsync<Product>();
}
// UPDATE - Update existing
public async Task<bool> UpdateProductAsync(Product product)
{
return await _connection.UpdateAsync(product);
}
// DELETE - Remove by ID
public async Task<bool> DeleteProductAsync(int id)
{
return await _connection.DeleteAsync<Product>(id);
}
}
Execute custom SQL queries with parameters, paging, and complex joins.
// Custom query with parameters
public async Task<IEnumerable<Product>> GetActiveProductsAsync(int categoryId)
{
var sql = @"
SELECT p.*, c.Name as CategoryName
FROM Products p
INNER JOIN Categories c ON p.CategoryId = c.Id
WHERE p.IsActive = @IsActive
AND (@CategoryId IS NULL OR p.CategoryId = @CategoryId)
ORDER BY p.CreatedAt DESC";
return await _connection.QueryAsync<Product>(sql, new
{
IsActive = true,
CategoryId = categoryId == 0 ? null : categoryId
});
}
// Paged results for large datasets
public async Task<PagedResult<User>> GetUsersPagedAsync(int page, int pageSize)
{
return await _connection.GetPagedAsync<User>(page, pageSize);
}
// Execute scalar queries
public async Task<int> GetProductCountAsync(int categoryId)
{
var sql = "SELECT COUNT(*) FROM Products WHERE CategoryId = @CategoryId";
return await _connection.ExecuteScalarAsync<int>(sql, new { CategoryId = categoryId });
}
Ensure data consistency with built-in transaction support and rollback capabilities.
// Transfer funds with transaction
public async Task TransferFundsAsync(int fromId, int toId, decimal amount)
{
using var transaction = await _connection.BeginTransactionAsync();
try
{
// Debit from source account
await _connection.ExecuteAsync(
"UPDATE Accounts SET Balance = Balance - @Amount WHERE Id = @Id",
new { Amount = amount, Id = fromId },
transaction);
// Credit to destination account
await _connection.ExecuteAsync(
"UPDATE Accounts SET Balance = Balance + @Amount WHERE Id = @Id",
new { Amount = amount, Id = toId },
transaction);
// Log the transaction
await _connection.InsertAsync(new TransactionLog
{
FromAccountId = fromId,
ToAccountId = toId,
Amount = amount,
TransactionDate = DateTime.UtcNow
}, transaction);
await transaction.CommitAsync();
}
catch
{
await transaction.RollbackAsync();
throw;
}
}
Use attributes to customize table mapping, column names, and database behavior.
using Noundry.Tuxedo.Contrib;
using System.ComponentModel.DataAnnotations;
[Table("Products")]
public class Product
{
[Key]
[ExplicitKey] // For manually assigned IDs
public int Id { get; set; }
[Required]
[StringLength(100)]
public string Name { get; set; }
[Column("product_price")] // Custom column name
public decimal Price { get; set; }
[ForeignKey("CategoryId")]
public int CategoryId { get; set; }
[Computed] // Not included in INSERT/UPDATE
public string CategoryName { get; set; }
[Write(false)] // Read-only property
public DateTime CreatedAt { get; set; }
}
Build complex queries dynamically with LINQ-style expressions and fluent API.
Build queries with conditional WHERE clauses
Inner, Left, Right joins with multiple tables
Dynamic ordering and efficient pagination
// Dynamic query building
using Noundry.Tuxedo.QueryBuilder;
var query = _connection.QueryBuilder<Product>()
.Where(p => p.IsActive)
.Join<Category>(p => p.CategoryId, c => c.Id)
.Select(p => new {
p.Name,
p.Price,
CategoryName = p.Category.Name
})
.OrderBy(p => p.Name)
.Take(50);
var results = await query.ToListAsync();
public async Task<IEnumerable<Product>> SearchProductsAsync(
string? searchTerm = null,
decimal? minPrice = null,
decimal? maxPrice = null,
int? categoryId = null,
string sortBy = "Name",
int page = 1,
int pageSize = 20)
{
var query = _connection.QueryBuilder<Product>()
.Where(p => p.IsActive);
// Dynamic search term
if (!string.IsNullOrEmpty(searchTerm))
{
query = query.Where(p => p.Name.Contains(searchTerm) ||
p.Description.Contains(searchTerm));
}
// Price range filters
if (minPrice.HasValue)
query = query.Where(p => p.Price >= minPrice.Value);
if (maxPrice.HasValue)
query = query.Where(p => p.Price <= maxPrice.Value);
// Category filter
if (categoryId.HasValue)
query = query.Where(p => p.CategoryId == categoryId.Value);
// Dynamic sorting
query = sortBy.ToLower() switch
{
"price" => query.OrderBy(p => p.Price),
"date" => query.OrderByDescending(p => p.CreatedDate),
_ => query.OrderBy(p => p.Name)
};
// Pagination
return await query
.Skip((page - 1) * pageSize)
.Take(pageSize)
.ToListAsync();
}
Process thousands of records efficiently with built-in bulk operations and batch processing.
// Bulk insert thousands of records
var products = GenerateProducts(10000);
await _connection.BulkInsertAsync(products);
// Bulk update with conditions
await _connection.BulkUpdateAsync<Product>(
p => p.CategoryId == 1,
new { IsDiscounted = true });
// Batch processing with progress
await _connection.BatchProcessAsync(
products,
batchSize: 1000,
processor: batch => ProcessBatch(batch));
with bulk operations
Built-in fault tolerance, intelligent caching, and comprehensive diagnostics for production-ready applications.
Configurable exponential backoff
Fail fast on consecutive errors
Configurable command timeouts
Automatic query result caching
Smart cache invalidation strategies
Redis and in-memory providers
Execution time and performance
Database connectivity monitoring
Structured logging with context
public class EcommerceService
{
private readonly IDbConnection _connection;
// Get products with category information
public async Task<IEnumerable<ProductWithCategory>> GetProductsWithCategoryAsync()
{
var sql = @"
SELECT p.*, c.Name as CategoryName, c.Description as CategoryDescription
FROM Products p
INNER JOIN Categories c ON p.CategoryId = c.Id
WHERE p.IsActive = 1
ORDER BY p.Name";
return await _connection.QueryAsync<ProductWithCategory>(sql);
}
// Create order with items (transaction)
public async Task<int> CreateOrderWithItemsAsync(Order order, List<OrderItem> items)
{
using var transaction = await _connection.BeginTransactionAsync();
try
{
// Insert order
var orderId = await _connection.InsertAsync(order, transaction);
// Insert order items
foreach (var item in items)
{
item.OrderId = orderId;
await _connection.InsertAsync(item, transaction);
// Update product stock
await _connection.ExecuteAsync(
"UPDATE Products SET StockQuantity = StockQuantity - @Quantity WHERE Id = @ProductId",
new { item.Quantity, item.ProductId },
transaction);
}
await transaction.CommitAsync();
return orderId;
}
catch
{
await transaction.RollbackAsync();
throw;
}
}
}
// Program.cs - Resiliency setup
builder.Services.AddTuxedoSqlServer(connectionString, options =>
{
// Retry policy configuration
options.EnableRetryOnFailure = true;
options.MaxRetryCount = 3;
options.MaxRetryDelay = TimeSpan.FromSeconds(30);
// Circuit breaker
options.CircuitBreakerThreshold = 5;
options.CircuitBreakerDuration = TimeSpan.FromSeconds(60);
// Timeout settings
options.CommandTimeout = TimeSpan.FromSeconds(30);
// Health checks
options.EnableHealthChecks = true;
});
// Add caching with Redis
builder.Services.AddStackExchangeRedisCache(options =>
{
options.Configuration = "localhost:6379";
});
builder.Services.AddTuxedoCache(cache =>
{
cache.DefaultExpiration = TimeSpan.FromMinutes(15);
cache.EnableDistributedCache = true;
});
// Diagnostics service
public class ProductService
{
private readonly IDbConnection _connection;
private readonly ITuxedoMetrics _metrics;
private readonly ILogger _logger;
public async Task<Product> GetProductAsync(int id)
{
using var activity = _metrics.StartActivity("GetProduct");
activity?.SetTag("product.id", id.ToString());
try
{
// Cached query with metrics
var product = await _connection
.QueryCachedAsync<Product>(
"SELECT * FROM Products WHERE Id = @id",
new { id },
cacheKey: $"product:{id}",
expiration: TimeSpan.FromMinutes(30));
_metrics.IncrementCounter("products.retrieved");
_logger.LogInformation("Retrieved product {ProductId}", id);
return product;
}
catch (Exception ex)
{
_metrics.IncrementCounter("products.errors");
_logger.LogError(ex, "Error retrieving product {ProductId}", id);
throw;
}
}
}
Automatically create and update database schemas from your .NET models using Noundry.Tuxedo.Bowtie - attribute-driven, declarative database management.
Define schema using C# attributes on your model classes
Automatically create/update tables, indexes, and constraints
SQL Server, PostgreSQL, MySQL, SQLite with unified API
// Program.cs - Bowtie setup
builder.Services.AddNoundryTuxedoBowtie();
var app = builder.Build();
// Synchronize database schema in development
if (app.Environment.IsDevelopment())
{
await app.Services.SynchronizeDatabaseAsync(
connectionString: "Data Source=app.db",
provider: DatabaseProvider.SQLite
);
}
// Or for SQL Server
await app.Services.SynchronizeDatabaseAsync(
connectionString: "Server=.;Database=MyApp;Integrated Security=true;",
provider: DatabaseProvider.SqlServer
);
[Table("Products")]
public class Product
{
[Key]
public int Id { get; set; }
[Column(MaxLength = 200)]
[Index("IX_Products_Name")]
public string Name { get; set; }
[Column(MaxLength = 1000)]
public string? Description { get; set; }
[CheckConstraint("Price > 0")]
public decimal Price { get; set; }
[ForeignKey("Categories")]
[Index("IX_Products_CategoryId")]
public int CategoryId { get; set; }
[Column(DefaultValue = true)]
public bool IsActive { get; set; }
[Column(DefaultValue = "GETUTCDATE()")]
public DateTime CreatedDate { get; set; }
}
// PostgreSQL-specific example
[Table("Documents")]
public class Document
{
[Key]
public int Id { get; set; }
// GIN index for JSON searches
[Index("IX_Documents_Content_GIN", IndexType.GIN)]
public string JsonContent { get; set; }
}
Translate database-specific exceptions into meaningful, typed exceptions with detailed contextual information.
using Noundry.Tuxedo.Exceptions;
// Auto-detect database provider
var wrappedConn = connection.WithExceptionTranslation();
try
{
await wrappedConn.InsertAsync(user);
}
catch (UniqueConstraintException ex)
{
if (ex.ColumnName?.Contains("Email") == true)
return "Email already registered";
}
catch (ForeignKeyException ex)
{
return $"Cannot delete: referenced by {ex.ReferencingTable}";
}
public class UserService
{
public async Task<Result> RegisterUserAsync(User user)
{
var conn = _connection.WithExceptionTranslation();
try
{
await conn.InsertAsync(user);
return Result.Success();
}
catch (UniqueConstraintException ex)
{
if (ex.ColumnName?.Contains("Email") == true)
return Result.Error("Email address is already registered");
if (ex.ColumnName?.Contains("Username") == true)
return Result.Error("Username is already taken");
return Result.Error("User already exists");
}
catch (DataTruncationException ex)
{
return Result.Error($"{ex.ColumnName} is too long (max {ex.MaxLength})");
}
}
}
Combine Exception Translation with Auditing and Resiliency:
using Noundry.Tuxedo.Exceptions;
using Noundry.Tuxedo.Auditor;
using Noundry.Tuxedo.Resiliency;
// Compose multiple wrappers
var connection = new SqlConnection(connectionString)
.WithAuditing(config => config.UserProvider = userProvider)
.WithExceptionTranslation()
.WithRetryPolicy(retryPolicy);
Comprehensive change tracking for Insert, Update, and Delete operations with automatic audit trail generation.
using Noundry.Tuxedo.Auditor;
using Noundry.Tuxedo.Auditor.UserProviders;
// Wrap connection with auditing
var auditedConn = connection.WithAuditing(config =>
{
config.UserProvider = new StaticAuditUserProvider("admin");
});
// All CRUD operations are automatically audited
await auditedConn.InsertAsync(product); // Logged: Insert with new values
await auditedConn.UpdateAsync(product); // Logged: Update with old/new values
await auditedConn.DeleteAsync(product); // Logged: Delete with old values
// Static user
config.UserProvider = new StaticAuditUserProvider("admin");
// Delegate (from current thread)
config.UserProvider = new DelegateAuditUserProvider(() =>
Thread.CurrentPrincipal?.Identity?.Name ?? "Anonymous"
);
// HttpContext (ASP.NET Core)
services.AddScoped<IDbConnection>(sp =>
{
var httpContext = sp.GetRequiredService<IHttpContextAccessor>();
var connection = new SqlConnection(connectionString);
return connection.WithAuditing(config =>
{
config.UserProvider = new HttpContextAuditUserProvider(httpContext);
});
});
Automatically detects soft deletes when entities have IsDeleted and DeletedAt properties:
public class User
{
[Key]
public int Id { get; set; }
public string Email { get; set; }
public bool IsDeleted { get; set; }
public DateTime? DeletedAt { get; set; }
}
// Regular update
user.Email = "newemail@example.com";
await auditedConn.UpdateAsync(user);
// Event Type: Update
// Soft delete
user.IsDeleted = true;
user.DeletedAt = DateTime.UtcNow;
await auditedConn.UpdateAsync(user);
// Event Type: SoftDelete
// Get audit history for a specific entity
var entityHistory = connection.Query<AuditLog>(@"
SELECT * FROM audit_log
WHERE table_name = @Table AND entity_id = @EntityId
ORDER BY timestamp DESC",
new { Table = "Users", EntityId = "123" }
);
// Get all changes by a specific user
var userChanges = connection.Query<AuditLog>(
"SELECT * FROM audit_log WHERE username = @Username",
new { Username = "admin" }
);
Start using Noundry.Tuxedo today and build fast, reliable data access layers for your .NET applications.