File: Dispatcher.cs
Web Access
Project: src\src\Components\Components\src\Microsoft.AspNetCore.Components.csproj (Microsoft.AspNetCore.Components)
// 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.Rendering;
using Microsoft.AspNetCore.Components.RenderTree;
using Microsoft.AspNetCore.Components.Sections;
 
namespace Microsoft.AspNetCore.Components;
 
/// <summary>
/// Dispatches external actions to be executed on the context of a <see cref="Renderer"/>.
/// </summary>
public abstract class Dispatcher
{
    private SectionRegistry? _sectionRegistry;
 
    /// <summary>
    /// Creates a default instance of <see cref="Dispatcher"/>.
    /// </summary>
    /// <returns>A <see cref="Dispatcher"/> instance.</returns>
    public static Dispatcher CreateDefault() => new RendererSynchronizationContextDispatcher();
 
    /// <summary>
    /// Provides notifications of unhandled exceptions that occur within the dispatcher.
    /// </summary>
    internal event UnhandledExceptionEventHandler? UnhandledException;
 
    /// <summary>
    /// Gets the <see cref="Sections.SectionRegistry"/> associated with the dispatcher.
    /// </summary>
    internal SectionRegistry SectionRegistry => _sectionRegistry ??= new();
 
    /// <summary>
    /// Validates that the currently executing code is running inside the dispatcher.
    /// </summary>
    public void AssertAccess()
    {
        if (!CheckAccess())
        {
            throw new InvalidOperationException(
                "The current thread is not associated with the Dispatcher. " +
                "Use InvokeAsync() to switch execution to the Dispatcher when " +
                "triggering rendering or component state.");
        }
    }
 
    /// <summary>
    /// Returns a value that determines whether using the dispatcher to invoke a work item is required
    /// from the current context.
    /// </summary>
    /// <returns><c>true</c> if invoking is required, otherwise <c>false</c>.</returns>
    public abstract bool CheckAccess();
 
    /// <summary>
    /// Invokes the given <see cref="Action"/> in the context of the associated <see cref="Renderer"/>.
    /// </summary>
    /// <param name="workItem">The action to execute.</param>
    /// <returns>A <see cref="Task"/> that will be completed when the action has finished executing.</returns>
    public abstract Task InvokeAsync(Action workItem);
 
    /// <summary>
    /// Invokes the given <see cref="Func{TResult}"/> in the context of the associated <see cref="Renderer"/>.
    /// </summary>
    /// <param name="workItem">The asynchronous action to execute.</param>
    /// <returns>A <see cref="Task"/> that will be completed when the action has finished executing.</returns>
    public abstract Task InvokeAsync(Func<Task> workItem);
 
    /// <summary>
    /// Invokes the given <see cref="Func{TResult}"/> in the context of the associated <see cref="Renderer"/>.
    /// </summary>
    /// <param name="workItem">The function to execute.</param>
    /// <returns>A <see cref="Task{TResult}"/> that will be completed when the function has finished executing.</returns>
    public abstract Task<TResult> InvokeAsync<TResult>(Func<TResult> workItem);
 
    /// <summary>
    /// Invokes the given <see cref="Func{TResult}"/> in the context of the associated <see cref="Renderer"/>.
    /// </summary>
    /// <param name="workItem">The asynchronous function to execute.</param>
    /// <returns>A <see cref="Task{TResult}"/> that will be completed when the function has finished executing.</returns>
    public abstract Task<TResult> InvokeAsync<TResult>(Func<Task<TResult>> workItem);
 
    /// <summary>
    /// Called to notify listeners of an unhandled exception.
    /// </summary>
    /// <param name="e">The <see cref="UnhandledExceptionEventArgs"/>.</param>
    protected void OnUnhandledException(UnhandledExceptionEventArgs e)
    {
        ArgumentNullException.ThrowIfNull(e);
 
        UnhandledException?.Invoke(this, e);
    }
}