Skip to content

Commit

Permalink
Document table-specific facet configuration
Browse files Browse the repository at this point in the history
Document composite PK configuration using data annotations
Document unidirectional many-to-many configuration and additional UsingEntity overloads

Fixes #3849
Fixes #2944
  • Loading branch information
AndriySvyryd committed Nov 8, 2022
1 parent e4fea3c commit 387b485
Show file tree
Hide file tree
Showing 6 changed files with 165 additions and 9 deletions.
2 changes: 1 addition & 1 deletion entity-framework/core/modeling/indexes.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ Attempting to insert more than one entity with the same values for the index's c
> [!NOTE]
> This feature is being introduced in EF Core 7.0.
In most databases, each column covered by an index can be either ascending or descending. For indexes covering only one column, this typically does not matter: the database can traverse the index in reverse order asif needed. However, for composite indexes, the ordering can be crucial for good performance, and can mean the difference between an index getting used by a query or not. In general, the index columns' sort orders should correspond to those specified in the `ORDER BY` clause of your query.
In most databases, each column covered by an index can be either ascending or descending. For indexes covering only one column, this typically does not matter: the database can traverse the index in reverse order as needed. However, for composite indexes, the ordering can be crucial for good performance, and can mean the difference between an index getting used by a query or not. In general, the index columns' sort orders should correspond to those specified in the `ORDER BY` clause of your query.

The index sort order is ascending by default. You can make all columns have descending order as follows:

Expand Down
6 changes: 6 additions & 0 deletions entity-framework/core/modeling/inheritance.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ In the TPT mapping pattern, all the types are mapped to individual tables. Prope
> [!TIP]
> Instead of calling `ToTable` on each entity type you can call `modelBuilder.Entity<Blog>().UseTptMappingStrategy()` on each root entity type and the table names will be generated by EF.
> [!TIP]
> To configure different column names for the primary key columns in each table see [Table-specific facet configuration](xref:core/modeling/table-splitting#table-specific-facet-configuration).
EF will create the following database schema for the model above.

```sql
Expand Down Expand Up @@ -115,6 +118,9 @@ In the TPC mapping pattern, all the types are mapped to individual tables. Each
> [!TIP]
> Instead of calling `ToTable` on each entity type just calling `modelBuilder.Entity<Blog>().UseTpcMappingStrategy()` on each root entity type will generate the table names by convention.
> [!TIP]
> To configure different column names for the primary key columns in each table see [Table-specific facet configuration](xref:core/modeling/table-splitting#table-specific-facet-configuration).
EF will create the following database schema for the model above.

```sql
Expand Down
29 changes: 23 additions & 6 deletions entity-framework/core/modeling/keys.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
title: Keys - EF Core
description: How to configure keys for entity types when using Entity Framework Core
author: AndriySvyryd
ms.date: 1/10/2021
ms.date: 10/14/2022
uid: core/modeling/keys
---
# Keys
Expand All @@ -22,17 +22,34 @@ You can configure a single property to be the primary key of an entity as follow

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

[!code-csharp[Main](../../../samples/core/Modeling/Keys/DataAnnotations/KeySingle.cs?name=KeySingle&highlight=3)]
[!code-csharp[KeySingle](../../../samples/core/Modeling/Keys/DataAnnotations/KeySingle.cs?name=KeySingle&highlight=3)]

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

[!code-csharp[Main](../../../samples/core/Modeling/Keys/FluentAPI/KeySingle.cs?name=KeySingle&highlight=4)]
[!code-csharp[KeySingle](../../../samples/core/Modeling/Keys/FluentAPI/KeySingle.cs?name=KeySingle&highlight=4)]

***

You can also configure multiple properties to be the key of an entity - this is known as a composite key. Composite keys can only be configured using the Fluent API; conventions will never set up a composite key, and you can not use Data Annotations to configure one.
You can also configure multiple properties to be the key of an entity - this is known as a composite key. Conventions will only set up a composite key in specific cases - like for an owned type collection.

[!code-csharp[Main](../../../samples/core/Modeling/Keys/FluentAPI/KeyComposite.cs?name=KeyComposite&highlight=4)]
### [Data Annotations](#tab/data-annotations)

<!--
[PrimaryKey(nameof(State), nameof(LicensePlate))]
internal class Car
{
public string State { get; set; }
public string LicensePlate { get; set; }
public string Make { get; set; }
public string Model { get; set; }
}
-->
[!code-csharp[KeyComposite](../../../samples/core/Modeling/Keys/DataAnnotations/KeyComposite.cs?name=KeyComposite&highlight=1)]

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

[!code-csharp[KeyComposite](../../../samples/core/Modeling/Keys/FluentAPI/KeyComposite.cs?name=KeyComposite&highlight=4)]

## Value generation

Expand All @@ -50,7 +67,7 @@ While EF Core supports using properties of any primitive type as the primary key

Key properties must always have a non-default value when adding a new entity to the context, but some types will be [generated by the database](xref:core/modeling/generated-properties). In that case EF will try to generate a temporary value when the entity is added for tracking purposes. After [SaveChanges](/dotnet/api/Microsoft.EntityFrameworkCore.DbContext.SaveChanges) is called the temporary value will be replaced by the value generated by the database.

> [!Important]
> [!IMPORTANT]
> If a key property has its value generated by the database and a non-default value is specified when an entity is added, then EF will assume that the entity already exists in the database and will try to update it instead of inserting a new one. To avoid this, turn off value generation or see [how to specify explicit values for generated properties](xref:core/modeling/generated-properties#overriding-value-generation).
## Alternate Keys
Expand Down
8 changes: 7 additions & 1 deletion entity-framework/core/modeling/relationships.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
title: Relationships - EF Core
description: How to configure relationships between entity types when using Entity Framework Core
author: AndriySvyryd
ms.date: 11/15/2021
ms.date: 10/14/2022
uid: core/modeling/relationships
---
# Relationships
Expand Down Expand Up @@ -310,6 +310,9 @@ It is common to apply configuration to the join entity type. This action can be

[!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 to determine the property names created by convention.

[!code-csharp[Main](../../../samples/core/Modeling/Relationships/FluentAPI/ManyToManyShared.cs?name=Seeding)]
Expand All @@ -324,6 +327,9 @@ EF uses two one-to-many relationships on the join entity type to represent the m

[!code-csharp[Main](../../../samples/core/Modeling/Relationships/FluentAPI/ManyToManyShared.cs?name=Components)]

> [!NOTE]
> The `UsingEntity` overloads that don't have a `Action<EntityTypeBuilder> 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<string, object>`, which is recommended when you don't plan on using the join entity directly.
> [!NOTE]
> The ability to configure many-to-many relationships was introduced in EF Core 5.0, for previous version use the following approach.
Expand Down
109 changes: 108 additions & 1 deletion entity-framework/core/modeling/table-splitting.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ This is achieved in EF7 by calling `SplitToTable` for each split in the entity t
-->
[!code-csharp[EntitySplitting](../../../samples/core/Miscellaneous/NewInEFCore7/ModelBuildingSample.cs?name=EntitySplitting)]

Notice also that, if necessary, different column names can be specified for each of the tables.
Notice also that, if necessary, different column names can be specified for each of the tables. To configure the column name for the main table see [Table-specific facet configuration](#table-specific-facet-configuration).

### Configuring the linking foreign key

Expand All @@ -177,3 +177,110 @@ The FK linking the mapped tables is targeting the same properties on which it is

- Entity splitting can't be used for entity types in hierarchies.
- For any row in the main table there must be a row in each of the split tables (the fragments are not optional).

## Table-specific facet configuration

Some mapping patterns result in the same CLR property being mapped to a column in each of multiple different tables. EF7 allows these columns to have different names. For example, consider a simple inheritance hierarchy:

<!--
public class Animal
{
public int Id { get; set; }
public string Breed { get; set; } = null!;
}
public class Cat : Animal
{
public string? EducationalLevel { get; set; }
}
public class Dog : Animal
{
public string? FavoriteToy { get; set; }
}
-->
[!code-csharp[Animals](../../../samples/core/Miscellaneous/NewInEFCore7/ModelBuildingSample.cs?name=Animals)]

With the TPT [inheritance mapping strategy](xref:core/modeling/inheritance), these types will be mapped to three tables. However, the primary key column in each table may have a different name. For example:

```sql
CREATE TABLE [Animals] (
[Id] int NOT NULL IDENTITY,
[Breed] nvarchar(max) NOT NULL,
CONSTRAINT [PK_Animals] PRIMARY KEY ([Id])
);

CREATE TABLE [Cats] (
[CatId] int NOT NULL,
[EducationalLevel] nvarchar(max) NULL,
CONSTRAINT [PK_Cats] PRIMARY KEY ([CatId]),
CONSTRAINT [FK_Cats_Animals_CatId] FOREIGN KEY ([CatId]) REFERENCES [Animals] ([Id]) ON DELETE CASCADE
);

CREATE TABLE [Dogs] (
[DogId] int NOT NULL,
[FavoriteToy] nvarchar(max) NULL,
CONSTRAINT [PK_Dogs] PRIMARY KEY ([DogId]),
CONSTRAINT [FK_Dogs_Animals_DogId] FOREIGN KEY ([DogId]) REFERENCES [Animals] ([Id]) ON DELETE CASCADE
);
```

EF7 allows this mapping to be configured using a nested table builder:

<!--
modelBuilder.Entity<Animal>().ToTable("Animals");
modelBuilder.Entity<Cat>()
.ToTable(
"Cats",
tableBuilder => tableBuilder.Property(cat => cat.Id).HasColumnName("CatId"));
modelBuilder.Entity<Dog>()
.ToTable(
"Dogs",
tableBuilder => tableBuilder.Property(dog => dog.Id).HasColumnName("DogId"));
-->
[!code-csharp[AnimalsTpt](../../../samples/core/Miscellaneous/NewInEFCore7/ModelBuildingSample.cs?name=AnimalsTpt)]

With the TPC inheritance mapping, the `Breed` property can also be mapped to different column names in different tables. For example, consider the following TPC tables:

```sql
CREATE TABLE [Cats] (
[CatId] int NOT NULL DEFAULT (NEXT VALUE FOR [AnimalSequence]),
[CatBreed] nvarchar(max) NOT NULL,
[EducationalLevel] nvarchar(max) NULL,
CONSTRAINT [PK_Cats] PRIMARY KEY ([CatId])
);

CREATE TABLE [Dogs] (
[DogId] int NOT NULL DEFAULT (NEXT VALUE FOR [AnimalSequence]),
[DogBreed] nvarchar(max) NOT NULL,
[FavoriteToy] nvarchar(max) NULL,
CONSTRAINT [PK_Dogs] PRIMARY KEY ([DogId])
);
```

EF7 supports this table mapping:

<!--
modelBuilder.Entity<Animal>().UseTpcMappingStrategy();
modelBuilder.Entity<Cat>()
.ToTable(
"Cats",
builder =>
{
builder.Property(cat => cat.Id).HasColumnName("CatId");
builder.Property(cat => cat.Breed).HasColumnName("CatBreed");
});
modelBuilder.Entity<Dog>()
.ToTable(
"Dogs",
builder =>
{
builder.Property(dog => dog.Id).HasColumnName("DogId");
builder.Property(dog => dog.Breed).HasColumnName("DogBreed");
});
-->
[!code-csharp[AnimalsTpc](../../../samples/core/Miscellaneous/NewInEFCore7/ModelBuildingSample.cs?name=AnimalsTpc)]
20 changes: 20 additions & 0 deletions samples/core/Modeling/Keys/DataAnnotations/KeyComposite.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using Microsoft.EntityFrameworkCore;

namespace EFModeling.Keys.DataAnnotations.KeyComposite;

internal class MyContext : DbContext
{
public DbSet<Car> Cars { get; set; }
}

#region KeyComposite
[PrimaryKey(nameof(State), nameof(LicensePlate))]
internal class Car
{
public string State { get; set; }
public string LicensePlate { get; set; }

public string Make { get; set; }
public string Model { get; set; }
}
#endregion

0 comments on commit 387b485

Please sign in to comment.