A high-performant .NET library for batching multiple database queries into a single roundtrip with type-safe, reusable query definitions.
// 3 sequential roundtrips, no concurrency allowed
var user = await db.Users.FirstOrDefaultAsync(u => u.Id == userId);
var orders = await db.Orders.Where(o => o.UserId == userId).ToListAsync();
var addresses = await db.Addresses.Where(a => a.UserId == userId).ToListAsync();
// Each query waits for the previous one. Database executes them one by one.// hard to read, maintain, and reuse
var sql = @"
SELECT * FROM Users WHERE Id = @userId;
SELECT * FROM Orders WHERE UserId = @userId;
SELECT * FROM Addresses WHERE UserId = @userId;";
using var multi = await connection.QueryMultipleAsync(sql, new { userId = 5 });
var user = await multi.ReadFirstOrDefaultAsync<User>();
var orders = (await multi.ReadAsync<Order>()).ToList();
var addresses = (await multi.ReadAsync<Address>()).ToList();TypedQuery batches your queries into a single SQL script.
var result = await db
.ToTypedQuery()
.Add(new GetUserById(userId))
.Add(new GetOrdersByUserId(userId))
.Add(new GetAddressesByUserId(userId))
.ExecuteAsync();
var user = result.Get<UserDto>();
var orders = result.GetList<OrderDto>();
var addresses = result.GetList<AddressDto>();
// One roundtrip.This drives developers toward cleaner code while improving performance when multiple independent queries are needed together and database roundtrips are expensive.
dotnet add package TypedQueryor for EF Core integration =>
dotnet add package TypedQuery.EntityFrameworkCorepublic class GetUserById(int id) : ITypedQuery<UserDto>
{
public QueryDefinition Build(QueryBuildContext context)
{
return new QueryDefinition(
"SELECT Id, Name, Email FROM Users WHERE Id = @id",
new { id });
}
}public class GetOrdersByUserId(int userId) : ITypedQuery<AppDbContext, OrderDto>
{
public IQueryable<OrderDto> Query(AppDbContext db)
{
return db.Orders
.Where(o => o.UserId == userId)
.Select(o => new OrderDto { Id = o.Id, Total = o.Total });
}
}
⚠️ Important: To use EF Core queries, you must register the TypedQuery interceptor:services.AddDbContext<AppDbContext>(options => { options.UseSqlServer(connectionString); options.UseTypedQuery(); // Required for EF Core mode! });
var result = await dbContext
.ToTypedQuery()
.Add(new GetUserById(5))
.Add(new GetOrdersByUserId(5))
.ExecuteAsync();
var user = result.Get<UserDto>();
var orders = result.GetList<OrderDto>();var result = await connection
.ToTypedQuery()
.Add(new GetUserById(5))
.Add(new GetActiveProducts())
.ExecuteAsync();
var user = result.Get<UserDto>();
var products = result.GetList<ProductDto>();| Feature | Description |
|---|---|
| Single Roundtrip | Multiple queries executed in one database call |
| Type-Safe | Queries and results are strongly typed |
| Reusable | Queries are first-class objects |
| Clean Structure | No manual SQL concatenation |
| SQL Caching | EF Core queries compiled once, reused with Dapper |
| High Performance | Faster than sequential EF Core for multi-query scenarios |
| Scenario | Time | Comparison |
|---|---|---|
| EF Core Direct | 71 μs | Baseline |
| TypedQuery EF Core (Warm) | 26 μs | 2.7× faster |
| TypedQuery Raw SQL | 28 μs | 2.5× faster |
| TypedQuery EF Core (Cold) | 808 μs | One-time cost |
| EF Core Sequential (5 queries) | 373 μs | Baseline |
| TypedQuery Batched (5 queries) | 132 μs | 2.8× faster |
TypedQuery uses a dual-mode execution strategy:
-
Cold execution (first use)
- EF Core translates LINQ to SQL
- SQL template and parameter metadata are cached
-
Warm execution (subsequent uses)
- EF Core is skipped
- Cached SQL is executed via Dapper
Implications:
- Cold cost is paid once per query type
- Warm execution is significantly faster and allocates less memory
| Package | Description |
|---|---|
| TypedQuery.Abstractions | Core interfaces |
| TypedQuery | Query execution engine |
| TypedQuery.EntityFrameworkCore | EF Core integration and SQL caching |
Contributions are welcome! This library is in active development.
- Built on top of Dapper for query execution
- Inspired by the need for cleaner batch query patterns in .NET