इकाई फ्रेमवर्क कोड प्रथम - एक ही तालिका से दो विदेशी कुंजी


260

मैंने अभी EF कोड का उपयोग करना शुरू किया है, इसलिए मैं इस विषय में कुल आरंभक हूं।

मैं टीमों और मैचों के बीच संबंध बनाना चाहता था:

1 मैच = 2 टीम (घर, अतिथि) और परिणाम।

मुझे लगा कि ऐसा मॉडल बनाना आसान है, इसलिए मैंने कोडिंग शुरू की:

public class Team
{
    [Key]
    public int TeamId { get; set;} 
    public string Name { get; set; }

    public virtual ICollection<Match> Matches { get; set; }
}


public class Match
{
    [Key]
    public int MatchId { get; set; }

    [ForeignKey("HomeTeam"), Column(Order = 0)]
    public int HomeTeamId { get; set; }
    [ForeignKey("GuestTeam"), Column(Order = 1)]
    public int GuestTeamId { get; set; }

    public float HomePoints { get; set; }
    public float GuestPoints { get; set; }
    public DateTime Date { get; set; }

    public virtual Team HomeTeam { get; set; }
    public virtual Team GuestTeam { get; set; }
}

और मुझे एक अपवाद मिलता है:

संदर्भित संबंध एक चक्रीय संदर्भ में परिणाम देगा जिसकी अनुमति नहीं है। [बाधा नाम = Match_GuestTeam]

मैं एक ही तालिका में 2 विदेशी कुंजियों के साथ ऐसा मॉडल कैसे बना सकता हूं?

जवाबों:


296

इसे इस्तेमाल करे:

public class Team
{
    public int TeamId { get; set;} 
    public string Name { get; set; }

    public virtual ICollection<Match> HomeMatches { get; set; }
    public virtual ICollection<Match> AwayMatches { get; set; }
}

public class Match
{
    public int MatchId { get; set; }

    public int HomeTeamId { get; set; }
    public int GuestTeamId { get; set; }

    public float HomePoints { get; set; }
    public float GuestPoints { get; set; }
    public DateTime Date { get; set; }

    public virtual Team HomeTeam { get; set; }
    public virtual Team GuestTeam { get; set; }
}


public class Context : DbContext
{
    ...

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Match>()
                    .HasRequired(m => m.HomeTeam)
                    .WithMany(t => t.HomeMatches)
                    .HasForeignKey(m => m.HomeTeamId)
                    .WillCascadeOnDelete(false);

        modelBuilder.Entity<Match>()
                    .HasRequired(m => m.GuestTeam)
                    .WithMany(t => t.AwayMatches)
                    .HasForeignKey(m => m.GuestTeamId)
                    .WillCascadeOnDelete(false);
    }
}

प्राथमिक कुंजी डिफ़ॉल्ट सम्मेलन द्वारा मैप की जाती हैं। टीम के पास दो मैचों का संग्रह होना चाहिए। आपके पास दो FKs द्वारा संदर्भित एकल संग्रह नहीं हो सकता है। मैच को कैस्केडिंग डिलीट किए बिना मैप किया जाता है क्योंकि यह इन सेल्फ रेफरेंस में कई-कई काम नहीं करता है।


3
यदि दो टीमों को केवल एक बार खेलने की अनुमति दी जाए तो क्या होगा?
ca9163d9

4
@ नाइक: यह कुछ ऐसा है जिसे आपको अपने आवेदन में संभालना है न कि मैपिंग में। मानचित्रण के दृष्टिकोण से, जोड़े को दो बार खेलने की अनुमति है (प्रत्येक अतिथि और एक बार घर है)।
लादिस्लाव मृणका

2
मेरे पास एक समान मॉडल है। यदि किसी टीम को हटा दिया जाता है, तो कैस्केड डिलीट को संभालने का उचित तरीका क्या है? मैंने DELETE ट्रिगर का एक INSTEAD बनाने पर ध्यान दिया, लेकिन निश्चित नहीं कि अगर कोई बेहतर उपाय है? मैं डीबी में इसे संभालना चाहूंगा, एप्लिकेशन को नहीं।
वुडचिपर

1
@ मृदुकाडांस: यह एक ही है। एक दृष्टिकोण धाराप्रवाह एपीआई और दूसरे डेटा एनोटेशन का उपयोग करता है।
लदिस्लाव मृंका

1
अगर मैं WillCascadeOnDelete का उपयोग करता हूं तो यदि मैं टीम को हटाना चाहता हूं तो यह त्रुटि है। 'Team_HomeMatches' एसोसिएशनसेट से एक संबंध 'हटाए गए' राज्य में है। बहुलता की कमी को देखते हुए, संबंधित 'Team_HomeMatches_Target' को 'हटाए गए' राज्य में भी होना चाहिए।
रूपेश कुमार तिवारी

55

ForeignKey()नेविगेशन संपत्ति पर विशेषता निर्दिष्ट करना भी संभव है :

[ForeignKey("HomeTeamID")]
public virtual Team HomeTeam { get; set; }
[ForeignKey("GuestTeamID")]
public virtual Team GuestTeam { get; set; }

इस तरह से आपको OnModelCreateविधि में कोई कोड जोड़ने की आवश्यकता नहीं है


4
मुझे उसी तरह का अपवाद मिलता है।
जो स्मो डे

11
यह विदेशी कुंजी निर्दिष्ट करने का मेरा मानक तरीका है जो सभी मामलों के लिए काम करता है EXCEPT जब एक इकाई में एक ही प्रकार की एक से अधिक संपत्ति होती है (HomeTeam और GuestTeam परिदृश्य के समान), तो उस स्थिति में EF SQL उत्पन्न करने में भ्रमित हो जाता है। समाधान को OnModelCreateस्वीकार किए गए उत्तर के साथ-साथ रिश्ते के दोनों पक्षों के लिए दो संग्रहों के अनुसार कोड जोड़ना है ।
स्टीवन मैनुअल

मैं उल्लिखित मामले को छोड़कर सभी मामलों में onmodelcreating का उपयोग करता हूं, मैं डेटा एनोटेशन विदेशी कुंजी का उपयोग करता हूं, साथ ही मुझे नहीं पता कि इसे क्यों स्वीकार नहीं किया गया है !!
होसम हेमली

48

मुझे पता है कि यह कई साल पुरानी पोस्ट है और आप उपरोक्त समस्या का समाधान कर सकते हैं। हालाँकि, मैं सिर्फ किसी ऐसे व्यक्ति के लिए InverseProperty का उपयोग करने का सुझाव देना चाहता हूं, जिसे अभी भी जरूरत है। कम से कम आपको OnModelCreating में कुछ भी बदलने की आवश्यकता नहीं है।

नीचे दिए गए कोड का परीक्षण नहीं किया गया है।

public class Team
{
    [Key]
    public int TeamId { get; set;} 
    public string Name { get; set; }

    [InverseProperty("HomeTeam")]
    public virtual ICollection<Match> HomeMatches { get; set; }

    [InverseProperty("GuestTeam")]
    public virtual ICollection<Match> GuestMatches { get; set; }
}


public class Match
{
    [Key]
    public int MatchId { get; set; }

    public float HomePoints { get; set; }
    public float GuestPoints { get; set; }
    public DateTime Date { get; set; }

    public virtual Team HomeTeam { get; set; }
    public virtual Team GuestTeam { get; set; }
}

आप MSDN पर InverseProperty के बारे में अधिक पढ़ सकते हैं: https://msdn.microsoft.com/en-us/data/jj591583?f=255&MSPPError=-2147217396#Relationships


1
इस उत्तर के लिए धन्यवाद, हालांकि यह मैच टेबल में विदेशी कुंजी कॉलम को अशक्त बनाता है।
रोबुर्द

यह ईएफ 6 में मेरे लिए बहुत अच्छा काम करता है, जहां अशक्त संग्रह की आवश्यकता होती है।
Pynt

यदि आप धाराप्रवाह एपि से बचना चाहते हैं (जो भी कारण #differentdiscussion के लिए है) यह काल्पनिक रूप से काम करता है। मेरे मामले में मुझे "मैच" इकाई पर एक अतिरिक्त foriegnKey एनोटेशन शामिल करने की आवश्यकता थी, क्योंकि मेरे खेतों / तालिकाओं में पीके के लिए तार हैं।
DiscipleMichael

1
इसने मेरे लिए बहुत काम किया। Btw। यदि आप स्तंभों को अशक्त नहीं करना चाहते हैं, तो आप [ForeignKey] विशेषता के साथ विदेशी कुंजी निर्दिष्ट कर सकते हैं। यदि कुंजी अशक्त नहीं है, तो आप सभी सेट हैं।
जैकब होलोव्स्की

16

आप इसे भी आजमा सकते हैं:

public class Match
{
    [Key]
    public int MatchId { get; set; }

    [ForeignKey("HomeTeam"), Column(Order = 0)]
    public int? HomeTeamId { get; set; }
    [ForeignKey("GuestTeam"), Column(Order = 1)]
    public int? GuestTeamId { get; set; }

    public float HomePoints { get; set; }
    public float GuestPoints { get; set; }
    public DateTime Date { get; set; }

    public virtual Team HomeTeam { get; set; }
    public virtual Team GuestTeam { get; set; }
}

जब आप एक FK कॉलम NULLS बनाते हैं, तो आप चक्र तोड़ रहे हैं। या हम ईएफ स्कीमा जनरेटर को धोखा दे रहे हैं।

मेरे मामले में, यह सरल संशोधन समस्या को हल करता है।


3
पाठकों को सावधानी। यद्यपि यह स्कीमा परिभाषा समस्या के आसपास काम कर सकता है, यह शब्दार्थ को बदल देता है। यह शायद ऐसा नहीं है कि दो टीमों के बिना एक मैच हो सकता है।
N8allan

14

ऐसा इसलिए है क्योंकि कैस्केड हटाए गए डिफ़ॉल्ट रूप से सक्षम हैं। समस्या यह है कि जब आप इकाई को हटाते हैं, तो यह प्रत्येक एफ-कुंजी संदर्भित संस्थाओं को भी हटा देगा। आपको इस समस्या को ठीक करने के लिए 'आवश्यक' मानों को अशक्त नहीं बनाना चाहिए। ईएफ कोड फर्स्ट कास्कैड हटाओ सम्मेलन में एक बेहतर विकल्प होगा:

modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>(); 

मैपिंग / कॉन्फिग के दौरान बच्चों में से प्रत्येक के लिए कैस्केड डिलीट करने के लिए यह स्पष्ट रूप से इंगित करना सुरक्षित है। सत्ता।


तो यह निष्पादित होने के बाद क्या है? Restrictके बजाय Cascade?
जो स्मो डे

4

InverseProperty ईएफ कोर में समाधान को आसान और साफ बनाता है।

InverseProperty

तो वांछित समाधान होगा:

public class Team
{
    [Key]
    public int TeamId { get; set;} 
    public string Name { get; set; }

    [InverseProperty(nameof(Match.HomeTeam))]
    public ICollection<Match> HomeMatches{ get; set; }

    [InverseProperty(nameof(Match.GuestTeam))]
    public ICollection<Match> AwayMatches{ get; set; }
}


public class Match
{
    [Key]
    public int MatchId { get; set; }

    [ForeignKey(nameof(HomeTeam)), Column(Order = 0)]
    public int HomeTeamId { get; set; }
    [ForeignKey(nameof(GuestTeam)), Column(Order = 1)]
    public int GuestTeamId { get; set; }

    public float HomePoints { get; set; }
    public float GuestPoints { get; set; }
    public DateTime Date { get; set; }

    public Team HomeTeam { get; set; }
    public Team GuestTeam { get; set; }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.