The dotnet CLI workflow

Solution and project scaffolding, package management, and the build, test, watch and format loop you actually live in.

Scaffolding a solution

A solution (.sln) is a grouping file for related projects — it is not required to build anything, but it gives the CLI and every editor one entry point. The usual shape is one solution containing an app project, a test project and one or more libraries.

dotnet new sln -n Shop

dotnet new classlib -n Shop.Core  -o src/Shop.Core
dotnet new webapi   -n Shop.Api   -o src/Shop.Api -f net8.0
dotnet new xunit    -n Shop.Tests -o tests/Shop.Tests

dotnet sln add src/Shop.Core src/Shop.Api tests/Shop.Tests
dotnet add src/Shop.Api reference src/Shop.Core
dotnet add tests/Shop.Tests reference src/Shop.Core
  • -n names the output, -o sets the folder, -f pins the target framework.
  • dotnet new list shows every installed template; dotnet new install adds community ones.
  • Keep tests in a sibling tests/ folder and reference projects with relative paths so the layout survives a move.

Packages, restore and lock files

Package commands edit the project file for you, which is why the CLI is worth learning even if you normally work in an IDE.

dotnet add src/Shop.Api package Microsoft.EntityFrameworkCore.Sqlite
dotnet add src/Shop.Api package Microsoft.EntityFrameworkCore.Sqlite --version 8.0.11
dotnet remove src/Shop.Api package Newtonsoft.Json

dotnet list package                    # direct dependencies
dotnet list package --outdated         # newer versions available
dotnet list package --vulnerable       # known CVEs in the graph
dotnet restore                         # download everything up front
ArtifactPurpose
obj/project.assets.jsonThe resolved dependency graph MSBuild reads during a build
packages.lock.jsonOptional lock file making restores reproducible; enable with RestorePackagesWithLockFile
~/nuget/packagesThe global package cache shared by every project on the machine
NuGet.configWhich feeds a repository may restore from

Commit the lock file for applications so CI restores the exact graph that passed review. For a library the lock file adds friction for consumers and is usually left out.

The inner loop

dotnet build                       # Debug
dotnet build -c Release            # optimised, warnings still checked
dotnet build --no-restore          # skip restore when nothing changed
dotnet run --project src/Shop.Api
dotnet run --project src/Shop.Api -- --port 5001   # args after -- reach the app

dotnet test                        # discover and run every test project
dotnet test --filter "FullyQualifiedName~Cart"     # narrow to one class

dotnet watch run --project src/Shop.Api            # hot reload on file save
dotnet format                      # apply the repo .editorconfig rules
  • dotnet watch rebuilds and restarts on save; edits to method bodies reload without restarting when the runtime supports it.
  • dotnet format --verify-no-changes is the CI-safe form: it exits non-zero instead of rewriting files.
  • --no-restore and --no-build matter in pipelines where a previous step already did the work.
  • Build output lands in bin/<config>/<tfm>/; never commit bin or obj.
💡
Run dotnet build -c Release locally before opening a pull request. Some analyser warnings are only reported in Release, and the CI configuration you forgot to test is exactly the configuration that breaks.

FAQ

What do I commit to source control?
Source, project files, global.json, Directory.Build.props, .editorconfig and the app lock file. Add a .gitignore that excludes bin/, obj/, .vs/ and *.user.
Build or publish?
build produces assemblies for development and testing. publish produces a deployable layout: dependencies resolved, runtime configuration applied, ready to copy to a host.

.NET SDK and project files Configuration and logging

Last refreshed 2026-09-18.