Mert Özen Her Satırda Daha İleri
Backend .NET SDK, CLI ve MSBuild — #01

dotnet CLI Haritası: SDK ve Runtime Farkı, Komut Ailesi, global.json ve SDK Sürüm Seçimi

Mert Özen 15 Kas 2026 15 dk 6 görüntülenme
dotnet CLI Haritası: SDK ve Runtime Farkı, Komut Ailesi, global.json ve SDK Sürüm Seçimi

dotnet komutu hangi SDK'yı kullanacağını, çalıştırıldığı klasörden yukarı doğru bulduğu ilk global.json'a göre seçer; rollForward verilmezse yalnız aynı feature band içinde ilerler, uygulamanın hangi runtime'da çalışacağına ise runtimeconfig.json karar verir. Bookstore.slnx'e CLI ile proje ekliyor, rollForward politikalarını üç kurulu SDK ile karşılaştırıyor, 10.0.1xx ile 10.0.4xx band'lerinin farkını, workload'ları ve .NET 11 CLI değişikliklerini gerçek çıktılarla gösteriyoruz.

Sürüm notu: Bu yazıdaki kodlar .NET 10 (SDK 10.0.404, runtime 10.0.15) ile derlenip denenmiştir; feature band karşılaştırmasında SDK 10.0.115, .NET 11 bölümünde SDK 11.0.100 kullanılmıştır.

Önce Tahmin Et

Makinende üç .NET SDK kurulu: 10.0.115, 10.0.404 ve 11.0.100. Repo kökündeki global.json şu kadar:

{
  "sdk": {
    "version": "10.0.100"
  }
}

10.0.100 kurulu değil. Bu klasörde dotnet --version ne yazar? Çoğu kişi ya "hata verir" ya da "en yenisi, 11.0.100" der. Gerçek çıktı:

10.0.115

Aynı dosyaya "rollForward": "latestFeature" eklersen cevap 10.0.404 olur, "latestMajor" yazarsan 11.0.100. Üç satırlık bir JSON dosyası, ekipteki herkesin ve CI'ın hangi derleme araçlarını kullanacağına karar veriyor. Bu yazıda bu kararın nasıl verildiğini, SDK ile runtime'ın neden ayrı şeyler olduğunu, dotnet komutlarının arka planda birbirini nasıl çağırdığını ve workload'ların ne zaman gerektiğini, sitedeki serilerin ortak örnek uygulaması Bookstore (bir online kitapçı) üzerinden göreceğiz.

ASP.NET Core Web API serisinin "İlk ASP.NET Core Web API Projesi: .NET SDK, global.json ve Proje Yapısı" yazısı global.json'u ilk proje için tanıttı; C# Temelleri serisi de solution ve NuGet'e giriş yaptı. Bu seri bir adım geri çekilip her gün dokunduğun ama nadiren öğrendiğin katmana, yani SDK, CLI ve MSBuild'e odaklanıyor.

SDK ile Runtime Aynı Şey Değil

.NET kurulumunda iki ayrı parça var. Runtime, derlenmiş uygulamayı çalıştıran kısımdır: CLR (Common Language Runtime; derleyicinin ürettiği ara kodu çalıştıran sanal makine), base class library ve ASP.NET Core gibi shared framework'ler. SDK ise uygulamayı üreten araçlardır: dotnet CLI komutları, MSBuild, NuGet, Roslyn derleyicisi, template'ler. Sunucuya yalnız runtime kurulur; geliştirici makinesine ve CI'a SDK kurulur, SDK kendi runtime'ını da yanında getirir. dotnet --info ikisini ayrı ayrı gösterir:

dotnet --info
.NET SDK:
 Version:           10.0.404
 Commit:            2ab2854867
 Workload version:  10.0.404
 MSBuild version:   18.9.15+2ab285486

Host:
  Version:      11.0.0
  Commit:       297acf930f

.NET SDKs installed:
  10.0.115 [C:\Program Files\dotnet\sdk]
  10.0.404 [C:\Program Files\dotnet\sdk]
  11.0.100 [C:\Program Files\dotnet\sdk]

.NET runtimes installed:
  Microsoft.AspNetCore.App 10.0.15 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 11.0.0 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 8.0.34 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 9.0.22 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 10.0.15 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 11.0.0 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]

global.json file:
  C:\src\Bookstore\global.json

Üç ayrı sürüm numarası var ve üçü de farklı. SDK 10.0.404 seçilmiş, çünkü Bookstore'un global.json'u bunu istiyor. Host 11.0.0, çünkü dotnet komutunun (muxer) SDK'yı bulmak için yüklediği hostfxr, makinede kurulu en yeni sürümden seçilir. Runtime listesinde de 8'den 11'e dört sürüm yan yana duruyor. Kurulu sürümlerin hâlâ destekte olup olmadığını dotnet sdk check söyler:

.NET SDKs:
Version       Status
-------------------------
10.0.115      Up to date.
10.0.404      Up to date.
11.0.100      Up to date.

.NET Runtimes:
Name                          Version      Status
----------------------------------------------------------------------
Microsoft.NETCore.App         8.0.34       .NET 8.0 is out of support.
Microsoft.NETCore.App         9.0.22       .NET 9.0 is out of support.
Microsoft.AspNetCore.App      10.0.15      Up to date.
Microsoft.NETCore.App         10.0.15      Up to date.
Microsoft.AspNetCore.App      11.0.0       Up to date.
Microsoft.NETCore.App         11.0.0       Up to date.

.NET 8 ve .NET 9'un desteği 10 Kasım 2026'da, .NET 11'in çıktığı gün bitti. Bu sürümleri hedefleyen projeler hâlâ derlenir, ama artık güvenlik yaması gelmiyor.

Derlenmiş bir uygulamanın hangi runtime ile çalışacağına SDK değil, uygulamanın yanındaki runtimeconfig.json karar verir. net10.0 hedefleyen bir uygulama "Microsoft.NETCore.App 10.0.0 veya üstü" ister; host da kurulu 10.0.x sürümlerinin en yüksek patch'ini seçer. Bir major sürüm yukarı kendiliğinden geçmez, ama istersen zorlayabilirsin:

dotnet Bookstore.Admin.dll info
DOTNET_ROLL_FORWARD=LatestMajor dotnet Bookstore.Admin.dll info
Runtime:     .NET 10.0.15
Environment: (not set)
Runtime:     .NET 11.0.0
Environment: (not set)

Aynı dll önce 10.0.15, sonra 11.0.0 üzerinde çalıştı. Yani "projeyi .NET 11 SDK ile derledim" demek, uygulamanın .NET 11'de çalıştığı anlamına gelmez. Hedef framework'ü csproj'daki TargetFramework belirler, derleme araçlarını global.json, çalışma anındaki runtime'ı host:

01-sdk-runtime-selection-tr.png

Komut Ailesi ve Örtük Adımlar

dotnet CLI'ın komutları birbirinden bağımsız görünür ama çoğu bir öncekini kendiliğinden çağırır. Hangi komutun neyi tetiklediğini bilmek, CI script'lerinde aynı işi iki kez yapmamak için önemli:

KomutNe yaparKendiliğinden çağırdığı
dotnet newTemplate'ten proje, solution ya da dosya üretirProje template'lerinde restore
dotnet restoreNuGet paketlerini indirir, obj/project.assets.json yazar-
dotnet buildDerler, bin/ çıktısını üretirrestore (--no-restore ile kapanır)
dotnet runProjeyi derleyip çalıştırırrestore + build (--no-build)
dotnet testTest projelerini çalıştırırrestore + build (--no-build)
dotnet packNuGet paketi üretir, varsayılan Releaserestore + build
dotnet publishDağıtıma hazır çıktı üretir, varsayılan Releaserestore + build
dotnet watchDosya değişince yeniden derler, hot reload uygularrun ya da test
dotnet format.editorconfig kurallarına göre kodu düzenlerrestore
dotnet sln, dotnet reference, dotnet packageSolution, proje referansı ve paket referansı yönetirpackage add restore yapar

.NET 10 ile dotnet package add, dotnet package list, dotnet reference add gibi isim-önce (noun-first) biçimler geldi. Eski dotnet add package yazımı çalışmaya devam ediyor, ama yeni script'lerde isim-önce biçimi kullanmak daha okunur.

Örtük restore'un ne yaptığını en iyi, temiz bir clone'da --no-restore vererek görürsün:

dotnet build tools/Bookstore.Admin --no-restore
C:\Program Files\dotnet\sdk\10.0.404\Sdks\Microsoft.NET.Sdk\targets\Microsoft.PackageDependencyResolution.targets(266,5): error NETSDK1004: Assets file 'C:\src\Bookstore\tools\Bookstore.Admin\obj\project.assets.json' not found. Run a NuGet package restore to generate this file. [C:\src\Bookstore\tools\Bookstore.Admin\Bookstore.Admin.csproj]
    0 Warning(s)
    1 Error(s)

Build, restore'un ürettiği project.assets.json olmadan çalışamıyor. CI'da doğru sıra şu: önce bir kez dotnet restore, sonra dotnet build --no-restore, en son dotnet test --no-build. Her adım bir öncekinin çıktısını kullanır ve aynı iş tekrarlanmaz. Yerelde ise bu ayrıma gerek yok; dotnet build restore'u kendisi yapar. Terminalde gördüğün çıktı .NET 9'dan beri terminal logger'ın çıktısıdır:

Restore complete (0.4s)
  Bookstore.Contracts net10.0 succeeded (0.1s) → src\Bookstore.Contracts\bin\Debug\net10.0\Bookstore.Contracts.dll
  Bookstore.Admin net10.0 succeeded (0.1s) → tools\Bookstore.Admin\bin\Debug\net10.0\Bookstore.Admin.dll
  Bookstore.Contracts.Tests net10.0 succeeded (0.1s) → tests\Bookstore.Contracts.Tests\bin\Debug\net10.0\Bookstore.Contracts.Tests.dll
  Bookstore.Api net10.0 succeeded (0.5s) → src\Bookstore.Api\bin\Debug\net10.0\Bookstore.Api.dll
  Bookstore.Api.Tests net10.0 succeeded (0.2s) → tests\Bookstore.Api.Tests\bin\Debug\net10.0\Bookstore.Api.Tests.dll
Build succeeded in 1.2s

Çıktı bir dosyaya ya da CI log'una yönlendirildiğinde SDK klasik console logger'a döner ve Bookstore.Admin -> ...dll satırlarını yazar. Türkçe Windows'ta SDK mesajları Türkçe gelir ("Oluşturma başarılı oldu"); hata kodunu internette aramak için DOTNET_CLI_UI_LANGUAGE=en ortam değişkeniyle İngilizceye çevirebilirsin. Bu yazıdaki çıktılar bu ayarla alındı.

Bookstore.slnx'i CLI ile Kurmak

.NET 10'dan beri dotnet new sln varsayılan olarak XML tabanlı .slnx dosyası üretir; eski biçim için --format sln vermen gerekir. Boş bir klasörde:

dotnet new sln -n Bookstore
dotnet new webapi -o src/Bookstore.Api
dotnet sln add src/Bookstore.Api
<Solution>
  <Folder Name="/src/">
    <Project Path="src/Bookstore.Api/Bookstore.Api.csproj" />
  </Folder>
</Solution>

Eski .sln dosyasındaki GUID'ler (Globally Unique Identifier; her projeye verilen benzersiz bir kimlik) ve configuration blokları yok; solution klasörü de projenin yolundan kendiliğinden çıkarıldı. Bookstore'un referans uygulamasında bu dosya zaten var ve iki proje içeriyor: Bookstore.Api ve Bookstore.Api.Tests. Bu seri boyunca üç proje daha ekleyeceğiz: paylaşılan tipleri taşıyan ve 4. yazıda paket olarak yayımlayacağımız Bookstore.Contracts, yönetim işleri için küçük bir komut satırı aracı olan bookstore-admin ve onun testleri. Hepsini CLI ile ekliyoruz:

dotnet new classlib -n Bookstore.Contracts -o src/Bookstore.Contracts
dotnet new console -n Bookstore.Admin -o tools/Bookstore.Admin
dotnet new console -n Bookstore.Contracts.Tests -o tests/Bookstore.Contracts.Tests
dotnet sln add src/Bookstore.Contracts tools/Bookstore.Admin tests/Bookstore.Contracts.Tests
dotnet reference add src/Bookstore.Contracts --project tools/Bookstore.Admin
dotnet reference add src/Bookstore.Contracts --project tests/Bookstore.Contracts.Tests
dotnet package add xunit.v3 --project tests/Bookstore.Contracts.Tests
Project `src/Bookstore.Contracts/Bookstore.Contracts.csproj` added to the solution.
Project `tools/Bookstore.Admin/Bookstore.Admin.csproj` added to the solution.
Project `tests/Bookstore.Contracts.Tests/Bookstore.Contracts.Tests.csproj` added to the solution.
Reference `..\..\src\Bookstore.Contracts\Bookstore.Contracts.csproj` added to the project.
Reference `..\..\src\Bookstore.Contracts\Bookstore.Contracts.csproj` added to the project.
info : PackageReference for package 'xunit.v3' added to 'C:\src\Bookstore\tests\Bookstore.Contracts.Tests\Bookstore.Contracts.Tests.csproj' and PackageVersion added to central package management file 'C:\src\Bookstore\Directory.Packages.props'.

Test projesini neden console template'iyle açtığımızı merak edebilirsin. xUnit v3 test projeleri kendi başına çalışan bir exe'dir ve .NET 10.0.4xx SDK'daki yerleşik xunit template'i hâlâ xUnit v2 üretiyor. Console template'i OutputType'ı Exe yapıyor, xunit.v3 paketi de giriş noktasını kendisi üretiyor; template'in getirdiği Program.cs'i silmek yetiyor. Son satırdaki mesaja da dikkat et: Bookstore CPM (Central Package Management; paket sürümlerinin tek bir Directory.Packages.props dosyasında toplandığı düzen) kullanıyor, bu yüzden package add csproj'a sürümsüz bir PackageReference yazdı. xunit.v3'ün sürümü o dosyada zaten olduğu için dosya değişmedi; mesaj her durumda aynı. CPM'i 3. yazıda ayrıntılı göreceğiz.

Bookstore.Contracts'a ISBN'i (International Standard Book Number; kitaplara verilen uluslararası numara) 13 haneli biçimde doğrulayan küçük bir tip koyduk; bookstore-admin aracı da onu kullanıyor:

return args switch
{
    ["info"] => PrintInfo(),
    ["isbn", string input] => CheckIsbn(input),
    _ => PrintUsage(),
};

static int PrintInfo()
{
    Console.WriteLine($"Runtime:     {RuntimeInformation.FrameworkDescription}");
    Console.WriteLine($"Environment: {Environment.GetEnvironmentVariable("BOOKSTORE_ENVIRONMENT") ?? "(not set)"}");
    return 0;
}
dotnet run --project tools/Bookstore.Admin -- isbn 978-0-441-17271-9
dotnet test --project tests/Bookstore.Contracts.Tests
valid: 9780441172719
Running tests from C:\src\Bookstore\tests\Bookstore.Contracts.Tests\bin\Debug\net10.0\Bookstore.Contracts.Tests.dll (net10.0|x64)
C:\src\Bookstore\tests\Bookstore.Contracts.Tests\bin\Debug\net10.0\Bookstore.Contracts.Tests.dll (net10.0|x64) passed (134ms)

Test run summary: Passed!
  total: 5
  failed: 0
  succeeded: 5
  skipped: 0
  duration: 513ms

dotnet run'da -- ayıracı önemli: ondan sonraki her şey uygulamaya gider, öncesi dotnet run'a. dotnet test'in --project parametresi almasının sebebi de Bookstore'un global.json'unda test runner olarak MTP'nin (Microsoft Testing Platform; .NET'in VSTest'in yerini alan yeni test çalıştırma altyapısı) seçilmiş olması. Ayrıntısı Unit Test serisinin "xUnit v3 ile Test Projesi" yazısında.

Feature Band'ler: Bir Major Sürümün İçinde Birden Fazla SDK

Runtime sürümü 10.0.15 gibi üç parçalıdır. SDK sürümünün üçüncü parçası ise iki bilgi taşır: 10.0.404'te yüzler basamağı (4) feature band'i, son iki basamak (04) o band'in patch'ini gösterir. Microsoft her çeyrek yeni bir band çıkarır ve her band bir Visual Studio sürümüyle eşleşir: 10.0.1xx Visual Studio 18.0 ile, 10.0.4xx Visual Studio 18.9 ile geldi. Yeni band, aynı runtime'ı hedeflerken yeni MSBuild, NuGet ve CLI özellikleri getirebilir. 10 Kasım 2026'daki servis güncellemesinde Microsoft iki .NET 10 SDK'sı yayımladı: 10.0.115 ve 10.0.404. İkisi de aynı 10.0.15 runtime'ını içeriyor.

Destek süresi band'e göre değişiyor. Microsoft'un sürüm tablosuna göre 1xx band'i ve bir major sürümün son band'i (.NET 10 için 4xx) runtime'ın ömrü boyunca destekleniyor; 2xx ve 3xx band'leri ise bir sonraki band çıkınca destek dışı kaldı. Aradaki farkı küçük bir CLI komutuyla görmek mümkün. Bookstore'un iki test projesini ayrı build edebilmek için bir solution filter (.slnf) dosyası oluşturup içine proje eklemek 10.0.404'te çalışıyor:

dotnet new slnf --parent-solution Bookstore.slnx --name Bookstore.Tests
dotnet sln Bookstore.Tests.slnf add tests/Bookstore.Contracts.Tests/Bookstore.Contracts.Tests.csproj tests/Bookstore.Api.Tests/Bookstore.Api.Tests.csproj
The template "Solution Filter File" was created successfully.

Project `tests/Bookstore.Contracts.Tests/Bookstore.Contracts.Tests.csproj` added to the solution.
Project `tests/Bookstore.Api.Tests/Bookstore.Api.Tests.csproj` added to the solution.

Dosyayı oluşturmak 10.0.115'te de çalışıyor, ama add komutu:

Unhandled exception: Object reference not set to an instance of an object.

İkisi de ".NET 10 SDK", ikisi de destekte. Ama biri Visual Studio 18.9 döneminin CLI'ını, diğeri 18.0 döneminin CLI'ını taşıyor. "Herkes .NET 10 kullanıyor" demek bu yüzden yetmiyor; band'i de sabitlemek gerekiyor.

global.json ve rollForward Politikaları

dotnet komutu çalışınca muxer, bulunduğun klasörden başlayıp üst klasörlere doğru ilk global.json'u arar. Bulursa oradaki sdk bölümüne göre kurulu SDK'lardan birini seçer, bulamazsa en yenisini kullanır. version tam sürüm olmalı (10.0.100; 10.0 ya da 10.0.x geçersizdir ve dosya yok sayılır). rollForward verilmezse varsayılan patch'tir. Üç SDK kurulu makinede "version": "10.0.100" ile her politikanın seçtiği SDK:

rollForwardSeçilen SDKKural
patch (varsayılan)10.0.115İstenen sürüm yoksa aynı band'in en yüksek patch'i
latestPatch10.0.115Aynı band, en yüksek patch
feature, minor, major10.0.115Önce aynı band; yalnız bulunamazsa üst band'e çıkar
latestFeature10.0.404Aynı major.minor içinde en yüksek band ve patch
latestMinor10.0.404Aynı major içinde en yüksek sürüm
latestMajor11.0.100Kurulu en yüksek sürüm
disableHataYalnız birebir aynı sürüm

latest ile başlayan politikalar "kurulu olanların en yükseğini al", diğerleri "önce isteneni dene, yoksa bir adım ilerle" der. Fark ancak birden fazla band kurulu olduğunda ortaya çıkar; tek SDK'lı bir makinede hepsi aynı sonucu verir. Bu yüzden sorun genelde geliştirici makinesinde değil, farklı SDK'ların kurulu olduğu CI agent'larında görünür.

.NET 10 SDK ile global.json'a iki yeni alan geldi. errorMessage, uygun SDK bulunamadığında gösterilecek mesajı belirler; paths ise SDK'nın aranacağı klasörleri sıralar (repo içine kurulmuş bir SDK için ".dotnet", makinedeki kurulum için "$host$"). Bookstore'un global.json'unu bu bilgilerle güncelledik:

{
  "sdk": {
    "version": "10.0.404",
    "rollForward": "latestPatch",
    "errorMessage": "Bookstore needs .NET SDK 10.0.4xx (10.0.404 or later). Install it from https://dotnet.microsoft.com/download/dotnet/10.0"
  },
  "test": {
    "runner": "Microsoft.Testing.Platform"
  }
}

Referans uygulamada "10.0.100" ve latestFeature vardı; bu ayar 10.0.1xx'ten 10.0.4xx'e kadar her şeyi kabul ediyordu. Yeni hâli 4xx band'ini sabitliyor: 10.0.404 ve sonraki 4xx patch'leri kabul, 10.0.1xx ve 11 değil. .NET 10'un son band'i olduğu için 4xx runtime'ın ömrü boyunca destek alacak, yani bu dosyayı her çeyrek değiştirmek gerekmiyor. Ekipte bu SDK'yı kurmamış biri komut çalıştırınca hata mesajı doğrudan ne yapacağını söylüyor:

The command could not be loaded, possibly because:
  * You intended to execute a .NET application:
      The application '--version' does not exist or is not a managed .dll or .exe.
  * You intended to execute a .NET SDK command:
      Bookstore needs .NET SDK 10.0.4xx (10.0.404 or later). Install it from https://dotnet.microsoft.com/download/dotnet/10.0

Dosyanın yeri de en az içeriği kadar önemli. Arama, projenin değil, komutu çalıştırdığın klasörün yerinden başlar. global.json'u yanlışlıkla src/ klasörüne koyduğumuzda:

dotnet --version
cd src/Bookstore.Api
dotnet --version
11.0.100
10.0.404

Repo kökünden çalışan CI script'i 11 ile, src altında çalışan geliştirici 10 ile derler. global.json'un yeri her zaman solution dosyasının yanı, yani repo kökü olmalı.

Workload'lar: Ne Zaman Gerekir?

Bazı uygulama türleri için gereken araçlar SDK'nın içinde gelmez, ayrı workload olarak kurulur: .NET MAUI (Multi-platform App UI; mobil ve masaüstü uygulamalar için framework), Android ve iOS, Blazor WebAssembly'de AOT (Ahead-of-Time; kodu çalışmadan önce makine koduna derleme) ve relinking için wasm-tools gibi. Bookstore'un ASP.NET Core API'si, console aracı ve testleri için workload gerekmez; Angular istemcisi de .NET tarafında değil. Yine de bir ekip arkadaşının Blazor WebAssembly denemesi için ne olduğunu görelim:

dotnet workload search wasm
dotnet workload install wasm-tools
dotnet workload list
Installing workload version 10.0.404.
...
Successfully installed workload(s) wasm-tools.

Workload version: 10.0.404

Installed Workload Id      Manifest Version       Installation Source
---------------------------------------------------------------------
wasm-tools                 10.0.115/10.0.100      SDK 10.0.400

Installation Source sütunu önemli: workload SDK'ya değil, SDK'nın feature band'ine kurulur. Aynı makinede SDK 11.0.100 ile dotnet workload list boş liste döndü. Workload'lar SDK'nın kurulu olduğu klasöre yazılır; Windows'ta bu C:\Program Files\dotnet olduğu için komut yönetici yetkisi ister. Bu kurulumda diske 1 GB'tan fazla pack indi. Bir projenin ihtiyaç duyduğu workload'ları tek tek yazmak yerine dotnet workload restore komutu proje dosyalarından bulup kurar; SDK güncellenince dotnet workload update çalıştırmak gerekir. Workload version: 10.0.404 satırı da workload set'lerini gösteriyor: belirli bir SDK sürümü için test edilmiş manifest sürümleri tek bir numarayla sabitlenir.

.NET 11 SDK'da CLI Tarafında Değişenler

.NET 11 SDK'sında dotnet komutunun giriş noktası Native AOT ile derlenmiş hâlde geliyor. --version, --info, her komutun --help'i ve dotnet sln list gibi komutlar managed runtime'ı hiç başlatmadan çalışıyor. Aynı klasörde global.json'u değiştirip her komutu on iki kez çalıştırdık, ilk iki ölçümü atıp medyanı aldık:

SDK 10.0.404  dotnet --version: median 57 ms
SDK 11.0.100  dotnet --version: median 12 ms
SDK 11.0.100  dotnet --version (DOTNET_CLI_ENABLEAOT=false): median 58 ms

Tek komutta fark küçük, ama shell completion ya da çok sayıda dotnet çağıran script'lerde hissediliyor. DOTNET_CLI_ENABLEAOT=false eski davranışa döndürür.

İkinci değişiklik, MSBuild server'ın varsayılan olarak açık gelmesi. Bir build bittikten sonra MSBuild süreci arka planda açık kalıyor ve sonraki build'ler onu kullanıyor. Açık kalan süreçleri görmek ve kapatmak için:

dotnet build-server shutdown
Shutting down MSBuild server...
Shutting down VB/C# compiler server...
VB/C# compiler server shut down successfully.
MSBuild server shut down successfully.

Kapatmak istersen DOTNET_CLI_USE_MSBUILD_SERVER=false ya da MSBUILDUSESERVER=0 ortam değişkenini kullanabilirsin; CI'da neden gerekebileceğini 9. yazıda göreceğiz.

.NET 11'in "what's new" sayfasında yer alan iki özellik aslında 10.0.404'te de var. dotnet run -e KEY=VALUE, uygulamaya ortam değişkeni geçirir ve 9.0.200'den beri mevcut; .NET 11'in eklediği şey, bu değerlerin MSBuild'e de RuntimeEnvironmentVariable item'ı olarak geçmesi:

dotnet run --project tools/Bookstore.Admin -e BOOKSTORE_ENVIRONMENT=Staging -- info
Runtime:     .NET 10.0.15
Environment: Staging

Solution filter dosyası üretmek ve düzenlemek (dotnet new slnf, dotnet sln X.slnf add) de aynı sayfada, ama yukarıda gördüğümüz gibi 10.0.4xx band'inde de çalışıyor, 10.0.1xx'te çalışmıyor. Bir özelliğin hangi SDK'da olduğunu sürüm notundan değil, kendi kullandığın SDK'da deneyerek doğrula.

Üretimde Dikkat

CI pipeline'ının ilk adımında dotnet --info çalıştır. Bir build yalnız CI'da kırıldığında ilk bakılacak şey hangi SDK'nın ve hangi MSBuild sürümünün seçildiğidir; bu bilgi log'da yoksa tahminle uğraşırsın. GitHub Actions'ta SDK'yı kuran adım aynı global.json'u okuyabilir; bunun ayrıntısı GitHub Actions serisinde.

SDK'yı sabitlemek runtime'ı sabitlemez. 10.0.404 ile derlenen uygulama sunucuda o an kurulu olan en yüksek 10.0.x runtime'ında çalışır. Bu iyi bir şey, çünkü güvenlik yamaları uygulamayı yeniden derlemeden gelir. Ama container image'ı ya da self-contained publish kullanıyorsan runtime image'ın içine gömülür; yamayı almak için yeniden build almak gerekir. Bunu 6. yazıda, publish seçenekleriyle birlikte göreceğiz.

Desteği bitmiş runtime'ları dotnet sdk check ile düzenli kontrol et. 8 ve 9 hâlâ derleme yapabiliyor, ancak destek dışı.

Sık Düşülen Tuzaklar

Geliştiricinin makinesinde 11, CI'da 10 SDK'sı. Repo'da global.json yoksa herkes kendi makinesindeki en yeni SDK ile derler. Visual Studio'yu güncelleyen geliştirici 11.0.100'ü alır, CI agent'ı 10.0.404'te kalır; farklı analyzer kuralları ve farklı CLI davranışlarıyla iki ayrı build ortaya çıkar. Repo köküne global.json koy ve band'i sabitle.

global.json'u yanlış klasöre koymak. Dosya src/ altındaysa kökten çalışan komutlar onu görmez. Bir de tersi var: repo'nun üst klasöründe başka bir projeden kalan global.json varsa ve senin repo'nda yoksa, o dosya geçerli olur.

"Hepimiz .NET 10 kullanıyoruz" demek. 10.0.115 ile 10.0.404 aynı runtime'ı hedefler ama CLI'ları farklıdır; solution filter örneğinde biri çalışıp diğeri exception fırlattı.

CI'da her job'da dotnet workload install çalıştırmak. Gerekmeyen workload'u kurmak dakikalar ve gigabaytlar harcar; gerekiyorsa dotnet workload restore ile yalnız projenin istediklerini kur ve SDK'yı içeren image'ı önceden hazırla.

Production'da dotnet run kullanmak. dotnet run geliştirme komutudur: her seferinde restore ve build kontrolü yapar, paketleri NuGet cache'ten okur. Sunucuya dotnet publish çıktısı gider.

Küçük Bir Deneme

★☆☆ Bookstore klasöründe dotnet --info çalıştır ve global.json file satırını bul. Sonra bir üst klasöre çık ve aynı komutu tekrarla. Seçilen SDK ve Host sürümü değişti mi?

★★☆ global.json'daki rollForward'ı sırayla latestFeature, latestMajor ve disable yap; her birinde dotnet --version çıktısını not et. disable ile version'ı makinende olmayan bir sürüme çevirdiğinde errorMessage'ın gösterildiğini doğrula.

★★★ bookstore-admin'i dotnet publish tools/Bookstore.Admin ile yayımla ve çıktı klasöründeki Bookstore.Admin.runtimeconfig.json'u aç. Uygulamayı DOTNET_ROLL_FORWARD=LatestMajor ile ve onsuz çalıştır; sonra aynı ayarı runtimeconfig.json'a rollForward alanı olarak yazıp ortam değişkenine gerek kalmadığını göster.

Bu yazıda dotnet komutunun hangi SDK'yı seçtiğini ve komutların birbirini nasıl çağırdığını gördük. CLI ile oluşturduğumuz üç csproj dosyası ise template'lerden gelen tekrarlarla dolu. Bir sonraki yazıda csproj'u satır satır okuyacağız: SDK-style projenin örtük davranışları, sık kullanılan property'ler, item'lar ve dotnet msbuild -getProperty ile bir proje dosyasının son hâlini görmek.