ASP.NET Core, .NET 11’de union türlerini ve kapalı hiyerarşileri System.Text.Json ile kullanmak için pratik destek sunar. Bu sayede bir değerin birden fazla türe veya belirli bir tür ailesine ait olması temsil edilebilir. Bu destek Minimal API’lere, MVC denetleyicilerine, SignalR’a, Blazor’a ve OpenAPI belgelerine uzanır; ancak okuma ve yazma biçimlerinde ve parametre bağlama sınırlarında önemli farklılıklar vardır.
Union ne zaman uygundur?
Union, JSON sarmalayıcısına veya discriminator alanına ihtiyaç duymadan bağımsız alternatifleri kabul eden sözleşmeler için uygundur. Örneğin bir değer tam sayı ya da metin olabilir veya bir ayar, Boolean değer ya da daha ayrıntılı seçenekler içeren bir nesne kabul edebilir. Serileştirme sırasında etkin durum doğrudan doğal biçiminde yazılır; union türü için özel bir sarmalayıcı veya discriminator eklenmez.
JSON yapısından açıkça ayırt edilebilen durumlar otomatik olarak serileştirilir. Ancak durumlar aynı yapıyı paylaşıyorsa, örneğin Cat ve Dog nesnelerini içeren bir union söz konusuysa, System.Text.Json türü yalnızca JSON belirtecinden belirleyemez. Bu durumda doğru durumu seçmek için özel bir JsonTypeClassifier sağlanabilir. Aynı durum, sayı veya metin kabul eden bazı istek sözleşmeleri için de geçerlidir; web’e özgü JSON ayarları sayıların metinlerden okunmasına izin verdiğinden, metin her iki durumdan biri olarak yorumlanabilir.
Kapalı hiyerarşiler union’dan farklıdır
closed değiştiricisinin kullanılması, çok biçimli serileştirmenin otomatik olarak etkinleştirileceği anlamına gelmez. PaymentAuthorized gibi somut bir türetilmiş türle çalışılırken System.Text.Json bunu diğer tüm türler gibi ele alır ve discriminator olmadan paymentId ve amount içeren JSON üretir.
Buna karşılık bir API’de temel PaymentEvent türü kullanıldığında hangi türetilmiş türün oluşturulması gerektiği belirtilmelidir. Bu özellik temel tür üzerinde JsonPolymorphic(InferClosedTypePolymorphism = true) kullanılarak veya JsonSerializerOptions.InferClosedTypePolymorphism aracılığıyla etkinleştirilebilir. Böylece System.Text.Json, kapalı hiyerarşideki türetilmiş türleri serileştirme sırasında çıkarır ve adlarını tanımlayıcı olarak kullanır; örneğin $type değerini PaymentAuthorized olarak ayarlar.
İki model arasındaki seçim sözleşmenin niteliğine bağlıdır: union’lar tek bir aileye ait olması gerekmeyen alternatifleri temsil ederken kapalı hiyerarşiler, açık bir discriminator gerektiren birbiriyle ilişkili olay veya varlık grupları için uygundur.
ASP.NET Core katmanları genelinde destek
Minimal API’lerde union, normal yürütme yolunda veya Request Delegate Generator üzerinden istek gövdesi parametresi ya da dönüş türü olarak kullanılabilir; her iki yoldaki davranış aynıdır. Union ayrıca başka bir modelin içinde, IAsyncEnumerable<T> öğesi olarak veya AsParameters kullanan bir kapsayıcı içinde de görünebilir. ConfigureHttpJsonOptions ayarları serileştirmeyi etkilemeye devam eder.
MVC denetleyicileri union’ları eylem parametreleri ve dönüş türleri olarak destekler; buna Task<TUnion> ve ValueTask<TUnion> da dahildir. MVC, JsonSerializerDefaults.Web ayarlarını kullandığından, HTTP girdilerinde sayı ile metin arasındaki belirsizlik sorunu burada da geçerlidir.
SignalR’da destek, parametreler, dönüş değerleri ve akış öğeleri için JsonHubProtocol üzerinden çalışır. Bu protokol kullanıldığında int veya string türlerinden oluşan bir union için classifier gerekmez; çünkü JsonHubProtocol bu durumda JSON metin belirtecini belirsiz olarak değerlendirmez. Ancak UnionPet(Cat, Dog) örneğinde olduğu gibi durumların aynı StartObject belirtecini paylaştığı union’lar yine de classifier gerektirir. SignalR, MessagePack veya Newtonsoft.Json hub protokolleri kullanılırken bu işlevi desteklemez.
Blazor ve OpenAPI
İşlem içindeki Blazor bileşen parametreleri doğrudan atama yoluyla işlendiğinden serileştirme gerektirmez. Ancak JavaScript interop, kaydedilmiş bileşen durumu ve prerendering sırasında kullanılan parametreler System.Text.Json üzerinden geçer ve union’ların kurallarını izler. Örneğin etkin duruma göre bir union, sarmalayıcı veya discriminator eklenmeden JavaScript’e Boolean ya da seçenekler nesnesi biçiminde aktarılabilir.
OpenAPI’de union, her durum için bir tür içeren anyOf şeması kullanılarak temsil edilir. Durumlar bağımsız türler olarak göründüğünde Cat ve Dog bileşenlerinin adları yeniden kullanılır. Bu, discriminator taşıyan ve şemaları ayırt edici adlarla sunulan çok biçimli türlerden farklıdır. ApiExplorer ayrıca aynı durum kodu ve içerik türü için birden fazla yanıt türünü anyOf içinde temsil edebilir.
Dikkate alınması gereken sınırlamalar
Union desteği System.Text.Json’a bağlıdır; bu nedenle JSON çözümlemesinden geçmeyen bağlama kaynaklarıyla çalışmaz. Bu kaynaklar arasında query string değerleri, yol değerleri, başlıklar ve form alanları bulunur. ?id=42 gibi bir değer, hedefin int mi, string mi yoksa Guid mi olduğunu belirlemek için yeterli değildir. Blazor’da [SupplyParameterFromQuery] ve [SupplyParameterFromForm] kullanılarak form bağlama için de aynı sınırlama geçerlidir. Kaynak, union’ların bu kaynaklardan bağlanmasının hâlâ incelendiğini ve geri bildirim toplandığını belirtir.
Editoryal değerlendirme: Buradaki temel değer, JSON için yeni bir biçim eklemek değil, .NET 11 ekosisteminin geniş bölümlerinde birden fazla durum içeren sözleşmelerle çalışmayı birleştirmektir. Bununla birlikte tasarımın başarısı, durumların ayırt edilebilir olmasına ve veri kaynağına bağlıdır. Bu nedenle classifier veya discriminator erken aşamada belirlenmeli ve serileştirme desteğinin tüm parametre bağlama yöntemlerinin de desteklendiği anlamına gelmediği varsayılmamalıdır.