- •Table of Contents
- •About the Author
- •Acknowledgments
- •Introduction
- •Version Support
- •Supported Versions
- •A Unified Platform
- •Roadmap
- •Supported Operating Systems
- •Command Line Interface
- •Desktop Development
- •Blazor
- •MAUI
- •Wrapping Up
- •.NET 6 Architecture
- •Runtimes
- •CoreCLR
- •Mono
- •WinRT
- •Managed Execution Process
- •Desktop Packs
- •Wrapping Up
- •Dotnet New
- •Dotnet Restore
- •NuGet.config
- •Dotnet Build
- •Dotnet Publish
- •Dotnet Run
- •Dotnet Test
- •Using the CLI in GitHub Actions
- •Other Commands
- •Wrapping Up
- •WinAPI
- •WinForms
- •STAThread
- •WinForms Startup
- •DPI Mode
- •Responding to Scale Events
- •Visual Styles
- •Text Rendering
- •The Message Loop
- •The Form Designer
- •WPF Startup
- •XAML Layout
- •Visual Tree
- •Data Binding
- •Windows App SDK
- •Building a Windows App SDK application
- •Using Windows APIs with Windows App SDK
- •Packaging
- •Migrating to .NET 6
- •Upgrade Assistant
- •Wrapping Up
- •Blazor WebAssembly
- •Creating a Blazor Wasm Project
- •Blazor Progressive Web Apps
- •Exploring the Blazor Client Project
- •Blazor in .NET 6
- •Blazor Component System
- •Creating Blazor Pages
- •Running a Blazor App
- •Blazor Server
- •SignalR
- •Blazor Desktop
- •Wrapping Up
- •Project Structure
- •Exploring MAUI
- •The Cross-Platform World
- •Application Lifecycle
- •MVVM
- •MVVM Toolkit
- •Wrapping Up
- •Model-View-Controller
- •Routing
- •Views
- •Controllers
- •Controller-Based APIs
- •Minimal APIs
- •Wrapping Up
- •Web Apps
- •Creating an App Service
- •Static Web Apps
- •Web App for Containers
- •Docker
- •Azure Functions
- •Deploying Azure Functions
- •Wrapping Up
- •Record Types
- •Monolith Architecture
- •Microservices
- •Container Orchestration
- •Kubernetes
- •Docker Compose
- •Dapr
- •Installing Dapr
- •Dapr State Management
- •Wrapping Up
- •Roslyn
- •Compiler API
- •Diagnostic API
- •Scripting API
- •Workspace API
- •Syntax Tree
- •Roslyn SDK
- •Source Generators
- •Writing a Source Generator
- •Debugging Source Generators
- •Wrapping Up
- •Garbage Collector
- •The Heap
- •The Stack
- •Garbage Collection
- •A Look at the Threadpool
- •Async in .NET 6
- •Await/Async
- •Cancellations
- •WaitAsync
- •Conclusion
- •Index
Chapter 11 Advanced .NET 6
public class ResourcesService
{
public async Task<string> FetchAllResources()
{
var client = RestClient.GetClientInstance(); var result = await client.GetAsync(“/api/data”). ConfigureAwait(false);
string json = await result.Content.ReadAsStringAsync(). ConfigureAwait(false);
return json;
}
}
The FetchAllResources method has two calls that are awaited and uses ConfigureAwait(false) because we do not need to switch back to the caller context. By not returning to caller context in that method, we prevent two context switches to occur.
The FetchData method doesn’t use ConfigureAwait(false) because it needs to return to the caller context. The caller context here is the UI thread. The property that the returned value is being set to will trigger a change notification, so we need to be on the UI thread.
Cancellations
In Async operations, we often make use of CancellationTokens to cancel long running tasks. These tokens are used quite a lot across the base class library as well. However, cancelling tasks does not happen that often so it would be interesting to be able to reuse
CancellationTokenSource, the object that generates CancellationTokens. Up until now, we couldn’t do this safely because we couldn’t be certain that some tasks were still referencing this token. In .NET 6, CancellationTokenSource was extended with a TryReset method. Listing 11-10 shows the use of the TryReset method.
308
Chapter 11 Advanced .NET 6
Listing 11-10. Try to reset a cancellation token
CancellationTokenSource _cancellationTokenSource = new
CancellationTokenSource();
private void CancelButton_OnClick(object sender, EventArgs args)
{
_cancellationTokenSource.Cancel();
}
public async Task DoWork()
{
if (!_cancellationTokenSource.TryReset())
{
_cancellationTokenSource = new CancellationTokenSource();
}
Task<string> data = FetchData(_cancellationTokenSource.Token);
}
public async Task<string> FetchData(CancellationToken token)
{
token.ThrowIfCancellationRequested();
var client = new HttpClient();
HttpResponseMessage response = await client.GetAsync("https://www. apress.com", token).ConfigureAwait(false);
string html = await response.Content.ReadAsStringAsync(token). ConfigureAwait(false);
return html;
}Once a token was actually cancelled it cannot be recycled and the TryReset method will return false.
The example shown here comes from a WinForms application where we can load data and cancel it using a cancel button. When calling the DoWork method, we try to reset the CancellationTokenSource; if we don’t succeed, we instantiate a new one.
309
Chapter 11 Advanced .NET 6
The CancellationTokenSource's CancellationToken is passed to the LoadData method. LoadData checks if the token is not cancelled and uses it for loading and deserializing the data. As long as the token was not cancelled, we can keep resetting the
CancellationTokenSource for reuse.
WaitAsync
In .NET 6, Microsoft is giving us more control over when to cancel or timeout asynchronous operations by adding WaitAsync methods to Task. With WaitAsync, we can specify a cancellation token or a timeout to a task.
Listing 11-11. WaitAsync with a cancellation token
CancellationToken token = _cancellationTokenSource.Token;
var client = new HttpClient();
Task<HttpResponseMessage> response = client.GetAsync("https://www.apress.com", token)
.WaitAsync(token);
await response;
Listing 11-12 shows the three different options.
Listing 11-12. All WaitAsync overloads
Task<HttpResponseMessage> taskWithToken = client
.GetAsync("https://www.apress.com", token)
.WaitAsync(token);
Task<HttpResponseMessage> taskWithTimeout = client
.GetAsync("https://www.apress.com", token)
.WaitAsync(new TimeSpan(0, 0, 10));
Task<HttpResponseMessage> taskWithBoth = client
.GetAsync("https://www.apress.com", token)
.WaitAsync(new TimeSpan(0, 0, 10), token);
310