diff --git a/entity-framework/core/miscellaneous/nullable-reference-types.md b/entity-framework/core/miscellaneous/nullable-reference-types.md index a483c625be..dbe659061e 100644 --- a/entity-framework/core/miscellaneous/nullable-reference-types.md +++ b/entity-framework/core/miscellaneous/nullable-reference-types.md @@ -7,7 +7,7 @@ uid: core/miscellaneous/nullable-reference-types --- # Working with Nullable Reference Types -C# 8 introduced a new feature called [nullable reference types (NRT)](/dotnet/csharp/tutorials/nullable-reference-types), allowing reference types to be annotated, indicating whether it is valid for them to contain null or not. If you are new to this feature, it is recommended that make yourself familiar with it by reading the C# docs. Nullable reference types are enabled by default in new project templates, but remain disabled in existing projects unless explicitly opted into. +C# 8 introduced a new feature called [nullable reference types (NRT)](/dotnet/csharp/tutorials/nullable-reference-types), allowing reference types to be annotated, indicating whether it is valid for them to contain `null` or not. If you are new to this feature, it is recommended that make yourself familiar with it by reading the C# docs. Nullable reference types are enabled by default in new project templates, but remain disabled in existing projects unless explicitly opted into. This page introduces EF Core's support for nullable reference types, and describes best practices for working with them. @@ -20,7 +20,7 @@ The main documentation on required and optional properties and their interaction ## Non-nullable properties and initialization -When nullable reference types are enabled, the C# compiler emits warnings for any uninitialized non-nullable property, as these would contain null. As a result, the following, common way of writing entity types cannot be used: +When nullable reference types are enabled, the C# compiler emits warnings for any uninitialized non-nullable property, as these would contain `null`. As a result, the following, common way of writing entity types cannot be used: ```csharp public class Customer @@ -44,15 +44,18 @@ If you're using an older version of C#, [Constructor binding](xref:core/modeling [!code-csharp[Main](../../../samples/core/Miscellaneous/NullableReferenceTypes/CustomerWithConstructorBinding.cs?name=CustomerWithConstructorBinding&highlight=6-9)] -Unfortunately, in some scenarios constructor binding isn't an option; navigation properties, for example, cannot be initialized in this way. In those cases, you can simply initialize the property to null with the help of the null-forgiving operator (but see below for more details): +Unfortunately, in some scenarios constructor binding isn't an option; navigation properties, for example, cannot be initialized in this way. In those cases, you can simply initialize the property to `null` with the help of the null-forgiving operator (but see below for more details): [!code-csharp[Main](../../../samples/core/Miscellaneous/NullableReferenceTypes/Order.cs?range=19)] ### Required navigation properties -Required navigation properties present an additional difficulty: although a dependent will always exist for a given principal, it may or may not be loaded by a particular query, depending on the needs at that point in the program ([see the different patterns for loading data](xref:core/querying/related-data)). At the same time, it is undesirable to make these properties nullable, since that would force all access to them to check for null, even if they are required. +Required navigation properties present an additional difficulty: although a dependent will always exist for a given principal, it may or may not be loaded by a particular query, depending on the needs at that point in the program ([see the different patterns for loading data](xref:core/querying/related-data)). At the same time, it may be undesirable to make these properties nullable, since that would force all access to them to check for `null`, even when the navigation is known to be loaded and therefore cannot be `null`. -This isn't necessarily a problem! As long as a required dependent is properly loaded (e.g. via `Include`), accessing its navigation property is guaranteed to always return non-null. On the other hand, accessing a navigation property without first loading the dependent is a programmer error; as such, it may be acceptable for the navigation property to return null, and when for the (buggy) code to throw a `NullReferenceException`. After all, the code is using EF incorrectly. +This isn't necessarily a problem! As long as a required dependent is properly loaded (e.g. via `Include`), accessing its navigation property is guaranteed to always return non-null. On the other hand, the application may choose to check whether or not the relationship is loaded by checking if the navigation is `null`. In such cases, it is reasonable to make the navigation nullable. This means that required navigations from the dependent to the principal: + +- Should be non-nullable if it is considered a programmer error to access a navigation when it is not loaded. +- Should be nullable if it acceptable for application code to check the navigation to determine whether or not the relationship is loaded. If you'd like a stricter approach, you can have a non-nullable property with a nullable [backing field](xref:core/modeling/backing-field): @@ -61,7 +64,7 @@ If you'd like a stricter approach, you can have a non-nullable property with a n As long as the navigation is properly loaded, the dependent will be accessible via the property. If, however, the property is accessed without first properly loading the related entity, an `InvalidOperationException` is thrown, since the API contract has been used incorrectly. > [!NOTE] -> Collection navigations, which contain references to multiple related entities, should always be non-nullable. An empty collection means that no related entities exist, but the list itself should never be null. +> Collection navigations, which contain references to multiple related entities, should always be non-nullable. An empty collection means that no related entities exist, but the list itself should never be `null`. ## DbContext and DbSet @@ -69,11 +72,11 @@ The common practice of having uninitialized DbSet properties on context types is [!code-csharp[Main](../../../samples/core/Miscellaneous/NullableReferenceTypes/NullableReferenceTypesContext.cs?name=Context&highlight=3-4)] -Another strategy is to use non-nullable auto-properties, but to initialize them to null, using the null-forgiving operator (!) to silence the compiler warning. The DbContext base constructor ensures that all DbSet properties will get initialized, and null will never be observed on them. +Another strategy is to use non-nullable auto-properties, but to initialize them to `null`, using the null-forgiving operator (!) to silence the compiler warning. The DbContext base constructor ensures that all DbSet properties will get initialized, and null will never be observed on them. ## Navigating and including nullable relationships -When dealing with optional relationships, it's possible to encounter compiler warnings where an actual null reference exception would be impossible. When translating and executing your LINQ queries, EF Core guarantees that if an optional related entity does not exist, any navigation to it will simply be ignored, rather than throwing. However, the compiler is unaware of this EF Core guarantee, and produces warnings as if the LINQ query were executed in memory, with LINQ to Objects. As a result, it is necessary to use the null-forgiving operator (!) to inform the compiler that an actual null value isn't possible: +When dealing with optional relationships, it's possible to encounter compiler warnings where an actual `null` reference exception would be impossible. When translating and executing your LINQ queries, EF Core guarantees that if an optional related entity does not exist, any navigation to it will simply be ignored, rather than throwing. However, the compiler is unaware of this EF Core guarantee, and produces warnings as if the LINQ query were executed in memory, with LINQ to Objects. As a result, it is necessary to use the null-forgiving operator (!) to inform the compiler that an actual `null` value isn't possible: [!code-csharp[Main](../../../samples/core/Miscellaneous/NullableReferenceTypes/Program.cs?name=Navigating)] @@ -81,11 +84,11 @@ A similar issue occurs when including multiple levels of relationships across op [!code-csharp[Main](../../../samples/core/Miscellaneous/NullableReferenceTypes/Program.cs?name=Including&highlight=2)] -If you find yourself doing this a lot, and the entity types in question are predominantly (or exclusively) used in EF Core queries, consider making the navigation properties non-nullable, and to configure them as optional via the Fluent API or Data Annotations. This will remove all compiler warnings while keeping the relationship optional; however, if your entities are traversed outside of EF Core, you may observe null values although the properties are annotated as non-nullable. +If you find yourself doing this a lot, and the entity types in question are predominantly (or exclusively) used in EF Core queries, consider making the navigation properties non-nullable, and to configure them as optional via the Fluent API or Data Annotations. This will remove all compiler warnings while keeping the relationship optional; however, if your entities are traversed outside of EF Core, you may observe `null` values although the properties are annotated as non-nullable. ## Limitations in older versions Prior to EF Core 6.0, the following limitations applied: -* The public API surface wasn't annotated for nullability (the public API was "null-oblivious"), making it sometimes awkward to use when the NRT feature is turned on. This notably includes the async LINQ operators exposed by EF Core, such as [FirstOrDefaultAsync](/dotnet/api/microsoft.entityframeworkcore.entityframeworkqueryableextensions.firstordefaultasync). The public API is fully annotated for nullability starting with EF Core 6.0. -* Reverse engineering did not support [C# 8 nullable reference types (NRTs)](/dotnet/csharp/tutorials/nullable-reference-types): EF Core always generated C# code that assumed the feature is off. For example, nullable text columns were scaffolded as a property with type `string` , not `string?`, with either the Fluent API or Data Annotations used to configure whether a property is required or not. If using an older version of EF Core, you can still edit the scaffolded code and replace these with C# nullability annotations. +- The public API surface wasn't annotated for nullability (the public API was "null-oblivious"), making it sometimes awkward to use when the NRT feature is turned on. This notably includes the async LINQ operators exposed by EF Core, such as [FirstOrDefaultAsync](/dotnet/api/microsoft.entityframeworkcore.entityframeworkqueryableextensions.firstordefaultasync). The public API is fully annotated for nullability starting with EF Core 6.0. +- Reverse engineering did not support [C# 8 nullable reference types (NRTs)](/dotnet/csharp/tutorials/nullable-reference-types): EF Core always generated C# code that assumed the feature is off. For example, nullable text columns were scaffolded as a property with type `string` , not `string?`, with either the Fluent API or Data Annotations used to configure whether a property is required or not. If using an older version of EF Core, you can still edit the scaffolded code and replace these with C# nullability annotations. diff --git a/entity-framework/core/modeling/owned-entities.md b/entity-framework/core/modeling/owned-entities.md index d8e22d9367..2a067aa1bf 100644 --- a/entity-framework/core/modeling/owned-entities.md +++ b/entity-framework/core/modeling/owned-entities.md @@ -38,7 +38,7 @@ The model above is mapped to the following database schema: See the [full sample project](https://github.com/dotnet/EntityFramework.Docs/tree/main/samples/core/Modeling/OwnedEntities) for more context. > [!TIP] -> The owned entity type can be marked as required, see [Required one-to-one dependents](xref:core/modeling/relationships#one-to-one) for more information. +> The owned entity type can be marked as required, see [Required one-to-one dependents](xref:core/modeling/relationships/navigations#required-navigations) for more information. ## Implicit keys @@ -120,7 +120,7 @@ Notice the `WithOwner` call used to define the navigation property pointing back It is also possible to achieve this result using `OwnedAttribute` on both `OrderDetails` and `StreetAddress`. -In addition, notice the `Navigation` call. Navigation properties to owned types can be further configured [as for non-owned navigation properties](xref:core/modeling/relationships#configuring-navigation-properties). +In addition, notice the `Navigation` call. Navigation properties to owned types can be further configured [as for non-owned navigation properties](xref:core/modeling/relationships/navigations#required-navigations). The model above is mapped to the following database schema: diff --git a/entity-framework/core/modeling/relationships.md b/entity-framework/core/modeling/relationships.md index 0d2a3fd8a0..8bbd02c425 100644 --- a/entity-framework/core/modeling/relationships.md +++ b/entity-framework/core/modeling/relationships.md @@ -1,338 +1,96 @@ --- title: Relationships - EF Core description: How to configure relationships between entity types when using Entity Framework Core -author: AndriySvyryd -ms.date: 10/14/2022 +author: ajcvickers +ms.date: 02/25/2023 uid: core/modeling/relationships --- # Relationships -A relationship defines how two entities relate to each other. In a relational database, this is represented by a foreign key constraint. - -> [!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. +A relationship defines how two entities relate to each other. In the EF Core model, a relationship is made up from one or more foreign key properties, together with optional navigation properties ("navigations") that connect the entity types. ## Definition of terms -There are a number of terms used to describe relationships - -* **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 properties. Sometimes referred to as the 'parent' of the relationship. - -* **Principal key:** The properties that uniquely identify the principal entity. This may be the primary key or an alternate key. - -* **Foreign key:** The properties in the dependent entity that are used to store the principal key values for the related entity. - -* **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 shows a one-to-many relationship between `Blog` and `Post` - -[!code-csharp[Main](../../../samples/core/Modeling/Relationships/Full.cs#Full)] - -* `Post` is the dependent entity - -* `Blog` is the principal entity - -* `Blog.BlogId` is the principal key (in this case it is a primary key rather than an alternate key) - -* `Post.BlogId` is the foreign key - -* `Post.Blog` is a reference navigation property - -* `Blog.Posts` is a collection navigation property - -* `Post.Blog` is the inverse navigation property of `Blog.Posts` (and vice versa) - -## Conventions - -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 cannot 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 - -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 with a name matching one of these patterns then it will be configured as the foreign key: - * `` - * `Id` - * `` - * `Id` - -[!code-csharp[Main](../../../samples/core/Modeling/Relationships/Full.cs?name=Full&highlight=6,15-16)] - -In this example the highlighted properties will be used to configure the relationship. - -> [!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. - -### 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](xref:core/modeling/shadow-properties) will be introduced with the name `` or `` if no navigation is present on the dependent type. - -[!code-csharp[Main](../../../samples/core/Modeling/Relationships/NoForeignKey.cs?name=NoForeignKey&highlight=6,15)] +There are a number of terms used to describe relationships. It is not necessary to understand all these terms up-front. Refer back here as needed when reading the relationships documentation pages. -In this example, the shadow foreign key is `BlogId` because prepending the navigation name would be redundant. +- **Dependent entity:** This is the entity that contains the foreign key property or properties. A dependent is sometimes called a "child". -> [!NOTE] -> If a property with the same name already exists, then the shadow property name will be suffixed with a number. +- **Principal entity:** This is the entity that contains the primary/alternate key property or properties. A principal is sometimes called the "parent". -### Single navigation property +- **Principal key:** The property or properties whose values uniquely identify the principal entity. The principal key may be the primary key or an alternate key. -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. +- **Foreign key:** The property or properties of the dependent entity type that are used to store the key values that match the principal key values of the related principal entity. -[!code-csharp[Main](../../../samples/core/Modeling/Relationships/OneNavigation.cs?name=OneNavigation&highlight=6)] +- **Navigation:** A property on the entity on one side of the relationship that references the related entity or entities at the other end of the relationship. -### Limitations + - **Collection navigation:** A navigation that contains references to many related entities. Used to reference the "many" side(s) of one-to-many and many-to-many relationships. -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. + - **Reference navigation:** A navigation that holds a reference to a single related entity. Used to reference the "one" side(s) of one-to-one and one-to-many relationships. -### Cascade delete + - **Inverse navigation:** When discussing a particular navigation, this term refers to the navigation on the other end of the relationship. -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. +- **Self-referencing relationship:** A relationship in which the dependent and the principal entity types are the same. -See the [Required and Optional Relationships](#required-and-optional-relationships) section for the difference between required and optional relationships. +- **Required relationship** A relationship represented by a non-nullable foreign key. A dependent entity in a required relationship cannot exist without a principal entity to which it refers. -See [Cascade Delete](xref:core/saving/cascade-delete) for more details about the different delete behaviors and the defaults used by convention. +- **Optional relationship** A relationship represented by a nullable foreign key. A dependent entity in an optional relationship can exist without referring to any principal entity. -## Manual configuration +- **Bidirectional relationship** A relationship that has navigations on both sides of the relationship. -### [Fluent API](#tab/fluent-api) +- **Unidirectional relationship** A relationship that has a navigation on one side of the relationship, but no navigation on the other side. -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. +## Cardinality -[!code-csharp[Main](../../../samples/core/Modeling/Relationships/FluentAPI/NoForeignKey.cs?name=NoForeignKey&highlight=8-10)] +EF supports three basic forms of relationship between entities: -### [Data annotations](#tab/data-annotations) +- In [one-to-many](xref:core/modeling/relationships/one-to-many) relationships, a single entity is associated with any number of other entities. For example, a `Blog` can have many associated `Posts`, but each `Post` is associated with only one `Blog`. +- In [one-to-one](xref:core/modeling/relationships/one-to-one) relationships, a single entity is associated with another single entity. For example, a `Blog` has one `BlogHeader`, and that `BlogHeader` belongs to a single `Blog`. +- In [many-to-many](xref:core/modeling/relationships/many-to-many) relationships, any number entities are associated with any number of other entities. For example, a `Post` can have many associated `Tags`, and each `Tag` can in turn be associated with many `Posts`. -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. +See the linked documentation pages for more details and examples of each of these different kinds of relationships. -[!code-csharp[Main](../../../samples/core/Modeling/Relationships/DataAnnotations/InverseProperty.cs?name=InverseProperty&highlight=20,23)] +### Optional and required relationships -> [!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. +For one-to-many and one-to-one relationships, the relationship can be either "optional" or "required". In required relationships, for the dependent(s) (child/children) to exist, the principal (parent) of the relationship _must_ exist. In optional relationships, the dependent(s) (child/children) may exist without any principal (parent). This makes optional relationships equivalent to "0..1 to 1" or "0..1 to many", although they are not referred to as such in the EF Core APIs or documentation. > [!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. - ---- +> EF does not, in general, support required dependents, where the principal entity cannot exist without its dependents. See [_Required navigations_](xref:core/modeling/relationships/navigations#required-navigations) for more information. -### Single navigation property +## Foreign keys and navigations -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. +At the most basic level, EF relationships are defined by foreign keys on a dependent entity type that reference primary or alternate keys on a principal entity type. Examples of how foreign keys are used can be found in the documentation for [one-to-many](xref:core/modeling/relationships/one-to-many), [one-to-one](xref:core/modeling/relationships/one-to-one), and [many-to-many](xref:core/modeling/relationships/many-to-many) relationships. [_Foreign principal keys in relationships_](xref:core/modeling/relationships/foreign-and-principal-keys) covers more specific information about how foreign keys map to the database. -[!code-csharp[Main](../../../samples/core/Modeling/Relationships/FluentAPI/OneNavigation.cs?name=OneNavigation&highlight=8-10)] - ---- - -### Configuring navigation properties - -After the navigation property has been created, you may need to further configure it. - -[!code-csharp[Main](../../../samples/core/Modeling/Relationships/FluentAPI/NavigationConfiguration.cs?name=NavigationConfiguration&highlight=7-9)] - -> [!TIP] -> Non-collection navigations can also be marked as required, see [Required one-to-one dependents](xref:core/modeling/relationships#one-to-one) for more information. - -> [!NOTE] -> This call cannot be used to create a navigation property. It is only used to configure a navigation property which has been previously created by defining a relationship or from a convention. - -### Foreign key - -#### [Fluent API (simple key)](#tab/fluent-api-simple-key) - -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/Relationships/FluentAPI/ForeignKey.cs?name=ForeignKey&highlight=11)] - -#### [Fluent API (composite key)](#tab/fluent-api-composite-key) - -You can use the Fluent API to configure which properties should be used as the composite foreign key properties for a given relationship: - -[!code-csharp[Main](../../../samples/core/Modeling/Relationships/FluentAPI/CompositeForeignKey.cs?name=CompositeForeignKey&highlight=13)] - -#### [Data annotations (simple key)](#tab/data-annotations-simple-key) - -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/Relationships/DataAnnotations/ForeignKey.cs?name=ForeignKey&highlight=17)] - -> [!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 on the dependent type. In this case the specified name will be used to create a shadow foreign key. - ---- +Navigations are layered over a foreign key to provide an object-oriented view of the relationship. Again, there are many examples of navigations in the documentation for the different relationship types. See also [_Relationship navigations_](xref:core/modeling/relationships/navigations) for more information specific to navigations. -#### Shadow foreign key +## Relationship configuration -You can use the string overload of `HasForeignKey(...)` to configure a shadow property as a foreign key (see [Shadow Properties](xref:core/modeling/shadow-properties) for more information). The shadow property needs to exist before it can be used, you can explicitly declare it first as shown below. +EF models are built using a combination of three mechanisms: conventions, mapping attributes, and the model builder API. Model building always starts with [conventions](xref:core/modeling/relationships/conventions), which discover entity types, their properties, and the relationships between the types. The behavior of these conventions can be modified or overridden using [mapping attributes](xref:core/modeling/relationships/mapping-attributes), or using the [model building API](xref:core/modeling/index) in `OnModelCreating`. -[!code-csharp[Main](../../../samples/core/Modeling/Relationships/FluentAPI/ShadowForeignKey.cs?name=ShadowForeignKey&highlight=10,16)] +The model-building API is the final source of truth for the EF model--it will always take precedence over configuration discovered by convention or specified by mapping attributes. It is also the only mechanism with full fidelity to configure every aspect of the EF model. Many examples of using the model building API are shown in the documentation for [one-to-many](xref:core/modeling/relationships/one-to-many), [one-to-one](xref:core/modeling/relationships/one-to-one), and [many-to-many](xref:core/modeling/relationships/many-to-many) relationships, and when discussing [foreign and principal keys](xref:core/modeling/relationships/foreign-and-principal-keys). -#### Foreign key constraint name +## Cascade deletes and deleting orphans -By convention, when targeting a relational database, foreign key constraints are named FK\_\\_\\_\. For composite foreign keys, \ becomes an underscore separated list of foreign key property names. +Cascading deletes and automatic deletion of orphans are configured by convention for required relationships. Examples showing how to change the cascading behavior are included in the documentation for [one-to-many](xref:core/modeling/relationships/one-to-many), [one-to-one](xref:core/modeling/relationships/one-to-one), and [many-to-many](xref:core/modeling/relationships/many-to-many) relationships. -You can also configure the constraint name as follows: +See [_Cascade Delete_](xref:core/saving/cascade-delete) for more information on how cascading behaviors work in `SaveChanges` and `SaveChangesAsync`. -[!code-csharp[Main](../../../samples/core/Modeling/Relationships/FluentAPI/ConstraintName.cs?name=ConstraintName&highlight=6-7)] +## Owned entity types -### Without navigation property +Aggregates of entity types cane be defined using a special type of "owning" relationship that implies a stronger connection between the two types than the "normal" relationships discussed here. Many of the concepts described here for normal relationships are carried over to owned relationships. However, owned relationships also have their own special behaviors, which are covered in the [owned entity types](xref:core/modeling/owned-entities) documentation. -You don't necessarily need to provide a navigation property. You can simply provide a foreign key on one side of the relationship. +## Relationships across context boundaries -[!code-csharp[Main](../../../samples/core/Modeling/Relationships/FluentAPI/NoNavigation.cs?name=NoNavigation&highlight=8-11)] +Relationships are defined in the EF model between entity types included in that model. Some relationships may need to reference an entity type in the model of a different context--for example, when using the [BoundedContext pattern](https://www.martinfowler.com/bliki/BoundedContext.html). In these situation, the foreign key column(s) should be mapped to normal properties, and these properties can then be manipulated manually to handle changes to the relationship. -### 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 set up as an [alternate key](xref:core/modeling/keys#alternate-keys). - -#### [Simple key](#tab/simple-key) - -[!code-csharp[Main](../../../samples/core/Modeling/Relationships/FluentAPI/PrincipalKey.cs?name=PrincipalKey&highlight=11)] - -#### [Composite key](#tab/composite-key) - -[!code-csharp[Main](../../../samples/core/Modeling/Relationships/FluentAPI/CompositePrincipalKey.cs?name=CompositePrincipalKey&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 - -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](xref:core/modeling/entity-properties#required-and-optional-properties) for more information). - -The foreign key properties are located on the dependent entity type, so if they are configured as required it means that every dependent entity is required to have a corresponding principal entity. - -[!code-csharp[Main](../../../samples/core/Modeling/Relationships/FluentAPI/Required.cs?name=Required&highlight=6)] - -> [!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](xref:core/saving/cascade-delete) for a detailed discussion of each option. - -[!code-csharp[Main](../../../samples/core/Modeling/Relationships/FluentAPI/CascadeDelete.cs?name=CascadeDelete&highlight=6)] - -## Other relationship patterns - -### One-to-one - -One to one relationships have a reference navigation property on both sides. They follow the same conventions as one-to-many relationships, but a unique index is introduced on the foreign key property to ensure only one dependent is related to each principal. - -[!code-csharp[Main](../../../samples/core/Modeling/Relationships/OneToOne.cs?name=OneToOne&highlight=6,15-16)] - -> [!NOTE] -> EF will choose one of the entities to be the dependent based on its ability to detect a foreign key property. If the wrong entity is chosen as the dependent, you can use the Fluent API to correct this. - -When configuring the relationship with the Fluent API, you use the `HasOne` and `WithOne` methods. - -When configuring the foreign key you need to specify the dependent entity type - notice the generic parameter provided to `HasForeignKey` in the listing below. In a one-to-many relationship it is clear that the entity with the reference navigation is the dependent and the one with the collection is the principal. But this is not so in a one-to-one relationship - hence the need to explicitly define it. - -[!code-csharp[Main](../../../samples/core/Modeling/Relationships/FluentAPI/OneToOne.cs?name=OneToOne&highlight=11)] - -The dependent side is considered optional by default, but can be configured as required. However EF will not validate whether a dependent entity was provided, so this configuration will only make a difference when the database mapping allows it to be enforced. A common scenario for this are reference owned types that use table splitting by default. - -[!code-csharp[Main](../../../samples/core/Modeling/OwnedEntities/OwnedEntityContext.cs?name=Required&highlight=12-13)] - -With this configuration the columns corresponding to `ShippingAddress` will be marked as non-nullable in the database. - -> [!NOTE] -> If you are using [non-nullable reference types](/dotnet/csharp/nullable-references) calling `IsRequired` is not necessary. - -### Many-to-many - -Many-to-many relationships require a collection navigation property on both sides. They will be discovered by convention like other types of relationships. - -[!code-csharp[Main](../../../samples/core/Modeling/Relationships/FluentAPI/ManyToManyShared.cs?name=ManyToManyShared)] - -The way this relationship is implemented in the database is by a join table that contains foreign keys to both `Post` and `Tag`. For example this is what EF will create in a relational database for the above model. - -```sql -CREATE TABLE [Posts] ( - [PostId] int NOT NULL IDENTITY, - [Title] nvarchar(max) NULL, - [Content] nvarchar(max) NULL, - CONSTRAINT [PK_Posts] PRIMARY KEY ([PostId]) -); - -CREATE TABLE [Tags] ( - [TagId] nvarchar(450) NOT NULL, - CONSTRAINT [PK_Tags] PRIMARY KEY ([TagId]) -); - -CREATE TABLE [PostTag] ( - [PostsId] int NOT NULL, - [TagsId] nvarchar(450) NOT NULL, - CONSTRAINT [PK_PostTag] PRIMARY KEY ([PostsId], [TagsId]), - CONSTRAINT [FK_PostTag_Posts_PostsId] FOREIGN KEY ([PostsId]) REFERENCES [Posts] ([PostId]) ON DELETE CASCADE, - CONSTRAINT [FK_PostTag_Tags_TagsId] FOREIGN KEY ([TagsId]) REFERENCES [Tags] ([TagId]) ON DELETE CASCADE -); -``` - -Internally, EF creates an entity type to represent the join table that will be referred to as the join entity type. `Dictionary` is currently used for it to handle any combination of foreign key properties, see [property bag entity types](xref:core/modeling/shadow-properties#property-bag-entity-types) for more information. More than one many-to-many relationships can exist in the model, therefore the join entity type must be given a unique name, in this case `PostTag`. The feature that allows this is called shared-type entity type. - -> [!IMPORTANT] -> The CLR type used for join entity types by convention may change in future releases to improve performance. Do not depend on the join type being `Dictionary` unless this has been explicitly configured, as described in the next section. - -The many-to-many navigations are called skip navigations as they effectively skip over the join entity type. If you are employing bulk configuration all skip navigations can be obtained from . - -[!code-csharp[Main](../../../samples/core/Modeling/Relationships/FluentAPI/ManyToManyShared.cs?name=Metadata)] - -#### Join entity type configuration - -It is common to apply configuration to the join entity type. This action can be accomplished via `UsingEntity`. - -[!code-csharp[Main](../../../samples/core/Modeling/Relationships/FluentAPI/ManyToManyShared.cs?name=SharedConfiguration)] - -> [!TIP] -> If there is no navigation on the other side `WithMany()` can be called without any arguments. - -[Model seed data](xref:core/modeling/data-seeding) can be provided for the join entity type by using anonymous types. You can examine the model [debug view](xref:core/modeling/index#debug-view) to determine the property names created by convention. - -[!code-csharp[Main](../../../samples/core/Modeling/Relationships/FluentAPI/ManyToManyShared.cs?name=Seeding)] - -Additional data can be stored in the join entity type, but for this it's best to create a bespoke CLR type. When configuring the relationship with a custom join entity type both foreign keys need to be specified explicitly. - -[!code-csharp[Main](../../../samples/core/Modeling/Relationships/FluentAPI/ManyToManyPayload.cs?name=ManyToManyPayload)] - -#### Joining relationships configuration - -EF uses two one-to-many relationships on the join entity type to represent the many-to-many relationship. You can configure these relationships in the `UsingEntity` arguments. - -[!code-csharp[Main](../../../samples/core/Modeling/Relationships/FluentAPI/ManyToManyShared.cs?name=Components)] - -> [!NOTE] -> The `UsingEntity` overloads that don't have a `Action configureJoinEntityType` parameter return an `EntityTypeBuilder` for the join entity type, so the configuration can be chained. Also, starting with EF Core 7.0 there are overloads without a `Type` parameter. These will assume that the type is `Dictionary`, which is recommended when you don't plan on using the join entity directly. - -#### Indirect many-to-many relationships - -You can also represent a many-to-many relationship by just adding the join entity type and mapping two separate one-to-many relationships. - -[!code-csharp[Main](../../../samples/core/Modeling/Relationships/FluentAPI/ManyToMany.cs?name=ManyToMany&highlight=16-19,21-24)] - -> [!NOTE] -> Support for scaffolding many-to-many relationships from the database is not yet added. See [tracking issue](https://github.com/dotnet/efcore/issues/22475). +## Using relationships -## Additional resources +Relationships defined in the model can be used in various ways. For example: -* [.NET Data Community Standup session](https://www.youtube.com/watch?v=W1sxepfIMRM&list=PLdo4fOcmZ0oX-DBuRG4u58ZTAJgBAeQ-t&index=32), with a deep dive into Many-to-many and the infrastructure underpinning it. +- Relationships can be used to [query related data](xref:core/querying/related-data) in any of three ways: + - [Eagerly](xref:core/querying/related-data/eager) as part of a LINQ query, using `Include`. + - [Lazily](xref:core/querying/related-data/lazy) using lazy-loading proxies, or lazy-loading without proxies. + - [Explicitly](xref:core/querying/related-data/explicit) using the `Load` or `LoadAsync` methods. +- Relationships can be used in [data seeding](xref:core/modeling/data-seeding). +- Relationships can be used to [track graphs of entities](xref:core/change-tracking/index). Relationships are then used by the change tracker to: + - [Detect changes in relationships and perform fixup](xref:core/change-tracking/relationship-changes) + - [Send foreign key updates to the database](xref:core/saving/related-data) with `SaveChanges` or `SaveChangesAsync` diff --git a/entity-framework/core/modeling/relationships/conventions.md b/entity-framework/core/modeling/relationships/conventions.md new file mode 100644 index 0000000000..9634b0fb29 --- /dev/null +++ b/entity-framework/core/modeling/relationships/conventions.md @@ -0,0 +1,450 @@ +--- +title: Conventions for relationship discovery - EF Core +description: How navigations, foreign keys, and other aspects of relationships are discovered by EF Core model building conventions +author: ajcvickers +ms.date: 02/25/2023 +uid: core/modeling/relationships/conventions +--- +# Conventions for relationship discovery + +EF Core uses a set of [conventions](xref:core/modeling/bulk-configuration#conventions) when discovering and building a [model](xref:core/modeling/index) based on entity type classes. This document summaries the conventions used for discovering and configuring [relationships between entity types](xref:core/modeling/relationships). + +> [!IMPORTANT] +> The conventions described here can be overridden by explicit configuration of the relationship using either [mapping attributes](xref:core/modeling/relationships/mapping-attributes) or the model building API. + +> [!TIP] +> The code below can be found in [RelationshipConventions.cs](https://github.com/dotnet/EntityFramework.Docs/tree/main/samples/core/Modeling/Relationships/RelationshipConventions.cs). + +## Discovering navigations + +Relationship discovery begins by discovering [navigations](xref:core/modeling/relationships/navigations) between entity types. + +### Reference navigations + +A property of an entity type is discovered as a [reference navigation](xref:core/modeling/relationships/navigations) when: + +- The property is public. +- The property has a getter and a setter. + - The setter does not need to be public; it can be private or have any other [accessibility](/dotnet/csharp/language-reference/keywords/access-modifiers?source=recommendations). + - The setter can be [Init-only](/dotnet/csharp/properties#init-only). +- The property type is, or could be, an entity type. This means that the type + - Must be a [reference type](/dotnet/csharp/language-reference/keywords/reference-types). + - Must not have been configured explicitly as a [primitive property type](xref:core/modeling/entity-properties). + - Must not be mapped as a primitive property type by the database provider being used. + - Must not be [automatically convertable](xref:core/modeling/value-conversions) to a primitive property type mapped by the database provider being used. +- The property is not static. +- The property is not an [indexer property](/dotnet/csharp/programming-guide/indexers/). + +For example, consider the following entity types: + + +[!code-csharp[ReferenceNavigations](../../../../samples/core/Modeling/Relationships/RelationshipConventions.cs?name=ReferenceNavigations)] + +For these types, `Blog.Author` and `Author.Blog` are discovered as reference navigations. On the other hand, the following properties are _not_ discovered as reference navigations: + +- `Blog.Id`, because `int` is a mapped primitive type +- `Blog.Title`, because 'string` is a mapped primitive type +- `Blog.Uri`, because `Uri` is automatically converted to a mapped primitive type +- `Blog.ConsoleKeyInfo`, because `ConsoleKeyInfo` is a C# value type +- `Blog.DefaultAuthor`, because the property does not have a setter +- `Author.Id`, because `Guid` is a mapped primitive type +- `Author.Name`, because 'string` is a mapped primitive type +- `Author.BlogId`, because `int` is a mapped primitive type + +### Collection navigations + +A property of an entity type is discovered as a [collection navigation](xref:core/modeling/relationships/navigations) when: + +- The property is public. +- The property has a getter. Collection navigations can have setters, but this is not required. +- The property type is or implements `IEnumerable`, where `TEntity` is, or could be, an entity type. This means that the type of `TEntity`: + - Must be a [reference type](/dotnet/csharp/language-reference/keywords/reference-types). + - Must not have been configured explicitly as a [primitive property type](xref:core/modeling/entity-properties). + - Must not be mapped as a primitive property type by the database provider being used. + - Must not be [automatically convertable](xref:core/modeling/value-conversions) to a primitive property type mapped by the database provider being used. +- The property is not static. +- The property is not an [indexer property](/dotnet/csharp/programming-guide/indexers/). + +For example, in the following code, both `Blog.Tags` and `Tag.Blogs` are discovered as collection navigations: + + +[!code-csharp[CollectionNavigations](../../../../samples/core/Modeling/Relationships/RelationshipConventions.cs?name=CollectionNavigations)] + +### Pairing navigations + +Once a navigation going from, for example, entity type A to entity type B is discovered, it must next be determined if this navigation has an inverse going in the opposite direction--that is, from entity type B to entity type A. If such an inverse is found, then the two navigations are paired together to form a single, bidirectional relationship. + +The type of relationship is determined by whether the navigation and its inverse are reference or collection navigations. Specifically: + +- If one navigation is a collection navigation and the other is a reference navigation, then the relationship is [one-to-many](xref:core/modeling/relationships/one-to-many). +- If both navigations are reference navigations, then the relationship is [one-to-one](xref:core/modeling/relationships/one-to-one). +- If both navigations are collection navigations, then the relationship is [many-to-many](xref:core/modeling/relationships/many-to-many). + +Discovery of each of these types of relationship is shown in the examples below: + +A single, one-to-many relationship is discovered between `Blog` and `Post` is discovered by pairing the `Blog.Posts` and `Post.Blog` navigations: + + +[!code-csharp[OneToManySingleRelationship](../../../../samples/core/Modeling/Relationships/RelationshipConventions.cs?name=OneToManySingleRelationship)] + +A single, one-to-one relationship is discovered between `Blog` and `Author` is discovered by pairing the `Blog.Author` and `Author.Blog` navigations: + + +[!code-csharp[OneToOneSingleRelationship](../../../../samples/core/Modeling/Relationships/RelationshipConventions.cs?name=OneToOneSingleRelationship)] + +A single, many-to-many relationship is discovered between `Post` and `Tag` is discovered by pairing the `Post.Tags` and `Tag.Posts` navigations: + + +[!code-csharp[ManyToManySingleRelationship](../../../../samples/core/Modeling/Relationships/RelationshipConventions.cs?name=ManyToManySingleRelationship)] + +> [!NOTE] +> This pairing of navigations may be incorrect if the two navigations represent two, different, unidirectional relationships. In this case, the two relationships must be configured explicitly. + +Pairing of relationships only works when there is a single relationship between two types. Multiple relationships between two types must be configured explicitly. + +> [!NOTE] +> The descriptions here are in terms of relationships between two different types. However, it is possible for the same type to be on both ends of a relationship, and therefore for a single type to have two navigations both paired with each other. This is called a self-referencing relationship. + +## Discovering foreign key properties + +Once the navigations for a relationship have either been discovered or configured explicitly, then these navigations are used to discover appropriate foreign key properties for the relationship. A property is discovered as a foreign key when: + +- The property type is compatible with the primary or alternate key on the principal entity type. + - Types are compatible if they are the same, or if the foreign key property type is a nullable version of the primary or alternate key property type. +- The property name matches one of the naming conventions for a foreign key property. The naming conventions are: + - `` + - `Id` + - `` + - `Id` + +> [!TIP] +> The "Id" suffix can have any casing. + +The following entity types show examples for each of these naming conventions. + +`Post.TheBlogKey` is discovered as the foreign key because it matches the pattern ``: + + +[!code-csharp[NavigationPrincipalKeyFKName](../../../../samples/core/Modeling/Relationships/RelationshipConventions.cs?name=NavigationPrincipalKeyFKName)] + +`Post.TheBlogID` is discovered as the foreign key because it matches the pattern `Id`: + + +[!code-csharp[NavigationIdFKName](../../../../samples/core/Modeling/Relationships/RelationshipConventions.cs?name=NavigationIdFKName)] + +`Post.BlogKey` is discovered as the foreign key because it matches the pattern ``: + + +[!code-csharp[PrincipalTypePrincipalKeyFKName](../../../../samples/core/Modeling/Relationships/RelationshipConventions.cs?name=PrincipalTypePrincipalKeyFKName)] + +`Post.Blogid` is discovered as the foreign key because it matches the pattern `Id`: + + +[!code-csharp[PrincipalTypeIdFKName](../../../../samples/core/Modeling/Relationships/RelationshipConventions.cs?name=PrincipalTypeIdFKName)] + +> [!NOTE] +> In the case of one-to-many navigations, the foreign key properties must be on the type with the reference navigation, since this will be the dependent entity. In the case of one-to-one relationships, discovery of a foreign key property is used to determine which type represents the dependent end of the relationship. If no foreign key property is discovered, then the dependent end must be configured using `HasForeignKey`. See [_One-to-one relationships_](xref:core/modeling/relationships/one-to-one) for examples of this. + +The rules above also apply to [composite foreign keys](xref:core/modeling/relationships/foreign-and-principal-keys), where each property of the composite must have a compatible type with the corresponding property of the primary or alternate key, and each property name must match one of the naming conventions described above. + +## Determining cardinality + +EF uses the discovered navigations and foreign key properties to determine the cardinality of the relationship together with its principal and dependent ends: + +- If there is one, unpaired reference navigation, then the relationship is configured as a unidirectional [one-to-many](xref:core/modeling/relationships/one-to-many), with the reference navigation on the dependent end. +- If there is one, unpaired collection navigation, then the relationship is configured as a unidirectional [one-to-many](xref:core/modeling/relationships/one-to-many), with the collection navigation on the principal end. +- If there are paired reference and collection navigations, then the relationship is configured as a bidirectional [one-to-many](xref:core/modeling/relationships/one-to-many), with the collection navigation on the principal end. +- If a reference navigation is paired with another reference navigation, then: + - If a foreign key property was discovered on one side but not the other, then the relationship is configured as a bidirectional [one-to-one](xref:core/modeling/relationships/one-to-one), with the foreign key property on the dependent end. + - Otherwise, the dependent side cannot be determined and EF throws an exception indicating that the dependent must be explicitly configured. +- If a collection navigation is paired with another collection navigation, then the relationship is configured as a bidirectional [many-to-many](xref:core/modeling/relationships/many-to-many). + +## Shadow foreign key properties + +If EF has determined the dependent end of the relationship but no foreign key property was discovered, then EF will create a [shadow property](xref:core/modeling/shadow-properties) to represent the foreign key. The shadow property: + +- Has the type of the primary or alternate key property at the principal end of the relationship. + - The type is made nullable by default, making the relationship optional by default. +- If there is a navigation on the dependent end, then the shadow foreign key property is named using this navigation name concatenated with the primary or alternate key property name. +- If there is no navigation on the dependent end, then the shadow foreign key property is named using principal entity type name concatenated with the primary or alternate key property name. + +## Cascade delete + +By convention, non-nullable foreign key properties are configured to [cascade delete](xref:core/saving/cascade-delete). Nullable foreign key properties are configured to not cascade delete. + +## Many-to-many + +[Many-to-many relationships](xref:core/modeling/relationships/many-to-many) do not have principal and dependent ends, and neither end contains a foreign key property. Instead, many-to-many relationships use a join entity type which contains pairs of foreign keys pointing to either end of the many-to-many. Consider the following entity types, for which a many-to-many relationship is discovered by convention: + + +[!code-csharp[ManyToManySingleRelationship](../../../../samples/core/Modeling/Relationships/RelationshipConventions.cs?name=ManyToManySingleRelationship)] + +The conventions used in this discovery are: + +- The join entity type is named ``. So, `PostTag` in this example. + - The join table has the same name as the join entity type. +- The join entity type is given a foreign key property for each direction of the relationship. These are named ``. So, in this example, the foreign key properties are `PostsId` and `TagsId`. + - For a unidirectional many-to-many, the foreign key property without an associated navigation is named ``. +- The foreign key properties are non-nullable, making both relationships to the join entity required. + - The cascade delete conventions mean that these relationships will be configured for cascade delete. +- The join entity type is configured with a composite primary key consisting of the two foreign key properties. So, in this example, the primary key is made up of `PostsId` and `TagsId`. + +This results in the following EF model: + +```output +Model: + EntityType: Post + Properties: + Id (int) Required PK AfterSave:Throw ValueGenerated.OnAdd + Skip navigations: + Tags (ICollection) CollectionTag Inverse: Posts + Keys: + Id PK + EntityType: Tag + Properties: + Id (int) Required PK AfterSave:Throw ValueGenerated.OnAdd + Skip navigations: + Posts (ICollection) CollectionPost Inverse: Tags + Keys: + Id PK + EntityType: PostTag (Dictionary) CLR Type: Dictionary + Properties: + PostsId (no field, int) Indexer Required PK FK AfterSave:Throw + TagsId (no field, int) Indexer Required PK FK Index AfterSave:Throw + Keys: + PostsId, TagsId PK + Foreign keys: + PostTag (Dictionary) {'PostsId'} -> Post {'Id'} Cascade + PostTag (Dictionary) {'TagsId'} -> Tag {'Id'} Cascade + Indexes: + TagsId +``` + +And translates to the following database schema when using SQLite: + +```sql +CREATE TABLE "Posts" ( + "Id" INTEGER NOT NULL CONSTRAINT "PK_Posts" PRIMARY KEY AUTOINCREMENT); + +CREATE TABLE "Tag" ( + "Id" INTEGER NOT NULL CONSTRAINT "PK_Tag" PRIMARY KEY AUTOINCREMENT); + +CREATE TABLE "PostTag" ( + "PostsId" INTEGER NOT NULL, + "TagsId" INTEGER NOT NULL, + CONSTRAINT "PK_PostTag" PRIMARY KEY ("PostsId", "TagsId"), + CONSTRAINT "FK_PostTag_Posts_PostsId" FOREIGN KEY ("PostsId") REFERENCES "Posts" ("Id") ON DELETE CASCADE, + CONSTRAINT "FK_PostTag_Tag_TagsId" FOREIGN KEY ("TagsId") REFERENCES "Tag" ("Id") ON DELETE CASCADE); + +CREATE INDEX "IX_PostTag_TagsId" ON "PostTag" ("TagsId"); +``` + +## Indexes + +By convention, EF creates a [database index](xref:core/modeling/indexes) for the property or properties of a foreign key. The type of index created is determined by: + +- The cardinality of the relationship +- Whether the relationship is optional or required +- The number of properties that make up the foreign key + +For a [one-to-many relationship](xref:core/modeling/relationships/one-to-many), a straightforward index is created by convention. The same index is created for optional and required relationships. For example, on SQLite: + +```sql +CREATE INDEX "IX_Post_BlogId" ON "Post" ("BlogId"); +``` + +Or on SQL Server: + +```sql +CREATE INDEX [IX_Post_BlogId] ON [Post] ([BlogId]); +``` + +For an required [one-to-one relationship](xref:core/modeling/relationships/one-to-one), a unique index is created. For example, on SQLite: + +```sql +CREATE UNIQUE INDEX "IX_Author_BlogId" ON "Author" ("BlogId"); +``` + +Or on SQL Sever: + +```sql +CREATE UNIQUE INDEX [IX_Author_BlogId] ON [Author] ([BlogId]); +``` + +For optional one-to-one relationships, the index created on SQLite is the same: + +```sql +CREATE UNIQUE INDEX "IX_Author_BlogId" ON "Author" ("BlogId"); +``` + +However, on SQL Server, an `IS NOT NULL` filter is added to better handle null foreign key values. For example: + +```sql +CREATE UNIQUE INDEX [IX_Author_BlogId] ON [Author] ([BlogId]) WHERE [BlogId] IS NOT NULL; +``` + +For composite foreign keys, an index is created covering all the foreign key columns. For example: + +```sql +CREATE INDEX "IX_Post_ContainingBlogId1_ContainingBlogId2" ON "Post" ("ContainingBlogId1", "ContainingBlogId2"); +``` + +### How to stop EF creating indexes for foreign keys + +Indexes have overhead, and, [as asked here](https://github.com/dotnet/efcore/issues/10855), it may not always be appropriate to create them for all FK columns. To achieve this, the `ForeignKeyIndexConvention` can be removed when building the model: + +```csharp +protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder) +{ + configurationBuilder.Conventions.Remove(typeof(ForeignKeyIndexConvention)); +} +``` + +When desired, indexes can still be [explicitly created](xref:core/modeling/indexes) for those foreign key columns that do need them. + +## Foreign key constraint names + +By convention foreign key constraints are named `FK___`. For composite foreign keys, `` becomes an underscore separated list of foreign key property names. + +## Additional resources + +- [.NET Data Community Standup video on custom model conventions](https://www.youtube.com/live/6apfe1L1FhY?feature=share). diff --git a/entity-framework/core/modeling/relationships/foreign-and-principal-keys.md b/entity-framework/core/modeling/relationships/foreign-and-principal-keys.md new file mode 100644 index 0000000000..4af134c561 --- /dev/null +++ b/entity-framework/core/modeling/relationships/foreign-and-principal-keys.md @@ -0,0 +1,313 @@ +--- +title: Foreign and principal keys in relationships - EF Core +description: The use and configuration of foreign keys, alternate keys, and primary keys in relationships +author: ajcvickers +ms.date: 02/25/2023 +uid: core/modeling/relationships/foreign-and-principal-keys +--- +# Foreign principal keys in relationships + +All [one-to-one](xref:core/modeling/relationships/one-to-one) and [one-to-many](xref:core/modeling/relationships/one-to-many) relationships are defined by a foreign key on the dependent end that references a primary or alternate key on the principal end. For convenience, this primary or alternate key is known as the "principal key" for the relationship. [Many-to-many](xref:core/modeling/relationships/one-to-many) relationships are composed of two one-to-many relationships, each of which is itself defined by a foreign key referencing a principal key. + +> [!TIP] +> The code below can be found in [ForeignAndPrincipalKeys.cs](https://github.com/dotnet/EntityFramework.Docs/tree/main/samples/core/Modeling/Relationships/ForeignAndPrincipalKeys.cs). + +## Foreign keys + +The property or properties that make up foreign key are often [discovered by convention](xref:core/modeling/relationships/conventions). The properties can also be configured explicitly using either [mapping attributes](xref:core/modeling/relationships/mapping-attributes) or with `HasForeignKey` in the model building API. `HasForeignKey` can be used with a lambda expression. For example, for a foreign key made up of a single property: + + +[!code-csharp[ForeignKeyByLambda](../../../../samples/core/Modeling/Relationships/ForeignAndPrincipalKeys.cs?name=ForeignKeyByLambda)] + +Or, for a composite foreign key made up of more than one property: + + +[!code-csharp[CompositeForeignKeyByLambda](../../../../samples/core/Modeling/Relationships/ForeignAndPrincipalKeys.cs?name=CompositeForeignKeyByLambda)] + +> [!TIP] +> Using lambda expressions in model building API ensures that the property use is available for code analysis and refactoring, and also provides the property type to the API for use in further chained methods. + +`HasForeignKey` can also be passed the name of the foreign key property as a string. For example, for a single property: + + +[!code-csharp[ForeignKeyByString](../../../../samples/core/Modeling/Relationships/ForeignAndPrincipalKeys.cs?name=ForeignKeyByString)] + +Or, for a composite foreign key: + + +[!code-csharp[CompositeForeignKeyByString](../../../../samples/core/Modeling/Relationships/ForeignAndPrincipalKeys.cs?name=CompositeForeignKeyByString)] + +Using a string is useful when: + +- The property or properties are private. +- The property or properties do not exist on the entity type and should be created as [shadow properties](xref:core/modeling/shadow-properties). +- The property name is calculated or constructed based on some input to the model building process. + +### Non-nullable foreign key columns + +As described in [_Optional and required relationships_](xref:core/modeling/relationships#optional-and-required-relationships), the nullability of the foreign key property determines whether a relationship is optional or required. However, a nullable foreign key property can used for a required relationship using the [`[Required]` attribute](xref:core/modeling/relationships/mapping-attributes), or by calling `IsRequired` in the model building API. For example: + + +[!code-csharp[RequiredForeignKeyConfig](../../../../samples/core/Modeling/Relationships/ForeignAndPrincipalKeys.cs?name=RequiredForeignKeyConfig)] + +Or, if the foreign key is [discovered by convention](xref:core/modeling/relationships/conventions), then `IsRequired` can be used without a call to `HasForeignKey`: + + +[!code-csharp[RequiredForeignKeyConfig2](../../../../samples/core/Modeling/Relationships/ForeignAndPrincipalKeys.cs?name=RequiredForeignKeyConfig2)] + +The end result of this is that the foreign key column in the database is made non-nullable even if the foreign key property is nullable. The same thing can be achieved by explicitly configuring the foreign key property itself as required. For example: + + +[!code-csharp[RequiredForeignKeyConfigByProperty](../../../../samples/core/Modeling/Relationships/ForeignAndPrincipalKeys.cs?name=RequiredForeignKeyConfigByProperty)] + +### Shadow foreign keys + +Foreign key properties can be created as [shadow properties](xref:core/modeling/shadow-properties). A shadow property exists in the EF model but does not exist on the .NET type. EF keeps track of the property value and state internally. + +Shadow foreign keys are usually used when there is a desire to hide the relational concept of a foreign key from the domain model used by application code/business logic. This application code then manipulates the relationship entirely through [navigations](xref:core/modeling/relationships/navigations). + +> [!TIP] +> If entities are going to be serialized, for example to send over a wire, then the foreign key values can be a useful way to keep the relationship information intact when the entities are not in an object/graph form. It is therefore often pragmatic to keep foreign key properties in the .NET type for this purpose. Foreign key properties can be private, which is often a good compromise to avoid exposing the foreign key while allowing its value to travel with the entity. + +Shadow foreign key properties are often [created by convention](xref:core/modeling/relationships/conventions). A shadow foreign key will also be created if the argument to `HasForeignKey` does not match any .NET property. For example: + + +[!code-csharp[ShadowForeignKeyConfig](../../../../samples/core/Modeling/Relationships/ForeignAndPrincipalKeys.cs?name=ShadowForeignKeyConfig)] + +By convention, a shadow foreign key gets its type from the principal key in the relationship. This type is made nullable unless the relationship is detected as or configured as required. + +The shadow foreign key property can also be created explicitly, which is useful for configuring facets of the property. For example, to make the property non-nullable: + + +[!code-csharp[ShadowForeignKeyConfigByProperty](../../../../samples/core/Modeling/Relationships/ForeignAndPrincipalKeys.cs?name=ShadowForeignKeyConfigByProperty)] + +> [!TIP] +> By convention, foreign key properties inherit facets such as maximum length and Unicode support from the principal key in the relationship. It is therefore rarely necessary to explicitly configure facets on a foreign key property. + +The creation of a shadow property if the given name does not match any property of the entity type can be disabled using `ConfigureWarnings`. For example: + + +[!code-csharp[ThrowForShadow](../../../../samples/core/Modeling/Relationships/ForeignAndPrincipalKeys.cs?name=ThrowForShadow)] + +### Foreign key constraint names + +By convention foreign key constraints are named `FK___`. For composite foreign keys, `` becomes an underscore separated list of foreign key property names. + +This can be changed in the model building API using `HasConstraintName`. For example: + + +[!code-csharp[ForeignKeyConstraintNameConfig](../../../../samples/core/Modeling/Relationships/ForeignAndPrincipalKeys.cs?name=ForeignKeyConstraintNameConfig)] + +> [!TIP] +> The constraint name is not used by the EF runtime. It is only used when creating a database schema using [EF Core Migrations](xref:core/managing-schemas/migrations/index). + +### Indexes for foreign keys + +By convention, EF creates a database index for the property or properties of a foreign key. See [_Model building conventions_](xref:core/modeling/relationships/conventions) for more information about the types of indexes created by convention. + +## Principal keys + +By convention, foreign keys are constrained to the primary key at the principal end of the relationship. However, an alternate key can be used instead. This is achieved using `HasPrincipalKey` on the model building API. For Example, for a single property foreign key: + + +[!code-csharp[AlternateKeyConfigurationByLambda](../../../../samples/core/Modeling/Relationships/ForeignAndPrincipalKeys.cs?name=AlternateKeyConfigurationByLambda)] + +Or for a composite foreign key with multiple properties: + + +[!code-csharp[CompositeAlternateKeyConfigurationByLambda](../../../../samples/core/Modeling/Relationships/ForeignAndPrincipalKeys.cs?name=CompositeAlternateKeyConfigurationByLambda)] + +`HasPrincipalKey` can also be passed the name of the alternate key property as a string. For example, for a single property key: + + +[!code-csharp[AlternateKeyConfigurationByString](../../../../samples/core/Modeling/Relationships/ForeignAndPrincipalKeys.cs?name=AlternateKeyConfigurationByString)] + +Or, for a composite key: + + +[!code-csharp[CompositeAlternateKeyConfigurationByString](../../../../samples/core/Modeling/Relationships/ForeignAndPrincipalKeys.cs?name=CompositeAlternateKeyConfigurationByString)] + +> [!NOTE] +> The order of the properties in the principal and foreign key must match. This is also the order in which the key is defined in the database schema. It does not have to be the same as the order of the properties in the entity type or the columns in the table. + +There is no need to call `HasAlternateKey` to define the alternate key on the principal entity; this is done automatically when `HasPrincipalKey` is used with properties that are not the primary key properties. However, `HasAlternateKey` can be used for further configure the alternate key, such as to set its database constraint name. See [_Keys_](xref:core/modeling/keys) for more information. + +## Relationships to keyless entities + +Every relationship must have a foreign key that references a principal (primary or alternate) key. This means that a [keyless entity type](xref:core/modeling/keyless-entity-types) cannot act as the principal end of a relationship, since there is no principal key for the foreign keys to reference. + +> [!TIP] +> An entity type cannot have an alternate key but no primary key. In this case, the alternate key (or one of the alternate keys, if there are several) must be promoted to the primary key. + +However, keyless entity types can still have foreign keys defined, and hence can act as the dependent end of a relationship. For example, consider these types, where `Tag` has no key: + + +[!code-csharp[ForeignKeyInKeylessType](../../../../samples/core/Modeling/Relationships/ForeignAndPrincipalKeys.cs?name=ForeignKeyInKeylessType)] + +`Tag` can be configured at the dependent end of the relationship: + + +[!code-csharp[ForeignKeyInKeylessTypeConfig](../../../../samples/core/Modeling/Relationships/ForeignAndPrincipalKeys.cs?name=ForeignKeyInKeylessTypeConfig)] + +> [!NOTE] +> EF does not support navigations pointing to keyless entity types. See [GitHub Issue #30331](https://github.com/dotnet/efcore/issues/30331). + +## Foreign keys in many-to-many relationships + +In [many-to-many relationships](xref:core/modeling/relationships/many-to-many), the foreign keys are defined on the join entity type and mapped to foreign key constraints in the join table. Everything described above can also be applied to these join entity foreign keys. For example, setting the database constraint names: + + +[!code-csharp[ManyToManyForeignKeyConstraintNamesConfig](../../../../samples/core/Modeling/Relationships/ForeignAndPrincipalKeys.cs?name=ManyToManyForeignKeyConstraintNamesConfig)] diff --git a/entity-framework/core/modeling/relationships/many-to-many.md b/entity-framework/core/modeling/relationships/many-to-many.md new file mode 100644 index 0000000000..ee560fb6fe --- /dev/null +++ b/entity-framework/core/modeling/relationships/many-to-many.md @@ -0,0 +1,1071 @@ +--- +title: Many-to-many relationships - EF Core +description: How to configure many-to-many relationships between entity types when using Entity Framework Core +author: ajcvickers +ms.date: 02/25/2023 +uid: core/modeling/relationships/many-to-many +--- +# Many-to-many relationships + +Many-to-many relationships are used when any number entities of one entity type is associated with any number of entities of the same or another entity type. For example, a `Post` can have many associated `Tags`, and each `Tag` can in turn be associated with any number of `Posts`. + +## Understanding many-to-many relationships + +Many-to-many relationships are different from [one-to-many](xref:core/modeling/relationships/one-to-many) and [one-to-one](xref:core/modeling/relationships/one-to-one) relationships in that they cannot be represented in a simple way using just a foreign key. Instead, when foreign key, or common value associations are use, an additional entity type is needed to "join" the two sides of the relationship. This is known as the "join entity type" and maps to a "join table" in a relational database. The entities of this join entity type contain pairs of foreign key values, where one of each pair points to an entity on one side of the relationship, and the other points to an entity on the other side of the relationship. Each join entity, and therefore each row in the join table, therefore represents one association between the entity types in the relationship. + +EF Core can hide the join entity type and manage it behind the scenes. This allows the navigations of a many-to-many relationship to be used in a natural manner, adding or removing entities from each side as needed. However, it is useful to understand what is happening behind the scenes so that their overall behavior, and in particular the mapping to a relational database, makes sense. Let's start with a relational database schema setup to represent a many-to-many relationship between posts and tags: + +```sql +CREATE TABLE "Posts" ( + "Id" INTEGER NOT NULL CONSTRAINT "PK_Posts" PRIMARY KEY AUTOINCREMENT); + +CREATE TABLE "Tags" ( + "Id" INTEGER NOT NULL CONSTRAINT "PK_Tags" PRIMARY KEY AUTOINCREMENT); + +CREATE TABLE "PostTag" ( + "PostsId" INTEGER NOT NULL, + "TagsId" INTEGER NOT NULL, + CONSTRAINT "PK_PostTag" PRIMARY KEY ("PostsId", "TagsId"), + CONSTRAINT "FK_PostTag_Posts_PostsId" FOREIGN KEY ("PostsId") REFERENCES "Posts" ("Id") ON DELETE CASCADE, + CONSTRAINT "FK_PostTag_Tags_TagsId" FOREIGN KEY ("TagsId") REFERENCES "Tags" ("Id") ON DELETE CASCADE); +``` + +In this schema, `PostTag` is the join table. It contains two columns: `PostsId`, which is a foreign key to the primary key of the `Posts` table, and `TagsId`, which is a foreign key to primary key of the `Tags` table. Each row in this table therefore represents an association between one `Post` and one `Tag`. + +A simplistic mapping for this schema in EF Core consists of three entity types--one for each table. If each of these entity types are represented by a .NET class, then those classes might look the following: + + +[!code-csharp[DirectJoinTableMapping](../../../../samples/core/Modeling/Relationships/ManyToMany.cs?name=DirectJoinTableMapping)] + +Notice that in this mapping there is no many-to-many relationship, but rather two one-to-many relationships, one for each of the foreign keys defined in the join table. This is not an unreasonable way to map these tables, but doesn't reflect the intent of the join table, which is to represent a single many-to-many relationship, rather than two one-to-many relationships. + +EF allows for a more natural mapping through the introduction of two collection navigations, one on `Post` containing its related `Tags`, and an inverse on `Tag` containing its related `Posts`. For example: + + +[!code-csharp[FullMappingWithJoinEntity](../../../../samples/core/Modeling/Relationships/ManyToMany.cs?name=FullMappingWithJoinEntity)] + +> [!TIP] +> These new navigations are known as "skip navigations", because they skip over the join entity to provide direct access to the other side of the many-to-many relationship. + +As is shown in the examples below, a many-to-many relationship can be mapped in this way--that is, with a .NET class for the join entity, and with both navigations for the two one-to-many relationships _and_ skip navigations exposed on the entity types. However, EF can manage the join entity transparently, without a .NET class defined for it, and without navigations for the two one-to-many relationships. For example: + + +[!code-csharp[BasicManyToMany](../../../../samples/core/Modeling/Relationships/ManyToMany.cs?name=BasicManyToMany)] + +Indeed, EF [model building conventions](xref:core/modeling/relationships/conventions) will, by default, map the `Post` and `Tag` types shown here to the three tables in the database schema at the top of this section. This mapping, without explicit use of the join type, is what is typically meant by the term "many-to-many". + +## Examples + +The following sections contain examples of many-to-many relationships, including the configuration needed to achieve each mapping. + +> [!TIP] +> The code for all the examples below can be found in [ManyToMany.cs](https://github.com/dotnet/EntityFramework.Docs/tree/main/samples/core/Modeling/Relationships/ManyToMany.cs). + +### Basic many-to-many + +In the most basic case for a many-to-many, the entity types on each end of the relationship both have a collection navigation. For example: + + +[!code-csharp[BasicManyToMany](../../../../samples/core/Modeling/Relationships/ManyToMany.cs?name=BasicManyToMany)] + +This relationship is [mapped by convention](xref:core/modeling/relationships/conventions). Even though it is not needed, an equivalent explicit configuration for this relationship is shown below as a learning tool: + + +[!code-csharp[BasicManyToManyConfig](../../../../samples/core/Modeling/Relationships/ManyToMany.cs?name=BasicManyToManyConfig)] + +Even with this explicit configuration, many aspects of the relationship are still configured by convention. A more complete explicit configuration, again for learning purposes, is: + + +[!code-csharp[BasicManyToManyFullConfig](../../../../samples/core/Modeling/Relationships/ManyToMany.cs?name=BasicManyToManyFullConfig)] + +> [!IMPORTANT] +> Please don't attempt to fully configure everything even when it is not needed. As can be seen above, the code gets complicated quickly and its easy to make a mistake. And even in the example above there are many things in the model that are still configured by convention. It's not realistic to think that everything in an EF model can always be fully configured explicitly. + +Regardless of whether the relationship is built by convention or using either of the shown explicit configurations, the resulting mapped schema (using SQLite) is: + +```sql +CREATE TABLE "Posts" ( + "Id" INTEGER NOT NULL CONSTRAINT "PK_Posts" PRIMARY KEY AUTOINCREMENT); + +CREATE TABLE "Tags" ( + "Id" INTEGER NOT NULL CONSTRAINT "PK_Tags" PRIMARY KEY AUTOINCREMENT); + +CREATE TABLE "PostTag" ( + "PostsId" INTEGER NOT NULL, + "TagsId" INTEGER NOT NULL, + CONSTRAINT "PK_PostTag" PRIMARY KEY ("PostsId", "TagsId"), + CONSTRAINT "FK_PostTag_Posts_PostsId" FOREIGN KEY ("PostsId") REFERENCES "Posts" ("Id") ON DELETE CASCADE, + CONSTRAINT "FK_PostTag_Tags_TagsId" FOREIGN KEY ("TagsId") REFERENCES "Tags" ("Id") ON DELETE CASCADE); +``` + +> [!TIP] +> When using a Database First flow to [scaffold a DbContext from an existing database](xref:core/managing-schemas/scaffolding), EF Core 6 and later looks for this pattern in the database schema and scaffolds a many-to-many relationship as described in this document. This behavior can be changed through use of a [custom T4 template](xref:core/managing-schemas/scaffolding/templates). For other options, see [_Many-to-many relationships without mapped join entities are now scaffolded_](xref:core/what-is-new/ef-core-6.0/breaking-changes#many-to-many). + +> [!IMPORTANT] +> Currently, EF Core uses `Dictionary` to represent join entity instances for which no .NET class has been configured. However, to improve performance, a different type may be used in a future EF Core release. Do not depend on the join type being `Dictionary` unless this has been explicitly configured. + +### Unidirectional many-to-many + +> [!NOTE] +> Unidirectional many-to-many relationships were introduced in EF Core 7. In earlier releases, a private navigation could be used as a workaround. + +It is not necessary to include a navigation on both sides of the many-to-many relationship. For example: + + +[!code-csharp[UnidirectionalManyToMany](../../../../samples/core/Modeling/Relationships/ManyToMany.cs?name=UnidirectionalManyToMany)] + +EF needs some configuration to know that this should be a many-to-many relationship, rather than a one-to-many. This is done using `HasMany` and `WithMany`, but with no argument passed on the side without a navigation. For example: + + +[!code-csharp[UnidirectionalManyToManyConfig](../../../../samples/core/Modeling/Relationships/ManyToMany.cs?name=UnidirectionalManyToManyConfig)] + +Removing the navigation does not affect the database schema: + +```sql +CREATE TABLE "Posts" ( + "Id" INTEGER NOT NULL CONSTRAINT "PK_Posts" PRIMARY KEY AUTOINCREMENT); + +CREATE TABLE "Tags" ( + "Id" INTEGER NOT NULL CONSTRAINT "PK_Tags" PRIMARY KEY AUTOINCREMENT); + +CREATE TABLE "PostTag" ( + "PostId" INTEGER NOT NULL, + "TagsId" INTEGER NOT NULL, + CONSTRAINT "PK_PostTag" PRIMARY KEY ("PostId", "TagsId"), + CONSTRAINT "FK_PostTag_Posts_PostId" FOREIGN KEY ("PostId") REFERENCES "Posts" ("Id") ON DELETE CASCADE, + CONSTRAINT "FK_PostTag_Tags_TagsId" FOREIGN KEY ("TagsId") REFERENCES "Tags" ("Id") ON DELETE CASCADE); +``` + +### Many-to-many with named join table + +In the previous example, the join table was named `PostTag` by convention. It can be given an explicit name with `UsingEntity`. For example: + + +[!code-csharp[ManyToManyNamedJoinTableConfig](../../../../samples/core/Modeling/Relationships/ManyToMany.cs?name=ManyToManyNamedJoinTableConfig)] + +Everything else about the mapping remains the same, with only the name of the join table changing: + +```sql +CREATE TABLE "PostsToTagsJoinTable" ( + "PostsId" INTEGER NOT NULL, + "TagsId" INTEGER NOT NULL, + CONSTRAINT "PK_PostsToTagsJoinTable" PRIMARY KEY ("PostsId", "TagsId"), + CONSTRAINT "FK_PostsToTagsJoinTable_Posts_PostsId" FOREIGN KEY ("PostsId") REFERENCES "Posts" ("Id") ON DELETE CASCADE, + CONSTRAINT "FK_PostsToTagsJoinTable_Tags_TagsId" FOREIGN KEY ("TagsId") REFERENCES "Tags" ("Id") ON DELETE CASCADE); +``` + +### Many-to-many with join table foreign key names + +Following on from the previous example, the names of the foreign key columns in the join table can also be changed. There are two ways to do this. The first is to explicitly specify the foreign key property names on the join entity. For example: + + +[!code-csharp[ManyToManyNamedForeignKeyColumnsConfig](../../../../samples/core/Modeling/Relationships/ManyToMany.cs?name=ManyToManyNamedForeignKeyColumnsConfig)] + +The second way is to leave the properties with their by-convention names, but then map these properties to different column names. For example: + + +[!code-csharp[ManyToManyNamedForeignKeyColumnsAlternateConfig](../../../../samples/core/Modeling/Relationships/ManyToMany.cs?name=ManyToManyNamedForeignKeyColumnsAlternateConfig)] + +In either case, the mapping remains the same, with only the foreign key column names changed: + +```sql +CREATE TABLE "PostTag" ( + "PostForeignKey" INTEGER NOT NULL, + "TagForeignKey" INTEGER NOT NULL, + CONSTRAINT "PK_PostTag" PRIMARY KEY ("PostForeignKey", "TagForeignKey"), + CONSTRAINT "FK_PostTag_Posts_PostForeignKey" FOREIGN KEY ("PostForeignKey") REFERENCES "Posts" ("Id") ON DELETE CASCADE, + CONSTRAINT "FK_PostTag_Tags_TagForeignKey" FOREIGN KEY ("TagForeignKey") REFERENCES "Tags" ("Id") ON DELETE CASCADE); +``` + +> [!TIP] +> Although not shown here, the previous two examples can be combined to map change the join table name and its foreign key column names. + +### Many-to-many with class for join entity + +So far in the examples, the join table has been automatically mapped to a [shared-type entity type](xref:core/modeling/entity-types#shared-type-entity-types). This removes the need for a dedicated class to be created for the entity type. However, it can be useful to have such a class so that it can be referenced easily, especially when navigations or a payload are added to the class, as is shown in later examples below. Do do this, first create a type `PostTag` for the join entity in addition to the existing types for `Post` and `Tag`: + + +[!code-csharp[ManyToManyWithJoinClass](../../../../samples/core/Modeling/Relationships/ManyToMany.cs?name=ManyToManyWithJoinClass)] + +> [!TIP] +> The class can have any name, but it is common to combine the names of the types at either end of the relationship. + +Now the `UsingEntity` method can be used to configure this as the join entity type for the relationship. For example: + + +[!code-csharp[ManyToManyWithJoinClassConfig](../../../../samples/core/Modeling/Relationships/ManyToMany.cs?name=ManyToManyWithJoinClassConfig)] + +The `PostId` and `TagId` are automatically picked up as the foreign keys and are configured as the composite primary key for the join entity type. The properties to use for the foreign keys can be explicitly configured for cases where they don't match the EF convention. For example: + + +[!code-csharp[ManyToManyWithJoinClassFkConfig](../../../../samples/core/Modeling/Relationships/ManyToMany.cs?name=ManyToManyWithJoinClassFkConfig)] + +The mapped database schema for the join table in this example is structurally equivalent to the previous examples, but with some different column names: + +```sql +CREATE TABLE "PostTag" ( + "PostId" INTEGER NOT NULL, + "TagId" INTEGER NOT NULL, + CONSTRAINT "PK_PostTag" PRIMARY KEY ("PostId", "TagId"), + CONSTRAINT "FK_PostTag_Posts_PostId" FOREIGN KEY ("PostId") REFERENCES "Posts" ("Id") ON DELETE CASCADE, + CONSTRAINT "FK_PostTag_Tags_TagId" FOREIGN KEY ("TagId") REFERENCES "Tags" ("Id") ON DELETE CASCADE); +``` + +### Many-to-many with navigations to join entity + +Following on from the previous example, now that there is a class representing the join entity, it becomes easy to add navigations that reference this class. For example: + + +[!code-csharp[ManyToManyWithNavsToJoinClass](../../../../samples/core/Modeling/Relationships/ManyToMany.cs?name=ManyToManyWithNavsToJoinClass)] + +> [!IMPORTANT] +> As shown in this example, navigations to the join entity type can be used _in addition to_ the skip navigations between the two ends of the many-to-many relationship. This means that the skip navigations can be used to interact with the many-to-many relationship in a natural manner, while the navigations to the join entity type can be used when greater control over the join entities themselves is needed. In a sense, this mapping provides the best of both worlds between a simple many-to-many mapping, and a mapping that more explicitly matches the database schema. + +Nothing needs to be changed in the `UsingEntity` call, since the navigations to the join entity are picked up by convention. Therefore, the configuration for this example is the same as for the last example: + + +[!code-csharp[ManyToManyWithJoinClassConfig](../../../../samples/core/Modeling/Relationships/ManyToMany.cs?name=ManyToManyWithJoinClassConfig)] + +The navigations can be configured explicitly for cases where the they cannot be determined by convention. For example: + + +[!code-csharp[ManyToManyWithNavsToJoinClassWithNavConfig](../../../../samples/core/Modeling/Relationships/ManyToMany.cs?name=ManyToManyWithNavsToJoinClassWithNavConfig)] + +The mapped database schema is not affected by including navigations in the model: + +```sql +CREATE TABLE "PostTag" ( + "PostId" INTEGER NOT NULL, + "TagId" INTEGER NOT NULL, + CONSTRAINT "PK_PostTag" PRIMARY KEY ("PostId", "TagId"), + CONSTRAINT "FK_PostTag_Posts_PostId" FOREIGN KEY ("PostId") REFERENCES "Posts" ("Id") ON DELETE CASCADE, + CONSTRAINT "FK_PostTag_Tags_TagId" FOREIGN KEY ("TagId") REFERENCES "Tags" ("Id") ON DELETE CASCADE); +``` + +### Many-to-many with navigations to and from join entity + +The previous example added navigations to the join entity type from the entity types at either end of the many-to-many relationship. Navigations can also be added in the other direction, or in both directions. For example: + + +[!code-csharp[ManyToManyWithNavsToAndFromJoinClass](../../../../samples/core/Modeling/Relationships/ManyToMany.cs?name=ManyToManyWithNavsToAndFromJoinClass)] + +Nothing needs to be changed in the `UsingEntity` call, since the navigations to the join entity are picked up by convention. Therefore, the configuration for this example is the same as for the last example: + + +[!code-csharp[ManyToManyWithNavsToAndFromJoinClassConfig](../../../../samples/core/Modeling/Relationships/ManyToMany.cs?name=ManyToManyWithNavsToAndFromJoinClassConfig)] + +The navigations can be configured explicitly for cases where the they cannot be determined by convention. For example: + + +[!code-csharp[ManyToManyWithNavsToAndFromJoinClassWithNavConfig](../../../../samples/core/Modeling/Relationships/ManyToMany.cs?name=ManyToManyWithNavsToAndFromJoinClassWithNavConfig)] + +The mapped database schema is not affected by including navigations in the model: + +```sql +CREATE TABLE "PostTag" ( + "PostId" INTEGER NOT NULL, + "TagId" INTEGER NOT NULL, + CONSTRAINT "PK_PostTag" PRIMARY KEY ("PostId", "TagId"), + CONSTRAINT "FK_PostTag_Posts_PostId" FOREIGN KEY ("PostId") REFERENCES "Posts" ("Id") ON DELETE CASCADE, + CONSTRAINT "FK_PostTag_Tags_TagId" FOREIGN KEY ("TagId") REFERENCES "Tags" ("Id") ON DELETE CASCADE); +``` + +### Many-to-many with navigations and changed foreign keys + +The previous example showed a many-to-many with navigations to and from the join entity type. This example is the same, except that the foreign key properties used are also changed. For example: + + +[!code-csharp[ManyToManyWithNamedFksAndNavsToAndFromJoinClass](../../../../samples/core/Modeling/Relationships/ManyToMany.cs?name=ManyToManyWithNamedFksAndNavsToAndFromJoinClass)] + +Again, the `UsingEntity` method is used to configure this: + + +[!code-csharp[ManyToManyWithNamedFksAndNavsToAndFromJoinClassConfig](../../../../samples/core/Modeling/Relationships/ManyToMany.cs?name=ManyToManyWithNamedFksAndNavsToAndFromJoinClassConfig)] + +The mapped database schema is now: + +```sql +CREATE TABLE "PostTag" ( + "PostForeignKey" INTEGER NOT NULL, + "TagForeignKey" INTEGER NOT NULL, + CONSTRAINT "PK_PostTag" PRIMARY KEY ("PostForeignKey", "TagForeignKey"), + CONSTRAINT "FK_PostTag_Posts_PostForeignKey" FOREIGN KEY ("PostForeignKey") REFERENCES "Posts" ("Id") ON DELETE CASCADE, + CONSTRAINT "FK_PostTag_Tags_TagForeignKey" FOREIGN KEY ("TagForeignKey") REFERENCES "Tags" ("Id") ON DELETE CASCADE); +``` + +### Many-to-many with alternate keys + +So far, all the examples have shown the foreign keys in the join entity type being constrained to the primary keys of the entity types on either side of the relationship. Each foreign key, or both, can instead be constrained to an alternate key. For example, consider this model where`Tag` and `Post` have alternate key properties: + + +[!code-csharp[ManyToManyAlternateKeys](../../../../samples/core/Modeling/Relationships/ManyToMany.cs?name=ManyToManyAlternateKeys)] + +The configuration for this model is: + + +[!code-csharp[ManyToManyAlternateKeysConfig](../../../../samples/core/Modeling/Relationships/ManyToMany.cs?name=ManyToManyAlternateKeysConfig)] + +And the resulting database schema, for clarity, including also the tables with the alternate keys: + +```sql +CREATE TABLE "Posts" ( + "Id" INTEGER NOT NULL CONSTRAINT "PK_Posts" PRIMARY KEY AUTOINCREMENT, + "AlternateKey" INTEGER NOT NULL, + CONSTRAINT "AK_Posts_AlternateKey" UNIQUE ("AlternateKey")); + +CREATE TABLE "Tags" ( + "Id" INTEGER NOT NULL CONSTRAINT "PK_Tags" PRIMARY KEY AUTOINCREMENT, + "AlternateKey" INTEGER NOT NULL, + CONSTRAINT "AK_Tags_AlternateKey" UNIQUE ("AlternateKey")); + +CREATE TABLE "PostTag" ( + "PostsAlternateKey" INTEGER NOT NULL, + "TagsAlternateKey" INTEGER NOT NULL, + CONSTRAINT "PK_PostTag" PRIMARY KEY ("PostsAlternateKey", "TagsAlternateKey"), + CONSTRAINT "FK_PostTag_Posts_PostsAlternateKey" FOREIGN KEY ("PostsAlternateKey") REFERENCES "Posts" ("AlternateKey") ON DELETE CASCADE, + CONSTRAINT "FK_PostTag_Tags_TagsAlternateKey" FOREIGN KEY ("TagsAlternateKey") REFERENCES "Tags" ("AlternateKey") ON DELETE CASCADE); +``` + +The configuration for using alternate keys is slightly different if the join entity type is represented by a .NET type. For example: + + +[!code-csharp[ManyToManyWithNavsAndAlternateKeys](../../../../samples/core/Modeling/Relationships/ManyToMany.cs?name=ManyToManyWithNavsAndAlternateKeys)] + +The configuration can now use the generic `UsingEntity<>` method: + + +[!code-csharp[ManyToManyWithNavsAndAlternateKeysConfig](../../../../samples/core/Modeling/Relationships/ManyToMany.cs?name=ManyToManyWithNavsAndAlternateKeysConfig)] + +And the resulting schema is: + +```sql +CREATE TABLE "Posts" ( + "Id" INTEGER NOT NULL CONSTRAINT "PK_Posts" PRIMARY KEY AUTOINCREMENT, + "AlternateKey" INTEGER NOT NULL, + CONSTRAINT "AK_Posts_AlternateKey" UNIQUE ("AlternateKey")); + +CREATE TABLE "Tags" ( + "Id" INTEGER NOT NULL CONSTRAINT "PK_Tags" PRIMARY KEY AUTOINCREMENT, + "AlternateKey" INTEGER NOT NULL, + CONSTRAINT "AK_Tags_AlternateKey" UNIQUE ("AlternateKey")); + +CREATE TABLE "PostTag" ( + "PostId" INTEGER NOT NULL, + "TagId" INTEGER NOT NULL, + CONSTRAINT "PK_PostTag" PRIMARY KEY ("PostId", "TagId"), + CONSTRAINT "FK_PostTag_Posts_PostId" FOREIGN KEY ("PostId") REFERENCES "Posts" ("AlternateKey") ON DELETE CASCADE, + CONSTRAINT "FK_PostTag_Tags_TagId" FOREIGN KEY ("TagId") REFERENCES "Tags" ("AlternateKey") ON DELETE CASCADE); +``` + +### Many-to-many and join table with separate primary key + +So far, the join entity type in all the examples has a primary key composed of the two foreign key properties. This is because each combination of values for these properties can occur at most once. These properties therefore form a natural primary key. + +> [!NOTE] +> EF Core does not support duplicate entities in any collection navigation. + +If you control the database schema, then there is no reason for the join table to have an additional primary key column, However, it is possible that an existing join table may have a primary key column defined. EF can still map to this with some configuration. + +It is perhaps easiest to this by creating a class to represent the join entity. For example: + + +[!code-csharp[ManyToManyWithJoinClassHavingPrimaryKey](../../../../samples/core/Modeling/Relationships/ManyToMany.cs?name=ManyToManyWithJoinClassHavingPrimaryKey)] + +This `PostTag.Id` property is now picked up as the primary key by convention, so the only configuration needed is a call to `UsingEntity` for the `PostTag` type: + + +[!code-csharp[ManyToManyWithJoinClassHavingPrimaryKeyConfig](../../../../samples/core/Modeling/Relationships/ManyToMany.cs?name=ManyToManyWithJoinClassHavingPrimaryKeyConfig)] + +And the resulting schema for the join table is: + +```sql +CREATE TABLE "PostTag" ( + "Id" INTEGER NOT NULL CONSTRAINT "PK_PostTag" PRIMARY KEY AUTOINCREMENT, + "PostId" INTEGER NOT NULL, + "TagId" INTEGER NOT NULL, + CONSTRAINT "FK_PostTag_Posts_PostId" FOREIGN KEY ("PostId") REFERENCES "Posts" ("Id") ON DELETE CASCADE, + CONSTRAINT "FK_PostTag_Tags_TagId" FOREIGN KEY ("TagId") REFERENCES "Tags" ("Id") ON DELETE CASCADE); +``` + +A primary key can also be added to the join entity without defining a class for it. For example, with just `Post` and `Tag` types: + + +[!code-csharp[ManyToManyWithPrimaryKeyInJoinEntity](../../../../samples/core/Modeling/Relationships/ManyToMany.cs?name=ManyToManyWithPrimaryKeyInJoinEntity)] + +The key can be added with this configuration: + + +[!code-csharp[ManyToManyWithPrimaryKeyInJoinEntityConfig](../../../../samples/core/Modeling/Relationships/ManyToMany.cs?name=ManyToManyWithPrimaryKeyInJoinEntityConfig)] + +Which results in a join table with a separate primary key column: + +```sql +CREATE TABLE "PostTag" ( + "Id" INTEGER NOT NULL CONSTRAINT "PK_PostTag" PRIMARY KEY AUTOINCREMENT, + "PostsId" INTEGER NOT NULL, + "TagsId" INTEGER NOT NULL, + CONSTRAINT "FK_PostTag_Posts_PostsId" FOREIGN KEY ("PostsId") REFERENCES "Posts" ("Id") ON DELETE CASCADE, + CONSTRAINT "FK_PostTag_Tags_TagsId" FOREIGN KEY ("TagsId") REFERENCES "Tags" ("Id") ON DELETE CASCADE); +``` + +### Many-to-many and join table with payload + +In the examples so far, the join table has been used only to store the foreign key pairs representing each association. However, it can also be used to store information about the association--for example, the time it was created. In such cases it is best to define a type for the join entity and add the "association payload" properties to this type. It is also common to create navigations to the join entity in addition to the "skip navigations" used for the many-to-many relationship. These additional navigations allow the join entity to be easily referenced from code, thereby facilitating reading and/or changing the payload data. For example: + + +[!code-csharp[ManyToManyWithPayloadAndNavsToJoinClass](../../../../samples/core/Modeling/Relationships/ManyToMany.cs?name=ManyToManyWithPayloadAndNavsToJoinClass)] + +It is also common to use generated values for payload properties--for example, a database timestamp that is automatically set when the association row is inserted. This requires some minimal configuration. For example: + + +[!code-csharp[ManyToManyWithPayloadAndNavsToJoinClassConfig](../../../../samples/core/Modeling/Relationships/ManyToMany.cs?name=ManyToManyWithPayloadAndNavsToJoinClassConfig)] + +The result maps to a entity type schema with a timestamp set automatically when a row is inserted: + +```sql +CREATE TABLE "PostTag" ( + "PostId" INTEGER NOT NULL, + "TagId" INTEGER NOT NULL, + "CreatedOn" TEXT NOT NULL DEFAULT (CURRENT_TIMESTAMP), + CONSTRAINT "PK_PostTag" PRIMARY KEY ("PostId", "TagId"), + CONSTRAINT "FK_PostTag_Posts_PostId" FOREIGN KEY ("PostId") REFERENCES "Posts" ("Id") ON DELETE CASCADE, + CONSTRAINT "FK_PostTag_Tags_TagId" FOREIGN KEY ("TagId") REFERENCES "Tags" ("Id") ON DELETE CASCADE); +``` + +> [!TIP] +> The SQL shown here is for SQLite. On SQL Server/Azure SQL, use `.HasDefaultValueSql("GETUTCDATE()")`. + +### Custom shared-type entity type as a join entity + +The previous example used the type `PostTag` as the join entity type. This type is specific to the posts-tags relationship. However, if you have multiple join tables with the same shape, then the same CLR type can be used for all of them. For example, imagine that all our join tables have a `CreatedOn` column. We can map these using `JoinType` class mapped as a [shared-type entity type](xref:core/modeling/entity-types#shared-type-entity-types): + +```csharp +public class JoinType +{ + public int Id1 { get; set; } + public int Id2 { get; set; } + public DateTime CreatedOn { get; set; } +} +``` + +This type can then be referenced as the join entity type by multiple different many-to-many relationships. For example: + + +[!code-csharp[ManyToManyWithCustomSharedTypeEntityType](../../../../samples/core/Modeling/Relationships/ManyToMany.cs?name=ManyToManyWithCustomSharedTypeEntityType)] + +And these relationships can then be configured appropriately to map the join type to a different table for each relationship: + + +[!code-csharp[ManyToManyWithCustomSharedTypeEntityTypeConfig](../../../../samples/core/Modeling/Relationships/ManyToMany.cs?name=ManyToManyWithCustomSharedTypeEntityTypeConfig)] + +This results in the following tables in the database schema: + +```sql +CREATE TABLE "BlogAuthor" ( + "Id1" INTEGER NOT NULL, + "Id2" INTEGER NOT NULL, + "CreatedOn" TEXT NOT NULL DEFAULT (CURRENT_TIMESTAMP), + CONSTRAINT "PK_BlogAuthor" PRIMARY KEY ("Id1", "Id2"), + CONSTRAINT "FK_BlogAuthor_Authors_Id1" FOREIGN KEY ("Id1") REFERENCES "Authors" ("Id") ON DELETE CASCADE, + CONSTRAINT "FK_BlogAuthor_Blogs_Id2" FOREIGN KEY ("Id2") REFERENCES "Blogs" ("Id") ON DELETE CASCADE); + + +CREATE TABLE "PostTag" ( + "Id1" INTEGER NOT NULL, + "Id2" INTEGER NOT NULL, + "CreatedOn" TEXT NOT NULL DEFAULT (CURRENT_TIMESTAMP), + CONSTRAINT "PK_PostTag" PRIMARY KEY ("Id1", "Id2"), + CONSTRAINT "FK_PostTag_Posts_Id2" FOREIGN KEY ("Id2") REFERENCES "Posts" ("Id") ON DELETE CASCADE, + CONSTRAINT "FK_PostTag_Tags_Id1" FOREIGN KEY ("Id1") REFERENCES "Tags" ("Id") ON DELETE CASCADE); +``` + +### Many-to-many without cascading delete + +In all the examples shown above, the foreign keys created between the join table and the two sides of the many-to-many relationship are created with [cascading delete](xref:core/saving/cascade-delete) behavior. This is very useful because it means that if an entity on either side of the relationship is deleted, then the rows in the join table for that entity are automatically deleted. Or, in other words, when an entity no longer exists, then its relationships to other entities also no longer exist. + +It's hard to imagine when it is useful to change this behavior, but it can be done if desired. For example: + + +[!code-csharp[ManyToManyWithNoCascadeDeleteConfig](../../../../samples/core/Modeling/Relationships/ManyToMany.cs?name=ManyToManyWithNoCascadeDeleteConfig)] + +The result maps to a entity type schema with a timestamp set automatically when a row is inserted: + +```sql +CREATE TABLE "PostTag" ( + "PostsId" INTEGER NOT NULL, + "TagsId" INTEGER NOT NULL, + CONSTRAINT "PK_PostTag" PRIMARY KEY ("PostsId", "TagsId"), + CONSTRAINT "FK_PostTag_Posts_PostsId" FOREIGN KEY ("PostsId") REFERENCES "Posts" ("Id") ON DELETE RESTRICT, + CONSTRAINT "FK_PostTag_Tags_TagsId" FOREIGN KEY ("TagsId") REFERENCES "Tags" ("Id") ON DELETE RESTRICT); +``` + +### Self-referencing many-to-many + +The same entity type can be used at both ends of a many-to-many relationship; this is known as a "self-referencing" relationship. For example: + + +[!code-csharp[SelfReferencingManyToMany](../../../../samples/core/Modeling/Relationships/ManyToMany.cs?name=SelfReferencingManyToMany)] + +This maps to a join table called `PersonPerson`, with both foreign keys pointing back to the `People` table: + +```sql +CREATE TABLE "PersonPerson" ( + "ChildrenId" INTEGER NOT NULL, + "ParentsId" INTEGER NOT NULL, + CONSTRAINT "PK_PersonPerson" PRIMARY KEY ("ChildrenId", "ParentsId"), + CONSTRAINT "FK_PersonPerson_People_ChildrenId" FOREIGN KEY ("ChildrenId") REFERENCES "People" ("Id") ON DELETE CASCADE, + CONSTRAINT "FK_PersonPerson_People_ParentsId" FOREIGN KEY ("ParentsId") REFERENCES "People" ("Id") ON DELETE CASCADE); +``` + +### Symmetrical self-referencing many-to-many + +Sometimes a many-to-many relationship is naturally symmetrical. That is, if entity A is related to entity B, then entity B is also related to entity A. This is naturally modeled using a single navigation. For example, imagine the case where is person A is friends with person B, then person B is friends with person A: + + +[!code-csharp[SelfReferencingUnidirectionalManyToMany](../../../../samples/core/Modeling/Relationships/ManyToMany.cs?name=SelfReferencingUnidirectionalManyToMany)] + +Unfortunately, this is not easy to map. The same navigation cannot be used for both ends of the relationship. The best that can be done is to map it as a unidirectional many-to-many relationship. For example: + + +[!code-csharp[SelfReferencingUnidirectionalManyToManyConfig](../../../../samples/core/Modeling/Relationships/ManyToMany.cs?name=SelfReferencingUnidirectionalManyToManyConfig)] + +However, to make sure two people are both related to each other, each person will need to be manually added to the other person's `Firends` collection. For example: + +```csharp +ginny.Friends.Add(hermione); +hermione.Friends.Add(ginny); +``` + +### Direct use of join table + +ALl of the examples above make use of the EF Core many-to-many mapping patterns. However, it is also possible to map a join table to a normal entity type and just use the two one-to-many relationships for all operations. + +For example, these entity types represent the mapping of two normal tables and join table without using any many-to-many relationships: + +```csharp +public class Post +{ + public int Id { get; set; } + public List PostTags { get; } = new(); +} + +public class Tag +{ + public int Id { get; set; } + public List PostTags { get; } = new(); +} + +public class PostTag +{ + public int PostId { get; set; } + public int TagId { get; set; } + public Post Post { get; set; } = null!; + public Tag Tag { get; set; } = null!; +} +``` + +This requires no special mapping, since these are normal entity types with normal [one-to-many](xref:core/modeling/relationships/one-to-many) relationships. + +## Additional resources + +* [.NET Data Community Standup session](https://www.youtube.com/watch?v=W1sxepfIMRM&list=PLdo4fOcmZ0oX-DBuRG4u58ZTAJgBAeQ-t&index=32), with a deep dive into Many-to-many and the infrastructure underpinning it. diff --git a/entity-framework/core/modeling/relationships/mapping-attributes.md b/entity-framework/core/modeling/relationships/mapping-attributes.md new file mode 100644 index 0000000000..109659ca9f --- /dev/null +++ b/entity-framework/core/modeling/relationships/mapping-attributes.md @@ -0,0 +1,209 @@ +--- +title: Mapping attributes (aka Data Annotations) for relationships - EF Core +description: Using mapping attributes (also know as Data Annotations) to configure Entity Framework Core relationships +author: ajcvickers +ms.date: 02/25/2023 +uid: core/modeling/relationships/mapping-attributes +--- +# Mapping attributes (aka data annotations) for relationships + +Mapping attributes are used to modified or override the configuration discovered by [model building conventions](xref:core/modeling/relationships/conventions). The configuration performed by mapping attributes can itself be overridden by [the model building API used in `OnModelCreating`](xref:core/modeling/index). + +> [!IMPORTANT] +> This document only covers mapping attributes in the context of relationship configuration. Other uses of mapping attributes are covered in the relevant sections of the wider [modeling documentation](xref:core/modeling/index). + +> [!TIP] +> The code below can be found in [MappingAttributes.cs](https://github.com/dotnet/EntityFramework.Docs/tree/main/samples/core/Modeling/Relationships/MappingAttributes.cs). + +## Where to get mapping attributes + +Many mapping attributes come from the [System.ComponentModel.DataAnnotations](/dotnet/api/system.componentmodel.dataannotations) and [System.ComponentModel.DataAnnotations.Schema](/dotnet/api/system.componentmodel.dataannotations.schema) namespaces. The attributes in these namespaces are included as part of the base framework in all supported versions of .NET, and so do not require the installation of any additional NuGet packages. These mapping attributes are commonly called "data annotations" and are used by a variety of frameworks, including EF Core, EF6, ASP.NET Core MVC, and so on. They are also used for validation. + +The use of data annotations across many technologies and for both mapping and validation has led to differences in semantics across technologies. All new mapping attributes designed for EF Core are now specific to EF Core, thereby keeping their semantics and use simple and clear. These attributes are contained in the [Microsoft.EntityFrameworkCore.Abstractions](https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.Abstractions/) NuGet package. This package is included as a dependency whenever the main [Microsoft.EntityFrameworkCore](https://www.nuget.org/packages/Microsoft.EntityFrameworkCore/) package, or one of the associated database provider packages, is used. However, the Abstractions package is a lightweight package that can be referenced directly by application code without bringing in all of EF Core and its dependencies. + +## RequiredAttribute + + is applied to a property to indicate that the property cannot be `null`. In the context of relationships, `[Required]` is usually used on a foreign key property. Doing do makes the foreign key not nullable, thereby making the relationship required. For example, with the following types, the `Post.BlogId` property is made non-nullable, and the relationship becomes required. + + +[!code-csharp[RequiredOnForeignKey](../../../../samples/core/Modeling/Relationships/MappingAttributes.cs?name=RequiredOnForeignKey)] + +> [!NOTE] +> When using [C# nullable reference types](/dotnet/csharp/tutorials/nullable-reference-types), the `BlogId` property in this example is already non-nullable, which means the `[Required]` attribute will have no affect. + +`[Required]` placed on the dependent navigation has the same effect. That is, making the foreign key non-nullable, and thereby making the relationship required. For example: + + +[!code-csharp[RequiredOnDependentNavigation](../../../../samples/core/Modeling/Relationships/MappingAttributes.cs?name=RequiredOnDependentNavigation)] + +If `[Required]` is found on the dependent navigation and the foreign key property is in shadow state, then shadow property is made non-nullable, thereby making the relationship required. For example: + + +[!code-csharp[RequiredOnDependentNavigationShadowFk](../../../../samples/core/Modeling/Relationships/MappingAttributes.cs?name=RequiredOnDependentNavigationShadowFk)] + +> [!NOTE] +> Using `[Required]` on the principal navigation side of a relationship has no effect. + +## ForeignKeyAttribute + + is used to connect a foreign key property with its navigations. `[ForeignKey]` can be placed on the foreign key property with the name of the dependent navigation. For example: + + +[!code-csharp[ForeignKeyOnProperty](../../../../samples/core/Modeling/Relationships/MappingAttributes.cs?name=ForeignKeyOnProperty)] + +Or, `[ForeignKey]` can be placed on either the dependent or principal navigation with the name of the property to use as the foreign key. For example: + + +[!code-csharp[ForeignKeyOnDependentNavigation](../../../../samples/core/Modeling/Relationships/MappingAttributes.cs?name=ForeignKeyOnDependentNavigation)] + +When `[ForeignKey]` is placed on a navigation and the name provided does not match any property name, then a [shadow property](xref:core/modeling/shadow-properties) with that name will be created to act as the foreign key. For example: + + +[!code-csharp[ForeignKeyOnDependentNavigationShadowFk](../../../../samples/core/Modeling/Relationships/MappingAttributes.cs?name=ForeignKeyOnDependentNavigationShadowFk)] + +## InversePropertyAttribute + + is used to connect a navigation with its inverse. For example, in the following entity types, there are two relationships between `Blog` and `Post`. Without any configuration, [EF conventions](xref:core/modeling/relationships/conventions) cannot determine which navigations between the two types should be paired. Adding `[InverseProperty]` to one of the paired navigations resolves this ambiguity and allows EF to build the model. + + +[!code-csharp[InverseOnPrincipalNavigation](../../../../samples/core/Modeling/Relationships/MappingAttributes.cs?name=InverseOnPrincipalNavigation)] + +> [!IMPORTANT] +> `[InverseProperty]` is only needed when there is more than one relationship between the same types. With a single relationship, the two navigations are paired automatically. + +## DeleteBehaviorAttribute + +[By convention](xref:core/modeling/relationships/conventions), EF uses the the `ClientSetNull` for optional relationships, and the `Cascade` behavior for required relationships. This can be changed by placing the on one of the navigations of the relationship. For example: + + +[!code-csharp[DeleteBehaviorOnDependentNavigation](../../../../samples/core/Modeling/Relationships/MappingAttributes.cs?name=DeleteBehaviorOnDependentNavigation)] + +See [_Cascade delete_](xref:core/saving/cascade-delete) for more information on cascading behaviors. diff --git a/entity-framework/core/modeling/relationships/navigations.md b/entity-framework/core/modeling/relationships/navigations.md new file mode 100644 index 0000000000..4d61d39ba6 --- /dev/null +++ b/entity-framework/core/modeling/relationships/navigations.md @@ -0,0 +1,179 @@ +--- +title: Relationship navigations - EF Core +description: Reference and collection navigations in Entity Framework Core +author: ajcvickers +ms.date: 02/25/2023 +uid: core/modeling/relationships/navigations +--- +# Relationship navigations + +EF Core relationships are defined by [foreign keys](xref:core/modeling/relationships/foreign-and-principal-keys). Navigations are layered over foreign keys to provide a natural, object-oriented view for reading and manipulating relationships. By using navigations, applications can work with graphs of entities without being concerned with what is happening to the foreign key values. + +> [!IMPORTANT] +> Multiple relationships cannot share navigations. Any foreign key can be associated with at most one navigation from principal to dependent, and at most one navigation from dependent to principal. + +> [!TIP] +> There is no need to make navigations virtual unless they are being used by [lazy-loading](xref:core/querying/related-data/lazy) or [change-tracking](xref:core/change-tracking/change-detection#change-tracking-proxies) proxies. + +## Reference navigations + +Navigations come in two forms--reference and collection. Reference navigations are simple object references to another entity. They represent the "one" side(s) of [one-to-many](xref:core/modeling/relationships/one-to-many) and [one-to-one](xref:core/modeling/relationships/one-to-one) relationships. For example: + +```csharp +public Blog TheBlog { get; set; } +``` + +Reference navigations must have a setter, although it does not need to be public. Reference navigations should not be automatically initialized to a non-null default value; doing so is equivalent to asserting that an entity exists when it does not. + +When using [C# nullable reference types](/dotnet/csharp/tutorials/nullable-reference-types), reference navigations must be nullable for optional relationships: + +```csharp +public Blog? TheBlog { get; set; } +``` + +Reference navigations for required relationships can be nullable or non-nullable. + +## Collection navigations + +Collection navigations are instances of a .NET collection type; that is, any type implementing . The collection contains instances of the related entity type, of which there can be any number. They represent the "many" side(s) of [one-to-many](xref:core/modeling/relationships/one-to-many) and [many-to-many](xref:core/modeling/relationships/many-to-many) relationships. For example: + +```csharp +public ICollection ThePosts { get; set; } +``` + +Collection navigations do not need to have a setter. It is common to initialize the collection inline, thereby removing the need to ever check if the property is `null`. For example: + +```csharp +public ICollection ThePosts { get; } = new List(); +``` + +> [!TIP] +> Don't accidentally create an expression bodied property, such as `public ICollection ThePosts => new List();`. This will create a new, empty collection instance each time the property is accessed, and will therefore be useless as a navigation. + +### Collection types + +The underlying collection instance must be implement , and must have a working `Add` method. It is common to use or . `List` is efficient for small numbers of related entities and maintains a stable ordering. `HashSet` has more efficient lookups for large numbers of entities, but does not have stable ordering. You can also use your own custom collection implementation. + +> [!IMPORTANT] +> The collection must use reference equality. When creating a `HashSet` for a collection navigation, make sure to use . + +Arrays cannot be used for collection navigations because, even though they implement `ICollection`, the `Add` method throws an exception when called. + +Even though the collection instance must be an `ICollection`, the collection does not need to be exposed as such. For example, it is common to expose the navigation as an , which provides a read-only view that cannot be randomly modified by application code. For example: + +```csharp +public class Blog +{ + public int Id { get; set; } + public IEnumerable ThePosts { get; } = new List(); +} +``` + +A variation on this pattern includes methods for manipulation of the collection as needed. For example: + +```csharp +public class Blog +{ + private readonly List _posts = new(); + + public int Id { get; set; } + + public IEnumerable Posts => _posts; + + public void AddPost(Post post) => _posts.Add(post); +} +``` + +Application code could still cast the exposed collection to an `ICollection` and then manipulate it. If this is a concern, then the entity could return a defensive copy of the collection. For example: + +```csharp +public class Blog +{ + private readonly List _posts = new(); + + public int Id { get; set; } + + public IEnumerable Posts => _posts.ToList(); + + public void AddPost(Post post) => _posts.Add(post); +} +``` + +Carefully consider whether the value gained from this is high enough that it outweighs the overhead of creating a copy of the collection every time the navigation is accessed. + +> [!TIP] +> This final pattern works because, by-default, EF accesses the collection through its backing field. This means that EF itself adds and removes entities from the actual collection, while applications only interact with a defensive copy of the collection. + +### Initialization of collection navigations + +Collection navigations can be initialized by the entity type, either eagerly: + +```csharp +public class Blog +{ + public ICollection Posts { get; } = new List(); +} +``` + +Or lazily: + +```csharp +public class Blog +{ + private ICollection? _posts; + + public ICollection Posts => _posts ??= new List(); +} +``` + +If EF needs to add an entity to a collection navigation, for example, while executing a query, then it will initialize the collection if it is currently `null`. The instance created depends on the exposed type of the navigation. + +- If the navigation is exposed as a `HashSet`, then an instance of `HashSet` using `ReferenceEqualityComparer` is created. +- Otherwise, if the navigation is exposed as a concrete type with a parameterless constructor, then an instance of that concrete type is created. This applies to `List`, but also to other collection types, including custom collection types. +- Otherwise, if the navigation is exposed as an `IEnumerable`, an `ICollection`, or an `ISet`, then an instance of `HashSet` using `ReferenceEqualityComparer` is created. +- Otherwise, if the navigation is exposed as an `IList`, then an instance of `List` is created. +- Otherwise, an exception is thrown. + +> [!NOTE] +> If [notification entities](xref:core/change-tracking/change-detection#notification-entities), including [change-tracking proxies](xref:core/change-tracking/change-detection#change-tracking-proxies), are being used, then and are used in place of `List` and `HashSet`. + +## Configuring navigations + +Navigations are included in the model as part of configuring a relationship. That is, by [convention](xref:core/modeling/relationships/conventions), or using `HasOne`, `HasMany`, etc. in the model building API. Most configuration associated with navigations is done by configuring the relationship itself. + +However, there are some types of configuration which are specific to the navigation properties themselves, rather than being part of the overall relationship configuration. This type of configuration is done with the `Navigation` method. For example, to force EF to access the navigation through its property, rather than using the backing field: + +```csharp +protected override void OnModelCreating(ModelBuilder modelBuilder) +{ + modelBuilder.Entity() + .Navigation(e => e.Posts) + .UsePropertyAccessMode(PropertyAccessMode.Property); + + modelBuilder.Entity() + .Navigation(e => e.Blog) + .UsePropertyAccessMode(PropertyAccessMode.Property); +} +``` + +> [!NOTE] +> The `Navigation` call cannot be used to create a navigation property. It is only used to configure a navigation property which has been previously created by defining a relationship or from a convention. + +### Required navigations + +A navigation from dependent to principal is required if the relationship is required, which in turn means that the foreign key property is non-nullable. Conversely, the navigation is optional if the foreign key is nullable, and the relationship is therefore optional. + +Reference navigations from principal to dependent are different. In most cases, a principal entity can _always_ exist without any dependent entities. That is, a required relationship does _not_ indicate that there will always be at least one dependent entity. There is no way in the EF model, and also no standard way in a relational database, to ensure that a principal is associated with a certain number of dependents. If this is needed, then it must be implemented in application (business) logic. + +There is one exception to this rule--when the principal and dependent types are sharing the same table in a relational database, or contained in a document. This can happen with [owned types](xref:core/modeling/owned-entities), or non-owned types [sharing the same table](xref:core/modeling/table-splitting). In this case, the navigation property from the principal to the dependent can be marked as required, indicating that the dependent must exist. + +Configuration of the navigation property as required is done using the `Navigation` method. For example: + +```csharp +protected override void OnModelCreating(ModelBuilder modelBuilder) +{ + modelBuilder.Entity() + .Navigation(e => e.BlogHeader) + .IsRequired(); +} +``` diff --git a/entity-framework/core/modeling/relationships/one-to-many.md b/entity-framework/core/modeling/relationships/one-to-many.md new file mode 100644 index 0000000000..7ae5f1acd4 --- /dev/null +++ b/entity-framework/core/modeling/relationships/one-to-many.md @@ -0,0 +1,585 @@ +--- +title: One-to-many relationships - EF Core +description: How to configure one-to-many relationships between entity types when using Entity Framework Core +author: ajcvickers +ms.date: 02/25/2023 +uid: core/modeling/relationships/one-to-many +--- +# One-to-many relationships + +One-to-many relationships are used when a single entity is associated with any number of other entities. For example, a `Blog` can have many associated `Posts`, but each `Post` is associated with only one `Blog`. + +A one-to-many relationship is made up from: + +- One or more [primary or alternate key](xref:core/modeling/relationships/foreign-and-principal-keys#principal-keys) properties on the principal entity--the "one" end of the relationship. For example, `Blog.Id`. +- One or more [foreign key](xref:core/modeling/relationships/foreign-and-principal-keys#foreign-keys) properties on the dependent entity--the "many" end of the relationship. For example, `Post.BlogId`. +- Optionally, a [collection navigation](xref:core/modeling/relationships/navigations#collection-navigations) on the principal entity referencing the dependent entities. For example, `Blog.Posts`. +- Optionally, a [reference navigation](xref:core/modeling/relationships/navigations#reference-navigations) on the dependent entity referencing the principal entity. For example, `Post.Blog`. + +## Examples + +The following sections contain examples of one-to-many relationships showing different combinations of navigations, foreign keys, and primary or alternate keys. + +> [!TIP] +> The code for all the examples below can be found in [OneToMany.cs](https://github.com/dotnet/EntityFramework.Docs/tree/main/samples/core/Modeling/Relationships/OneToMany.cs). + +### Required one-to-many + + +[!code-csharp[OneToManyRequired](../../../../samples/core/Modeling/Relationships/OneToMany.cs?name=OneToManyRequired)] + +For this relationship: + +- The foreign key property `Post.BlogId` is not nullable. This makes the relationship "required" because every dependent (`Post`) _must be related to some principal_ (`Blog`), since its foreign key property must be set to some value. +- Both entities have navigations pointing to the related entity or entities on the other side of the relationship. + +> [!NOTE] +> A required relationship ensures that every dependent entity must be associated with some principal entity. However, a principal entity can _always_ exist without any dependent entities. That is, a required relationship does _not_ indicate that there will always be at least one dependent entity. There is no way in the EF model, and also no standard way in a relational database, to ensure that a principal is associated with a certain number of dependents. If this is needed, then it must be implemented in application (business) logic. See [_Required navigations_](xref:core/modeling/relationships/navigations#required-navigations) for more information. + +> [!TIP] +> A relationship with two navigations--one from dependent to principal and an inverse from principal to dependents--is known as a bidirectional relationship. + +This relationship is [discovered by convention](xref:core/modeling/relationships/conventions). That is: + +- `Blog` is discovered as the principal in the relationship, and `Post` is discovered as the dependent. +- `Post.BlogId` is discovered as a foreign key of the dependent referencing the `Blog.Id` primary key of the the principal. The relationship is discovered as required because `Post.BlogId` is not nullable. +- `Blog.Posts` is discovered as the collection navigation. +- `Post.Blog` is discovered as the reference navigation. + +> [!IMPORTANT] +> When using [C# nullable reference types](/dotnet/csharp/tutorials/nullable-reference-types), the reference navigation must be nullable if the foreign key property is nullable. If the foreign key property is non-nullable, then the reference navigation may be nullable or not. In this case, `Post.BlogId` is non-nullable and `Post.Blog` is also non-nullable. The `= null!;` construct is used to mark this as intentional for the C# compiler, since EF typically sets the `Blog` instance and it cannot be null for a fully loaded relationship. See [_Working with Nullable Reference Types_](xref:core/miscellaneous/nullable-reference-types) for more information. + +For cases where the navigations, foreign key, or required/optional nature of the relationship are not discovered by convention, these things can be configured explicitly. For example: + + +[!code-csharp[OneToManyRequiredFromPrincipal](../../../../samples/core/Modeling/Relationships/OneToMany.cs?name=OneToManyRequiredFromPrincipal)] + +In the example above, configuration of the relationships starts with `HasMany` on the principal entity type (`Blog`) and then follows this with `WithOne`. As with all relationships, it is exactly equivalent to start with dependent entity type (`Post`) and use `HasOne` followed by `WithMany`. For example: + + +[!code-csharp[OneToManyRequiredFromDependent](../../../../samples/core/Modeling/Relationships/OneToMany.cs?name=OneToManyRequiredFromDependent)] + +Neither of these options is better than the other; they both result in exactly the same configuration. + +> [!TIP] +> It is never necessary to configure a relationship twice, once starting from the principal, and then again starting from the dependent. Also, attempting to configure the principal and dependent halves of a relationship separately generally does not work. Choose to configure each relationship from either one end or the other and then write the configuration code only once. + +### Optional one-to-many + + +[!code-csharp[OneToManyOptionalShadow](../../../../samples/core/Modeling/Relationships/OneToMany.cs?name=OneToManyOptionalShadow)] + +This is the same as the previous example, except that the foreign key property and navigation to the principal are now nullable. This makes the relationship "optional" because a dependent (`Post`) can _not_ be related _any_ principal (`Blog`) by setting its foreign key property and navigation to `null`. + +> [!IMPORTANT] +> When using [C# nullable reference types](/dotnet/csharp/tutorials/nullable-reference-types), the reference navigation must be nullable if the foreign key property is nullable. In this case, `Post.BlogId` is nullable, so `Post.Blog` must be nullable too. See [_Working with Nullable Reference Types_](xref:core/miscellaneous/nullable-reference-types) for more information. + +As before, this relationship is [discovered by convention](xref:core/modeling/relationships/conventions). For cases where the navigations, foreign key, or required/optional nature of the relationship are not discovered by convention, these things can be configured explicitly. For example: + + +[!code-csharp[OneToManyOptionalFromPrincipal](../../../../samples/core/Modeling/Relationships/OneToMany.cs?name=OneToManyOptionalFromPrincipal)] + +### Required one-to-many with shadow foreign key + + +[!code-csharp[OneToManyRequiredShadow](../../../../samples/core/Modeling/Relationships/OneToMany.cs?name=OneToManyRequiredShadow)] + +Following on from the previous two examples, this example removes the foreign key property from the dependent entity type. EF therefore creates a [shadow foreign key property](xref:core/modeling/shadow-properties) called `BlogId` of type `int`. + +An important point to note here is that [C# nullable reference types](/dotnet/csharp/tutorials/nullable-reference-types) are being used, so the nullability of the reference navigation is used to determine whether or not the foreign key property is nullable, and therefore whether the relationship is optional or required. If nullable reference types are not being used, then the shadow foreign key property will be nullable by default making the relationship optional by default. In this case, use `IsRequired` to force the shadow foreign key property to be non-nullable and make the relationship required. + +As before, this relationship is [discovered by convention](xref:core/modeling/relationships/conventions). For cases where the navigations, foreign key, or required/optional nature of the relationship are not discovered by convention, these things can be configured explicitly. For example: + + +[!code-csharp[OneToManyRequiredShadowFromPrincipal](../../../../samples/core/Modeling/Relationships/OneToMany.cs?name=OneToManyRequiredShadowFromPrincipal)] + +### Optional one-to-many with shadow foreign key + + +[!code-csharp[OneToManyOptionalShadow](../../../../samples/core/Modeling/Relationships/OneToMany.cs?name=OneToManyOptionalShadow)] + +Like the previous example, the foreign key property has been removed from the dependent entity type. EF therefore creates a [shadow foreign key property](xref:core/modeling/shadow-properties) called `BlogId` of type `int?`. Unlike the previous example, this time the foreign key property is created as nullable because [C# nullable reference types](/dotnet/csharp/tutorials/nullable-reference-types) are being used and the navigation on the dependent entity type is nullable. This makes the relationship optional. + +When C# nullable reference types are not being used, then the foreign key property will also, by default, be created as nullable. This means relationships with automatically created shadow properties are optional by default. + +As before, this relationship is [discovered by convention](xref:core/modeling/relationships/conventions). For cases where the navigations, foreign key, or required/optional nature of the relationship are not discovered by convention, these things can be configured explicitly. For example: + + +[!code-csharp[OneToManyOptionalShadowFromPrincipal](../../../../samples/core/Modeling/Relationships/OneToMany.cs?name=OneToManyOptionalShadowFromPrincipal)] + +### One-to-many without navigation to principal + + +[!code-csharp[OneToManyRequiredNoNavigationToPrincipal](../../../../samples/core/Modeling/Relationships/OneToMany.cs?name=OneToManyRequiredNoNavigationToPrincipal)] + +For this example, the foreign key property has been re-introduced, but the navigation on the dependent has been removed. + +> [!TIP] +> A relationship with only one navigation--one from dependent to principal or one from principal to dependent(s), but not both--is known as a unidirectional relationship. + +As before, this relationship is [discovered by convention](xref:core/modeling/relationships/conventions). For cases where the navigations, foreign key, or required/optional nature of the relationship are not discovered by convention, these things can be configured explicitly. For example: + + +[!code-csharp[OneToManyRequiredFromPrincipalNoNavigationToPrincipal](../../../../samples/core/Modeling/Relationships/OneToMany.cs?name=OneToManyRequiredFromPrincipalNoNavigationToPrincipal)] + +Notice that the call to `WithOne` has no arguments. This is the way to tell EF that there is no navigation from `Post` to `Blog`. + +If configuration starts from the entity with no navigation, then the type of the entity on the other end of the relationship must be explicitly specified using the generic `HasOne<>()` call. For example: + + +[!code-csharp[OneToManyRequiredFromDependentNoNavigationToPrincipal](../../../../samples/core/Modeling/Relationships/OneToMany.cs?name=OneToManyRequiredFromDependentNoNavigationToPrincipal)] + +### One-to-many without navigation to principal and with shadow foreign key + + +[!code-csharp[OneToManyRequiredShadowNoNavigationToPrincipal](../../../../samples/core/Modeling/Relationships/OneToMany.cs?name=OneToManyRequiredShadowNoNavigationToPrincipal)] + +This example combines two of the previous examples by removing both the foreign key property and the navigation on the dependent. + +This relationship is [discovered by convention](xref:core/modeling/relationships/conventions) as an optional relationship. Since there is nothing in the code that could be used to indicate that it should be required, some minimal configuration using `IsRequired` is needed to create a required relationship. For example: + + +[!code-csharp[OneToManyRequiredShadowNoNavigationToPrincipalConfig](../../../../samples/core/Modeling/Relationships/OneToMany.cs?name=OneToManyRequiredShadowNoNavigationToPrincipalConfig)] + +A more complete configuration can be used to explicitly configure the navigation and foreign key name, with an appropriate call to `IsRequired()` or `IsRequired(false)` as needed. For example: + + +[!code-csharp[OneToManyRequiredShadowFromPrincipalNoNavigationToPrincipal](../../../../samples/core/Modeling/Relationships/OneToMany.cs?name=OneToManyRequiredShadowFromPrincipalNoNavigationToPrincipal)] + +### One-to-many without navigation to dependents + + +[!code-csharp[OneToManyRequiredNoNavigationToDependents](../../../../samples/core/Modeling/Relationships/OneToMany.cs?name=OneToManyRequiredNoNavigationToDependents)] + +The previous two examples had navigations from the principal to dependents, but no navigation from the dependent to principal. For the next couple of examples, the navigation on the dependent is re-introduced, while the navigation on the principal is removed instead. + +As before, this relationship is [discovered by convention](xref:core/modeling/relationships/conventions). For cases where the navigations, foreign key, or required/optional nature of the relationship are not discovered by convention, these things can be configured explicitly. For example: + + +[!code-csharp[OneToManyRequiredFromDependentNoNavigationToDependents](../../../../samples/core/Modeling/Relationships/OneToMany.cs?name=OneToManyRequiredFromDependentNoNavigationToDependents)] + +Notice again that `WithMany()` is called with no arguments to indicate that there is no navigation in this direction. + +If configuration starts from the entity with no navigation, then the type of the entity on the other end of the relationship must be explicitly specified using the generic `HasMany<>()` call. For example: + + +[!code-csharp[OneToManyRequiredFromPrincipalNoNavigationToDependents](../../../../samples/core/Modeling/Relationships/OneToMany.cs?name=OneToManyRequiredFromPrincipalNoNavigationToDependents)] + +### One-to-many with no navigations + +Occasionally, it can be useful to configure a relationship with no navigations. Such a relationship can only be manipulated by changing the foreign key value directly. + + +[!code-csharp[OneToManyRequiredNoNavigations](../../../../samples/core/Modeling/Relationships/OneToMany.cs?name=OneToManyRequiredNoNavigations)] + +This relationship is not discovered by convention, since there are no navigations indicating that the two types are related. It can be configured explicitly in `OnModelCreating`. For example: + + +[!code-csharp[OneToManyRequiredNoNavigationsConfig](../../../../samples/core/Modeling/Relationships/OneToMany.cs?name=OneToManyRequiredNoNavigationsConfig)] + +With this configuration, the `Post.BlogId` property is still detected as the foreign key by convention, and the relationship is required because the foreign key property is not nullable. The relationship can be made "optional" by making the foreign key property nullable. + +A more complete explicit configuration of this relationship is:: + + +[!code-csharp[OneToManyRequiredFromPrincipalNoNavigations](../../../../samples/core/Modeling/Relationships/OneToMany.cs?name=OneToManyRequiredFromPrincipalNoNavigations)] + +### One-to-many with alternate key + +In all the examples so far, the foreign key property on the dependent is constrained to the primary key property on the principal. The foreign key can instead be constrained to a different property, which then becomes an alternate key for the principal entity type. For example: + + +[!code-csharp[OneToManyRequiredWithAlternateKey](../../../../samples/core/Modeling/Relationships/OneToMany.cs?name=OneToManyRequiredWithAlternateKey)] + +This relationship is not discovered by convention, since EF will always, by convention, create a relationship to the primary key. It can be configured explicitly in `OnModelCreating` using a call to `HasPrincipalKey`. For example: + + +[!code-csharp[OneToManyRequiredWithAlternateKeyConfig](../../../../samples/core/Modeling/Relationships/OneToMany.cs?name=OneToManyRequiredWithAlternateKeyConfig)] + +`HasPrincipalKey` can be combined with other calls to explicitly configure the navigations, foreign key properties, and required/optional nature. For example: + + +[!code-csharp[OneToManyRequiredFromPrincipalWithAlternateKey](../../../../samples/core/Modeling/Relationships/OneToMany.cs?name=OneToManyRequiredFromPrincipalWithAlternateKey)] + +### One-to-many with composite foreign key + +In all the examples so far, the primary or alternate key property of the principal consisted of a single property. Primary or alternate keys can also be formed form more than one property--these are known as ["composite keys"](xref:core/modeling/keys). When the principal of a relationship has a composite key, then the foreign key of the dependent must also be a composite key with the same number of properties. For example: + + +[!code-csharp[OneToManyRequiredWithCompositeKey](../../../../samples/core/Modeling/Relationships/OneToMany.cs?name=OneToManyRequiredWithCompositeKey)] + +This relationship is discovered by convention. However, it will only be discovered if the composite key has been configured explicitly, since composite keys are not discovered automatically. For example: + + +[!code-csharp[OneToManyRequiredWithCompositeKeyConfig](../../../../samples/core/Modeling/Relationships/OneToMany.cs?name=OneToManyRequiredWithCompositeKeyConfig)] + +> [!IMPORTANT] +> A composite foreign key value is considered to be `null` if any of its property values are null. A composite foreign key with one property null and another non-null will not be considered a match for a primary or alternate key with the same values. Both will be considered `null`. + +Both `HasForeignKey` and `HasPrincipalKey` can be used to explicitly specify keys with multiple properties. For example: + + +[!code-csharp[OneToManyRequiredFromPrincipalWithCompositeKey](../../../../samples/core/Modeling/Relationships/OneToMany.cs?name=OneToManyRequiredFromPrincipalWithCompositeKey)] + +> [!TIP] +> In the code above, the calls to `HasKey` and `HasMany` have been grouped together into a nested builder. Nested builders remove the need to call `Entity<>()` multiple times for the same entity type, but are functionally equivalent to calling `Entity<>()` multiple times. + +### Required one-to-many without cascade delete + + +[!code-csharp[RequiredWithoutCascadeDelete](../../../../samples/core/Modeling/Relationships/OneToMany.cs?name=RequiredWithoutCascadeDelete)] + +By convention, required relationships are configured to [cascade delete](xref:core/saving/cascade-delete). This is because the dependent cannot exist in the database once the principal has been deleted. The database can be configured to generate an error, typically crashing the application, instead of automatically deleting dependent rows that can no longer exist. This requires some configuration: + + +[!code-csharp[RequiredWithoutCascadeDeleteConfig](../../../../samples/core/Modeling/Relationships/OneToMany.cs?name=RequiredWithoutCascadeDeleteConfig)] + +### Self-referencing one-to-many + +In all the previous examples, the principal entity type was different from the dependent entity type. This does not have to be the case. For example, in the types below, each `Employee` is related to other `Employees`. + + +[!code-csharp[SelfReferencing](../../../../samples/core/Modeling/Relationships/OneToMany.cs?name=SelfReferencing)] + +This relationship is [discovered by convention](xref:core/modeling/relationships/conventions). For cases where the navigations, foreign key, or required/optional nature of the relationship are not discovered by convention, these things can be configured explicitly. For example: + + +[!code-csharp[SelfReferencingConfig](../../../../samples/core/Modeling/Relationships/OneToMany.cs?name=SelfReferencingConfig)] diff --git a/entity-framework/core/modeling/relationships/one-to-one.md b/entity-framework/core/modeling/relationships/one-to-one.md new file mode 100644 index 0000000000..dc2ac7bfbe --- /dev/null +++ b/entity-framework/core/modeling/relationships/one-to-one.md @@ -0,0 +1,682 @@ +--- +title: One-to-one relationships - EF Core +description: How to configure one-to-one relationships between entity types when using Entity Framework Core +author: ajcvickers +ms.date: 02/25/2023 +uid: core/modeling/relationships/one-to-one +--- +# One-to-one relationships + +One-to-one relationships are used when one entity is associated with at most one other entity. For example, a `Blog` has one `BlogHeader`, and that `BlogHeader` belongs to a single `Blog`. + +A one-to-one relationship is made up from: + +- One or more [primary or alternate key](xref:core/modeling/relationships/foreign-and-principal-keys#principal-keys) properties on the principal entity. For example, `Blog.Id`. +- One or more [foreign key](xref:core/modeling/relationships/foreign-and-principal-keys#foreign-keys) properties on the dependent entity. For example, `BlogHeader.BlogId`. +- Optionally, a [reference navigation](xref:core/modeling/relationships/navigations#reference-navigations) on the principal entity referencing the dependent entity. For example, `Blog.Header`. +- Optionally, a [reference navigation](xref:core/modeling/relationships/navigations#reference-navigations) on the dependent entity referencing the principal entity. For example, `BlogHeader.Blog`. + +It is not always obvious which side of a one-to-one relationship should be the principal, and which side should be the dependent. Some considerations are: + +- If the database tables for the two types already exist, then the table with the foreign key column(s) must map to the dependent type. +- A type is usually the dependent type if it cannot logically exist without the other type. For example, it makes no sense to have a header for a blog that does not exist, so `BlogHeader` is naturally the dependent type. +- If there is a natural parent/child relationship, then the child is usually the dependent type. + +## Examples + +The following sections contain examples of one-to-one relationships showing different combinations of navigations, foreign keys, and primary or alternate keys. + +> [!TIP] +> The code for all the examples below can be found in [OneToOne.cs](https://github.com/dotnet/EntityFramework.Docs/tree/main/samples/core/Modeling/Relationships/OneToOne.cs). + +### Required one-to-one + + +[!code-csharp[OneToOneRequired](../../../../samples/core/Modeling/Relationships/OneToOne.cs?name=OneToOneRequired)] + +For this relationship: + +- The foreign key property `BlogHeader.BlogId` is not nullable. This makes the relationship "required" because every dependent (`BlogHeader`) _must be related to some principal_ (`Blog`), since its foreign key property must be set to some value. +- Both entities have navigations pointing to the related entity on the other side of the relationship. + +> [!NOTE] +> A required relationship ensures that every dependent entity must be associated with some principal entity. However, a principal entity can _always_ exist without any dependent entity. That is, a required relationship does _not_ indicate that there will always be a dependent entity. There is no way in the EF model, and also no standard way in a relational database, to ensure that a principal is associated with a dependent. If this is needed, then it must be implemented in application (business) logic. See [_Required navigations_](xref:core/modeling/relationships/navigations#required-navigations) for more information. + +> [!TIP] +> A relationship with two navigations--one from dependent to principal and an inverse from principal to dependent--is known as a bidirectional relationship. + +This relationship is [discovered by convention](xref:core/modeling/relationships/conventions). That is: + +- `Blog` is discovered as the principal in the relationship, and `BlogHeader` is discovered as the dependent. +- `BlogHeader.BlogId` is discovered as a foreign key of the dependent referencing the `Blog.Id` primary key of the the principal. The relationship is discovered as required because `BlogHeader.BlogId` is not nullable. +- `Blog.BlogHeader` is discovered as a reference navigation. +- `BlogHeader.Blog` is discovered as a reference navigation. + +> [!IMPORTANT] +> When using [C# nullable reference types](/dotnet/csharp/tutorials/nullable-reference-types), the navigation from the dependent to the principal must be nullable if the foreign key property is nullable. If the foreign key property is non-nullable, then the navigation may be nullable or not. In this case, `BlogHeader.BlogId` is non-nullable, and `BlogHeader.Blog` is also non-nullable. The `= null!;` construct is used to mark this as intentional for the C# compiler, since EF typically sets the `Blog` instance and it cannot be null for a fully loaded relationship. See [_Working with Nullable Reference Types_](xref:core/miscellaneous/nullable-reference-types) for more information. + +For cases where the navigations, foreign key, or required/optional nature of the relationship are not discovered by convention, these things can be configured explicitly. For example: + + +[!code-csharp[OneToOneRequiredFromPrincipal](../../../../samples/core/Modeling/Relationships/OneToOne.cs?name=OneToOneRequiredFromPrincipal)] + +In the example above, configuration of the relationships starts the principal entity type (`Blog`). As with all relationships, it is exactly equivalent to start with dependent entity type (`BlogHeader`) instead. For example: + + +[!code-csharp[OneToOneRequiredFromDependent](../../../../samples/core/Modeling/Relationships/OneToOne.cs?name=OneToOneRequiredFromDependent)] + +Neither of these options is better than the other; they both result in exactly the same configuration. + +> [!TIP] +> It is never necessary to configure a relationship twice, once starting from the principal, and then again starting from the dependent. Also, attempting to configure the principal and dependent halves of a relationship separately generally does not work. Choose to configure each relationship from either one end or the other and then write the configuration code only once. + +### Optional one-to-one + + +[!code-csharp[OneToOneOptionalShadow](../../../../samples/core/Modeling/Relationships/OneToOne.cs?name=OneToOneOptionalShadow)] + +This is the same as the previous example, except that the foreign key property and navigation to the principal are now nullable. This makes the relationship "optional" because a dependent (`BlogHeader`) can _not_ be related _any_ principal (`Blog`) by setting its foreign key property and navigation to `null`. + +> [!IMPORTANT] +> When using [C# nullable reference types](/dotnet/csharp/tutorials/nullable-reference-types), the navigation property from dependent to principal must be nullable if the foreign key property is nullable. In this case, `BlogHeader.BlogId` is nullable, so `BlogHeader.Blog` must be nullable too. See [_Working with Nullable Reference Types_](xref:core/miscellaneous/nullable-reference-types) for more information. + +As before, this relationship is [discovered by convention](xref:core/modeling/relationships/conventions). For cases where the navigations, foreign key, or required/optional nature of the relationship are not discovered by convention, these things can be configured explicitly. For example: + + +[!code-csharp[OneToOneOptionalFromPrincipal](../../../../samples/core/Modeling/Relationships/OneToOne.cs?name=OneToOneOptionalFromPrincipal)] + +### Required one-to-one with primary key to primary key relationship + + +[!code-csharp[OneToOneRequiredPkToPk](../../../../samples/core/Modeling/Relationships/OneToOne.cs?name=OneToOneRequiredPkToPk)] + +Unlike with one-to-many relationships, the dependent end of a one-to-one relationship may use its primary key property or properties as the foreign key property or properties. This is often called a PK-to-PK relationship. This is only possible when the principal and dependent types have the same primary key types, and the resulting relationship is always required, since the primary key of the dependent cannot be nullable. + +Any one-to-one relationship where the foreign key is not discovered by convention must be configured to indicate the principal and dependent ends of the relationship. This is typically done with a call to `HasForeignKey`. For example: + + +[!code-csharp[OneToOneRequiredPkToPkConfig](../../../../samples/core/Modeling/Relationships/OneToOne.cs?name=OneToOneRequiredPkToPkConfig)] + +> [!TIP] +> `HasPrincipalKey` can also used for this purpose, but doing so is less common. + +When no property is specified in the call to `HasForeignKey`, and the primary key is suitable, then it is used as the foreign key. For cases where the navigations, foreign key, or required/optional nature of the relationship are not discovered by convention, these things can be configured explicitly. For example: + + +[!code-csharp[OneToOneRequiredPkToPkFromPrincipal](../../../../samples/core/Modeling/Relationships/OneToOne.cs?name=OneToOneRequiredPkToPkFromPrincipal)] + +### Required one-to-one with shadow foreign key + + +[!code-csharp[OneToOneRequiredShadow](../../../../samples/core/Modeling/Relationships/OneToOne.cs?name=OneToOneRequiredShadow)] + +Following on from the previous example, this example removes the foreign key property from the dependent entity type. However, instead of using the primary key, EF is instead instructed to create a [shadow foreign key property](xref:core/modeling/shadow-properties) called `BlogId` of type `int`. + +An important point to note here is that [C# nullable reference types](/dotnet/csharp/tutorials/nullable-reference-types) are being used, so the nullability of the navigation from dependent to principal is used to determine whether or not the foreign key property is nullable, and therefore whether the relationship is optional or required. If nullable reference types are not being used, then the shadow foreign key property will be nullable by default making the relationship optional by default. In this case, use `IsRequired` to force the shadow foreign key property to be non-nullable and make the relationship required. + +This relationship again needs some configuration to indicate the principal and dependent ends: + + +[!code-csharp[OneToOneRequiredShadowConfig](../../../../samples/core/Modeling/Relationships/OneToOne.cs?name=OneToOneRequiredShadowConfig)] + +For cases where the navigations, foreign key, or required/optional nature of the relationship are not discovered by convention, these things can be configured explicitly. For example: + + +[!code-csharp[OneToOneRequiredShadowFromPrincipal](../../../../samples/core/Modeling/Relationships/OneToOne.cs?name=OneToOneRequiredShadowFromPrincipal)] + +### Optional one-to-one with shadow foreign key + + +[!code-csharp[OneToOneOptionalShadow](../../../../samples/core/Modeling/Relationships/OneToOne.cs?name=OneToOneOptionalShadow)] + +Like the previous example, the foreign key property has been removed from the dependent entity type. However, unlike the previous example, this time the foreign key property is created as nullable because [C# nullable reference types](/dotnet/csharp/tutorials/nullable-reference-types) are being used and the navigation on the dependent entity type is nullable. This makes the relationship optional. + +When C# nullable reference types are not being used, then the foreign key property will, by default, be created as nullable. This means relationships with automatically created shadow properties are optional by default. + +As before, this relationship needs some configuration to indicate the principal and dependent ends: + + +[!code-csharp[OneToOneOptionalShadowConfig](../../../../samples/core/Modeling/Relationships/OneToOne.cs?name=OneToOneOptionalShadowConfig)] + +For cases where the navigations, foreign key, or required/optional nature of the relationship are not discovered by convention, these things can be configured explicitly. For example: + + +[!code-csharp[OneToOneOptionalShadowFromPrincipal](../../../../samples/core/Modeling/Relationships/OneToOne.cs?name=OneToOneOptionalShadowFromPrincipal)] + +### One-to-one without navigation to principal + + +[!code-csharp[OneToOneRequiredNoNavigationToPrincipal](../../../../samples/core/Modeling/Relationships/OneToOne.cs?name=OneToOneRequiredNoNavigationToPrincipal)] + +For this example, the foreign key property has been re-introduced, but the navigation on the dependent has been removed. + +> [!TIP] +> A relationship with only one navigation--one from dependent to principal or one from principal to dependent, but not both--is known as a unidirectional relationship. + +This relationship is [discovered by convention](xref:core/modeling/relationships/conventions), since the foreign key is discovered, thereby indicating the dependent side. For cases where the navigations, foreign key, or required/optional nature of the relationship are not discovered by convention, these things can be configured explicitly. For example: + + +[!code-csharp[OneToOneRequiredFromPrincipalNoNavigationToPrincipal](../../../../samples/core/Modeling/Relationships/OneToOne.cs?name=OneToOneRequiredFromPrincipalNoNavigationToPrincipal)] + +Notice that the call to `WithOne` has no arguments. This is the way to tell EF that there is no navigation from `BlogHeader` to `Blog`. + +If configuration starts from the entity with no navigation, then the type of the entity on the other end of the relationship must be explicitly specified using the generic `HasOne<>()` call. For example: + + +[!code-csharp[OneToOneRequiredFromDependentNoNavigationToPrincipal](../../../../samples/core/Modeling/Relationships/OneToOne.cs?name=OneToOneRequiredFromDependentNoNavigationToPrincipal)] + +### One-to-one without navigation to principal and with shadow foreign key + + +[!code-csharp[OneToOneRequiredShadowNoNavigationToPrincipal](../../../../samples/core/Modeling/Relationships/OneToOne.cs?name=OneToOneRequiredShadowNoNavigationToPrincipal)] + +This example combines two of the previous examples by removing both the foreign key property and the navigation on the dependent. + +As before, this relationship needs some configuration to indicate the principal and dependent ends: + + +[!code-csharp[OneToOneRequiredShadowNoNavigationToPrincipalConfig](../../../../samples/core/Modeling/Relationships/OneToOne.cs?name=OneToOneRequiredShadowNoNavigationToPrincipalConfig)] + +A more complete configuration can be used to explicitly configure the navigation and foreign key name, with an appropriate call to `IsRequired()` or `IsRequired(false)` as needed. For example: + + +[!code-csharp[OneToOneRequiredShadowFromPrincipalNoNavigationToPrincipal](../../../../samples/core/Modeling/Relationships/OneToOne.cs?name=OneToOneRequiredShadowFromPrincipalNoNavigationToPrincipal)] + +### One-to-one without navigation to dependents + + +[!code-csharp[OneToOneRequiredNoNavigationToDependents](../../../../samples/core/Modeling/Relationships/OneToOne.cs?name=OneToOneRequiredNoNavigationToDependents)] + +The previous two examples had navigations from the principal to dependents, but no navigation from the dependent to principal. For the next couple of examples, the navigation on the dependent is re-introduced, while the navigation on the principal is removed instead. + +By convention, EF will treat this as a one-to-many relationship. Some minimal configuration is needed to make it one-to-one: + + +[!code-csharp[OneToOneRequiredNoNavigationToDependentsConfig](../../../../samples/core/Modeling/Relationships/OneToOne.cs?name=OneToOneRequiredNoNavigationToDependentsConfig)] + +Notice again that `WithOne()` is called with no arguments to indicate that there is no navigation in this direction. + +For cases where the navigations, foreign key, or required/optional nature of the relationship are not discovered by convention, these things can be configured explicitly. For example: + + +[!code-csharp[OneToOneRequiredFromDependentNoNavigationToDependents](../../../../samples/core/Modeling/Relationships/OneToOne.cs?name=OneToOneRequiredFromDependentNoNavigationToDependents)] + +If configuration starts from the entity with no navigation, then the type of the entity on the other end of the relationship must be explicitly specified using the generic `HasMany<>()` call. For example: + + +[!code-csharp[OneToOneRequiredFromPrincipalNoNavigationToDependents](../../../../samples/core/Modeling/Relationships/OneToOne.cs?name=OneToOneRequiredFromPrincipalNoNavigationToDependents)] + +### One-to-one with no navigations + +Occasionally, it can be useful to configure a relationship with no navigations. Such a relationship can only be manipulated by changing the foreign key value directly. + + +[!code-csharp[OneToOneRequiredNoNavigations](../../../../samples/core/Modeling/Relationships/OneToOne.cs?name=OneToOneRequiredNoNavigations)] + +This relationship is not discovered by convention, since there are no navigations indicating that the two types are related. It can be configured explicitly in `OnModelCreating`. For example: + + +[!code-csharp[OneToOneRequiredNoNavigationsConfig](../../../../samples/core/Modeling/Relationships/OneToOne.cs?name=OneToOneRequiredNoNavigationsConfig)] + +With this configuration, the `BlogHeader.BlogId` property is still detected as the foreign key by convention, and the relationship is "required" because the foreign key property is not nullable. The relationship can be made "optional" by making the foreign key property nullable. + +A more complete explicit configuration of this relationship is:: + + +[!code-csharp[OneToOneRequiredFromPrincipalNoNavigations](../../../../samples/core/Modeling/Relationships/OneToOne.cs?name=OneToOneRequiredFromPrincipalNoNavigations)] + +### One-to-one with alternate key + +In all the examples so far, the foreign key property on the dependent is constrained to the primary key property on the principal. The foreign key can instead be constrained to a different property, which then becomes an alternate key for the principal entity type. For example: + + +[!code-csharp[OneToOneRequiredWithAlternateKey](../../../../samples/core/Modeling/Relationships/OneToOne.cs?name=OneToOneRequiredWithAlternateKey)] + +This relationship is not discovered by convention, since EF will always, by convention, create a relationship to the primary key. It can be configured explicitly in `OnModelCreating` using a call to `HasPrincipalKey`. For example: + + +[!code-csharp[OneToOneRequiredWithAlternateKeyConfig](../../../../samples/core/Modeling/Relationships/OneToOne.cs?name=OneToOneRequiredWithAlternateKeyConfig)] + +`HasPrincipalKey` can be combined with other calls to explicitly configure the navigations, foreign key properties, and required/optional nature. For example: + + +[!code-csharp[OneToOneRequiredFromPrincipalWithAlternateKey](../../../../samples/core/Modeling/Relationships/OneToOne.cs?name=OneToOneRequiredFromPrincipalWithAlternateKey)] + +### One-to-one with composite foreign key + +In all the examples so far, the primary or alternate key property of the principal consisted of a single property. Primary or alternate keys can also be formed form more than one property--these are known as ["composite keys"](xref:core/modeling/keys). When the principal of a relationship has a composite key, then the foreign key of the dependent must also be a composite key with the same number of properties. For example: + + +[!code-csharp[OneToOneRequiredWithCompositeKey](../../../../samples/core/Modeling/Relationships/OneToOne.cs?name=OneToOneRequiredWithCompositeKey)] + +This relationship is discovered by convention. However, it will only be discovered if the composite key has been configured explicitly, since composite keys are not discovered automatically. For example: + + +[!code-csharp[OneToOneRequiredWithCompositeKeyConfig](../../../../samples/core/Modeling/Relationships/OneToOne.cs?name=OneToOneRequiredWithCompositeKeyConfig)] + +> [!IMPORTANT] +> A composite foreign key value is considered to be `null` if any of its property values are null. A composite foreign key with one property null and another non-null will not be considered a match for a primary or alternate key with the same values. Both will be considered `null`. + +Both `HasForeignKey` and `HasPrincipalKey` can be used to explicitly specify keys with multiple properties. For example: + + +[!code-csharp[OneToOneRequiredFromPrincipalWithCompositeKey](../../../../samples/core/Modeling/Relationships/OneToOne.cs?name=OneToOneRequiredFromPrincipalWithCompositeKey)] + +> [!TIP] +> In the code above, the calls to `HasKey` and `HasOne` have been grouped together into a nested builder. Nested builders remove the need to call `Entity<>()` multiple times for the same entity type, but are functionally equivalent to calling `Entity<>()` multiple times. + +### Required one-to-one without cascade delete + + +[!code-csharp[RequiredWithoutCascadeDelete](../../../../samples/core/Modeling/Relationships/OneToOne.cs?name=RequiredWithoutCascadeDelete)] + +By convention, required relationships are configured to [cascade delete](xref:core/saving/cascade-delete). This is because the dependent cannot exist in the database once the principal has been deleted. The database can be configured to generate an error, typically crashing the application, instead of automatically deleting dependent rows that can no longer exist. This requires some configuration: + + +[!code-csharp[RequiredWithoutCascadeDeleteConfig](../../../../samples/core/Modeling/Relationships/OneToOne.cs?name=RequiredWithoutCascadeDeleteConfig)] + +### Self-referencing one-to-one + +In all the previous examples, the principal entity type was different from the dependent entity type. This does not have to be the case. For example, in the types below, each `Person` is optionally related to another `Person`. + + +[!code-csharp[SelfReferencing](../../../../samples/core/Modeling/Relationships/OneToOne.cs?name=SelfReferencing)] + +This relationship is [discovered by convention](xref:core/modeling/relationships/conventions). For cases where the navigations, foreign key, or required/optional nature of the relationship are not discovered by convention, these things can be configured explicitly. For example: + + +[!code-csharp[SelfReferencingConfig](../../../../samples/core/Modeling/Relationships/OneToOne.cs?name=SelfReferencingConfig)] diff --git a/entity-framework/core/modeling/table-splitting.md b/entity-framework/core/modeling/table-splitting.md index a3ca91b233..aca280051b 100644 --- a/entity-framework/core/modeling/table-splitting.md +++ b/entity-framework/core/modeling/table-splitting.md @@ -45,7 +45,7 @@ Saving and querying entities using table splitting is done in the same way as ot If all of the columns used by a dependent entity are `NULL` in the database, then no instance for it will be created when queried. This allows modeling an optional dependent entity, where the relationship property on the principal would be null. Note that this would also happen if all of the dependent's properties are optional and set to `null`, which might not be expected. -However, the additional check can impact query performance. In addition, if the dependent entity type has dependents of its own, then determining whether an instance should be created becomes non-trivial. To avoid these issues the dependent entity type can be marked as required, see [Required one-to-one dependents](xref:core/modeling/relationships#one-to-one) for more information. +However, the additional check can impact query performance. In addition, if the dependent entity type has dependents of its own, then determining whether an instance should be created becomes non-trivial. To avoid these issues the dependent entity type can be marked as required, see [Required one-to-one dependents](xref:core/modeling/relationships/navigations#required-navigations) for more information. ### Concurrency tokens @@ -162,7 +162,7 @@ Notice also that, if necessary, different column names can be specified for each ### Configuring the linking foreign key -The FK linking the mapped tables is targeting the same properties on which it is declared. Normally it wouldn't be created in the database, as it would be redundant. But there's an exception for when the entity type is mapped to more than one table. To change its facets you can use the [relationship configuration Fluent API](xref:core/modeling/relationships#foreign-key): +The FK linking the mapped tables is targeting the same properties on which it is declared. Normally it wouldn't be created in the database, as it would be redundant. But there's an exception for when the entity type is mapped to more than one table. To change its facets you can use the [relationship configuration Fluent API](xref:core/modeling/relationships/foreign-and-principal-keys):