密钥充当每个实体实例的唯一标识符。 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; }
}
还可以将多个属性配置为实体的键 - 这称为复合键。 约定仅在特定情况下设置复合键,例如为自有类型集合设置。
[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 支持使用任何基元类型的属性作为主键,包括string
Guid
和其他byte[]
属性,但并非所有数据库都支持所有类型作为键。 在某些情况下,键值可以自动转换为受支持的类型,否则应 手动指定转换。
向上下文添加新实体时,键属性必须始终具有非默认值,但某些类型将由 数据库生成。 在这种情况下,在添加实体以用于跟踪目的时,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");
}