@@ -110,7 +110,9 @@ The `IAggregateStore.UpdateAsync` method allows to load, modify and save the agg
110
110
``` csharp
111
111
public class TestController (IAggregateStore aggregateStore ) : ControllerBase
112
112
{
113
- public async Task Ping (Guid id )
113
+ public async Task Ping (
114
+ Guid id ,
115
+ CancellationToken cancellationToken )
114
116
{
115
117
var testId = TestId .With (id );
116
118
var sourceId = TestId .New ;
@@ -123,7 +125,7 @@ public class TestController(IAggregateStore aggregateStore) : ControllerBase
123
125
aggregate .Ping (" ping" );
124
126
return Task .CompletedTask ;
125
127
},
126
- CancellationToken . None );
128
+ cancellationToken );
127
129
}
128
130
}
129
131
```
@@ -157,14 +159,16 @@ await aggregateStore.StoreAsync<TestAggregate, TestId>(
157
159
CancellationToken .None );
158
160
```
159
161
160
- ### Using the CQRS approach.
162
+ ### Using the CQRS approach
161
163
162
164
Another way to change the aggregate is by following the CQRS (Command Query Responsibility Segregation) pattern.
163
165
164
166
``` csharp
165
167
public class TestController (ICommandBus commandBus ) : ControllerBase
166
168
{
167
- public async Task Ping (Guid id )
169
+ public async Task Ping (
170
+ Guid id ,
171
+ CancellationToken cancellationToken )
168
172
{
169
173
var testId = TestId .With (id );
170
174
@@ -175,10 +179,75 @@ public class TestController(ICommandBus commandBus) : ControllerBase
175
179
};
176
180
177
181
// Publish the command using the command bus
178
- await commandBus .PublishAsync (command , CancellationToken . None );
182
+ await commandBus .PublishAsync (command , cancellationToken );
179
183
}
180
184
}
181
185
```
182
186
183
187
For more details on commands and command handlers, check the [ documentation] ( ../basics/commands.md ) .
184
188
189
+ ## Reading aggregate events
190
+
191
+ To read events for a specific aggregate, you can use the ` IEventStore ` interface. This allows you to load events for an aggregate by its identity.
192
+
193
+ ### Load all events
194
+
195
+ In the example below, the ` GetAuditLog ` method loads ** all events** for the specified aggregate and maps them to a DTO.
196
+
197
+ ``` csharp
198
+ public class TestController (IEventStore eventStore ) : ControllerBase
199
+ {
200
+ public async Task <IEnumerable <EventDto >> GetAuditLog (
201
+ Guid id ,
202
+ CancellationToken cancellationToken )
203
+ {
204
+ var testId = TestId .With (id );
205
+
206
+ var events = await eventStore .LoadEventsAsync <TestAggregate , TestId >(
207
+ testId ,
208
+ cancellationToken );
209
+
210
+ return events .Select (e => new EventDto
211
+ {
212
+ EventType = e .EventType .Name ,
213
+ Timestamp = e .Timestamp
214
+ });
215
+ }
216
+ }
217
+
218
+ public class EventDto
219
+ {
220
+ public required string EventType { get ; init ; }
221
+ public required DateTimeOffset Timestamp { get ; init ; }
222
+ }
223
+ ```
224
+
225
+ ### Load events for a specific range
226
+
227
+ You can also load events for a specific range. In the example below, ` from ` and ` to ` represent the range of sequence numbers for the events you want to load. This can be useful for pagination or retrieving a specific subset of events for an aggregate.
228
+
229
+ ``` csharp
230
+ public class TestController (IEventStore eventStore ) : ControllerBase
231
+ {
232
+ public async Task <IEnumerable <EventDto >> GetEventsInRange (
233
+ Guid id ,
234
+ int from ,
235
+ int to ,
236
+ CancellationToken cancellationToken )
237
+ {
238
+ var testId = TestId .With (id );
239
+
240
+ var events = await eventStore .LoadEventsAsync <TestAggregate , TestId >(
241
+ testId ,
242
+ from ,
243
+ to ,
244
+ cancellationToken );
245
+
246
+ return events .Select (e => new EventDto
247
+ {
248
+ EventType = e .EventType .Name ,
249
+ Timestamp = e .Timestamp
250
+ });
251
+ }
252
+ }
253
+ ```
0 commit comments