0

我已經與數據庫很長一段時間了,但現在是實體框架的新手。我處理編程和數據庫開發的兩個方面。作爲一名db開發人員,我試圖保持它的清潔,所以我想出的這個結構對我來說工作得很好,但我不確定Entity Framework是否支持它,因爲我已經嘗試了幾天,使用了不同的場景,Data Annotations以及流利的API,但無法得到這個工作。EF核心一對多對多對一

我想要做的可能有點不合常規,但我試圖避免的是必須爲每個區域複製一個文件表,因此我定義了一個文件表,可以使用關係在多個區域使用。因此,我擁有的是:一個[公司,員工或項目]可以有多個文件(一對多)。同樣,文件表可以來源於任何區域(多到很多,在這種情況下,它不是數據而是結構,希望這是有意義的)。文件記錄僅與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

你在問什麼叫多態關聯。作爲一名db開發人員,您知道這不能用關係數據庫中的FK關係表示。因此,任何EF版本(包括Core)都不支持。 –

+0

謝謝這個詞!我很高興能夠明白我的觀點。我不是一個全職的db開發人員,我只根據需要做事情,是的,我知道你不能以這種方式設置FK關係。我懷疑EF不支持這個,但由於我只是在學習EF的第三週,所以我不確定EF是否有另一種方法來處理這個問題。我認爲我提出的解決方案應該可行。 –

回答

0

正如Ivan指出的那樣,由於外鍵限制,EF不支持此功能,但我能夠提出一個可行的解決方案。但是,我必須警告你,我只在EF的第三週,所以我不知道這可能會導致什麼後果,但這是我做的,對於那些可能感興趣的人。

事實證明(通過試驗和錯誤),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對象。但是,請記住,這是一種黑客行爲,未來版本的EF可能不支持它。乾杯!