Creating a new dotnet webapp

A few things I like to do when starting a new web project using .Net 5


I've been working with .Net Core since the 1.0 release.  A few habits developed in my project structure over time.  It might be possible to build a template to auto-create folder structure with some of these styles.  But I have not delved into doing that just yet.  For now, in this post, I wanted to jot a few of these down.  Maybe someone will find them useful. First up, get the latest .Net 5 SDK and a fresh copy of Visual Studio Code.  Yes, VSCode.  Because for the basic setup, before things get too complicated, you don't need a full Visual Studio IDE. Open up a terminal window in your empty folder for the solution and type: dotnet new.  All the available templates appear.  Note that I'll be using my VSCode terminal set to bash as the default shell. But wait, first things first.  Setup a folder structure.  Type the following to create a src folder.

$ mkdir src

Then, create a global.json file to store your preferred .Net version.  While it sounds like the recommendation going forward will be not to do this, I like doing it anyway so I know what all my dependencies are expecting.

$ dotnet new globaljson

I then like to add a few other support folders so some other aspects of my developer workflow.  Folders to add are builds, docs, local-packages, scripts, tests.  I like a Readme.md file in the root.  It's nice to have a nuget.config in the root as well in case you want to build custom packages not hosted on NuGet (hence the local-packages folder).  Depending on the web UI framework you use, other things like a tsconfig.json, a packages.json can also be added.  

$ mkdir docs
$ mkdir builds
$ mkdir local-packages
$ mkdir scripts
$ mkdir tests

It's always good to add a .gitignore file in the root too so Git won't include your node_modules folder with all the nested dependencies that has.  I tend to like the open source file found at this repo: VisualStudio.gitignore.

$ touch .gitignore

After creating this file, copy/paste in the raw text from the link above.  It's a start.  You can add other folders or files to ignore later.

At this point, I like to add a solution file.  If I like my root folder name then this is easy.

$ dotnet new sln

So at this point, there is enough basic structure to add a new webapp.  Change directory to the src folder and create another sub-folder with the site or project name.  I like to add the kind of platform the project is at the end with something like ".web", or ".desktop".  For now, I'll just add a web project.

$ cd src
$ mkdir structure.web
$ cd structure.web

It is always a good idea to take a look at the project template help before actually create it from a template.  There are some flags you may want to set to configure some things out of the box.

$ dotnet new webapp --help

I like most of the defaults for a simple web application.  If you want to use the authentication templates that is available, for example.  There is a -f | --framework flag that can be useful when you are working with preview versions of .Net.  There are some other nice template helpers in there too.  It's worth a look to see what each one does and decide if it is right for your project.

Since this is a simple example, I'll use the defaults.

$ dotnet new webapp

At this point, there is a solution file in the root that is completely unaware of the project two folders below.  Before adding it, I like to update the .csproj file with some build settings.  Also, a nice keyboard shortcut in VSCode is CTRL+K+F.  So, I like to open up the .csproj file and fix up the formatting.  Out of the box this file looks like this: 

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

    <PropertyGroup>

        <TargetFramework>net5.0</TargetFramework>

    </PropertyGroup>

</Project>

If building and targeting a Windows server for deployment, use the RuntimeIdentifiers setting of win-x64 (more on that here: RuntimeIdentifiers).  Next I'll put in Debug, Release Configurations.  With those two settings it looks like this:

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

    <PropertyGroup>

        <TargetFramework>net5.0</TargetFramework>

        <RuntimeIdentifiers>win-x64</RuntimeIdentifiers>

        <Configurations>Debug;Release</Configurations>

    </PropertyGroup>

</Project>

I also like to add a PropertyGroup that has a Version, PackageVersion, AssemblyVersion, and Copyright.  Those settings can be changed in an automated way later or manually updated.

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

    <PropertyGroup>

        <TargetFramework>net5.0</TargetFramework>

        <RuntimeIdentifiers>win-x64</RuntimeIdentifiers>

        <Configurations>Debug;Release</Configurations>

    </PropertyGroup>

    <PropertyGroup>

        <Version>1.0.0</Version>

        <PackageVersion>1.0.0</PackageVersion>

        <AssemblyVersion>1.0.0</AssemblyVersion>

        <Copyright>Copyright © 2011-2021 Allen Newton. All rights reserved.</Copyright>

    </PropertyGroup>

</Project>

Now it's time to add the project to the solution.  Change directory to the root folder and add the reference.

$ dotnet sln add ./src/structure.web/structure.web.csproj

At this point, VSCode sees that there are some build config files that would be good to add.  A prompt will ask if you want to add missing required assets.  Click yes.  That will drop in a .vscode folder with a launch.json and a tasks.json file.  When VSCode doesn't prompt you, it is easy enough to add those either through the Run and Debug window or through the Run/Add Configuration menu.

Click Debug and Run to start the web app.  If the launch.json has the following setting, the Omnisharp C# extension of VSCode will detect with the standard console output has a match to the regular expression pattern.  When matched, your default browser will open to the correct web site Url.  (Well, it should open your browser, anyway.)

// Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser

"serverReadyAction": {

    "action": "openExternally",

    "pattern": "\\\\bNow listening on:\\\\s+(https?://\\\\S+)"

},

The next thing I tend to do is split up the Startup.cs file.  This is an approach I have not seen many people do.  It came about after adding a lot of code in my Startup.cs file.  This approach makes it easier, at least for me, to navigate.  If you take a look at the Startup.cs code, you'll notice that it is not a partial class.  It isn't difficult to change that.  So, break that file up into the following files, Startup.cs, Startup.Pipeline.cs, and Startup.Services.cs.  Later on when additional startup configuration code is added, it is easy to create another separate partial class code file.  From the web project folder, add the files.

$ touch Startup.Pipelines.cs
$ touch Startup.Services.cs

Edit the new files to have the correct namespace and then change each one to a partial Startup class like so:

public partial class Startup {}

When completed the original Startup.cs file should look like the following.  The default webapp project template adds a lot of unnecessary using statements.  It's also good to remove those and keep the using statements clean as you go.

using Microsoft.Extensions.Configuration;

 

namespace structure.web

{

    public partial class Startup

    {

        public Startup(IConfiguration configuration)

        {

            Configuration = configuration;

        }

        public IConfiguration Configuration { get; }

    }

}

Finally, it's a useful idea to create a custom AppSettings class that can give easier access to appsettings.json config values.  There is a built in way to reference those using the IConfiguration extension.  But it has always felt like it should be more robust instead of relying on magic strings to lookup values.  Like this example:

var defaultLogLevel = Configuration["Logging:LogLevel:Default"];

That is a whole other topic for a different post.