File: OwningComponentBaseTest.cs
Web Access
Project: src\src\Components\Components\test\Microsoft.AspNetCore.Components.Tests.csproj (Microsoft.AspNetCore.Components.Tests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using Microsoft.AspNetCore.Components.Test.Helpers;
using Microsoft.Extensions.DependencyInjection;
 
namespace Microsoft.AspNetCore.Components;
 
public class OwningComponentBaseTest
{
    [Fact]
    public void CreatesScopeAndService()
    {
        var services = new ServiceCollection();
        services.AddSingleton<Counter>();
        services.AddTransient<MyService>();
        var serviceProvider = services.BuildServiceProvider();
 
        var counter = serviceProvider.GetRequiredService<Counter>();
        var renderer = new TestRenderer(serviceProvider);
        var component1 = (MyOwningComponent)renderer.InstantiateComponent<MyOwningComponent>();
 
        Assert.NotNull(component1.MyService);
        Assert.Equal(1, counter.CreatedCount);
        Assert.Equal(0, counter.DisposedCount);
 
        ((IDisposable)component1).Dispose();
        Assert.Equal(1, counter.CreatedCount);
        Assert.Equal(1, counter.DisposedCount);
    }
 
    [Fact]
    public async Task DisposeAsyncReleasesScopeAndService()
    {
        var services = new ServiceCollection();
        services.AddSingleton<Counter>();
        services.AddTransient<MyService>();
        var serviceProvider = services.BuildServiceProvider();
 
        var counter = serviceProvider.GetRequiredService<Counter>();
        var renderer = new TestRenderer(serviceProvider);
        var component1 = (MyOwningComponent)renderer.InstantiateComponent<MyOwningComponent>();
 
        Assert.NotNull(component1.MyService);
        Assert.Equal(1, counter.CreatedCount);
        Assert.Equal(0, counter.DisposedCount);
        Assert.False(component1.IsDisposedPublic);
 
        await ((IAsyncDisposable)component1).DisposeAsync();
        Assert.Equal(1, counter.CreatedCount);
        Assert.Equal(1, counter.DisposedCount);
        Assert.True(component1.IsDisposedPublic);
    }
 
    [Fact]
    public void ThrowsWhenAccessingScopedServicesAfterDispose()
    {
        var services = new ServiceCollection();
        services.AddSingleton<Counter>();
        services.AddTransient<MyService>();
        var serviceProvider = services.BuildServiceProvider();
 
        var renderer = new TestRenderer(serviceProvider);
        var component1 = (MyOwningComponent)renderer.InstantiateComponent<MyOwningComponent>();
 
        // Access service first to create scope
        var service = component1.MyService;
 
        ((IDisposable)component1).Dispose();
 
        // Should throw when trying to access services after disposal
        Assert.Throws<ObjectDisposedException>(() => component1.MyService);
    }
 
    [Fact]
    public async Task ThrowsWhenAccessingScopedServicesAfterDisposeAsync()
    {
        var services = new ServiceCollection();
        services.AddSingleton<Counter>();
        services.AddTransient<MyService>();
        var serviceProvider = services.BuildServiceProvider();
 
        var renderer = new TestRenderer(serviceProvider);
        var component1 = (MyOwningComponent)renderer.InstantiateComponent<MyOwningComponent>();
 
        // Access service first to create scope
        var service = component1.MyService;
 
        await ((IAsyncDisposable)component1).DisposeAsync();
 
        // Should throw when trying to access services after disposal
        Assert.Throws<ObjectDisposedException>(() => component1.MyService);
    }
 
    private class Counter
    {
        public int CreatedCount { get; set; }
        public int DisposedCount { get; set; }
    }
 
    private class MyService : IDisposable
    {
        public MyService(Counter counter)
        {
            Counter = counter;
            Counter.CreatedCount++;
        }
 
        public Counter Counter { get; }
 
        void IDisposable.Dispose() => Counter.DisposedCount++;
    }
 
    private class MyOwningComponent : OwningComponentBase<MyService>
    {
        public MyService MyService => Service;
 
        // Expose IsDisposed for testing
        public bool IsDisposedPublic => IsDisposed;
    }
}