A navigation property represents a related entity or collection of entities in an Entity Framework Core model. A Product can have Category and Supplier properties alongside CategoryId and SupplierId; a Category or Supplier can have a Products collection. The ID properties identify relationships, while the navigation properties let code work with the related objects. section 18.2.2
A navigation property is not a promise that related data has been loaded. For example, a simple product lookup can return Category and Supplier as null. Use Include when the query needs related objects: context.Suppliers.Include(s => s.Products) loads the selected supplier’s products into its Products property. section 19.5.6 section 20.2
A supplier response that fails
A GET request to /api/suppliers/1 loads a Supplier with its Products collection. Entity Framework Core also assigns that supplier to each loaded Product.Supplier property. Returning the resulting object graph from a web service produces a JSON serialization error for an object cycle. The relationship is useful for querying, but returning the whole graph is a separate decision. section 20.2
For a response that does not need related objects, select only the required properties or configure JSON serialization to omit null properties. Projection controls the response explicitly; JsonIgnoreCondition.WhenWritingNull removes null-valued properties without loading anything. Neither changes which relationships Include follows. section 19.5.6 section 20.2
In an MVC view or Razor Page, asp-for can select a nested value such as Category.Name after the query includes Category. The view model is not serialized, so the web-service object-cycle problem does not arise there. Nullable navigation properties require care in model expressions: the null-conditional operator cannot be used, and the examples explicitly suppress warning CS8602 around those expressions. section 27.4.3
Listing 20.4 Related data in the SuppliersController.cs file in the Controllers folder
using Microsoft.AspNetCore.Mvc;
using WebApp.Models;
using Microsoft.EntityFrameworkCore;
namespace WebApp.Controllers {
[ApiController]
[Route("api/[controller]")]
public class SuppliersController : ControllerBase {
private DataContext context;
public SuppliersController(DataContext ctx) {
context = ctx;
}
[HttpGet("{id}")]
public async Task<Supplier?> GetSupplier(long id) {
return await context.Suppliers
.Include(s => s.Products)
.FirstAsync(s => s.SupplierId == id);
}
}
}
The supplier controller query shows the exact Include call that populates a collection navigation property before the response is serialized. section 20.2
Listing 27.21 Displaying related data in the Form.cshtml file in the Views/Form folder
@model Product
@{ Layout = "_SimpleLayout"; }
<h5 class="bg-primary text-white text-center p-2">HTML Form</h5>
<form asp-action="submitform" method="post" id="htmlform">
<div class="form-group">
<label>Id</label>
<input class="form-control" asp-for="ProductId" />
</div>
<div class="form-group">
<label>Name</label>
<input class="form-control" asp-for="Name" />
</div>
<div class="form-group">
<label>Price</label>
<input class="form-control" asp-for="Price" />
</div>
<div class="form-group">
<label>Category</label>
@{ #pragma warning disable CS8602 }
<input class="form-control" asp-for="Category.Name" />
@{ #pragma warning restore CS8602 }
</div>
<div class="form-group">
<label>Supplier</label>
@{ #pragma warning disable CS8602 }
<input class="form-control" asp-for="Supplier.Name" />
@{ #pragma warning restore CS8602 }
</div>
<button type="submit" class="btn btn-primary mt-2">Submit</button>
</form>
<button form="htmlform" asp-action="submitform"
class="btn btn-primary mt-2">
Submit (Outside Form)
</button>
The form view shows asp-for selecting a related entity’s Name and the local handling of its nullable-property warning. section 27.4.3


