Null Atanabilir Başvuru Türleriyle Çalışma

C# 8, boş değer atanabilir başvuru türleri (NRT) adlı yeni bir özellik getirerek başvuru türlerinin ek açıklama eklemesine olanak tanıyarak bunların içermesi veya içermemesi null için geçerli olup olmadığını gösterir. Bu özelliği kullanmaya yeniyseniz C# belgelerini okuyarak bu özelliği tanımanız önerilir. Yeni proje şablonlarında null atanabilir başvuru türleri varsayılan olarak etkinleştirilir, ancak açıkça kabul edilmediği sürece mevcut projelerde devre dışı kalır.

Bu sayfada EF Core'un null atanabilir başvuru türlerine yönelik desteği tanıtılır ve bunlarla çalışmaya yönelik en iyi yöntemler açıklanır.

Gerekli ve isteğe bağlı özellikler

Gerekli ve isteğe bağlı özelliklerle ilgili ana belgeler ve bunların null atanabilir başvuru türleriyle etkileşimi Gerekli ve İsteğe Bağlı Özellikler sayfasıdır. Öncelikle bu sayfayı okuyarak başlamanız önerilir.

Dekont

Mevcut bir projede null atanabilir başvuru türlerini etkinleştirirken dikkatli olun: Önceden isteğe bağlı olarak yapılandırılan başvuru türü özellikleri, boş değer atanacak şekilde açıkça açıklama eklenmediği sürece artık gerektiği gibi yapılandırılacaktır. İlişkisel veritabanı şemasını yönetirken, bu durum veritabanı sütununun null atanabilirliğini değiştiren geçişlerin oluşturulmasına neden olabilir.

Null değer atanamayan özellikler ve başlatma

Null atanabilir başvuru türleri etkinleştirildiğinde, C# derleyicisi başlatılmamış null atanamayan tüm özellikler için uyarılar yayar; bunlar içerir null. Sonuç olarak, varlık türlerini yazmanın yaygın yolu aşağıdakiler kullanılamaz:

public class Customer
{
    public int Id { get; set; }

    // Generates CS8618, uninitialized non-nullable property:
    public string Name { get; set; }
}

C# 11 veya üzerini kullanıyorsanız, gerekli üyeler bu soruna mükemmel bir çözüm sağlar:

public required string Name { get; set; }

Derleyici artık kodunuz bir Müşteri örneği oluşturduğunda her zaman Name özelliğini başlattığını garanti eder. Özelliğine eşlenen veritabanı sütunu null atanamaz olduğundan, EF tarafından yüklenen tüm örnekler de her zaman null olmayan bir Ad içerir.

C# uygulamasının eski bir sürümünü kullanıyorsanız Oluşturucu bağlaması, null değer atanamayan özelliklerinizin başlatıldığından emin olmak için alternatif bir tekniktir:

public class CustomerWithConstructorBinding
{
    public int Id { get; set; }
    public string Name { get; set; }

    public CustomerWithConstructorBinding(string name)
    {
        Name = name;
    }
}

Ne yazık ki bazı senaryolarda oluşturucu bağlama bir seçenek değildir; örneğin gezinti özellikleri bu şekilde başlatılamaz. Bu gibi durumlarda, null-forgiving işlecinin yardımıyla özelliğini null başlatmanız yeterlidir (ancak daha fazla ayrıntı için aşağıya bakın):

public Product Product { get; set; } = null!;

Gerekli gezinti özellikleri

Gerekli gezinti özellikleri ek bir zorluk sunar: Belirli bir sorumlu için her zaman bağımlı olsa da, programdaki bu noktadaki gereksinimlere bağlı olarak belirli bir sorgu tarafından yüklenebilir veya yüklenmeyebilir (verileri yüklemek için farklı desenlere bakın). Aynı zamanda, gezintinin yüklendiği bilinse ve bu nedenle yapılamasa nullbile, bu özelliklerin null atanabilir nullhale getirmek istenmeyen bir durum olabilir.

Bu bir sorun olmayabilir! Gerekli bir bağımlı düzgün yüklendiği sürece (örneğin aracılığıyla Include), gezinti özelliğine erişildiğinde her zaman null olmayan bir değer döndürüleceği garanti edilir. Öte yandan uygulama, gezintinin nullolup olmadığını denetleyerek ilişkinin yüklenip yüklenmediğini denetlemeyi seçebilir. Bu gibi durumlarda, gezintiyi boş değer atanabilir hale getirmek mantıklıdır. Bu, bağımlıdan sorumluya gerekli gezintilerin yapılması gerektiği anlamına gelir:

  • Yüklenmeyen bir gezintiye erişmek için bir programcı hatası olarak kabul edilirse null atanamaz olmalıdır.
  • İlişkinin yüklenip yüklenmediğini belirlemek için uygulama kodunun gezintiyi denetlemesi kabul edilebilirse null atanabilir olmalıdır.

Daha katı bir yaklaşım istiyorsanız, null atanabilir bir yedekleme alanına sahip null atanamaz bir özelliğe sahip olabilirsiniz:

private Address? _shippingAddress;

public Address ShippingAddress
{
    set => _shippingAddress = value;
    get => _shippingAddress
           ?? throw new InvalidOperationException("Uninitialized property: " + nameof(ShippingAddress));
}

Gezinti düzgün yüklendiği sürece, bağımlıya özelliği aracılığıyla erişilebilir. Ancak, özelliğine önce ilgili varlık düzgün yüklenmeden erişilirse, API sözleşmesi yanlış kullanıldığından bir InvalidOperationException oluşturulur.

Dekont

Birden çok ilgili varlık başvurusu içeren koleksiyon gezintileri her zaman null değer atanamaz olmalıdır. Boş bir koleksiyon, ilişkili varlık olmadığı, ancak listenin hiçbir zaman olmaması nullgerektiği anlamına gelir.

DbContext ve DbSet

EF ile, bağlam türlerinde başlatılmamış DbSet özelliklerine sahip olmak yaygın bir uygulamadır:

public class MyContext : DbContext
{
    public DbSet<Customer> Customers { get; set;}
}

Bu genellikle bir derleyici uyarısına neden olsa da EF Core 7.0 ve üzeri bu uyarıyı bastırır, çünkü EF bu özellikleri yansıma yoluyla otomatik olarak başlatır.

EF Core'un eski sürümünde bu soruna aşağıdaki gibi geçici bir çözüm bulabilirsiniz:

public class MyContext : DbContext
{
    public DbSet<Customer> Customers => Set<Customer>();
}

Başka bir strateji de null atanamayan otomatik özellikleri kullanmak, ancak derleyici uyarısını susturmak için null-forgiving işlecini (!) kullanarak bu nullözellikleri 'ye başlatmaktır. DbContext temel oluşturucu, tüm DbSet özelliklerinin başlatılmasını ve null değerinin hiçbir zaman bu özelliklerde gözlemlenmemesini sağlar.

İsteğe bağlı ilişkilerle ilgilenirken, gerçek null başvuru özel durumunun mümkün olmadığı derleyici uyarılarıyla karşılaşabilirsiniz. EF Core, LINQ sorgularınızı çevirirken ve yürütürken isteğe bağlı bir ilgili varlık yoksa, bu varlıkta herhangi bir gezintinin atmak yerine yok sayılacağını garanti eder. Ancak, derleyici bu EF Core garantisinin farkında değildir ve LINQ sorgusu bellekte LINQ to Objects ile yürütülür gibi uyarılar üretir. Sonuç olarak, derleyiciye gerçek null bir değerin mümkün olmadığını bildirmek için null-forgiving işlecinin (!) kullanılması gerekir:

var order = context.Orders
    .Where(o => o.OptionalInfo!.SomeProperty == "foo")
    .ToList();

İsteğe bağlı gezintilerde birden çok ilişki düzeyi dahil edildiğinde de benzer bir sorun oluşur:

var order = context.Orders
    .Include(o => o.OptionalInfo!)
    .ThenInclude(op => op.ExtraAdditionalInfo)
    .Single();

Kendinizi bunu çok fazla yapıyorsanız ve söz konusu varlık türleri ağırlıklı olarak (veya yalnızca) EF Core sorgularında kullanılıyorsa, gezinti özelliklerini null değer atanamaz hale getirmeyi ve Bunları Fluent API veya Veri Ek Açıklamaları aracılığıyla isteğe bağlı olarak yapılandırmayı göz önünde bulundurun. Bu, ilişkiyi isteğe bağlı tutarken tüm derleyici uyarılarını kaldırır; ancak, varlıklarınız EF Core dışından geçirilirse, özellikler null atanamaz olarak ek açıklama eklense de değerleri gözlemleyebilirsiniz null .

Eski sürümlerdeki sınırlamalar

EF Core 6.0'ın öncesinde aşağıdaki sınırlamalar uygulanmıştır:

  • Genel API yüzeyine null atanabilirlik için açıklama eklenmemiştir ("null-oblivious" genel API'ydi), bu da NRT özelliği açıldığında kullanılmasını bazen garip hale getirir. Bu özellikle Ef Core tarafından kullanıma sunulan FirstOrDefaultAsync gibi zaman uyumsuz LINQ işleçlerini içerir. GENEL API, EF Core 6.0'dan başlayarak null atanabilirlik için tam olarak açıklama ekler.
  • Tersine mühendislik C# 8 null atanabilir başvuru türlerini (NTS) desteklemedi: EF Core her zaman özelliğin kapalı olduğunu varsayan C# kodu oluşturdu. Örneğin, null atanabilir metin sütunları, bir özelliğin gerekli olup olmadığını yapılandırmak için kullanılan Fluent API’si veya Veri Açıklamaları ile string? değil, string türünde bir özellik olarak yapı iskelesi oluşturulmuştur. EF Core'un eski bir sürümünü kullanıyorsanız, yapı iskelesi oluşturulmuş kodu düzenlemeye devam edebilir ve bunları C# null atanabilirlik ek açıklamaları ile değiştirebilirsiniz.