键用作每个实体实例的唯一标识符。 EF 中的大多数实体都有一个键,该键映射到关系数据库中主键的概念(对于没有键的实体,请参阅无键实体。 实体可以有除主键之外的其他键(有关详细信息,请参阅备选键)。

配置主键

根据约定,名为 Id<type name>Id 的属性将被配置为实体的主键。

internal class Car
{
    public string Id { get; set; }

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

internal class Truck
{
    public string TruckId { get; set; }

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

注意

从属实体类型使用不同的规则来定义键。

可将单个属性配置为实体的主键,如下所示:

internal class Car
{
    [Key]
    public string LicensePlate { get; set; }

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

还可将多个属性配置为实体的键 - 这称为组合键。 约定仅在特定情况下设置复合键,例如为自有类型集合设置。

注意

EF Core 7.0 中引入了 [PrimaryKey] 属性。 在旧版本中使用 Fluent API。

[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; }
}

创造价值

对于非复合数字和 GUID 主键,EF Core 根据约定设置值生成。 例如,SQL Server 中的数字主键会自动设置为 IDENTITY 列。 有关详细信息,请参阅有关值生成的文档以及有关特定继承映射策略的指南

主键名称

根据约定,在关系数据库上,主键使用名称 PK_<type name> 进行创建。 可按如下方式配置主键约束的名称:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasKey(b => b.BlogId)
        .HasName("PrimaryKey_BlogId");
}

键类型和值

虽然 EF Core 支持使用任何基元类型的属性作为主键(包括 stringGuidbyte[] 等),但并非所有数据库都支持所有类型作为键。 在某些情况下,键值可以自动转换为支持的类型,否则应手动指定转换。

向上下文添加新实体时,键属性必须始终具有非默认值,但某些类型将由数据库生成。 在这种情况下,当添加实体以用于跟踪时,EF 将尝试生成一个临时值。 调用 SaveChanges 后,临时值将替换为数据库生成的值。

重要

如果键属性的值由数据库生成,并且在添加实体时指定了非默认值,则 EF 将假定该实体已存在于数据库中,并尝试更新它,而不是插入新的实体。 若要避免这种情况,请禁用值生成或了解如何为生成的属性指定显式值

备用键

除主键外,备选键还充当每个实体实例的备用唯一标识符;它可以用作关系的目标。 使用关系数据库时,它会映射到备选键列上的唯一索引/约束的概念以及引用该列的一个或多个外键约束。

提示

如果只想对列强制执行唯一性,请定义唯一索引而不是备选键(请参阅索引)。 在 EF 中,备选键是只读的,并且提供对唯一索引的其他语义,因为它们可以用作外键的目标。

备选建通常根据需要引入,无需手动配置。 根据约定,当你将不是主键的属性标识为关系的目标时,会引入备选键。

internal class MyContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }
    public DbSet<Post> Posts { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Post>()
            .HasOne(p => p.Blog)
            .WithMany(b => b.Posts)
            .HasForeignKey(p => p.BlogUrl)
            .HasPrincipalKey(b => b.Url);
    }
}

public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }

    public List<Post> Posts { get; set; }
}

public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public string BlogUrl { get; set; }
    public Blog Blog { get; set; }
}

还可将单个属性配置为备选键:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Car>()
        .HasAlternateKey(c => c.LicensePlate);
}

还可将多个属性配置为备选键(即复合备选键):

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Car>()
        .HasAlternateKey(c => new { c.State, c.LicensePlate });
}

最后,根据约定,为备选键引入的索引和约束将命名为 AK_<type name>_<property name>(复合备选键 <property name> 成为下划线分隔的属性名称列表)。 可配置备选键的索引和唯一约束的名称:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Car>()
        .HasAlternateKey(c => c.LicensePlate)
        .HasName("AlternateKey_LicensePlate");
}