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.
OnModelCreating
. L'exemple repose uniquement sur les conventions de mappage et les annotations de données.MemberId
etCommentId
colonnes et non une troisième colonne supplémentaireMember_CommentId
(ou quelque chose comme ça) - ce qui signifie que vous n'avez pas des noms de correspondance exacte à travers des objets pour vos clésExcellente réponse de Slauma.
Je vais juste publier le code pour ce faire en utilisant le mappage d' API fluide .
Sur votre
DbContext
classe dérivée, vous pouvez faire ceci:Elle a le même effet que la réponse acceptée, avec une approche différente, qui n'est ni meilleure ni pire.
EDIT:
j'ai changé CreatedDate de bool à DateTime.EDIT 2: Par manque de temps, j'ai placé un exemple à partir d'une application sur laquelle je travaille pour être sûr que cela fonctionne.
la source
In your classes you can easily describe a many to many relationship with properties that point to each other.
extrait de: msdn.microsoft.com/en-us/data/hh134698.aspx . Julie Lerman ne peut pas se tromper.Comments
propriété dansMember
. Et vous ne pouvez pas simplement résoudre ce problème en renommant l'HasMany
appelMemberComments
car l'MemberComment
entité n'a pas de collection inverse pourWithMany
. En fait, vous devez configurer deux relations un-à-plusieurs pour obtenir le bon mappage.@Esteban, le code que vous avez fourni est correct, merci, mais incomplet, je l'ai testé. Il manque des propriétés dans la classe "UserEmail":
Je poste le code que j'ai testé si quelqu'un est intéressé. Cordialement
la source
Je veux proposer une solution où les deux saveurs d'une configuration plusieurs-à-plusieurs peuvent être obtenues.
Le «catch» est que nous devons créer une vue qui cible la table de jointure, car EF valide que la table d'un schéma peut être mappée au plus une fois par
EntitySet
.Cette réponse s'ajoute à ce qui a déjà été dit dans les réponses précédentes et ne remplace aucune de ces approches, elle s'appuie sur elles.
Le modèle:
La configuration:
Le contexte:
De la réponse de Saluma (@Saluma)
Cela fonctionne toujours ...
... mais pourrait aussi l'être maintenant ...
Cela fonctionne toujours ...
... mais pourrait aussi l'être maintenant ...
Si vous souhaitez supprimer un commentaire d'un membre
Si vous voulez
Include()
les commentaires d'un membreTout cela ressemble à du sucre syntaxique, mais cela vous donne quelques avantages si vous êtes prêt à passer par la configuration supplémentaire. Quoi qu'il en soit, vous semblez être en mesure de tirer le meilleur parti des deux approches.
la source
EntityTypeConfiguration<EntityType>
la clé et les propriétés du type d'entité. Par exemple,Property(x => x.MemberID).HasColumnType("int").IsRequired();
semble être redondant avecpublic int MemberID { get; set; }
. Pourriez-vous effacer ma compréhension confuse s'il vous plaît?TLDR; (semi-lié à un bogue de l'éditeur EF dans EF6 / VS2012U5) si vous générez le modèle à partir de la base de données et que vous ne pouvez pas voir la table m: m attribuée: supprimez les deux tables liées -> Enregistrer .edmx -> Générer / ajouter à partir de la base de données - > Enregistrer.
Pour ceux qui sont venus ici se demander comment obtenir une relation plusieurs-à-plusieurs avec des colonnes d'attributs à afficher dans le fichier EF .edmx (car il ne serait actuellement pas affiché et traité comme un ensemble de propriétés de navigation), ET vous avez généré ces classes à partir de votre table de base de données (ou de la base de données d'abord dans le jargon MS, je crois.)
Supprimez les 2 tables en question (pour prendre l'exemple OP, Member et Comment) dans votre .edmx et ajoutez-les à nouveau via 'Générer le modèle à partir de la base de données'. (c.-à-d. n'essayez pas de laisser Visual Studio les mettre à jour - supprimer, enregistrer, ajouter, enregistrer)
Il créera ensuite un 3e tableau en ligne avec ce qui est suggéré ici.
Ceci est pertinent dans les cas où une relation plusieurs-à-plusieurs pure est ajoutée dans un premier temps, et les attributs sont conçus dans la base de données plus tard.
Ce n'était pas immédiatement clair à partir de ce fil / Google. Il suffit donc de le mettre en ligne car il s'agit du lien n ° 1 sur Google à la recherche du problème, mais venant du côté DB en premier.
la source
Une façon de résoudre cette erreur consiste à placer l'
ForeignKey
attribut au-dessus de la propriété que vous souhaitez en tant que clé étrangère et à ajouter la propriété de navigation.Remarque: Dans l'
ForeignKey
attribut, entre parenthèses et guillemets doubles, placez le nom de la classe référencée de cette manière.la source