con un clic
dapper
Use when writing read-optimized queries with Dapper alongside EF Core.
Instalar con Codex o Claude Copia este prompt, pégalo en Codex, Claude u otro asistente, y deja que revise la página de la skill y la instale por ti.
Menú
Use when writing read-optimized queries with Dapper alongside EF Core.
Instalar con Codex o Claude Copia este prompt, pégalo en Codex, Claude u otro asistente, y deja que revise la página de la skill y la instale por ti.
Basado en la clasificación ocupacional SOC
Use when adding caching to .NET APIs or optimizing response times with distributed cache, output cache, or ETags.
Use when configuring API response formats, custom formatters, or Accept header handling.
Use when building controller-based REST APIs with action results, model binding, or MediatR integration.
Use when creating RESTful API controllers with MediatR dispatch and ProblemDetails error responses.
Use when designing gRPC services, proto files, or adding gRPC-Web or JSON transcoding.
Use when building minimal API endpoints with route groups, filters, or TypedResults.
| name | dapper |
| description | Use when writing read-optimized queries with Dapper alongside EF Core. |
| metadata | {"category":"data","agent":"ef-specialist","when-to-use":"When writing read-optimized SQL queries using Dapper alongside EF Core"} |
| Scenario | Use |
|---|---|
| Complex read queries with joins | Dapper |
| Reporting and aggregation | Dapper |
| Bulk insert/update | Dapper |
| CRUD operations | EF Core |
| Change tracking | EF Core |
| Migrations | EF Core |
namespace {Company}.{Domain}.Infrastructure.Queries;
public sealed class OrderQueryService(IDbConnectionFactory connectionFactory)
{
public async Task<OrderDetailDto?> GetByIdAsync(Guid id, CancellationToken ct)
{
using var connection = connectionFactory.Create();
const string sql = """
SELECT o.Id, o.CustomerName, o.Total, o.Status, o.CreatedAt
FROM Orders o
WHERE o.Id = @Id
""";
return await connection.QuerySingleOrDefaultAsync<OrderDetailDto>(
new CommandDefinition(sql, new { Id = id }, cancellationToken: ct));
}
}
public async Task<IEnumerable<OrderWithItemsDto>> GetOrdersWithItemsAsync(
CancellationToken ct)
{
using var connection = connectionFactory.Create();
const string sql = """
SELECT o.Id, o.CustomerName, o.Total,
i.Id AS ItemId, i.ProductName, i.Quantity, i.UnitPrice
FROM Orders o
LEFT JOIN OrderItems i ON i.OrderId = o.Id
ORDER BY o.CreatedAt DESC
""";
var lookup = new Dictionary<Guid, OrderWithItemsDto>();
await connection.QueryAsync<OrderWithItemsDto, OrderItemDto, OrderWithItemsDto>(
new CommandDefinition(sql, cancellationToken: ct),
(order, item) =>
{
if (!lookup.TryGetValue(order.Id, out var existing))
{
existing = order;
existing.Items = [];
lookup[order.Id] = existing;
}
if (item is not null)
existing.Items.Add(item);
return existing;
},
splitOn: "ItemId");
return lookup.Values;
}
public async Task<PaginatedResult<OrderSummaryDto>> GetPaginatedAsync(
string? search, string? status, int page, int pageSize, CancellationToken ct)
{
using var connection = connectionFactory.Create();
var builder = new SqlBuilder();
var countTemplate = builder.AddTemplate(
"SELECT COUNT(*) FROM Orders o /**where**/");
var selectTemplate = builder.AddTemplate("""
SELECT o.Id, o.CustomerName, o.Total, o.Status, o.CreatedAt
FROM Orders o /**where**/ /**orderby**/
OFFSET @Offset ROWS FETCH NEXT @PageSize ROWS ONLY
""");
if (!string.IsNullOrEmpty(search))
builder.Where("o.CustomerName LIKE @Search", new { Search = $"%{search}%" });
if (!string.IsNullOrEmpty(status))
builder.Where("o.Status = @Status", new { Status = status });
builder.OrderBy("o.CreatedAt DESC");
var total = await connection.ExecuteScalarAsync<int>(
new CommandDefinition(countTemplate.RawSql, countTemplate.Parameters,
cancellationToken: ct));
var items = await connection.QueryAsync<OrderSummaryDto>(
new CommandDefinition(selectTemplate.RawSql,
new DynamicParameters(selectTemplate.Parameters)
{
{ "Offset", (page - 1) * pageSize },
{ "PageSize", pageSize }
}, cancellationToken: ct));
return new PaginatedResult<OrderSummaryDto>(items.ToList(), total, page, pageSize);
}
public interface IDbConnectionFactory
{
IDbConnection Create();
}
public sealed class SqlConnectionFactory(IOptions<DatabaseOptions> options)
: IDbConnectionFactory
{
public IDbConnection Create() =>
new SqlConnection(options.Value.ConnectionString);
}
services.AddSingleton<IDbConnectionFactory, SqlConnectionFactory>();
services.AddScoped<OrderQueryService>();
| Anti-Pattern | Correct Approach |
|---|---|
| String concatenation for SQL | Use SqlBuilder or parameterized queries |
| Not disposing connections | using var connection = ... |
| Using Dapper for simple CRUD | Use EF Core for CRUD |
| Forgetting CancellationToken | Pass via CommandDefinition |
grep -r "Dapper\|SqlMapper\|QueryAsync\|SqlBuilder" --include="*.cs"
grep -r "IDbConnection\|SqlConnection" --include="*.cs"
Dapper and Dapper.SqlBuilder NuGet packagesIDbConnectionFactory if not already present