These days, end to end browser testing is a pretty standard practice amongst modern development teams. Whether that’s with Selenium, WebDriver.IO or Cypress, realistically as long as you are getting the tests up and running, you’re doing well.

Over the past couple of years, Cypress had become a defacto end to end testing framework. I don’t think in the last 5 years I’ve been at a company that hasn’t atleast given it a try and built out some proof of concepts. And look, I like Cypress, but after some time I started getting irritated with a few caveats (Many of which are listed by Cypress themselves here).

Notably :

  • The “same origin” URL limitation (Essentially you must be on the same root domain for the entire test) is infuriating when many web applications run some form of SSO/OAuth, even if using something like Auth0 or Azure AD B2C. So you’re almost dead in the water immediately.
  • Cypress does not handle multiple tabs
  • Cypress cannot run multiple browsers at the same time (So testing some form of two way communication between two browsers is impossible)
  • The “Promise” model and chaining of steps in a test seemed ludicrously unwieldy. And when trying to get more junior team members to write tests, things quickly entered into a “Pyramid of doom“.

As I’ll talk about later in another post, the biggest thing was that we wanted a simple model for users writing tests in Gherkin type BDD language. We just weren’t getting that with Cypress and while I’m sure people will tell me all the great things Cypress can do, I went out looking for an alternative.

I came across Playwright, a cross platform, cross browser automation testing tool that did exactly what it says on the tin with no extras. Given my list of issues above with Cypress, I did have to laugh that this is a very prominent quote on their homepage :

Multiple everything. Test scenarios that span multiple tabs, multiple origins and multiple users. Create scenarios with different contexts for different users and run them against your server, all in one test.

They definitely know which audience they are playing up to.

Playwright has support for tests written in NodeJS, Java, Python and of course, C# .NET. So let’s take a look at the latter and how much work it takes to get up and running.

For an example app, let’s assume that we are going to write a test that has the following test scenario :

Given I am on
When I type into the search box
And I press the button with the text "Google Search"
Then the first result is domain

Obviously this is a terrible example of a test as the result might not always be the same! But I wanted to just show a little bit of a simple test to get things going.

Let’s get cracking on a C# test to execute this!

Now the thing with Playwright is, it’s actually just a C# library. There isn’t some magical tooling that you have to download or extensions to Visual Studio that you need to get everything working nicely. You can write everything as if you were writing a simple C# unit test.

For this example, let’s just create a simple MSTest project in Visual Studio. You can of course create a test project with NUnit, XUnit or any other testing framework you want and it’s all going to work much the same.

Next, let’s add the PlayWright nuget package with the following command in our Package Manager Console. Because we are using MSTest, let’s add the MSTest specific Nuget package as this has a few helpers that speed things up in the future (Realistically, you don’t actually need this and can install Microsoft.Playwright if you wish)

Install-Package Microsoft.Playwright.MSTest

Now here’s my test. I’m going to dump it all here and then walk through a little bit on how it works.

public class MyUnitTests : PageTest
    public async Task WhenDotNetCoreTutorialsSearchedOnGoogle_FirstResultIsDomainDotNetCoreTutorialsDotCom()
        //Given I am on
        await Page.GotoAsync("");
        //When I type into the search box
        await Page.FillAsync("[title='Search']", "");
        //And I press the button with the text "Google Search"
        await Page.ClickAsync("[value='Google Search'] >> nth=1");
        //Then the first result is domain
        var firstResult = await Page.Locator("//cite >> nth=0").InnerTextAsync();
        Assert.AreEqual("", firstResult);

Here’s some things you may notice!

First, our unit test class inherits from “PageTest” like so :

public class MyUnitTests : PageTest

Why? Well because the PlayWright.MSTest package contains code to set up and tear down browser objects for us (And it also handles concurrent tests very nicely). If we didn’t use this package, either because we are using a different test framework or we want more control, the set up code would look something like :

IPage Page;
public async Task TestInitialize()
    var playwright = await Playwright.CreateAsync();
    var browser = await playwright.Chromium.LaunchAsync(new BrowserTypeLaunchOptions
        Headless = false
    Page = await browser.NewPageAsync();

So it’s not the end of the world, but it’s nice that the framework can handle it for us!

Next what you’ll notice is that there are no timeouts *and* all methods are async. By timeouts, what I mean is the bane of every selenium developers existence is “waiting” for things to show up on screen, especially in javascript heavy web apps.

For example, take these two calls one after the other :

//Given I am on
await Page.GotoAsync("");
//When I type into the search box
await Page.FillAsync("[title='Search']", "");

In other frameworks we might have to :

  • Add some sort of arbitrary delay after the GoTo call to wait for the page to properly load
  • Write some code to check if a particular element is on screen before continuing (Like a WaitUntil type call)
  • Write some custom code for our Fill method that will poll or retry until we can find that element and type

Instead, Playwright handles that all under the hood for you and assumes that when you want to fill a textbox, that eventually it’s going to show and so it will wait till it does. The fact that everything is async also means it’s non-blocking, which is great if you are using playwright locally since it’s not gonna freeze everything on your screen for seconds at a time!

The rest of the test should be pretty self explanatory, we are using some typical selectors to fill out the google search and find the top result, and our Assert is from our own framework. Playwright does come packaged with it’s own assertion framework, but you don’t have to use it if you don’t want to!

And.. That’s it!

There are some extremely nifty tools that come packaged with Playwright that I’m going to write about in the coming days, including the ability to wire up with Specflow for some BDD goodness. What I will say so far is that I like the fact that Playwright has hit the right balance between being an automation test framework *and* being able to do plain browser automation (For example to take a screenshot of a web page). Cypress clearly leans on the testing side, and Selenium I feel often doesn’t feel like a testing framework as much as it feels like a scripting framework that you can jam into your tests. So far, so good!

Join over 3,000 subscribers who are receiving our weekly post digest, a roundup of this weeks blog posts.
We hate spam. Your email address will not be sold or shared with anyone else.

Visual Studio 2022 17.2 shipped the other day, and in it was a handy little feature that I can definitely see myself using a lot going forward. That is the IEnumerable Visualizer! But before I dig into what it does (And really it’s quite simple), I wanted to quickly talk about why it was so desperately needed.

Let’s imagine I have a class like so :

class Person
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }

And somewhere in my code, I have a List of people with a breakpoint on it. Essentially, I want to quickly check the contents of this list and make sure while debugging that I have the right data in the right place.

Our first port of call might be to simply is do the “Hover Results View” method like so :

But… As we can see it doesn’t exactly help us to view the contents easily. We can either then go and open up each item individually, or in some cases we can override the ToString method. Neither of which may be preferable or even possible depending on our situation.

We can of course use the “Immediate Window” to run queries against our list if we know we need to find something in particular. Something like :

? people.Where(x => x.FirstName == "John")

Again, it’s very adhoc and doesn’t give us a great view of the data itself, just whether something exists or not.

Next, we can use the Autos/Watch/Locals menu which does have some nice pinning features now, but again, is a tree view and so it’s hard to scroll through large pieces of data easily. Especially if we are trying to compare multiple properties at once.

But now (Again, you require Visual Studio 2022 17.2), notice how in the Autos view we have a little icon called “View” right at the top of the list there. Click that and…

This is the new IEnumerable visualizer! A nice tabular view of the data, that you can even export to excel if you really need to. While it’s a simple addition and really barebones, it’s something that will see immediate use in being able to debug your collections more accurately.

Join over 3,000 subscribers who are receiving our weekly post digest, a roundup of this weeks blog posts.
We hate spam. Your email address will not be sold or shared with anyone else.

A number of times in recent years, I’ve had the chance to work in companies that completely design out entire API’s using OpenAPI, before writing a single line of code. Essentially writing YAML to say which endpoints will be available, and what each API should accept and return.

There’s pros and cons to doing this of course. A big pro is that by putting in upfront time to really thinking about API structure, we can often uncover issues well before we get half way through a build. But a con is that after spending a bunch of time defining things like models and endpoints in YAML, we then need to spend days doing nothing but creating C# classes as clones of their YAML counterparts which can be tiresome and frankly, demoralizing at times.

That’s when I came across Open API Generator :

It’s a tool to take your API definitions, and scaffold out APIs and Clients without you having to lift a finger. It’s surprisingly configurable, but at the same time it isn’t too opinionated and allows you to do just the basics of turning your definition into controllers and models, and nothing more.

Let’s take a look at a few examples!

Installing Open API Generator

If you read the documentation here, it would look like installing is a huge ordeal of XML files, Maven and JAR files. But for me, using NPM seemed to be simple enough. Assuming you have NPM installed already (Which you should!), then you can simply run :

npm install @openapitools/openapi-generator-cli -g

And that’s it! Now from a command line you can run things like :

openapi-generator-cli version

Scaffolding An API

For this example, I actually took the PetStore API available here :

It’s just a simple YAML definition that has CRUD operations on an example API for a pet store. I took this YAML and stored it as “petstore.yaml” locally. Then I ran the following command in the same folder  :

openapi-generator-cli generate -i petstore.yaml -g aspnetcore -o PetStore.Web --package-name PetStore.Web

Pretty self explanatory but one thing I do want to point out is the -g flag. I’m passing in aspnetcore here but in reality, Open API Generator has support to generate API’s for things like PHP, Ruby, Python etc. It’s not C# specific at all!

Our project is generated and overall, it looks just like any other API you would build in .NET

Notice that for each group of API’s in our definition, it’s generated a controller each and models as well.

The controllers themselves are well decorated, but are otherwise empty. For example here is the AddPet method :

/// <summary>
/// Add a new pet to the store
/// </summary>
/// <param name="body">Pet object that needs to be added to the store</param>
/// <response code="405">Invalid input</response>
[Consumes("application/json", "application/xml")]
public virtual IActionResult AddPet([FromBody]Pet body)
    //TODO: Uncomment the next line to return response 405 or use other options such as return this.NotFound(), return this.BadRequest(..), ...
    // return StatusCode(405);
    throw new NotImplementedException();

I would note that this is obviously rather verbose (With the comments, Consumes attribute etc), but a lot of that is because that’s what we decorated our OpenAPI definition with, therefore it tries to generate a controller that should act and function identically.

But also notice that it hasn’t generated a service or data layer. It’s just the controller and the very basics of how data gets in and out of the API. It means you can basically scaffold things and away you go.

The models themselves also get generated, but they can be rather verbose. For example, each model gets an override of the ToString method that looks a bit like so :

/// <summary>
/// Returns the string presentation of the object
/// </summary>
/// <returns>String presentation of the object</returns>
public override string ToString()
    var sb = new StringBuilder();
    sb.Append("class Pet {\n");
    sb.Append("  Id: ").Append(Id).Append("\n");
    sb.Append("  Category: ").Append(Category).Append("\n");
    sb.Append("  Name: ").Append(Name).Append("\n");
    sb.Append("  PhotoUrls: ").Append(PhotoUrls).Append("\n");
    sb.Append("  Tags: ").Append(Tags).Append("\n");
    sb.Append("  Status: ").Append(Status).Append("\n");
    return sb.ToString();

It’s probably overkill, but you can always delete it if you don’t like it.

Obviously there isn’t much more to say about the process. One command and you’ve got yourself a great starting point for an API. I would like to say that you should definitely dig into the docs for the generator as there is actually a tonne of flags to use that likely solve a lot of hangups you might have about what it generates for you. For example there is a flag to use NewtonSoft.JSON instead of System.Text.Json if that is your preference!

I do want to touch on a few pros and cons on using a generator like this though…

The first con is that updates to the original Open API definition really can’t be “re-generated” into the API. There are ways to do it using the tool but in reality, I find it unlikely that you would do it like this. So for the most part, the generation of the API is going to be a one time thing.

Another con is as I’ve already pointed out, the generator has it’s own style which may or may not suit the way you like to develop software. On larger API’s fixing some of these quirks of the generator can be annoying. But I would say that for the most part, fixing any small style issues is still likely to take less time than writing the entire API from scratch by hand.

Overall however, the pro of this is that you have a very consistent style. For example, I was helping out a professional services company with some of their code practices recently. What I noticed is that they spun up new API’s every month for different customers. Each API was somewhat beholden to the tech leads style and preferences. By using an API generator as a starting point, it meant that everyone had a somewhat similar starting point for the style we wanted to go for, and the style that we should use going forward.

Generating API Clients

I want to quickly touch on another functionality of the Open API Generator, and that is generating clients for an API. For example, if you have a C# service that needs to call out to a web service, how can you quickly whip up a client to interact with that API?

We can use the following command to generate a Client library project :

openapi-generator-cli generate -i petstore.yaml -g csharp -o PetStore.Client --package-name PetStore.Client

This generates a very simple PetApi interface/class that has all of our methods to call the API.

For example, take a look at this simple code :

var petApi  = new PetApi("");
var myPet = petApi.GetPetById(123);
myPet.Name = "John Smith";

Unlike the server code we generated, I find that the client itself is often able to be regenerated as many times as needed, and over long periods of time too.

As I mentioned, the client code is very handy when two services need to talk to each other, but I’ve also found it useful for writing large scale integration tests without having to copy and paste large models between projects or be mindful about what has changed in an API, and copy those changes over to my test project.

Join over 3,000 subscribers who are receiving our weekly post digest, a roundup of this weeks blog posts.
We hate spam. Your email address will not be sold or shared with anyone else.

Now that the flames have simmered down on the Hot Reload Debacle, maybe it’s time again to revisit this feature!

I legitimately feel this is actually one of the best things to be released with .NET in a while. The amount of frustrating times I’ve had to restart my entire application because of one small typo… whereas now it’s Hot Reload to the rescue!

It’s actually a really simple feature so this isn’t going to be too long. You’ll just have to give it a crack and try it out yourself. In short, it looks like this when used :

In case it’s too small, you can click to make it bigger. But in short, I have a console application that is inside a never ending loop. I can change the Console.WriteLine text, and immediately see the results of my change *without* restarting my application. That’s the power of Hot Reload!

And it isn’t just limited to Console Applications. It (should) work with Web Apps, Blazor, WPF applications, really anything you can think of. Obviously there are some limitations. Notably that if you edit your application startup (Or other run-once type code), your application will hot reload, it doesn’t re-run any code blocks, meaning you’ll need to restart your application to get that startup ran again. I’ve also at times had the Hot Reload fail with various errors, usually meaning I just restart and we are away again.

Honestly, one of the biggest things to get used to is the mentality of Hot Reload actually doing something. It’s very hard to “feel” like your changes have been applied. If I’m fixing a bug, and I do a reload and the bug still exists…. It’s hard for me to not stop the application completely and restart just to be sure!

Hot Reload In Visual Studio 2022

Visual Studio 2019 *does* have a hot reload functionality, but it’s less featured (Atleast for me). Thus I’m going to show off Visual Studio 2022 instead!

All we need to do is edit our application while it’s running, then look to our nice little task bar in Visual Studio for the following icon :

That little icon with two fishes swimming after each other (Or.. atleast that’s what it looks like to me) is Hot Reload. Press it, and you are done!

If that’s a little too labour intensive for you, there is even an option to Hot Reload on file save.

If you’re coming from a front end development background you’ll be used to file watchers recompiling your applications based on a save only. On larger projects I’ve found this to maybe be a little bit more pesky (If Hot Reload is having issues, having popups firing off every save is a bit annoying), but on smaller projects I’ve basically run this without a hitch everytime.

Hot Reload From Terminal

Hot Reload from a terminal or command line is just as easy. Simple run the following from your project directory :

dotnet watch

Note *without* typing run after (Just incase you used to use “dotnet watch run”). And that’s it!

Your application will now run with Hot Reload on file save switched on! Doing this you’ll see output looking something like

watch : Files changed: F:\Projects\Core Examples\HotReload\Program.cs~RF1f7ccc54.TMP, F:\Projects\Core Examples\HotReload\Program.cs, F:\Projects\Core Examples\HotReload\qiprgg31.zfd~
watch : Hot reload of changes succeeded.

And then you’re away laughing again!


Join over 3,000 subscribers who are receiving our weekly post digest, a roundup of this weeks blog posts.
We hate spam. Your email address will not be sold or shared with anyone else.

A big reason people who develop in .NET languages rave about Visual Studio being the number one IDE, is auto complete and intellisense features. Being able to see what methods/properties are available on a class or scrolling through overloads of a particular method are invaluable. While more lightweight IDE’s like VS Code are blazingly fast.. I usually end up spending a couple of hours setting up extensions to have it function more like Visual Studio anyway!

That being said. When I made the switch to Visual Studio 2022, there was something off but I couldn’t quite put my finger on it. I actually switched a couple of times back to Visual Studio 2019, because I felt more “productive”. I couldn’t quite place it until today.

What I saw was this :

Notice that intellisense has been extended to also predict entire lines, not just the complete of the method/type/class I am currently on. At first this felt amazing but then I started realizing why this was frustrating to use.

  1. The constant flashing of the entire line subconsciously makes me stop and read what it’s suggesting to see if I’ll use it. Maybe this is just something I would get used to but I noticed myself repeatedly losing my flow or train of thought to read the suggestions. Now that may not be that bad until you realize…
  2. The suggestions are often completely non-sensical when working on business software. Take the above suggestion, there is no type called “Category”. So it’s actually suggesting something that should I accept, will actually break anyway.
  3. Even if you don’t accept the suggestions, my brain subconsciously starts typing what they suggest, and therefore end up with broken code regardless.
  4. And all of the above is made even worse because the suggestions completely flip non-stop. In a single line, and even at times following it’s lead, I get suggested no less than 4 different types.

Here’s a gif of what I’m talking about with all 4 of the issues present.


Now maybe I’ll get used to the feature but until then, I’m going to turn it all off. So if you are like me and want the same level of intellisense that Visual Studio 2019 had, you need to go :

Tools -> Options -> Intellicode (Not intellisense!)

Then disable the following :

  • Show completions for whole lines of code
  • Show completions on new lines

After disabling these, restart Visual Studio and you should be good to go!

Again, this only affects the auto-complete suggestions for complete lines. It doesn’t affect completing the type/method, or showing you a method summary etc.

Join over 3,000 subscribers who are receiving our weekly post digest, a roundup of this weeks blog posts.
We hate spam. Your email address will not be sold or shared with anyone else.

The past few days I’ve been setting up a SonarQube server to do some static analysis of code. For the most part, I was looking for SonarQube to tell us if we had some serious vulnerabilities lurking anywhere deep in our codebases, especially some of the legacy code that was written 10+ years ago. While this sort of static analysis is pretty limited, it can pick up things like SQL Injection, XSS, and various poor security configuration choices that developers sometimes make in the heat of the moment.

And how did we do? Well.. Actually pretty good! We don’t write raw SQL queries, instead preferring to use EntityFramework Linq2SQL, which for the most part protects us from SQL injection. And most of our authentication/authorization mechanisms are out of the box .NET/.NET Core components, so if we have issues there… Then the entire .NET ecosystem has bigger problems.

What we did find though was millions of non-critical warnings such as this :

Unused "using" should be removed

I’ll be the first to admit, I’m probably more lenient than most when it comes to warnings such as this. It doesn’t make any difference to the code, and you rarely notice it anyway. Although I have worked with other developers who *always* pull things like this up in code reviews, so each to their own!

My problem was, SonarQube is right, I probably should remove these. But I really didn’t want to manually go and open each file and remove the unused using statements. I started searching around and low and behold, Visual Studio has a feature inbuilt to do exactly this!

If you right click a solution or project in Visual Studio, you should see an option to “Analyze and Code Cleanup” like so :

I recommend first selecting “Configure Code Cleanup” from this sub menu so that we can configure exactly what we want to clean up :

As you can see from the above screenshot, for me I turned off everything except removing unnecessary usings and sorting them. You can of course go through the other available fixers and add them to your clean up Profile before hitting OK.

Right clicking your Solution/Project, and selecting “Analyze and Code Cleanup” then “Run Code Analysis” will instantly run these fixers over your entire project or solution. Instantly letting you pass this pesky rule, and cleaning up your code at the same time!

Now I know, this isn’t exactly a big deal removing unused usings. I think for me, it was more the fact I didn’t even know this tool existed right there in vanilla Visual Studio.

Join over 3,000 subscribers who are receiving our weekly post digest, a roundup of this weeks blog posts.
We hate spam. Your email address will not be sold or shared with anyone else.

Ever since I started using constructor dependency injection in my .NET/.NET Core projects, there has been essentially a three step process to adding a new dependency into a class.

  1. Create a private readonly field in my class, with an underscore prefix on the variable name
  2. Edit the constructor of my class to accept the same type, but name the parameter without a prefix
  3. Set the private readonly field to be the passed in parameter in the constructor

In the end, we want something that looks like this :

public class UserService
    private readonly IUserRepository _userRepository;
    public UserService(IUserRepository userRepository)
        _userRepository = userRepository;

Well at least, this used to be the process. For a few years now, I’ve been using a nice little “power user” trick in Visual Studio to do the majority of this work for me. It looks a bit like this :

The feature of auto creating variables passed into a constructor is actually turned on by default in Visual Studio, however the private readonly with an underscore naming convention is not (Which is slightly annoying because that convention is now in Microsoft’s own standards for C# code!).

To add this, we need do the following in Visual Studio. The exact path to the setting is :

Tools => Options => Text Editor => C# => Code Style => Naming

That should land you on this screen :

The first thing we need to do is click the “Manage naming styles” button, then click the little cross to add. We should fill it out like so :

I would add that in our example, we are doing a camelCase field with an underscore prefix, but if you have your own naming conventions you use, you can also do it here. So if you don’t use the underscore prefix, or you use kebab casing (ew!) or snake casing (double ew!), you can actually set it up here too!

Then on the naming screen, add a specification for Private or Internal, using your _fieldName style. Move this all the way to the top :

And we are done!

Now, simply add parameters to the constructor and move your mouse to the left of the code window to pop the Quick Actions option, and use the “Create and Assign Field” option.

Again, you can actually do this for lots of other types of fields, properties, events etc. And you can customize all of the naming conventions to work how you like.

I can’t tell you how many times I’ve been sharing my screen while writing code, and people have gone “What was that?! How did you do that?!”, and by the same token, how many times I’ve been watching someone else code and felt how tedious it is to slowly add variables one by one and wire them up!

Join over 3,000 subscribers who are receiving our weekly post digest, a roundup of this weeks blog posts.
We hate spam. Your email address will not be sold or shared with anyone else.

In the coming months I’ll be covering the new features of .NET 6, including things like MAUI (Cross platform GUI in .NET), PriorityQueue, and so much more. So I thought it would be worth talking a little bit on how to actually get set up to use .NET 6 if you are looking to touch this new tech early on.

In previous versions of .NET/.NET Core, the process to accessing preview versions was somewhat convoluted and involved installing a second “preview” version of Visual Studio to get up and running, but all that’s out the window now and things are easier than ever!

Setting Up .NET 6 SDK

To start, head over to the .NET 6 download page and download the latest SDK (not runtime!) for your operating system :

After installing, from a command prompt, you should be able to run “dotnet –info” and see something similar to the below :

.NET SDK (reflecting any global.json):
 Version:   6.0.100-preview.2.21155.3
 Commit:    1a9103db2d

It is important to note that this is essentially telling you that the default version of the .NET SDK is .NET 6 (Although, sometimes in Visual Studio, it will ignore preview versions as we will shortly see). This is important to note because any projects that don’t utilize a global.json will now be using .NET 6 (and a preview version at that). We have a short guide on how global.json can determine which SDK is used right here.

Setting Up Visual Studio For .NET 6

Now for the most part, developers of .NET will use Visual Studio. And the number one issue I find when people say “the latest version of .NET does not work in Visual Studio”, is because they haven’t downloaded the latest updates. I don’t care if a guide says “You only need version X.Y.Z” and you already have that. Obviously you need Visual Studio 2019, but no matter what anyone tells you about required versions, just be on the latest version.

You can check this by going to Help, then Check For Updates inside Visual Studio. The very latest version at the time of writing is 16.9.1, but again, download whatever version is available to you until it tells you you are up to date.

After installing the latest update, there is still one more feature flag to check. Go Tools -> Options, then select Preview Features as per the screenshot below. Make sure to tick the “Use previews of the .NET Core SDK”. Without this, Visual Studio will use the latest version of the .NET SDK installed, that is *not* a preview version. Obviously once .NET 6 is out of preview, you won’t need to do this, but if you are trying to play with the latest and greatest you will need this feature ticked.

After setting this, make sure to restart Visual Studio manually as it does not automatically do it for you. And you don’t want to be on the receiving end of your tech lead asking if you have “turned it off and on again” do you!

Migrating A Project To .NET 6

For the most part, Visual Studio will likely not create projects in .NET 6 by default when creating simple applications like console applications. This seems to vary but.. It certainly doesn’t for me. But that’s fine, all we have to do is edit our csproj file and change our target framework to net6.0 like so :


If you build and you get the following :

The current .NET SDK does not support targeting .NET 6.0.  Either target .NET 5.0 or lower, or use a version of the .NET SDK that supports .NET 6.0

The first thing to check is

  1. Do you have the correct .NET 6 SDK installed. If not, install it.
  2. Is .NET 6 still in preview? Then make sure you have the latest version of Visual Studio *and* have the “Use previews of the .NET Core SDK” option ticked as per above.

And that’s it! You’re now all set up to use the juicy goodness of .NET 6, and all those early access features in preview versions you’ve been waiting for!

Join over 3,000 subscribers who are receiving our weekly post digest, a roundup of this weeks blog posts.
We hate spam. Your email address will not be sold or shared with anyone else.

Free C# 9 Video Course

Are you one of those people that sees a big wall of text on a programming blog and instantly tunes out? Well you aren’t alone! In fact, I got hassled so much with people asking me “Can’t you just quickly share your screen and show me?” rather than reading a blog post, I decided I may as well quickly record the need to know bits of C# 9. So that’s what I’ve done!

My “What’s New In C# 9” course is completely free and will get you up and using C# 9 features in less than an hour (seriously!). Did I mention it’s FREE?

Watch For Free Now!

As .NET 5 is rolled out, so too is another version of C#, this time being C# 9. I’m actually pretty excited for some of the features that, fingers crossed, should make it into the final release. But as always, I don’t want to wait till the official release, and instead jump in early and play with all the new shiny things.

So in actual fact, getting setup with C# 9 is basically the same as getting setup with .NET Preview 5. And luckily for you, we have a shiny guide right here : But for the cliffnotes of that post :

  • Download and install the latest .NET 5 SDK from here :
  • Ensure that your version of Visual Studio 2019 is atleast 16.7 by clicking Help => Check For Updates inside Visual Studio. If in doubt, update.
  • Go Tools => Options inside Visual Studio, then select “Preview Features” and tick the box that says “Use previews of the .NET Core SDK”. Then restart Visual Studio.

Once the .NET 5 Preview SDK is installed and setup, then the only thing you need to do is edit your .csproj file and add a lang element like so :

<Project Sdk="Microsoft.NET.Sdk">


If you’ve done all of that and you get either of these errors :

The reference assemblies for .NETFramework,Version=v5.0 were not found. 


Invalid option '9.0' for /langversion. Use '/langversion:?' to list supported values.

Then here’s your quick and easy troubleshooting list :

  • Are you sure you’ve got the latest .NET 5 SDK installed? Remember it’s the SDK, not the runtime, and it’s .NET 5.
  • Are you sure you’re Visual Studio is up to date? Even if you have the SDK installed, Visual Studio plays by it’s own rules and needs to be updated.
  • Are you using Visual Studio 2019? Any version lower will not work.
  • Are you sure you enabled Preview SDKs? Remember Tools => Options, then “Preview Feature”

Keeping Up To Date

A new preview version of .NET 5/C#9 comes out every few months. So if you’re reading about a new C# 9 or .NET 5 feature that someone is using but you can’t seem to get it to work, then always head back to and download the latest preview version and install. Similar for Visual Studio, while typically less of an issue, try and keep it up to date as so often latest features not working are simply that I’m on a version that was perfectly fine a month ago, but is now out of date.

C# 9 Features

We are slowly working our way through all new C# 9 features, if you are interested in other new additions to the language, check out some of the posts below.

All of these will be covered in detail coming soon!

Join over 3,000 subscribers who are receiving our weekly post digest, a roundup of this weeks blog posts.
We hate spam. Your email address will not be sold or shared with anyone else.

NOTE : This post was initially written in early 2017 (!!!) but has now been completely revamped in 2020 to be up to date with .NET Core 3+ Watch feature. The original article was written in pre 1.0 days and is no longer relevant. But this is still a killer feature and should be used far more often!

One of the most overlooked features of .NET Core CLI is the “dotnet watch” command. With it, it allows you to have a “live reload” of your ASP.NET Core site running without having to either run the “dotnet run” command, or worse do the “Stop the process in Visual Studio. Write your changes. Recompile and run again” routine. The latter can be very annoying when all you are trying to do is do a simple one line fix.

If you are used to watches in other languages/tooling (Especially task runners like Gulp), then you will know how much of a boost watches are to productivity.

The Basics

I highly recommend creating a simple ASP.NET Core project to run through this tutorial with. The tooling can be a little finicky with large projects so it’s easier to get up and running with something small, then take what you’ve learned onto something a little larger.

For this demo, I have a simple controller that has a get method that returns a string value.

public class HomeController : Controller
	public string Get()
		return "Old Value";

Open a command prompt in your project directory, a terminal in VS Code, or even the Package Manager Console in Visual Studio and run the following command :

dotnet watch run

Note that if you previously have had to run the “dotnet run” command with other flags (e.g. “dotnet run -f net451” to specify the framework), this will still work using the watch command. Essentially it’s saying “Do a watch, and when something changes, do ‘this’ thing” which in our case is the run command.  You should see something similar to the following :

dotnet watch run
watch : Started
info: Microsoft.Hosting.Lifetime[0]
      Now listening on: https://localhost:5001
info: Microsoft.Hosting.Lifetime[0]
      Now listening on: http://localhost:5000
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development

This means you are up and running. If I go to http://localhost:5000/api/home, I should see my controller return “Old Value”.

Now I head back to my controller and I change the Get action to instead return “New Value”. As soon as I hit save on this file I see the following in my console window :

watch : Exited
watch : File changed: C:\Projects\WatchExample\Controllers\HomeController.cs
watch : Started
info: Microsoft.Hosting.Lifetime[0]
      Now listening on: https://localhost:5001
info: Microsoft.Hosting.Lifetime[0]
      Now listening on: http://localhost:5000

What we see is that the watch immediately exits, and it tells us that a file has been changed in HomeController.cs and so it starts recompiling immediately. When we browse to http://localhost:5000/api/home we see our “New Value” shown and we didn’t have to do anything else in terms of manually recompiling or running a new dotnet run command.

On larger projects, the recompile process can actually be a little slow so it’s not always an instant “change code and immediately refresh the browser” moment. Especially if you have a large dependency tree that means several projects need to be rebuilt before the site is back up and running. But it’s still going to be faster than whatever manual process you are used to.

Debugging With dotnet watch

So previously, debugging while using dotnet watch was almost a fruitless exercise. Each time your watch started and ran, a new “dotnet.exe” process would spin up. But the issue was it was impossible to find the “right” process to attach your debugger to. You might have a half dozen or so dotnet.exe processes running and you sort of had to wing it and pick the one that had been created most recent and hope for the best. Then each time you made a change, a *new* dotnet.exe would be spun up and your attached debugger was useless with you having to start the attach to debugger process all over again. Ugh!

As of .NET Core 3+, this is now much much easier.

Suppose I have my project up and running on a watch. In Visual Studio I simply go  Debug -> Attach To Debugger. I then filter all processes by the actual name of my project. In my case I called my project “WatchExample”, so I just start typing that into the filter box.

I click the Attach button and I’m away! Unfortunately each time you make a change the debugger is stopped while your project recompiles, but a helpful hint is to learn the “Reattach To Process” hotkey which in default Visual Studio key bindings is “Shift + Alt + P”. This immediately attaches the debugger to the last process it was on (Which in our case, is the our project exe), and we are away debugging again!

Join over 3,000 subscribers who are receiving our weekly post digest, a roundup of this weeks blog posts.
We hate spam. Your email address will not be sold or shared with anyone else.