Skip to content

Commit

Permalink
Improve the relationship docs
Browse files Browse the repository at this point in the history
Fixes #489
Fixes #1307
Fixes #1315
Fixes #1346
Fixes #1617
Fixes #1658
  • Loading branch information
AndriySvyryd committed Nov 26, 2019
1 parent bdccd6a commit c0fca12
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 49 deletions.
5 changes: 2 additions & 3 deletions entity-framework/core/modeling/relational/fk-constraints.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
---
title: Foreign Key Constraints - EF Core
author: rowanmiller
ms.date: 10/27/2016
ms.assetid: dbaf4bac-1fd5-46c0-ac57-64d7153bc574
author: AndriySvyryd
ms.date: 11/21/2019
uid: core/modeling/relational/fk-constraints
---
# Foreign Key Constraints
Expand Down
128 changes: 82 additions & 46 deletions entity-framework/core/modeling/relationships.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
---
title: Relationships - EF Core
author: rowanmiller
ms.date: 10/27/2016
ms.assetid: 0ff736a3-f1b0-4b58-a49c-4a7094bd6935
description: How to configure relationships between entity types when using Entity Framework Core
author: AndriySvyryd
ms.date: 11/21/2019
uid: core/modeling/relationships
---
# Relationships
Expand All @@ -12,27 +12,31 @@ A relationship defines how two entities relate to each other. In a relational da
> [!NOTE]
> Most of the samples in this article use a one-to-many relationship to demonstrate concepts. For examples of one-to-one and many-to-many relationships see the [Other Relationship Patterns](#other-relationship-patterns) section at the end of the article.
## Definition of Terms
## Definition of terms

There are a number of terms used to describe relationships

* **Dependent entity:** This is the entity that contains the foreign key property(s). Sometimes referred to as the 'child' of the relationship.
* **Dependent entity:** This is the entity that contains the foreign key properties. Sometimes referred to as the 'child' of the relationship.

* **Principal entity:** This is the entity that contains the primary/alternate key property(s). Sometimes referred to as the 'parent' of the relationship.
* **Principal entity:** This is the entity that contains the primary/alternate key properties. Sometimes referred to as the 'parent' of the relationship.

* **Foreign key:** The property(s) in the dependent entity that is used to store the values of the principal key property that the entity is related to.
* **Foreign key:** The properties in the dependent entity that are used to store the principal key values for the related entity.

* **Principal key:** The property(s) that uniquely identifies the principal entity. This may be the primary key or an alternate key.
* **Principal key:** The properties that uniquely identify the principal entity. This may be the primary key or an alternate key.

* **Navigation property:** A property defined on the principal and/or dependent entity that contains a reference(s) to the related entity(s).
* **Navigation property:** A property defined on the principal and/or dependent entity that references the related entity.

* **Collection navigation property:** A navigation property that contains references to many related entities.

* **Reference navigation property:** A navigation property that holds a reference to a single related entity.

* **Inverse navigation property:** When discussing a particular navigation property, this term refers to the navigation property on the other end of the relationship.

* **Self-referencing relationship:** A relationship in which the dependent and the principal entity types are the same.

The following code listing shows a one-to-many relationship between `Blog` and `Post`
The following code shows a one-to-many relationship between `Blog` and `Post`

[!code-csharp[Main](../../../samples/core/Modeling/Conventions/Relationships/Full.cs#Entities)]

* `Post` is the dependent entity

Expand All @@ -48,127 +52,159 @@ The following code listing shows a one-to-many relationship between `Blog` and `

* `Post.Blog` is the inverse navigation property of `Blog.Posts` (and vice versa)

[!code-csharp[Main](../../../samples/core/Modeling/Conventions/Relationships/Full.cs#Entities)]

## Conventions

By convention, a relationship will be created when there is a navigation property discovered on a type. A property is considered a navigation property if the type it points to can not be mapped as a scalar type by the current database provider.
By default, a relationship will be created when there is a navigation property discovered on a type. A property is considered a navigation property if the type it points to can not be mapped as a scalar type by the current database provider.

> [!NOTE]
> Relationships that are discovered by convention will always target the primary key of the principal entity. To target an alternate key, additional configuration must be performed using the Fluent API.
### Fully Defined Relationships
### Fully defined relationships

The most common pattern for relationships is to have navigation properties defined on both ends of the relationship and a foreign key property defined in the dependent entity class.

* If a pair of navigation properties is found between two types, then they will be configured as inverse navigation properties of the same relationship.

* If the dependent entity contains a property named `<primary key property name>`, `<navigation property name><primary key property name>`, or `<principal entity name><primary key property name>` then it will be configured as the foreign key.
* If the dependent entity contains a property with a name mathing one of these patterns then it will be configured as the foreign key:
* `<navigation property name><principal key property name>`
* `<navigation property name>Id`
* `<principal entity name><principal key property name>`
* `<principal entity name>Id`

[!code-csharp[Main](../../../samples/core/Modeling/Conventions/Relationships/Full.cs?name=Entities&highlight=6,15,16)]

> [!WARNING]
> If there are multiple navigation properties defined between two types (that is, more than one distinct pair of navigations that point to each other), then no relationships will be created by convention and you will need to manually configure them to identify how the navigation properties pair up.
In this example the highlighted properties will be used to configure the relationship.

### No Foreign Key Property
> [!NOTE]
> If the property is the primary key or is of a type not compatible with the principal key then it won't be configured as the foreign key.
While it is recommended to have a foreign key property defined in the dependent entity class, it is not required. If no foreign key property is found, a shadow foreign key property will be introduced with the name `<navigation property name><principal key property name>` (see [Shadow Properties](shadow-properties.md) for more information).
> [!NOTE]
> Before EF Core 3.0 the property named exactly the same as the principal key property [was also matched as the foreign key](https://github.com/aspnet/EntityFrameworkCore/issues/13274)
### No foreign key property

While it is recommended to have a foreign key property defined in the dependent entity class, it is not required. If no foreign key property is found, a [shadow foreign key property](shadow-properties.md) will be introduced with the name `<navigation property name><principal key property name>` or `<principal entity name><principal key property name>` if no navigation is present on the dependent type.

[!code-csharp[Main](../../../samples/core/Modeling/Conventions/Relationships/NoForeignKey.cs?name=Entities&highlight=6,15)]

### Single Navigation Property
In this example the shadow foreign key is `BlogId` because prepending the navigation name would be redundant.

> [!NOTE]
> If a property with the same name already exists then the shadow property name will be suffixed with a number.
### Single navigation property

Including just one navigation property (no inverse navigation, and no foreign key property) is enough to have a relationship defined by convention. You can also have a single navigation property and a foreign key property.

[!code-csharp[Main](../../../samples/core/Modeling/Conventions/Relationships/OneNavigation.cs?name=Entities&highlight=6)]

### Cascade Delete
### Limitations

When there are multiple navigation properties defined between two types (that is, more than just one pair of navigations that point to each other) the relationships represented by the navigation properties are ambiguous. You will need to manually configure them to resolve the ambiguity.

### Cascade delete

By convention, cascade delete will be set to *Cascade* for required relationships and *ClientSetNull* for optional relationships. *Cascade* means dependent entities are also deleted. *ClientSetNull* means that dependent entities that are not loaded into memory will remain unchanged and must be manually deleted, or updated to point to a valid principal entity. For entities that are loaded into memory, EF Core will attempt to set the foreign key properties to null.

See the [Required and Optional Relationships](#required-and-optional-relationships) section for the difference between required and optional relationships.

See [Cascade Delete](../saving/cascade-delete.md) for more details about the different delete behaviors and the defaults used by convention.

## Data Annotations

There are two data annotations that can be used to configure relationships, `[ForeignKey]` and `[InverseProperty]`. These are available in the `System.ComponentModel.DataAnnotations.Schema` namespace.
## Manual configuration

### [ForeignKey]

You can use the Data Annotations to configure which property should be used as the foreign key property for a given relationship. This is typically done when the foreign key property is not discovered by convention.
#### [Fluent API](#tab/fluent-api)

[!code-csharp[Main](../../../samples/core/Modeling/DataAnnotations/Relationships/ForeignKey.cs?highlight=30)]
To configure a relationship in the Fluent API, you start by identifying the navigation properties that make up the relationship. `HasOne` or `HasMany` identifies the navigation property on the entity type you are beginning the configuration on. You then chain a call to `WithOne` or `WithMany` to identify the inverse navigation. `HasOne`/`WithOne` are used for reference navigation properties and `HasMany`/`WithMany` are used for collection navigation properties.

> [!TIP]
> The `[ForeignKey]` annotation can be placed on either navigation property in the relationship. It does not need to go on the navigation property in the dependent entity class.
[!code-csharp[Main](../../../samples/core/Modeling/FluentAPI/Relationships/NoForeignKey.cs?highlight=14-16)]

### [InverseProperty]
#### [Data annotations](#tab/data-annotations)

You can use the Data Annotations to configure how navigation properties on the dependent and principal entities pair up. This is typically done when there is more than one pair of navigation properties between two entity types.

[!code-csharp[Main](../../../samples/core/Modeling/DataAnnotations/Relationships/InverseProperty.cs?highlight=33,36)]

## Fluent API
> [!NOTE]
> You can only use [Required] on properties on the dependent entity to impact the requiredness of the relationship. [Required] on the navigation from the principal entity is usually ignored, but it may cause the entity to become the dependent one.
To configure a relationship in the Fluent API, you start by identifying the navigation properties that make up the relationship. `HasOne` or `HasMany` identifies the navigation property on the entity type you are beginning the configuration on. You then chain a call to `WithOne` or `WithMany` to identify the inverse navigation. `HasOne`/`WithOne` are used for reference navigation properties and `HasMany`/`WithMany` are used for collection navigation properties.
> [!NOTE]
> The data annotations `[ForeignKey]` and `[InverseProperty]` are available in the `System.ComponentModel.DataAnnotations.Schema` namespace. `[Required]` is available in the `System.ComponentModel.DataAnnotations` namespace.
[!code-csharp[Main](../../../samples/core/Modeling/FluentAPI/Relationships/NoForeignKey.cs?highlight=14-16)]
---

### Single Navigation Property
### Single navigation property

If you only have one navigation property then there are parameterless overloads of `WithOne` and `WithMany`. This indicates that there is conceptually a reference or collection on the other end of the relationship, but there is no navigation property included in the entity class.

[!code-csharp[Main](../../../samples/core/Modeling/FluentAPI/Relationships/OneNavigation.cs?highlight=14-16)]

### Foreign Key
### Foreign key

#### [Fluent API](#tab/fluent-api)

You can use the Fluent API to configure which property should be used as the foreign key property for a given relationship.

[!code-csharp[Main](../../../samples/core/Modeling/FluentAPI/Relationships/ForeignKey.cs?highlight=17)]

The following code listing shows how to configure a composite foreign key.
#### [Data annotations](#tab/data-annotations)

You can use the Data Annotations to configure which property should be used as the foreign key property for a given relationship. This is typically done when the foreign key property is not discovered by convention.

[!code-csharp[Main](../../../samples/core/Modeling/DataAnnotations/Relationships/ForeignKey.cs?highlight=30)]

> [!TIP]
> The `[ForeignKey]` annotation can be placed on either navigation property in the relationship. It does not need to go on the navigation property in the dependent entity class.
> [!NOTE]
> The property specified using `[ForeignKey]` on a navigation property doesn't need to exist the the dependent type. In this case the specified name will be used to create a shadow foreign key.
---

The following code shows how to configure a composite foreign key.

[!code-csharp[Main](../../../samples/core/Modeling/FluentAPI/Relationships/CompositeForeignKey.cs?highlight=20)]

You can use the string overload of `HasForeignKey(...)` to configure a shadow property as a foreign key (see [Shadow Properties](shadow-properties.md) for more information). We recommend explicitly adding the shadow property to the model before using it as a foreign key (as shown below).

[!code-csharp[Main](../../../samples/core/Modeling/FluentAPI/Relationships/ShadowForeignKey.cs#Sample)]

### Without Navigation Property
### Without navigation property

You don't necessarily need to provide a navigation property. You can simply provide a Foreign Key on one side of the relationship.
You don't necessarily need to provide a navigation property. You can simply provide a foreign key on one side of the relationship.

[!code-csharp[Main](../../../samples/core/Modeling/FluentAPI/Relationships/NoNavigation.cs?highlight=14-17)]

### Principal Key
### Principal key

If you want the foreign key to reference a property other than the primary key, you can use the Fluent API to configure the principal key property for the relationship. The property that you configure as the principal key will automatically be setup as an alternate key (see [Alternate Keys](alternate-keys.md) for more information).
If you want the foreign key to reference a property other than the primary key, you can use the Fluent API to configure the principal key property for the relationship. The property that you configure as the principal key will automatically be setup as an [alternate key](alternate-keys.md).

[!code-csharp[Main](../../../samples/core/Modeling/FluentAPI/Relationships/PrincipalKey.cs?name=PrincipalKey&highlight=11)]

The following code listing shows how to configure a composite principal key.
The following code shows how to configure a composite principal key.

[!code-csharp[Main](../../../samples/core/Modeling/FluentAPI/Relationships/CompositePrincipalKey.cs?name=Composite&highlight=11)]

> [!WARNING]
> The order in which you specify principal key properties must match the order in which they are specified for the foreign key.
### Required and Optional Relationships
### Required and optional relationships

You can use the Fluent API to configure whether the relationship is required or optional. Ultimately this controls whether the foreign key property is required or optional. This is most useful when you are using a shadow state foreign key. If you have a foreign key property in your entity class then the requiredness of the relationship is determined based on whether the foreign key property is required or optional (see [Required and Optional properties](required-optional.md) for more information).

[!code-csharp[Main](../../../samples/core/Modeling/FluentAPI/Relationships/Required.cs?name=Required&highlight=11)]

### Cascade Delete
> [!NOTE]
> Calling `IsRequired(false)` also makes the foreign key property optional unless it's configured otherwise.
### Cascade delete

You can use the Fluent API to configure the cascade delete behavior for a given relationship explicitly.

See [Cascade Delete](../saving/cascade-delete.md) on the Saving Data section for a detailed discussion of each option.
See [Cascade Delete](../saving/cascade-delete.md) for a detailed discussion of each option.

[!code-csharp[Main](../../../samples/core/Modeling/FluentAPI/Relationships/CascadeDelete.cs?name=CascadeDelete&highlight=11)]

## Other Relationship Patterns
## Other relationship patterns

### One-to-one

Expand Down

0 comments on commit c0fca12

Please sign in to comment.