Nie można utworzyć relacji wiele do wielu za pomocą dostosowanej tabeli łączenia. W relacji wiele do wielu EF zarządza wewnętrzną i ukrytą tabelą łączenia. To stół bez klasy Entity w twoim modelu. Aby pracować z taką tabelą łączenia z dodatkowymi właściwościami, musisz utworzyć dwie relacje jeden do wielu. Może to wyglądać tak:
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; }
}
Jeśli chcesz teraz znaleźć wszystkie komentarze członków z LastName
= = Smith, możesz na przykład napisać takie zapytanie:
var commentsOfMembers = context.Members
.Where(m => m.LastName == "Smith")
.SelectMany(m => m.MemberComments.Select(mc => mc.Comment))
.ToList();
... lub ...
var commentsOfMembers = context.MemberComments
.Where(mc => mc.Member.LastName == "Smith")
.Select(mc => mc.Comment)
.ToList();
Lub, aby utworzyć listę członków o nazwie „Smith” (zakładamy, że jest ich więcej) wraz z ich komentarzami, możesz użyć projekcji:
var membersWithComments = context.Members
.Where(m => m.LastName == "Smith")
.Select(m => new
{
Member = m,
Comments = m.MemberComments.Select(mc => mc.Comment)
})
.ToList();
Jeśli chcesz znaleźć wszystkie komentarze członka z MemberId
= 1:
var commentsOfMember = context.MemberComments
.Where(mc => mc.MemberId == 1)
.Select(mc => mc.Comment)
.ToList();
Teraz możesz również filtrować według właściwości w tabeli łączenia (co nie byłoby możliwe w relacji wiele do wielu), na przykład: Filtruj wszystkie komentarze członka 1, które mają właściwość 99 Something
:
var filteredCommentsOfMember = context.MemberComments
.Where(mc => mc.MemberId == 1 && mc.Something == 99)
.Select(mc => mc.Comment)
.ToList();
Z powodu leniwego ładowania rzeczy mogą stać się łatwiejsze. Jeśli masz załadowany Member
, powinieneś być w stanie uzyskać komentarze bez wyraźnego zapytania:
var commentsOfMember = member.MemberComments.Select(mc => mc.Comment);
Myślę, że leniwe ładowanie automatycznie pobierze komentarze za kulisami.
Edytować
Dla zabawy kilka przykładów, jak dodawać encje i relacje oraz jak je usuwać w tym modelu:
1) Utwórz jednego członka i dwa komentarze tego członka:
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) Dodaj trzeci komentarz członka1:
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) Utwórz nowego członka i powiąż go z istniejącym komentarzem2:
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) Utwórz relację między istniejącym członkiem2 a komentarzem3:
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) Usuń tę relację ponownie:
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) Usuń członka 1 i wszystkie jego powiązania z komentarzami:
var member1 = context.Members.Where(m => m.FirstName == "Pete")
.SingleOrDefault();
if (member1 != null)
{
context.Members.Remove(member1);
context.SaveChanges();
}
Spowoduje to usunięcie relacji w MemberComments
zbyt ponieważ jeden-do-wielu relacje między Member
i MemberComments
oraz między Comment
i MemberComments
są ustawione z kaskadowego usuwania umownie. I to jest tak dlatego, że MemberId
i CommentId
w MemberComment
są wykrywane jako obcych dla kluczowych właściwości Member
i Comment
nawigacyjnych właściwości i od właściwości FK są typu non-pustych int
wymagana jest relacja, która ostatecznie powoduje kaskadowe-delete-setup. Myślę, że ma sens w tym modelu.
OnModelCreating
. Przykład opiera się tylko na konwencjach mapowania i adnotacjach danych.MemberId
iCommentId
kolumn, a nie dodatkową trzecią kolumnęMember_CommentId
(lub coś podobnego) - co oznacza, że nie mają dokładnych nazw dopasowania między obiektami dla twoich kluczyDoskonała odpowiedź Slauma.
Po prostu opublikuję kod, aby to zrobić, korzystając z płynnego mapowania interfejsu API .
W
DbContext
klasie pochodnej możesz to zrobić:Ma taki sam efekt jak zaakceptowana odpowiedź, z innym podejściem, co nie jest ani lepsze, ani gorsze.
EDYCJA:
Zmieniłem CreatedDate z bool na DateTime.EDYCJA 2: Z powodu braku czasu umieściłem przykład z aplikacji, nad którą pracuję, aby mieć pewność, że to zadziała.
źródło
In your classes you can easily describe a many to many relationship with properties that point to each other.
pochodzi z: msdn.microsoft.com/en-us/data/hh134698.aspx . Julie Lerman nie może się mylić.Comments
właściwościMember
. Nie można tego po prostu zmienić, zmieniając nazwęHasMany
wywołania,MemberComments
ponieważMemberComment
jednostka nie ma kolekcji odwrotnejWithMany
. W rzeczywistości musisz skonfigurować dwie relacje jeden do wielu, aby uzyskać odpowiednie mapowanie.@ Esteban, podany kod jest poprawny, dziękuję, ale niekompletny, przetestowałem go. Brakuje właściwości w klasie „UserEmail”:
Wysyłam kod, który przetestowałem, jeśli ktoś jest zainteresowany. pozdrowienia
źródło
Chcę zaproponować rozwiązanie, w którym można uzyskać oba smaki konfiguracji „wiele do wielu”.
„Złap” polega na tym, że musimy utworzyć widok ukierunkowany na tabelę łączenia, ponieważ EF sprawdza, czy tabela schematu może być mapowana co najwyżej raz na
EntitySet
.Ta odpowiedź uzupełnia to, co zostało powiedziane w poprzednich odpowiedziach i nie zastępuje żadnego z tych podejść, opiera się na nich.
Model:
Konfiguracja:
Kontekst:
Od (@Saluma) Saluma za odpowiedź
To nadal działa ...
... ale teraz może być również ...
To nadal działa ...
... ale teraz może być również ...
Jeśli chcesz usunąć komentarz członka
Jeśli chcesz
Include()
dodać komentarz członkaWszystko to przypomina cukier syntaktyczny, jednak daje ci kilka korzyści, jeśli chcesz przejść przez dodatkową konfigurację. Tak czy inaczej wydaje się, że jesteś w stanie uzyskać najlepsze z obu podejść.
źródło
EntityTypeConfiguration<EntityType>
klucza i właściwości typu encji. Np.Property(x => x.MemberID).HasColumnType("int").IsRequired();
Wydaje się zbędnypublic int MemberID { get; set; }
. Czy możesz wyjaśnić moje mylące zrozumienie, proszę?TLDR; (częściowo związany z błędem edytora EF w EF6 / VS2012U5), jeśli generujesz model z DB i nie widzisz przypisanej tabeli m: m: Usuń dwie powiązane tabele -> Zapisz .edmx -> Wygeneruj / dodaj z bazy danych - > Zapisz.
Dla tych, którzy tu przybyli, zastanawiając się, jak uzyskać relację wiele do wielu z kolumnami atrybutów, aby wyświetlać się w pliku EF .edmx (ponieważ obecnie nie byłby wyświetlany i traktowany jako zestaw właściwości nawigacyjnych), ORAZ wygenerowałeś te klasy ze stołu bazy danych (lub, jak sądzę, pierwsza baza danych w MS Lingo).
Usuń 2 tabele, o których mowa (na przykład OP, Członek i Komentarz) w .edmx i dodaj je ponownie poprzez „Generuj model z bazy danych”. (tj. nie próbuj pozwolić Visual Studio na ich aktualizację - usuń, zapisz, dodaj, zapisz)
Następnie utworzy 3. tabelę zgodnie z tym, co jest tutaj sugerowane.
Jest to istotne w przypadkach, gdy najpierw dodaje się czystą relację wiele do wielu, a atrybuty są projektowane w DB później.
Nie było to od razu jasne z tego wątku / Googling. Więc po prostu go opublikuj, ponieważ jest to link nr 1 w Google, który szuka problemu, ale najpierw pochodzi od strony DB.
źródło
Jednym ze sposobów rozwiązania tego błędu jest umieszczenie
ForeignKey
atrybutu nad właściwością, którą chcesz jako klucz obcy, i dodanie właściwości nawigacji.Uwaga: W
ForeignKey
atrybucie, pomiędzy nawiasami i podwójnymi cudzysłowami, umieść nazwę klasy, o której mowa w ten sposób.źródło