Null yapılabilir başvuru türleriyle çalışmaWorking with Nullable Reference Types

C# 8, null yapılabilir başvuru türleriolarak adlandırılan yeni bir özellik sunmuştur, bu, başvuru türlerinin açıklanmasına izin verir ve bunların null içermesi için geçerli olup olmadığını gösterir.C# 8 introduced a new feature called nullable reference types, allowing reference types to be annotated, indicating whether it is valid for them to contain null or not. Bu özellik için yeni bir özelliktir, C# belgelerini okuyarak onu tanımanız önerilir.If you are new to this feature, it is recommended that make yourself familiar with it by reading the C# docs.

Bu sayfa, EF Core null yapılabilir başvuru türleri desteğini tanıtır ve bunlarla çalışmak için en iyi yöntemleri açıklar.This page introduces EF Core's support for nullable reference types, and describes best practices for working with them.

Gerekli ve isteğe bağlı özelliklerRequired and optional properties

Gerekli ve isteğe bağlı özellikler hakkındaki ana belgeler ve bunların null yapılabilir başvuru türleriyle etkileşimi, gerekli ve Isteğe bağlı özellikler sayfasıdır.The main documentation on required and optional properties and their interaction with nullable reference types is the Required and Optional Properties page. Önce bu sayfayı okuyarak başlatmanız önerilir.It is recommended you start out by reading that page first.

Not

Mevcut bir projede null yapılabilir başvuru türlerini etkinleştirirken dikkatli olun: daha önce isteğe bağlı olarak yapılandırılmış olan başvuru türü özellikleri, açıkça null olarak açıklanmadığı sürece artık gerekli olarak yapılandırılır.Exercise caution when enabling nullable reference types on an existing project: reference type properties which were previously configured as optional will now be configured as required, unless they are explicitly annotated to be nullable. İlişkisel bir veritabanı şemasını yönetirken, bu, veritabanı sütununun null değer alabilme durumu belirleyen geçişlerin oluşturulmasına neden olabilir.When managing a relational database schema, this may cause migrations to be generated which alter the database column's nullability.

DbContext ve DbSetDbContext and DbSet

Null yapılabilir başvuru türleri etkinleştirildiğinde, C# derleyicisi başlatılmamış null yapılamayan hiçbir özellik için uyarı yayar, çünkü bunlar null içerir.When nullable reference types are enabled, the C# compiler emits warnings for any uninitialized non-nullable property, as these would contain null. Sonuç olarak, bir bağlam türü üzerinde başlatılmamış DbSet özelliklerinin olmasının yaygın bir yöntemi artık bir uyarı oluşturacak.As a result, the common practice of having uninitialized DbSet properties on a context type will now generate a warning. Bunu yapmak için, DbSet özelliklerinizi salt okunurdur ve bunları şu şekilde başlatın:To fix this, make your DbSet properties read-only and initialize them as follows:

public class NullableReferenceTypesContext : DbContext
{
    public DbSet<Customer> Customers => Set<Customer>();
    public DbSet<Order> Orders => Set<Order>();

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        => optionsBuilder
            .UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=EFNullableReferenceTypes;Trusted_Connection=True;ConnectRetryCount=0");
}

Null yapılamayan Özellikler ve başlatmaNon-nullable properties and initialization

Başlatılmamış null yapılamayan başvuru türleri için derleyici uyarıları ayrıca varlık türlerinizin normal özellikleri için bir sorundur.Compiler warnings for uninitialized non-nullable reference types are also a problem for regular properties on your entity types. Yukarıdaki örnekte, null olamayan özelliklerle sorunsuz bir şekilde çalışacak, bu uyarıların her zaman başlatılmış olduğundan emin olmak için Oluşturucu bağlamayıkullanarak bu uyarıları önliyoruz.In our example above, we avoided these warnings by using constructor binding, a feature which works perfectly with non-nullable properties, ensuring they are always initialized. Ancak, bazı senaryolarda Oluşturucu bağlamasında bir seçenek olmaz: gezinti özellikleri, örneğin, bu şekilde başlatılamaz.However, in some scenarios constructor binding isn't an option: navigation properties, for example, cannot be initialized in this way.

Gerekli gezinti özellikleri ek bir zorluk sunsun: belirli bir sorumlu için her zaman bağımlı bir değere sahip olsa da, programda o noktadaki gereksinimlere bağlı olarak belirli bir sorgu tarafından yüklenemeyebilir veya yüklenmeyebilir (bkz. veri yükleme için farklı desenler).Required navigation properties present an additional difficulty: although a dependent will always exist for a given principal, it may or may not be loaded by a particular query, depending on the needs at that point in the program (see the different patterns for loading data). Aynı zamanda, gerekli olsa bile, bu özelliklerin null olup olmadığını denetlemek için tüm erişimleri zorlamadıklarından, bu özellikleri null yapılabilir hale getirmek de mümkün değildir.At the same time, it is undesirable to make these properties nullable, since that would force all access to them to check for null, even if they are required.

Bu senaryolarla başa çıkmak için bir yol, null yapılabilir bir yedekleme alanıolan null atanamaz bir özelliğe sahiptir:One way to deal with these scenarios, is to have a non-nullable property with a nullable backing field:

private Address? _shippingAddress;

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

Gezinti özelliği null değer atanamaz olduğundan, gerekli bir gezinti yapılandırılır; gezinti düzgün şekilde yüklendiği sürece, bağımlı olacaktır, özelliği aracılığıyla erişilebilir olur.Since the navigation property is non-nullable, a required navigation is configured; and as long as the navigation is properly loaded, the dependent will be accessible via the property. Ancak, özellikle ilgili varlığı doğru şekilde yüklemeden özelliğe erişildiğinde, API sözleşmesi yanlış kullanıldığı için bir InvalidOperationException atılır.If, however, the property is accessed without first properly loading the related entity, an InvalidOperationException is thrown, since the API contract has been used incorrectly. Değer, ayarlanmadığında bile değeri okuyabildiğine bağlı olarak, özelliği değil, her zaman yedekleme alanına erişecek şekilde yapılandırılmalıdır. Bunun nasıl yapılacağı hakkındaki alanları ve PropertyAccessMode.Field yapılandırmanın doğru olduğundan emin olmak için belirtmeyi göz önünde bulundurun.Note that EF must be configured to always access the backing field and not the property, as it relies on being able to read the value even when unset; consult the documentation on backing fields on how to do this, and consider specifying PropertyAccessMode.Field to make sure the configuration is correct.

Bir Terme alternatifi olarak, null-forverme işlecinin (!) yardımı ile özelliği null olarak başlatmak mümkündür:As a terser alternative, it is possible to simply initialize the property to null with the help of the null-forgiving operator (!):

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

Bir programlama hatasının sonucu haricinde gerçek bir null değer hiçbir zaman gözlemlenmeyecektir, örn. ilgili varlığı önceden düzgün şekilde yüklemeden gezinti özelliğine erişim.An actual null value will never be observed except as a result of a programming bug, e.g. accessing the navigation property without properly loading the related entity beforehand.

Not

Birden çok ilgili varlığa başvurular içeren koleksiyon gezginlerinin her zaman null değer atanamaz olması gerekir.Collection navigations, which contain references to multiple related entities, should always be non-nullable. Boş bir koleksiyon, ilgili hiçbir varlık olmadığı anlamına gelir, ancak listenin kendisi hiçbir şekilde null olmamalıdır.An empty collection means that no related entities exist, but the list itself should never be null.

İsteğe bağlı ilişkilerle ilgilenirken, gerçek bir null başvuru özel durumunun imkansız olacağı Derleyici uyarıları ile karşılaşmak mümkündür.When dealing with optional relationships, it's possible to encounter compiler warnings where an actual null reference exception would be impossible. LINQ EF Core sorgularınızı çevirirken ve yürütürken, isteğe bağlı bir ilgili varlık yoksa, bunun için herhangi bir gezinti yapmak yerine yalnızca yok sayılacak olur.When translating and executing your LINQ queries, EF Core guarantees that if an optional related entity does not exist, any navigation to it will simply be ignored, rather than throwing. Ancak, derleyici bu EF Core garantisi hakkında farkında değildir ve LINQ sorgusu bellekte yürütülürse, LINQ to Objects birlikte uyarılar üretir.However, the compiler is unaware of this EF Core guarantee, and produces warnings as if the LINQ query were executed in memory, with LINQ to Objects. Sonuç olarak, derleyiciye gerçek bir null değerin mümkün olmadığını bildirmek için null-forverme işlecinin (!) kullanılması gerekir:As a result, it is necessary to use the null-forgiving operator (!) to inform the compiler that an actual null value isn't possible:

Console.WriteLine(order.OptionalInfo!.ExtraAdditionalInfo!.SomeExtraAdditionalInfo);

İsteğe bağlı gezinmeler arasında birden çok ilişki düzeyi dahil edildiğinde de benzer bir sorun oluşur:A similar issue occurs when including multiple levels of relationships across optional navigations:

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

Bunu çok fazla fark ederseniz ve söz konusu varlık türleri EF Core sorgularda kullanılan ağırlıklı (veya özel olarak) ise, gezinti özelliklerini null değer atanamaz hale getirmeyi ve bunları akıcı API veya veri ek açıklamaları aracılığıyla isteğe bağlı olarak yapılandırmayı düşünün.If you find yourself doing this a lot, and the entity types in question are predominantly (or exclusively) used in EF Core queries, consider making the navigation properties non-nullable, and to configure them as optional via the Fluent API or Data Annotations. 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ışına çıkardığında, Özellikler null değer atanamaz olarak açıklansa da null değerler gözlemleyebilirsiniz.This will remove all compiler warnings while keeping the relationship optional; however, if your entities are traversed outside of EF Core, you may observe null values although the properties are annotated as non-nullable.

SınırlamalarLimitations

  • Tersine mühendislik Şu anda C# 8 Nullable başvuru türlerini (NRTs)desteklememektedir: EF Core her zaman özelliğin kapalı olduğunu varsayan c# kodu üretir.Reverse engineering does not currently support C# 8 nullable reference types (NRTs): EF Core always generates C# code that assumes the feature is off. Örneğin, null yapılabilir metin sütunları, string string? bir özelliğin gerekli olup olmadığını yapılandırmak Için kullanılan akıcı API veya veri ek açıklamaları ile değil, türünde bir özellik olarak yapı haline getirilecektir.For example, nullable text columns will be scaffolded as a property with type string , not string?, with either the Fluent API or Data Annotations used to configure whether a property is required or not. Yapı iskelesi kodunu düzenleyebilir ve bunları C# null olabilme ek açıklamalarıyla değiştirebilirsiniz.You can edit the scaffolded code and replace these with C# nullability annotations. Null yapılabilir başvuru türleri için yapı iskelesi desteği #15520soruna göre izlenir.Scaffolding support for nullable reference types is tracked by issue #15520.
  • EF Core genel API 'SI yüzeyi, null olabilme için henüz açıklanmamıştır (ortak API "null-yükümlülüğü"), bu, bazen NRT özelliği açık olduğunda daha fazla zaman alabilir.EF Core's public API surface has not yet been annotated for nullability (the public API is "null-oblivious"), making it sometimes awkward to use when the NRT feature is turned on. Bu özellikle, Firstordefaultasyncgibi EF Core tarafından sunulan zaman uyumsuz LINQ işleçlerini içerir.This notably includes the async LINQ operators exposed by EF Core, such as FirstOrDefaultAsync. Bu 5,0 sürümü için bu adresi ele almak için planlıyoruz.We plan to address this for the 5.0 release.