Monthly Archives: August 2011
Korištenje LEFT i RIGHT JOIN pomoću LINQ
LINQ je, slobodno možemo reći, uveliko promijenio način programiranja u .NET programskim jezicima na način da je nešto što je prije LINQ-a izgledalo jako komplikovano i vrlo zahtjevno danas sa LINQom jednostavno, lagano i „vrlo prosto“.
O LINQ-u sam najviše napisao blog postova. Skoro da nema posta ovdje koji se nekim dijelom ne dotiče LINQa. Ali, s LINQom uvijek se nešto otkrije i pokuša primjeniti u stvarnom primjeru, a inspiraciju nađemo ili u standradnom SQL jeziku ili je inspiracija pronađena u nekom od postojećih algoritama.
Danas ćemo vidjeti kako se preko LINQa implementira LEFT i RIGHT JOIN, koji predstavljaju osnovne operatore SQL jezika. Šta je JOIN odnosno LEFT i RIGHT JOIN možete pogledati na ovom linku , a jednostavno možemo reći da su to osnovni operatoru spajanja dva ili više skupova podataka.
Obični JOIN vrlo lako možemo implementirati preko LINQ. Npr. pretpostavimo da imamo dva tipa podataka: Vehicle tip i VehicleType tip podataka. Pretpostavimo i to da svaki Vehicle tip sadrži osobinu koja nam govori o kakvom VehicleType tipu je riječ. Prevedeno na domaći jezik možemo kazati da svako Vozilo (eng. Vehicle) pripada nekom tipu vozila (eng. VehicleType). Neka imamo sljedeću deklaraciju tipova podataka:
class VehicleGroup
{
public int? ID;
public string Name;
}
class VehicleType
{
public int? ID;
public string Name;
public int? vehiclegroupID;
}
class Vehicle
{
public int? ID;
public string Name;
public int? vehicletypeID;
}
Svako vozilo (Vehicle) pripada tipu vozila, a tipovi vozila pripadaju određenoj grupi vozila (VehicleGroup). Kada bi željeli da izlistamo sva vozila sa pripadajućem tipom i grupom, LINQ izraz bi izgedao kao na sljedeći način:
//List all vehicles with coresponded types and groups without LEFT JOIN
var q = from v in vehicles
from t in types
where v.vehicletypeID == t.ID
from g in groups
where t.vehiclegroupID == g.ID
select new
{
VehicleID = v.ID,
VehicleName = v.Name,
VehicleTypeName = t.Name,
VehicleGroupName = g.Name
};
Ovdje se može primjetiti da je operator JOIN u LINQ implementiran preko WHERE operatora. Mana ovog izraza jesta ta da u koliko ne postoji Vehicle objekat nekog specifičnog VehicleType tipa odnosno grupe, dotični tip i i grupa se neće pojaviti u rezultatu.
U koliko želimo da izlistamo sve grupe i tipove bez obzira da li imamo Vehicle tip definisan, to nećemo moći gornjom implementacijom. Ista situacija se dešava i u relacijskim bazama podataka pa je nužno koristiti LEFT ili RIGHT JOIN.
Da bi prikazali sve grupe i tipove bez obzira da li za dotične postoji Vehicle tip implementacija LINQa data je na sljedećem listingu:
//List all vehicles with coresponded types and groups with RIGHT JOIN,
//If we want LEFT JOIN from below query order of the collections will be as in the previous query
var q = from grp in groups
from typ in types
.Where(t=>t.vehiclegroupID==grp.ID)
.DefaultIfEmpty()//this is important method for RIGHT JOIN
from veh in vehicles
.Where(v=>v.vehicletypeID == typ.ID)
.DefaultIfEmpty()//this is important method for RIGHT JOIN
select new
{
VehicleID = veh==null?0: veh.ID,
VehicleName = veh == null ? "unknown" : veh.Name,
VehicleTypeName = typ==null? "unknown" : typ.Name,
VehicleGroupName = grp==null? "unknown":grp.Name
};
Vidimo da je implementacija RIGHT JOIN slična prethodnoj implementaciji. Razlika se sastoji u poretku kolekcija, gdje ovdje idemo od najvišeg tipa po hijearhiji do najnižeg. Druga stvar koju smo ovdje koristili jeste metoda DefaultIfEmpty() koja nam sprečava da LINQ upit “pukne” kada se u procesuiranju dođe do null vrijednosti. Na kraju pravimo novi tip u koji unosimo vrijednosti iz svih kolekcija. Međutim pošto se ovdje može desiti da Vehicle ne postoji za određeni tip ili da tip i vehicle ne postoji za određenu grupu, prilikom inicijalizacije kolekcije sa rezultatom provjeravamo da li je dotični property null. Na ovaj način se također sprečavamo da nam LINQ izraz ne “pukne” u toku procesuiranja. LEFT JOIN se jednostavno implementira na način da ovom upitu zamjenimo poredak kolekcija, odnosno da poredak bude isti kao u prvom upitu, a ostalo ostaje isto.
Na kraju napravimo jednostavni demo koji će nam demonstrirati prethodno rečeno. Implementirajno prezentirane kolekcije vehicle, types i group sa podacima, ali na taj način da imamo jedan VehicleType koji neće sadržavati ni jedan Vehicle.
private static void InitData(out List groups,out List types,out List vehicles)
{
groups = new List()
{
new VehicleGroup()
{
ID=0,
Name="Unknown"
},
new VehicleGroup()
{
ID=1,
Name= "Group1"
},
new VehicleGroup()
{
ID=2,
Name= "Group2"
}
};
types = new List()
{
new VehicleType()
{
ID=0,
Name="Unknown",
vehiclegroupID=0
},
new VehicleType()
{
ID=1,
Name= "Type1",
vehiclegroupID=1
},
new VehicleType()
{
ID=2,
Name= "Type2",
vehiclegroupID=1
},
new VehicleType()
{
ID=3,
Name= "Type3",
vehiclegroupID=2
},
new VehicleType()
{
ID=4,
Name= "Type4",
vehiclegroupID=1
}
};
vehicles = new List()
{
new Vehicle()
{
ID=0,
Name="Unknown",
vehicletypeID=0
},
new Vehicle()
{
ID=1,
Name= "Vehicle1",
vehicletypeID=1
},
new Vehicle()
{
ID=2,
Name= "Vehicle2",
vehicletypeID=2
},
new Vehicle()
{
ID=3,
Name= "Vehicle3",
vehicletypeID=3
},
new Vehicle()
{
ID=4,
Name= "Vehicle4",
vehicletypeID=1
},
new Vehicle()
{
ID=5,
Name= "Vehicle5",
vehicletypeID=2
},
new Vehicle()
{
ID=6,
Name= "Vehicle6",
vehicletypeID=2
},
new Vehicle()
{
ID=7,
Name= "Vehicle7",
vehicletypeID=2
},
new Vehicle()
{
ID=8,
Name= "Vehicle8",
vehicletypeID=2
},
new Vehicle()
{
ID=9,
Name= "Vehicle9",
vehicletypeID=3
},
new Vehicle()
{
ID=10,
Name= "Vehicle10",
vehicletypeID=3
}
};
}
Sada implementirajmo obični i RIGHT JOIN a rezultat pokretanja demo aplikacije vidimo na sljedećoj slici:
Sa slike se može vidjeti da u prvom primjeru imamo 11 stavki, a u drugoj 12. 12-ta stavka je upravo ona stavka koja nije prikazana u prvom slučaju, odnosno nije prikazana stavka sa VehicleType.Name=”Type4″.
Cjelokupan demo može se preuzeti sa ovog linka.









