Es ist nicht möglich, eine Viele-zu-Viele-Beziehung mit einer benutzerdefinierten Verknüpfungstabelle zu erstellen. In einer Viele-zu-Viele-Beziehung verwaltet EF die Verknüpfungstabelle intern und versteckt. Es ist eine Tabelle ohne Entitätsklasse in Ihrem Modell. Um mit einer solchen Verknüpfungstabelle mit zusätzlichen Eigenschaften zu arbeiten, müssen Sie tatsächlich zwei Eins-zu-Viele-Beziehungen erstellen. Es könnte so aussehen:
public class Member
{
public int MemberID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual ICollection<MemberComment> MemberComments { get; set; }
}
public class Comment
{
public int CommentID { get; set; }
public string Message { get; set; }
public virtual ICollection<MemberComment> MemberComments { get; set; }
}
public class MemberComment
{
[Key, Column(Order = 0)]
public int MemberID { get; set; }
[Key, Column(Order = 1)]
public int CommentID { get; set; }
public virtual Member Member { get; set; }
public virtual Comment Comment { get; set; }
public int Something { get; set; }
public string SomethingElse { get; set; }
}
Wenn Sie jetzt beispielsweise alle Kommentare von Mitgliedern mit LastName
= "Smith" finden möchten, können Sie eine Abfrage wie folgt schreiben:
var commentsOfMembers = context.Members
.Where(m => m.LastName == "Smith")
.SelectMany(m => m.MemberComments.Select(mc => mc.Comment))
.ToList();
... oder ...
var commentsOfMembers = context.MemberComments
.Where(mc => mc.Member.LastName == "Smith")
.Select(mc => mc.Comment)
.ToList();
Oder um eine Liste der Mitglieder mit dem Namen "Smith" (wir nehmen an, dass es mehr als eines gibt) zusammen mit ihren Kommentaren zu erstellen, können Sie eine Projektion verwenden:
var membersWithComments = context.Members
.Where(m => m.LastName == "Smith")
.Select(m => new
{
Member = m,
Comments = m.MemberComments.Select(mc => mc.Comment)
})
.ToList();
Wenn Sie alle Kommentare eines Mitglieds mit MemberId
= 1 finden möchten :
var commentsOfMember = context.MemberComments
.Where(mc => mc.MemberId == 1)
.Select(mc => mc.Comment)
.ToList();
Jetzt können Sie auch nach den Eigenschaften in Ihrer Join-Tabelle filtern (was in einer Viele-zu-Viele-Beziehung nicht möglich wäre). Beispiel: Filtern Sie alle Kommentare von Mitglied 1 mit einer 99-Eigenschaft Something
:
var filteredCommentsOfMember = context.MemberComments
.Where(mc => mc.MemberId == 1 && mc.Something == 99)
.Select(mc => mc.Comment)
.ToList();
Durch das verzögerte Laden könnten die Dinge einfacher werden. Wenn Sie eine geladen haben Member
, sollten Sie in der Lage sein, die Kommentare ohne eine explizite Abfrage zu erhalten:
var commentsOfMember = member.MemberComments.Select(mc => mc.Comment);
Ich denke, dass das verzögerte Laden die Kommentare automatisch hinter den Kulissen abruft.
Bearbeiten
Nur zum Spaß ein paar Beispiele mehr, wie man Entitäten und Beziehungen hinzufügt und wie man sie in diesem Modell löscht:
1) Erstellen Sie ein Mitglied und zwei Kommentare dieses Mitglieds:
var member1 = new Member { FirstName = "Pete" };
var comment1 = new Comment { Message = "Good morning!" };
var comment2 = new Comment { Message = "Good evening!" };
var memberComment1 = new MemberComment { Member = member1, Comment = comment1,
Something = 101 };
var memberComment2 = new MemberComment { Member = member1, Comment = comment2,
Something = 102 };
context.MemberComments.Add(memberComment1); // will also add member1 and comment1
context.MemberComments.Add(memberComment2); // will also add comment2
context.SaveChanges();
2) Fügen Sie einen dritten Kommentar von member1 hinzu:
var member1 = context.Members.Where(m => m.FirstName == "Pete")
.SingleOrDefault();
if (member1 != null)
{
var comment3 = new Comment { Message = "Good night!" };
var memberComment3 = new MemberComment { Member = member1,
Comment = comment3,
Something = 103 };
context.MemberComments.Add(memberComment3); // will also add comment3
context.SaveChanges();
}
3) Erstellen Sie ein neues Mitglied und verknüpfen Sie es mit dem vorhandenen Kommentar2:
var comment2 = context.Comments.Where(c => c.Message == "Good evening!")
.SingleOrDefault();
if (comment2 != null)
{
var member2 = new Member { FirstName = "Paul" };
var memberComment4 = new MemberComment { Member = member2,
Comment = comment2,
Something = 201 };
context.MemberComments.Add(memberComment4);
context.SaveChanges();
}
4) Erstellen Sie eine Beziehung zwischen vorhandenem Mitglied2 und Kommentar3:
var member2 = context.Members.Where(m => m.FirstName == "Paul")
.SingleOrDefault();
var comment3 = context.Comments.Where(c => c.Message == "Good night!")
.SingleOrDefault();
if (member2 != null && comment3 != null)
{
var memberComment5 = new MemberComment { Member = member2,
Comment = comment3,
Something = 202 };
context.MemberComments.Add(memberComment5);
context.SaveChanges();
}
5) Löschen Sie diese Beziehung erneut:
var memberComment5 = context.MemberComments
.Where(mc => mc.Member.FirstName == "Paul"
&& mc.Comment.Message == "Good night!")
.SingleOrDefault();
if (memberComment5 != null)
{
context.MemberComments.Remove(memberComment5);
context.SaveChanges();
}
6) Löschen Sie member1 und alle seine Beziehungen zu den Kommentaren:
var member1 = context.Members.Where(m => m.FirstName == "Pete")
.SingleOrDefault();
if (member1 != null)
{
context.Members.Remove(member1);
context.SaveChanges();
}
Dadurch werden auch die Beziehungen gelöscht, MemberComments
da die Eins-zu-Viele-Beziehungen zwischen Member
und MemberComments
und zwischen Comment
und MemberComments
gemäß Konvention mit kaskadierendem Löschen eingerichtet werden. Und dies ist der Fall, weil MemberId
und CommentId
in MemberComment
als Fremdschlüsseleigenschaften für die Member
und Comment
Navigationseigenschaften erkannt werden und da die FK-Eigenschaften vom Typ nicht nullbar sind, ist int
die Beziehung erforderlich, die schließlich das Kaskadierungs-Lösch-Setup verursacht. Sinnvoll in diesem Modell, denke ich.
OnModelCreating
. Das Beispiel basiert nur auf Zuordnungskonventionen und Datenanmerkungen.MemberId
undCommentId
Spalten und keine zusätzliche dritte SpalteMember_CommentId
(oder ähnliches) haben. Dies bedeutet, dass Sie keine genau übereinstimmenden Namen hatten über Objekte für Ihre SchlüsselHervorragende Antwort von Slauma.
Ich werde einfach den Code veröffentlichen, um dies mithilfe der fließenden API- Zuordnung zu tun .
In Ihrer
DbContext
abgeleiteten Klasse können Sie Folgendes tun:Es hat den gleichen Effekt wie die akzeptierte Antwort, mit einem anderen Ansatz, der weder besser noch schlechter ist.
BEARBEITEN:
Ich habe CreatedDate von bool in DateTime geändert.EDIT 2: Aus Zeitgründen habe ich ein Beispiel aus einer Anwendung platziert, an der ich arbeite, um sicherzustellen, dass dies funktioniert.
quelle
In your classes you can easily describe a many to many relationship with properties that point to each other.
entnommen aus: msdn.microsoft.com/en-us/data/hh134698.aspx . Julie Lerman kann sich nicht irren.Comments
Eigenschaft in habenMember
. Und Sie können dies nicht einfach beheben, indem Sie denHasMany
Aufruf in umbenennen ,MemberComments
da dieMemberComment
Entität keine inverse Sammlung für hatWithMany
. Tatsächlich müssen Sie zwei Eins-zu-Viele-Beziehungen konfigurieren , um die richtige Zuordnung zu erhalten.@Esteban, der von Ihnen angegebene Code ist richtig, danke, aber unvollständig, ich habe ihn getestet. In der Klasse "UserEmail" fehlen Eigenschaften:
Ich poste den Code, den ich getestet habe, wenn jemand interessiert ist. Grüße
quelle
Ich möchte eine Lösung vorschlagen, bei der beide Varianten einer Viele-zu-Viele-Konfiguration erreicht werden können.
Der "Haken" ist, dass wir eine Ansicht erstellen müssen, die auf die Join-Tabelle abzielt, da EF überprüft, ob die Tabelle eines Schemas höchstens einmal pro Tabelle zugeordnet werden darf
EntitySet
.Diese Antwort ergänzt das, was bereits in früheren Antworten gesagt wurde, und überschreibt keinen dieser Ansätze, sondern baut auf ihnen auf.
Das Model:
Die Konfiguration:
Der Kontext:
Aus Salumas (@Saluma) Antwort
Das funktioniert immer noch ...
... könnte aber jetzt auch sein ...
Das funktioniert immer noch ...
... könnte aber jetzt auch sein ...
Wenn Sie einen Kommentar von einem Mitglied entfernen möchten
Wenn Sie
Include()
die Kommentare eines Mitglieds möchtenDies alles fühlt sich wie syntaktischer Zucker an, bringt Ihnen jedoch einige Vorteile, wenn Sie bereit sind, die zusätzliche Konfiguration durchzugehen. In beiden Fällen scheinen Sie in der Lage zu sein, das Beste aus beiden Ansätzen herauszuholen.
quelle
EntityTypeConfiguration<EntityType>
den Schlüssel und die Eigenschaften des Entitätstyps . ZBProperty(x => x.MemberID).HasColumnType("int").IsRequired();
scheint überflüssig zu seinpublic int MemberID { get; set; }
. Könnten Sie bitte mein verwirrendes Verständnis klären?TLDR; (Teilweise verwandt mit einem EF-Editor-Fehler in EF6 / VS2012U5) Wenn Sie das Modell aus der Datenbank generieren und die zugeordnete m: m-Tabelle nicht sehen können: Löschen Sie die beiden zugehörigen Tabellen -> .edmx speichern -> Aus Datenbank generieren / hinzufügen - > Speichern.
Für diejenigen, die hierher gekommen sind und sich gefragt haben, wie eine Viele-zu-Viele-Beziehung zu Attributspalten in der EF .edmx-Datei angezeigt werden kann (da diese derzeit nicht angezeigt und als Satz von Navigationseigenschaften behandelt wird), UND Sie haben diese Klassen generiert aus Ihrer Datenbanktabelle (oder Datenbank zuerst in MS-Jargon, glaube ich.)
Löschen Sie die beiden fraglichen Tabellen (um das OP-Beispiel "Mitglied" und "Kommentar" zu verwenden) in Ihrer .edmx-Datei und fügen Sie sie erneut über "Modell aus Datenbank generieren" hinzu. (dh versuchen Sie nicht, Visual Studio sie aktualisieren zu lassen - löschen, speichern, hinzufügen, speichern)
Anschließend wird eine dritte Tabelle erstellt, die den hier vorgeschlagenen entspricht.
Dies ist in Fällen relevant, in denen zunächst eine reine Viele-zu-Viele-Beziehung hinzugefügt wird und die Attribute später in der Datenbank entworfen werden.
Dies war aus diesem Thread / Googeln nicht sofort ersichtlich. Stellen Sie es einfach da raus, da dies Link Nr. 1 bei Google ist, der nach dem Problem sucht, aber zuerst von der DB-Seite kommt.
quelle
Eine Möglichkeit, diesen Fehler zu beheben, besteht darin, das zu setzen
ForeignKey
Attribut als Fremdschlüssel über die gewünschte Eigenschaft und die Navigationseigenschaft hinzuzufügen.Hinweis:
ForeignKey
Platzieren Sie im Attribut zwischen Klammern und doppelten Anführungszeichen den Namen der Klasse, auf die auf diese Weise verwiesen wird.quelle