feat: implement identity authentication, authorization policies, and MAUI platform support with Docker orchestration

This commit is contained in:
2026-04-29 20:37:41 +02:00
parent 10efed0369
commit 0210611edf
55 changed files with 2359 additions and 949 deletions
+54
View File
@@ -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`.