关系定义两个实体之间的关系。 在关系数据库中,这由外键约束表示。
下面的代码显示 Blog 与 Post 之间的一对多关系。
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 int BlogId { get; set; }
public Blog Blog { get; set; }
}Code language: JavaScript (javascript)
Post 是依赖实体
Blog 是主体实体
Post.BlogId 为外键
Blog.BlogId 是主体键(在这种情况下是主键,而不是备用键)
Post.Blog 是一个引用导航属性
Blog.Posts 是集合导航属性
Post.Blog 是 Blog.Posts 的反向导航属性(反之亦然)
术语定义
有许多术语用于描述关系
相关实体: 这是包含外键属性的实体。 有时称为关系的 “子级”。
主体实体: 这是包含主/备用键属性的实体。 有时称为关系的 “父项”。
外键: 用于存储相关实体的主体键值的依赖实体中的属性。
主体密钥: 唯一标识主体实体的属性。 这可能是主键或备用密钥。
导航属性: 在主体和/或从属实体上定义的属性,该属性引用相关实体。
集合导航属性: 一个导航属性,其中包含对多个相关实体的引用。
引用导航属性: 保存对单个相关实体的引用的导航属性。
反向导航属性: 讨论特定导航属性时,此术语是指关系另一端的导航属性。
自引用关系: 依赖关系和主体实体类型相同的关系。
无外键属性
尽管建议在依赖实体类中定义外键属性,但这并不是必需的。 如果未找到外键属性,则会引入名称为 <navigation property name><principal key property name> 或 <principal entity name><principal key property name> 的阴影外键属性(如果依赖类型上没有导航)。
单个导航属性
只包含一个导航属性(无反向导航,没有外键属性)就足以具有约定定义的关系。 还可以有一个导航属性和一个外键属性。
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; }
}Code language: JavaScript (javascript)
手动配置
若要在熟知的 API 中配置关系,请首先标识构成关系的导航属性。 HasOne 或 HasMany 标识正在开始配置的实体类型上的导航属性。 然后,将调用链接到 WithOne 或 WithMany 来标识反向导航。 HasOne/WithOne 用于引用导航属性,HasMany/用于集合导航属性。
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);
}
}
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; }
}Code language: HTML, XML (xml)
您可以使用数据批注来配置依赖项和主体实体上的导航属性如何配对。 这通常在两个实体类型之间存在多个导航属性对时执行。
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int AuthorUserId { get; set; }
public User Author { get; set; }
public int ContributorUserId { get; set; }
public User Contributor { get; set; }
}
public class User
{
public string UserId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
[InverseProperty("Author")]
public List<Post> AuthoredPosts { get; set; }
[InverseProperty("Contributor")]
public List<Post> ContributedToPosts { get; set; }
}Code language: JavaScript (javascript)
单个导航属性
如果只有一个导航属性,则 WithOne 和 WithMany有无参数重载。 这表示在概念上,关系的另一端有一个引用或集合,但实体类中不包含导航属性。
class MyContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasMany(b => b.Posts)
.WithOne();
}
}
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; }
}Code language: HTML, XML (xml)