LightSwitch – Rad sa upitima

U prethodnom blog postu na temu Visual Studio LightSwitch su pokazane određene napredne stvari vezane za dizajn ekrana.

Ostale teme u seriji za LightSwitch:

Upiti (Queries) se mogu koristiti na više načina u okviru LightSwitch-a:

  • za sortiranje podataka
  • za filtriranje podataka i kreiranje ekrana na osnovu upita
  • za kreiranje upita u kodu (PreprocessQuery)
  • za kreiranje upita na osnovu nekog drugog upita
  • za kreiranje upita koji vraća samo jednu vrijednost

Cijeli Solution ovog blog post-a možete preuzeti ovdje (LS_QueryDemo 2,1 MB SkyDrive).

Uopšteno o upitima

Operacije sa upitima se koriste za dobijanje podataka iz entiteta sa mogućnošću filtriranja i sortiranja. Upiti mogu imati parametre i mogu vratiti jedan ili više rezultata. Za svaki od entiteta, LightSwitch obezbjeđuje “All” i “Single” upit. Tako na primjer, za tabelu Customer, tačnije za Entity SetCustomers, LightSwitch generiše Customers_All kako bi vratio sve pojave kupaca, i Customers_Single da vrati samo jednog kupca a na osnovu proslijeđenog ključa. Za entitet Customer, metode koje su dostupne u sekciji Write Code:

Query_All_Single

Upiti u LightSwitch su kompozitni, tačnije kreiranje novih upita može biti zasnovano na korišćenju postojećih upita. Na primjer, ako smo kreirali upit SortedCustomers koji sortira kupce po nazivu, možemo kreirati novi upit CustomersByCity (string city) koji je zasnovan na SortedCustomers. Pored toga  je moguće koristi LINQ upite na klijentskoj strani biznis logike za “nasleđivanje” upita. Ono što se zaista dešava je da LightSwitch klijent koristi WCF RIA Services za kreiranje zahtjeva za upit i vrši serijalizaciju u DomainService na strani logičkog nivoa (LightSwitch je zasnovan na troslojnog arhitekturi: prezentacioni, logički i nivo podataka).

Operacije sa upitima mogu sadržavati parametre koji se koriste u okviru filtera kao where klauzula. LightSwitch podržava i opcione parametre (optional parameters). Ako parametar ima null vrijednost, LightSwitch u toku izvršavanja programa ne uzima u obzir parametar koji nije proslijeđen, tj. ne uzima se u obzir where klauzula. Opcioni parametri mogu biti korisni kod ekrana gdje korisnik može a ne mora da unese filter. Na primjer, ukoliko kreiramo upit za pretraživanje Customer-a i želimo da kao rezultat dobijemo sve pojave iz istog entiteta onda ne unesemo parametar active. Ali ako je parametar “active” poznat, onda naš upit vraća aktivne ili neaktivne kupce.  Filter se definiše u okviru dizajnera za upite:

Filter

U pozadini, LightSwitch generiše where klauzulu kao:

where (!active.HasValue || Customer.IsActive == active.Value)

pri čemu je u sekciji Parameter definisan parameter sa nazivom active:

Parameter

Dizajner upita se pokreće preko eniteta u Solution Explorer-u (desni klik na entitet i stavka Add Query):

AddQuery

Tokom izvršavanja upita, operacije koje čine upit se prosljeđuju kroz tok upita (“query pipeline”) u toku čijeg procesa je moguće koristiti custom server kod. Taj tok je sačinjen iz sledećih koraka:

1. Pre-processing

  • CanExecute – provjera da li se može prozvati upit ili ne
  • Executing – izvršava se prije procesiranja upita
  • Pre-process izraz za upit – osnovni upit

2. Execution – prosljeđivanje upita ka provajderu podataka na izvršavanje (data provider se nalazi na logičkom nivou arhitekture. LightSwitch obezbjeđuje provajder podataka za Microsoft SQL Server i za Microsoft SharePoint)

3. Post-processing

  • Executed – izvršava se nakon izvršavanja upita ali prije vraćanja rezultata
  • ExecuteFailed – okida se ako se ne uspije izvršiti upit

Poslije teoretskog dijela koji je potreban za razumijevanje dijela arhitekture LightSwitch-a vezanog za upite demonstrirat ću upotrebu upita na osnovu par primjera iz prakse.

Za primer ću koristiti enitete sa poljima koji su povezani vezom One-To-Many:

DBDefinition

  • City (Name-String, PostCode-String, Region-String)
  • Customer (Name-String, Phone-Phone Number, Email-Email Address, IsActive-Boolean, CreatedBy-String)
  • Order (Product-String, Unit Price-Double, Quantity-Double, DateOfOrder-Date)

Kreiranje upita za sortiranje podataka

Kreiraću upit za City pod nazivom SortedCities čiji je cilj sortitanje podataka (Add Query nad tabelom u Solution Explorer) a na osnovu polja iz entitea CityName:

SortedCities

Napomena: Nije moguće koristiti polje koje se koristi kao Computed  Property za upite (filtriranje i sortiranje) jer se u upitima koriste samo polja iz eniteta.

Ovaj upit ću primijeniti na ekran CreateNewCustomer (šablon New Data Screen). Cilj je da prilikom izbora City pri unosu podataka za Customer da se pokrene Modalni dijalog za izbor eniteta City koji će pri tome biti sortiran (koristeći naš kreirani upit SortedCities). Potrebno je dodati na ekran AddDataItem i odabrati kreirani upit SortedCities:

AddDataItemSortedCities

Sa ovim smo postavili upit na ekran. Za kontrolu City ćemo odabrati Modal Window Picker:

CityModalWindowPicker

i na kraju ćemo za kontrolu City vezati upit preko opcije Choises:

Choises

Kao rezultat prilikom izbora entiteta City koristi se Window Modal Picker sa stavkama iz entieta City koje su sortirane po polju Name po opadajućem redu (prethodno je korišćen ekran za unos podataka za grad):

SelectCity

Kreiranje upita za filtriranje podataka

Cilj je da se na osnovu upita kreira ekran sa sledećom funkcionalnošću:

CitiesByRegionListDetail

Dakle, polje City Region služi za unos regije za grad kao filter kako bi za određeni grad prikazali sve kupce. Kreiraćemo upit CitiesByRegion za entiet City sa parametrom MyRegion koji je neobavezan. U Properties za MyRegion postaviti Is Optional na true:

ParamIsOptional

što za posledicu ima da ne moramo unijeti podatak u polje City Region da bi dobili spisak svih gradova. Izgled cijelog upita sa filterom i parametrom MyRegion:

CitiesByRegion

Na osnovu upita ćemo kreirati ekran CitiesByRegionListDetail koristeći šablon List and Detail Screen. U lijevom dijelu ekrana su prikazani svi gradovi ako kriterijum Region nije unešen u polje City Region ili su prikazani samo oni gradovi koji pripadaju unešenom regionu. Kada selektujemo određeni grad u desnom dijelu ekrana kao rezultat dobijamo spisak svih kupaca u tom gradu.

Kreiranje upita u kodu (PreprocessQuery)

Ovaj tip upita – PreprocessQuery se izvršava prije nego se glavni upit izvrši (uradili smo presretanje upita). Sve što se doda u okviru PreprocessQuery na nivou entiteta će se uvijek izvršavati.

  • Varijanta 1

Cilj: prikazati u Search Screen za enitet Customer samo one kupce koje je unio radnik koji je ulogovan u program (da radnik vidi samo one podatke koje je on unio. Ovo ima smisla kada više korisnika koristi aplikaciju). Krajnji rezulta bi trebalo da bude:

CustomersUser1

Prvo je potrebno da se snimi informacija o korisniku koji je unio podatke u polje CreatedBy(metoda Created za entitet Customer):

partial void Customer_Created()
{
  this.CreatedBy = this.Application.User.Name;
}

Potom ćemo dodati dodatni upit koristeći LINQ i lambda izraz za entitet Customer (Write Code):

WriteCode1

partial void Customers_All_PreprocessQuery(ref IQueryable query)
{
  query = query.Where(c => c.CreatedBy == this.Application.User.Name);
}

Pokretanjem aplikacije, gdje god se prikazuju podaci iz eniteta Customer, podaci će biti filtrirani na taj način što će se uvijek prikazivati samo oni podaci koje je taj radnik unio.

Napomena: dio koda sa Application.User.Name će funkcionisati samo ukoliko se izvrši Publish aplikacije ili se u Debug modu koristi Forms authentication. Kako je u konkretnoj aplikaciji isključena autentifikacija (Do not enable authentication) u toku debuga ne zna informacija o User.Name (korisnik se ne loguje u aplikaciju). Ukoliko bi željeli ovo da testiramo i u Debug režimu umjesto this.Application.User.Name možemo zamijeniti sa stringom “User1” u obje metode (Created i PreprocessQuery) ili da uključimo autetifikaciju na nivou ekrana.

  • Varijanta 2

Cilj: Proširiti upit na osnovu koga je kreiran ekran CitiesByRegionListDetail  tako što će default regija biti jedan od unešenih regija (“RS”,”FBiH”) ukoliko regija nije unešena a ukoliko je unešen parametar za regiju u polju City Region da se prikažu gradovi iz date regije.

Način na koji možemo odraditi prošireenje kreiranog upita CitiesByRegion je da za taj upit kreiramo PreprocessQuery pomoću LINQ-a:

partial void CitiesByRegion_PreprocessQuery(string MyRegion, ref IQueryable query)
{
  if (String.IsNullOrEmpty(MyRegion))
  {
    query = from reg in query
    where reg.Region == "RS"
    select reg;
  }
}

Na ekranu možemo pogledati kako funkcioniše dorađeni upit gdje za slučaj da nije unešen region u polju City Region da se prikažu samo gradovi iz regiona iz prethodnog LINQ upita:

CitiesByRegionDefault

Kreiranje upita na osnovu nekog drugog upita

LightSwith omogućava kreiranje jednog upita na osnovu nekog drugog upita (Composed query). Tačnije, omogućava kreiranje novog upita na osnovu rezultata nekog drugog upita.

Kada smo kreirali upit SortedCities, izvor podataka za ovaj upit je entitet City (Source: Cities) što za rezultat daje sortiranu listu gradova po nazivu po rastućem redosiljedu (na početku liste se nalaze gradovi čiji nazivi počinju sa a, b, c…):

SortedCitiesDataSource

Kada smo kreirali upit CitiesByRegion imamo mogućnost izbora za izvor podataka ili entitet City ili već postojeći upit SortedCities:

CitiesByRegionDataSource

Ukoliko odaberemo da nam je izvor za upit CitiesByRegion već postojeći upit SortedCities onda na taj način koritimo već postojeću logiku kreiranu u jednom upitu (sortiranje) za kreiranje novog upita.

Pomoću ovog koncepta možemo kreirati višenivojsko “nasleđivanje” upita pri čemu treba voditi računa da se ne pojavi ciklično referenciranje. Ta pojava se dešava ako je npr. Upit A izvor za kreiranje Upita B i ako je Upit B osnov za kreiranje Upita A (upiti su izvor podataka jedan drugom). LightSwitch će prepoznati ovo ciklično referenciranje i prilikom izbora Source će prikazati samo ispravne izvore.

Kreiranje upita koji vraća samo jednu vrijednost

Ova vrsta upita (Scalar query) vraća samo jednu vrijednost. Da bi demostrirali ovu vrstu upita koristićemo enitet Order kako bi unijeli određenu kupovinu za proizvode koristeći šablon Add New Data Screen za entite Order (unos Order podataka i vezivanje Order podataka za Customer-a). Upit nad enitetom Customer sa nazivom BiggestOrder ćemo iskoristiti za pregled kupca sa najboljom kupovinom.

Za rezultat ovog upita ćemo promijeniti broj pojava sa Many na One jer želimo da dobijemo samo kupca koji je obavio najveću kupovinu:

BiggestOrderNumber

Za upit BiggestOrder ćemo kreirati dodatni upit preko dugmeta Edit Additional Query Code:

partial void BiggestOrder_PreprocessQuery(ref IQueryable query)
{
  query = query.OrderByDescending(ord => ord.Order.Quantity * ord.Order.UnitPrice).Take(1);
}

Na kraju, da bi vidjeli rezultat ovog tipa upita kreiraćemo ekran za unos klijenta sa najboljom kupovinom. Prvo ćemo kreirati prazan ekran (npr. šablon Add New Data Screen pri čemu se za Screen Data odabere stavka None i postavi naziv ekrana na MostValuableCustomer). Potom za novokreirani ekran treba dodati Data Item (Add Data Item):

BiggestOrderQuery

Prevući BiggestOrder na ekran:

BiggestOrderAtScreen

Na kraju promijeniti tipa prikaza podataka za enitet Order na Columns Layout te pokrenuti (F5) program i kao rezultat dobijamo ekran:

MVC

Napomena: ova vrsta upita (scalar query) se ne može koristiti kao izvor za kreiranje drugih upita.

Zaključak

Prikazan je samo mali dio mogućnosti koje nam pružaju upiti. Kao što smo vidjeli, upiti mogu bi korisni kako za filtriranje  i sortiranje podataka ali isto tako se mogu koristi kao osnov za druge upite. Na osnovu upita je moguće kreirati i ekrane za pregled i unos podataka. Takođe su prikazane i dvije vrste upita: složeni upiti i upiti koji vraćaju samo jednu vrijednost. Moguće je koristiti i veoma složene LINQ upite ali sa određenim ograničenjima u LightSwitch okruženju.

About Spaso Lazarevic

Spaso Lazarevic is Senior Software Developer working with Microsoft technologies. Leader of .NET User Group Bijeljina, speaker at Microsoft events, writter and blogger. Microsoft MVP for Visual C#.
This entry was posted in Programming and tagged , , . Bookmark the permalink.

6 Responses to LightSwitch – Rad sa upitima

  1. Kivito says:

    Svaka cast Spaso! Odlicni clanci, i barem se ne treba muciti sa prijevodom..😉
    Samo tako nastavi.. Poz!

    • Hvala na pohvalama.
      Nisam bas siguran da sam shvatio ovo sa prijevodom. Ako si mislio na dio koda u kojem koristim engleske termine, to sam odabrao kao koncept rada. Inace su mi sve varijable na engleskom, pa zasto i sve ostalo ne bi bilo tako. A i neke stvari je zaista tesko prevesti kada smo navikli na IT termine na engleskom.
      Vidim ja da mi je Kivito poznato, kada sam i skontako odakle🙂 Nije lose, obojica smo u prvih deset, samo sto se ja ne krijem (bez ljutnje).
      Sto se tice clanaka, toliko toga ima sto bi se moglo napisati, ali sam opet odabrao koncept primjera kojim bi obuhvatio sve osnovne stvari za pocetak.
      Pozdrav

      • Kivito says:

        bok Spaso!
        ovo sa prijevodom konkretno znaci, s obzirom da nisam imao u skoli priliku uciti engleski, da nekad nije lako pohvatati svu strucnu terminologiju, pogotovo ako si pocetnik (poput mene – inace sam sistemac, hardware i odrzavanje sustava za svoju firmu gdje su drugi u stvari programeri, a ja “mali od sarafcigera”).. lightswitch radim i ucim u slobodno vrijeme i pokusavam ga progurati u svojoj firmi.. mozda je to i jedan od razloga zasto se “krijem”, znas kad covjek shvati da nesto na kraju krajeva i “zna”, moze stajati iza toga imenom i prezimenom..😉 u svakom slucaju drago mi je da i u nasim krajevima ima ljudi koji su voljni prezentirati nove tehnologije i odvojiti dragocjeno vrijeme da ih priblize drugima.. (mislim da si ti trenutno jedini koji ima blog na “nasem” jeziku)
        poz!

  2. slavko says:

    Spaso, imam tri tabele čija, pojedina, polja(atribute) trebam staviti na jedan screen tipa “Editable Grid Screen”. Taj screen bi trebao da predstavlja tu “novu tabelu”.
    Kako to najlakše izvesti?
    Hvala.

  3. slavko says:

    Zdravo Spaso,
    Zaboravio sam reći da te tri tabele nisu međusobno povetzane, ali su povezane sa master tabelom
    koja mi nije potrebna u privremenoj tabeli (nazovimo je tako) koja treba da se prikaže na tom screenu. Mislio sam da pomoću LINQ_a napravim upit (da pokupim potrebne podatke iz te tri odnosno četiri tabele-da bih ih povezao) i da ga onda povežem sa tim screenom,
    ali nisam dosada našao u helpu tu mogućnost ili nisam bio dovoljno pažljiv u čitanju. Tu analogiju sam uzeo iz DataSet_a i TabelAdaptere iz VS.Net_a.
    Hvala na prethodnom odgovoru i posetiću preporučeni link.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s