ASP.NET Core Development in Linux

When .Net Core came out there was a lot of excitement about the idea of developing .Net code outside of Windows.

The IDE of choice for .Net, Visual Studio, is and probably will always be, a Windows-only program.

There are alternatives though, and today it is possible to fully develop an ASP.NET Core application outside of Windows.

In this blog post we will be looking into that, specifically in Linux.

Please note that all of the tooling described here is cross-platform all of this will work on any platform that supports .Net Core.

The IDE

The full version of Visual Studio is only available in Windows. However, there's an alternative that has a somewhat confusing name: Visual Studio Code.

You might think that VS Code is an inferior version of the full Visual Studio product, but that is not the case.

VS Code is a different project altogether. It's built using a different set of technologies and this brings some features that full VS doesn't have, for example being easily extensible and customizable, and also less demanding in terms of resources.

VS Code offers the ability to extend its functionality by the installation of extensions. For C# you should install the C# extension. You don't need to go through the trouble of looking for it though. When you open a project that has C#, Visual Studio Code will prompt you to install the C# extension if you don't have it already.

In the rest of the blog post we will create a hypothetical ASP.NET Core web application in order to illustrate most of the required tasks when developing in .Net. Namely how to create projects and solutions, reference projects, install nuget packages, use dotnet tools (like entity framework) and run tests.

A Typical Project

The imaginary project we'll be creating is a reminder application. The idea here is to describe a project with sufficient complexity that would require multiple projects that reference each other, a solution file and tests.

The solution will contain an ASP.NET Core project that could, for example, be used to see past and future reminders. A Console application so that we can create reminders from the command line (and to illustrate how we can have a solution with more than one running project) and a class library that will contain all the common logic. We'll also add a test project to illustrate how unit tests can be run from the command line and inside VS Code.

One of the options of the dotnet command is "new". To check that and other options' help page just do dotnet [option] --help, in this case dotnet new --help.

That will display a list of possible project templates to choose from. For example to create an ASP.NET Core MVC project:

$ dotnet new mvc

If you run that as it is, a new MVC project will be created in the current folder, using the name of the current folder as the default namespace and project name.

You might not want that. In case you don't, you can use the --name and --output options. Using --name (or -n) you can specify the default namespace and with --output (or -o) you can specify the folder name to create and where to place the project files. If you use --name and don't specify --output, the output folder will implicitly take the same value as --name.

It is probably a good idea to define the project folder structure before we continue. Here it is:

This will create 4 projects, one MVC, one console, a class library and a xUnit test project.

It's a good idea to create a solution file as well. Especially if eventually you want to open these projects in full Visual Studio, which will prompt you to create one until you eventually do it.

There are advantages in having a solution file other than avoiding full Visual Studio nagging you about creating the .sln file. If you have one you can just go to the Reminders folder and do a dotnet build or a dotnet restore and that build/restore all projects referenced in the .sln file.

Also, if you have a .sln file you can open all projects in VS Code by initiating VS Code in the folder where the .sln file is located.

Let's create our .sln file, name it Reminders.sln and add the four projects to it (inside the Reminders folder):

Adding project references

In full Visual Studio when you want to add a reference to a project you can just right click on references and select Add Reference, pick the project you want to add and you're done.

VS Code does not have that functionality. To do this we need to resort to the command line, but it's just as easy. For example, let's reference Reminders.Common in Reminders.Web, Reminders.Cli and Reminders.Tests.

If you want to have a look at which other projects are referenced by a particular project you can either open the .csproj file and have a look at the ProjectReference entries or if you want to do it from the command line: dotnet list PathToCsProj reference.

You can also navigate to the folder where a particular project is and simply do dotnet add reference pathToOtherProject.csprj.

Picking the startup project

Open the solution in VS Code. The easiest way to do that is to navigate to the Reminders folder (where the .sln file is) and type code ..

When you do that you should get a message in VS Code: "Required assets to build and debug are missing from 'Reminders'. Add them?"

Click Yes.

That will create a .vscode folder.

In that folder there are two files: launch.json and tasks.json. launch.json configures what happens when you press F5 inside VS Code.

For me the project that will run is the Reminders.Cli console project:

It is possible to customize these two files so that you can pick which project to run. In this case we want to be able to run either Reminders.Web or Reminders.Cli.

The first thing you should do is to remove the second item in tasks.json's args array: "${workspaceFolder}/Reminders.Cli/Reminders.Cli.csproj". We don't need it because we have the solution file (Reminders.sln). When dotnet build is executed in the folder that contains the sln file all the projects referenced in the solution get built.

After that we can now go to launch.json and click the "Add Configuration..." button in the bottom left corner.

Select ".Net: Launch a local .NET Core Web Application" and in the generated json change "program" and "cwd" (current working directory) to:

You can now go to the Debug "section" of Visual Studio Code and see the Web and Console app configuration there:

Note that you can change the configuration names if you like, just change the "name" property in launch.json.

Now when you press F5 inside VS Code the project that will run is the one that is selected in the Debug section of VS Code.

This only affects VS Code though, the dotnet run command is unaffected by these changes. In the terminal, if you navigate to the solution's folder and type dontet run you'll get an error (Couldn't find a project to run...). You need to use the --project argument and specify the path to the csproj file you want to run, e.g.: dotnet run --project Reminders.Web/Reminders.Web.csproj. Alternatively you can navigate to the project's folder and execute dontet run there.

Adding Classes and Interfaces

In full Visual Studio you have several templates available when adding new files to a project.

In VS Code the only out of the box option is to create a new file and use the VS Code snippets (e.g. typing class and then TAB).

This extension also adds the ability to generate a class' constructor from properties, generate properties from the constructor and add read-only properties and initialize them (there are demos of this in the extension's page).

This section wouldn't be complete without mentioning a currently abandoned alternative named yeoman, specifically the generator-aspnet.

Yeoman is a nodejs application that allows you to install yeoman generators which generate code. For ASP.NET Core there was a particularly interesting generator named generator-aspnet.

That generator provided a set of project templates similar to what dotnet new now offers (mvc application, console app, etc). But that was not the most interesting thing about generator-aspnet. It was its set of subgenerators.

A subgenerator in yeoman is a small generator for creating just one file, for example a class, an interface or an empty html page. Here's an example of using generator-asp to create a new class: yo aspnet:class ClassName

Unfortunately they were removed in version 0.3.0. This list is from generator-asp@0.2.6 (latest version as of today, 25/2/2018 is 0.3.3).

From the talk on the generator-aspnet github issues, although never mentioned explicitly, it seems that dotnet new is the only way to go from now on. Unfortunately the only generation commands that are similar to these subgenerators in dotnet new are Nuget Config, Web Config, Razor Page, MVC ViewImports and MVC Viewstart (you get a list of them when you type dotnet new --help).

You can still install this specific version of generator-aspnet though (npm install generator-aspnet@0.2.6) and use it (some of the generators are still useful).

However, for creating classes the extension I mentioned previously is the easiest and most convenient to use. Let's use it to create a Reminder class inside the Reminder.Common project.

The Reminder class has an Id property, a Description, a Date and a boolean flag IsFinished to indicate the reminder was acknowledged.

First thing you need to do is install the "C# Extensions extension". Then right click on Reminders.Common in the explorer view and select New Class:

Name it Reminder.cs and create the three properties. In the end it should look like this:

Although the constructor isn't really necessary (and it won't play well with Entity Framework if you are planning to use it) , it's just an example of some nice features you get from the extension. Also, when defining properties you can use the same snippets that are available in full Visual Studio (i.e. type prop and press TAB).

Razor views

Razor pages (.cshtml) is definitely an area where VS Code is lacking. There's no intellisense or even auto-indent.

There's an extension in the marketplace named ASP.NET Helper which will get you intellisense, however you have to import the namespaces for your models in _ViewImports.chtml (the extension's requirements are at the bottom of the extensions page). Also in its current version it only seems to work if the model you use is in the same project as the view.

This extension can be useful in some situations but it's very limited.

If this is a deal breaker for you here's the github issue for adding Razor support to the language service that VS Code relies on and which would provide intellisense similar to what is there for the full version of Visual Studio. If you upvote or leave a comment there it will bring visibility to this issue.

If you want a good experience while creating razor pages today and you don't mind paying a little bit of money you can try JetBrains Rider. Rider runs on Windows, Mac and Linux and is from the same people that created ReSharper. I tried it a good few months ago when it was in beta. At that time it required a fairly decent machine to run smoothly and it was lacking some features. I tried it again today while I'm writing this and it seems much much better. Razor support seems perfect.

Even though the experience with Razor in VS Code right now is not ideal, if you go with a front-end JavaScript framework (Angular, React, Vue, etc) instead of Razor, you'll find that VS Code is excellent. I've used mostly Angular and TypeScript and this is an area where VS Code is arguably better then the full version of Visual Studio.

If you are somewhat familiar with Angular and are planning to use ASP.NET Core as a Web Api for your front end check out my other article Angular and ASP.NET Core.

Nuget packages

In the full version of Visual Studio there's an option to Manage NuGet packages. You get an UI where you can search, update and install NuGet packages. In VS Code there's no support for NuGet out of the box.

Thankfully there's an extension you can install that will enable searching, adding and removing NuGet packages from VS Code. The extension is named NuGet Package Manager.

Alternatively you can use the command line. To add a package to a project the syntax is: dotnet add PathToCsproj package PackageName. Or, if you navigate to the project you want to add the NuGet package to, you can omit the path to the csproj file: dotnet add package PackageName.

Imagine you wanted to add the Microsoft.EntityFrameworkCore package to Reminders.Common class library. After navigating to it:

$ dotnet add package Microsoft.EntityFrameworkCore

Adding tools

An example of a CLI tool is for example Entity Framework's tool. That's what runs when you type in a project that has the EF tooling installed:

For the tooling you should create another ItemGroup and inside it instead of PackageReference use DotNetCliToolReference (there may already be an ItemGroup with DotNetCliToolReference in your project, if that's the case just add to it).

Let's imagine we want to use the Entity Framework tooling in our web project. Navigate to Reminders.Web and open the csproj file.

Make note of the version of Microsoft.AspNetCore.All package. For example, let's imagine it's "2.0.0".

Create a new ItemGroup (or find the ItemGroup that already has DotNetCliToolReferences) and inside add the DotNetCliToolReference for the Microsoft.EntityFrameworkCore.Tools.DotNet with Version="2.0.0".

The reason the version must match "Microsoft.AsNetCore.All" is because that package is a metapackage, i.e. a package that just references other packages. Some of those packages are Entity Framework packages which would cause compatibility issues with the tooling if the version does not match.

If you prefer to do everything inside VS Code you can create tasks for running tests. First go to tasks.json and make a copy of the build task, change its label to test (or whatever you want to call it) and update the args so that instead of build, test is executed with the path to the test project, for example for Reminders.Tests:

You can run tasks in VS Code by using the command palette (Ctrl + Shift + P) and typing "Run Task" and then selecting the task you want to run. You can even set a task as the "test task" and assign a keyboard shortcut for it (the same way you can do Ctrl + Shift + B to trigger a task configured as the "build task").

To set a task as the "test task" open the command palette and select "Tasks: Configure Default Test Task", choose the test task and you're done.

One last tip

Sometimes VS Code (actually Omnisharp) seems to go haywire, for example intellisense stops working. When that happens you can either reload VS Code by using the command palette and selecting "Reload Window" or the less aggressive option is to restart Omnisharp (the command name is: Omnisharp: Restart Omnisharp).

Great article! I've just started my journey with Linux and dotnet core.I was planning to write the whole series of articles about that. You have recently abandoned my with your post about MVC 😁 I need to think up something new.