[Blazor] OwningComponentBase Dispose method in .NET 10 gets back the original behavior#64695
[Blazor] OwningComponentBase Dispose method in .NET 10 gets back the original behavior#64695ilonatommy merged 11 commits intodotnet:mainfrom
OwningComponentBase Dispose method in .NET 10 gets back the original behavior#64695Conversation
OwningComponentBase Dispose method in .NET 10 gets back the original behavior
There was a problem hiding this comment.
Pull request overview
This PR fixes a backward compatibility issue introduced in .NET 10 where components overriding Dispose(bool disposing) with logic inside if (disposing) { ... } blocks stopped working correctly. The framework now calls DisposeAsync() instead of Dispose(), and the previous implementation passed disposing=false to avoid double-disposal, which broke user code expecting disposing=true.
Key Changes:
- Modified
DisposeAsync()to callDispose(disposing: true)instead ofDispose(disposing: false)to restore backward compatibility with .NET 9 behavior - Moved
IsDisposed = trueassignment to theDispose(bool)method to centralize disposal state management - Fixed two spelling errors in XML documentation comments ("inhertidoc" → "inheritdoc")
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| src/Components/Components/src/OwningComponentBase.cs | Updated disposal pattern to call Dispose(true) from DisposeAsync() for backward compatibility, centralized IsDisposed flag management, and fixed XML doc comment typos |
| src/Components/Components/test/OwningComponentBaseTest.cs | Added regression test to verify Dispose(bool disposing) receives disposing=true when disposed via DisposeAsync() |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
oroztocil
left a comment
There was a problem hiding this comment.
Do we have any components with non-trivial dispose logic on hand? So that we could check that they do not break after change (with double dispose, not executing some code that was executed before, etc.)?
|
We don't have to protect against double Dispose; the framework is responsible for correctly disposing the components only Once. Ideally we should just be passing Dispose(disposing: true) instead of false. |
|
/backport to release/10.0 |
|
Started backporting to |
Fixes #64669.
In .NET 9, when a Blazor component was disposed, it called
IDisposable.Dispose()which invokedDispose(bool disposing)withdisposing=true. In .NET 10, after PR #62583 addedIAsyncDisposablesupport, the Blazor framework started callingIAsyncDisposable.DisposeAsync()instead. Following the standard .NET dispose pattern,DisposeAsync()calledDispose(false)to avoid double-disposal of managed resources. This broke user code that overrodeDispose(bool disposing)and expecteddisposing=truewhen the component was being cleaned up, causing any logic insideif (disposing) { ... }blocks to no longer execute. The fix ensuresDispose(true)is called fromDisposeAsync()to maintain backward compatibility while still properly handling async disposal of the service scope.Changes
Dispose(true)fromDisposeAsync()to ensure user overrides ofDispose(bool disposing)receive [disposing=true], maintaining backward compatibility with .NET 9 behavior.IsDisposed = truefromDisposeAsyncCore()]- the responsibility of setting this flag is in theDispose(bool disposing)method to avoid duplication and ensure consistency whether disposal happens via sync or async path.Tests
DisposeAsync_CallsDispose_WithDisposingTrue- Verifies thatDisposeAsync()passesdisposing=trueto user overrides ofDispose(bool disposing), which is the core fix for issue.DisposeAsync_ThenDispose_IsIdempotent- Ensures that calling bothDisposeAsync()andDispose()doesn't cause double-disposal of resources, proving the implementation is safe for multiple disposal calls.ComplexComponent_DisposesResourcesOnlyWhenDisposingIsTrue- Demonstrates that with the fix (disposing=true), components with non-trivial disposal logic (timers, cancellation tokens, events) properly clean up managed resources.