持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第25天, 点击查看活动详情
在上下文中包含一种类型的 DbSet,这意味着它包含在 EF Core 的模型中;我们通常将此类类型称为 实体 。 EF Core 可以从/向数据库中读取和写入实体实例,如果使用的是关系数据库,EF Core 可以通过迁移为实体创建表。
模型中包含类型
在上下文中的 DbSet 属性中公开的类型作为实体包含在模型中。 还包括在
OnModelCreating
方法中指定的实体类型,就像通过递归方式浏览其他发现的实体类型的导航属性找到的任何类型一样。包含了所有类型:
Blog
包含在内,因为它在上下文的 DbSet 属性中公开。
Post
是因为它是通过
Blog.Posts
导航属性发现的。
AuditEntry
,因为它是在
OnModelCreating
中指定的。
class MyContext : DbContext
public DbSet<Blog> Blogs { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
modelBuilder.Entity<AuditEntry>();
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 Blog Blog { get; set; }
public class AuditEntry
public int AuditEntryId { get; set; }
public string Username { get; set; }
public string Action { get; set; }
使用关系数据库时,表按约定在数据库的默认架构中创建。 例如,Microsoft SQL Server 将使用 dbo
架构(SQLite 不支持架构)。
可以配置要在特定架构中创建的表
[Table("blogs")]
public class Blog
public int BlogId { get; set; }
public string Url { get; set; }
您还可以在模型级别定义 Fluent API 的默认架构,而不是为每个表指定架构:
protected override void OnModelCreating(ModelBuilder modelBuilder)
modelBuilder.HasDefaultSchema("blogging");
键充当每个实体实例的唯一标识符。 EF 中的大多数实体都有一个键,此键映射到关系数据库中主键的概念(对于没有键的实体无键实体)。 实体可以有超过主键的其他,将名为 Id
或 <type name>Id
的属性配置为实体的主键。
protected override void OnModelCreating(ModelBuilder modelBuilder)
modelBuilder.Entity<Blog>()
.HasKey(b => b.BlogId)
.HasName("PrimaryKey_BlogId");
还可以将多个属性配置为实体的键,这称为组合键。 复合密钥只能使用熟知的 API 进行配置;约定将永远不会设置组合键,不能使用数据批注来配置。
protected override void OnModelCreating(ModelBuilder modelBuilder)
modelBuilder.Entity<Car>()
.HasKey(c => new { c.State, c.LicensePlate });
按照约定,使用名称 PK_<type name>
创建关系数据库主键。 可以按如下所示配置 primary key 约束的名称:
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 中,备用键为只读,并在唯一索引之上提供附加语义,因为它们可用作外键的目标。
系统通常会在需要时为你引入备用键,无需手动配置。当标识的属性不是作为关系目标的主键时,将引入备用密钥。
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>
变成以下划线分隔的属性名称列表)。 您可以配置备用密钥的 index 和 unique 约束的名称:
protected override void OnModelCreating(ModelBuilder modelBuilder)
modelBuilder.Entity<Car>()
.HasAlternateKey(c => c.LicensePlate)
.HasName("AlternateKey_LicensePlate");
复制代码