# .NET Async/Await Standards

> **Scope:** universal
> **Layer:** 0 (always load)
> **Keywords:** async, await, task, asynchronous, threading
> **Load When:** always

Asynchronous programming patterns for .NET applications

---

## Basic Patterns

### Async Method Signatures

```csharp
// ✅ CORRECT
public async Task<User> GetUserAsync(int id)
{
    return await _repository.GetByIdAsync(id);
}

// ✅ CORRECT (no return value)
public async Task DeleteUserAsync(int id)
{
    await _repository.DeleteAsync(id);
}

// ❌ WRONG (async void - only for event handlers)
public async void ProcessOrder(int orderId) // Don't do this!
{
    await _orderService.ProcessAsync(orderId);
}
```

### Naming Convention

**All async methods MUST end with `Async` suffix**

```csharp
// ✅ CORRECT
Task<Order> GetOrderAsync(int id)
Task SaveChangesAsync()
Task<bool> ValidateAsync(string input)

// ❌ WRONG
Task<Order> GetOrder(int id)
Task SaveChanges()
```

---

## CancellationToken

### Always Accept CancellationToken

```csharp
// ✅ CORRECT
public async Task<User> GetUserAsync(int id, CancellationToken ct = default)
{
    return await _context.Users
        .Where(u => u.Id == id)
        .FirstOrDefaultAsync(ct);
}

// ✅ CORRECT (in controller)
[HttpGet("{id}")]
public async Task<IActionResult> GetUser(int id, CancellationToken ct)
{
    var user = await _userService.GetUserAsync(id, ct);
    return Ok(user);
}
```

### Pass CancellationToken Down

```csharp
public async Task<Order> ProcessOrderAsync(int orderId, CancellationToken ct = default)
{
    // Pass ct to all async calls
    var order = await _repository.GetByIdAsync(orderId, ct);
    await _paymentService.ChargeAsync(order, ct);
    await _emailService.SendConfirmationAsync(order, ct);

    return order;
}
```

---

## ConfigureAwait

### Library Code

Use `ConfigureAwait(false)` in library/infrastructure code:

```csharp
// Infrastructure layer
public async Task<User> GetByIdAsync(int id)
{
    return await _context.Users
        .FindAsync(id)
        .ConfigureAwait(false); // ✅ Library code
}
```

### Application Code

DON'T use `ConfigureAwait(false)` in application/API layer:

```csharp
// Controller/Service layer
public async Task<IActionResult> GetUser(int id)
{
    // ✅ CORRECT (no ConfigureAwait in app layer)
    var user = await _userService.GetByIdAsync(id);
    return Ok(user);
}
```

---

## Async Best Practices

### DO

✅ **Use async all the way**

```csharp
// ✅ CORRECT
public async Task<ActionResult> CreateOrder()
{
    var order = await _service.CreateOrderAsync();
    await _context.SaveChangesAsync();
    return Ok(order);
}
```

✅ **Use Task.WhenAll for parallel operations**

```csharp
// ✅ CORRECT (parallel)
var userTask = _userService.GetUserAsync(userId);
var ordersTask = _orderService.GetOrdersAsync(userId);
var settingsTask = _settingsService.GetSettingsAsync(userId);

await Task.WhenAll(userTask, ordersTask, settingsTask);

var user = await userTask;
var orders = await ordersTask;
var settings = await settingsTask;
```

✅ **Return Task directly when no await needed**

```csharp
// ✅ CORRECT (no unnecessary async/await)
public Task<User> GetUserAsync(int id)
{
    return _repository.GetByIdAsync(id); // No await needed
}
```

### DON'T

❌ **Don't use async void (except event handlers)**

```csharp
// ❌ WRONG
public async void ProcessOrder(int id)
{
    await _service.ProcessAsync(id); // Exceptions swallowed!
}

// ✅ CORRECT
public async Task ProcessOrderAsync(int id)
{
    await _service.ProcessAsync(id);
}
```

❌ **Don't use .Result or .Wait()**

```csharp
// ❌ WRONG (blocks thread, risk of deadlock)
var user = _userService.GetUserAsync(id).Result;
_orderService.ProcessAsync(orderId).Wait();

// ✅ CORRECT
var user = await _userService.GetUserAsync(id);
await _orderService.ProcessAsync(orderId);
```

❌ **Don't create unnecessary tasks**

```csharp
// ❌ WRONG
public async Task<int> GetCountAsync()
{
    return await Task.FromResult(_items.Count); // Unnecessary!
}

// ✅ CORRECT
public Task<int> GetCountAsync()
{
    return Task.FromResult(_items.Count); // Or just return synchronously
}
```

---

## Async Patterns

### Pattern: Retry with Exponential Backoff

```csharp
public async Task<T> RetryAsync<T>(
    Func<Task<T>> operation,
    int maxRetries = 3,
    CancellationToken ct = default)
{
    for (int i = 0; i < maxRetries; i++)
    {
        try
        {
            return await operation();
        }
        catch (Exception) when (i < maxRetries - 1)
        {
            var delay = TimeSpan.FromSeconds(Math.Pow(2, i)); // 1s, 2s, 4s
            await Task.Delay(delay, ct);
        }
    }

    throw new InvalidOperationException($"Failed after {maxRetries} retries");
}
```

### Pattern: Timeout

```csharp
public async Task<User> GetUserWithTimeoutAsync(int id)
{
    using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));

    try
    {
        return await _repository.GetByIdAsync(id, cts.Token);
    }
    catch (OperationCanceledException)
    {
        throw new TimeoutException("Operation timed out after 5 seconds");
    }
}
```

### Pattern: Background Work (Fire and Forget)

```csharp
// ❌ WRONG (async void)
public async void LogAsync(string message)
{
    await _logger.WriteAsync(message);
}

// ✅ CORRECT (use background service or Hangfire)
public Task LogAsync(string message)
{
    _ = Task.Run(async () =>
    {
        try
        {
            await _logger.WriteAsync(message);
        }
        catch (Exception ex)
        {
            // Handle exceptions
        }
    });

    return Task.CompletedTask;
}
```

---

## Common Mistakes

### Mistake 1: Mixing Sync and Async

```csharp
// ❌ WRONG
public User GetUser(int id)
{
    return _repository.GetByIdAsync(id).Result; // Deadlock risk!
}

// ✅ CORRECT
public async Task<User> GetUserAsync(int id)
{
    return await _repository.GetByIdAsync(id);
}
```

### Mistake 2: Not Awaiting in Loop

```csharp
// ❌ WRONG (sequential, slow)
foreach (var id in userIds)
{
    await _service.ProcessAsync(id);
}

// ✅ CORRECT (parallel)
var tasks = userIds.Select(id => _service.ProcessAsync(id));
await Task.WhenAll(tasks);
```

### Mistake 3: Unnecessary Async/Await

```csharp
// ❌ WRONG (extra state machine)
public async Task<User> GetUserAsync(int id)
{
    return await _repository.GetByIdAsync(id);
}

// ✅ CORRECT (no async needed)
public Task<User> GetUserAsync(int id)
{
    return _repository.GetByIdAsync(id);
}
```

---

## Blazor-Specific

### Async in OnInitializedAsync

```csharp
// ✅ CORRECT
protected override async Task OnInitializedAsync()
{
    users = await UserService.GetUsersAsync();
    StateHasChanged(); // Optional, called automatically
}
```

### Async Event Handlers

```csharp
// ✅ CORRECT
private async Task HandleSubmitAsync()
{
    await OrderService.CreateAsync(order);
    NavigationManager.NavigateTo("/orders");
}

// In Razor:
<button @onclick="HandleSubmitAsync">Submit</button>
```

---

*MORPH-SPEC by Polymorphism Tech*
