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

150 lines
8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# .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):
```bash
# 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
```bash
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.