ASP.NET Core 9 MVC — Bölüm 1: Sıfırdan Proje Kurulumu ve Mimari
ASP.NET Core 9 MVC serisinin ilk bölümü: dotnet new mvc ile proje kurulumu, çözüm yapısı, .NET 9 minimal hosting modeli, Program.cs'in detayları, appsettings katmanları, attribute vs conventional routing, Area konsepti ve IStartupFilter ile startup pipeline'ı — mimariyi başlatan temel bölüm.
Merhaba arkadaşlar, bu makalemizde yepyeni bir seriye başlıyoruz: ASP.NET Core 9 MVC üzerinden sıfırdan profesyonel bir web uygulaması inşa edeceğiz. 5 bölümlük bu seride proje kurulumundan başlayıp Controller'lara, EF Core ile veritabanına, Identity ile kimlik doğrulamaya ve en sonunda production deploy'a kadar her aşamayı tek tek işleyeceğiz. Başlamadan önce bir kahve alın, çünkü bu ilk bölümde mimariyi ve modern .NET 9 hosting modelini uzun uzun konuşacağız.
Neden ASP.NET Core 9?
.NET 9 Kasım 2024'te çıktı ve performans, developer experience ve cloud-native destek tarafında ciddi iyileştirmeler getirdi. STS (Standard Term Support) olmasına rağmen, .NET 10 LTS'e kadar kısa bir köprü olarak düşünebilirsiniz — .NET 10'a geçişi çok kolaylaştıracak bir sürüm. Benim gördüğüm en kıymetli yenilikler:
- Kestrel performans iyileştirmeleri: HTTP/3 olgunlaştı, TLS handshake daha hızlı.
- Built-in OpenAPI: Swashbuckle'a bağımlılık olmadan API dokümantasyonu.
- Output caching ve hybrid cache: Response caching'in büyük kardeşi geldi.
- Native AOT desteği genişledi, başlatma süresi milisaniyelere indi.
- EF Core 9: Daha hızlı LINQ, complex type'lar, better JSON columns.
Bu serinin sonunda tüm bu yenilikleri pratik olarak kullanıyor olacağız.
Seriye Genel Bakış
5 bölümde nereye varıyoruz:
- Bölüm 1 (şu an): Proje kurulumu, çözüm yapısı, Program.cs ve mimari.
- Bölüm 2: Controller'lar, View'lar, Model Binding, Tag Helper'lar.
- Bölüm 3: EF Core 9 ile veritabanı, migration, Repository + Service pattern.
- Bölüm 4: Identity, Authentication, rol tabanlı yetkilendirme.
- Bölüm 5: Performans, caching, health check'ler ve production deploy.
Gereksinimler
Başlamadan önce makinenizde şunların olması lazım:
- .NET 9 SDK (dotnet --version komutuyla kontrol edin, 9.0.x görmelisiniz).
- Visual Studio 2022 17.12+, VS Code + C# Dev Kit veya Rider 2024.3+ — hangisini severseniz.
- SQL Server / LocalDB veya PostgreSQL (sonraki bölümlerde gerekecek).
- Git.
Hazırsak ilk projemizi oluşturalım.
dotnet new mvc — Template'ten Proje Oluşturma
Terminal açıp şu komutları çalıştırıyoruz:
mkdir SemGoksuMvc
cd SemGoksuMvc
dotnet new sln -n SemGoksuMvc
dotnet new mvc -n SemGoksuMvc.Web -o src/SemGoksuMvc.Web
dotnet sln add src/SemGoksuMvc.Web/SemGoksuMvc.Web.csproj
Burada tek projeli değil çözüm tabanlı bir yapı kuruyorum çünkü ilerleyen bölümlerde Domain, Infrastructure ve Application katmanlarını ayrı projeler olarak ekleyeceğiz. Clean Architecture'a benzer bir yapı kuracağız ama aşırıya kaçmadan.
Çözüm Yapısı
Seri bittiğinde klasör yapımız şöyle görünecek:
SemGoksuMvc/
├── SemGoksuMvc.sln
├── src/
│ ├── SemGoksuMvc.Web/ (MVC UI - Controllers, Views, wwwroot)
│ ├── SemGoksuMvc.Core/ (Entity'ler, interface'ler, domain kurallari)
│ └── SemGoksuMvc.Infrastructure/(DbContext, Repository, external services)
├── tests/
│ └── SemGoksuMvc.Tests/ (xUnit + bogus + FluentAssertions)
├── global.json (SDK pinning)
├── Directory.Build.props (ortak MSBuild ayarlari)
└── README.md
Visual Studio'dan: Visual Studio tarafinda Dosya > Yeni > Proje ile ayni sihirbazi acabilirsiniz. MVC sablonunu sectikten sonra hedef cerceve olarak .NET 9.0 secin.
Şimdilik sadece Web projesini oluşturduk, diğerlerini sonraki bölümlerde ekleyeceğiz. Ama global.json ile SDK versiyonunu sabitleyelim — ekip arkadaşlarınız veya CI agent'ınız farklı SDK kullanırsa başınız ağrır:
{
"sdk": {
"version": "9.0.100",
"rollForward": "latestFeature"
}
}
Program.cs — .NET 9'da Minimal Hosting Modeli
Template'in oluşturduğu Program.cs'a bakalım. .NET 6 ile gelen ve artık standart olan minimal hosting modelini kullanıyor — eski Startup.cs yok, her şey tek dosyada:
var builder = WebApplication.CreateBuilder(args);
// Servisleri DI container'a ekle
builder.Services.AddControllersWithViews();
var app = builder.Build();
// HTTP pipeline'ini yapilandir
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
Bu 20 satırlık dosyada çok şey oluyor. Parçalayalım:
1. WebApplication.CreateBuilder(args)
IHostBuilder, IWebHostBuilder ve IServiceCollection'ı tek bir yerden yöneten fabrika. Eskiden ayrı ayrı kurardık, artık tek API:
- Configuration otomatik yüklenir: appsettings.json, appsettings.{Environment}.json, environment variables, command line args, user secrets (development'ta).
- Logging konfigüre edilmiş gelir: Console, Debug, EventSource provider'ları.
- Kestrel default web server olarak kurulur.
- DI container hazır gelir, builder.Services üzerinden servis ekliyoruz.
2. AddControllersWithViews()
MVC için gerekli tüm servisleri ekler: routing, model binding, view engine (Razor), data annotations, anti-forgery. API projesi yapıyorsanız AddControllers() yeterli (view yok). View Components da istiyorsanız bu aynı metotla geliyor.
3. builder.Build()
Servisleri dondurur ve WebApplication instance'ı döner. Bundan sonra app.Services üzerinden sevisleri okuyabilirsiniz ama artık yenisini ekleyemezsiniz.
4. Middleware Pipeline
app.Use...() çağrılarının sırası çok önemli. Her middleware kendinden sonrakini çağırır (veya kısa devre yapar). Sıra:
- UseExceptionHandler — prod'da hata yakalar.
- UseHsts — Strict-Transport-Security header'ı.
- UseHttpsRedirection — HTTP'den HTTPS'e yönlendirir.
- UseStaticFiles — wwwroot içeriğini sunar.
- UseRouting — gelen URL'i endpoint'e map eder.
- UseAuthorization — [Authorize] kurallarını uygular.
- MapControllerRoute — route tablosunu kurar.
- app.Run() — uygulamayı başlatır.
appsettings ve Environment
ASP.NET Core üç ortam tanır: Development, Staging, Production. ASPNETCORE_ENVIRONMENT environment variable'ı ile belirlenir. Ayarlarımızı ortam bazlı ayırıyoruz:
// appsettings.json (tum ortamlar)
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"Default": ""
}
}
// appsettings.Development.json (sadece dev)
{
"Logging": {
"LogLevel": {
"Default": "Debug",
"Microsoft.EntityFrameworkCore.Database.Command": "Information"
}
},
"ConnectionStrings": {
"Default": "Server=(localdb)\\MSSQLLocalDB;Database=SemGoksuMvc;Trusted_Connection=True;MultipleActiveResultSets=true"
}
}
Development'ta hassas bilgileri User Secrets'a almayı unutmayın. Proje klasöründe:
dotnet user-secrets init
dotnet user-secrets set "ConnectionStrings:Default" "Server=..."
Bu bilgiler %APPDATA%\Microsoft\UserSecrets\ altında tutulur, repo'ya sızmaz.
Attribute Routing vs Conventional Routing
MVC iki tür routing sunar, ikisini de anlamak lazım.
Conventional routing (klasik, template'ten gelen):
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
Avantajı: URL yapısı tutarlı, tüm controller'lar otomatik aynı pattern'i takip eder. Dezavantajı: REST-ful API'lerde veya farklı URL yapıları istediğinizde sıkıntı yaratır.
Attribute routing:
[Route("urunler")]
public class UrunlerController : Controller
{
[HttpGet("")] // GET /urunler
public IActionResult Index() => View();
[HttpGet("{id:int}")] // GET /urunler/5
public IActionResult Detay(int id) => View();
[HttpGet("kategori/{slug}")] // GET /urunler/kategori/elektronik
public IActionResult Kategori(string slug) => View();
}
Avantajı: her action'ın URL'si bağımsız tanımlanabilir, SEO dostu "slug" URL'ler kolayca yazılır. Benim tavsiyem: statik sayfalar için conventional, kompleks/SEO-önemli sayfalar için attribute routing kullanın — hibrit yaklaşım en rahatı.
Area Konsepti
Büyük projelerde controller'lar kalabalıklaşınca Area'lar devreye girer. Her area kendi MVC hiyerarşisine sahip mini bir modül:
Areas/
├── Admin/
│ ├── Controllers/
│ │ └── DashboardController.cs
│ ├── Views/
│ │ └── Dashboard/
│ │ └── Index.cshtml
│ └── Models/
└── Blog/
├── Controllers/
└── Views/
Area controller'ı şöyle işaretlenir:
[Area("Admin")]
public class DashboardController : Controller
{
public IActionResult Index() => View();
}
Program.cs'te area routing'i ekliyoruz:
app.MapControllerRoute(
name: "areas",
pattern: "{area:exists}/{controller=Home}/{action=Index}/{id?}");
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
Artık /Admin/Dashboard URL'i çalışır. Admin panelini ayırmak için ideal yapı.
Startup Filters ve IHostedService
.NET 9'da uygulama başlatılırken kod çalıştırmak istiyorsanız iki yol var. Hızlı bir IStartupFilter örneği:
public class CustomHeaderStartupFilter : IStartupFilter
{
public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
{
return app =>
{
app.Use(async (ctx, n) =>
{
ctx.Response.Headers["X-Powered-By"] = "SemGoksu";
await n();
});
next(app);
};
}
}
// Program.cs
builder.Services.AddTransient<IStartupFilter, CustomHeaderStartupFilter>();
Uzun süren başlatma işlemleri (seed data, warmup) için ise IHostedService veya BackgroundService tercih edilir:
public class DbSeedHostedService(IServiceProvider sp, ILogger<DbSeedHostedService> log) : IHostedService
{
public async Task StartAsync(CancellationToken ct)
{
log.LogInformation("Seeding database...");
using var scope = sp.CreateScope();
// seed islemleri
await Task.CompletedTask;
}
public Task StopAsync(CancellationToken ct) => Task.CompletedTask;
}
builder.Services.AddHostedService<DbSeedHostedService>();
İlk Çalıştırma
Şimdi uygulamayı çalıştıralım:
dotnet run --project src/SemGoksuMvc.Web
Terminal size bir URL gösterecek (örn. https://localhost:7174). Tarayıcıda açtığınızda default MVC template'i karşılar: Home, Privacy sayfaları ve bootstrap-based layout. Bu noktadan sonra üzerine inşa edeceğiz.
Hot Reload
Geliştirme sırasında dotnet watch kullanın — kod değiştirdiğinizde uygulama otomatik yeniden yüklenir:
dotnet watch run --project src/SemGoksuMvc.Web
Controller veya Razor view'da yapacağınız değişiklikler anında yansır, geliştirmede çok vakit kazandırır.
Bu Bölümde Ne Yaptık?
- dotnet new mvc ile proje oluşturduk, çözüm yapımızı kurduk.
- .NET 9 minimal hosting modelini ve Program.cs'in her satırını anladık.
- Middleware pipeline'ın neden bu sırada olduğunu gördük.
- appsettings katmanları, User Secrets, environment-bazlı konfigürasyon işledik.
- Conventional ve attribute routing farkını, Area konseptini öğrendik.
- IStartupFilter ve IHostedService ile başlatma işlemlerini tanıdık.
Bir sonraki bölümde Controller'ların derinliklerine iniyoruz: action return type'ları, model binding, tag helper'lar, partial view'lar, view component'ler ve Razor syntax. Bol kolay gelsin, bir sonraki bölümde görüşürüz!