Skip to main content


Showing posts from October, 2021

GraphQL–Strawberry Shake GraphQL client

Until recently I always used the GraphQL.Client as the GraphQL client of my choice. This client is straightforward and easy-to-use. For a new project I decided to give Strawberry Shake a try. Strawberry Shake was created by Chilicream, the creators of the HotChocolate GraphQL backend for .NET . Strawberry Shake is using a different approach as the GraphQL.Client as it heavily relies on code generation and looks similar to the Apollo GraphQL client from a design point of view. I mostly followed the “Get started” documentation to get the Strawberry Shake client up and running, but I didn’t get everything up and running immediatelly so I’ll add some extra detail on the points where I got into trouble. Add the CLI tools We start by adding the Strawberry Shake CLI tools Open the folder that contains the project where you want to add the Strawberry Shake GraphQL client. Now wee need to first create a dotnet tool-manifest. dotnet new tool-manifest Getting ready...

Azure Pipelines - Unable to determine the location of vstest.console.exe

A colleague forwarded me a question about a failing build pipeline. When I took a look at the build results, I noticed that the Visual Studio Test task was failing. Inside the logs I found more details explaining what was going on: ##[warning]No results found to publish. ##[debug]Processed: ##vso[task.logissue type=warning]No results found to publish. ##[error]System.Management.Automation.CmdletInvocationException: Unable to determine the location of vstest.console.exe ---> System.IO.FileNotFoundException: Unable to determine the location of vstest.console.exe ##[debug]Processed: ##vso[task.logissue type=error;]System.Management.Automation.CmdletInvocationException: Unable to determine the location of vstest.console.exe ---> System.IO.FileNotFoundException: Unable to determine the location of vstest.console.exe    at Microsoft.TeamFoundation.DistributedTask.Task.Internal.InvokeVSTestCmdlet.GetVsTestLocation()    at Microsoft.TeamFoundation.Distributed

.NET Tools - Cannot find a manifest file

A .NET tool is a special NuGet package that contains a console application. You can install a .NET tool as a global tool (using the --global argument) or as a local tool (using the  --local argument). However when I tried to install a specific tool locally, it failed with the following error message: “Cannot find a manifest file.” dotnet tool install StrawberryShake.Tools --local Cannot find a manifest file. For a list of locations searched, specify the "-d" option before the tool name. If you intended to install a global tool, add `--global` to the command. If you would like to create a manifest, use `dotnet new tool-manifest`, usually in the repo root directory. To install a tool for local access only, it has to be added to a tool manifest file. As I didn’t create such a file, I got the error message mentioned above. To fix this, we first need to create a tool manifest file by running the dotnet new tool-manifest command: dotnet new tool-ma : Bringing VS Code to the browser

Although I’m still using Visual Studio (or Rider depending on the mood) for my day to day C# development(and F# occasionally), I use Visual Studio Code for all other languages and web development. With , your favorite code editor becomes available everywhere without the need to leave the browser and install anything. Thanks to the File System Access API support in modern browser, can access the local file system. This enables scenario’s like local file viewing end editing. Integration with Github and Azure DevOps is also available allowing you to sync your changes with repositories on both platforms. However don’t expect that is already on par with the desktop version in terms of functionality. For example, there's no internal debugging or terminal with . More information:

MassTransit - Stop handling erroneous messages

By default when a MassTransit consumer fails to handle a message (and throws an exception), the message is moved to an _error queue (prefixed by the receive endpoint queue name). This is OK for transient exceptions but probably not what you want when you have a bug in your system or there is another reason why none of the messages can be handled succesfully. In that case, another feature of MassTransit becomes handy; the kill switch . A Kill Switch is used to prevent failing consumers from moving all the messages from the input queue to the error queue. By monitoring message consumption and tracking message successes and failures, a Kill Switch stops the receive endpoint when a trip threshold has been reached. You can configure a kill switch for a specific endpoint or for all receiver endpoints on the bus. Here is a short example on how to configure the kill switch for all receiver endpoints:   In the above example, the kill switch will activate after 10 messages

Service decomposition and service design

Finding the boundaries of your system and decompose it into multiple services sounds easy, but it certainly isn’t. If you are interested in this topic, check out the blog series by Vadim Samokhin: Why you should split the monolith Wrong way of defining service boundaries What characteristics my services should possess How to define service boundaries Remark: After writing this post, I noticed that Vadim created a blog post linking to the series above and included also some other related posts.

Azure AKS–Save some money using spot node pools

One of the ways you can save some money using Azure is by using spot node pools for your Azure Kubernetes Service cluster. What’s a spot node pool? Using a spot node pool allows you to take advantage of unused Azure capacity at a significant cost savings. At any point in time when Azure needs the capacity back, the Azure infrastructure will evict spot nodes. Therefore, Spot nodes are great for workloads that can handle interruptions like batch processing jobs, dev/test environments, large compute workloads, and more. Remark: A spot node pool can't be the cluster's default node pool. A spot node pool can only be used for a secondary pool. Pricing for a spot node pool Pricing for spot instances is variable , based on region and SKU. For more information, see pricing for Linux and Windows . You do have the option to set a max price. In case the price is exceeded the spot node is evicted from your cluster. Schedule a deployment to use the spot node pool A spot node

Winget–A package manager for Windows

I’ve been using Chocolatey for a long time as an easy way to get my Windows machine configured with all the software I need. With the release of version 1.1 of the Windows Package Manager(WinGet) I thought it was a good time to give it a try. Installation Chances are high that WinGet is already available on your machine. Open a terminal and type winget. If it is available you should see something like this: If not, the Windows Package Manager is distributed with the App Installer from the Microsoft Store. You can also download and install the Windows Package Manager from GitHub , or just directly install the latest available released version. Searching a package The list of available packages is quite large(more than 2,600 packages in the Windows Package Manager app repository ). Just run winget search <SomePackage> to see if the package you are looking for has available there. For example let’s search for my favorite git client GitKraken: PS C:\Users\bawu

ASP.NET Core–Running Swagger behind a reverse proxy

Today I helped out a colleague who was struggling with the following use case: We have an ASP.NET Core Web API with OpenAPI(Swagger) integration enabled. This ASP.NET Core Web API was running behind a reverse proxy( Yarp in case you want to know) and isn’t directly accessibel. To explain the problem he had, let’s start from the following situation: The ASP.NET Core application was running on http://internalname:5000 The YARP reverse proxy was running on https://localhost/proxy   When browsing to the Swagger endpoint on https://localhost/proxy/swagger , we got the following: Do you notice the application url in the address bar vs the url in the Servers dropdown? If we try to invoke a specific endpoint through the Swagger UI, Swagger tries to do the call directly to http://internalname:5000 which results in 404 error as the service is not available directly on this address. It seems that Swagger doesn’t respect the Forwarded headers as provided through Yarp.

Internal.Cryptography.CryptoThrowHelper+WindowsCryptographicException–The system cannot find the file specified

After deploying an application to production, it failed with the following error message: The system cannot find the file specified Internal.Cryptography.CryptoThrowHelper+WindowsCryptographicException: at Internal.Cryptography.Pal.CertificatePal.FilterPFXStore (System.Security.Cryptography.X509Certificates, Version=, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a) at Internal.Cryptography.Pal.CertificatePal.FromBlobOrFile (System.Security.Cryptography.X509Certificates, Version=, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a) at System.Security.Cryptography.X509Certificates.X509Certificate..ctor (System.Security.Cryptography.X509Certificates, Version=, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a) at System.Security.Cryptography.X509Certificates.X509Certificate2..ctor (System.Security.Cryptography.X509Certificates, Version=, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a) Let’s take a look at the code that caused this issue: Not muc

DaprCon is coming on October 19th-20th 2021

What is Dapr ? Dapr helps developers build event-driven, resilient distributed applications. Whether on-premises, in the cloud, or on an edge device, Dapr helps you tackle the challenges that come with building microservices and keeps your code platform agnostic. The Dapr ecosystem keeps growing and now they’ll have their first virtual conference ‘DaprCon’ next week. DaprCon will include a variety of content including a keynote, technical sessions, panel discussions and real-world experiences of adopters building with Dapr DaprCon is a fully virtual event that will be streamed on YouTube and attendance is free! To watch the live events just follow these two links: DaprCon Day 1 DaprCon Day 2 More information:

MassTransit–Message versioning

MassTransit dedicated a whole documentation page to message versioning but it still wasn’t completely clear to me how it worked. Let’s use this blog post to see what’s going on… Publishing messages Let’s first focus on the sending side. Publishing a first version of our message contract We’ll start with a first version of our message contract: Let’s send this to RabbitMQ using: Time to open the RabbitMQ Management portal and take a look how the message payload is constructed: Creating a second version of our message contract Let’s introduce a v2 version of our message contract: If we send it to RabbitMQ in the same way: There isn’t such a big difference when comparing the payloads: The ‘messagetype’ value is different and of course the message itself. But that’s it. Send a backwards compatible version Let’s now construct a message that implements both contracts: And send that one: If we now check the payload, we see that 1 message is put on th

.NET 6.0 - Migrate to ASP.NET Core 6

.NET 6 introduces a new hosting model for ASP.NET Core applications. This model is streamlined and reduces the amount of boilerplate code required to get a basic ASP.NET Core application up and running. There are a lot of blog posts out there explaining this new hosting model, but I like to share the guide written by David Fowler, software architect on the ASP.NET team. He walks you through the building blocks , explains the differences in the hosting model , shares a list of frequently asked questions and provides you a cheatsheet .  

ASP.NET Core - InvalidOperationException: 'VaryByQueryKeys' requires the response cache middleware.

Yes, I’m still rewriting an existing ASP.NET Web API application to ASP.NET Core. I ported an existing action method where I was using response caching. I previously ported other action methods that used caching but this one was a little bit different because I had to take the querystring values into account for the caching. This is easily arranged by specifying the name of the query string parameter using the VaryByQueryKeys property on the ResponseCache attribute. Small tip: If you want to take all query string parameters into account, you can use “*” as a single value. When I tried to call this method I got a 500 error. Inside the logs I noticed the following error message: InvalidOperationException: 'VaryByQueryKeys' requires the response cache middleware. Although caching seemed to work for other scenario’s, when I used the VaryByQueryKeys property I had to add the response caching middleware. Here is my Startup.ConfigureServices(): And my Startup.Conf

ASP.NET Core–Route arguments are case sensitive

I’m currently rewriting an existing ASP.NET Web API application to ASP.NET Core. Along the way I encountered some issues, here is one specific lesson I learned… After porting an ASP.NET Web API controller to .NET Core, a specific action method looked like this: Looked OK to me. But when I tried to invoke this action method, I got a 400 error back: {   "type" : " " ,   "title" : "One or more validation errors occurred." ,   "status" : 400 ,   "traceId" : "00-3c02d7d0541cbb49a1790b11a71d871a-885c9350d2c4864c-00" ,   "errors" : {     "ID" : [       "The value '{id}' is not valid."     ]   } } It wasn’t immediatelly obvious to me what I did wrong. Maybe you spot my mistake? Let’s have a look at the route attribute: [HttpGet(“{ id }”)] and at the metho

ASP.NET Core - Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorException: Ambiguous HTTP method for action

I’m currently rewriting an existing ASP.NET Web API application to ASP.NET Core. I ported a controller from ASP.NET Web API to ASP.NET Core. Here is the end result: Everything looked OK at first sight, but when I tried to run the application I got the following error message: Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorException: Ambiguous HTTP method for action - Controllers.AuthTypeController.GetAll (VLM.IAM.Services). Actions require an explicit HttpMethod binding for Swagger/OpenAPI 3.0    at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GenerateOperations(IEnumerable`1 apiDescriptions, SchemaRepository schemaRepository)    at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GeneratePaths(IEnumerable`1 apiDescriptions, SchemaRepository schemaRepository)    at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GetSwagger(String documentName, String host, String basePath)    at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext

ASP.NET Core - ActionResult doesn’t work with IList

I’m currently rewriting an existing ASP.NET Web API application to ASP.NET Core. Yesterday I blogged about the use of the ActionResult<T> to combine the type safety of typed controllers with the list of out-of-the-box actionresults. While introducing the ActionResult<T> class everywhere, I stumbled over one use case where I got a compiler error. Here is the specific code: When you try to compile this code in Visual Studio it fails with the following error message: CS0029: Cannot implicitly convert type ‘System.Collection.Generic.IList<T> to Microsoft.AspNetCore.Mvc.ActionResult<System.Collection.Generic.IList<T>>. The reason is because C# doesn't support implicit cast operators on interfaces. Consequently, we need to convert the interface to a concrete type if we want to use it as our type argument for ActionResult<T>. An easy fix is to wrap the IList in a concrete type as I did in the example below:

ASP.NET Core - How to handle the ‘NotFound’ use case when using a typed controller

I’m currently rewriting an existing ASP.NET Web API application to ASP.NET Core. While doing that, I (re)discovered some ASP.NET Core features I forgot they existed. One of the features that ASP.NET Core offers are ‘typed controllers’. I don’t think that is the official name but you’ll know what I mean when you take a look at the example below: In the example above I created an action method GetTodoItem that returned a typed object of type ‘ TodoItem’ (or to be even more correct a Task<TodoItem> ).  This makes your life as a developer easy as you don’t have to think about the ins and outs of ASP.NET. It almost feels like that this action method is exactly the same as any other method you’ll find on a class. But what if wanted to return a 404 message when no TodoItem could be found for the specified id. Should I throw a specific exception? In ASP.NET Web API this was possible through the use of the HttpResponseException . In ASP.NET Core there is a better alternative t

GraphQL - Use @skip and @include on fragments

GraphQL has multiple built-in directives like @deprecated, @include, @skip and some others like @stream and @defer that are not part of the official spec (yet). With the @include directive you can conditionally include fields based on a specified argument: The @skip directive does exactly the opposite and excludes fields based on a specified argument: But applying these directives for every field that should be included or excluded feels like a lot of repetition: Here is a trick for you, the @skip and @include directives can be used on fields, fragment spreads, and inline fragments. This can help us to make our GraphQL queries more readable. Here is the example rewritten using an inline fragment: And here is the same example with the fragment spread into the query:

Azure DevOps–Emoji’s

Both in pull request comments and wiki pages in Azure DevOps, you can use emojis while documenting, adding comments or reviewing requests. If you want to find out what emoji’s are supported; you can find the full list here .  

Application Insights Telemetry enricher for Kubernetes

Quick tip if you are hosting your ASP.NET Core application in Kubernetes: have a look at the Microsoft Application Insights for Kubernetes nuget package . Through this package you can enrich your Application Insights telemetry data with Kubernetes specific information: After adding the nuget package you can register the enricher by adding the following code: More information: