diff --git a/CHANGELOG.md b/CHANGELOG.md index e46f932a..4d1acb34 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,9 +21,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [0.22.0] - 2026-08-04 -Repository consolidation only — the `Wolfgang.Etl.TestKit` and `Wolfgang.Etl.TestKit.Xunit` packages now -build and release from this repository. **No public API change** to any of the four packages; a -release-source move, not a behavioural change. +Repository consolidation — the `Wolfgang.Etl.TestKit` and `Wolfgang.Etl.TestKit.Xunit` packages now +build and release from this repository. **No public API change** to any of the four packages. The +consolidation itself is a release-source move; it ships alongside one behavioural fix, noted below. + +### Fixed + +- **`await foreach` in `ExtractorBase` and `TransformerBase` now uses `ConfigureAwait(false)`.** Four + sites (`ExtractorBase.ExtractAsync` / `ExtractWithProgressAsync`, `TransformerBase.TransformAsync` / + `TransformWithProgressAsync`) resumed on the captured synchronization context, a deadlock risk for + consumers calling sync-over-async on the `net462` and `netstandard2.0` targets. `CA2007` does not + analyse `await foreach`, so this was invisible to the analyzer gate; the rest of the package already + used `ConfigureAwait(false)` at every other `await foreach`. ### Changed diff --git a/src/Wolfgang.Etl.Abstractions/ExtractorBase.cs b/src/Wolfgang.Etl.Abstractions/ExtractorBase.cs index 5dbccf69..1b1eb0cb 100644 --- a/src/Wolfgang.Etl.Abstractions/ExtractorBase.cs +++ b/src/Wolfgang.Etl.Abstractions/ExtractorBase.cs @@ -323,7 +323,7 @@ [EnumeratorCancellation] CancellationToken token { ResetRunState(); - await foreach (var item in WrapWorkerExecution(ExtractWorkerAsync, token)) + await foreach (var item in WrapWorkerExecution(ExtractWorkerAsync, token).ConfigureAwait(false)) { yield return item; } @@ -341,7 +341,7 @@ private async IAsyncEnumerable ExtractWithProgressAsync( try { - await foreach (var item in WrapWorkerExecution(ExtractWorkerAsync, token)) + await foreach (var item in WrapWorkerExecution(ExtractWorkerAsync, token).ConfigureAwait(false)) { yield return item; } diff --git a/src/Wolfgang.Etl.Abstractions/TransformerBase.cs b/src/Wolfgang.Etl.Abstractions/TransformerBase.cs index 95e4a1ef..7e6eef45 100644 --- a/src/Wolfgang.Etl.Abstractions/TransformerBase.cs +++ b/src/Wolfgang.Etl.Abstractions/TransformerBase.cs @@ -331,7 +331,7 @@ [EnumeratorCancellation] CancellationToken token { ResetRunState(); - await foreach (var item in WrapWorkerExecution(ct => TransformWorkerAsync(items, ct), token)) + await foreach (var item in WrapWorkerExecution(ct => TransformWorkerAsync(items, ct), token).ConfigureAwait(false)) { yield return item; } @@ -349,7 +349,7 @@ private async IAsyncEnumerable TransformWithProgressAsync( try { - await foreach (var item in WrapWorkerExecution(ct => TransformWorkerAsync(items, ct), token)) + await foreach (var item in WrapWorkerExecution(ct => TransformWorkerAsync(items, ct), token).ConfigureAwait(false)) { yield return item; }