Skip to content

Commit 4fe295c

Browse files
committed
update blog posts
1 parent c7e864f commit 4fe295c

12 files changed

+4173
-11
lines changed

MyApp/_posts/2025-10-15_ormlite-new-configuration.md

Lines changed: 483 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
---
2+
title: RDBMS Async Tasks Builder
3+
summary: Effortlessly run multiple async DB Requests in parallel with OrmLite's new Async Tasks Builder
4+
tags: [db,ormlite]
5+
author: Demis Bellot
6+
image: https://servicestack.net/img/posts/ormlite-async-task-builder/bg.webp
7+
---
8+
9+
### Sequential Async DB Access
10+
11+
Async improves I/O thread utilization in multi-threaded apps like Web Servers. However, it doesn't improve the performance
12+
of individual API Requests that need to execute multiple independent DB Requests. These are often written to run async
13+
db access sequentially like this:
14+
15+
```csharp
16+
var rockstars = await Db.SelectAsync<Rockstar>();
17+
var albums = await Db.SelectAsync<Album>();
18+
var departments = await Db.SelectAsync<Department>();
19+
var employees = await Db.SelectAsync<Employee>();
20+
```
21+
22+
The issue being that it's not running them in parallel as each DB Request is executed sequentially with the Request for
23+
Albums not starting until the Request for Rockstars has completed.
24+
25+
To run them in parallel you would need to open multiple scoped DB Connections, await them concurrently then do the
26+
syntax boilerplate gymnastics required to extract the generic typed results, e.g:
27+
28+
```csharp
29+
var connections = await Task.WhenAll(
30+
DbFactory.OpenDbConnectionAsync(),
31+
DbFactory.OpenDbConnectionAsync(),
32+
DbFactory.OpenDbConnectionAsync(),
33+
DbFactory.OpenDbConnectionAsync()
34+
);
35+
36+
using var dbRockstars = connections[0];
37+
using var dbAlbums = connections[1];
38+
using var dbDepartments = connections[2];
39+
using var dbEmployees = connections[3];
40+
41+
var tasks = new List<Task>
42+
{
43+
dbRockstars.SelectAsync<Rockstar>(),
44+
dbAlbums.SelectAsync<Album>(),
45+
dbDepartments.SelectAsync<Department>(),
46+
dbEmployees.SelectAsync<Employee>()
47+
};
48+
await Task.WhenAll(tasks);
49+
50+
var rockstars = ((Task<List<Rockstar>>)tasks[0]).Result;
51+
var albums = ((Task<List<Album>>)tasks[1]).Result;
52+
var departments = ((Task<List<Department>>)tasks[2]).Result;
53+
var employees = ((Task<List<Employee>>)tasks[3]).Result;
54+
```
55+
56+
Even without Error handling, writing coding like this can quickly become tedious, less readable and error prone that
57+
as a result is rarely done.
58+
59+
### Parallel DB Requests in TypeScript
60+
61+
This is easier to achieve in languages like TypeScript where typed ORMs like [litdb.dev](https://litdb.dev)
62+
can run multiple DB Requests in parallel with just:
63+
64+
```csharp
65+
const [rockstars, albums, departments, employees] = await Promise.all([
66+
db.all<Rockstar>($.from(Rockstar)), //= Rockstar[]
67+
db.all<Album>($.from(Album)), //= Album[]
68+
db.all<Department>($.from(Department)), //= Department[]
69+
db.all<Employee>($.from(Employee)), //= Employee[]
70+
])
71+
```
72+
73+
Which benefits from TypeScript's powerful type system that allows destructuring arrays whilst preserving their positional types,
74+
whilst its single threaded event loop lets you reuse the same DB Connection to run DB Requests in parallel without
75+
multi-threading issues.
76+
77+
## OrmLite's new Async Tasks Builder
78+
79+
OrmLite's new `AsyncDbTasksBuilder` provides a similar benefit of making it effortless to run multiple async DB Requests
80+
in parallel, which looks like:
81+
82+
```csharp
83+
var results = await DbFactory.AsyncDbTasksBuilder()
84+
.Add(db => db.SelectAsync<Album>())
85+
.Add(db => db.SelectAsync<Rockstar>())
86+
.Add(db => db.SelectAsync<Employee>())
87+
.Add(db => db.SelectAsync<Department>())
88+
.RunAsync();
89+
var (albums, rockstars, employees, departments) = results;
90+
```
91+
92+
Which just like TypeScript's destructuring returns a positionally typed tuple of the results which can be destructured back
93+
into their typed variables, e.g:
94+
95+
```csharp
96+
(List<Album> albums,
97+
List<Rockstar> rockstars,
98+
List<Employee> employees,
99+
List<Department> departments) = results;
100+
```
101+
102+
### Supports up to 8 Tasks
103+
104+
It allows chaining up to **8 async Tasks in parallel** as C#'s Type System doesn't allow for preserving different
105+
positional generic types in an unbounded collection. Instead each Task returns a new Generic Type builder which preserves
106+
the positional types before it.
107+
108+
### Supports both Async `Task<T>` and `Task` APIs
109+
110+
Where `Task<T>` and `Task` APIs can be mixed and matched interchangeably:
111+
112+
```csharp
113+
var builder = DbFactory.AsyncDbTasksBuilder()
114+
.Add(db => db.InsertAsync(rockstars[0],rockstars[1]))
115+
.Add(db => db.SelectAsync<Rockstar>())
116+
.Add(db => db.InsertAsync(albums[2],albums[3]))
117+
.Add(db => db.SelectAsync<Album>())
118+
.Add(db => db.InsertAsync([department]))
119+
.Add(db => db.SelectAsync<Department>())
120+
.Add(db => db.InsertAsync([employee]))
121+
.Add(db => db.SelectAsync<Employee>());
122+
```
123+
124+
Where to preserve the results chain, `Task` APIs return `bool` results, e.g:
125+
126+
```csharp
127+
(bool r1,
128+
List<Rockstar> r2,
129+
bool r3,
130+
List<RockstarAlbum> r4,
131+
bool r5,
132+
List<Department> r6,
133+
bool r7,
134+
List<Employee> r8) = await builder.RunAsync();
135+
```
136+
137+
### Error Handling
138+
139+
Whilst tasks are executed in parallel when they're added, any Exceptions are only thrown when the task is awaited:
140+
141+
```csharp
142+
using var Db = await OpenDbConnectionAsync();
143+
144+
var builder = DbFactory.AsyncDbTasksBuilder()
145+
.Add(db => db.InsertAsync(rockstars[0]))
146+
.Add(db => db.InsertAsync(rockstars[0])); // <-- Duplicate PK Exception
147+
148+
// Exceptions are not thrown until the task is awaited
149+
try
150+
{
151+
var task = builder.RunAsync();
152+
}
153+
catch (Exception e)
154+
{
155+
throw;
156+
}
157+
```
158+
159+
---
160+
161+
👈 [OrmLite's new Configuration Model](/posts/ormlite-new-configuration)

0 commit comments

Comments
 (0)