Skip to main content

Posts

Showing posts with the label .NET Core

Mixing AddDbContext and AddDbContextFactory: 'Cannot consume scoped service'

Recently I ran into a nasty startup error after registering both AddDbContext and AddDbContextFactory for the same DbContext in an ASP.NET Core project: System.AggregateException: 'Some services are not able to be constructed' System.InvalidOperationException: 'Error while validating the service descriptor 'ServiceType: Microsoft.EntityFrameworkCore.IDbContextFactory`1[StartStopLijsten.ApiService.Data.StartStopDbContext] Lifetime: Singleton ImplementationType: Microsoft.EntityFrameworkCore.Internal.DbContextFactory`1[StartStopLijsten.ApiService.Data.StartStopDbContext]': Cannot consume scoped service 'Microsoft.EntityFrameworkCore.DbContextOptions`1[StartStopLijsten.ApiService.Data.StartStopDbContext]' from singleton 'Microsoft.EntityFrameworkCore.IDbContextFactory`1[StartStopLijsten.ApiService.Data.StartStopDbContext]'.' The app refuses to start, and although the message is clear why it refuses to start, it doesn't make it obvious how...

Passing custom parameters through OpenIddict's client authorization request

In my passthrough authentication post , the login endpoint set a provider value on AuthenticationProperties so the OpenIddict server would know whether to challenge GitHub or ADFS: app.MapGet("login/{provider}", (string provider) => Results.Challenge(new AuthenticationProperties { RedirectUri = "/", Items = { ["provider"] = provider } }, [OpenIddictClientAspNetCoreDefaults.AuthenticationScheme])); Run that as-is and the server never sees it. Items round-trips through your own app's correlation cookie — it was never meant to become part of the outgoing OAuth2 request. Why this doesn't just work AuthenticationProperties.Items is an ASP.NET Core concept. It's how the authentication middleware remembers things like the redirect URI across the round trip to an external provider and back. The OpenIddict client builds the actual /connect/authorize request from a completely different object — an OpenIddictReq...

.NET Aspire: The price of forgetting WithReference

Recently I lost way more time than I'd like to admit on an error that turned out to be one missing line of code. The symptom looked like a networking problem. The cause was a missing WithReference call in my Aspire AppHost. Here's the exception my proxy threw the moment it tried to forward a request to my API: System.Net.Http.HttpRequestException: No such host is known. (api:443) ---> System.Net.Sockets.SocketException (11001): No such host is known. at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.ThrowException(SocketError error, CancellationToken cancellationToken) at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.System.Threading.Tasks.Sources.IValueTaskSource.GetResult(Int16 token) at System.Net.Http.HttpConnectionPool.ConnectToTcpHostAsync(String host, Int32 port, HttpRequestMessage initialRequest, Boolean async, CancellationToken cancellationToken) --- End of inner exception stack trace --- at System.Net.Http.HttpConnectionPool....

Cache stampede: when our cache turned against us

While investigating some performance issues, we ran into an ASP.NET Core API that cached a fairly expensive aggregation query for 60 seconds. Under normal load, that was fine: one request rebuilds the cache, everyone else reads from it. Under peak load, dozens of requests would arrive in that same expiry window, all see a cache miss, and all fire the same expensive query in parallel. The database didn't like that. That was the moment when our caching layer stopped helping and started hurting. A burst of requests comes in at the same time, all miss the cache, and all go hammer the database or the downstream API at once. That's a cache stampede . The cache was supposed to protect our backend, and for a few hundred milliseconds it did the opposite. Why this happens IMemoryCache.GetOrCreate (and its async sibling) looks like it protects you, but it doesn't add any locking on its own. Look at the naive version: public async Task<Report> GetReportAsync(string key) ...

YARP and Aspire: "https+http scheme is not supported"

Recently I was wiring up a YARP reverse proxy in front of a couple of Aspire-managed services: an API and an Angular frontend. Aspire gives you service discovery for free, so the obvious move is to point your YARP clusters at the logical service names instead of hardcoded URLs. My first attempt looked like this: "Clusters": { "api-cluster": { "Destinations": { "api-destination": { "Address": "https+http://api" } } }, "frontend-cluster": { "Destinations": { "frontend-destination": { "Address": "https+http://angular-frontend" } } } } The https+http:// scheme is the standard Aspire service discovery convention: try HTTPS first, fall back to HTTP. It works fine when you're resolving endpoints through HttpClient . Unfortunately YARP doesn’t like this configuration. After setting it up with these values ...

GitHub Copilot SDK issue after upgrading to the 1.0.0-beta.10

After upgrading the GitHub Copilot SDK to the 1.0.0-beta.10 version, initiating the CopilotClient no longer worked. Instead I got the following error message: System.Text.Json.JsonException: The JSON value could not be converted to System.DateTimeOffset. Path: $.timestamp | LineNumber: 0 | BytePositionInLine: 43.  ---> System.InvalidOperationException: Cannot get the value of a token type 'Number' as a string.    at System.Text.Json.ThrowHelper.ThrowInvalidOperationException_ExpectedString(JsonTokenType tokenType)    at System.Text.Json.Utf8JsonReader.TryGetDateTimeOffset(DateTimeOffset& value)    at System.Text.Json.Utf8JsonReader.GetDateTimeOffset()    at System.Text.Json.Serialization.Converters.DateTimeOffsetConverter.Read(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options)    at System.Text.Json.Serialization.Metadata.JsonPropertyInfo`1.ReadJsonAndSetMember(Object o...

EF Core 10–Smarter parameterized collections

If you've been using Entity Framework Core for a while, you've probably written a query like this: var ids = new[] { 1, 2, 3, 4, 5 }; var blogs = await context.Blogs .Where(b => ids.Contains(b.Id)) .ToListAsync(); Simple enough. But under the hood, how EF Core translates that ids collection into SQL has quietly changed(again) in EF Core 10, and this time the change is significant enough to be listed as a breaking change. Let's dig into what's happening, why the EF team made this call, and what it means for your applications. A brief history To understand EF Core 10's approach, it helps to know where things stood before. EF Core 8: The OPENJSON era In EF Core 8, when you passed a collection to a LINQ Contains or Where clause, EF encoded the entire collection as a JSON string and sent it as a single parameter. SQL Server would then unpack it using the OPENJSON function: SELECT [b].[Id], [b].[Name] FROM [Blogs] AS [b] WHERE [b].[Id] IN ( ...

Compile-Time options validation with the OptionsValidator source generator

In the previous post, we looked at how to implement IValidateOptions<T> by hand — writing a dedicated validator class, injecting services, and expressing cross-property constraints that Data Annotations can't handle. That approach gives you full control and is the right tool when validation logic is genuinely complex. While researching that post I discovered another feature that's worth knowing about: when your validation can be expressed with Data Annotation attributes, the options validation source generator (available since .NET 8) will write the IValidateOptions<T> implementation for you at compile time. You get the safety of startup validation without the boilerplate, and as a bonus the generated code is reflection-free and AOT-compatible. The problem with runtime data annotations Before the source generator existed, the standard way to add annotation-based validation was ValidateDataAnnotations() : This works, but it uses reflection at runtime to ...

Validating configuration at startup with IValidateOptions in .NET

When you build .NET applications with strongly typed configuration, IOptions<T> and its variants give you a clean way to bind appsettings.json sections to C# classes. But binding isn't the same as validating - a missing required value or an out-of-range number will happily bind to a default and silently break your app at runtime. IValidateOptions<T> is the hook .NET provides to fix that. The problem: silent misconfiguration Consider a typical options class: If Host is missing from appsettings.json , your app starts fine. The failure surfaces only when the first email is sent — in production, at 2 AM. Data Annotations ( [Required] , [Range] ) combined with ValidateDataAnnotations() help, but they fall short when you need: Cross-property validation (e.g., Port must be 465 when UseSsl is true ) Async or database-backed checks Conditional logic depending on environment Reusable validators shared across multiple options types This is where I...

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 ...

ASP.NET Core - TryParse error when using Minimal APIs

Minimal APIs are the recommended approach for building fast HTTP APIs with ASP.NET Core. They allow you to build fully functioning REST endpoints with minimal code and configuration. You don't need a controller but can just declare your API using a fluent API approach: This makes it very convenient to build your APIs. However you need to be aware that a lot of magic is going behind the scenes when using this approach. And this magic can bite you in the foot. Exactly what happened to me while building an autonomous agent invoked through a web hook. The code In my application I created the following API endpoint using the minimal API approach: The minimal API injects an AzureDevOpsWebhookParser that looks like this: Nothing special… The problem The problem was when I called this endpoint, it failed with the following error message: InvalidOperationException: TryParse method found on AzureDevOpsWebhookParser with incorrect format. Must be a static method with for...

Replacing EventCounters with the new Metrics API

If you've been using EventCounters for instrumenting your .NET applications, it's time to consider migrating to the newer System.Diagnostics.Metrics API. Based on the OpenTelemetry specification, the Metrics API offers a more modern, flexible, and standardized approach to application instrumentation. Why migrate? The Metrics API provides several advantages over EventCounters: Industry Standard : Built on OpenTelemetry, ensuring compatibility with a wide ecosystem of monitoring tools Better Performance : More efficient with lower overhead Richer Functionality : Support for histograms, exemplars, and more sophisticated metric types Improved API Design : Cleaner, more intuitive interface for defining and recording metrics Better Tooling Support : Growing ecosystem support from APM vendors and monitoring solutions Microsoft has indicated that EventCounters are in maintenance mode, with new development focused on the Metrics API. So reasons enough to m...

Code signing your .NET Core application

We stopped a long time signing our code of our backend applications however we still do it for the applications that are running on a local machine(desktop applications, console apps,...). This is because we whitelist what can be installed and executed on a local machine. Code signing remains a valuable security practice that validates the authenticity and integrity of your .NET Core applications. When you sign your code, you're essentially providing a digital certificate that proves the software comes from you and hasn't been tampered with since it was signed. Why sign your code? Before diving into the how, let's understand why code signing matters: Trust and Authenticity : Users can verify that your application comes from a legitimate source Integrity Verification : The signature ensures the code hasn't been modified after signing Windows SmartScreen : Signed applications are less likely to trigger security warnings Enterprise Requirements : Man...

Help! My Application Insights telemetry stopped working.

I want to start this blog post by stating that I have a new hero and his name is Stijn. Let me explain why… I recently upgraded an older .NET Full Framework application to the latest Application Insights NuGet package. This to make the switch from the obsolete instrumentationkey to the newer connectionstring approach. After upgrading our packages.config file (yes, this project is so old) looked like this: Looking good right? Unfortunately, the same could not be said about our telemetry itself as our Application Insights logs remained awfully empty. I doublechecked all the config files, reinstalled the nuget packages, tried to explicitly force to flush the telemetry data, everything I could think of I tried. But nothing worked. I handed the problem over to another team member (Stijn, my new hero) and he found the solution. After debugging the Application Insights code he arrived at a no-op method: This method should have all the logic to read the configuration information ...

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...