question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[API Proposal] Allow Dispatching to Blazor Renderer Sync Context

See original GitHub issue

Background and Motivation

Currently, there is not an easy or clean way to marshal exceptions occurring off the Blazor sync context back onto it. The following API would allow developers to directly dispatch exceptions to the renderer.

Fix for #44920

Related community requests: #44871, #27716

Proposed API

namespace Microsoft.AspNetCore.Components;

public abstract class ComponentBase : IComponent, IHandleEvent, IHandleAfterRender
{
+    protected Task DispatchExceptionAsync(Exception exception);
}

public readonly struct RenderHandle
{
+    public Task DispatchExceptionAsync(Exception exception);
}

Usage Examples

// Some code in a component
try
{
    await InvokeAsync(async () => { /* some code that may throw, possibly asynchronously */ });
}
catch (Exception e)
{
    await DispatchExceptionAsync(e);
}

Alternative Designs

An alternative would be to have a method Dispatch() that is used like InvokeAsync() but returns void instead of a task. This would require fewer lines of code to use it, but also would not allow the developer to get a result back from their work item or know when the async operation is completed.

Risks

None that I can think of. Does not cause a breaking change, nor should cause a performance regression.

Issue Analytics

  • State:closed
  • Created 8 months ago
  • Reactions:4
  • Comments:5 (5 by maintainers)

github_iconTop GitHub Comments

1reaction
halter73commented, Jan 20, 2023

InvokeAsync returns a task. So if the invoked call throws, the returned task will be in a failed state with this exception. If we didn’t do that, there would be no way for the caller to handle the exceptions.

I was thinking it could rethrow, but I did not realize how fatal these dispatched exceptions are. After reading your explanation about this treating the exceptions as equivalent to exceptions in lifecycle methods and then reading https://learn.microsoft.com/en-us/aspnet/core/blazor/fundamentals/handle-errors?view=aspnetcore-7.0#lifecycle-methods, this API makes more sense to me. This is not something you’d want to do by default when the caller to InvokeAsync may be able to handle the exception gracefully.

No, it needs to be usable by components that don’t inherit from ComponentBase.

Okay. This API is approved as proposed then. This is somethings we said would be easy to go and add back after API review. I’ve updated the approval comment, and I’ll mention this in the email with the meeting notes.

0reactions
SteveSandersonMScommented, Jan 20, 2023

Why doesn’t InvokeAsync call DispatchExceptionAsync internally? Will this ever be used for exceptions thrown from something other than InvokeAsync?

InvokeAsync returns a task. So if the invoked call throws, the returned task will be in a failed state with this exception. If we didn’t do that, there would be no way for the caller to handle the exceptions.

How are people going to know to call try/catch? Documentation.

The question probably indicates that we were not clear about the use case. The point of DispatchExceptionAsync is when you have exceptions that come from some external system (not the Blazor lifecycle methods) and want to treat them the same as lifecycle method exceptions. So, the premise that you have an exception already implies you caught it. If you don’t catch an external exception, then you have an unhandled exception which has nothing to do with Blazor.

Can RenderHandle.DispatchExceptionAsync be internal? Maybe.

No, it needs to be usable by components that don’t inherit from ComponentBase.

Can this be worked around? Maybe using ExceptionDispatchInfo and SynchronizatinContext.Post? Maybe not that workaround, but there are ugly workarounds that we do not want to suggest to users.

The only existing workaround, which we use internally in a couple of places, is to write some code that causes a component to re-render itself and to rethrow the exception during rendering. That is a nasty hack, so adding DispatchExceptionAsync is intended to give a clean and obvious way to achieve this.

Read more comments on GitHub >

github_iconTop Results From Across the Web

API for dispatching to renderer sync context and capturing ...
I was just proposing some way to run code (which may be async, but not value-returning) within the context of a component. The...
Read more >
Blazor - How to launch task on UI Dispatcher thread from a ...
Use InvokeAsync() to switch execution to the Dispatcher when triggering rendering or component state.' Now, I understand what the Exception is ...
Read more >
ASP.NET Core Blazor synchronization context
Learn about Blazor's synchronization context, how to avoid thread-blocking calls, and how to invoke component methods externally.
Read more >
ASP.NET Core Razor component rendering
Learn about Razor component rendering in ASP.NET Core Blazor apps, including when to manually trigger a component to render.
Read more >
How to Use Context API with Hooks While Avoiding ...
Learn how to efficiently create and consume Context API with the use of hooks without performance issues.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found