Skip to main content

Posts

Showing posts with the label .NET 9

Cleaner Minimal API Endpoints with [AsParameters]

I only recently started using the ASP.NET Core's minimal API style, but an annoying thing I already encountered is the "long parameter list" problem. Route handlers that accept five, six, or seven parameters start to feel unwieldy fast. The good news is that a solution exists through the [AsParameters] attribute, introduced in .NET 7,  that gives you a clean way out. The problem it solves Minimal APIs are appealing precisely because they're lightweight — no controllers, no ceremony. But that simplicity starts to break down as your endpoints grow more complex. Consider this example endpoint: That's eight(!) parameters before you've written a single line of business logic. It's hard to read, hard to test, and grows more painful every time requirements change. Enter [AsParameters] [AsParameters] lets you group related parameters into a plain C# class or record and bind them all at once. ASP.NET Core inspects the type's constructor and public ...

Why AI helps you to discover new parts of your favorite SDK–The NullLogger

I'll admit it - I first encountered NullLogger in code generated by an AI coding assistant. At first glance, I almost dismissed it as one of those "AI quirks," but then I realized it was actually a real class available in the .NET Framework that I'd been missing out on. What is NullLogger? NullLogger is part of Microsoft.Extensions.Logging and implements the null object pattern for logging. It's a logger that does absolutely nothing - all of its methods are no-ops. While that might sound useless at first, it's actually quite valuable. Imagine you have the following code: Now when testing this code I would normally mock the logger, but thanks to the built-in NullLogger I no longer have to do that: How to use it In the example above I showed the typical way to get an ILogger<T> instance. There is also a non-generic one to create an ILogger instance: Both are singletons, so you're always getting the same instance - no memory overhe...

Concurrent changes on non-concurrent collections

I don’t do it on purpose but sometimes it can be so much fun to dive into an exception you’ve never seen before. You always come out with some new acquired wisdom. It all started with the following exception during the execution of our unit tests: System.InvalidOperationException : Operations that change non-concurrent collections must have exclusive access. A concurrent update was performed on this collection and corrupted its state. The collection's state is no longer correct. A look at the stacktrace brought us to the initialization system of our application where multiple modules are configured and initialized: at System.Collections.Generic.Dictionary`2.TryInsert(TKey key, TValue value, InsertionBehavior behavior) at System.Collections.Generic.Dictionary`2.set_Item(TKey key, TValue value) at SOFACore.EntityFramework.EntityFrameworkModule.Initialize(IServiceCollection services) in /_/SOFACore/SOFACore.EntityFramework/EntityFrameworkModule.cs:line 30 Inside this mo...

Understanding AsNoTrackingWithIdentityResolution in Entity Framework Core

When working with Entity Framework Core, understanding change tracking behavior is crucial for both performance and data consistency. While I was ware of the AsNoTracking() method, I discovered a lesser-known but powerful alternative: AsNoTrackingWithIdentityResolution() during a code review.  Let's explore what makes this method special and when you should use it. Quick recap: What is AsNoTracking()? Before diving into AsNoTrackingWithIdentityResolution , let's briefly review AsNoTracking() . By default, EF Core tracks all entities returned from queries in the change tracker. This tracking enables: Automatic detection of changes to entities Update operations without explicitly attaching entities Identity resolution (ensuring only one instance per entity exists in memory) However, tracking comes with overhead. When you're performing read-only operations where you don't need to update data, AsNoTracking() improves performance by skipping the change...

Securing File Uploads Part 4: Malware Scanning with Windows AMSI

Welcome to the final post in our file upload security series. We've covered content type validation, file size validation, and file signature validation—each providing a crucial layer of defense. Today, we're implementing the final and most sophisticated protection: malware scanning using Windows Antimalware Scan Interface (AMSI) . The last line of defense Even after all our previous validation steps, a determined attacker could still upload malicious content: A legitimate PDF with embedded JavaScript exploits A valid Office document containing malicious macros An actual image file with embedded steganographic payloads A genuine archive containing malware Zero-day exploits targeting file processing libraries These files pass all our previous validations because they are legitimate file formats—they're just weaponized. This is where malware scanning becomes essential. Why AMSI? Windows Antimalware Scan Interface (AMSI) is a powerful, oft...

Securing File Uploads Part 3: File Signature Validation

In our previous posts, we covered content type validation and file size validation as the first two layers of defense in our file upload security pipeline. Today, we're diving into what I consider the most critical validation step: file signature validation , also known as "magic number" validation. This is where we stop trusting what files claim to be and start verifying what they actually are. The Problem: files that lie Here's a sobering truth: both content type headers and file extensions are trivially easy to manipulate. An attacker can: Rename malicious.php to harmless.jpg Upload a PHP web shell with the content type set to image/jpeg Disguise an executable as a PDF by simply changing the extension Bypass your content type validation while still delivering malicious payloads Consider this scenario: Your application accepts image uploads for user profiles. You've implemented content type validation that only allows image/jpeg , image/...

Securing File Uploads Part 2: File Size Validation

In the first post of this series, we explored how content type validation serves as the first line of defense against malicious file uploads. Today, we're tackling another critical security concern: file size validation and why it's essential for protecting your application from resource exhaustion attacks. The threat: Death by a thousand uploads File size validation might seem like a simple feature requirement, but it's actually a crucial security control. Without proper size limits, attackers can: Exhaust disk space : Fill up your storage with massive files, causing system failures Consume bandwidth : Drain network resources by uploading gigantic files repeatedly Trigger out-of-memory errors : Crash your application by forcing it to process files larger than available memory Enable denial-of-service attacks : Tie up server resources processing oversized files, preventing legitimate users from accessing your application Inflate storage costs : In c...

How to get rid of the smartcard popup when interacting with LDAP over SSL

In one of our applications we are connecting with LDAP through System.DirectoryServices.AccountManagement. . This code worked fine for years until we had to make the switch from LDAP to LDAPS and incorporate SSL in our connections. Let me start by showing you the original code (or at least a part of it): We thought that making the switch to SSL would be easy. We therefore added the ContextOptions.SecureSocketLayer to the ContextOptions enum; However after doing that, we get a SmartCard popup everytime this code is called: I couldn’t find a good solution to fix it while keeping the PrincipalContext class. After some help of GitHub Copilot and some research I discovered that I could get it working when I switched to the underlying LdapConnection and explicitly setting the ClientCertificate to null : More information c# - PrincipalContext with smartcard inserted - Stack Overflow c# - How to validate server SSL certificate for LDAP+SSL connection - Stack Overflow

Fixing integration test issues with Microsoft.AspNetCore.Mvc.Testing in .NET 9

When upgrading an ASP.NET Core application to .NET 9, I encountered the following error in my integration tests: System.InvalidOperationException: No application configured. Please specify an application via IWebHostBuilder.UseStartup, IWebHostBuilder.Configure, or specifying the startup assembly via StartupAssemblyKey in the web host configuration. I had updated my custom TestHost to switch from using a Startup.cs file to directly using the Program.cs file and the Minimal API approach. Therefore I added a partial Program.cs and updated the WebApplicationFactory class to use the Program.cs instead (more about this change in this post ). Here is the updated code: But this code didn’t work and resulted in the error message above. While giving the code a second look, I noticed that I was still referring to the Startup.cs that I didn't remove yet. I updated the code to use my Program.cs file instead: Doing that resulted in another error: A public method named ...

Compiler error ASPIRE007 after upgrading Aspire

After upgrading the .NET Aspire NuGet packages to the latest version on one of my projects, I started to get the following compiler error: 'Project' requires a reference to "Aspire.AppHost.Sdk" with version "9.0.0" or greater to work correctly. Please add the following line after the Project declaration <Sdk Name="Aspire.AppHost.Sdk" Version="9.0.0" /> . The error message is self explanatory and clearly defines how we could fix this. Add the Sdk reference to your csproj file to get rid of this compiler error: The strange thing was that after applying the upgrade on one branch, I also started to get the same compiler error on other branches although the Aspire NuGet were not (yet) upgraded on these branches. Bizar! More information Compiler Error ASPIRE007 - .NET Aspire | Microsoft Learn

.NET 9–OpenAPI and Scalar–Passing an API key

Let's continue our joruney in discovering Scalar . Today I want to talk about how we can integrate security. Most API's that we build today are secured in a way. This could be as simple as an API key or as complex as using OAuth with PKCE. In this post we’ll look at how to pass an API key through the Scalar UI. Let’s dive in… I assume that you already have registered an authentication scheme for your API like this: Now we need to write an extra transformer to include the authentication information in our OpenAPI metadata. Don’t forget to register this transformer in our OpenAPI configuration: At the Scalar level we don’t have to change anything: But if we now browse to the Scalar UI, the authentication scheme is recognized and we get the option to pass an API key:   Nice! More information .NET 9–OpenAPI and Scalar–Introduction .NET 9–OpenAPI and Scalar–Adding custom headers

.NET 9–OpenAPI and Scalar–Adding custom headers

In this post I continue my investigation of using Scalar as an alternative to Swashbuckle that I was using before to expose my OpenAPI metadata in a userfriendly way. If you have no idea what Scalar is, I would recommend to check out my introduction post first before you continue reading. Today I want to have a look at how we can transform the OpenAPI metadata. On this specific API, it is expected that one of a set of custom headers is passed when calling the API. To simplify the experience, I originally created  an IOperationFilter for Swashbuckle to show these extra headers: How to customize the OpenAPI metadata in .NET 9? The generated OpenAPI document can be customized using “transformers”, which can operate on the entire document, on operations, or on schemas. Transformers are classes that implement the IOpenApiDocumentTransformer , IOpenApiOperationTransformer , or IOpenApiSchemaTransformer interfaces. Each of these interfaces has a single async method that receives...

.NET 9–OpenAPI and Scalar–Introduction

With the release of .NET 9 , Microsoft has removed Swashbuckle from the default Web API templates. If you have never heard about Swashbuckle before, it allowed you to generate OpenAPI metadata for your web api's. Although I had no complaints using the Swagger UI, I decided to use the opportunity to have a look at library, Scalar, to generate an UI based on the OpenAPI documentation. In this post, I’ll walk you through my transition from Swashbuckle to Scalar, highlighting the benefits, challenges, and key implementation steps. Why the change? Microsoft decided to drop Swashbuckle due to maintenance issues and a shift toward integrated OpenAPI support . While Swashbuckle provided automatic documentation , Swagger UI integration , and customizability , Scalar introduces a sleek UI , mobile-friendly interface , and enhanced search capabilities . Scalar not only provides great integration for .NET but also works on a lot of other platforms. Setting Up Scalar in .NET 9 To i...

Static File handling in ASP.NET Core 9.0

I know, I know, .NET 10 is already in preview and I am still catching up on what was added to .NET 9.0. Today while upgrading an older application to .NET 9, I decided to have a look at the new static file handling introduced in .NET 9 through the MapStaticAssets feature. Static File middleware (before .NET 9) Before .NET 9, static files(Javascript, CSS, images, …) were handled through the UseStaticFiles middleware This middleware is still there as the new MapStaticAssets feature does not support all the features that the original middleware had. From the documentation : Serving files from disk or embedded resources, or other locations Serve files outside of web root Set HTTP response headers Directory browsing Serve default documents FileExtensionContentTypeProvider Serve files from multiple locations Serving files from disk or embedded resources, or other locations Serve files outside of web root Set HTTP response headers Direc...

.NET Conf Focus on Modernization

I don’t know what happened but somehow I succeeded to miss the latest .NET Conf Focus edition. And this time, it was all about a topic that every developer has to handle sooner or later during his professional career; Application Modernization! Here was the original announcement: Get Ready for .NET Conf: Focus on Modernization | Microsoft Community Hub and a screenshot from the agenda: The good news is all the content is available on YouTube here: .NET Conf - Focus on Modernization: Day 1 I’m especially interested in the session about the upcoming AI-assisted tooling to upgrade .NET apps. With the current pace at which new(er) LTS versions of .NET are released, any help to keep these applications supported and up-to-date is more then welcome:

.NET Aspire Dashboard - The mystery of the hidden endpoint

One of the cool features of .NET Aspire is the Aspire Dashboard(also available standalone by the way). It allows you to closely track various aspects of your app and its resources, including logs, traces, and environment configurations, in real-time. After migrating an existing solution to .NET Aspire, I noticed that no URL was shown for some of the endpoints: I couldn’t find a direct reason why this was the case, but I found online that it could be related to the launchsettings.json files. Here is the launchsettings.json file for the ‘api’ project (where the endpoint URL is shown): And here is the launchsettings.json file the for the ‘webapp’ project (where no endpoint is shown): With some trial and error, I found that the order of the profiles in the launchsettings.json file matters and that Aspire will use the first profile found. I switched the profiles for the ‘webapp’ project: And indeed, after doing that, the endpoint URL became visible on the dashboard: ...

.NET 9 - Goodbye sln!

Although the csproj file evolved and simplified a lot over time, the Visual Studio solution file (.sln) remained an ugly file format full of magic GUIDs. With the latest .NET 9 SDK(9.0.200), we finally got an alternative; a new XML-based solution file(.slnx) got introduced in preview. So say goodbye to this ugly sln file: And meet his better looking slnx brother instead: To use this feature we first have to enable it: Go to Tools -> Options -> Environment -> Preview Features Check the checkbox next to Use Solution File Persistence Model Now we can migrate an existing sln file to slnx using the following command: dotnet sln migrate AICalculator.sln .slnx file D:\Projects\Test\AICalculator\AICalculator.slnx generated. Or create a new Visual Studio solution using the slnx format: dotnet new sln --format slnx The template "Solution File" was created successfully. The new format is not yet recognized by VSCode but it does work in Jetbr...

Using legacy text encodings in .NET Core

Upgrading an (old) .NET application to .NET core turned into a learning experience when we got the following error message after the upgrade was done: System.NotSupportedException : No data is available for encoding 1252. For information on defining a custom encoding, see the documentation for the Encoding.RegisterProvider method. TLDR; The application was using an older Windows-1252 text encoding causing the error above when trying to use this in .NET Core which doesn’t support this encoding out-of-the-box. Introduction to Text Encodings Text encoding is a method used to convert text data into a format that can be easily processed by computers. Computers inherently understand numbers, not characters, so text encoding maps characters to numerical values. This process ensures that text data can be stored, transmitted, and interpreted correctly across different systems and platforms. There are various text encodings, each designed to support different sets of characters. Some c...

Enabling .NET Aspire for an existing solution

I hope that you already had a change to try .NET Aspire, a comprehensive set of tools, templates, and packages designed to help developers build observable, production-ready applications. It enhances the development experience by providing dev-time orchestration, integrations with commonly used services, and robust tooling support. .The goal is to simplify the management of multi-project applications, container resources, and other dependencies, making it easier to develop interconnected apps. To make it even better, it includes features like service discovery, connection string management, and environment variable configuration, streamlining the setup process for local development environments. And if that couldn’t convince you yet, than maybe the Aspire Dashboard will. Sounds great right? Reason enough to add it to your existing projects if you are not using it yet. A great tutorial exists on Microsoft Learn to help you add it to your existing projects. Unfortunately I had a ...

.NET 9 upgrade–notnull constraint

With the Christmas Holidays after us, I finally got some time to start upgrading some older .NET Core applications to .NET 9. First thing I noticed after upgrading is that I got some new warnings related to the ‘notnull’ constraint. The ‘notnull’ constraint was introduced as part of the nullable reference types feature in C#.  The goal of nullable reference types was to solve the ‘million dollar mistake’ and help you to prevent common issue of null reference exceptions. They allow  to explicitly state whether a reference type variable can be null or not, helping to catch potential null dereferences at compile time. The feature is in fact a combination of multiple elements all with the goal of avoiding unexpected null reference exceptions: Nullable and Non-Nullable Reference Types : Non-Nullable : If you declare a reference type without the ? suffix (e.g., string name ), it is considered non-nullable. The compiler issues warnings if you try to assign a...