Skip to main content

Posts

Showing posts with the label .NET 6

How to uninstall older .NET Core versions

Over time, your development machine and servers can accumulate multiple versions of .NET Core runtime and SDK installations. While having multiple versions is often necessary for compatibility, old versions you no longer need can consume valuable disk space and clutter your system. In this post, we'll walk through the process of safely identifying and uninstalling older .NET Core versions. Why uninstall old .NET Core versions? Before we dive into the how, let's understand why you might want to clean up old .NET installations: Disk Space : Each SDK version takes up several hundred megabytes Clarity : Fewer versions make it easier to manage your development environment Security : Older versions may have known vulnerabilities Maintenance : Keeping only what you need simplifies updates and troubleshooting Checking installed versions Before uninstalling anything, you need to know what's currently installed on your system. Open your terminal or command...

Implementing an OAuth client credentials flow with ADFS–Part 3–Debugging the flow

It looked like most of the world has made the switch to Microsoft Entra(Azure Active Directory). However one of my clients is still using ADFS. Unfortunately there isn't much information left on how to get an OAuth flow up and running in ADFS. Most of the links I found point to documentation that no longer exists. So therefore this short blog series to show you end-to-end how to get an OAuth Client Credentials flow configured in ADFS. Part 1 - ADFS configuration Part 2 – Application configuration Part 3 (this post) – Debugging the flow In the first 2 posts I showed you the happy path. So if you did everything exactly as I showed, you should end up with a working Client Credentials flow in ADFS. Unfortunately there are a lot of small details that matter, and if you make one mistake you’ll end with a wide range of possible errors. In today’s post, I focus on the preparation work to help us debug the process and better understand what is going on. Updating your OAuth Confi...

Implementing an OAuth client credentials flow with ADFS–Part 2–Application configuration

It looked like most of the world has made the switch to Microsoft Entra(Azure Active Directory). However one of my clients is still using ADFS. Unfortunately there isn't much information left on how to get an OAuth flow up and running in ADFS. Most of the links I found point to documentation that no longer exists. So therefore this short blog series to show you end-to-end how to get an OAuth Client Credentials flow configured in ADFS. Part 1 - ADFS configuration Part 2 (this post) – Application configuration After doing all the configuration work in ADFS, I’ll focus today on the necessary work that needs to be done on the application side. Configuring the API We’ll start by configuring the API part. First create a new ASP.NET Core API project dotnet new webapi --use-controllers -o ExampleApi Add the ‘ Microsoft.AspNetCore.Authentication.JwtBearer ’ package to your project: dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer Add the auth...

C# 12- Type Aliasing

During a code review I noticed a file I had not seen before in this application; a GlobalUsings.cs file. When opening the file I noticed it had a combination of global using statements and type aliases . Turns out that this is an emerging pattern emerge in .NET applications where developers are defining a GlobalUsings.cs file to encapsulate all (or most) using directives into a single file. If you have no clue what  these 2 language features are, here is a short summary for you. Global Usings Global usings were introduced in C# 10 and allows you to declare a namespace once in your application and make it available everywhere. So if we want to use a specific namespace, we need to add the following line to any source file: Remark: As I mentioned in the introduction, I would recommend to centralize these global usings in one file instead of spreading them out over multiple files in your codebase. What we can also do is instead of declaring this inside a source file, ...

Microsoft.Extensions.DependencyInjection - Check if a service is registered in the DI container

After using other DI containers like Microsoft Unity, StructureMap and Autofac, I'm now using the built-in Microsoft.Extensions.DependencyInjection DI container most of the time.  The default DI container lacks some more advanced features that these other containers have, but for most use cases it is sufficient. This week I was looking at a way to check if a specific service was registered in the DI container without resolving and constructing the service instance. Turns out that this feature was introduced in .NET 6 through the IServiceProviderIsService interface (what’s in a name). This interface provides a single method that you can invoke to check whether a given service type is registered in the DI container. To use it, you can resolve the IServiceProviderIsService service itself from the container and call the IsService method: Nice!

GetHashCode() in .NET Core

If you ever had to implement the Equals() method to compare two instances of a type in .NET, you had to implement the GetHashCode() method too. The GetHashCode method returns a numeric value which is used to identify an object during equality testing. It can also serve as an index for an object in a collection. The purpose of the method is to create a key for hashtable. It is by design useful for only one thing: putting an object in a hash table. It is faster to use the return value of GetHashCode to determine whether two objects are equal than to call the default implementation of Equals on the object type. In other words, GetHashCode is used to generate a unique identifier for an object that can be used to compare it with other objects. It is used internally by the .NET framework for quick comparisons. If you had to implement the GetHashCode method, there were some rules that should be followed which could make it quite a challenge to implement it correctly: ...

Add custom properties to ILogger

By default when logging messages through ILogger only objects that have a placeholder in the message template are logged. For example, when executing the following line of code: the following information is logged: This is something that you can also notice when you check the warning you get when hovering over the message:   But what if you want to log these extra properties? One way to get this done is by using scopes to add custom properties. Typically this is used for multiple log entries, but it also works for single log statements:   More information: Logging in C# - .NET | Microsoft Learn

.NET 6 - Parallel.ForEachAsync

You maybe used Parallel.ForEach() before. It allows to iterate over a collection in a parallel way. It works similar to a Parallel.For loop. The loop partitions the source collection and schedules the work on multiple threads based  on the available processors in a system. Unfortunately the Parallel.ForEach() cannot be used for asynchronous work. Async vs parallel It is important to understand that "async" and "parallel" are two different concepts. Although they are both related to concurrent programming, they serve different purposes and are used in different contexts. Async is used to make non-blocking I/O operations and asynchronous code execution. It is primarily used for tasks that may take some time to complete, like reading from a file, making a network request, or performing database operations. Parallel however refers to parallel programming, which is about executing multiple tasks or operations simultaneously to improve performance and ...

Source Generator playground

If you are new to source generators and want to experiment with its possibilities, I can recommend the Source Generator Playground . This Blazor app gives you a simple console application and a source generator and allows you to observe the generated output. You can try it out live here: https://wengier.com/SourceGeneratorPlayground   They are some sample generators available that you can start modifying to see what happens: This is a great way to learn what is possible.

Simplify Source Generator creation with RoslynQuoter

I’m currently writing my own Source generator which can become a challenge when you start to create more complex constructs. One of the tools that helped me during the process was RoslynQuoter . RoslynQuoter is a tool that for a given C# program shows the syntax factory API calls to construct its syntax tree. You can try it out live at: http://roslynquoter.azurewebsites.net .   Nice! More information Source Generators - C# | Microsoft Learn roslyn/docs/features/source-generators.cookbook.md at main · dotnet/roslyn (github.com) roslyn-sdk/samples/CSharp/SourceGenerators at main · dotnet/roslyn-sdk (github.com) KirillOsenkov/RoslynQuoter: Roslyn tool that for a given C# program shows syntax tree API calls to construct its syntax tree (github.com)

Create an ASP.NET Core backgroundservice that runs at regular intervals using PeriodicTimer

Today I had to implement an ASP.NET Core backgroundservice that need to execute at certain intervals. Before .NET 6 I would have used Task.Delay or System.Threading.Timer , but in .NET 6 we have a better alternative through the PeriodicTimer . The PeriodicTimer uses an asynchronous approach based on the System.Threading.Tasks. It has a method WaitForNextTickAsync that allows to pause execution until the next time the timer is elapsed. I first created a backgroundservice: In the ExecuteAsync method I can now introduce the PeriodicTimer and call the WaitForNextTickAsync() method in a while loop . The loop shall run while no cancellation of the background service is requested in the CancellationToken and wait for the next tick of the timer:   Remark: The PeriodicTimer is intended to be used only by a single consumer at a time: only one call to WaitForNextTickAsync() may be in flight at any given moment. So make sure that your business logic is executed before the int...

.NET 6 - Async scopes

Services in .NET Core can be registered using the built-in dependency injection functionality with different lifetimes: Transient Scoped Singleton Scoped services are created for a specific period of time linked to a scope. For example when using ASP.NET Core a scoped service lifetime is coupled to a client request. An important feature of scoped services is that they are disposed when the scope is closed. It is possible to create a scope yourself using the CreateScope method on the IServiceProvider : If you run the example above you’ll see that the Foo instance is correctly disposed after leaving the scope. So far, so good. But what about when your service implements the IAsyncDisposable interface instead? To support this scenario in a non-breaking way, a new CreateAsyncScope method is introduced on the IServiceProvider interface in .NET 6. Here is an updated example using this feature: Remark : ASP.NET Core was updated as well and implementations o...

Reading a connectionstring from secrets.json

As we are still using SQL accounts to connect to our databases locally, I wanted to avoid accidently checking in the SQL account information. So instead of storing my connectionstring directly in the appsettings.json file I wanted to use the secrets.json file instead. Let us find out how to achieve this... When storing your connectionstring inside your appsettings.json you can use the GetConnectionString() method to fetch a connectionstring: The same technique also works when using the built-in secret manager tool and the corresponding secrets.json. By default user secrets are loaded when your environmentname is set to Development . You can explicitly enable this by adding the following code: User Secrets are stored in a separate secrets.json. You can edit the secrets using the Secret Manager tool (dotnet user-secrets) or directly in Visual Studio by right clicking on the web project and choosing ‘Manage User Secrets’ : Remark: You can find the secrets.json file here at %...

Error NETSDK1005: Assets file 'project.assets.json' doesn't have a target for 'netcoreapp3.1'

Short post today as a reminder for myself if I ever get this error again... When trying to build and deploy an older .NET Core 3.1 app, the build server returned the following error message: :\Program Files\dotnet\sdk\5.0.100\Sdks\Microsoft.NET.Sdk\targets\Microsoft.PackageDependencyResolution.targets(241,5): Error NETSDK1005: Assets file 'D:\b\4\agent\_work\2\s\Loket\obj\project.assets.json' doesn't have a target for 'netcoreapp3.1'. Ensure that restore has run and that you have included 'netcoreapp3.1' in the TargetFrameworks for your project. Process 'msbuild.exe' exited with code '1'. We discovered that the issue was caused by a change in the .NET SDK starting from .NET 5.0. The project.assets.json mentioned in the error message above is created by NuGet in the obj\ folder. The .NET SDK uses it to get information about packages to pass into the compiler. In .NET 5, Nuget added a new field called TargetFrameworkAlias, and thus in...

Reduce heap allocations by using static anonymous functions

As a C# developer you got used to apply anonymous functions everywhere. Everytime you write a LINQ statement or use a lamdbda expression , you are creating an anonymous function. But what you maybe are not aware of is that anonymous functions have a performance impact and don’t come cheap : Overhead of a delegate invocation (very very small, but it does exist). 2 heap allocations if a lambda captures local variable or argument of enclosing method (one for closure instance and another one for a delegate itself). 1 heap allocation if a lambda captures an enclosing instance state (just a delegate allocation). 0 heap allocations only if a lambda does not capture anything or captures a static state. One way to limit the number of allocations is through local functions but today I want to focus on another option; using static anonymous functions . Let’s use a really simple example where static anonymous functions can help: In the example above we capture the va...

Upgrading to .NET 6 -ASP0014 warning

In preparation of a GraphQL workshop I'm giving later this week, I was updating my demos to .NET 7. The original demos were written in .NET 5, but with the introduction of minimal API's in .NET 6 I decided to take that route. So I removed my Startup.cs file and copied everything inside my Program.cs file: This however resulted in the following error message(which is in fact a warning but I have 'Treat Warnings as Errors' enabled in Visual Studio): ASP0014: Suggest using top level route registrations In .NET 6 routes no longer need to be nested inside an UseEndpoints call, so I can rewrite the code above to get rid of this warning: More information: ASP0014: Suggest using top level route registrations | Microsoft Learn

InvalidOperationException: Cannot resolve from root provider because it requires scoped service

A colleague contacted me with a specific error she got. The strange thing was that the error never appeared during local development but only after deploying the application to our Development environment. Let's first have a look at the error message: The error happens during the bootstrapping of the application. For me it was immediately clear what the problem was but if you don’t spot the root cause, no worries. I’ll show you the related code, maybe that helps. First here is the specific Serilog enricher: And here is the bootstrapping code: The problem occurs when we resolve the ILogger singleton instance. When we do this a transient enricher is used that injects a scoped IUserFactory. As there is no scope available at that moment, this leads to the error above. To fix it, we have to change the lifetime of our IUserFactory to transient: > Than the question remains: Why this error doesn’t happen during local development? The answer is Scope Validation ...

Strawberry Shake–Access to the path ‘obj\\berry’ is denied

When trying to build a C# project in Visual Studio 2022 v17.5, I got compiler errors. This was strange because the only thing that has changed was the Visual Studio version.  Here is the exact error I got: In this project I was using the Strawberry Shake GraphQL client . This client uses a custom GraphQL compiler build action and it was exactly this step that failed. The quick fix Let me first explain a quick fix. This problem is indeed related to the Visual Studio version which seems to became more strict on what is allowed and what not.  To get rid of the error, restart your Visual Studio instance and run it under Administrator privileges. Updating to Strawberry Shake 13 Unfortunately before I arrived at the quick fix above I took the long path and started an update to Strawberry Shake v13. This turned to be more work than I had expected. Let me walk you through the steps: Remove the package references to StrawberryShake.CodeGeneration.CSharp.Analyzers a...

HotChocolate 13– Updated workshop

I'm a big fan of GraphQL and HotChocolate is the GraphQL server of my choice when building .NET applications. If you are new to HotChocolate I can recommend having a look at the workshop that is available on their Github . Unfortunately this workshop is still for .NET 5.0 and HotChocolate 11. I'm working on porting the workshop to HotChocolate 13, the latest version on the moment of writing this post. If you want to checkout this version, you can find my fork here: https://github.com/wullemsb/graphql-workshop Remark: I have planned to provide the updated version as a pull request once I’ve ported everything.

Package Validation

I recently discovered a .NET feature I didn’t know it existed; Package validation . Package validation was introduced as a part of the .NET 6 SDK. It allows you as a package author to check if your NuGet packages are consistent and well formed. At the moment of writing this post, the tooling provides the following checks: Validates that there are no breaking changes across versions. Validates that the package has the same set of publics APIs for all the different runtime-specific implementations. Helps developers catch any applicability holes. Enabling package validation is nothing more than setting the EnablePackageValidation property to true in your csproj file: Now 3 different validators can kick in everytime you run dotnet pack : The Baseline version validator validates your library project against a previously released, stable version of your package. The Compatible runtime validator validates that your runtime-specific implementation assemblies are...