Pomoc przy zapytaniu LINQ

Odpowiedz Nowy wątek
2018-12-29 19:25
0

Witam,

potrzebuję pomocy przy zapytaniu LINQ... Nie mam kompletnie pomysłu jak to ugryźć. Mam w bazie Naprawy i Serwisantów. Chcę zrobić listę serwisantów z ilością przypisanych do nich napraw.
W SQL zrobiłem coś takiego, co pokazuje mi id, username oraz ilość napraw.

SELECT u.Id, u.Username, count(r.Id) FROM Users u JOIN Repairs r ON r.TechnicianId=u.Id GROUP BY u.Id, u.Username;

W LINQ zdążyłem coś takiego zrobić:

Users.GroupJoin(Repairs, u => u.Id, r => r.TechnicianId, (u, r) => new { Users = u, Repairs = r });

I To mi zwraca użytkownika i obok listę jego napraw, a chciałbym jak powyżej.

W sumie dążę do tego, aby wrzucić wyniki do tego modelu:

public class BestTechniciansReportModel
    {
        public int Id { get; set; }
        public string Firstname { get; set; }
        public string Lastname { get; set; }
        public string Username { get; set; }
        public int NumberOfRepairs { get; set; }
    }

Czy mógłby ktoś coś podpowiedzieć z tym LINQ?

edytowany 1x, ostatnio: lukaszek016, 2018-12-29 19:25

Pozostało 580 znaków

2018-12-29 19:36
4

Można np tak:

Users.Select(user => new BestTechniciansReportModel {
    Id = user.Id,
    Firstname = user.Firstname,
    Lastname = user.Lastname,
    Username = user.Username,
    NumberOfRepairs = Repairs.Count(repair => repair.TechnicianId == user.Id),
}
Przypadkiem Includa nie trzeba? :P - WeiXiao 2018-12-29 19:40
Przy projekcji na własną klasę nie trzeba ;) - mad_penguin 2018-12-29 19:40
Zresztą, nie odwołuję się tutaj do żadnych navigation properties :) - mad_penguin 2018-12-29 19:41

Pozostało 580 znaków

2018-12-29 19:42
0

Fakt, dzięki za pomoc ;)

Ciekaw jestem jak z wydajnością, Twój kod generuje coś podobnego:

SELECT [t3].[Id], [t3].[Firstname], [t3].[Lastname], [t3].[Username], [t3].[value] AS [NumberOfRepairs]
FROM (
    SELECT [t0].[Id], [t0].[Firstname], [t0].[Lastname], [t0].[Username], (
        SELECT COUNT(*)
        FROM (
            SELECT 
                (CASE 
                    WHEN [t1].[TechnicianId] = ([t0].[Id]) THEN 1
                    WHEN NOT ([t1].[TechnicianId] = ([t0].[Id])) THEN 0
                    ELSE NULL
                 END) AS [value]
            FROM [Repairs] AS [t1]
            ) AS [t2]
        WHERE [t2].[value] = 1
        ) AS [value]
    FROM [Users] AS [t0]
    ) AS [t3]
ORDER BY [t3].[value] DESC

Spory potworek :)

Pozostało 580 znaków

2018-12-29 19:50
cw
1

Zamiast kombinować w LINQ (który jest fajny, ale tylko przy prostych zapytaniach) może prościej będzie po prostu stworzyć odpowiedni widok SQL w bazie danych i z niego pobrać dane za pomocą LINQ. Osobiście unikam tworzenia złożonych zapytań w LINQ bo są mało czytelne, wolę pobrać dane z widoku lub procedury przechowywanej poza tym zapewnia to lepszą wydajność zapytań.

edytowany 3x, ostatnio: cw, 2018-12-29 19:55

Pozostało 580 znaków

2018-12-29 20:10
0

@cw: no też pomysł mi się podoba z widokami. Ale tutaj z mojej strony pojawiłyby się kolejne wątpliwości, np. taka, że podczas braku bazy w moim projekcie uruchamia się mój inicjalizer, któy dziedziczy po CreateDatabaseIfNotExists i na początku wywołuję Seed z klasy bazowej, pytanie, czy mi nie stworzy tabeli w bazie o nazwie załóżmy BestTechnicianReportView, bo aby się do tego potem odwoływać z LINQ, to muszę to mieć w klasie DbContext jako nowe DbSet, a z tego Seed tworzy bazę, a nie wiem, czy da się oznaczyć, aby stworzył widok :) No generalnie sporo siedzenia przede mną, myślę, że interesujące, jednak ze względu na czas muszę zostać przy rozwiązaniu LINQowym.

ja jestem ze starej szkoły i tam gdzie mogę stosuję podejście database first - cw 2018-12-29 20:16
Seed tworzy bazę? W jaki sposób? Macie migracje? Jeśli tak to migracja może utworzyć widok. Potem zapytanie do widoku w czystym sql-u mapujące na klasę modelu. - jacek.placek 2018-12-29 23:52
Nie no napisałem tylko, ze metoda Seed jest wykonywana, ale faktycznie czytając to, co napisałem można odnieść wrażenie, że to ona tworzy strukturę bazy. Tak, są migracje. Ale chyba jak potem podłączam się do czystej bazy to każda migracja nie jest po kolei wykonywana? - lukaszek016 2018-12-30 00:09

Pozostało 580 znaków

2018-12-29 20:13
0
lukaszek016 napisał(a):

Spory potworek :)

To jest nic, w robocie nam kiedyś SQL Server wywalił błąd o zbyt głębokim zagnieżdżeniu, jak EF nam wygenerował 1 MB SQLa :D
A tak poważnie to wygląda to trochę słabo, ale bez sprawdzenia planu zapytania ciężko powiedzieć
UPDATE: nie mam doświadczenia z widokami, ale powinno dać się skonfigurować EF żeby wiedział, że coś jest widokiem a nie tabelą.

edytowany 1x, ostatnio: mad_penguin, 2018-12-29 20:15
Pokaż pozostałe 3 komentarze
To nie magia, po prostu EF generuje bardzo gównianego SQLa. Jakby użył normalnego ORMa, to by miał prosty i czytelny SQL na wyjściu. - somekind 2018-12-30 03:13
Być może ale jakie to ma znaczenie czy sql wygenerowany jest prosty czy nie dla człowieka? Oby był akceptowalnie wydajny. - jacek.placek 2018-12-30 13:03
Znaczenie ma takie, że łatwiej debuguje się czytelny kod. Co do wydajności - w crudzie dla warzywniaka za rogiem nie ma to znaczenia, ale przy bardziej obciążonych systemach zbędne left joiny i podzapytania zbierają swoje żniwo. Pytanie - po co używać czegoś, co ogranicza? To tak jakby zawsze jeździć na drugim biegu. - somekind 2018-12-30 16:19
Dlatego napisałem, że wydajność ma być akceptowalna. Generalnie się zgadzam chociaż są pojazdu co mają jeden bieg do przodu i świetnie spełniają swoje zadanie :) - jacek.placek 2018-12-30 20:12
Przecież jackowi wyszło, że ten SQL który się tam generuje jest jednak normalny. - WeiXiao 2018-12-30 20:15

Pozostało 580 znaków

2018-12-29 23:25
0
lukaszek016 napisał(a):

Fakt, dzięki za pomoc ;)

Ciekaw jestem jak z wydajnością, Twój kod generuje coś podobnego:

SELECT [t3].[Id], [t3].[Firstname], [t3].[Lastname], [t3].[Username], [t3].[value] AS [NumberOfRepairs]
FROM (
    SELECT [t0].[Id], [t0].[Firstname], [t0].[Lastname], [t0].[Username], (
        SELECT COUNT(*)
        FROM (
            SELECT 
                (CASE 
                    WHEN [t1].[TechnicianId] = ([t0].[Id]) THEN 1
                    WHEN NOT ([t1].[TechnicianId] = ([t0].[Id])) THEN 0
                    ELSE NULL
                 END) AS [value]
            FROM [Repairs] AS [t1]
            ) AS [t2]
        WHERE [t2].[value] = 1
        ) AS [value]
    FROM [Users] AS [t0]
    ) AS [t3]
ORDER BY [t3].[value] DESC

Spory potworek :)

Jesli potrafisz lepiej to sobie napisz zapytanie sql i odpal w ef. I tak zwróci Ci kolekcję określonych obiektów klasy modelu.

Pozostało 580 znaków

2018-12-30 00:30
0
jacek.placek napisał(a):

Jesli potrafisz lepiej to sobie napisz zapytanie sql i odpal w ef. I tak zwróci Ci kolekcję określonych obiektów klasy modelu.

Napisałem wyżej co wymyśliłem w sql

SELECT u.Id, u.Username, COUNT(r.Id) FROM Users u JOIN Repairs r ON r.TechnicianId=u.Id GROUP BY u.Id, u.Username;

Kwestia uzupełnić selecta o więcej wartości.

Nie napisałem, że wygenerowany kod przez EF jest niewydajny czy coś. Przemyślę kwestię czystego sqla.

Pozostało 580 znaków

2018-12-30 13:36
1

Ja Cię nie atakuję tylko zwracam uwagę, że w EF można spokojnie używać zapytań SQL-wych.

Czy u Ciebie user ma definiowaną navigation property Repairs?
Jeśli tak to takie coś

Users.Select(user => new BestTechniciansReportModel {
    Id = user.Id,
    Firstname = user.Firstname,
    Lastname = user.Lastname,
    Username = user.Username,
    NumberOfRepairs = user.Repairs.Count(),
}

generuje raczej standardowy dla ormów kod sql

SELECT 
    [Extent1].[Id] AS [Id], 
    [Extent1].[Name] AS [Name], 
    (SELECT 
        COUNT(1) AS [A1]
        FROM [dbo].[Repairs] AS [Extent2]
        WHERE [Extent1].[Id] = [Extent2].[UserId]) AS [C1]
    FROM [dbo].[Users] AS [Extent1]

Zresztą wersja podana przez @mad_penguin generuje w LinqPad dokładnie taki sam kod SQL. Nie wiem skąd mas ten swój. EF Core? Brak FK Repair -> User?

Mi też taki wyszedł, więc coś jest nie tak u OPa - WeiXiao 2018-12-30 20:23

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