style: format all files with prettier

This commit is contained in:
Seth Hobson
2026-01-19 17:07:03 -05:00
parent 8d37048deb
commit 56848874a2
355 changed files with 15215 additions and 10241 deletions

View File

@@ -4,22 +4,24 @@ Advanced patterns for high-performance data access with Dapper in .NET.
## Why Dapper?
| Aspect | Dapper | EF Core |
|--------|--------|---------|
| Performance | ~10x faster for simple queries | Good with optimization |
| Control | Full SQL control | Abstracted |
| Learning curve | Low (just SQL) | Higher |
| Complex mappings | Manual | Automatic |
| Change tracking | None | Built-in |
| Migrations | External tools | Built-in |
| Aspect | Dapper | EF Core |
| ---------------- | ------------------------------ | ---------------------- |
| Performance | ~10x faster for simple queries | Good with optimization |
| Control | Full SQL control | Abstracted |
| Learning curve | Low (just SQL) | Higher |
| Complex mappings | Manual | Automatic |
| Change tracking | None | Built-in |
| Migrations | External tools | Built-in |
**Use Dapper when:**
- Performance is critical (hot paths)
- You need complex SQL (CTEs, window functions)
- Read-heavy workloads
- Legacy database schemas
**Use EF Core when:**
- Rich domain models with relationships
- Need change tracking
- Want LINQ-to-SQL translation
@@ -74,7 +76,7 @@ public class ProductRepository
{
// Connection opens automatically, closes on dispose
using var connection = _factory.CreateConnection();
return await connection.QueryFirstOrDefaultAsync<Product>(
new CommandDefinition(
"SELECT * FROM Products WHERE Id = @Id",
@@ -120,7 +122,7 @@ var inserted = await connection.QuerySingleAsync<Product>(
// UPDATE
var rowsAffected = await connection.ExecuteAsync(
"""
UPDATE Products
UPDATE Products
SET Name = @Name, Price = @Price, UpdatedAt = @UpdatedAt
WHERE Id = @Id
""",
@@ -190,7 +192,7 @@ public async Task<Product?> GetProductWithCategoryAsync(string id)
""";
using var connection = _factory.CreateConnection();
var result = await connection.QueryAsync<Product, Category, Product>(
sql,
(product, category) =>
@@ -218,7 +220,7 @@ public async Task<Order?> GetOrderWithItemsAsync(int orderId)
var orderDictionary = new Dictionary<int, Order>();
using var connection = _factory.CreateConnection();
await connection.QueryAsync<Order, OrderItem, Product, Order>(
sql,
(order, item, product) =>
@@ -254,9 +256,9 @@ public async Task<(IReadOnlyList<Product> Products, int TotalCount)> SearchWithC
const string sql = """
-- First result set: count
SELECT COUNT(*) FROM Products WHERE CategoryId = @CategoryId;
-- Second result set: data
SELECT * FROM Products
SELECT * FROM Products
WHERE CategoryId = @CategoryId
ORDER BY Name
OFFSET @Offset ROWS FETCH NEXT @PageSize ROWS ONLY;
@@ -288,14 +290,14 @@ public async Task<IReadOnlyList<Product>> GetByIdsAsync(IEnumerable<string> ids)
// Create DataTable matching TVP structure
var table = new DataTable();
table.Columns.Add("Id", typeof(string));
foreach (var id in ids)
{
table.Rows.Add(id);
}
using var connection = _factory.CreateConnection();
var results = await connection.QueryAsync<Product>(
"SELECT p.* FROM Products p INNER JOIN @Ids i ON p.Id = i.Id",
new { Ids = table.AsTableValuedParameter("dbo.StringIdList") });
@@ -313,7 +315,7 @@ public async Task<IReadOnlyList<Product>> GetByIdsAsync(IEnumerable<string> ids)
public async Task<IReadOnlyList<Product>> GetTopProductsAsync(int categoryId, int count)
{
using var connection = _factory.CreateConnection();
var results = await connection.QueryAsync<Product>(
"dbo.GetTopProductsByCategory",
new { CategoryId = categoryId, TopN = count },
@@ -334,7 +336,7 @@ public async Task<(Order Order, string ConfirmationCode)> CreateOrderAsync(Order
parameters.Add("ConfirmationCode", dbType: DbType.String, size: 20, direction: ParameterDirection.Output);
using var connection = _factory.CreateConnection();
await connection.ExecuteAsync(
"dbo.CreateOrder",
parameters,
@@ -354,9 +356,9 @@ public async Task<Order> CreateOrderWithItemsAsync(Order order, List<OrderItem>
{
using var connection = _factory.CreateConnection();
await connection.OpenAsync();
using var transaction = await connection.BeginTransactionAsync();
try
{
// Insert order
@@ -384,7 +386,7 @@ public async Task<Order> CreateOrderWithItemsAsync(Order order, List<OrderItem>
transaction);
await transaction.CommitAsync();
order.Items = items;
return order;
}
@@ -487,7 +489,7 @@ public abstract class DapperRepositoryBase<T> where T : class
protected async Task<T?> GetByIdAsync<TId>(TId id, CancellationToken ct = default)
{
var sql = $"SELECT * FROM {TableName} WHERE Id = @Id";
using var connection = ConnectionFactory.CreateConnection();
return await connection.QueryFirstOrDefaultAsync<T>(
new CommandDefinition(sql, new { Id = id }, cancellationToken: ct));
@@ -496,17 +498,17 @@ public abstract class DapperRepositoryBase<T> where T : class
protected async Task<IReadOnlyList<T>> GetAllAsync(CancellationToken ct = default)
{
var sql = $"SELECT * FROM {TableName}";
using var connection = ConnectionFactory.CreateConnection();
var results = await connection.QueryAsync<T>(
new CommandDefinition(sql, cancellationToken: ct));
return results.ToList();
}
protected async Task<int> ExecuteAsync(
string sql,
object? parameters = null,
string sql,
object? parameters = null,
CancellationToken ct = default)
{
using var connection = ConnectionFactory.CreateConnection();

View File

@@ -159,13 +159,13 @@ services.AddDbContext<AppDbContext>(options =>
maxRetryCount: 3,
maxRetryDelay: TimeSpan.FromSeconds(10),
errorNumbersToAdd: null);
sqlOptions.CommandTimeout(30);
});
// Performance settings
options.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
// Development only
if (env.IsDevelopment())
{
@@ -196,7 +196,7 @@ public class Product
{
public string Id { get; set; }
public string Name { get; set; }
[Timestamp]
public byte[] RowVersion { get; set; } // SQL Server rowversion
}
@@ -214,13 +214,13 @@ catch (DbUpdateConcurrencyException ex)
{
var entry = ex.Entries.Single();
var databaseValues = await entry.GetDatabaseValuesAsync(ct);
if (databaseValues == null)
{
// Entity was deleted
throw new NotFoundException("Product was deleted by another user");
}
// Client wins - overwrite database values
entry.OriginalValues.SetValues(databaseValues);
await _context.SaveChangesAsync(ct);
@@ -237,12 +237,12 @@ try
// Multiple operations
_context.Orders.Add(order);
await _context.SaveChangesAsync(ct);
await _context.OrderItems.AddRangeAsync(items, ct);
await _context.SaveChangesAsync(ct);
await _paymentService.ProcessAsync(order.Id, ct);
await transaction.CommitAsync(ct);
}
catch
@@ -264,14 +264,14 @@ public class ProductConfiguration : IEntityTypeConfiguration<Product>
// Unique index
builder.HasIndex(p => p.Sku)
.IsUnique();
// Composite index for common query patterns
builder.HasIndex(p => new { p.CategoryId, p.Name });
// Filtered index (SQL Server)
builder.HasIndex(p => p.Price)
.HasFilter("[IsDeleted] = 0");
// Include columns for covering index
builder.HasIndex(p => p.CategoryId)
.IncludeProperties(p => new { p.Name, p.Price });
@@ -335,7 +335,7 @@ builder.HasIndex(p => p.FullName);
services.AddDbContext<AppDbContext>(options =>
{
options.UseSqlServer(connectionString);
options.LogTo(
filter: (eventId, level) => eventId.Id == CoreEventId.QueryExecutionPlanned.Id,
logger: (eventData) =>