sozsoft-platform/.github/instructions/dotnet.instructions.md

8 KiB
Raw Blame History

.NET / ABP Kod Standardı

Bu doküman api/ altındaki tüm projeler için bağlayıcıdır. Platform davranışına dair kurallar için ai.instructions.md esastır; çelişki olursa o dosya kazanır.

Standart derlemede zorlanır: api/Directory.Build.props analizörleri açar, api/.editorconfig kural şiddetlerini tanımlar. Bu iki dosya standardın tek kaynağıdır; proje dosyalarında tekrar edilmez.

1. Hedef sürümler

Katman Sürüm Nerede tanımlı
SDK .NET 10 (10.0.300) api/global.json
TargetFramework net10.0 proje dosyaları
Dil C# 14 (LangVersion=latest) api/common.props
ABP 10.0.0 Volo.Abp.* paket referansları

Sürüm yükseltmesi tek noktadan yapılır: global.json + common.props + paket sürümleri. Proje dosyalarına TFM/LangVersion tekrar yazılmaz.

2. Nullable

Directory.Build.props içinde çözüm geneli Nullable ayarı yapılmaz. Sebebi EF Core: kolonun zorunlu olup olmadığını, entity assembly'sinin nullable annotation bağlamından türetir. Global bir enable/annotations anahtarı, bir sonraki dotnet ef migrations add çağrısında tüm string özelliklerini sessizce NOT NULL kolona çevirir — seeder'ın doldurmadığı her alan da o anda kırılır.

Kural:

  • Nullable, proje bazında ve bilinçli olarak açılır (<Nullable>enable</Nullable>). Şu an açık olanlar: Sozsoft.Platform.Application, Sozsoft.Notifications.*, test projeleri.
  • Entity taşıyan projelerde (*.Domain) nullable açılacaksa, aynı PR'da dotnet ef migrations add çıktısı gözden geçirilir; şema değişikliği isteniyorsa kabul edilir, istenmiyorsa kolon zorunluluğu PlatformDbContext içinde .IsRequired(false) ile açıkça sabitlenir.
  • Kolon zorunluluğu tercihen NRT'ye değil, PlatformDbContext yapılandırmasına yazılır; böylece derleyici anahtarından bağımsız olur.

3. Ölü kod politikası

Aşağıdaki kurallar ihlal edilmiş kod merge edilmez:

Kural Anlamı
IDE0005 Kullanılmayan using
IDE0051 Çağrılmayan private üye
IDE0052 Yazılıp hiç okunmayan private alan (kullanılmayan enjekte bağımlılık)
IDE0059 Gereksiz değer ataması
IDE0161 namespace X; (file-scoped)

Tarama ve otomatik düzeltme (kalıcı yapılandırma gerektirmez):

# Rapor
dotnet build api/Sozsoft.Platform.sln --no-incremental \
  -p:EnforceCodeStyleInBuild=true -p:GenerateDocumentationFile=true

# Düzeltme
dotnet format api/Sozsoft.Platform.sln --diagnostics IDE0005,IDE0161 --severity warn

Kurallar .editorconfig'te warning seviyesindedir ve EnforceCodeStyleInBuildık olduğu için normal dotnet build de bunları raporlar.

IDE0052 düzeltilirken alan ile birlikte constructor parametresi de silinir; yoksa DI hâlâ gereksiz servisi çözer.

CS0162 (erişilemez kod) Directory.Build.props içinde gerekçesiyle susturulmuştur: veritabanı sağlayıcısı derleme zamanı sabiti olduğu için diğer sağlayıcının dalları erişilemez görünür. Bu durum dışında erişilemez kod bırakılmaz.

Ek kurallar:

  • Bir tip/dosya silinmeden önce repo genelinde (api + ui + configs) referans taraması yapılır. DI ile çözülen tipler (AppService, Manager, Provider, Repository, Seeder, Localizer, Notification sağlayıcıları) düz metin aramasında "referanssız" görünür — bunlar yalnızca modül/DI kaydı incelendikten sonra silinir.
  • Yorum satırına alınmış kod bloğu bırakılmaz; ihtiyaç varsa git geçmişinden alınır.
  • "İleride lazım olur" gerekçesiyle kullanılmayan DTO/Input/Consts tutulmaz.
  • Migrations/ ve *.g.cs üretilmiş koddur, bu kuralların dışındadır.

4. Modern C# kullanımı (yeni kod)

  • File-scoped namespace: namespace Sozsoft.Platform.ListForms;
  • Primary constructor: public class XAppService(IRepository<X, Guid> repo) : PlatformAppService — parametre doğrudan kullanılır, private readonly ... = param; şeklinde tekrar tanımlanmaz.
  • Koleksiyon ifadeleri ([], [.. items]), is not null, switch ifadesi, desen eşleme, Index/Range, hedefi belli new().
  • out var, tuple deconstruction; kullanılmayan çıktı için _.
  • async metotlar Async ile biter ve Task/Task<T> döner; async void yasak. ABP'de ConfigureAwait kullanılmaz.
  • Biçim: api/.editorconfig geçerlidir (4 boşluk girinti, using blokları namespace dışında ve System önce, dosya sonunda satır sonu, UTF-8 BOM).

5. ABP kuralları

  • Katman sınırı: Domain.Shared → Domain → Application.Contracts → Application → HttpApi → HttpApi.Host. Ters yönde referans verilmez; EntityFrameworkCore yalnızca Domain'e bakar. DbMigrator yalnızca Application.Contracts + EntityFrameworkCore referansı taşır; Application'a referans verilmez — seeder'ın uygulama servislerine ihtiyaç duyması, kodun yanlış katmanda olduğunun işaretidir.
  • Hem seeder'ın hem AppService'in ihtiyaç duyduğu dönüşüm: ortak kod Application.Contracts'a konur (DTO'nun kendi metodu olarak; yeni yardımcı sınıf üretilmez). Dönüşümün entity'ye yazması gerekiyorsa entity'nin yazılabilir kolon sözleşmesi Domain.Shared'de bir arayüz olarak tanımlanır (IListFormArtifact, IListFormFieldArtifact), entity bu arayüzü uygular ve dönüşüm arayüz üzerinden yazar. Böylece Contracts, Domain'i tanımadan entity doldurabilir ve aynı dönüşüm iki yerde kopyalanmaz.
  • AppService: PlatformAppService'ten türer, arayüzü Application.Contracts'ta tanımlanır. Controller yazılmaz — ABP otomatik API üretir. Controller sadece ABP konvansiyonunun karşılamadığı durumda (dosya indirme, harici webhook) eklenir.
  • Yetki: her AppService/metot [Authorize(PlatformConsts.AppCodes....)] ile korunur; izin adları PermissionDefinitionProvider içinde tanımlanır. Yetki kontrolü koda gömülmez, izin adı sabitten okunur.
  • Tenant izolasyonu: sorgular ICurrentTenant bağlamında çalışır; IMultiTenant entity'lerde manuel TenantId filtresi yazılmaz, veri filtreleri devre dışı bırakılacaksa (ICurrentTenant.Change, IDataFilter) gerekçe yorumla yazılır.
  • Repository: özel sorgu gerekiyorsa Domain'de arayüz + EntityFrameworkCore'da implementasyon. AppService içinde DbContext doğrudan kullanılmaz.
  • Dinamik SQL: parametreli çalıştırılır; string birleştirme ile sorgu kurulmaz.
  • Mapping: Mapperly ([Mapper] partial class) kullanılır; AutoMapper eklenmez. RMG012 uyarısı "hedefte karşılığı olmayan alan" demektir; yeni mapper eklerken bu uyarı bırakılmaz.
  • Localization: kullanıcıya görünen metin koda gömülmez, PlatformResource anahtarı üzerinden verilir.
  • Çok değerli alan taşıyan girdi DTO'su: ekranda dxTagBox (ya da çoklu seçimli dxGridBox) ile doldurulan bir alan tipli bir DTO'ya bağlanıyorsa property string kalır ve [JsonConverter(typeof(MultiValueStringJsonConverter))] ile işaretlenir — UI diziyi olduğu gibi yollar, converter PlatformConsts.MultiValueDelimiter ile tek string üretir. Varsayılan list-form-data/* ucundan geçen ekranlarda bu iş QueryHelper tarafından yapılır, DTO yoktur; okuma DTO'larına öznitelik eklenmez.
  • Sabitler: bağlantı dizesi, tenant id, anahtar/secret koda yazılmaz; konfigürasyon veya SettingDefinition üzerinden okunur.

6. Değişiklik öncesi kontrol listesi

dotnet build api/Sozsoft.Platform.sln --no-incremental \
  -p:EnforceCodeStyleInBuild=true -p:GenerateDocumentationFile=true   # 0 hata, yeni IDE00xx yok
dotnet format api/Sozsoft.Platform.sln --verify-no-changes --diagnostics IDE0005,IDE0161 --severity warn
dotnet test  api/test/Sozsoft.Platform.EntityFrameworkCore.Tests

Not: EntityFrameworkCore testleri şu an SQLite şema oluşturmada (nvarchar(max)SQLite Error 1) kırık; bu kırıklık bu standarttan önce de vardı ve ayrı bir iş olarak ele alınmalıdır.