Skip to content

Conversation

@ilonatommy
Copy link
Member

@ilonatommy ilonatommy commented Dec 8, 2025

Fixes #64669.

In .NET 9, when a Blazor component was disposed, it called IDisposable.Dispose() which invoked Dispose(bool disposing) with disposing=true. In .NET 10, after PR #62583 added IAsyncDisposable support, the Blazor framework started calling IAsyncDisposable.DisposeAsync() instead. Following the standard .NET dispose pattern, DisposeAsync() called Dispose(false) to avoid double-disposal of managed resources. This broke user code that overrode Dispose(bool disposing) and expected disposing=true when the component was being cleaned up, causing any logic inside if (disposing) { ... } blocks to no longer execute. The fix ensures Dispose(true) is called from DisposeAsync() to maintain backward compatibility while still properly handling async disposal of the service scope.

Changes

  • Call Dispose(true) from DisposeAsync() to ensure user overrides of Dispose(bool disposing) receive [disposing=true], maintaining backward compatibility with .NET 9 behavior.
  • Removed IsDisposed = true from DisposeAsyncCore()] - the responsibility of setting this flag is in the Dispose(bool disposing) method to avoid duplication and ensure consistency whether disposal happens via sync or async path.

Tests

  • DisposeAsync_CallsDispose_WithDisposingTrue - Verifies that DisposeAsync() passes disposing=true to user overrides of Dispose(bool disposing), which is the core fix for issue.
  • DisposeAsync_ThenDispose_IsIdempotent - Ensures that calling both DisposeAsync() and Dispose() 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 of DisposeAsyncCore() throws an exception, Dispose(true) is still called via the try-finally block 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.

@ilonatommy ilonatommy added this to the .NET 11 Planning milestone Dec 8, 2025
@ilonatommy ilonatommy self-assigned this Dec 8, 2025
@ilonatommy ilonatommy requested a review from a team as a code owner December 8, 2025 11:30
Copilot AI review requested due to automatic review settings December 8, 2025 11:30
@github-actions github-actions bot added the area-blazor Includes: Blazor, Razor Components label Dec 8, 2025
@ilonatommy ilonatommy changed the title Fix 64669 [Blazor] OwningComponentBase Dispose method in .NET 10 gets back the original behavior Dec 8, 2025
Copy link
Contributor

Copilot AI left a 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 call Dispose(disposing: true) instead of Dispose(disposing: false) to restore backward compatibility with .NET 9 behavior
  • Moved IsDisposed = true assignment to the Dispose(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()

Copy link
Member

@oroztocil oroztocil left a 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.)?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-blazor Includes: Blazor, Razor Components

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Blazor] OwningComponentBase Dispose method in .NET 10 differs from .NET9

3 participants