EF Core一对多对多关系

时间:2017-05-25 15:43:50

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

我已经与数据库合作了很长时间,但我是Entity Framework的新手。我处理编程和数据库开发的两个方面。作为一个db开发人员,我试着保持它干净,所以我提出的这个结构对我来说很有效,但是我不确定Entity Framework是否支持它已经尝试了好几天,使用不同的方案,数据注释以及Fluent API但无法实现这一点。

我尝试做的事情可能有些不同寻常,但我想避免的是必须为每个区域复制一个文件表,因此我定义了一个可以被多个区域使用的文件表使用关系。因此,我所拥有的是:一个[公司,员工或项目]可以拥有多个文件(一个到多个)。类似地,文件表可以由任何区域(多对多来源,在这种情况下,它不是数据而是结构,希望这是有意义的)来源。文件记录仅与1个区域[公司,员工或项目](多对一)相关。

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

以下是我试图完成的结构的简化示例。可以在实体框架中完成吗?

  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
  }

1 个答案:

答案 0 :(得分:0)

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

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

  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命令,因此您需要在其中对其进行注释。向上功能。

        //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");

现在你可以做一个Update-Database,它应该运行得很好。运行该应用程序也完美无缺。我能够使用EF方法获取带有相关文件的Project,并使用FileAccess对象。但是,请记住,这是一个黑客攻击,EF的未来版本可能不支持它。干杯!