System.InvalidOperationException: Unable to determine the relationship represented by navigation 'Customer.Orders' of type 'List<Order>'.
— Order had two navigations back to Customer
(Buyer and Seller), and Customer.Orders could pair
with either. Say which one with [InverseProperty(nameof(Order.Buyer))] on
Orders.
The message itself offers two ways out: configure the relationship, or ignore the property
with [NotMapped] or Ignore. When the collection is meant to hold
data, configuring it is the one you want; the attribute below is the shortest form.
The error
PS> dotnet run
Unhandled exception. System.InvalidOperationException: Unable to determine the relationship represented by navigation 'Customer.Orders' of type 'List<Order>'. Either manually configure the relationship, or ignore this property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
at Microsoft.EntityFrameworkCore.Infrastructure.ModelValidator.ValidatePropertyMapping(IConventionTypeBase structuralType, IConventionModel model, IDiagnosticsLogger`1 logger)
...
at Program.<Main>$(String[] args) in ...\EfRel\Program.cs:line 5
The cut hides EF Core and DI frames.
Line 5 is db.Database.EnsureDeleted(), the first call that needed the model,
and the top frame is ModelValidator: the model was rejected before any SQL
ran.
Why it happens
public class Customer
{
public int Id { get; set; }
public string Name { get; set; } = "";
public List<Order> Orders { get; set; } = [];
}
public class Order
{
public int Id { get; set; }
public Customer Buyer { get; set; } = null!;
public Customer Seller { get; set; } = null!;
}
An order has a buyer and a seller, both customers. Customer.Orders is a
collection of orders, but nothing in the classes says whether it lists the orders a
customer bought or the ones they sold. In this run EF Core rejected the model and named the
navigation it could not place.
The message names only one side: Customer.Orders, class first, and its
declared type List<Order>. Buyer and Seller
do not appear in it, so the competing navigations have to be found by opening the class
the collection points to, here Order, and looking for every property of type
Customer.
The fix
[InverseProperty(nameof(Order.Buyer))]
public List<Order> Orders { get; set; } = [];
PS> dotnet run -p:DefineConstants=FIXED
Maria Garcia bought 1 order(s)
The attribute (from System.ComponentModel.DataAnnotations.Schema) pairs
Customer.Orders with Order.Buyer. In the lab both versions sit in
one file behind #if FIXED, and -p:DefineConstants=FIXED compiles
the attribute in. With it, the program created the database, saved one order with Maria
Garcia as buyer and David Chen as seller, and loaded Maria with
Include(c => c.Orders): one order. Seller stayed a navigation
without a collection on the other side, and the model accepted that.
How it was reproduced
A console project from dotnet new console -n EfRel on .NET SDK 10.0.403 with
Microsoft.EntityFrameworkCore.Sqlite 10.0.14 and a SQLite file shop.db.
dotnet run without the attribute threw at the first database call;
dotnet run -p:DefineConstants=FIXED printed the order count. The two
customers are fictional.
Frequently asked
- What does Unable to determine the relationship represented by navigation mean?
- EF Core could not decide which navigation on the other entity a collection or reference pairs with. In the reproduction Order had two Customer navigations, Buyer and Seller, and Customer.Orders matched both.
- How do I fix Unable to determine the relationship with InverseProperty?
- Put [InverseProperty(nameof(Order.Buyer))] on the collection, naming the navigation on the other class it belongs to. In the reproduction that alone made the model valid.
- When does EF Core throw Unable to determine the relationship?
- In the reproduction it was thrown by ModelValidator at the first database call, EnsureDeleted, before any SQL ran.
More decoded errors in the Fixes category.