簡體   English   中英

映射“一對多”關系的正確方法。 在多個實體中具有相同關系時

[英]Correct way of mapping a 'one to many' relationship. When having the same relation in multiple entities

假設類和關系的以下結構:

class Document
{
    public List<Version> DocumentVersions { get; set; }
    // Other properties
}

class Register
{
    public List<Version> RegisterVersions { get; set; }
    // Other properties
}

class Version
{
    public int VersionNumber { get; set; }
    // Other properties
}

使用EF Core時,它將分別產生3個表D,R和V,其中V將具有2個FK,一個用於D,一個用於R。

我的問題是:

  • EF Core默認方法正確嗎? V是否沒有FK會導致無效狀態,因為兩個FK都可以為空。
  • 我讀過這篇文章 ,它幾乎回答了我的第一個問題,但是卻引出了另一個問題:
    • 我如何告訴EF采用這種方法:我是否必須為其每個所有者創建派生類型的V? 還是有什么方法可以將單個實體映射到多個表並告訴EF哪些關系屬於哪個表?

也許值得一提的是,我的示例過於簡化,實際上我有6個實體使用相同的V實體。

我不認為這真的是一對多的關系,請看這里

如果(例如) Document具有多個(例如,一個列表) Version那將是一對多的關系。

如果希望多個實體引用相同的實體類型,則可以將外鍵明確地放置在DocumentRegister類中:

class Document
{
    public Version DocumentVersion { get; set; }
    public int DocumentVersionId { get; set; } // Or whatever datatype your ID is
    // Other properties
}

class Register
{
    public Version RegisterVersion { get; set; }
    public int RegisterVersionId { get; set; } // Or whatever datatype your ID is
    // Other properties
}

class Version
{
    public int VersionNumber { get; set; }
    // Other properties
}

因此,難題是:

A)我應該在Version保留兩個FK還是

B)建立兩個表DocumentVersionRegisterVersion而不是Version

好吧,事實是兩者都可以。 您只需要決定哪種方法更適合您的系統即可。 讓我們快速瀏覽。

方法A

回答你的問題; 是EF的默認方法是正確的。 在創建兩個FK和構建兩個表的過程中,它將創建兩個FK。 僅在用於多對多關系的中間表的情況下,它將創建一個額外的表。

不過,我始終建議我們自己創建所有FK,而不要讓EF為我們做。 這樣,我們就可以更好地控制關系的行為,並且還可以訪問應用程序中的FK,因為它們是實體的屬性。

public class Version
{
    [Key]
    public int VersionNumber { get; set; }

    public int? DocumentID { get; set; }
    public virtual Document Document { get; set; }

    public int? RegisterID { get; set; }
    public virtual Register Register { get; set; }

    //Other properties
}

由於Version具有PK,因此它可以創建記錄,而沒有任何FK具有任何值。 如果您的業務模型允許這樣做,請保持原樣。 您以后可以提供一個UI,以將“版本”分配給“文檔”或“注冊”。

如果要在“ Version表中添加一些規則,請執行以下操作: 例如,每條記錄應至少具有一個FK或只有一個FK,您可以通過重寫DbContext類的ValidateEntity方法(或可能通過數據庫中的某些sql約束)來做到這一點。

    protected override DbEntityValidationResult ValidateEntity(
        DbEntityEntry entityEntry, IDictionary<object, object> items)
    {
        // validate entity from annotations
        var result = base.ValidateEntity(entityEntry, items);

        // custom validation rules
        if (entityEntry.Entity is Version && 
            (entityEntry.State == EntityState.Added || entityEntry.State == EntityState.Modified))
        {
            Version version = ((Version)entityEntry.Entity);
            if (version.DocumentID == null && version.RegisterID == null)
                result.ValidationErrors.Add(new DbValidationError(null, "A document or register must be specified."));
        }

        return result;
    }

請注意,您可以創建自己的注釋以驗證實體屬性。 但是這些僅限於單個屬性。 如果要添加組合多個屬性的ValidateEntityValidateEntity方法是我所知道的唯一方法。

方法B

有兩種方法可以實現此方法。 首先是保留Version表,並在頂部添加兩個中間表。

public class Document
{
    public virtual List<DocumentVersion>  Versions { get; set; }
    // other properties
}

public class Register
{
    public virtual List<RegisterVersion> Versions { get; set; }
    // other properties
}

public class Version
{
    [Key]
    public int VersionNumber { get; set; }

    //Other properties
}

public class DocumentVersion
{
    public int DocumentID { get; set; }
    public virtual Document Document { get; set; }

    public int VersionID { get; set; }
    public virtual Version Version { get; set; }

    // other properties
}

public class RegisterVersion
{
    public int RegisterID { get; set; }
    public virtual Register Register { get; set; }

    public int VersionID { get; set; }
    public virtual Version Version { get; set; }

    // other properties
}

實際上,這允許多對多關系,但是您可以將其用作一對多關系。 第二種方法是使Version抽象(不是數據庫表)並構建兩個新表以從Version繼承:

public class Document
{
    public virtual List<DocumentVersion>  Versions { get; set; }
    // other properties
}

public class Register
{
    public virtual List<RegisterVersion> Versions { get; set; }
    // other properties
}

// don't make this a DbSet
public abstract class Version
{
    [Key]
    public int VersionNumber { get; set; }

    //Other properties
}

public class DocumentVersion : Version
{
    public int DocumentID { get; set; }
    public virtual Document Document { get; set; }

    // other properties
}

public class RegisterVersion : Version
{
    public int RegisterID { get; set; }
    public virtual Register Register { get; set; }}

    // other properties
}

這是一種正確而清晰的一對多關系。

結論底線是您可以使用兩種方法中的任何一種,並且可以進行更改以適合您的需求。

我已經成功使用了兩種方法,但是我傾向於使用第二種方法(以及抽象類繼承)。 第一種方法似乎更多地是減少數據庫資源或簡化開發的方法,但是現代數據庫根本沒有更多的壓力,並且開發可能變得不必要地復雜。 此外,第二種方法允許通過分別向每個連接表添加其他屬性來擴展關系的功能。 對於必須處理的6個實體,采用第二種方法對我來說似乎更安全。 我在具有許多文件類型和關系的應用程序中使用了這種方法,並且它總是非常簡單和可擴展的。 每個沖突表中的那些額外屬性也非常方便。

希望我能幫忙,祝您編程愉快!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM