# Entity Framework Core Considerations

EF Core uses nullable annotations to infer database schema. Enabling NRTs in a project that uses EF Core has effects beyond compiler warnings:

- **Schema changes from annotations**: When NRTs are enabled, EF Core treats `string` properties as required (NOT NULL) columns and `string?` as optional (NULL) columns. If you enable NRTs on an existing model without reviewing every entity property, running `Add-Migration` can generate migrations that make previously nullable columns required — potentially causing data loss if those columns already store nulls.
- **Always review generated migrations**: After enabling NRTs on entity classes, run `Add-Migration` and carefully inspect the output before applying it. Look for unexpected `AlterColumn` calls that change column nullability.
- **Navigation properties**: Required navigation properties present a design choice because they are null until loaded. The official EF Core docs describe three approaches: **(a)** Non-nullable with `= null!` — appropriate when accessing an unloaded navigation is a programmer error; **(b)** Nullable (`public Order? Order { get; set; }`) — appropriate when code legitimately checks whether the navigation is loaded; **(c)** Non-nullable property wrapping a nullable backing field that throws `InvalidOperationException` on uninitialized access — the strictest pattern. Collection navigations should always be non-nullable (initialize to an empty collection, e.g., `= new List<Comment>()`; an empty collection means no related entities exist, but the list itself should never be null).
- **Migrate entity classes carefully**: Consider annotating entity model classes one at a time rather than enabling NRTs project-wide, to control the scope of schema impact.
- **Use `#nullable disable`, not `#nullable disable warnings` on entity files**: `#nullable disable warnings` only suppresses compiler warnings — the nullable annotations remain active and EF Core still reads them via reflection. This means properties without `?` are still treated as required, potentially altering schema. To fully opt entity files out of NRT effects, use `#nullable disable` which disables both warnings and the annotation context.
- **Private parameterless constructors — always pair `#pragma` disable with restore**: When suppressing CS8618 for a private parameterless constructor required by EF Core, always pair `#pragma warning disable CS8618` with `#pragma warning restore CS8618` immediately after the constructor. Without `restore`, the suppression leaks to all subsequent members in the file — any new property or constructor added later will silently skip the CS8618 check. Example:
  ```csharp
  #pragma warning disable CS8618 // Required by Entity Framework
  private Order() { }
  #pragma warning restore CS8618
  ```
  As an alternative, use `= null!` on each non-nullable property instead of a pragma — this is more explicit and does not risk suppression leakage, but is more verbose for entities with many properties.
- **DbSet properties**: Keep `DbSet<T>` properties non-nullable — EF Core always initializes them. EF Core 7.0+ (`.NET 7`) automatically suppresses CS8618 for DbSet properties. On older versions, initialize with `= null!` or use a read-only expression body: `public DbSet<Customer> Customers => Set<Customer>();`.
- **LINQ queries with optional navigations**: EF Core translates LINQ queries to SQL, so navigating through an optional relationship in `Where` or `Include` won't cause a `NullReferenceException` at runtime — EF handles the null case server-side. However, the compiler doesn't know this and will warn. Use the null-forgiving operator in these expressions: `.Where(o => o.OptionalNav!.Prop == "foo")` and `.Include(o => o.OptionalNav!).ThenInclude(n => n.Child)`.
