键
键充当每个实体实例(对应数据库表中的一条记录)的唯一标识符。
EF中大多数实体都要一个键,此键映射到关系型数据库中‘主键’的概念。
实体还可以有超过主键的其他键——备用键。
1、约定:默认将名为Id或名为<type name>Id的属性配置为实体的主键:
class Car { public string Id { get; set; } public string Make { get; set; } public string Model { get; set; } } class Truck { public string TruckId { get; set; } public string Make { get; set; } public string Model { get; set; } }
2、可以将单个属性配置为实体的主键:
class Car { [Key] public string LicensePlate { get; set; } public string Make { get; set; } public string Model { get; set; } }
3、还可以将多个属性配置为主键(联合主键,或者叫组合键)。组合键的关键是确认复合秘钥(下文中的new { 属性1, 属性2 }),只能使用Fluent API进行配置。不会默认配置组合键,且不能通过数据批注来配置:
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Car>() .HasKey(c => new { c.State, c.LicensePlate }); }
主键名称
约定:对于关系型数据库,使用属性名创建主键的名称为 PK_<type name>。当然,也可以通过Fluent API的方式自定义配置主键约束名:
modelBuilder.Entity<PostModel>().HasKey(c => new { c.Title, c.Content }).HasName("PrimaryKey_Title_Content");
键类型和值
尽管EF Core 支持使用任何基元类型的属性作为主键(包括 string
、 Guid
、byte[]和其他类型),但并不是所有数据库都支持将所有类型作为键。 在某些情况下,可以自动将键值转换为支持的类型,否则应 手动指定转换。
在DbContext中添加实体时,键属性必须没有默认值,但某些类型将由数据库生成。这种情况下,当对实体进行追踪时,EF会尝试生成一个临时值,在调用SaveChanges方法后,临时值将被替换为数据库生成的值。
重要:如果某个键属性的值由数据库生成(比如SQL Server中的自增列IDENTITY属性),而在添加实体时指定了一个非默认值,则 EF 将假定该实体在数据库中已存在,并且将尝试对其进行更新而不是插入一个新的值。 若要避免这种情况,请禁用值生成或了解 如何为生成的属性指定显式值。
备用键
除了主键,备用键还可用作每个实体实例的唯一标识符的替代(备用)。它可以作为一种关系的目标。在使用关系数据库时,这将映射到备用键列上的唯一索引/约束和引用该列的一个或多个外键约束的概念。
注意:如果只希望对列强制唯一性,则定义唯一索引而不是备用键(请参阅索引)。在EF中,备用键是只读的,并且在唯一索引上提供额外的语义,因为它们可以用作外键的目标。
备用键通常在需要时为您引入,您不需要手动配置它们。按照惯例,当您将一个不是主键的属性标识为关系的目标时,将为您引入一个备用键:
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_<类型名>_<属性名>(对于复合备用键<属性名>将成为一个下划线分隔的属性名列表)。你可以配置备用键的索引和唯一约束的名称:
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Car>() .HasAlternateKey(c => c.LicensePlate) .HasName("AlternateKey_LicensePlate"); }