【问题标题】:Modeling Employee-Assistant(s) relationship with EF Code First使用 EF Code First 对员工-助理关系建模
【发布时间】:2016-08-29 04:15:13
【问题描述】:

简而言之,以下是业务需求:

  • 所有员工都需要存储在数据库中
  • 有些员工有助理,有些则没有
  • 有些员工有不止一名助理
  • 助理也是员工

显然存在一些自我参照的情况。但与典型的“员工-经理”情况不同的是,一个员工可以有 0 个或多个助手。所以,Employee 和员工的Assistants 的组合需要以 Employee 和 Employee 之间的一对多关系存储在单独的表中strong>员工助理。但是我很困惑如何在 Entity Framework 6 Code First 中对此进行建模。

我是从这个开始的:

public class Employee
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

public class EmployeeAssistant
{
   [ForeignKey("Employee")]
   public int EmployeeId { get; set; }
   public virtual Employee Employee { get; set; }

   [ForeignKey("Assistant")]
   public int AssistantId { get; set; }
   public virtual Employee Assistant { get; set; }
}

但在Update-Database 命令期间出现错误:

在表“EmployeeAssistant”上引入 FOREIGN KEY 约束“FK_dbo.EmployeeAssistant_dbo.Employee_EmployeeId”可能会导致循环或多个级联路径。指定 ON DELETE NO ACTION 或 ON UPDATE NO ACTION,或修改其他 FOREIGN KEY 约束。

我错过了什么?我应该以不同的方式处理这个问题吗?

【问题讨论】:

标签: c# asp.net-mvc ef-code-first entity-framework-6 self-referencing-table


【解决方案1】:

由于每个员工可能有一个或多个助手(每个助手将有一个或多个员工)并且都是员工,最简单的解决方案是一个类,助手和员工两个集合,关系将由框架:

public class Employee
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public ICollection<Employee> Assistants { get; set; }
    public ICollection<Employee> Employees { get; set; }
}

当您使用包管理器控制台添加迁移时,它会自动创建两个表,一个用于员工,另一个用于多对多关系。

那么您所要做的就是使用 Include 扩展方法找到相关的助理和/或员工。

db.Employees.Where(x=>x.Id==id).Include(x=>x.Assistants).FirstOrDefault()

和/或

db.Employees.Where(x=>x.Id==id).Include(x=>x.Employees).FirstOrDefault()

【讨论】:

  • 嗯,没想到这行得通,但确实行得通!我唯一遇到的问题是生成的多对多表的名称为EmployeeEmployee,字段为Employee_Id 和Employee_Id1。有没有办法通过数据注释(最好)或 Fluent 映射来控制这些元素的名称?
  • 您不必更改这些字段,因为您不会使用它,框架会为您完成这项肮脏的工作:) 顺便说一句,在您从员工表中删除实体之前,您必须清除与其他实体的关系。
  • 是的,我知道我不必这样做,但我需要数据库本身有意义,因为它将用于报告和其他潜在的直接查询。我不能将它用作“黑匣子”数据持久性。我已经找到了如何使用流利的映射来做到这一点,只是不确定如何使用数据注释来做到这一点,如果可能的话。当我有机会时,会发布一个包含更多细节的答案。
【解决方案2】:

基于此链接Introducing FOREIGN KEY constraint may cause cycles or multiple cascade paths - why?

如果您使用代码删除EmployeeAssistant,似乎会导致两个级联删除路径。

我会建议这样的结构:

修改后

public class Employee
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public List<EmployeeAssistant> Assistants { get; set; } //if an employee has no assistants this List can easily just be empty
    OR
    public ICollection<EmployeeAssistant> Assistants { get; set; } // depending on your architecture, choose the one that would suit you better
}
public class EmployeeAssistant
{
   [ForeignKey("Employee")]
   public int EmployeeId { get; set; } //this is the employee who 'has' this assistant
   public virtual Employee Employee { get; set; }

   public int Id { get; set; }  //this is the assistant's own information - identical to employee's basic info
   public string FirstName { get; set; }
   public string LastName { get; set; }
}

经过一段时间的脑筋急转弯,我什至想出了一个可能性,你只需要 1 个类,Employee,但它包含一个 bool IsAssistant 表示该员工是否是助理,AssistantEmployeeId 这是一个“拥有”此助理的员工的员工 ID。 例如:

public class Employee
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public bool IsAssistant { get; set; }
    public int EmployeeAssistantID { get; set; }
}

我知道第二种方法非常混乱,可能不适合您的需求,但我个人总是先创建数据库表,然后执行“从数据库生成模型”,因此我对代码优先方法缺乏经验。

【讨论】:

  • 嗯,但是你怎么知道哪个助理和哪个员工一起去?如果 EmployeeAssistant 实体只有Employee 引用,那么这将在数据库EmployeeAssistant 中创建一个表,其中包含字段Id 和EmployeeId。因此,将员工与助手联系起来缺少数据。对吗?
  • 我在您编辑后重新阅读了您的答案。我很欣赏令人难以置信的脑洞!但是,第一种方法意味着我必须复制 EmployeeAssistant 类中的所有 Employee 属性,这样做没有意义,因为 EmployeeAssistant 本身就是 Employee。第二种方法不起作用,因为它只处理一个助理案例,而不是多个助理。
  • 那么在第二种方法中添加一个 EmployeeAssistantID 列表就可以了?例如public List&lt;int&gt; AssistantIDs { get; set; }
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-26
  • 1970-01-01
相关资源
最近更新 更多