c# - EF Core 中的一对零或一对关系
问题描述
让我简化一下:
给定两个模型
Department
模型
public Guid Id { get; set; }
public string DepartmentID { get; set; }
public string DepartmentName { get; set; }
public Guid? DepartmentManager { get; set; }
Employee
模型
public Guid ID { get; set; }
public string EmployeeID { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public string MiddleName { get; set; }
我希望能够分配一名 - 并且只有一名 - 员工作为部门的部门经理。
示例:部门是财务。部门经理是 Joe, Blow。
部门模型中的 DepartmentManager 应包含来自员工模型的 Joe Blow 的 EmployeeID
我认为一旦我在模型中得到正确的关系,我将能够完成 cshtml 页面以正确处理 CRUD。
谢谢,约翰
解决方案
假设这些是您的模型类:
public class Department
{
public Guid Id { get; set; }
public string DepartmentID { get; set; }
public string DepartmentName { get; set; }
public Guid? DepartmentManager { get; set; }
}
和
public class Employee
{
public Guid ID { get; set; }
public string EmployeeID { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public string MiddleName { get; set; }
}
您有几个选项可以显式配置实体(数据模型)之间的关系。
一、数据属性
如果您对 Fluent API 不满意,您可以使用数据属性装饰您的模型,EF 会在内存中构建模型时发现并使用这些属性。所以,对于你的情况,这样的事情是有效的:
[Table(nameof(Department))]
public class Department
{
[Key]
public Guid Id { get; set; }
public string DepartmentID { get; set; }
public string DepartmentName { get; set; }
[ForeignKey(nameof(DepartmentManager))]
public Guid? DepartmentManagerID { get; set; }
public virtual Employee DepartmentManager { get; set; }
}
和
[Table(nameof(Employee))]
public class Employee
{
public Employee()
{
Departments = new HashSet<Department>();
}
[Key]
public Guid ID { get; set; }
public string EmployeeID { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public string MiddleName { get; set; }
public virtual ICollection<Department> Departments { get; set; }
}
这TableAttribute
是可选的 - 我用它来明确声明表使用单数命名约定(因此表称为“部门”,而不是“部门”)。KeyAttribute
也应该是可选的,因为 EF 的约定之一是假设“ID”字段是主键,但再次[Key]
使其明确(在这里感知主题?)。
我还建议使用DepartmentManagerID
作为外键属性,而不是DepartmentManager
,以保持一致。这还允许您添加一个导航属性 ,DepartmentManager
该属性可用于在查询部门时包含员工记录。
Employee
can (should?) 还有一个导航属性 ,Departments
代表 Employee 和 Department 之间“一对多”关系的“多”端——一个 Department 只能有一个 Employee(经理),但是一个 Employee 可以管理许多部门。将此virtual
属性设为允许 EF 延迟加载该属性,因此您可以查询员工记录,而无需始终获取关联的部门。
2.流畅的API
除了数据属性之外,还可以使用 fluent API,也可以使用 fluent API(您的选择)。我仍然会添加相关的导航属性,所以你的模型看起来像这样:
public class Department
{
public Guid Id { get; set; }
public string DepartmentID { get; set; }
public string DepartmentName { get; set; }
public Guid? DepartmentManagerID { get; set; }
public virtual Employee DepartmentManager { get; set; }
}
和
public class Employee
{
public Employee()
{
Departments = new HashSet<Department>();
}
public Guid ID { get; set; }
public string EmployeeID { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public string MiddleName { get; set; }
public virtual ICollection<Department> Departments { get; set; }
}
然后,在您的 DbContext 类中(为了保持这个简单(ish)),您将配置您的模型及其关系:
public partial class JohnsDbContext : DbContext
{
public JohnsDbContext(DbContextOptions<JohnsDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// To set the table name, uncomment:
//modelBuilder.Entity<Department>()
// .ToTable(nameof(Department));
modelBuilder.Entity<Department>()
.HasKey(m => m.ID);
modelBuilder.Entity<Department>()
.HasOne(m => m.DepartmentManager) // Department.DepartmentManager
.WithMany(m => m.Departments) // Employee.Departments
.HasForeignKey(m => m.DepartmentManagerID); // Department.DepartmentManagerID
modelBuilder.Entity<Employee>()
.HasKey(m => m.ID);
}
}
这几乎是使用 fluent API 建立关系的最低要求。如果您需要,还有更多可用的设置,Intellisense 将帮助您发现它们。