Avatar billede pnr Nybegynder
28. oktober 2010 - 14:09 Der er 20 kommentarer og
1 løsning

Segmentere efter type i Linq

Jeg bruger Entity framwork og skal lave en query mod databasen.
Min struktur er følgende:
Jeg har en bruger klasse og en medarbejder klasse. Medarbejder klassen nedarver fra bruger klassen.

Jeg har et query der retunerer alle medarbejder:

var get = from emp  in context.Brugere.OfType<Medarbejder>() where emp.Id == id select emp;

Nu vil jeg gerne lave en query hvor jeg få alle bruger som ikke er medarbejder, men hvordan gør jeg det?

På forhånd tak for hjælpen!
Avatar billede FarmerHE Nybegynder
28. oktober 2010 - 14:15 #1
Hvad kendetegner dem?

Hvis de har null eller et bestemt tal som emp.Id så brug det på højre side af samme statement og så burde du få dem der ikke har et id.
Avatar billede Syska Mester
28. oktober 2010 - 14:26 #2
var list = context.Brugere.Except(get);

// "get" er hvad du har kaldt din liste med "Medarbejdere" i dit eget eksempel.

mvh
Avatar billede pnr Nybegynder
28. oktober 2010 - 14:37 #3
Buzzzz: Mange tak for hurtig kommentar, smid et svar så er der point :-)
Avatar billede Syska Mester
28. oktober 2010 - 14:48 #4
Virker det ?

Men du skal nok være opmærksom på at det nok lagt fra er den mest optimale måde at gøre det på ... mht performance.

Men har du ikke som FarmerHE er inde på, noget der indikere om det er en bruger eller medarbejder ?

og svar

mvh
Avatar billede pnr Nybegynder
28. oktober 2010 - 15:01 #5
Ja det virker som det skal, men du har nok ret i at det rent performance mæssigt ikke er så pænt :-/

Jeg sidder og leger med EF og bruger model-first modellen, så jeg lader EF om at oprette mine tabeller.

Den har så oprettet en bruger og en medarbejder tabel, hvor der i medarbejder tabellen er en reference til brugeren. men i min query har jeg jo kun fat i brugerne og på dem er der jo ingen forskel...
Avatar billede janus_007 Nybegynder
28. oktober 2010 - 19:49 #6
Jeg forstår ikke hvorfor der ikke er nogen forskel i Brugertabellen?

Hvis du har en tabel med medarbejdere og brugere, så er det jo bare at kigge om referencen findes til medarbejderen.


var medarbejdere = from b in context.Brugere
where b.MedarbejderId != null
select b;
Avatar billede pnr Nybegynder
29. oktober 2010 - 08:19 #7
Hej janus_007

tak for din kommentar! Jeg arbejder som nævnt med Entity Framework 4.0 (tester det :-)), og har lavet en nedarvning fra brugere til medarbejdere. Når jeg skal lave min query, kan jeg kun få alle brugere context.Brugere (inkl. medarbejdere) eller jeg kan vælge alle medarbejdere context.Brugere.OfType(<Medarbejdere>). Derfor kan jeg ikke segmenter på fremmedenøglen i medarbejder, når jeg skal bruge alle dem der kun er brugere.

var get = from bruger  in context.Brugere where select bruger;

Ovenstående kender ikke til de attributter der er på medarbejder!

(Kan man iøvrigt ikke forhøje point på et spørgsmål mere her på exp?)
Avatar billede Syska Mester
29. oktober 2010 - 08:47 #8
#Janus
Ville den reference ikke være den anden vej? ellers kender "bruger" jo til medarbejder, som vel reelet set ikke er meningen.

Det må vel være Medarbejder som har et BrugerId den er koblet til. og deraf kender Bruger ikke noget til medarbejder.

mvh
Avatar billede janus_007 Nybegynder
29. oktober 2010 - 16:07 #9
Hej buzzz, tjaa nu kommer det jo an på hvordan man ville designe det. Jeg ville da nok bare have en id i Brugertabellen som relation til  MedarbejderId ellers kunne du have flere medarbejdere på samme bruger :) Det kan naturligvis ordnes med constraints, men hvorfor ikke holde det så naturligt som muligt. Der er for og imod begge ting :)

Anyway....

Hvis relationen går den anden vej så...

var brugereSomIkkeErMedarbejde = from m in Brugeres
where !m.BrugerMedarbejderes.Select (bm => bm.BrugerId).Contains(m.Id)
select m;

Jeg ville ihvertfald være rimelig sikker på hvad: context.Brugere.OfType(<Medarbejdere>). bliver eksekveret som på basen :)

Well men uden at røbe for meget så ved jeg at den jeg har skrevet giver en full table scan og den du selv har faktisk gør det samme bare i C#, dvs. den vil ikke performe lige så godt som når det klares på basen.

Nu hvor du eksperimenterer og jeg stadig ikke har prøvet Model First, så vil jeg da mene, udfra denne her lille øvelse at afhængig af hvor store datamængder man arbejder med bør man måske selv lave sit design for at undgå sådanne scenarier her :)
Avatar billede janus_007 Nybegynder
29. oktober 2010 - 16:14 #10
Så også lige denne:

"var get = from bruger  in context.Brugere where select bruger;

Ovenstående kender ikke til de attributter der er på medarbejder!"

Jo så skal du bare et hak længere ind...
var get = from bruger  in context.Brugere
select bruger.BrugerMedarbejderes.Where(x => x.Field.... == "uu")

Eller:
from bruger  in Brugeres
select (from bm in bruger.BrugerMedarbejderes
where bm.Field == "uu"
select bm
)
Avatar billede Syska Mester
29. oktober 2010 - 17:52 #11
#janus
Nok bare mig, men jeg ville aldrig lave bruger have et Id til medarbejde ... hvad nu hvis jeg lavede endnu en table med Alkoholikere ... ( kunne ikke komme på bedre ting efter fredags øl )

Men any ways ... kært barn har mange navne. Når man ikke kender det specifikke problem, er det svært at komme med måder det burde laves på.

Men ville måske mene at Except brugte Keys til at fjerne dem, så den burde ikke lave en full table scan så ... men hvis den laver en full table scan, så er der ingen tvivl om at det er noget sovs hvis der er maneg millioner rows. Alt under 10k kan jo nemt være i memory.

mvh
Avatar billede janus_007 Nybegynder
29. oktober 2010 - 18:01 #12
Jeg tænker at Excepten har loadet alle medarbejdere ind og først herefter tager de brugere som ikke er der.

Samtidigt er jeg ikke helt sikker på hvordan den vil gøre hvis :
var get = from emp  in context.Brugere.OfType<Medarbejder>() where emp.Id == id select emp;

...er IQueryable, men i bedste fald vil den projekteres ned i Sql og så bliver det alligevel til en "not in" og derved table scan.
Avatar billede Syska Mester
29. oktober 2010 - 18:19 #13
Givetvis ... derfor også  min kommentar i starten om det performance mæssigt nok ikke er helt optimalt. :-)

Men det er jo bare at smide en Profiler på eller bruge LinqPad(tror jeg det hedder) så kan man jo nemt finde ud af det.

Men da jeg ikke har hans data, og ikke gider opstille sammen eksempel kom jeg bare med min kommentar omkring det i starten at det nok ikke var så smart.

Men altid noget vi tænker i samme retning :-)

mvh
Avatar billede pnr Nybegynder
30. oktober 2010 - 09:04 #14
Hej alle, det er da fantatisk hvordan det her spørgsmål udvikler sig, mange tak for det!!

Janus_007, jeg kan ikke helt forstå din kommentar #10, det synes jeg ikke at jeg kan få lov til?

Jeg har prøvet at køre en profiler på "buzzzz's" første forslag, og den laver følgende SQL:

SELECT
[Except1].[C1] AS [C1],
[Except1].[Id] AS [C2],
[Except1].[Username] AS [C3],
[Except1].[Password] AS [C4],
[Except1].[Name] AS [C5],
[Except1].[Deleted] AS [C6],
[Except1].[C2] AS [C7],
[Except1].[C3] AS [C8],
[Except1].[C4] AS [C9]
FROM  (SELECT
    CASE WHEN ( NOT (([Project1].[C1] = 1) AND ([Project1].[C1] IS NOT NULL))) THEN '0X' ELSE '0X0X' END AS [C1],
    [Extent1].[Id] AS [Id],
    [Extent1].[Username] AS [Username],
    [Extent1].[Password] AS [Password],
    [Extent1].[Name] AS [Name],
    [Extent1].[Deleted] AS [Deleted],
    CASE WHEN ( NOT (([Project1].[C1] = 1) AND ([Project1].[C1] IS NOT NULL))) THEN CAST(NULL AS varchar(1)) ELSE [Project1].[Adress] END AS [C2],
    CASE WHEN ( NOT (([Project1].[C1] = 1) AND ([Project1].[C1] IS NOT NULL))) THEN CAST(NULL AS varchar(1)) ELSE [Project1].[Zipcode] END AS [C3],
    CASE WHEN ( NOT (([Project1].[C1] = 1) AND ([Project1].[C1] IS NOT NULL))) THEN CAST(NULL AS varchar(1)) ELSE [Project1].[Employeenr] END AS [C4]
    FROM  [dbo].[Brugere] AS [Extent1]
    LEFT OUTER JOIN  (SELECT
        [Extent2].[Adress] AS [Adress],
        [Extent2].[Zipcode] AS [Zipcode],
        [Extent2].[Employeenr] AS [Employeenr],
        [Extent2].[Id] AS [Id],
        cast(1 as bit) AS [C1]
        FROM [dbo].[Brugere_Employee] AS [Extent2] ) AS [Project1] ON [Extent1].[Id] = [Project1].[Id]
    WHERE 0 = [Extent1].[Deleted]
EXCEPT
    SELECT
    '0X0X' AS [C1],
    [Extent3].[Id] AS [Id],
    [Extent4].[Username] AS [Username],
    [Extent4].[Password] AS [Password],
    [Extent4].[Name] AS [Name],
    [Extent4].[Deleted] AS [Deleted],
    [Extent3].[Adress] AS [Adress],
    [Extent3].[Zipcode] AS [Zipcode],
    [Extent3].[Employeenr] AS [Employeenr]
    FROM  [dbo].[Brugere_Employee] AS [Extent3]
    INNER JOIN [dbo].[Brugere] AS [Extent4] ON [Extent3].[Id] = [Extent4].[Id]
    WHERE 0 = [Extent4].[Deleted]) AS [Except1]
Avatar billede janus_007 Nybegynder
30. oktober 2010 - 09:43 #15
Jep dvs. at den arbejder videre på den første IQueryable.

Altså hvis man nu bare skulle skrive det i Sql, så ville man jo gøre sådan:
select * from Brugere
where BrugerId not in(select BrugerId from Medarbejdere)

Årsagen til den monstersql er at der arbejdes på entiteten Medarbejdere og ikke den direkte relation, hvordan det kan være.. hmmm, kan tænkes en fejl i EF eller måske skal den gøre sådan, hvilket jeg ikke forstår *GG*

Hvad laver denne af Sql:

var brugereSomIkkeErMedarbejde = from m in Brugeres
where !m.BrugerMedarbejderes.Select (bm => bm.BrugerId).Contains(m.Id)
select m;

Og hvad er det med #10, måske har jeg misforstået spørgsmålet. Men jeg læste det som at du ikke kunne selecte på child attributterne.
Avatar billede pnr Nybegynder
30. oktober 2010 - 10:58 #16
Hmmm jeg er godt nok lidt "grøn" i Linq :-/ Men jeg kan ikke tilgå brugerMedarbejder eller andet der ligner på "m" fra dit eksempel.

På bruger i det eksempel kan jeg kun vælge de "attributter" der er på "klassen".
Avatar billede pnr Nybegynder
04. november 2010 - 15:26 #17
Lad os lukke dem her, tusind tak for jeres hjælp!!! smid nogle svar så er der point.
Avatar billede Syska Mester
04. november 2010 - 19:17 #18
svar
Avatar billede pnr Nybegynder
09. november 2010 - 19:16 #19
Janus, et svar tak :)
Avatar billede pnr Nybegynder
19. november 2010 - 22:52 #20
Janus.
Avatar billede pnr Nybegynder
02. december 2010 - 11:57 #21
janus, et svar...
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
IT-kurser om Microsoft 365, sikkerhed, personlig vækst, udvikling, digital markedsføring, grafisk design, SAP og forretningsanalyse.

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester