You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
<!-- MySQL: Aspire/Pomelo integration on net9; the Microting fork (a Pomelo fork) fills the EF Core 10 / net10 gap until the official Pomelo/Aspire packages ship it. -->
Copy file name to clipboardExpand all lines: docs/articles/cqrs-pipeline.md
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -80,7 +80,7 @@ Pipeline behaviors wrap every handler invocation, allowing you to add cross-cutt
80
80
81
81
### Validation
82
82
83
-
`ValidationPipelineBehavior` runs all registered `IValidator<TCommand>` instances before the handler executes. When validation fails, it short-circuits and returns a`Result` containing a `ValidationError`:
83
+
`ValidationPipelineBehavior` runs all registered `IValidator<TCommand>` instances before the handler executes. When validation fails it short-circuits: a command returning`Result`or `Result<T>` receives a failed result containing a `ValidationError`, while a command with any other response type throws a `ValidationException` (there is no in-band way to represent failure for a non-result response):
@@ -102,7 +102,7 @@ No additional wiring is needed – the validator is picked up automatically from
102
102
103
103
### Transactional
104
104
105
-
`TransactionalPipelineBehavior` wraps `ITransactionalCommand` handlers inside a database transaction. If the handler succeeds, the transaction commits; if it throws, the transaction rolls back.
105
+
`TransactionalPipelineBehavior` wraps `ITransactionalCommand` handlers inside a database transaction. If the handler returns a successful `Result`, the transaction commits; if it returns a failed `Result` or throws, the transaction rolls back. (The non-generic `ITransactionalCommand` marker returns `Result` and is wrapped the same way as `ITransactionalCommand<Result>`.)
Copy file name to clipboardExpand all lines: docs/articles/domain-modeling.md
+2-5Lines changed: 2 additions & 5 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -14,13 +14,10 @@
14
14
15
15
## Defining an Entity
16
16
17
-
Entities are compared by identity, not by attribute values:
17
+
Entities are compared by identity, not by attribute values. Give the identifier value equality — a `record` or `readonly record struct` — so two entities with the same id compare as equal:
Copy file name to clipboardExpand all lines: docs/articles/outbox-pattern.md
+13-6Lines changed: 13 additions & 6 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -8,7 +8,7 @@ The transactional outbox pattern guarantees that domain events raised by aggrega
8
8
2. The aggregate root's event collection is cleared.
9
9
3. A background service (`OutboxBackgroundService`) relays unprocessed outbox messages — woken immediately once the captured rows are durable (on transaction commit, or right after a non-transactional `SaveChanges` that captured domain events) for low latency, and polling on an interval as the backstop.
10
10
4. Each message is routed by its `OutboxDestination` to the registered `IOutboxDispatcher` that handles it (in-process domain events by default, or the broker — see below).
11
-
5. Successfully relayed messages are marked as processed; failures are retried up to the configured maximum.
11
+
5. Successfully relayed messages are marked as processed; failures are retried up to the configured maximum, after which the message is dead-lettered — its `FailedOnUtc` timestamp is set, it is no longer relayed, and an error is logged with the last failure (the `Error` column).
12
12
13
13
This guarantees **at-least-once delivery** because the event and the business data are committed atomically.
The `OutboxMessage` entity configuration is applied automatically by `BaseDbContext.OnModelCreating`.
55
+
`BaseDbContext` owns the `OutboxMessages``DbSet`; apply the provider-optimized mapping in `OnModelCreating`by calling your provider's extension — `ApplyNpgsqlOutbox()`, `ApplyMySqlOutbox()`, or `ApplyCosmosOutbox()` (as shown above). The agnostic `ApplyOutbox()` is available for custom providers.
50
56
51
57
## Outbox Processing Options
52
58
53
59
| Property | Default | Description |
54
60
|---|---|---|
55
61
|`BatchSize`| 10 | Number of messages fetched per poll cycle |
56
-
|`MaxRetries`| 3 | Maximum publish attempts before a message is abandoned |
57
-
|`EnableParallelPublishing`|`false`| Publish messages in parallel within a batch |
62
+
|`MaxRetries`| 3 | Publish attempts before a message is dead-lettered (`FailedOnUtc` set, no longer relayed) |
63
+
|`EnableParallelPublishing`|`false`| Publish messages in parallel within a batch (each dispatch runs in its own DI scope) |
64
+
|`MaxDegreeOfParallelism`| 4 | Maximum concurrent dispatches when `EnableParallelPublishing` is enabled |
58
65
|`OutboxProcessingDelayInSeconds`| 2 | Base polling delay between processing cycles |
59
66
|`MaxDelaySeconds`| 60 | Maximum back-off delay when no messages are found |
60
67
|`EnableTracing`|`true`| Carry the originating trace identifier when publishing |
@@ -137,7 +144,7 @@ OutboxBackgroundService polls
137
144
↓
138
145
OutboxProcessor deserialises & publishes via IDomainEventPublisher
139
146
↓
140
-
Message marked as processed (or retried on failure)
147
+
Message marked as processed (or retried on failure, then dead-lettered after MaxRetries)
Copy file name to clipboardExpand all lines: docs/articles/packages/vulthil-messaging-inbox-cosmos.md
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -13,7 +13,7 @@ deduplication of redeliveries over **idempotent-by-design** consumer writes.
13
13
14
14
## Pattern
15
15
16
-
- Your `DbContext` implements `ISaveInboxMessages` and applies `CosmosInboxMessageEntityConfiguration`
16
+
- Your `DbContext` implements `ISaveInboxMessages` and applies the Cosmos inbox mapping via `ApplyCosmosInbox()`
17
17
- The marker is a self-contained document keyed + partitioned by `MessageId` in its own container; a duplicate insert conflicts and is treated as already-processed
18
18
- The marker is written after the consumer's own commit (no ambient transaction) — keep consumer writes idempotent
19
19
@@ -25,7 +25,7 @@ public sealed class AppDbContext(DbContextOptions<AppDbContext> options) : DbCon
Copy file name to clipboardExpand all lines: docs/articles/packages/vulthil-sharedkernel-api.md
+3-5Lines changed: 3 additions & 5 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -48,11 +48,10 @@ app.MapOpenApiEndpoints();
48
48
49
49
### Controller-based endpoints
50
50
51
-
Controllers can return typed results directly for OpenAPI documentation:
51
+
Derive from `BaseController` for the standard `[ApiController]`/route conventions and a `Logger` property resolved for the concrete controller type (no constructor logger argument required). Controllers can return typed results directly for OpenAPI documentation:
Copy file name to clipboardExpand all lines: docs/articles/packages/vulthil-sharedkernel-infrastructure-cosmos.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -11,7 +11,7 @@ Use `Vulthil.SharedKernel.Infrastructure.Cosmos` to run the shared infrastructur
11
11
## Pattern
12
12
13
13
- Call `UseCosmosDb("connectionName")` on the database infrastructure configurator – it both registers the EF Core context and selects the Cosmos outbox strategy
14
-
- Configure the Cosmos-specific entity model for `OutboxMessage` via `OutboxMessageEntityConfiguration` (applied automatically when `EnableOutboxProcessing` runs)
14
+
- Configure the Cosmos-specific entity model for `OutboxMessage` via the `ApplyCosmosOutbox()` model-builder extension (call it from your `OnModelCreating`)
15
15
- Order between `UseCosmosDb` and `EnableOutboxProcessing` does not matter; the configurator defers the underlying call until the full chain has executed
Copy file name to clipboardExpand all lines: docs/articles/packages/vulthil-sharedkernel-infrastructure-mysql.md
+29-11Lines changed: 29 additions & 11 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,39 +1,57 @@
1
1
# Vulthil.SharedKernel.Infrastructure.MySql
2
2
3
-
Use `Vulthil.SharedKernel.Infrastructure.MySql` to select the MySQL-tuned outbox strategy when your `DbContext`runs against MySQL.
3
+
Use `Vulthil.SharedKernel.Infrastructure.MySql` to wire a MySQL-backed `DbContext`together with the MySQL-tuned outbox strategy.
4
4
5
5
## When to use
6
6
7
7
- MySQL is the underlying database for an application's primary `DbContext`
8
-
- Outbox processing should use the MySQL strategy
8
+
- Outbox processing should use the MySQL strategy (row-level locking via `FOR UPDATE SKIP LOCKED`)
9
9
10
10
## Pattern
11
11
12
-
- Call `UseMySql()` on the database infrastructure configurator to swap in the MySQL outbox strategy
13
-
- Register the `DbContext` itself with your preferred MySQL EF Core integration (e.g. `Pomelo.EntityFrameworkCore.MySql`) – `UseMySql` only switches the outbox strategy and does not register the EF Core provider
14
-
- Apply migrations on startup with `MigrateAsync<TDbContext>()` from `Vulthil.SharedKernel.Infrastructure.Relational` (pulled in transitively)
12
+
- Call `UseMySql("ConnectionStringKey")` on the database infrastructure configurator – it registers a pooled EF Core context for the named connection string, selects the MySQL outbox strategy, and (when outbox processing is enabled) wires the commit-time relay trigger
13
+
- Order between `UseMySql` and `EnableOutboxProcessing` does not matter; the configurator defers the underlying registration until the full chain has executed
14
+
- Retrying execution strategies are fully supported – the outbox processor runs its transactional unit inside the context's execution strategy (`Database.CreateExecutionStrategy().ExecuteAsync`)
15
+
16
+
## Provider
17
+
18
+
The MySQL EF Core provider is resolved per target framework:
19
+
20
+
-**.NET 9** – the Aspire client integration [`Aspire.Pomelo.EntityFrameworkCore.MySql`](https://www.nuget.org/packages/Aspire.Pomelo.EntityFrameworkCore.MySql), which resolves the connection string and adds health checks, telemetry, and connection resiliency (parity with the Npgsql and Cosmos integrations)
21
+
-**.NET 10** – the API-compatible [`Microting.EntityFrameworkCore.MySql`](https://www.nuget.org/packages/Microting.EntityFrameworkCore.MySql) fork of Pomelo, registered directly, until the official Pomelo and Aspire packages ship EF Core 10 support
22
+
23
+
On .NET 10 the MySQL server version is detected from the connection at startup via `ServerVersion.AutoDetect`, so the database must be reachable when the host starts.
`UseMySql` takes an optional `configure` delegate. Because the Aspire integration only exists on .NET 9, its type differs per target framework — the Aspire `PomeloEntityFrameworkCoreMySqlSettings` on .NET 9, and the EF Core `MySqlDbContextOptionsBuilder` on .NET 10:
0 commit comments