EF Core 中的一对零或一对关系

Posted

技术标签:

【中文标题】EF Core 中的一对零或一对关系【英文标题】:One to zero or one relationship in EF Core 【发布时间】:2021-06-25 00:13:22 【问题描述】:

让我简化一下:

给定两个模型

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。

谢谢, 约翰

【问题讨论】:

您的问题与 MVC(作为一种模式)或 ASP.NET(Core 或其他)实现没有任何关系。您使用的是 Entity Framework 6 还是 EF Core? 来自我的部门模型代码:使用 Microsoft.AspNetCore.Mvc;而且,它没有变灰,所以我知道该页面正在引用该库。 NuGet 包是 Microsoft.EntityFrameworkCore.Tools plus ...Sqlserver v5.0.4 谢谢 我认为您正在使用问题中未显示的属性或其他内容 - Microsoft.AspNetCore.Mvc 不是使用 EF Core 的要求。没什么大不了的,它只是有助于将您的标签限制在与您的问题实际相关的内容上。 您能否更新您的问题以显示您正在尝试做的更具体的示例? EF 确实需要一些配置来创建模型之间的关联,无论是通过下面提到的 fluent API 还是通过数据属性 - 这些都不包含在您所显示的内容中。如果您都没有,这也很重要,因为它显示了答案需要从哪里开始 @TiesonT。我可能遗漏了一些东西,但 MS 的 EF Core 教程既不使用数据属性,也不流畅。我帖子中的第一个示例来自该教程。 【参考方案1】:

假设这些是您的模型类:

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; 

您有几个选项可以显式配置实体(您的数据模型)之间的关系。

1。数据属性

如果您对 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 可以(应该?)也有一个导航属性,Departments,它代表 Employee 和 Department 之间“一对多”关系的“多”端——一个 Department 只能有一个 Employee (经理),但一个员工可以管理多个部门。将此设置为 virtual 属性允许 EF 延迟加载该属性,因此您可以查询 Employee 记录,而无需始终获取关联的 Departments。

2。流畅的 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 将帮助您发现它们。

【讨论】:

Tieson,非常感谢您。它对我有很大帮助。除了,我认为(对于这种情况)这将是一对一的关系,因为每个部门只有一个部门经理。对于将成为员工表中的字段的每个部门的成员而不是经理的员工:DepartmentID。这如何改变你给我的东西?再说一次,你在这件事上帮了大忙,我欠你六包你最喜欢的饮料——除非是唐培里侬! :) 我明白了!!!!!!你对一个员工可以管理多个部门的评论填补了我大脑中的空白。一旦我意识到这是很多方面,一切都点击了。你是我的救赎! 可以在 Entity Framework 中设置 1:1 关系,但这需要一些工作。不过,我想我明白了您想要做的事情,这实际上并不需要 1:1 的关系(除非您的业务规则规定员工只能管理一个部门)。如果您想查看将“DepartmentId”添加到 Employee 的代码,我可以修改此答案,但我认为您已经弄清楚了。

以上是关于EF Core 中的一对零或一对关系的主要内容,如果未能解决你的问题,请参考以下文章

EF6基础系列(五)---EF中的实体关系

使用 EF7 映射一对零或一

EF 代码优先 - 配置一对零或一关系,无需共享 PK/FK

具有一对一关系的 EF Core 批量插入

用于一对零或一关系的实体框架 (EF) 代码优先级联删除

EF Core 一对一 一对多 多对多 关系定义