feat: implement identity authentication, authorization policies, and MAUI platform support with Docker orchestration
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
# .NET Async Best Practices: Avoiding `async void`
|
||||
|
||||
This document defines the core rules for handling asynchronous operations in .NET, specifically focused on the dangers of `async void`.
|
||||
|
||||
## The Rule: NEVER Use `async void`
|
||||
`async void` is a critical anti-pattern in .NET that must be avoided in almost all scenarios.
|
||||
|
||||
### Why `async void` is Dangerous:
|
||||
1. **Untraceable Crashes**: Exceptions thrown in an `async void` method cannot be caught by a `try-catch` block outside the method. They often crash the entire process.
|
||||
2. **Impossible to Await**: Callers cannot know when the operation has finished, leading to race conditions and "disposed object" exceptions (especially with `DbContext`).
|
||||
3. **Broken Lifecycle**: In Blazor and ASP.NET Core, the DI container might dispose of scoped services (like `DbContext`) before the `async void` method completes its work.
|
||||
|
||||
## Correct Patterns
|
||||
|
||||
### 1. Standard Methods
|
||||
Always return `Task` or `ValueTask` instead of `void`.
|
||||
```csharp
|
||||
// BAD
|
||||
public async void SaveDataAsync() { ... }
|
||||
|
||||
// GOOD
|
||||
public async Task SaveDataAsync() { ... }
|
||||
```
|
||||
|
||||
### 2. Async Events (Blazor/Services)
|
||||
When dealing with events that are defined as `Action` or `EventHandler`, do not use `async void` in the handler. Instead, refactor the event to support `Task`.
|
||||
|
||||
#### Pattern A: Use `Func<Task>` for Events
|
||||
Instead of `Action`, use `Func<Task>` so the invoker can await all handlers.
|
||||
```csharp
|
||||
// Interface
|
||||
event Func<Task>? OnChanged;
|
||||
|
||||
// Invoker
|
||||
if (OnChanged != null)
|
||||
{
|
||||
foreach (var handler in OnChanged.GetInvocationList().Cast<Func<Task>>())
|
||||
{
|
||||
await handler();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Pattern B: Wrapper for Legacy Events
|
||||
If you *must* subscribe to a legacy `void` event, use a Task-returning method and a lambda, but be aware of the "fire-and-forget" risks.
|
||||
```csharp
|
||||
// Better than async void, but still has lifecycle risks
|
||||
NavigationService.OnChanged += () => { _ = HandleChangedAsync(); };
|
||||
```
|
||||
|
||||
## Special Exceptions
|
||||
The ONLY valid place for `async void` is in **Top-level Event Handlers** in legacy UI frameworks (WinForms/WPF) where the event signature is fixed and cannot be changed. Even then, the method body should be wrapped in a `try-catch` that logs or handles all exceptions.
|
||||
|
||||
In modern Blazor and Web development, there is **zero justification** for `async void`.
|
||||
Reference in New Issue
Block a user