Skip to content

Commit 337c093

Browse files
author
Bart Koelman
committed
Documented required one-to-one relationships mapping
1 parent b643c06 commit 337c093

File tree

2 files changed

+81
-2
lines changed

2 files changed

+81
-2
lines changed

docs/usage/resources/relationships.md

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,84 @@ public class TodoItem : Identifiable<int>
2020

2121
The left side of this relationship is of type `TodoItem` (public name: "todoItems") and the right side is of type `Person` (public name: "persons").
2222

23+
### Required one-to-one relationships in Entity Framework Core
24+
25+
By default, Entity Framework Core generates an identifying foreign key for a required 1-to-1 relationship.
26+
This means no foreign key column is generated, instead the primary keys point to each other directly.
27+
28+
The next example defines that each car requires an engine, while an engine is optionally linked to a car.
29+
30+
```c#
31+
public sealed class Car : Identifiable<int>
32+
{
33+
[HasOne]
34+
public Engine Engine { get; set; }
35+
}
36+
37+
public sealed class Engine : Identifiable<int>
38+
{
39+
[HasOne]
40+
public Car Car { get; set; }
41+
}
42+
43+
public sealed class AppDbContext : DbContext
44+
{
45+
protected override void OnModelCreating(ModelBuilder builder)
46+
{
47+
builder.Entity<Car>()
48+
.HasOne(car => car.Engine)
49+
.WithOne(engine => engine.Car)
50+
.HasForeignKey<Car>()
51+
.IsRequired();
52+
}
53+
}
54+
```
55+
56+
Which results in EF Core generating the next database objects:
57+
```sql
58+
CREATE TABLE "Engine" (
59+
"Id" integer GENERATED BY DEFAULT AS IDENTITY,
60+
CONSTRAINT "PK_Engine" PRIMARY KEY ("Id")
61+
);
62+
CREATE TABLE "Cars" (
63+
"Id" integer NOT NULL,
64+
CONSTRAINT "PK_Cars" PRIMARY KEY ("Id"),
65+
CONSTRAINT "FK_Cars_Engine_Id" FOREIGN KEY ("Id") REFERENCES "Engine" ("Id")
66+
ON DELETE CASCADE
67+
);
68+
```
69+
70+
That mechanism does not make sense for JSON:API, because patching a relationship would result in also
71+
changing the identity of a resource. Naming the foreign key explicitly fixes the problem by forcing to
72+
create a foreign key column.
73+
74+
```c#
75+
protected override void OnModelCreating(ModelBuilder builder)
76+
{
77+
builder.Entity<Car>()
78+
.HasOne(car => car.Engine)
79+
.WithOne(engine => engine.Car)
80+
.HasForeignKey<Car>("EngineId") // Explicit foreign key name added
81+
.IsRequired();
82+
}
83+
```
84+
85+
Which generates the correct database objects:
86+
```sql
87+
CREATE TABLE "Engine" (
88+
"Id" integer GENERATED BY DEFAULT AS IDENTITY,
89+
CONSTRAINT "PK_Engine" PRIMARY KEY ("Id")
90+
);
91+
CREATE TABLE "Cars" (
92+
"Id" integer GENERATED BY DEFAULT AS IDENTITY,
93+
"EngineId" integer NOT NULL,
94+
CONSTRAINT "PK_Cars" PRIMARY KEY ("Id"),
95+
CONSTRAINT "FK_Cars_Engine_EngineId" FOREIGN KEY ("EngineId") REFERENCES "Engine" ("Id")
96+
ON DELETE CASCADE
97+
);
98+
CREATE UNIQUE INDEX "IX_Cars_EngineId" ON "Cars" ("EngineId");
99+
```
100+
23101
## HasMany
24102

25103
This exposes a to-many relationship.

test/JsonApiDotNetCoreTests/IntegrationTests/RequiredRelationships/DefaultBehaviorDbContext.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,9 @@ protected override void OnModelCreating(ModelBuilder builder)
2525

2626
// By default, Entity Framework Core generates an identifying foreign key for a required 1-to-1 relationship.
2727
// This means no foreign key column is generated, instead the primary keys point to each other directly.
28-
// That mechanism does not make sense for JSON:API, because patching a relationship would result in
29-
// also changing the identity of a resource. Naming the key explicitly forces to create a foreign key column.
28+
// That mechanism does not make sense for JSON:API, because patching a relationship would result in also
29+
// changing the identity of a resource. Naming the foreign key explicitly fixes the problem by forcing to
30+
// create a foreign key column.
3031
builder.Entity<Order>()
3132
.HasOne(order => order.Shipment)
3233
.WithOne(shipment => shipment.Order)

0 commit comments

Comments
 (0)