28. oktober 2010 - 14:09Der 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?
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...
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?)
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 :)
#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.
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.
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.
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]
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.
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.