Il n'est pas possible de créer une relation plusieurs-à-plusieurs avec une table de jointure personnalisée. Dans une relation plusieurs-à-plusieurs, EF gère la table de jointure en interne et est masquée. Il s'agit d'une table sans classe Entity dans votre modèle. Pour travailler avec une telle table de jointure avec des propriétés supplémentaires, vous devrez créer en fait deux relations un-à-plusieurs. Cela pourrait ressembler à ceci:
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; }
}
Si vous voulez maintenant trouver tous les commentaires des membres avec LastName
= "Smith" par exemple, vous pouvez écrire une requête comme celle-ci:
var commentsOfMembers = context.Members
.Where(m => m.LastName == "Smith")
.SelectMany(m => m.MemberComments.Select(mc => mc.Comment))
.ToList();
... ou ...
var commentsOfMembers = context.MemberComments
.Where(mc => mc.Member.LastName == "Smith")
.Select(mc => mc.Comment)
.ToList();
Ou pour créer une liste de membres avec le nom "Smith" (nous supposons qu'il y en a plus d'un) avec leurs commentaires, vous pouvez utiliser une projection:
var membersWithComments = context.Members
.Where(m => m.LastName == "Smith")
.Select(m => new
{
Member = m,
Comments = m.MemberComments.Select(mc => mc.Comment)
})
.ToList();
Si vous souhaitez retrouver tous les commentaires d'un membre avec MemberId
= 1:
var commentsOfMember = context.MemberComments
.Where(mc => mc.MemberId == 1)
.Select(mc => mc.Comment)
.ToList();
Maintenant, vous pouvez également filtrer par les propriétés de votre table de jointure (ce qui ne serait pas possible dans une relation plusieurs-à-plusieurs), par exemple: Filtrez tous les commentaires du membre 1 qui ont une propriété 99 dans Something
:
var filteredCommentsOfMember = context.MemberComments
.Where(mc => mc.MemberId == 1 && mc.Something == 99)
.Select(mc => mc.Comment)
.ToList();
En raison du chargement paresseux, les choses pourraient devenir plus faciles. Si vous avez un chargé, Member
vous devriez pouvoir obtenir les commentaires sans requête explicite:
var commentsOfMember = member.MemberComments.Select(mc => mc.Comment);
Je suppose que le chargement paresseux récupérera automatiquement les commentaires dans les coulisses.
Éditer
Juste pour le plaisir, quelques exemples supplémentaires sur la façon d'ajouter des entités et des relations et de les supprimer dans ce modèle:
1) Créez un membre et deux commentaires de ce membre:
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) Ajoutez un troisième commentaire de membre1:
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) Créez un nouveau membre et associez-le au commentaire existant2:
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) Créez une relation entre le membre2 existant et le commentaire3:
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) Supprimez à nouveau cette relation:
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) Supprimez le membre1 et toutes ses relations avec les commentaires:
var member1 = context.Members.Where(m => m.FirstName == "Pete")
.SingleOrDefault();
if (member1 != null)
{
context.Members.Remove(member1);
context.SaveChanges();
}
Cela supprime également les relations MemberComments
car les relations un à plusieurs entre Member
et MemberComments
et entre Comment
et MemberComments
sont configurées avec la suppression en cascade par convention. Et cela est le cas parce que MemberId
et CommentId
en MemberComment
sont détectés comme des propriétés clés étrangères pour les Member
et Comment
propriétés navigation et étant donné que les propriétés FK sont de type non annulable int
la relation est nécessaire , ce qui provoque finalement la configuration en cascade-suppression. Cela a du sens dans ce modèle, je pense.