Zapytanie linq/ef z warunkiem w tabeli many-to-many

Odpowiedz Nowy wątek
2019-02-08 10:58
0

Witam,

Mam taki mały problem: (tutaj przyklad):
Mam klasę User (identityUser), w której mam relationship many-to-many do "friends'ów" (czyli tej samej klasy).

 // tutaj zmienne typu email, id etc pominąłem
 public ICollection<UserUser> FriendWith { get; set; }
 public ICollection<UserUser> FriendOf { get; set; }

oraz klase łączącą UserUser:

public class UserUser
    {
        public virtual User User { get; set; }
        public string UserId { get; set; }
        public virtual User Friend { get; set; }
        public string FriendId { get; set; }

        public bool Confirmed { get; set; }
    }

W EF modelBuilder mam:

 modelBuilder.Entity<UserUser>()
                .HasKey(k => new {k.UserId, k.FriendId});

            modelBuilder.Entity<UserUser>()
                .HasOne(pt => pt.User)
                .WithMany(p => p.FriendWith)
                .HasForeignKey(pt => pt.UserId)
                .OnDelete(DeleteBehavior.Restrict);

            modelBuilder.Entity<UserUser>()
                .HasOne(pt => pt.Friend)
                .WithMany(t => t.FriendOf)
                .HasForeignKey(pt => pt.FriendId)
                .OnDelete(DeleteBehavior.Restrict);

Bez problemu pobieram "friendsow" itp ale zaciąłem się kiedy chciałbym podać np email któregoś usera i otrzymać z powrotem listę wszystkich userów ale żebym mógł sprwadzić jaką ma wartość Confirmed dla danego usera w stosunku do ID zalogowanego usera (w many-to-many) i ustawic w zwracanym modelu. Czy da się to zrobić w jedym zapytaniu czy muszę to jakoś rozbić?
na chwilę obecną mam:

            var users = await _userManager.Users
                .Where(u => (u.Email.Contains(request.substring) || u.UserName.Contains(request.substring)) && u.Id != request.Id)
                .Select(user=> new UserInfo()
                {
                    // tutaj tworze model z danymi ktore potrzebuje
                    confirmed = isConfirmed // tutaj potrzebuję ustawic na true/false/null
                })
                .ToListAsync(cancellationToken);

Do pobierania User'a z firiend-listy używam:

.SelectMany(e => e.FriendWith, (user, friend) =>

Z góry dziękuję za pomoc.
Zaciąłem się na tym nie wiem dlaczego...

Edit:
Czy cos w stylu:

 .Include(s => s.FriendOf)

a później przy tworzeniu modelu:

.Select(user => new UserInfo()
{
      confirmed = user.FriendOf.FirstOrDefault(e => e.FriendId == request.Id).Confirmed
})

czy takie działania są poprawne??

Edit 2:
Działać->działa ale czy tak to się robi "profesjonalnie" czy może jest jakiś lepszy sposób?
Ostatecznie mam:

 var users = await _userManager.Users
     .Include(s => s.FriendWith)
     .Where(u => (u.Email.Contains(request.substring) || u.UserName.Contains(request.substring)) && u.Id != request.Id)
       .Select(user => new UserInfo()
           {
              confirmed = user.FriendWith.FirstOrDefault(e => e.FriendId == request.Id).Confirmed
           }).ToListAsync(cancellationToken);
edytowany 5x, ostatnio: RoboCat, 2019-02-08 12:10

Pozostało 580 znaków

2019-02-08 14:48
0

Zainstaluj z nugeta Z.EntityFramework.Plus.EF6 i zobaczy przykłady z IncudeFilter
https://entityframework-plus.net/query-include-filter

Pozostało 580 znaków

2019-02-08 14:59
0

Dzieki,
Ale w opisie jest :
Entity Framework Core:
Not supported yet.
Many to Many relation:
Not supported yet

Wiec mi sie nie przyda :/ (uzywam core i many-to-many).
Takie zapytanie mi ladnie dziala, tylko czy jest to "profesjonalne" rozwiazanie?...

var users = await _userManager.Users
     .Include(s => s.FriendWith)
     .Where(u => (u.Email.Contains(request.substring) || u.UserName.Contains(request.substring)) && u.Id != request.Id)
       .Select(user => new UserInfo()
           {
              confirmed = user.FriendWith.FirstOrDefault(e => e.FriendId == request.Id).Confirmed
           }).ToListAsync(cancellationToken);

W skrocie co chce osiagnac to Lista osob ktore moge zaprosic (wyszukiwanie):
Pobrac z back-endu liste userow ktorych email/username zawieraja ciag znakow (request.substring) i ustawic Confirmed na false/true/null w zaleznosci jaka jest/lub brak wartosci w UserUser.Confirmed (nullable bool?). Na froncie mam friend-liste ktora wyswietla element friend-list-item w zaleznosci od tego Confirmed. Jak jest null -> to wyswietla z buttonem Invite, jak jest true to nie dodaje do listy w ogóle (juz jest znajomym), a jak false to wyswietla z buttonem (cancel invitation request i/albo accept invitation -> w zaleznosci od requestSendByMe dolaczany do modelu). (pozniej bedzie dodane - pagination)

W miejscu:

.Where(u => (u.Email.Contains(request.substring) || u.UserName.Contains(request.substring)) && u.Id != request.Id)

moglbym dac u.Id != request.Id na poczatek ifa zeby nie musiec sprwadzac reszty

edytowany 1x, ostatnio: RoboCat, 2019-02-08 15:01
A to trzeba było napisać, że to Core i Many2Many. To nie wiem :) - jacek.placek 2019-02-08 15:24
W sumie racja ze moglem od razu napisac :) Kurcze dziala bo dziala -> ale czy jest to dobrze napisane, zawsze bylem "du..a" z bardziej zlozonych query... - RoboCat 2019-02-08 15:25

Pozostało 580 znaków

2019-02-08 17:05
0

Tu nie będzie czasem n+ przez to, co jest w Select? Najlepiej odpal profielr i sprawdź.
Jeśli nie ma n+, to chyba i tak lepiej nie będzie. W końcu to EF.


"HUMAN BEINGS MAKE LIFE SO INTERESTING. DO YOU KNOW, THAT IN A UNIVERSE SO FULL OF WONDERS, THEY HAVE MANAGED TO INVENT BOREDOM."

Pozostało 580 znaków

2019-02-08 19:19
0

No wlasnie o to co jest w select sie martwie najbardziej.
Pozniej jak bede przy kompie to zobacze profilerem.
A moze jakas alternatywa? Moze normalnie sql'em? bez EF'a ?

Do tego dochodzi jeszcze zapytanie czy user jest tym ktory dal invite... nie wiem czy nie za duzo tego na jeden "call".

edytowany 2x, ostatnio: RoboCat, 2019-02-08 19:20

Pozostało 580 znaków

2019-02-09 20:09
0

Moze mi ktos pomoze,
@somekind
Moglbys mnie naprowadzic jak to najlepiej sprwadzic?

Uzylem Jetbrains dotTrace, jednak nie widze zadnych sql-calls, nie wiem dlaczego nie moge ich pokazac. (powinna byc mozliwosc wybrania jezeli jakies byly, ale pewnie robie cos zle)
Na focie nizej widac ze cala metoda Handle trwa 1.7ms z czego Linq to 0.4ms. (ale to przy kilku userach). 50% to system code.
Musze doczytac jak EF przerabia takie include a potem select na nim, moze nie robi kolejnego query do bazy danych?
trace.jpg

Pozostało 580 znaków

2019-02-09 20:13
0

Chodziło mi o SQL profiler, żeby zobaczyć, czy czasem nie ma n+1 zapytań do bazy.


"HUMAN BEINGS MAKE LIFE SO INTERESTING. DO YOU KNOW, THAT IN A UNIVERSE SO FULL OF WONDERS, THEY HAVE MANAGED TO INVENT BOREDOM."
ok, zaraz zobacze. Wiem ze jak dodam np .Include z jakas wartoscia ktorej pozniej nie uzyje to EF daje warrning ze nie zostanie "zincludowane" bo nie ma do tego odwolania wiec moze on nie robi dodatkowych query przy odwolywaniu sie do elementu z include (musze o tym doczytac). - RoboCat 2019-02-09 20:16

Pozostało 580 znaków

2019-02-09 22:40
0

@somekind:
Moglbys spojzec i przeanalizowac? (naprawde sql'e itp to nie moja mocna strona).
Mi to wyglada na 1 zapytanie, ale ja to glupi jestem wiec lepiej zapytac kogos madrzejszego.

trace.jpg

Czy o to chodzilo?
Z gory dzieki.

Pozostało 580 znaków

2019-02-11 01:23
0

Owszem, to jest jedno zapytanie. Jeśli jesteś pewien, że to wszystkie zapytania, które idą po sieci podczas wykonywania tego kodu, to jest dobrze.


"HUMAN BEINGS MAKE LIFE SO INTERESTING. DO YOU KNOW, THAT IN A UNIVERSE SO FULL OF WONDERS, THEY HAVE MANAGED TO INVENT BOREDOM."

Pozostało 580 znaków

2019-02-11 09:11
0

Dzieki za pomoc.
Tak to jest tylko jedno query, robilem zapytanie postmanem do API i tylko to wyskoczylo.

No to moge kontynuowac.
Widze ze dobrze kombinowalem, jeszcze az tak glupi nie jestem :P

Pozostało 580 znaków

2019-02-11 23:00
0

Napotkalem kolejna przeszkode.
Mianowicie w ktoryms momencie potrzebuje pobrac FriendsWith i FriendsOf jednego usera, przeiterowac po obydwuch i zwrocic odpowiedni model (ten sam dla obu, jedynie jedna zmienna ma inna wartosc true/false).
Zaczalem z takim czyms:

       var me = await _userManager.Users
                .Include(fw => fw.FriendWith.Select(ffw => ffw.Confirmed == false))
                .Include(fo => fo.FriendOf.Select(ffo => ffo.Confirmed == false))

Teraz mam usera razem z dwoma listami.
Czy istnieje mozliwosc zrobienia Select/SelectMany na obu? Bo w momencie zrobienia np

.SelectMany(u => u.FriendWith, (user, useruser) => new { object }

to kolejny .SelectMany zadziala na wyniku poprzedniego, wiec nie moge zrobic .SelectMany na u.FriendOf.
Chyba ze jest jakis trick ktorego po prostu nie znam.

Chwilowym rozwiazaniem (zeby chociaz dzialalo) jest foreach na kazdej liscie z osobna i dodania do jednej wspolnej kolekcji, ale to wydaje mi sie take "amatorskie" rozwiazanie.

Z gory dzieki.

Nie można ich złączyć w jedną? :P - WeiXiao 2019-02-11 23:44
Znaczy sie tych dwoch list? Mozna tylko jak bede wiedzial ktory item jest z ktorej listy... w sumie bede mial wtedy liste obiektow useruser ale to oznacza ify czy userId to moj id... - RoboCat 2019-02-12 00:12
zbierasz swoich friendsów oraz tych, którzy mają ciebie w friendsach (w ogóle to jakieś dziwne) - skąd twój id? :P - WeiXiao 2019-02-12 00:17
Mam relation many-to-many na tej samej klasie users-to-users - dlatego dwie listy. FrientWith - to user do ktorych wyslalem invite a FriendOf ci co wyslali mi. Przy pobieraniu friendsow potrzebuje rozroznic te dwie rzeczy i kiedy confirmed jest jako false - tzn pobieram tylko userow z "zaproszeniami". id - z query ([from body]); Dzisiaj na swiezo do tego usiade, moze wczoraj wieczorem juz mi mozg nie pracowal jak nalezy. - RoboCat 2019-02-12 08:58

Pozostało 580 znaków

Odpowiedz
Liczba odpowiedzi na stronę

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0