Ausgabe
Ich habe dieses Szenario:
public class Member
{
public int MemberID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual ICollection<Comment> Comments { get; set; }
}
public class Comment
{
public int CommentID { get; set; }
public string Message { get; set; }
public virtual ICollection<Member> Members { get; set; }
}
public class MemberComment
{
public int MemberID { get; set; }
public int CommentID { get; set; }
public int Something { get; set; }
public string SomethingElse { get; set; }
}
Wie konfiguriere ich meine Verbindung mit der Fluent-API ? Oder gibt es eine bessere Möglichkeit, die Zuordnungstabelle zu erstellen?
Lösung
Es ist nicht möglich, eine Viele-zu-Viele-Beziehung mit einer benutzerdefinierten Join-Tabelle zu erstellen. In einer Viele-zu-Viele-Beziehung verwaltet EF die Join-Tabelle intern und verborgen. Es ist eine Tabelle ohne eine Entitätsklasse in Ihrem Modell. Um mit einer solchen Join-Tabelle 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; }
}
LastName
Wenn Sie nun alle Kommentare von Mitgliedern mit z. B. = “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 von Mitgliedern 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();
If you want to find all comments of a member with MemberId
= 1:
var commentsOfMember = context.MemberComments
.Where(mc => mc.MemberId == 1)
.Select(mc => mc.Comment)
.ToList();
Now you can also filter by the properties in your join table (which would not be possible in a many-to-many relationship), for example: Filter all comments of member 1 which have a 99 in property Something
:
var filteredCommentsOfMember = context.MemberComments
.Where(mc => mc.MemberId == 1 && mc.Something == 99)
.Select(mc => mc.Comment)
.ToList();
Because of lazy loading things might become easier. If you have a loaded Member
you should be able to get the comments without an explicit query:
var commentsOfMember = member.MemberComments.Select(mc => mc.Comment);
I guess that lazy loading will fetch the comments automatically behind the scenes.
Edit
Just for fun a few examples more how to add entities and relationships and how to delete them in this model:
1) Create one member and two comments of this member:
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) Add a third comment of member1:
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) Create new member and relate it to the existing comment2:
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) Create relationship between existing member2 and comment3:
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) Diese Beziehung wieder löschen:
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 in gelöscht MemberComments
, da die Eins-zu-viele-Beziehungen zwischen Member
und MemberComments
und zwischen Comment
und MemberComments
per Konvention mit kaskadierendem Löschen eingerichtet werden. Und dies ist der Fall, weil MemberId
und CommentId
in MemberComment
als Fremdschlüsseleigenschaften für die Navigationseigenschaften und erkannt Member
werden Comment
und da die FK-Eigenschaften vom Typ Non-Nullable sind int
, ist die Beziehung erforderlich, die schließlich das Cascading-Delete-Setup verursacht. Macht bei diesem Modell Sinn, finde ich.
Beantwortet von – Slauma
Antwort geprüft von – David Marino (FixError Volunteer)