Setting up .NET: SDK, CLI and project layout
Install the SDK, create and run projects from the CLI, understand target frameworks, and know what each command in the loop does.
The SDK and the command loop
The .NET SDK contains the runtime, the compilers and the CLI. One installation can build and run for several target frameworks, so you rarely need more than one SDK version installed locally.
dotnet --version # the SDK in use
dotnet --list-sdks
dotnet --list-runtimes
dotnet --info # everything, including the RID and base path
dotnet new console -o Hello
cd Hello
dotnet run # restore, build and run in one step
dotnet build -c Release
dotnet publish -c Release -r linux-x64 --self-contained false -o out
dotnet test
dotnet format # apply the standard style and analysers| Command | What it does | When you need it |
|---|---|---|
dotnet new <template> | Scaffolds a project from a template | Starting anything new |
dotnet restore | Downloads NuGet packages listed in the project | CI, or after editing the project file |
dotnet build | Compiles without running | Checking compile errors in CI |
dotnet run | Builds and runs the startup project | Local development |
dotnet publish | Produces a deployable output directory | Shipping |
dotnet test | Discovers and runs test projects | Any test run |
dotnet watch | Rebuilds and restarts on file change | Iterating on a service or app |
global.json pins the SDK version for a repository, which is how you stop a machine with a newer SDK from producing a subtly different build. It belongs in version control next to the solution file.
Solutions, projects and target frameworks
<!-- App.csproj — the SDK-style project file -->
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<LangVersion>latest</LangVersion>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<InvariantGlobalization>true</InvariantGlobalization>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.0" />
<ProjectReference Include="../Core/Core.csproj" />
</ItemGroup>
</Project>dotnet new sln -n Shop
dotnet new classlib -o Shop.Core
dotnet new web -o Shop.Api
dotnet sln add Shop.Core Shop.Api
dotnet add Shop.Api reference Shop.Core
dotnet add Shop.Api package Microsoft.EntityFrameworkCore.Sqlite
# multi-targeting: one library, two runtimes
# <TargetFrameworks>net8.0;net10.0</TargetFrameworks>ImplicitUsingsadds a standard set ofusingdirectives for the SDK in use; it is convenient and hides which namespace a type came from.Nullable enableturns nullability into compile-time analysis. Treat the warnings as errors on new projects, or the annotations decay into noise.InvariantGlobalizationremoves ICU data from the output and makes string comparison culture-independent. Enable it for containers and services; leave it off for user-facing formatting.Directory.Build.propsat the repository root applies settings to every project below it, which is the right place for warnings-as-errors and the language version.
<!-- Directory.Build.props — one place for settings shared by every project -->
<Project>
<PropertyGroup>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<AnalysisLevel>latest-recommended</AnalysisLevel>
<Deterministic>true</Deterministic>
</PropertyGroup>
</Project>File-based apps and running a snippet
# a single file with top-level statements and inline package directives
# hello.cs
# ---
# #!/usr/bin/env dotnet
# #:package [email protected]
# Console.WriteLine("Quick check".Humanize());
# ---
dotnet run hello.cs
# script-style execution without creating a project
dotnet tool install -g dotnet-script
dotnet script scratch.csx💡
Use file-based apps for experiments and repro cases, and always create a real project for anything you intend to keep. The one-file form has no test project, no dependency pinning beyond the directive and no place to put a second class.
FAQ
What is the difference between the SDK and the runtime?
The runtime executes an already built application; the SDK adds the compiler, the CLI and the templates. A build machine or a development machine needs the SDK; a deployment target only needs the runtime, unless you publish self-contained.
Why does dotnet run rebuild every time?
It runs an implicit restore and build unless nothing changed. For a faster loop use
dotnet watch, which keeps the build server warm, or run the built binary directly after a one-off dotnet build.Related
Syntax and types Dependency injection and configuration
Last refreshed 2026-09-18.