【问题标题】:EF Core one to many to many to one relationshipEF Core 一对多对多对一关系
【发布时间】:2017-10-26 08:15:35
【问题描述】:

我已经使用数据库很长时间了,但对 Entity Framework 还是陌生的。我处理编程和数据库开发的两个方面。作为一个数据库开发人员,我尽量保持它的清洁,所以我想出的这个结构对我来说效果很好,但我不确定 Entity Framework 是否支持它,因为我已经尝试了几天,使用不同的场景,数据注释以及 Fluent API,但无法使其正常工作。

我想要做的可能有点不合常规,但我想要避免的是必须为每个区域复制一个文件表,因此我定义了一个文件表,可以使用关系由多个区域使用。因此,我所拥有的是:一个 [公司、员工或项目] 可以有许多文件(一对多)。同样,文件表可以来自任何区域(多对多,在这种情况下,不是数据而是结构,希望这是有意义的)。文件记录仅与 1 个区域 [公司、员工或项目] 相关(多对一)。

这种方法的明显优势是我可以避免管理 3 个文件表,但它并没有就此结束。从 FileAccess 表中可以看出,我不需要在这里有多个表或多个字段来表示指向多个表的指针,而是只需要管理 1 个表进行文件访问。关键在于 RelationTable 和 RelationId 而不是特定的 File.Id。

下面是我试图完成的结构的简化示例。可以在Entity Framework中完成吗?

  public class Company
  {
    public Guid Id { get; set; }
    public string Name { get; set; }
    public virtual ICollection<File> Files { get; set; }
  }

  public class Employee
  {
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual ICollection<File> Files { get; set; }
  }

  public class Project
  {
    public int Id { get; set; }
    public Guid? CompanyId { get; set; }
    public string ProjectNo {get; set; }
    public virtual ICollection<File> Files { get; set; }
  }

  public class File
  {
    public int Id { get; set; }
    public Int16 RelationTable { get; set; } 0=Company, 1=Employee, 2=Project
    public string RelationId { get; set; } Company.Id, Employee.Id, Project.Id
    public string FileName { get; set; }
  }

  public class FileAccess
  {
    public int Id { get; set; }
    public int EmployeeId { get; set; }
    public Int16 RelationTable { get; set; } 0=Company, 1=Employee, 2=Project
    public string RelationId { get; set; } Company.Id, Employee.Id, Project.Id
    public string AccessType
  }

【问题讨论】:

  • 你问的是多态关联。作为数据库开发人员,您知道这不能用关系数据库中的 FK 关系来表示。因此,包括 Core 在内的任何 EF 版本都不支持。
  • 感谢您的任期!我很高兴我能够表达我的观点。我不是全职的数据库开发人员,我只根据需要做事,是的,我知道你不能那样设置 FK 关系。我怀疑 EF 不支持这一点,但由于我只是在学习 EF 的第三周,我不确定 EF 是否有不同的方法来处理这个问题。我认为我想出的解决方案应该可行。
  • 您可以将File 抽象化并创建层次结构。 ProjectFile : File 在您的 Project 上有 ICollection&lt;ProjectFile&gt;

标签: data-annotations entity-framework-core relationships ef-fluent-api


【解决方案1】:

正如 Ivan 指出的,由于外键限制,EF 不支持这一点,但我能够提出一个可行的解决方案。但是,我必须警告您,我才 EF 的第 3 周,所以我不知道这可能会导致什么后果,但这就是我所做的,对于那些可能感兴趣的人。

事实证明(通过反复试验),EF 只需要 OnModelCreating 来连接对象之间的关系,它并不真正需要创建 FK,因此我以这种方式定义了关系:

  modelBuilder.Entity<File>()
  .HasIndex(k => new { k.RelationTable, k.RelationId }); //for performance

  modelBuilder.Entity<FileAccess>()
    .HasMany(fa => fa.Files)
    .WithOne(f => f.FileAccess)
    .HasForeignKey(k => new { k.RelationTable, k.RelationId })
    .HasPrincipalKey(k => new { k.RelationTable, k.RelationId });

//Using enumerations to control 
    relationships and adding PERSISTED so it doesn't need to be maintained plus it 
    won't break the Add-Migration with the "non persistent error"

  modelBuilder.Entity<Project>()
    .Property(f => f.RelationTable)
    .HasComputedColumnSql((int)NTGE.Database.Shared.eFileRelTable.Projects + " PERSISTED") //This injects the value so we don't have to store it
    .HasDefaultValue(123); //This doesn't really matter, we just need it so EF doesn't try to insert a value when saving, which will cause an error

  modelBuilder.Entity<Project>()
    .HasMany(p => p.Files)
    .WithOne(f => f.Project)
    .HasForeignKey(k => new { k.RelationTable, k.RelationId })
    .HasPrincipalKey(k => new { k.RelationTable, k.Id });

当您添加上述代码并运行 Add-Migration 时,它会导致它添加以下代码,这会破坏 Update-Database 命令,因此您需要在 Up 函数中将其注释掉。

        //migrationBuilder.AddForeignKey(
        //    name: "FK_Files_Projects_RelationTable_RelationId",
        //    table: "Files",
        //    columns: new[] { "RelationTable", "RelationId" },
        //    principalTable: "Projects",
        //    principalColumns: new[] { "RelationTable", "Id" },
        //    onDelete: ReferentialAction.Cascade);

        //migrationBuilder.AddForeignKey(
        //    name: "FK_Files_FileAccess_RelationTable_RelationId",
        //    table: "Files",
        //    columns: new[] { "RelationTable", "RelationId" },
        //    principalTable: "FileAccess",
        //    principalColumns: new[] { "RelationTable", "RelationId" },
        //    onDelete: ReferentialAction.Cascade);

您需要对 Down 函数执行相同操作,否则您将无法回滚更改。

        //migrationBuilder.DropForeignKey(
        //    name: "FK_Files_Projects_RelationTable_RelationId",
        //    table: "Files");

        //migrationBuilder.DropForeignKey(
        //    name: "FK_Files_FileAccess_RelationTable_RelationId",
        //    table: "Files");

现在你可以做一个更新数据库,它应该运行得很好。运行该应用程序也可以正常工作。我可以使用 EF 方法来获取带有关联文件的项目,并且还可以使用 FileAccess 对象。但是,请记住,这是一个 hack,EF 的未来版本可能不支持它。干杯!

【讨论】:

  • 嘿托尼,你还在用这个方法吗?我的解决方案是将这 3 个关系设置为 virtual 和 nullable ,并在我需要它们时获取它们... public class Job 。公共虚拟 ICollection Inhouse { get;放; } 。公共虚拟 ICollection Agency { 获取;放; } 。公共虚拟 ICollection JobNotes { get;放; } .
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-10-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-02
  • 2021-12-29
相关资源
最近更新 更多