-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[Blazor] OwningComponentBase Dispose method in .NET 10 gets back the original behavior
#64695
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
OwningComponentBase Dispose method in .NET 10 gets back the original behavior
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 <[email protected]>
oroztocil
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.)?
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.DisposeAsyncCore_Override_WithException_StillCallsDispose- Confirms that if a user's override ofDisposeAsyncCore()throws an exception,Dispose(true)is still called via thetry-finallyblock to ensure cleanup happens.ComplexComponent_DisposesResourcesOnlyWhenDisposingIsTrue- Demonstrates that with the fix (disposing=true), components with non-trivial disposal logic (timers, cancellation tokens, events) properly clean up managed resources.