-
-
Notifications
You must be signed in to change notification settings - Fork 10
5. Asynchronous programming
All available extensions that can execute SQL have asynchronous versions with the name ending with Async suffix.
All asynchronous overloads of ExecuteAsync and SingleAsync extensions are returning ValueTask that represents an asynchronous operation that can return a value.
Anyone familiar with asynchronous programming in .NET will have no problem using these extensions, just follow standard async await concept.
All asynchronous overloads of ReadAsync and QueryAsync are NOT returning Task or ValueTask.
Instead, they will build an asynchronous iterator that returns IAsyncEnumerable instead.
IAsyncEnumerable interface allows for asynchronous streaming - which you can use in Norm to create an asynchronous stream from your database.
Examples:
// Asynchronously stream values directly from database
await foreach(var (orderId, productId, quantity) in connection.ReadAsync<int, int, int>("SELECT TOP 10 OrderId, ProductId, Quantity FROM OrderDetails"))
{
Console.WriteLine($"order={orderId}, product={productId} with quantity {quantity}");
}public record OrderDetail(int OrderId, int ProductId, int Quantity);
//...
// Asynchronously stream instances directly from database
await foreach(var d in connection.ReadAsync<OrderDetail>("SELECT TOP 10 OrderId, ProductId, Quantity FROM OrderDetails"))
{
Console.WriteLine($"order={d.OrderId}, product={d.ProductId} with quantity {d.Quantity}");
}This await foreach language construct will create an asynchronous stream that will return values from your query as they appear on your connection.
Synchronous extensions are returning normal enumerators which means - you can use the standard LINQ library to build an expression tree for your enumerator.
Most common examples are creating a list, creating a dictionary, or just using the first or default value:
using System.Linq;
using Norm;
// list of instances
var list = await connection.ReadAsync<Customer>("SELECT * FROM Customers").ToListAsync();
// dictionary of values
var dict = await connection.ReadAsync<(int id, string value)>("select CustomerID, CustomerName from Customers").ToDictionaryAsync(t => t.id, t => t.value);
// first or default value
var id = await connection.ReadAsync<int>("select CustomerID from Customers").FirstOrDefaultAsync();However, asynchronous versions QueryAsync and ReadAsync do not return standard enumerator IEnumerable but IAsyncEnumerable - so we can't use standard LINQ expressions.
The solution is to reference an System.Linq.Async into the project.
This library is created and supported by the ".NET Foundation and Contributors" and it provides support for Language-Integrated Query (LINQ) over IAsyncEnumerable<T> sequences. It implements - all the same extensions as the standard LINQ library.
And it also uses the same namespace.
The same example from above would look something like this:
using System.Linq;
using Norm;
// list of instances
var list = await connection.ReadAsync<Customer>("SELECT * FROM Customers").ToListAsync();
// dictionary of values
var dict = await connection.ReadAsync<(int id, string value)>("select CustomerID, CustomerName from Customers").ToDictionaryAsync(t => t.id, t => t.value);
// first or default value
var id = await connection.ReadAsync<int>("select CustomerID from Customers").FirstOrDefaultAsync();- 2.1. Execute(command, params) connection extension
- 2.2. Read(command, params) connection extensions
- 2.3. Multiple(command, params) connection extension
- 3.1. Working with basic type parameters
- 3.2. Working with class and record type parameters
- 3.3. Working with tuple type parameters
- 4.1. Working with parameters
- 4.2. Working with array types
- 6. Utility extensions