Managing a .NET Core Project from the Command Line
From the day Visual Studio Code was released, I’ve been a huge fan. At that time, I mostly worked with Python projects, which played well with the concept of a lightweight editor. Running tests or code all happened from the command line, which I became used to over the next several years as my projects adopted more other command-line based tools like Terraform.
As .NET Core started to take off, I felt the tug to get back up to speed on all things C#. I had grown attached to VS Code over the years, so I challenged myself to continue using it rather than switching back to Visual Studio. However, all of my previous experience with creating new .NET projects involved the “File -> New Project” menu in Visual Studio, so I decided to compromise. I used Visual Studio to create solutions, projects, and templated classes like API controllers and used VS Code for coding. However, this felt very wrong.
When I started several new personal projects earlier this year, I decided
to take the time to better understand the .NET command line experience and
find alternative ways to handle tasks that I depended on Visual Studio for.
During that time, I found alternatives to most of the functionality that I
depended on Visual Studio to provide. I realized that the dotnet
command was
more useful for more than just dotnet new
. I also that where the capabilities
of the dotnet
command ended, an ecosystem of command-line applications (an unofficial
list of tools can be found here) created
to support developing .NET Core applications. I wanted to share some of the
solutions I found that made working solely from the command line possible.
Solution Management
When using dotnet new
to create a new project, one of the first things I
noticed missing from the project was a solution file. Given that new
usually
creates a single project, a solution file isn’t always necessary. However,
if you end up creating several projects or are working with someone using Visual
Studio, having a solution file becomes more useful. I was pleased to learn that
dotnet new
can generate solution files as well.
1dotnet new webapi -n CliDemo
2cd CliDemo
3dotnet new sln
Perfect! This creates an empty solution file, so there is a bit more work to be
done. While you could manually edit the solution to add a reference to your
projects, the dotnet sln
command is a more reliable way of adding, listing,
and deleting projects from the solution.
1❯ dotnet sln add .\CliDemo.csproj
2Project `CliDemo.csproj` added to the solution.
Now we can run dotnet sln list
to verify the project was added successfully.
1❯ dotnet sln list
2Project(s)
3----------
4CliDemo.csproj
Source Control
While adding source control to a project is a quick git init
away, there’s
still one thing missing: a .gitignore file. While you could track
down the official GitHub repo that has .gitignore files for most language types
or just copy one from an old project, the .NET team has thankfully added a
dotnet new gitignore
command to generate the file for you. It’s one of those
nice additions that save time and doesn’t leave you wondering if you found the
“right” .gitignore file.
Package Management
Replicating Visual Studio’s NuGet package manager experience turned out to be less challenging than I thought. The NuGet website provides an easy way to search for packages that has the additional benefit of providing the command that needs to be run to add a reference to the package. The nagging issue I had was how to find out out if versions of my packages were out of date. A bit of research lead me to the NuKeeper global tool which provided exactly what I needed.
1❯ nukeeper inspect
2Found 5 packages
3Found 5 packages in use, 5 distinct, in 1 projects.
4Json.Net, Microsoft.EntityFrameworkCore.Design, Microsoft.EntityFrameworkCore.SqlServer, Microsoft.VisualStudio.Web.CodeGeneration.Design, Swashbuckle.AspNetCore
5Found 2 possible updates
6Microsoft.EntityFrameworkCore.SqlServer from 3.1.4 to 3.1.8 in CliDemo.csproj
7Swashbuckle.AspNetCore from 5.5.1 to 5.6.3 in CliDemo.csproj
8
9Found 2 package updates
10Microsoft.EntityFrameworkCore.SqlServer to 3.1.8 from 3.1.4 in 1 place since 1 month ago.
11Swashbuckle.AspNetCore to 5.6.3 from 5.5.1 in 1 place since 19 days ago.
Running nukeeper inspect
provides a report on which packages are behind their
latest versions without performing any updates. If everything looks in order,
executing nukeeper update
updates the version numbers in the csproj file and
downloads the new package versions.
Database Migrations
If you’ve worked with Entity Framework before, you’re likely used to switching
to the package manager console in Visual Studio to add or run migrations.
The dotnet ef
global tool provides the same functionality with similar command
syntax.
1dotnet ef migrations add InitialDb
2dotnet ef database update
Controller Scaffolding
I’ll be honest: I can’t write a .NET Core Web API Controller from scratch.
My muscle memory expects right-clicking the Controllers directory to pop up the
handy Add -> Controller
link that Visual Studio provides.

By coincidence, I happened upon a workshop
that introduced me to the dotnet-aspnet-codegenerator
global tool which
provides the same capabilitiy. This tool has dependencies on other NuGet
packages, so the package references should be added to the project you are
working on.
While searching for a solution, the host of a live coding stream mentioned a
workshop that introduced me to the dotnet-aspnet-codegenerator
global tool, which provides the same capabilities for generating controllers. This tool has dependencies on other NuGet packages, so the package references should be added to the project you are working on.
1dotnet add package Microsoft.EntityFrameworkCore.Design
2dotnet add package Microsoft.EntityFrameworkCore.SqlServer
3dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design
4dotnet tool install -g dotnet-aspnet-codegenerator
This tool assumes that your web project follows the convention of storing data models in a Models
directory. You also need to provide the new controller’s name, the path to your Controllers directory, and the namespace of your DbContext. Don’t worry if you don’t haven’t created a DbContext at this point. If the generator determines that the DbContext doesn’t exist, the tool will create one for you.
1❯ dotnet aspnet-codegenerator controller -api -name ToolsController -m Tool -dc Data.AppDbContext -outDir Controllers
2Building project ...
3Finding the generator 'controller'...
4Running the generator 'controller'...
5Generating a new DbContext class 'Data.AppDbContext'
6Attempting to compile the application in memory with the added DbContext.
7Attempting to figure out the EntityFramework metadata for the model and DbContext: 'Tool'
8Added DbContext : '\Data\AppDbContext.cs'
9Added Controller : '\Controllers\ToolsController.cs'.
10RunTime 00:00:07.58
There’s a lot to unpack here. The tool creates a new DbContext, wires up the new DbContext in Startup.cs, and finally creates the new API controller with the standard create/read/update/delete routes. This approach may not always meet your needs, it is a great way to get quick prototypes up and running.
Wrap Up
.NET has come a long way from being tightly coupled with Visual Studio. Having the command line as an alternative workflow allows those developers to have an experience equal to those working in Visual Studio. To keep this post concise, I only mentioned a few of the global tools I’ve integrated into my workflow. Still, there is a growing ecosystem of tools to handle everything from code formatting, measuring code coverage, or finding just the right gif. Go give them a try!
