Files
thereadme/slides/i-like-having-options-for-control-flow/index.md
T

189 lines
4.3 KiB
Markdown
Raw Normal View History

2026-01-30 14:24:52 -06:00
---
2026-01-30 17:42:57 -06:00
title: Why I Love Control Flow in C#
2026-01-30 14:24:52 -06:00
theme: default
layout: center
class: text-center
fonts:
sans: "CaskaydiaCove Nerd Font Mono"
---
# Why I love control flow in C#
---
layout: center
class: text-center
---
# The false dichotomy
<div class="grid grid-cols-2 gap-12 mt-16 text-left">
<v-click>
<div class="border-l-4 border-red-500 pl-6">
<h3 class="text-red-400 text-2xl font-bold mb-2">Team "Never Throw"</h3>
<p class="text-gray-400">"Exceptions are expensive! Treat errors as values! Not every failure is exceptional!"</p>
</div>
</v-click>
<v-click>
<div class="border-l-4 border-blue-500 pl-6">
<h3 class="text-blue-400 text-2xl font-bold mb-2">Team "Always Throw"</h3>
<p class="text-gray-400">"Results are noisy! Just throw and let the middleware handle it! Keep the code clean!"</p>
</div>
</v-click>
</div>
<v-click>
<div class="mt-16 text-2xl text-green-400">
> Why not both?
</div>
</v-click>
---
layout: center
---
# The sweet spot
<div class="grid grid-cols-4 gap-4 mt-8 h-full">
<v-click>
<div class="bg-gray-800 p-4 rounded border-t-4 border-red-500">
<div class="font-bold mb-2 text-center">Global Handler</div>
<div class="text-sm text-center">Prevent crashes</div>
</div>
</v-click>
<v-click>
<div class="bg-gray-800 p-4 rounded border-t-4 border-green-500">
<div class="font-bold mb-2 text-center">Controller</div>
<div class="text-sm text-center">Matches result</div>
</div>
</v-click>
<v-click>
<div class="bg-gray-800 p-4 rounded border-t-4 border-blue-500">
<div class="font-bold mb-2 text-center">Service Layer</div>
<div class="text-sm text-center">Returns results</div>
</div>
</v-click>
<v-click>
<div class="bg-gray-800 p-4 rounded border-t-4 border-purple-500">
<div class="font-bold mb-2 text-center">Deep / Domain</div>
<div class="text-sm text-center">Throws exceptions</div>
</div>
</v-click>
</div>
---
layout: center
---
# An example in a web API
---
layout: center
---
# Use exceptions for context agnostic code
```csharp
public async Task<User?> GetUserAsync(int userId)
{
// NOTE: EF Core might throw exceptions for connection issues, etc.
var user = await _dbContext.Users.FindAsync(userId);
// NOTE: Domain logic might throw exceptions for business rules
if (user?.IsBanned)
{
throw new UserBannedException(userId);
}
return user;
}
```
---
# Service layer returns results
```csharp
public async Task<Result<User>> GetUserAsync(int userId)
{
try
{
var user = await _userService.GetUserAsync(userId);
if (user is null)
{
return Result.Failure<User>(new UserNotFoundException(userId));
}
return Result.Success(user);
}
catch (UserBannedException ex)
{
return Result.Failure<User>(ex);
}
}
```
---
# Match results in the controller
```csharp
public static async Task<IResult> GetUserHandler(int userId, IUserService _service)
{
var result = await _service.GetUserAsync(userId);
return result.Match(
user => Ok(user),
error => error switch
{
UserNotFoundException => Results.NotFound(),
UserBannedException => Results.Forbid(),
_ => Results.InternalServerError()
}
);
}
```
---
# Global handler for unexpected errors
```csharp
public async ValueTask<bool> TryHandleAsync(
HttpContext httpContext,
Exception exception,
CancellationToken cancellationToken
)
{
_logger.LogError(
exception,
"Exception occurred: {Message}", exception.Message
);
httpContext.Response.StatusCode =
StatusCodes.Status500InternalServerError;
await httpContext.Response
.WriteAsync("An unexpected error occurred.", cancellationToken);
return true;
}
```
---
layout: center
---
# C# gives me the freedom to choose
<ul>
<v-click>
<li>Use exceptions when deep in the call stack.</li>
</v-click>
<v-click>
<li>Use result types where business logic is centralized.</li>
</v-click>
<v-click>
<li>Use global handlers for unexpected exceptions and to prevent crashes.</li>
</v-click>
</ul>