System.InvalidOperationException: Navigation property 'X' is null. Related data must be loaded using Include.
ID: dotnet/ef-include-ignored
Version Compatibility
| Version | Status | Introduced | Deprecated | Notes |
|---|---|---|---|---|
| 8 | active | — | — | — |
Root Cause
This InvalidOperationException occurs in Entity Framework Core when accessing a navigation property that was not eagerly loaded via .Include() and lazy loading is not configured. In EF Core, navigation properties are null by default unless explicitly loaded. This commonly surfaces after migrating from EF6 (which had lazy loading by default), when projecting entities without their related data, or when accessing navigation properties in serialization (JSON, AutoMapper). The fix involves adding appropriate .Include()/.ThenInclude() calls, switching to explicit projections with .Select(), or enabling lazy loading proxies if appropriate.
genericWorkarounds
-
93% success Add explicit .Include() and .ThenInclude() calls for all required navigation properties
Chain Include() calls for each navigation property needed: var orders = await context.Orders.Include(o => o.Customer).Include(o => o.OrderItems).ThenInclude(oi => oi.Product).Where(o => o.Status == OrderStatus.Active).ToListAsync(); For deeply nested relationships, use ThenInclude() to traverse the chain. Create extension methods for commonly used include patterns: public static IQueryable<Order> WithFullDetails(this IQueryable<Order> query) => query.Include(o => o.Customer).Include(o => o.OrderItems).ThenInclude(oi => oi.Product);
-
92% success Use .Select() projections to load only the fields needed instead of full entity graphs
Instead of loading entire entities with Include(), project to DTOs: var orderDtos = await context.Orders.Where(o => o.Status == OrderStatus.Active).Select(o => new OrderDto { Id = o.Id, CustomerName = o.Customer.Name, ItemCount = o.OrderItems.Count, Total = o.OrderItems.Sum(oi => oi.Price * oi.Quantity) }).ToListAsync(); EF Core translates the Select() expression into a single SQL query that joins only the needed tables. This avoids the Include() problem entirely and is more efficient for read-only scenarios -
87% success Use AsSplitQuery() to optimize complex Include() chains that generate inefficient SQL
When multiple collection navigations cause cartesian explosion in a single query, split them: var orders = await context.Orders.Include(o => o.OrderItems).Include(o => o.Payments).Include(o => o.ShippingHistory).AsSplitQuery().ToListAsync(); This generates separate SQL queries for each Include() and assembles results in memory, avoiding the cartesian product. Configure globally in DbContext: optionsBuilder.UseSqlServer(conn, o => o.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery)); Note: split queries have different consistency guarantees than single queries
Dead Ends
Common approaches that don't work:
-
Making the navigation property non-nullable and expecting EF Core to auto-populate it
95% fail
EF Core does not guarantee population of navigation properties based on nullability annotations. A non-nullable reference navigation (e.g., public Order Order { get; set; } = null!;) will still be null if not loaded via Include(). The null-forgiving operator suppresses compiler warnings but does not cause EF Core to load the data. This leads to NullReferenceException at runtime instead of a clear error about missing Include()
-
Enabling lazy loading globally by adding UseLazyLoadingProxies() to solve all missing Include() issues
70% fail
Lazy loading introduces N+1 query problems that severely degrade performance. Each navigation property access triggers a separate database roundtrip. In list/collection scenarios, this can result in hundreds of queries instead of one. Additionally, lazy loading requires all navigation properties to be virtual, and it silently fails (returns null) when the DbContext has been disposed, such as after async operations or when entities are returned from a service layer
-
Adding null checks around every navigation property access to prevent the exception
85% fail
Null checks hide the fact that required related data was never loaded, leading to silent data loss and incorrect business logic. For example, if order.Customer is null because Include was missing, skipping the customer processing silently produces incomplete results. The null navigation property is a data loading bug, not a valid null state