Varlık Çerçeve Kodu İlk - aynı tablodan iki Yabancı Anahtar


260

İlk önce EF kodunu kullanmaya başladım, bu yüzden bu konuda toplam bir acemiyim.

Takımlar ve Maçlar arasında ilişkiler kurmak istedim:

1 maç = 2 takım (ev sahibi, misafir) ve sonuç.

Böyle bir model oluşturmanın kolay olduğunu düşündüm, bu yüzden kodlamaya başladım:

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; }
}

Ve bir istisna alıyorum:

Referans ilişkisi, izin verilmeyen bir çevrimsel referans ile sonuçlanacaktır. [Kısıtlama adı = Match_GuestTeam]

Aynı masaya 2 yabancı anahtar içeren böyle bir modeli nasıl oluşturabilirim?

Yanıtlar:


297

Bunu dene:

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);
    }
}

Birincil anahtarlar varsayılan kurallara göre eşlenir. Takımın iki kibrit koleksiyonu olmalıdır. İki FK tarafından başvurulan tek bir koleksiyonunuz olamaz. Maç basamaklı silme olmadan eşlenir, çünkü bu kendinden referanslı çoktan çoğa çalışmaz.


3
İki takımın sadece bir kez oynamalarına izin verilirse ne olur?
ca9163d9

4
@NickW: Bu, eşlemede değil uygulamanızda işlemeniz gereken bir şeydir. Eşleme açısından bakıldığında, çiftlerin iki kez oynamalarına izin verilir (her biri bir kez konuk ve evdir).
Ladislav Mrnka

2
Benzer bir modelim var. Bir takım kaldırılırsa kademeli silme işlemini gerçekleştirmenin doğru yolu nedir? INSTEAD DELETE tetikleyici oluşturmaya baktım ama daha iyi bir çözüm olup olmadığından emin değilim? Ben uygulama değil, DB işlemek için tercih ederdim.
Woodchipper

1
@mrshickadance: Aynı. Bir yaklaşımda akıcı API ve başka bir veri ek açıklamaları kullanılır.
Ladislav Mrnka

1
Eğer WillCascadeOnDelete false kullanırsam, o zaman Ekibi silmek istersem, o zaman hata atıyor. 'Team_HomeMatches' AssociationSet öğesinden bir ilişki 'Silindi' durumunda. Çokluk kısıtlamaları göz önüne alındığında, karşılık gelen bir 'Team_HomeMatches_Target' de 'Silindi' durumunda olmalıdır.
Rupesh Kumar Tiwari

55

Bu ForeignKey()özellik, navigation özelliğinde belirtmek de mümkündür :

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

Bu şekilde, OnModelCreateyönteme herhangi bir kod eklemenize gerek kalmaz


4
Her iki şekilde de aynı istisnayı görüyorum.
Jo Smo

11
Bu, bir varlık aynı türde birden fazla nav özelliği (HomeTeam ve GuestTeam senaryosuna benzer) içerdiğinde, tüm durumlarda geçerli olan yabancı anahtarları belirtmenin standart yoludur, bu durumda EF SQL'i oluşturmakta kafası karışır. Çözüm, OnModelCreatekabul edilen cevaba göre kodun yanı sıra ilişkinin her iki tarafı için iki koleksiyon eklemek .
Steven Manuel

i belirtilen durum hariç tüm durumlarda onmodelcreating kullanın, ben veri notu yabancı anahtar kullanın, neden ben kabul edilmediğini bilmiyorum !!
hosam hemaily

48

Biliyorum birkaç yaşında bir yazı ve yukarıdaki çözüm ile sorununuzu çözebilirsiniz. Ancak, sadece hala ihtiyacı olan biri için InverseProperty kullanarak önermek istiyorum. En azından OnModelCreating uygulamasında hiçbir şeyi değiştirmeniz gerekmez.

Aşağıdaki kod test edilmemiştir.

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'de InverseProperty hakkında daha fazla bilgi edinebilirsiniz: https://msdn.microsoft.com/en-us/data/jj591583?f=255&MSPPError=-2147217396#Relationships


1
Bu yanıt için teşekkürler, ancak Eşleşme tablosunda Yabancı Anahtar sütunlarını geçersiz kılar.
RobHurd

Bu, null edilebilir koleksiyonların gerekli olduğu EF 6'da benim için harika çalıştı.
Pynt

Akıcı api'den kaçınmak istiyorsanız (#differentdiscussion herhangi bir nedenle) bu fevkalade çalışır. Benim durumumda, alanlar / tablolarımın PK'lar için dizeleri olduğu için, "Maç" varlığına ek bir foriegnKey ek açıklaması eklemem gerekiyordu.
DiscipleMichael

1
Bu benim için çok işe yaradı. Btw. sütunların boş bırakılmasını istemiyorsanız, yalnızca [ForeignKey] özniteliğiyle yabancı anahtar belirtebilirsiniz. Anahtar null değeri yoksa ayarlanmış demektir.
Jakub Holovsky

16

Bunu da deneyebilirsiniz:

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; }
}

Bir FK sütununu NULLS'a izin verdiğinizde, döngüyü bozuyorsunuz. Ya da sadece EF şema jeneratörünü kandırıyoruz.

Benim durumumda, bu basit değişiklik sorunu çözüyor.


3
Dikkat okuyucuları. Her ne kadar bu şema tanımlama sorunu geçici çözüm olsa da, anlambilimi değiştirir. Muhtemelen iki takım olmadan maç yapılabilir.
N8allan

14

Bunun nedeni, Basamaklı Silme işlemlerinin varsayılan olarak etkin olmasıdır. Sorun, varlık üzerinde bir silme çağırdığınızda, f-tuş başvurulan varlıkların her birini de siler. Bu sorunu gidermek için 'gerekli' değerleri boş bırakmamalısınız. Daha iyi bir seçenek, EF Code First'in Cascade silme kuralını kaldırmak olabilir:

modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>(); 

Eşleme / yapılandırma sırasında her bir çocuk için bir basamaklı silme işleminin ne zaman yapılacağını açıkça belirtmek daha güvenlidir. varlık.


Peki bu idam edildikten sonra ne olacak? Restrictyerine Cascade?
Jo Smo

4

InverseProperty EF Core'da çözümü kolay ve temiz hale getirir.

InverseProperty

Böylece istenen çözüm:

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; }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.