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.

I’ve recently had the opportunity to start a Specflow/Selenium end to end testing project from scratch and my gosh it’s been fun. I’m one of those people that absolutely love unit tests and trying to “trick” the code with complicated scenarios. End to end testing with Selenium is like that but on steroids. Seeing the browser flash infront of you and motor through tests is an amazing feeling.

But in saying that. A key part of using Selenium is the “ChromeWebDriver”. It’s the tool that actually allows you to manipulate the Google Chrome browser through selenium. And let me tell you, there are a few headaches getting this set up that I really didn’t expect. Version errors, Not finding the right exe, Nuget packages that actually include the exe but it’s the wrong version or can’t be found. Ugh.

If you are not that big into automation testing, you can probably skip this whole post. But if you use Specflow/Selenium even semi-regularly, I highly recommend bookmarking this post because I’m 99% sure you will hit atleast one of these bugs when setting up a new testing project.

Chrome, Gecko  and IE Drivers

While the below is mostly about using ChromeDriver, some of this is also applicable for Gecko (Firefox), and IE drivers. Obviously the error messages will be slightly different, but it’s also highly likely you will run into very similar issues.

Adding ChromeDriver.exe To Your Project

The first thing to note is that you’ve probably added the “Selenium.WebDriver” and maybe “Specflow” nuget packages. These however *do not* contain the actual ChromeDriver executable. They only contain the C# code required to interact with the driver, but *not* the driver itself. It is incredibly confusing at first but kinda makes sense because you may want to only use Chrome or only Firefox or a combination etc. So it’s left up to you to actually add the required driver EXEs.

If you try and run your selenium tests without it, it will actually compile all fine and look like starting only to bomb out with :

The chromedriver.exe file does not exist in the current directory or in a directory on the PATH environment variable.

Depending on your setup, it can also bomb out with :

The file C:\path\to\my\project\chromedriver.exe does not exist. 
The driver can be downloaded at

So there are two ways to add ChromeDriver to your project. The first is that you can install a nuget package that will write it to your bin folder when building. The most common nuget package that does this is here :

But a quick note, as we will see below, this only works if everywhere you run the tests has the correct version of chrome that matches the driver. What?! You didn’t know that? That’s right. The version of ChromeDriver.exe will have a version like that will typically only be able to run on machines that have chrome version 79 installed. The nuget package itself is typically marked with the version of Chrome you need, so it’s easy to figure out, but can still be a big pain in the butt to get going.

So with that in mind, the other option is to actually download the driver yourself from the chromium downloads page :

You need to then drop the exe into your project. And make sure it’s set to copy if newer for your build. Then when building, it should show up in your bin folder. Personally, I found the manual download of the chromium driver to be handy when working in an enterprise environment where the version of chrome might be locked down by some group policy, or you are working with others who may have wildly different versions of chrome and you can do funky things like have different versions for different developers.

Passing The ChromeDriver Location

So you’ve downloaded ChromeDriver and when you build, you can see it in your bin folder, but everything is still blowing up with the same error, what gives?!

One of the more irritating things I found is that in so many tutorials, they new’d up a chromedriver instance like so :

ChromeDriver = new ChromeDriver();

Now this may have worked in .NET Framework (I haven’t tried), but atleast for me in .NET Core, this never works. I think there must be something inside the constructor of ChromeDriver that looks up where it’s current executable is running (e.g. where the Bin folder is), and in .NET Core this must be different from Full Framework.

In anycase, you can change the constructor to instead take the folder location where it can find the driver. In my case I want that to be the bin folder :

ChromeDriver = new ChromeDriver(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location));

Obviously you can go ahead and change the path to anything which is super handy for differing dev setups. For example you could ask each dev to maintain their own version of chromedriver.exe somewhere on their C:\ drive, and then just pass that location into the constructor. Meaning that each developer can have a completely different version of chrome, and things will still run perfectly fine.

Versions Matter

We kinda touched on it above, but versions of ChromeDriver have to match the actual version of Chrome on the machine. If you are getting errors like so :

session not created: This version of ChromeDriver only supports Chrome version XX

Then you have a mismatch between versions.

The easiest way to rectify the issue is to manually download the correct version of ChromeDriver from here : and force your code to use it. If you are using a nuget package for the driver, then it’s highly likely you would need to switch away from it to a manual setup to give you better control over versioning.

Azure Devops (And Others) Have ChromeDriver Environment Variables

This is one that I really wish I knew about sooner. When I tried to run my Selenium tests on Azure Devops, I was getting version issues where the version of Chrome on my hosted build agent was just slightly different from the one on my machine. I tried to do all sorts of crazy things by swapping our the exe version etc, but then I found buried in a help doc that there is actually an environment variable named ChromeWebDriver that has the full path to a chromedriver that is guaranteed to match that of the chrome browser on the agent. So I wrote some quick code that if I was running inside Azure Devops, to grab that environment variable and pass that into my ChromeDriver constructor.

Again, this is only for Azure Devops. But if you are using Gitlab, Bamboo, TeamCity, whatever! Check to see if there is an environment variable on hosted agents that carries the location of ChromeDriver.

If you are using your own build agents, then it’s also a good idea to think about following the same pattern. It’s super handy to have the Build Agent look after it’s own versions rather than wrangling something in code to fudge it all.

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 a previous post we looked at how we can publish a single exe from a .NET Core project. Now in it, I talk about how the resulting binary is absolutely massive, 70MB for a simple hello world console app! But I also talked about how there are now ways to significantly chop that down.

Despite that, when shared with Reddit, there was some comments that weren’t so impressed. Most notably this gem :

But that’s cool. For Scellow and anyone else that wasn’t impressed with the size of the exe, let’s look at how we can chop it down.

You Need .NET Core 3.0 (Preview 6+)

Like all these features I’m writing about recently, you’re gonna need the very latest version of .NET Core 3.0. At the time of writing it’s Preview 6. Any version earlier will simply not fly. And again, if that isn’t you and you don’t want to muck around with preview versions, then you’ll just have to wait!

PublishedTrimmed Flag

Before we go on, you might hear IL Linker talked about a lot. Possibly because it’s in the title of this post! But that’s because IL Linker is essentially the backbone of this entire feature. However where previous it was this external tool you had to grab from a nuget feed, now it’s built directly into .NET Core 3.0+ which is pretty impressive!

Anyway we previously published our application using the following command :

dotnet publish -r win-x64 -c Release /p:PublishSingleFile=true

And with that, we ended up with an exe over 70MB big :

Not good. But literally all we have to do is add one flag and we can chop it down a bit :

dotnet publish -r win-x64 -c Release /p:PublishSingleFile=true /p:PublishTrimmed=true

So… 29MB. And this is about the time where you go “but shouldn’t this be KB in size?”. And honestly the answer is yes. It should be. But we have to remember that this is still a completely self contained application that doesn’t even require the target machine to know what .NET Core is. So even if we aren’t using things like say.. cryptography so we can get rid of that, there is probably a lot of basic and primitive .NET Core features that we just can’t get rid of.

Still. It’s an exe half the size with no functional difference between the two.

Reflected Assemblies

Through various forms of reflection, we may end up loading assemblies at runtime that aren’t direct references. Take this (very convoluted) example of loading an assembly at runtime :

static void Main(string[] args)

Now when debugging this locally, and we have .NET Core installed, we ask for System.Security and it knows what that is because we are using the installed .NET Core platform. So running it, we get :

System.Security, Version=, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a

But if we publish this using the PublishTrimmed flag from the command line, then run it :

Unhandled Exception: System.IO.FileNotFoundException: Could not load file or assembly 'System.Security, Culture=neutral, PublicKeyToken=null'. 
The system cannot find the file specified.

Unsurprisingly, we have trimmed a bit too much off. Because I don’t actually reference System.Security anywhere, it’s decided we aren’t actually using it. But there is a way to specify that we know best, and to package it up for us.

All we need to do is edit our csproj file for our project and add :

  <TrimmerRootAssembly Include="System.Security" />

Now we will bundle up System.Security with our single exe and everything will run smooth! Pretty nice I have to say!

You may ask yourself “Well how will I know when I need to do this?”. Frankly 99% of the time you won’t. If you decide to trim your exe, you will need to really give it a good test before letting it out into the wild.

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.

Say I have a simple “Hello World” console application that I want to send to a friend to run. The friend doesn’t have .NET Core installed so I know I need to build a self contained application for him. Easy, I just run the following command in my project directory :

dotnet publish -r win-x64 -c Release --self-contained

Pretty self explanatory :

  • Publish the project from the current directory
  • Build the project to run on Windows 64 bit machines
  • Build in release configuration mode
  • Publish everything as “self-contained” so that everything required to run the app is packaged up with our executable

So this works right, we end up with a folder that has our exe and everything that is required to run it, but the issue is that there is a tonne required to run even a HelloWorld console app.

All up it’s a little over 200 files. Crazy – but it makes sense. It’s essentially having to package the .NET Core runtime just to run Hello World.

So functionally, this works. But optics wise, it looks like a mess. I’ve sent folders like this to clients and had to say “uhh.. So I’m going to send you a folder with hundreds of files in it… But… Can you just find the one titled MyApplication.exe and run that and don’t worry about the rest?”. When people are used to having an icon on their desktop they double click and things just.. work… This just doesn’t cut it.

You Need .NET Core 3.0 (Preview 5+)

I need to put that in bold because this will not work if you don’t have .NET Core 3.0. And if you are an early adopter (At the time of this post .NET Core 3.0 is not GA yet), you will need atleast preview 5 upwards. If you’re not one to mess around in preview’s, then you can just wait a few months till .NET Core 3.0 is released!

The PublishSingleFile Flag

All that intro and it literally comes down to a single command flag :

dotnet publish -r win-x64 -c Release /p:PublishSingleFile=true

All this does is runs our publish command but tells it to package it within a single file. You’ll notice that we no longer specify the self-contained flag. That’s because it’s assumed that if you are packaging as a single exe, that you will want all it’s dependencies along with it. Makes sense.

And the output :

A single tidy exe! When this is executed, the dependencies are extracted to a temporary directory and then everything is ran from there. It’s essentially a zip of our previous publish folder! I’ve had a few plays around with it and honestly, it just works. There is nothing more to say about it. It just works.

Helpful Tip : Make sure you clean your publish directory or just outright delete your bin folder. It doesn’t break anything to not do so, but all those old DLL’s just hang around until you do so your nice single EXE is hard to spot. 

File Size And Startup Cost

Keen eyes will notice something about the above screenshot. The file size. It’s over 70MB! That’s crazy for an application that does nothing but print Hello World to the screen! This is solved in Preview 6 of .NET Core 3.0 with a feature called IL Linker or Publish trimmer that omits DLL’s that aren’t used. You can read more about that here!

The other issue you may find is that there is a slight startup cost when running the self contained executable for the first time. Because it needs to essentially unzip all dependencies to a temporary directory on first run, that’s going to take a little bit of time to complete. It’s not crazy (5 seconds or so), but it’s noticeable. Luckily on subsequent runs it uses this already unzipped temp folder and so startup is immediate.

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.