KEMBAR78
Dispose IServiceScope when window is destroyed to prevent memory leaks by Copilot · Pull Request #30196 · dotnet/maui · GitHub
Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Jun 25, 2025

Note

Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment if this change resolves your issue. Thank you!

Description

This PR implements proper disposal of IServiceScope instances when windows are destroyed, addressing a memory leak where window-scoped services were never being disposed.

Problem

Currently, .NET MAUI creates window-scoped service scopes in MauiContextExtensions.MakeWindowScope() but never disposes them when the associated window is closed or destroyed. This leads to memory leaks for services registered with scoped lifetime.

The issue was marked with a TODO comment in the code:

// TODO: We need to dispose of these services once the window closes
scope = mauiContext.Services.CreateScope();

Solution

  1. Added scope tracking to MauiContext: Added internal methods SetWindowScope() and DisposeWindowScope() to track the window-scoped service scope
  2. Updated MakeWindowScope: Modified to store the created scope in the MauiContext for later disposal
  3. Added disposal logic: Updated Window.Destroying() to dispose the scope before disconnecting the handler
  4. Maintained API compatibility: Used internal methods to avoid breaking public API surface

Testing

Added comprehensive tests to verify:

  • Service scopes are properly disposed when windows are destroyed
  • Null safety when no scope is present
  • Full integration with the window creation flow
  • Scoped service lifetime behavior

All existing tests continue to pass (53 window-related tests ✅).

Example Usage

The fix is transparent to users - no code changes required. Services registered with scoped lifetime will now be properly disposed when windows are destroyed:

// Services registered like this will now be properly disposed
services.AddScoped<MyWindowScopedService>();

Fixes #8538.


💬 Share your feedback on Copilot coding agent for the chance to win a $200 gift card! Click here to start the survey.

Copilot AI changed the title [WIP] IServiceScope should get disposed when the associated window is destroyed or closed Dispose IServiceScope when window is destroyed to prevent memory leaks Jun 25, 2025
Copilot AI requested a review from PureWeen June 25, 2025 12:19
Copilot finished work on behalf of PureWeen June 25, 2025 12:19
@PureWeen PureWeen changed the base branch from main to net10.0 August 4, 2025 19:45
@PureWeen PureWeen marked this pull request as ready for review August 4, 2025 19:45
@Copilot Copilot AI review requested due to automatic review settings August 4, 2025 19:45
@PureWeen PureWeen requested a review from a team as a code owner August 4, 2025 19:45
@PureWeen PureWeen requested a review from rmarinho August 4, 2025 19:45
@PureWeen PureWeen added this to the .NET 10.0-rc1 milestone Aug 4, 2025
@PureWeen PureWeen moved this from Todo to Ready To Review in MAUI SDK Ongoing Aug 4, 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 addresses a memory leak in .NET MAUI by implementing proper disposal of window-scoped service scopes when windows are destroyed. Previously, service scopes created for windows were never disposed, leading to memory leaks for scoped services.

  • Added internal scope tracking methods to MauiContext to store and dispose window scopes
  • Modified window destruction flow to dispose service scopes before disconnecting handlers
  • Added comprehensive tests to verify scope disposal behavior and integration

Reviewed Changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
src/Core/src/MauiContextExtensions.cs Updates MakeWindowScope to store the created scope in the context for later disposal
src/Core/src/MauiContext.cs Adds internal methods SetWindowScope() and DisposeWindowScope() to track window-scoped service scopes
src/Controls/tests/Core.UnitTests/WindowsTests.cs Adds comprehensive tests for scope disposal functionality and edge cases
src/Controls/src/Core/Window/Window.cs Implements scope disposal in IWindow.Destroying() before handler disconnection
Comments suppressed due to low confidence (2)

src/Controls/tests/Core.UnitTests/WindowsTests.cs:796

  • This test validates scope disposal by checking for ObjectDisposedException, but it should also verify that the MauiContext.DisposeWindowScope() method was actually called. Consider adding a mock or spy to verify the disposal method invocation.
			Assert.Throws<ObjectDisposedException>(() => scope.ServiceProvider.GetService<TestScopedService>());

src/Controls/tests/Core.UnitTests/WindowsTests.cs:774

  • The test creates a service scope manually but doesn't test the actual window creation flow through MakeWindowScope(). Consider testing the complete integration by using MakeWindowScope() to create the window context as it would be used in production.
			var serviceCollection = new ServiceCollection();

@rmarinho
Copy link
Member

rmarinho commented Aug 7, 2025

/azp run

@dotnet dotnet deleted a comment from azure-pipelines bot Aug 7, 2025
@dotnet dotnet deleted a comment from jsuarezruiz Aug 7, 2025
@azure-pipelines
Copy link

Azure Pipelines successfully started running 3 pipeline(s).

@PureWeen PureWeen added p/0 Work that we can't release without platform/macos macOS / Mac Catalyst platform/windows platform/android area-core-lifecycle XPlat and Native UIApplicationDelegate/Activity/Window lifecycle events platform/ios labels Aug 7, 2025
{
readonly WrappedServiceProvider _services;
readonly Lazy<IMauiHandlersFactory> _handlers;
IServiceScope? _windowScope;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be a WeakReference ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rmarinho no, the service scope's life is already tied to the window, it's not something that's just going to naturally finalize and dispose.

Having this here doesn't cause a memory leak

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using a WeakReference here wouldn't be appropriate because we need to maintain a strong reference to the scope to ensure proper disposal. If we used a WeakReference, the scope could be garbage collected before we dispose it, potentially causing memory leaks. The scope needs to stay alive until we explicitly dispose it when the window is destroyed.


internal void DisposeWindowScope()
{
_windowScope?.Dispose();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot set this to null after using this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done! Set _windowScope = null after disposal to clear the reference. Commit: c0f7dc0

Copilot AI requested a review from PureWeen August 13, 2025 06:39
Copilot finished work on behalf of PureWeen August 13, 2025 06:39
// Dispose the window-scoped service scope
if (Handler?.MauiContext is MauiContext mauiContext)
mauiContext.DisposeWindowScope();

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot change this block to be like this

var mauiContext = Handler?.MauiContext as MauiContext;
			Handler?.DisconnectHandler();

			// Dispose the window-scoped service scope
			mauiContext?.DisposeWindowScope();

we want to call mauiContext.DisposeWindowScope(); after we call Handler?disconnecthandler

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done! Changed the order of operations as requested - now we get the MauiContext reference first, disconnect the handler, and then dispose the scope. Commit: 6f1b547

Copy link
Contributor Author

Copilot AI commented Aug 13, 2025

Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details.

Warning

Firewall rules blocked me from connecting to one or more addresses

I tried to connect to the following addresses, but was blocked by firewall rules:

  • services.gradle.org

If you need me to access, download, or install something from one of these locations, you can either:

Copilot finished work on behalf of PureWeen August 13, 2025 07:15
Copilot AI requested a review from PureWeen August 13, 2025 07:15
Copilot AI and others added 5 commits August 13, 2025 02:48
Co-authored-by: PureWeen <5375137+PureWeen@users.noreply.github.com>

# Conflicts:
#	src/Controls/tests/Core.UnitTests/WindowsTests.cs
Co-authored-by: PureWeen <5375137+PureWeen@users.noreply.github.com>
Co-authored-by: PureWeen <5375137+PureWeen@users.noreply.github.com>
…r disconnecting handler

Co-authored-by: PureWeen <5375137+PureWeen@users.noreply.github.com>
@PureWeen
Copy link
Member

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 3 pipeline(s).

@github-project-automation github-project-automation bot moved this from Ready To Review to Approved in MAUI SDK Ongoing Aug 13, 2025
@rmarinho
Copy link
Member

@PureWeen can the failure tests be related?

@PureWeen PureWeen merged commit 719dc33 into net10.0 Aug 14, 2025
129 checks passed
@PureWeen PureWeen deleted the copilot/fix-8538 branch August 14, 2025 21:31
@github-project-automation github-project-automation bot moved this from Approved to Done in MAUI SDK Ongoing Aug 14, 2025
@github-actions github-actions bot locked and limited conversation to collaborators Sep 14, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-core-lifecycle XPlat and Native UIApplicationDelegate/Activity/Window lifecycle events p/0 Work that we can't release without platform/android platform/ios platform/macos macOS / Mac Catalyst platform/windows

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

IServiceScope should get disposed when the associated window is destroyed or closed

3 participants