Skip to content

feat: IAsyncDisposable/IDisposable on base classes (#92)#248

Merged
Chris-Wolfgang merged 3 commits into
mainfrom
feature/async-disposable
Jun 24, 2026
Merged

feat: IAsyncDisposable/IDisposable on base classes (#92)#248
Chris-Wolfgang merged 3 commits into
mainfrom
feature/async-disposable

Conversation

@Chris-Wolfgang

Copy link
Copy Markdown
Owner

Adds IAsyncDisposable + IDisposable to all three base classes. Closes #92.

Stacked on #247 (base feature/reset-run-state) — it's the tip of the base-class change chain (#244#247 → this).

What

ExtractorBase, LoaderBase, TransformerBase now implement both interfaces with the standard dispose pattern and a default no-op:

  • virtual ValueTask DisposeAsync() and void Dispose()Dispose(true) + GC.SuppressFinalize
  • protected virtual void Dispose(bool disposing) — base just marks disposed (idempotent); derived classes override to release the resources they own (streams, DB connections, HTTP clients, …) and call base.Dispose(disposing)
await using var extractor = new DatabaseExtractor(connectionString);
await foreach (var row in extractor.ExtractAsync(token)) { }

Every component works with await using out of the box, and base-class stages are now disposable via the #133 DisposeStagesOnCompletion path — the two features compose.

Compatibility

MINOR, source-compatible but binary-breaking (adding interfaces to a base class requires consumers to recompile; subclasses that already implement IDisposable switch to the override keyword). Needs a call-out in the 0.14.0 release notes. IAsyncDisposable is available on all TFMs via the existing Microsoft.Bcl.AsyncInterfaces reference.

Verified

  • Full Release build clean across all TFMs (0 warnings); PublicAPI.Shipped.txt regenerated.
  • 239 tests pass (5 new: interfaces present, default no-op is safe, override invoked via Dispose() / DisposeAsync() / await using).

Part of: #154

ExtractorBase, LoaderBase, and TransformerBase now implement IAsyncDisposable
and IDisposable with the standard dispose pattern and a default no-op:
- virtual ValueTask DisposeAsync() / void Dispose() -> Dispose(true) +
  SuppressFinalize
- protected virtual void Dispose(bool disposing) — base marks disposed
  (idempotent); derived classes override to release streams/connections/etc.

Every component now works with 'await using' out of the box, and base-class
stages are disposable via the #133 DisposeStagesOnCompletion path.

MINOR / source-compatible but BINARY-BREAKING (adds interfaces to the base
classes — consumers recompile; subclasses that already implement IDisposable
switch to 'override'). Call out in 0.14.0 release notes. PublicAPI.Shipped.txt
regenerated. Verified: full Release build clean across all TFMs (0 warnings);
239 tests pass (5 new disposal tests).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Base automatically changed from feature/reset-run-state to main June 24, 2026 01:25
Chris-Wolfgang and others added 2 commits June 23, 2026 21:25
The dispose pattern was added to all three base classes but only the extractor's
dispose was tested, dropping LoaderBase (85.9%) and TransformerBase (86.4%) under
the 90% per-class coverage gate. Adds ResourceOwningLoader / ResourceOwningTransformer
doubles asserting Dispose()/DisposeAsync() invoke the override and the interfaces
are implemented. Both classes now ~98% line coverage. 249 tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add IAsyncDisposable support to base classes

1 participant