Avatar billede Lasse Novice
05. oktober 2012 - 16:38 Der er 11 kommentarer

Linq 2 SQL

Hej

Jeg har foelgende db:
MM_Company - MM_Level1Codes : 1 - many
MM_Level1Codes - MM_Level2Codes : 1 - many
MM_Level2Codes - MM_Level3Codes : 1 - many
MM_Company - MM_Roles : 1 - many
MM_Roles - MM_UserRoles : 1 - many
MM_UserRoles - MM_Users : 1 - many

Jeg henter al den information ud for een MM_Company i 2 steps:

1)
from c in Dc.MM_Companies
join l1c in Dc.MM_Level1Codes on c.Id equals l1c.CompanyId
join l2c in Dc.MM_Level2Codes on l1c.Id equals l2c.Level1CodeId
join l3c in Dc.MM_Level3Codes on l2c.Id equals l3c.Level2CodeId
join r in Dc.MM_Roles on c.Id equals r.CompanyId
join ur in Dc.MM_UserRoles on r.Id equals ur.RoleId
join u in Dc.MM_Users on ur.UserId equals u.Id
where c.Id == id
select new { C = c, L1c = l1c, L2c = l2c, L3c = l3c, R = r, Ur = ur, U = u };

2)
Laeg alle unikke MM_Level1Codes ind i MM_Company
Laeg alle unikke MM_Level2Codes ind i MM_Level1Codes
Laeg alle unikke MM_Level3Codes ind i MM_Level2Codes
Laeg alle unikke MM_Roles ind i MM_Company
Laeg alle unikke MM_UserRoles ind i MM_Roles
Laeg alle unikke MM_Users ind i MM_UserRoles

2 spg:
1)
Der kan vaere meget data overhead i og med at MM_Company kommer med ud for HVER raekke uddrevet fra db. Hvordan ville du uddrive denne data? Foretraekker du at lave 7 forskellige queries, en for hver tabel, eller noget midt imellem?

2)
Step 2 kan udskiftes ved brug af LoadOptions. LoadOptions goer livet surt for TDD da man skal lave en my DataContext for hver ny LoadOptions, saa det holder jeg mig vaek fra. Findes der en anden metode til at uddrive disse data saa dataen automatisk bliver lagt ind i i de rigtige child collections?
Avatar billede janus_007 Nybegynder
05. oktober 2012 - 17:28 #1
Først....
1) Lav dine relationer i DBML'en og undgå alle dine joins.
2) TDD, mener du test driven development?
Avatar billede Lasse Novice
05. oktober 2012 - 18:28 #2
1) Jeg er ikke sikker paa at jeg forstaar, selvom jeg laver et "view", saa staar jeg stadig med samme problem stilling, eller hva?
2) TDD, yes test driven development.
Avatar billede Syska Mester
05. oktober 2012 - 18:36 #3
Hvorfor kan du ikke lave et view som kun trækker de information ud du skal bruge?

Linq kan man ting, joins er den ikke altid så godt til ... ofte ender det med dårlig performance.

Bruger du linq2sql, så ville jeg personligt gå view eller manuel sql vejen og lave en mapping class til netop dette formål.

LINQ er ofte kun vejen frem hvis du skal have alt fra A og undere med ud ... hvis du også vil group, sort, distinct på dem ... så synes jeg ofte SQL er nemmere. På den måde kan du i samme omgang også se om det performer godt nok.
Avatar billede janus_007 Nybegynder
05. oktober 2012 - 19:13 #4
Du skal som udgangspunkt ikke skrive dine joins i Linq, det er Linq To Sql der klarer den fornemme opgave på fineste måde, oftest smartere end man måske selv kan komme på :)

Jeg bruger stort set aldrig TSQL (og jeg har ellers arbejdet med det siden 1998) til C#-udvikling, det er noget andet hvis jeg skal lave diverse jobs som skal afvikles på SQL-serveren, SPROCS oa. men til udvikling der bruger jeg Linq To... i 99.9%.

I TDD skal du jo slet ikke bruge datacontexten, men lave mockobjekterne selv med data. Det er nemt at lave entitysets:


public static EntitySet<T> ToEntitySet<T> (this IEnumerable<T> source) where T : class
{
    var es = new EntitySet<T> ();
    es.AddRange (source);
    return es;
}

som du skal bruge som childs til dine mockede entiteter :)

Jeg forstår godt dit spørgsmål 1, men du kunne sig noget alá:


var result = from c in Dc.MM_Companies.Select(x => new{SomeField = x.SomeField, Level1 = new{x.MM_Level1Codes, Level2 = x.MM_Level1Codes.MM_Level2Codes}});


osv.

Nu er det hele skrevet lidt hurtigt men jeg håber du kan så modellen, ellers skriv igen :)
Avatar billede Syska Mester
05. oktober 2012 - 19:50 #5
#janus_007

Ja, til simple ting laver den samme SQL som man selv ville gøre, men at sige den laver fin SQL er vist lige i overkanten. Ofte oplever jeg det kan skrives pænere, men ofte er det ikke hurtigere. Man skal bare huske at perf teste sin app for at sikre sig den ikke laver mongo DUM sql for utroligt simple ting.

linq2sql er død og laver ikke optimal sql efter min mening, EF og NHibernate gør et meget bedre arbejde.

Men alt kommer an på hvad man laver af mærkelige ting i LINQ.

Mange joins, left outer join, distinct, where clause på sine join values. Det er steder jeg altid er opmærksom på om ens LINQ provider gør det ordenligt.
Avatar billede Lasse Novice
05. oktober 2012 - 20:00 #6
@buzzzz

>>Hvorfor kan du ikke lave et view som kun trækker de information ud du skal bruge?

Min linq2sql loesning er vist det der tilsvarer et view. Problemet er at min data er hierarkisk, paa klient siden vil jeg gerne have klasserne saaledes MM_Company har en stavl MM_Level1Codes, hver af disse har en stavl MM_Level2Codes, hver af disse har en stable MM_Level3Codes osv.
Naar man traekker det ud tabulaert kommer der en MASSE overhead ud, mit 1. spg lyder i retning af, hvad goer I normalt i disse omstaendigheder, lever I med overheadet (det kan jo blive kolossalt hvis der er mange levels), eller deler i sql saetningen op i flere?

@Janus_007
Jeg bruger datacontext da jeg laver database TDD fra C# (Jeg bryder mig ikke om at lave tests inde i T-SQL da det er svaert at laese bagefter).
>>var result = from c in Dc.MM_Companies.Select(x => new{SomeField = x.SomeField, Level1 = new{x.MM_Level1Codes, Level2 = x.MM_Level1Codes.MM_Level2Codes}});
Jeg forstaar ikke helt hvordan dette loeser mit problem med at laegge dataen ud hierarkisk paa klient siden, kunne du uddybe en lille smule, tak.
Avatar billede janus_007 Nybegynder
05. oktober 2012 - 20:27 #7
@buzzzz, jeg er til dels enig. Når SQL'en bliver avanceret kan Linq To.. godt blive lidt mærkelig, men så er det måske datamodellen der skal ændres istedet. Som tiden er gået er jeg blevet rimelig fleksibel mht. datamodellering, Boyce Codd, NF'ere osv. For mig handler det i langt de fleste tilfælde om at lave modellen simpel og brugbar end kompliceret og smart, hårfin balance :) og meget svær at beskrive. Jeg er også enig med dig i at man ofte kan skrive en langt pænere SQL selv, men hvorfor skulle man dog spilde tid på det?

Jeg kunne forøvrigt aldrig drømme om at skrive avanceret Linq To.. uden at have en Sql-profiler kørende ved siden af :) det tror jeg har sparet mig for mange uheldige oplevelser.

@gooky, ved at lave det hierarkisk i en anonym type kan du databinde på det ret nemt. Jeg formoder du vil vise dig data i en repeater? Repeaters understøtter objektgrafer.

Og jep, du har helt ret.. jo flere niveauer du har medfører performancenedgang, afhængig af datamængder oa. kunne du måske overveje en "load on demand" istedet? Men hvis alt skal kunne ses fra starten, altså ikke noget fold ind/ fold ud, jamen så.. det er svært, jeg ville nok forsøge mig lidt frem. Arbejd videre med fuld dataload og tjekke performance, måske spiller det :)
Avatar billede Lasse Novice
05. oktober 2012 - 21:50 #8
@janus_007
Dataen bliver hentet via webservice kald som desvaerre ikke spiller sammen med "Load on demand"/"anonyme typer".

Jeg vil gerne bruge de samme klasser som skabes via linq2sql samtidig med mindst mulig kode (derfor spg 2). I sidste ende har jeg det loest, mit spg er mere mod hvad goer mine arbejdskollegaer... 

Min loesning ser saaledes ud. Det virker som meget kode, men det er det bedste jeg kan finde paa, hvad mener i?

var list = from c in Db.Dc.MM_Companies
  join l1c in Db.Dc.MM_Level1Codes on c.Id equals l1c.CompanyId
  join l2c in Db.Dc.MM_Level2Codes on l1c.Id equals l2c.Level1CodeId
  join l3c in Db.Dc.MM_Level3Codes on l2c.Id equals l3c.Level2CodeId
  where c.Id == id
  select new { Company = c, Level1Code = l1c, Level2Code = l2c, Level3Code = l3c };
                   
Dictionary<int, MM_Company> companyMap = new Dictionary<int, MM_Company>();
Dictionary<int, MM_Level1Code> level1CodeMap = new Dictionary<int, MM_Level1Code>();
Dictionary<int, MM_Level2Code> level2CodeMap = new Dictionary<int, MM_Level2Code>();

foreach (var e in list)
{
  if (!companyMap.ContainsKey(e.Company.Id))
  {
    companyMap.Add(e.Company.Id, e.Company);
  }

  MM_Company company = companyMap[e.Company.Id];

  if (!level1CodeMap.ContainsKey(e.Level1Code.Id))
  {
    level1CodeMap.Add(e.Level1Code.Id, e.Level1Code);
                           
    company.MM_Level1Codes.Add(e.Level1Code);
  }

  MM_Level1Code level1Code = level1CodeMap[e.Level1Code.Id];

  if (!level2CodeMap.ContainsKey(e.Level2Code.Id))
  {
    level2CodeMap.Add(e.Level2Code.Id, e.Level2Code);

    level1Code.MM_Level2Codes.Add(e.Level2Code);
  }

  MM_Level2Code level2Code = level2CodeMap[e.Level2Code.Id];
 
  level2Code.MM_Level3Codes.Add(e.Level3Code);
}

var list2 = from c in Db.Dc.MM_Companies
  join r in Db.Dc.MM_Roles on c.Id equals r.CompanyId
  join ur in Db.Dc.MM_UserRoles on r.Id equals ur.RoleId
  join u in Db.Dc.MM_Users on ur.UserId equals u.Id
  where c.Id == id
  select new { Company = c, Role = r, UserRole = ur, User = u };

Dictionary<int, MM_Role> roleMap = new Dictionary<int, MM_Role>();

foreach (var e in list2)
{
  if (!companyMap.ContainsKey(e.Company.Id))
  {
    companyMap.Add(e.Company.Id, e.Company);
  }

  MM_Company company = companyMap[e.Company.Id];

  if (!roleMap.ContainsKey(e.Role.Id))
  {
    roleMap.Add(e.Role.Id, e.Role);

    company.MM_Roles.Add(e.Role);
  }

  MM_Role role = roleMap[e.Role.Id];

  role.MM_UserRoles.Add(e.UserRole);

  e.UserRole.MM_User = e.User;
}
Avatar billede Syska Mester
05. oktober 2012 - 22:30 #9
overhead betyder jo bare der er mere data end nødvendig, men lad være med at lave premature optimazation, hvis der reelt set ikke er et problem.

Hvad siger SQL Profiler eller SSMS om din query der bliver udført? Er den tung? Er den langsom? Hvor meget data bliver rent faktisk sendt tilbage? 100MB, 10MB, 10kb?

Jeg har selv tidligere prøvet at lave meget optimering på ting som ikke var et problem, men bare gjorde det hele mere kompelks.

SQL er tabulær data, end of story. Hvis du vil udover det, så kan du kigge på nosql databaser, men det lader ikke rigtig til at det løser dit problem.
Avatar billede Lasse Novice
05. oktober 2012 - 23:36 #10
@buzzzz

Ja, du har nok ret i premature optimazation; ikke noedvendigt. Jeg har ikke kigget paa data stoerrelse, jeg spurgte af ren nysgerrighed og gav et fiktivt eksempel. Som jeg forstaar det saa tag alt ud af db i et huk, gaar det langsomt, saa analize det, ellers fortsaet.

>>SQL er tabulær data, end of story

Det er korrekt, men det jeg er interesseret i er hvordan man paa klient siden hurtigt(faa linier kode) kan transformere den tabulaere om til en trae struktur. Det kan super nemt goeres ved LoadOptions i datacontext... men det vil jeg som sagt helst ikke bruge...
Avatar billede Syska Mester
06. oktober 2012 - 12:35 #11
Du skal bruge "GroupBy" hvis du giver det hele ud via et custom view ... subsequent queries vil ofte gøre det hele langsommere selvom man måske på den her måde får mange dubletter fra en SQL server.

Det er sådan EF, Linq2sql, NH og andre ORMs alligevel gør det. Man ser det bare ikke.

Manipulering af data på klienten er hurtig, selvom der kan være 100k rækker.

Test perf ... Men nu har du ikke nævnt noget om antal rækker endnu.
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

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