You are on page 1of 258

Sveučilište u Zagrebu

Fakultet elektrotehnike i računarštva

Krešimir Fertalj
Ivana Nižetić Kosović
Boris Milašinović

Razvoj primijenjene
programške potpore
Skripta

FER, ožujka 2016.


Sadržaj

Predgovor ................................................................................................................................................ 1
1 Osnove programskog inženjerstva .................................................................................................. 2
1.1 Programska potpora ................................................................................................................ 2
1.2 Osnove programskog inženjerstva .......................................................................................... 2
1.3 Programsko inženjerstvo i srodna područja ............................................................................ 2
1.4 Modeli razvojnog procesa ....................................................................................................... 3
1.5 Životni ciklus programske potpore .......................................................................................... 3
1.6 Vodopadni model .................................................................................................................... 5
1.7 Iterativni postupak razvoja ...................................................................................................... 6
1.8 Ekstremno programiranje ....................................................................................................... 7
1.9 Zadaci....................................................................................................................................... 9
2 Specifikacija zahtjeva..................................................................................................................... 10
2.1 Vrste zahtjeva ........................................................................................................................ 10
2.2 Primjeri loše definiranih zahtjeva.......................................................................................... 11
2.3 Prioriteti zahtjeva .................................................................................................................. 12
2.4 Dokumentiranje analize (zahtjeva)........................................................................................ 13
2.5 Zadaci..................................................................................................................................... 14
3 Projekt izrade aplikacije................................................................................................................. 15
3.1 Osnove upravljanja projektom .............................................................................................. 15
3.2 Integrirana razvojna okruženja i kontrola programskog koda .............................................. 16
3.3 Plan projekta ......................................................................................................................... 17
3.3.1 Zadaci projekta .............................................................................................................. 18
3.3.2 Resursi projekta ............................................................................................................. 20
3.3.3 Upravljanje konfiguracijom ........................................................................................... 21
3.4 Zadaci..................................................................................................................................... 24
4 Osnove razvojnog okvira Microsoft .NET i programskog jezika C# ............................................... 25
4.1 Elementi jezika i struktura programa .................................................................................... 25
4.1.1 Identifikatori i ključne riječi ........................................................................................... 26
4.1.2 Prostor imena ................................................................................................................ 26
4.1.3 Postupak Main ............................................................................................................... 27
i
4.1.4 Razred ............................................................................................................................ 28
4.1.5 Standardni ulaz i izlaz .................................................................................................... 28
4.1.6 Komentari ...................................................................................................................... 29
4.1.7 Konzolna i grafička aplikacija ......................................................................................... 29
4.1.8 Ponovno korištenje koda ............................................................................................... 29
4.2 Tipovi podataka ..................................................................................................................... 31
4.2.1 Osnovni tipovi ................................................................................................................ 32
4.2.2 Varijable i pridruživanje vrijednosti............................................................................... 33
4.2.3 Vrijednosti i reference ................................................................................................... 33
4.2.4 Razredi String i StringBuilder ......................................................................................... 33
4.2.5 Nabrajanje - Enum ......................................................................................................... 34
4.2.6 Polja ............................................................................................................................... 34
4.2.7 Konverzija tipova ........................................................................................................... 36
4.2.8 Nulabilni tipovi podataka .............................................................................................. 37
4.2.9 Tipovi podataka var i dynamic....................................................................................... 38
4.3 Operatori ............................................................................................................................... 38
4.3.1 Aritmetički operatori, operatori usporedbe i logički operatori..................................... 38
4.3.2 Ostali operatori.............................................................................................................. 38
4.3.3 Prioritet operatora ........................................................................................................ 39
4.4 Naredbe za upravljanje programskim tokom ........................................................................ 39
4.4.1 Selekcija ......................................................................................................................... 39
4.4.2 Petlje .............................................................................................................................. 40
4.4.3 Skokovi........................................................................................................................... 40
4.5 Zadaci..................................................................................................................................... 40
5 Objektno orijentirano programiranje ............................................................................................ 42
5.1 Koncepti................................................................................................................................. 42
5.2 Razredi ................................................................................................................................... 43
5.2.1 Članovi razreda .............................................................................................................. 43
5.2.2 Modifikatori tipova i članova ......................................................................................... 43
5.2.3 Statički članovi, konstante i atributi .............................................................................. 44
5.2.4 Konstruktori ................................................................................................................... 44
5.2.5 Postupci ......................................................................................................................... 45

ii
5.2.6 Svojstva.......................................................................................................................... 47
5.2.7 Indekseri ........................................................................................................................ 48
5.2.8 Preopterećenje postupaka ............................................................................................ 48
5.2.9 Preopterećenje operatora ............................................................................................. 49
5.3 Nasljeđivanje ......................................................................................................................... 49
5.3.1 Nasljeđivanje općenito .................................................................................................. 49
5.3.2 Apstraktni razredi .......................................................................................................... 52
5.3.3 Dinamičko višeobličje .................................................................................................... 54
5.3.4 Agregacija i kompozicija ................................................................................................ 55
5.3.5 Sučelja............................................................................................................................ 56
5.4 Kolekcije ................................................................................................................................ 57
5.4.1 Kolekcija ArrayList ......................................................................................................... 58
5.4.2 Kolekcije Stack i Queue.................................................................................................. 59
5.4.3 Generičke kolekcije........................................................................................................ 59
5.4.4 Kolekcije Hashtable i Dictionary .................................................................................... 61
5.4.5 Proširenja....................................................................................................................... 62
5.5 Zadaci..................................................................................................................................... 63
6 Tehnike programiranja .................................................................................................................. 65
6.1 Defenzivno programiranje..................................................................................................... 65
6.2 Tehnike obrade pogrešaka .................................................................................................... 65
6.3 Iznimke .................................................................................................................................. 66
6.3.1 Obrada iznimki............................................................................................................... 66
6.3.2 Prosljeđivanje iznimki .................................................................................................... 67
6.3.3 Vlastite iznimke ............................................................................................................. 69
6.3.4 Preporuke za korištenje iznimki .................................................................................... 70
6.4 Tvrdnje................................................................................................................................... 71
6.5 Barikade ................................................................................................................................. 72
6.6 Otkrivanje pogrešaka............................................................................................................. 74
6.6.1 Količina defenzivnog koda u završnoj verziji ................................................................. 75
6.7 Životni vijek objekta .............................................................................................................. 75
6.8 Zadaci..................................................................................................................................... 78
7 Grafičko korisničko sučelje ............................................................................................................ 79

iii
7.1 WindowsForms ...................................................................................................................... 79
7.2 Događaji i delegati ................................................................................................................. 79
7.3 Prijenos parametara među formama .................................................................................... 81
7.4 Provjera valjanosti unosa podataka ...................................................................................... 83
7.5 Vlastite kontrole i vlastiti događaji ........................................................................................ 84
7.6 Višenitnost i paralelno programiranje u jeziku C# ................................................................ 87
7.6.1 Primjer potrebe za paralelnim izvršavanjem................................................................. 88
7.6.2 Stvaranje zadatka u pozadini ......................................................................................... 90
7.6.3 Čekanje zadataka i asinkroni postupci .......................................................................... 90
7.6.4 Kontrola ProgressBar..................................................................................................... 91
7.6.5 Pristup kontrolama korisničkog sučelja iz drugi niti ...................................................... 91
7.7 Zadaci..................................................................................................................................... 92
8 Aplikacija nad bazom podataka ..................................................................................................... 93
8.1 Ogledna baza podataka ......................................................................................................... 93
8.2 Tehnologija ADO.NET ............................................................................................................ 93
8.3 Izravna obrada podataka ....................................................................................................... 95
8.3.1 Priključak na bazu podataka .......................................................................................... 95
8.3.2 Primjer izravne obrade .................................................................................................. 97
8.3.3 Zatvaranje priključka ..................................................................................................... 98
8.3.4 Neovisnost o konkretnoj implementaciji ...................................................................... 98
8.4 Lokalna obrada podataka ...................................................................................................... 98
8.5 Entity Framework .................................................................................................................. 99
8.5.1 Načini kreiranja modela u Entity Frameworku .............................................................. 99
8.5.2 Stvaranje modela na osnovu postojeće baze podataka ................................................ 99
8.5.3 Elementi EF modela ..................................................................................................... 102
8.5.4 Važnija svojstva i postupci razreda DbContext i DbSet ............................................... 102
8.5.5 Dodavanja novog zapisa .............................................................................................. 103
8.5.6 Ažuriranje postojećeg zapisa ....................................................................................... 103
8.5.7 Brisanje zapisa ............................................................................................................. 104
8.5.8 Upiti nad EF modelom ................................................................................................. 104
8.5.9 EF i pohranjene procedure .......................................................................................... 104
8.6 Zadaci................................................................................................................................... 105

iv
9 Povezivanje podataka i složene zaslonske maske ....................................................................... 106
9.1 Povezivanje podataka s kontrolama na formi ..................................................................... 106
9.1.1 Razred BindingSource .................................................................................................. 107
9.2 Osvježavanje povezivanja .................................................................................................... 109
9.2.1 Razredi List i BindingList .............................................................................................. 111
9.2.2 Rukovanje podacima korištenjem kontrole BindingSource ........................................ 114
9.2.3 Promjena povezanog podatka i sučelje INotifyPropertyChanged ............................... 114
9.3 Povezivanje na podatke korištenjem EF modela................................................................. 115
9.3.1 Dinamičko postavljanje povezivanja ........................................................................... 116
9.3.2 Navigacija podacima vlastitim metodama .................................................................. 117
9.3.3 Rad s podacima (dodavanje, izmjena, brisanje) .......................................................... 118
9.3.4 Opoziv izmjena ............................................................................................................ 119
9.4 Validacija unosa podataka ................................................................................................... 119
9.5 Kontrola DataGridView........................................................................................................ 121
9.6 Kontrola BindingNavigator .................................................................................................. 123
9.7 Složene zaslonske maske ..................................................................................................... 123
9.7.1 Potrebne preinake na Entity Framework modelu ....................................................... 123
9.7.2 Izračunata polja ........................................................................................................... 124
9.7.3 Odabir vrijednosti stranog ključa ................................................................................ 124
9.7.4 Sinkronizacija stavki..................................................................................................... 126
9.7.5 Početne i referentne vrijednosti stavki ....................................................................... 126
9.7.6 Dodavanje novog dokumenta ..................................................................................... 128
9.7.7 Spremanje podataka ................................................................................................... 128
9.7.8 Brisanje zapisa zaglavlja (skupa s detaljima) ............................................................... 129
9.7.9 Brisanje pojedinačne stavke ........................................................................................ 129
9.7.10 Opoziv izmjena ............................................................................................................ 130
9.8 Zadaci................................................................................................................................... 130
10 Korisničko sučelje i dijalozi ...................................................................................................... 132
10.1 Načela oblikovanja korisničkog sučelja ............................................................................... 132
10.1.1 Raspored...................................................................................................................... 132
10.1.2 Uvažavanje sadržaja .................................................................................................... 132
10.1.3 Estetika ........................................................................................................................ 132

v
10.1.4 Iskustvo korisnika ........................................................................................................ 133
10.1.5 Dosljednost .................................................................................................................. 133
10.1.6 Lakoća korištenja ......................................................................................................... 133
10.2 Vrste korisničkog sučelja ..................................................................................................... 134
10.3 Izbornici i dijalozi ................................................................................................................. 134
10.4 Kriterij za odabir kontrola grafičkog sučelja ........................................................................ 134
10.5 Zadaci................................................................................................................................... 135
11 Dizajn sustava .......................................................................................................................... 136
11.1 Opći dizajn ........................................................................................................................... 136
11.2 Detaljni dizajn ...................................................................................................................... 136
11.3 Dizajn arhitekture sustava ................................................................................................... 136
11.4 Elementi arhitekture sustava .............................................................................................. 137
11.4.1 Centralizirana obrada .................................................................................................. 137
11.4.2 Dvoslojna arhitektura klijent-poslužitelj (client-server) .............................................. 138
11.4.3 Primjer arhitekture klijent-poslužitelj ......................................................................... 139
11.4.4 Troslojna ili višeslojna arhitektura klijent-poslužitelj .................................................. 141
11.5 Višeslojna aplikacija ............................................................................................................. 142
11.5.1 Odnosi među slojevima ............................................................................................... 143
11.5.2 Poslovni objekt ............................................................................................................ 144
11.5.3 Dohvat podataka kroz slojeve ..................................................................................... 145
11.5.4 Načini punjenja poslovnog objekta ............................................................................. 147
11.5.5 Izvedba podatkovnog sloja pomoću ADO.NET-a ......................................................... 148
11.5.6 Izvedba podatkovnog sloja pomoću Entity Frameworka ............................................ 149
11.5.7 Dodavanje, ažuriranje i brisanje artikla ....................................................................... 150
11.5.8 Izvedba šifrarničkih formi višeslojno. .......................................................................... 150
11.6 Zadaci................................................................................................................................... 151
12 Primjer višeslojne aplikacije – aplikacija FirmaWin ................................................................. 152
12.1 Nedostaci postojećeg rješenja ............................................................................................ 152
12.2 Struktura rješenja ................................................................................................................ 152
12.3 Firma.Framework ................................................................................................................ 153
12.3.1 Poslovni objekt i sučelje IBusinessObject .................................................................... 154
12.3.2 Bazni razred za poslovni objekt ................................................................................... 155

vi
12.3.3 Sučelje liste poslovnih objekata .................................................................................. 155
12.3.4 Liste poslovnih objekata .............................................................................................. 155
12.4 Dohvat liste artikala............................................................................................................. 156
12.5 Izvedba podatkovnog sloja .................................................................................................. 158
12.6 Poslovni objekt sa stavkama ............................................................................................... 159
12.6.1 Dohvat i spremanje složenog objekta ......................................................................... 160
12.6.2 Spremanje složenog objekta ....................................................................................... 163
12.7 Refleksija i korisnički atributi ............................................................................................... 164
12.7.1 Naknadno povezivanje ................................................................................................ 165
12.7.2 Atributi......................................................................................................................... 166
12.7.3 Univerzalni i samoprilagodljivi programski moduli ..................................................... 167
12.8 Zadaci................................................................................................................................... 171
13 Web aplikacije - ASP.NET Web Forms ..................................................................................... 172
13.1 ASP.NET Web Forms ............................................................................................................ 172
13.1.1 Prevođenje i prikaz web stranica ................................................................................ 173
13.1.2 Web aplikacija i/ili web mjesto ................................................................................... 174
13.1.3 Objava aplikacije i web poslužitelji .............................................................................. 175
13.2 Uobičajeni postupak izrade web stranica............................................................................ 176
13.3 Struktura Web Forms aplikacije .......................................................................................... 176
13.3.1 Osnovne Web kontrole................................................................................................ 177
13.3.2 Sintaksa kontrola ......................................................................................................... 177
13.3.3 Sintaksa aspx stranica .................................................................................................. 177
13.3.4 Odvajanje dizajna od koda .......................................................................................... 177
13.4 Životni ciklus web stranice .................................................................................................. 178
13.5 Objekti ASP.NET aplikacije................................................................................................... 179
13.6 Glavna stranica i izbornici .................................................................................................... 179
13.6.1 Glavna stranica ............................................................................................................ 179
13.6.2 Izbornici ....................................................................................................................... 180
13.6.3 Mapa web sjedišta....................................................................................................... 181
13.7 Složene kontrole i povezivanje podataka ............................................................................ 182
13.7.1 Povezivanje podataka .................................................................................................. 182
13.7.2 Povezivanje jednostavnih lista .................................................................................... 182

vii
13.7.3 Kontrola ObjectDataSource ......................................................................................... 182
13.8 Kontrola GridView ............................................................................................................... 185
13.8.1 Svojstva i događaji unutar mreže s podacima ............................................................. 186
13.8.2 Primjer dodavanja izvora podataka za GridView......................................................... 187
13.8.3 Definiranje i povezivanje stupaca u mreži s podacima ............................................... 188
13.8.4 Ažuriranje redaka u mreži s podacima ........................................................................ 189
13.8.5 Posebno dizajnirani stupci ........................................................................................... 190
13.8.6 Prikaz slike iz baze podataka ....................................................................................... 190
13.8.7 Prijenos argumenata stranici....................................................................................... 191
13.8.8 Povezivanje za posebno dizajnirane stupce ................................................................ 191
13.8.9 Dodavanje novih zapisa ............................................................................................... 192
13.8.10 Validacijske kontrole ............................................................................................... 192
13.9 Primjer Dokument-Stavka ................................................................................................... 193
13.9.1 Kontrola FormView ...................................................................................................... 194
13.9.2 Dohvat dokumenta ...................................................................................................... 194
13.9.3 Predložak za prikaz dokumenta................................................................................... 195
13.9.4 Predložak za ažuriranje dokumenta ............................................................................ 196
13.9.5 Odabir vrijednosti stranog ključa padajućom listom................................................... 196
13.9.6 Dodavanje novog dokumenta ..................................................................................... 197
13.9.7 Brisanje dokumenta .................................................................................................... 199
13.9.8 Prikaz stavki dokumenta.............................................................................................. 200
13.9.9 Oblikovanje pojedinog stupca u mreži sa stavkama i dodavanje nove stavke............ 201
13.10 Zadaci............................................................................................................................... 202
14 ASP.NET MVC........................................................................................................................... 203
14.1 Usporedba ASP.NET web formi s MVC-om ......................................................................... 203
14.2 Struktura ASP.NET MVC aplikacije ...................................................................................... 204
14.3 Inicijalne postavke MVC aplikacije ...................................................................................... 205
14.3.1 Paketi datoteka............................................................................................................ 206
14.4 Način rada MVC aplikacije ................................................................................................... 206
14.4.1 Usmjeravanje zahtjeva ................................................................................................ 206
14.4.2 Uobičajeni rezultati akcije upravljači ........................................................................... 207
14.4.3 Glavna stranica ............................................................................................................ 208

viii
14.4.4 Sintaksa pogleda .......................................................................................................... 209
14.4.5 Parcijalni pogled .......................................................................................................... 210
14.5 Primjer prikaza mjesta ......................................................................................................... 210
14.5.1 Akcija dohvata i pregleda svih mjesta ......................................................................... 211
14.5.2 Ažuriranje podataka .................................................................................................... 212
14.5.3 Prijenos dodatnih vrijednosti pogledu ........................................................................ 213
14.5.4 Prihvat podataka ......................................................................................................... 214
14.5.5 Validacija ..................................................................................................................... 214
14.5.6 Proširenja za straničenje ............................................................................................. 215
14.5.7 Forma za brisanje mjesta............................................................................................. 215
14.6 Prikaz artikala ...................................................................................................................... 217
14.6.1 Prikaz i dodavanje slike................................................................................................ 217
14.6.2 Ažuriranje unutar retka ............................................................................................... 218
14.7 Primjer Master-Detail .......................................................................................................... 222
14.7.1 Popis akcija i pogleda .................................................................................................. 223
14.7.2 Prikaz dokumenta ........................................................................................................ 224
14.7.3 Ažuriranje dokumanta i stavki ..................................................................................... 225
14.7.4 Validacija podataka ..................................................................................................... 226
14.8 Zadaci................................................................................................................................... 226
15 Web servisi .............................................................................................................................. 227
15.1 Osnovna pravila servisno orijentirane arhitekture ............................................................. 227
15.2 Standardi za web servisi ...................................................................................................... 228
15.3 Struktura primjera s web servisima..................................................................................... 228
15.4 Primjer web servisa ............................................................................................................. 229
15.4.1 Generiranje proxy razreda za pozive web servisa ....................................................... 229
15.4.2 Poziv web servisa......................................................................................................... 230
15.4.3 Nedostatci klasičnih web servisa ................................................................................. 231
15.5 WCF servisi .......................................................................................................................... 232
15.5.1 Protokoli povezivanja .................................................................................................. 232
15.5.2 Primjer izrade WCF servisa .......................................................................................... 233
15.5.3 Program WCF Test Client ............................................................................................. 235
15.5.4 Korištenje WCF servisa dodavanjem reference........................................................... 235

ix
15.5.5 Korištenje WCF servisa bez dodavanja reference ....................................................... 236
15.5.6 WCF i serijalizacija podataka ....................................................................................... 238
15.5.7 Iznimke (pogreške) u radu s WCF servisima ................................................................ 239
15.5.8 Postavke povezivanja .................................................................................................. 240
15.6 REST servisi .......................................................................................................................... 241
15.6.1 REST i vrste HTTP poziva .............................................................................................. 241
15.6.2 REST i WCF ................................................................................................................... 241
15.6.3 Primjer poziva REST servisa iz .NET klijenta ................................................................ 244
15.6.4 Alati za inspekciju prometa i stvaranje HTTP zahtjeva ................................................ 244
15.6.5 WebApi ........................................................................................................................ 245
15.7 Zadaci................................................................................................................................... 246
16 Reference ................................................................................................................................ 247
16.1 Literatura ............................................................................................................................. 247
16.2 Korisne poveznice................................................................................................................ 247

x
Predgovor
Ova skripta namijenjena su polaznicima predmeta Razvoj primijenjene programske potpore (RPPP ili
R3P) na preddiplomskom studiju Fakulteta elektrotehnike i računarstva, ali može dobro doći i kao
nastavni materijal sličnog predmeta na drugim visokim učilištima kao i štivo za sve one koji
namjeravaju savladati osnove razvoja aplikacija nad bazom podataka.
Uvodno su izložene osnove programskog inženjerstva i upravljanja projektima, nakon čega slijede
koncepti objektno orijentiranog programiranja i tehnika programiranja te primjeri arhitektura
dvoslojnih i višeslojnih aplikacija uključujući aplikacije nad web servisima.
Pretpostavlja se da je čitatelj prethodno savladao osnove programiranja, algoritme i strukture
podataka i baze podataka. Budući da na FER-u postoji predmet Objektno orijentirano programiranje,
skripta ne ulaze u detalje objektno orijentiranog programiranja, ali su izloženi neki koncepti ključni za
razumijevanje pojedinih segmenata aplikacija.
Kompletan izvorni kod programskih primjera priložen je kao dodatak predavanjima, a ovdje su
prikazani reprezentativni programski odsječci koji objašnjavaju pojedine koncepte na kojima se
zasniva programska potpora.
Osim toga, upute za rad u grupi i verzioniranje programskog koda, kao i upute za laboratorijske
vježbe odvojeni su i objavljuju se zasebno.
Svim čitateljima želimo da nakon polaganja predmeta RPPP ili čitanja ovih skripata postanu
programski inženjeri i o(p)stanu u struci te napreduju kao projektanti informacijskih sustava i
upravitelji projekata .

Autori

1
1 Osnove programskog inženjerstva

1.1 Programska potpora


Softver ili programska potpora (oprema, podrška) je dio računalnog sustava koji nema fizikalnih
dimenzija te se koristi kao opći pojam za sve vrste programa, programskih jezika itd. Preciznije,
programska potpora je skup elemenata ili objekata u jedinstvenoj „konfiguraciji“ koju čine računalni
programi, podaci i dokumentacija. Izrada programske potpore je složen postupak, podložan
pogreškama. Programska potpora se ne troši, teško je mjerljiva, dugo se koristi i lako kopira (zajedno
s pogreškama), ali zastarijeva iako nema fizikalnih dimenzija. Za namjenski program, tj. računalom
podržano rješenje jednog ili više poslovnih problema ili potreba, upotrebljava se termin primijenjena
programska potpora ili računalna aplikacija. Više takvih aplikacija može se udružiti u sustav aplikacija
za upravljanje ljudskih aktivnostima što je objedinjeno pojmom informacijski sustav.
Početkom 90-tih razvijale su se jednokorisničke, samostalne aplikacije pisane u jezicima i alatima kao
što su dBase, Clipper, ZIM itd. nakon čega je slijedio razvoj poslužiteljskih aplikacija nad bazom
podataka (Informix, Oracle). Krajem 90-tih dolazi do razvoja klijentskih aplikacija u obliku debelih
klijenata (Microsoft Access, Visual Basic, Java…) s bazom podataka odvojenom na serveru. 2000-te
donose razvoj internetskih aplikacija u obliku tankih klijenata (aplikacija unutar Internet preglednika)
ili džepnih aplikacija (mobilni uređaji – Pocket PC). Slijedi razvoj mobilnih i distribuiranih aplikacija
baziranih na web servisima i novim mobilnim platformama (Android, iOS, …). U današnje vrijeme
naglasak je na višeslojnim aplikacijama, iako u pojedinim slučajevima i debeli klijenti mogu biti dobar
odabir. O arhitekturama više u jednom od narednih poglavlja.

1.2 Osnove programskog inženjerstva


Naziv programsko inženjerstvo (eng. software engineering) predložen je na konferencijama koje je
NATO organizirao 1968. i 1969. godine prilikom diskusije o problemu razvoja velikih složenih sustava.
Predložena je primjena inženjerskog pristupa na programsku opremu kako bi se smanjili troškovi
razvoja i povećala kvaliteta i pouzdanost programske potpore. Prema ISO/IEC/IEEE rječniku
programsko inženjerstvo je sistematičan, discipliniran i mjerljiv pristup razvoju, primjeni i održavanju
softvera, to jest primjena inženjerskog pristupa na programsku opremu. Programsko inženjerstvo je
inženjerska disciplina koja obuhvaća sve aspekte izrade programske opreme [Somerville]. Područje
programskog inženjerstva obuhvaća sve poslove kojima se oblikuje i razvija programska oprema i
sustavnu primjenu prikladnih alata i tehnika na čitav razvoj programske potpore.

1.3 Programsko inženjerstvo i srodna područja


 Fred Brooks: “A scientist builds in order to learn; an engineer learns in order to build.”1
Programsko inženjerstvo se razlikuje od računarske znanosti u tome što se računarska znanost
fokusira na teorijske osnove dok je programsko inženjerstvo orijentirano na praktičnu primjernu u
razvoju i isporuci programske potpore. Za razliku od računalnog inženjerstva programsko inženjerstvo
se bavi svim aspektima računalnog razvoja koji uključuje i hardver i softver te inženjerstvo procesa.

1
Gibbs, N.E. and Fairley, R.E. (Eds.). Software Engineering Education: Thr Educational Needs of the Software
Community. Springer-Verlag, New York, 1987.
2
1.4 Modeli razvojnog procesa
Model procesa je plan razvoja koji navodi opće postupke razvoja programskog proizvoda. Preciznije,
modelom procesa definira se koje aktivnosti (potprocesi) se trebaju obaviti, tko ih treba obaviti i u
kojoj ulozi kojim redoslijedom, koji će proizvodi biti razvijeni te kako ih vrednovati. Pri tome suradnik
koji obavlja posao u nekoj ulozi (voditelj projekta, arhitekt, programer…) koristi postojeće
programske uratke (dokumenti, modeli, programi) te primjenom metoda, preporuke, običaja, popisa
provjera, uzoraka te dostupne alati kreira novi ili izmijenjeni uradak (Slika 1). Programski proizvod je
tada skup programskih uradaka.

Slika 1. Aktivnosti razvojnog procesa

1.5 Životni ciklus programske potpore


Software/Systems development life-cycle (SDLC) je model razvojnog procesa i predstavlja unaprijed
propisan proces razvoja. SDLC definira faze i zadatke (aktivnosti) koje treba obaviti tijekom razvoja.
Postojanje životnog ciklusa osigurava „kontrolne točke“ za praćenje napretka, procjenu postignutih
rezultata i donošenje odluka o daljnjim koracima.
Slika 2 prikazuje tipične faze životnog ciklusa bez implikacije da se nužno radi o diskretnim i/ili
slijednim aktivnostima.

3
Slika 2. Tipične faze životnog ciklusa

Planiranje
Planiranje treba dati odgovor na pitanje zašto graditi sustav te odrediti poslovne ciljeve (koristi). U
studiji izvedivosti analizira se problemsko područja, utvrđuju problemi, određuju granice projekata o
procjenjuju ključni aspekti predloženog projekta – tehnička/ekonomska/organizacijska izvedivost.
Dolazi do ekipiranja projekta, određuje se način upravljanja i nadzora projekta te se izrađuje plan
rada (plan sustava, plan informatizacije).

Analiza
Analiza je traženje odgovora na pitanje tko će koristiti sustav, što će sustav raditi te gdje i kada će biti
korišten. Proučavaju se postojeći sustavi, ustanovljavaju moguća poboljšanja i razvija koncept novog
sustava. Rezultat je poslovni model sustava, tj. prijedlog sustava, odnosno specifikacija zahtjeva.

Oblikovanje (projektiranje)
Oblikovanje odgovara na pitanje kako napraviti sustav za određenu specifikaciju zahtjeva. Određuje
se kako će sustav funkcionirati sa stanovišta hardvera, softvera i mrežne infrastrukture, korisničkog
sučelja, programa te spremišta podataka. Specifikaciju sustava je tehnička specifikaciju koja se sastoji
od dizajna arhitekture, dizajna baze podataka i datoteka, dizajna sučelja i dizajna programa.
Specifikacija sustava predstavlja pogled projektanta (izvođača).

Izrada
Izrada predstavlja ugradnju (implementaciju) i provjeru rješenja. Na osnovu zadane specifikacije
konstrukcijom, testiranjem i instalacijom se isporučuje funkcionalni sustav.

Primjena i održavanje

4
Funkcionalni sustav se isporučuje korisniku čime postaje operabilni sustav koji se održava i
poboljšava.

Pregled
U ovoj fazi se utvrđuju popravci, dorade, prerade i nadogradnje koje treba izvršiti na sustavu. Time
se vrši revizija sustava, odnosno preispitivanje čitavog sustava kada su potrebne veće izmjene uslijed
promjena u poslovanju ili promjena poslovnih ciljeva nakon čega se ide u novi projekt i novi razvojni
ciklus.

1.6 Vodopadni model


Vodopadni model naziv je dobio zbog svog vizualnog prikaza (Slika 3) i najstariji je model razvoja
nastao po uzoru na ostale inženjerske discipline. U klasičnom vodopadnom modelu povratne veze
prema gore nisu dopuštene. Budući da je u takvom modelu potrebno planirati cijeli proces i sve
aktivnosti prije nego što posao započne, postoje i varijante vodopadnog modela: pseudostrukturni u
kojem je dozvoljena povratna veza na aktivnosti koja je bila neposredno prije i radikalni koji
dozvoljava povratak na bilo koju prethodnu aktivnost.

Slika 3. Vodopadni model (klasični, pseudostrukturni, radikalni)

5
Faze vodopadnog modela su:

 Definiranje zahtjeva (requirements analysis and definition) predstavlja fazu u kojoj se


definira funkcionalnost programske potpore prema zahtjevima korisnika.
 Dizajn, projektiranje sustava (system and software design) predstavlja fazu u kojoj se
definira cjelokupna arhitektura programske podrške, a grubi model sustava razrađuje se u
detaljni opis izvedbe.
 Ugradnja i testiranje jedinica (implementation and unit testing) predstavlja fazu kodiranja
tijekom koje se zahtjevi prevode u programski kod nakon čega se programske jedinice
zasebno testiraju provjerom naspram specifikacije.
 Integracija i testiranje sustava (integration and system testing) predstavlja fazu u kojoj se
programske jedinice povezuju se u cjelinu te se provjerava odgovara li programska potpora
zahtjevima korisnika
 Primjena i održavanje (deployment/operation and maintenance) započinje predajom sustava
korisnicima na uporabu te se tijekom održavanja uklanjaju naknadno uočene neispravnosti
nakon čega se sustav proširuje i poboljšava prema potrebama

1.7 Iterativni postupak razvoja


Izvorno razvijen pod nazivom Objectory, a danas pod nazivom IBM Rational Unified Process (RUP)
predstavlja ujedinjeni razvojni proces. RUP je primjer iterativnog i inkrementalnog razvoja u kojem se
softver razvija i objavljuje po dijelovima. Glavne faze obavljaju se kroz niz iteracija pri čemu se svaka
iteracija obavlja standardnim životnim ciklusom koji uključuje analizu, oblikovanje, ugradnju i
provjeru što se grafički može prikazati kao na narednoj slici
(

Slika 4).

6
Rezultat iteracije je proizvod završne kakvoće (eng. production-quality), provjeren i integriran, koji
zadovoljava podskup ukupnih zahtjeva. Isporuke mogu biti interne ili prema korisnicima. Za različite
modele razvoja i tipove projekata RUP sadrži niz "predložaka" razvojnih procesa (eng. roadmaps).

Slika 4. Veze između glavnih faza RUP procesa i faza standardnog životnog ciklusa

Faze su sljedeće:
 Faza počinjanja (eng. inception) ima svrhu opravdati razloge za pokretanje projekta. U ovoj
fazi prikupljaju se najvažniji zahtjevi (10% detaljno) te se određuje doseg projekta.
 U fazi elaboracije (eng. elaboration) detaljno se prikupljaju zahtjevi (80%) i vrši se globalna
(eng. high-level) analiza i dizajn, ustanovljavanja se osnovne arhitekture i planira
konstrukcija.

 U fazi konstrukcije (gradnje, eng. Construction) prikupljaju se ostali zahtjevi i evidentiraju


promjene zahtjeva. Razrađuje se arhitektura te se izrađuje sustav uz kontinuiranu integraciju.
 U fazi prijelaza (eng. transition) obavlja se beta testiranje, podešavaju se performanse, vrši se
poduka korisnika te se obavlja provjera prihvatljivosti i zadovoljstva korisnika.
Nakon isporuke započinje post implementacija (eng. post-deployment) a po potrebi dolazi do
nastavka evolucijskog razvoja uz očuvanje integriteta aplikacije.

1.8 Ekstremno programiranje


Ekstremno programiranje je agilni razvojni proces koji se temelji na 5 vrijednosti: komunikacija,
jednostavnost, povratne informacije, hrabrost, uvažavanje (eng. respect).

7
Istraživanje Planiranje Iteracije do izdanja Produkcija Održavanje Smrt

Stalni pregled

Priče za
iduću Programiranje u paru
Redovne
iteraciju Analiza Dizajn Plan testa Test
nadopune

Povratne Stalna
Prič Prioriteti informacije integracija
e
Procjena
napora
Test Malo Nadopunjeno Finalno
Skupna baza
izdanje izdanje izdanje

Odobrenje
korisnika

Slika 5. Faze ekstremnog programiranja

Slika 5 prikazuje faze u postupku ekstremnog programiranja.

 U fazi istraživanja korisnici bilježe svoje priče na kartice pri čemu svaka kartica sadrži jednu
mogućnost programa. Faza istraživanja traje nekoliko tjedana do nekoliko mjeseci pri čemu
se skup priča redovno nadopunjuje. U ovoj fazi projektni tim se pobliže upoznaje s alatima,
tehnologijom i postupcima projekta te se radi prototip sustava za testiranje tehnologije i
varijanti arhitekture sustava.

 Planiranje postavlja prioritete na korisničke priče, tj. svojstva programskog rješenja.


Određuje se prioriteti priča, određuje se vrijeme potrebno za implementaciju pojedine priče
(kartice) te se planira doseg prvog malog izdanja, nakon čega se određuje cjelokupni
vremenski raspored. Faza planiranja traje nekoliko dana, a rok za izdavanje prvog malog
izdanja obično je unutar dva mjeseca.
 Vremenski raspored iz faze planiranja se razlaže u više iteracija do izdanja pri čemu prije
prvog izdanja obično slijedi nekoliko iteracija sustava. Klijent određuje kartice koje će se
koristiti pri svakoj narednoj iteraciji. Pojedina iteracija traje jedan do četiri tjedna, a testovi
prihvatljivosti izvode se na kraju svake iteracije. Prva iteracija stvara takav sustav koji
obuhvaća cijelu arhitekturu ciljanog sustava, a na kraju posljednje iteracije sustav je spreman
za produkciju.

 U fazi produkcije vrši se dodatno testiranje i provjera performansi sustava prije isporuke
klijentu. Razrješavaju se primjedbe na sustav te se odlučuje da li će se primjedbe riješiti u
tekućem izdanju. Iteracije trajanja tri do najviše tjedan dana, a zakašnjele nove ideje i
prijedlozi se dokumentiraju i njihova implementacija odgađa.

8
 Nakon što je prvo izdanje pušteno u produkciju, XP projekt mora istovremeno održavati
softver u primjeni i proizvoditi nove iteracije te se zbog toga brzina implementacije smanjuje.
Održavanje može zahtijevati nove članove projektnog tima i promjenu strukture tima.
 Faza smrti je blizu kada klijent nema više novih kartica s pričama pri čemu se podrazumijeva
da sustav zadovoljava sve zahtjeve (npr. pouzdanost i stabilnost). Faza smrti je prikladno
vrijeme u XP projektu da se konačno napiše sva korisnička dokumentacija budući da više
nema promjena na arhitekturi, dizajnu i kodu sustava. „Smrt“ može nastupiti i kada sustav ne
ispunjava sva korisnička očekivanja, ili ako postane preskup za daljnji razvoj.

1.9 Zadaci
Zadatak 1. Koja je razlika između računarske znanosti i programskog inženjerstva?
Zadatak 2. Što su izlazi pojednih faza životnog ciklusa?
Zadatak 3. U kojoj fazi životnog ciklusa programske potpore se
a) razvija specifikacija zahtjeva,
b) odlučuje o dizajnu arhitekture, sučelja i baze podataka?
Zadatak 4. Koje su varijante vodopadnog modela razvoja?

9
2 Specifikacija zahtjeva
ISO/IEC/IEEE rječnik programskog inženjerstva definira zahtjev kao
1. uvjet ili sposobnost koje korisnik treba da bi riješio neki problem ili ostvario neki cilj
2. uvjet ili sposobnost koju mora posjedovati ili zadovoljiti sustav, komponenta sustava,
proizvod ili usluga da bi zadovoljila ugovor, standard, specifikaciju ili neki drugi formalni
dokument.
Norma koja se odnosi na inženjerstvo zahtjeva tomu dodaje
3. izjavu kojom se prevodi ili izražava potreba i njoj pridružena ograničenja i uvjeti
Udruga Project Management Institute definiciju proširuje izjavom da zahtjevi uključuju nabrojane i
dokumentirane potrebe, želje i očekivanja sponzora, korisnika i ostalih dionika u projektu.

2.1 Vrste zahtjeva


Prema vrsti zahtjevi se dijele na poslovne, korisničke, funkcionalne i nefunkcionalne zahtjeve.

Poslovni zahtjevi
Poslovni zahtjevi odgovaraju na pitanje zašto (se radi neki sustav) te su sadržani u dokumentima u
kojima se opisuje vizija i opseg projekta. Predstavljaju ciljeve organizacije ili korisničke zahtjeve na
višoj razini i ukratko opisuju problem koji treba riješiti. Neki primjeri poslovnih zahtjeva su:
 poboljšanje usluge postojećim klijentima tvrtke i pridobivanje novih
 evidencija članstva i automatizacija postupka primanja novih članova neke udruge
 praćenje financijskih podataka udruge i njenih članova
 poboljšanje procesa prodaje
 omogućavanje internetske prodaje
 podrška organiziranju natjecanja i okupljanja

U idealnom slučaju zahtjevi vlasnika podudaraju se s poslovnim ciljevima. Npr. za sustav


subvencionirane studetntske prehrane to može biti očekivana novčana ušteda koja bi se ostvarila ako
sustav bude tako koncipiran da prava na subvencioniranu prehranu može koristiti samo student koji
ih je stekao i da ih može koristiti samo u svrhu prehrane. Da bi se to ostvarilo poslovni zahtjev je da
sustav mora onemogućiti:
 korištenje subvencije od strane osoba koje nemaju na to pravo
 zaradu ilegalnih posrednika
 korištenje subvencije za druge svrhe osim prehrane
 naplatu usluga koje nisu pružene

Korisnički zahtjevi
Korisnički zahtjevi su zahtjevi krajnjih korisnika te opisuju zadatke koje korisnik mora moći obaviti.
Sadržani su u opisima slučajeva korištenja tj. opisima scenarija rada i obično se izražavaju u izjavama
oblika „Korisnik želi/treba/mora moći obaviti…“.
Neki primjeri korisničkih zahtjeva u sustavu subvencionirane prehrane studenata (X-ice):

10
 Korisnik mora moći ostvariti pravo na prehranu kod bilo kojeg pružatelja usluge - Novi sustav
mora omogućiti da student ostvaruje svoje pravo kod bilo kojeg pružatelja usluge
subvencionirane prehrane. Dosadašnja praksa je bila da svaki pružatelj usluga izdaje svoje
bonove koji se mogu koristiti samo u određenim restoranima
 Korisnik treba plaćati obroke nakon korištenja pojedinog obroka. - Treba izbjeći bilo kakvo
plaćanje od strane studenata za potrebe ostvarivanja prava, a posebice unaprijed.
 Korisnik mora moći prijaviti gubitak kartice – Potrebno je smanjiti rizik gubitka ostvarenih
prava te sustav mora onemogućiti zloporabu stečenih prava.
 Korisnik želi ostvariti i ostala prava iz studentskog standarda, npr. javni prijevoz po
povlaštenoj cijeni, kazališta, kina, smještaj u studentskim domovima, student-servis, itd.

Funkcionalni zahtjevi
Funkcionalni zahtjevi odgovaraju na pitanje što (se može/mora napraviti koristeći sustav).
Funkcionalni zahtjevi definiraju softversku funkcionalnost (očekivano ponašanje i operacije koje
sustav može izvoditi) koju treba ugraditi u proizvod da bi omogućio korisnicima obavljanje njihovih
zadataka ili posebno zanimljivu mogućnost programa (eng. feature) kao skup logički povezanih
funkcionalnih zahtjeva koje korisniku omogućuju ispunjavanje poslovnih zahtjeva.
Primjer funkcionalnih zahtjeva za sustav subvencionirane prehrane:

 Nakon što se studentu jednom zavedu prava na matičnoj ustanovi, sustav mora proslijediti
informaciju svim pružateljima usluga, odnosno omogućiti distribuirane upite
 Sustav treba dnevno kreirati izvještaje sa statistikom prehrane po pružateljima usluge i vrsti
obroka.

Nefunkcionalni zahtjevi
Za razliku od funkcionalnih zahtjevi koji opisuju što sustav radi, nefunkcionalni zahtjevi odgovaraju na
pitanje kako (sustav mora raditi). Nefunkcionalni zahtjevi su posljedica standarda, pravila i ugovora
kojih se proizvod mora pridržavati, opisi vanjskih sučelja, zahtjevi na performanse, ograničenja na
dizajn i implementaciju te svojstva kvalitete (preciziraju opis proizvoda navodeći karakteristike
proizvoda u različitim dimenzija koja su važne ili korisniku, ili graditelju).
U sustavu prehrane nefunkcionalni zahtjevi primjerice mogu biti vezani za oblik korisničke kartice,
protokol povezivanja,obvezu fiskalizacije itd.

2.2 Primjeri loše definiranih zahtjeva


Zahtjevi ne smiju sadržavati detalje dizajna ili implementacije, ali moraju biti dobro definirani kako bi
se izbjegli nepotpuni, neostvarivi ili neodređeni zahtjevi za koje nije moguće utvrditi da li je ugrađen
ispravno u sustav ili ne. U nastavku je izloženo nekoliko primjera loše definiranih zahtjeva.

Nepotpuni zahtjev
„Proizvod će dostaviti statusnu poruku u redovitim statusnim intervalima ne manjim od 60 sekundi“.
Ovo je primjer nepotpunog zahtjeva, jer nije jasno što je statusna poruka i pod kojim uvjetima će biti
dostavljena, koliko dugo ostaje vidljiva, koliko dosljedni intervali moraju biti te koji dio proizvoda će
dostaviti poruku te nije moguće odrediti.

11
Ovaj zahtjev treba preciznije i detaljnije definirati, primjerice sljedećim rečenicama:

 Modul za nadzor će ispisivati statusnu poruku u za to određeni dio sučelja.


 Poruka će se ažurirati svakih 60 sekundi (plus minus 10 sekundi) nakon što započne izvođenje
pozadinskog zadatka i bit će vidljiva cijelo vrijeme.
 Ukoliko se pozadinski zadatak izvodi normalno, modul za nadzor će ispisivati postotak
obavljenog posla.
 Modul za nadzor će ispisati "Zadatak obavljen." nakon što se zadatak obavi.
 Modul će ispisati poruku o pogrešci ukoliko dođe do zastoja u izvođenju.
 Problem je rastavljen u više zahtjeva jer će svaki zahtijevati posebno testiranje.
 Ukoliko je više zahtjeva grupirano u jedan lakše je previdjeti neki od njih tijekom izrade ili
testiranja.
Potrebno je naglasiti da u zahtjevu nije detaljno opisano kako će se poruka i gdje ispisivati. To će biti
odlučeno tijekom dizajna.

Neostvarivi zahtjev
„Proizvod će se trenutno prebaciti između ispisivanja i skrivanja znakova koji se ne mogu tiskati“. Ovo
je primjer neostvarivog zahtjeva jer računala ne mogu ništa napraviti trenutno i nije jasno da li
programska podrška sama odlučuje kad će se prebaciti iz jednog stanja u drugo ili je to inicirano
akcijom korisnika. Također, nije jasno određeno na koji dio teksta će se primijeniti promjena prikaza
(da li samo označeni tekst, cijeli dokument ili nešto treće) te što su znakovi koji se ne mogu tiskati
(skriveni znakovi, posebne oznake, kontrolni znakovi, …).
Bolji zahtjev, kojim se može ostvariti bi bio npr. „Korisnik će posebno dogovorenom akcijom, odabrati
da li će se HTML oznake u trenutno otvorenom dokumentu prikazivati ili neće." Na ovaj način bi bilo
jasno da je riječ o HTML oznakama te da korisnik mora obaviti nekakvu akciju, ali nije točno
navedeno kakvu (npr. kombinacija tipki), što se prepušta dizajnerima programa.

Neodređeni zahtjev
„Parser će brzo generirati izvješće o pogreškama HTML oznaka, koje omogućava brzi ispravak
pogrešaka kada program koriste početnici u HTML-u.“. Ovo je primjer neodređenog zahtjeva, jer se
koriste pojmovi koji nisu egzaktno mjerljivi ili se mogu različito tumačiti od osobe do osobe. Primjeri
takvih pojmova su riječ "brzo" koja nije određena nekom mjernom jedinicom, „generirati izvješće“ za
koje nije definirano kada se generira i što tvori izvješće i to čini zahtjev nekompletnim. Također
postavlja se pitanje kako bi se za ovaj zahtjev ovjerilo da li je zahtjev ispravno ugrađen u sustav -
pronašlo nekoga tko se smatra početnikom u HTML-u i zatim vidjeti kako brzo će, uz pomoć izvješća,
ispraviti pogreške?!
Bolja verzija zahtjeva bi bila: „Nakon što je HTML analizator obradio datoteku generirat će izvješće
koje sadrži broj linije i tekst pronađenih HTML pogrešaka, te opis svake pogreške. Ukoliko nema
pogrešaka prilikom analize, neće se generirati izvješće.“

2.3 Prioriteti zahtjeva


Za sve vrste zahtjeva potrebno je postaviti prioritete pojedinih zahtjeva. Neovisno o ljestvici koja će
se koristiti (npr. 1-5, 1-10, …) prioriteti zahtjeva mogu se podijeliti u tri vrste: nužno, poželjno i

12
neobvezno svojstvo odgovarajući na pitanje koliko neko svojstvo (proizašlo iz zahtjeva) treba
korisniku.
Nužno svojstvo (eng. must have) predstavlja ono što korisnik stvarno mora imati da bi mogao početi
koristiti neki sustav. Po definiciji, ako sustav ne uključuje nužne zahtjeve, taj sustav ne može ispuniti
svoju svrhu. Postoji tendencija da se previše zahtjeva proglasi nužnim! Stoga treba testirati svaki
zahtjev koji se smatra nužnim i probati ga rangirati. Ako se zahtjev može rangirati onda nije obvezan.
Potpuno obvezni zahtjevi se ne mogu rangirati jer su nužni za prvu verziju sustava.
Poželjno svojstvo (eng. should have) je funkcija sustava koju korisnik želi imati na kraju. Ranije verzije
sustava mogu pružiti (ne potpunu) funkcionalnost bez tih zahtjeva. Poželjni zahtjevi mogu i trebaju
biti rangirani.
Neobvezna svojstva (eng. could have) su mogućnosti sustava nastale iz proizvoljnih zahtjeva
pojedinih korisnika bez kojih sustav može raditi. Primjer neobveznog svojstva bi npr. bilo ostvarivanje
ostalih prava iz studentskog standarda iz primjera zahtjeva krajnjih korisnika. Iako bi ih lijepo bilo
imati, to nisu pravi zahtjevi. Ovi zahtjevi također mogu biti rangirani.
Po potrebi može se navesti i nepotrebna svojstva (eng. won’t have this time but potentially later), jer
se time dodatno precizira opseg projekta.

2.4 Dokumentiranje analize (zahtjeva)


Dokumentiranje analize vrši se kroz definiciju zahtjeva (eng. requirements definition) i specifikaciju
zahtjeva (eng. requirements specification).
Definicija zahtjeva je izjava o stanju i ograničenjima sustava te potrebama unutar narativnog
dokumenta namijenjenog korisniku ili pisana od strane korisnika. U definiciji zahtjeva navedeni su
poslovni i korisnički zahtjevi te njihovi prioriteti, uočeni problemi, ključne pretpostavke i preporuke
rješenja.
Specifikacija zahtjeva je strukturirani dokument s detaljnim opisom očekivanog ponašanja sustava.
Često se još naziva i funkcionalnom specifikacijom te je namijenjena ugovarateljima i izvoditeljima
razvoja. Specifikacija zahtjeva predstavlja ugradbeno nezavisan pogled na sustav i sadrži funkcionalne
i nefunkcionalne zahtjevi te njihovi prioritete, model organizacijske strukture (npr. strukturni
dijagrami), opis protoka dokumenata (npr. dijagrami toka), model procesa (npr. dijagram toka
podataka) i konceptualni model podataka (npr. dijagram entiteti-veze).
Primjer sadržaja dokumenta specifikacije zahtjeva izvedenog iz IEEE predloška prikazan je na sljedećoj
slici.

13
1. Uvod 4. Mogućnosti proizvoda
1.1 Namjena 4.x Svojstvo X
1.2 Konvencije dokumenta 4.x.1 Opis i prioriteti
1.3 Upute za čitanje dokumenta 4.x.2 Slijed pobuda/odziv
1.4 Opseg proizvoda 4.x.3 Funkcijski zahtjevi
1.5 Reference 5. Ostali nefunkcionalni zahtjevi
2. Sveobuhvatni pregled 5.1 Zahtjevi za performansama sustava
2.1 Kontekst proizvoda 5.2 Zahtjevi za sigurnošću korisnika
2.2 Funkcije proizvoda 5.3 Zahtjevi za sigurnošću podataka
2.3 Kategorije korisnika i svojstva 5.4 Kvaliteta programske podrške
2.4 Okružje u kojem se izvodi proizvod 5.5 Poslovna pravila
2.5 Ograničenja dizajna i ugradnje 5.6 Korisnička dokumentacija
2.6 Pretpostavke i ovisnosti 6. Ostali zahtjevi
3. Zahtjevi za sučeljem Dodatak A: Rječnik
3.1 Korisničko sučelje Dodatak B: Modeli i dijagrami
3.2 Hardversko sučelje Dodatak C: Lista nedovršenih/neodređenih
3.3 Softversko sučelje zahtjeva
3.4 Komunikacijsko sučelje

Slika 6. Sadržaj dokumenta specifikacije zahtjeva

2.5 Zadaci
Zadatak 1. Što je specifikacija zahtjeva?
Zadatak 2. Na koje pitanje odgovaraju poslovni, na koje funkcionalni, a na koje nefunkcionalni
zahtjevi?
Zadatak 3. Preoblikujte loše definirane u dobro definirane korisničke zahtjeve:
a) "Sustav treba javiti svim korisnicima ako dođe do pogreške."
b) "Sustav treba odmah prekinuti s radom ako dođe do sumnjive situacije korištenja."
c) "Sustav se treba prilagoditi hendikepiranim korisnicima."

14
3 Projekt izrade aplikacije
Izrada aplikacije nije samo kodiranje. Prije nego projektni tim započne pisanje programskog koda
aplikacije potrebno je izvršiti niz radnji, kao na primjer prikupiti korisničke zahtjeve, pobrojati
projektne zadatke, procijeniti trajanje projekta i tako dalje. Tijekom implementacije potrebno je
voditi računa o odstupanju od plana projekta, promjenama zahtjeva i slično, a poslije napisati
korisničku i tekničku dokumentaciju, organiziriati isporuku, plan održavanja te zatvoriti projekt.

3.1 Osnove upravljanja projektom


Neke od definicija projekta su:
 Projekt je vremenski određeno nastojanje da se proizvede jedinstven proizvod, usluga ili
rezultat.
 Projekt je niz jedinstvenih, složenih i povezanih aktivnosti koje imaju određeni cilj i koji se
mora postići u zadanom vremenskom roku, u okviru zadanog proračuna i u skladu sa
specifikacijama.
Osnovna svojstva projekta su vremenska određenost i jedinstvenost. Vremenska određenost
podrazumijeva da projekt mora imati jasno određen početak i kraj. Projekti mogu biti kratki ili trajati
godinama, ali će svakako završiti. Projekt završava u trenutku kada postane jasno da su ciljevi
projekta dostignuti ili kada se zaključi da ciljevi projekta ne mogu ili neće biti dostignuti.
Jedinstvenost znači da se projekt odnosi na rad na nečemu što prije nije postojalo i što se razlikuje od
rezultata nastalih sličnim projektima. Ipak, prisustvo određenih ponavljajućih elementa unutar
različitih projekata ne znači da projekt nije jedinstven.
Upravljanje ili rukovođenje projektom (eng. project management) je primjena znanja, vještina, alata
i tehnika u projektnim aktivnostima da bi se ispunili projektni zahtjevi.
Osoba koja upravlja projektom i odgovorna je za postizanje ciljeva projekta je voditelj projekta (eng.
project manager). Upravljanje projektom obuhvaća:

 Planiranje
o Utvrđivanje zahtjeva
o Postavljanje jasnih i ostvarivih ciljeva
o Uravnoteženje zahtjeva na kvalitetu, doseg, vrijeme i trošak
o Prilagodbu interesima i očekivanjima zainteresiranih strana – dionika (eng.
stakeholders)
 Organiziranje
o Formiranje projektnog tima
o Koordiniranje sudionika na projektu
o Raspoređivanje obaveza
o Tko, što i kada treba napraviti
 Usmjeravanje
o Nadgledanje, omogućavanje izvršenja
 Kontroliranje
o Provjera učinka i rezultata

15
Voditelj projekta odgovoran je i za identificiranje i upravljanje interesnim sudionicima projekta -
dionicima projekta (eng. stakeholders). Dionici projekta (uloge) su pojedinci ili organizacije koje su
aktivno uključene u projekt ili rezultati projekta imaju utjecaj na njih.
 Korisnik, Korisnik usluga, Klijent (eng. User, Customer, Client) - osoba ili grupa, naručitelj ili
krajnji korisnik
 Sponzor projekta (eng. project sponsor) - osoba ili grupa koja osigurava resurse za projekt
 Voditelj projekta (eng. project manager) - osoba imenovana kako bi ostvarila ciljeve projekta
 Projektna ekipa - Svi članovi ekipe, uključujući voditelja, a u nekim slučajevima i sponzora
o sistem analitičar – određivanje potreba, specifikacija zahtjeva i dizajna
o projektant / arhitekt – uspostava osnovne arhitekture
o razvojnik (developer, builder) – kodiranje, testiranje
o administrator baze podataka – administriranje sustava za upravljanje bazama
podataka
o sistem inženjer / sistem administrator – administriranje OS i mreže
Osim ljudskih resursa (osoba), ostali su resursi projekta su oprema, usluge, materijal, budžet ili druga
sredstva, koje je također potrebno identificirati i njima tijekom projekta upravljati.

3.2 Integrirana razvojna okruženja i kontrola programskog koda


Integrirano razvojno okruženje (eng. Integrated Development Environment - IDE) je okruženje tj.
okolina za programiranje koje integrira:

 uređivač izvornog koda (source code editor)


 kompilator (compiler) i/ili interpreter (interpreter)
 alat za izradu grafičkog korisničkog sučelja (GUI)
 sustav za ispravljanje pogrešaka (debugger)
 pomagalo za kontrolu verzija (version control system)
 alate za automatsku izgradnju (build-automation tools)
 alate za objektno orijentirano programiranje (npr. Class Browser)
 alate za testiranje
 itd.
Prva razvojna okruženja bila su namijenjena jednom programskom jeziku npr. Visual Basic, Delphi,
Jbuilder itd. Moderna okruženja podržavaju više jezika, na primjer:
 Visual Studio : C++, C#, Visual Basic.NET, ASP.NET
 Eclipse - Eclipse (na slici) je primarno okruženje za Javu, ali sadrži umetke (plugins) za C/C++,
Python, Perl, Ruby, Fortran, Cobol i dr.

Integriranja razvojna okruženja olakšavaju pisanje koda i njegovo prevođenje (kompiliranje) i


povćavaju učinkovitost u programiranju u odnosu na parcijalne alate. Osnovne funkcionalnosti i
namjene su:

 Označavanje sintakse (eng. syntax highlighting)


 Označavanje pogrešaka (eng. error highlighting)
 Pristup dokumentaciji (eng. documentation access)

16
 Kretanje kodom (eng. code navigation)
 Generiranje koda pomoću predložaka koda (eng. code generation through code
templates)
 Podrška za preradu, refaktoriranje (eng. refactoring)
 Analiza programskog koda na različitim razinama
Funkionalnosti razvojnog okruženja korištenog u okviru predmeta RPPP opisano je u uputama za
laboratorijske vježbe.

3.3 Plan projekta


Projekt se dokumentira tijekom različitih projektih faza. U početnoj fazi, važni dokumenti su povelja
projekta (eng. Project Charter) i plan projekta (eng. Project plan). Povelja projekta je dokument kojim
pokretač projekta ili sponzor odobrava projekt i ovlašćuje voditelja za primjenu organizacijskih
resursa u provedbi projekta.
Plan projekta (Plan upravljanja softverskim projektom) je dokument koji opisuje sveukupnu
organizaciju projekta. Format i sadržaj plana projekta specificiran je u normi IEEE Standard for
Software Project Management Plans 1058-1998.
Plan projekta uobičajeno sadrži sljedeće elemente:
 Svrha, cilj i doseg projekta
o Opis proizvoda bez ulaženja u detalje ugradnje
o Definiciju zahtjeva i ograničenja
 Procjena projekta
o Procjena cijene, napora i vremena potrebnog za provedbu projekta.
o Procjena tehnika realizacije i njihovih rezultata
o Procjena resursa projekta
 Raspored projekta
o Popis projektnih aktivnosti i zadataka
o Mrežni plan – međuzavisnot aktivnosti i zadataka
o Kontrolne točke (eng. milestones)
o Vremenski raspored (eng. schedule) dijelova plana
 Organizacija projekta
o Dionici
o Struktura tima (uloge i odgovornosti članova)
o Matrica odgovornosti i plan komunikacije
 Mehanizmi praćenja i kontrole
o Upravljanje promjenama
o Upravljanje kvalitetom
o …
U praksi plan projekta može naravno imati i dodatne dijelove ili dodatke kao što su plan upravljanja
nabavom, plan osiguranja kvalitete, plan upravljanja komunikacijom i drugi, a što nadilazi potrebe i
doseg ovog teksta.

17
3.3.1 Zadaci projekta
Osnovni gradbeni elementi svakog projekta su zadaci. Zadaci predstavljaju posao koji se mora obaviti
da bi se postigao cilj projekta te opisuju tijek događaja, trajanja i zahtjeva za resursima na projektu.
Razlikujemo tipove zadataka:
 Primitivni zadaci - zadaci koji se dekompozicijom ne mogu podijeliti na jednostavnije zadatke
 Skupni zadaci (eng. summary tasks) - zbrajaju trajanje i troškove primitivnih zadataka.
Trajanje, datum te izračunate vrijednosti se automatski izvode iz skupa primitivnih zadataka.
 Prekretnice ili miljokazi (eng. milestones) - ključni događaj ili krajnji rok odnosno cilj koji
treba postići. Miljokazi služe za provjeru stupnja dovršenosti drugih zadataka. Trajanje im je 0
(sati, dana). Pomak ključnog događaja ima za posljedicu vremenski preraspored.
Zadaci projekta mogu se organizirati hijerarhijski, grupiranjem u radne pakete, aktivnosti i faze.
Gledano s vrha prema dolje, struktura zadataka naziva se se hijerarhijskom raščlambom posla (eng.
work breakdown structure, skraćeno WBS), koja se može se prikazati tablično ili grafički.
Dva su pristupa razvoju hijerarhije posla:

 Planiranje s vrha prema dolje (eng. top-down) - pristup od općeg prema specifičnom.
Identificira glavne faze i rezultate projekta prije dodavanja zadataka potrebnih za završetak
tih faza. Složeni projekti mogu imati nekoliko slojeva razrade do zadataka na dnu hijerarhije.
 Planiranje s dna prema dolje (eng. bottom-up) - pristup od specifičnog prema općem.
Identificira što više zadataka najnižeg sloja prije grupiranja u više razine, što se ponavlja do
vrha hijerarhije.
Za procijeniti trajanje cijelog projekta, potrebno je procijeniti trajanje pojednih zadatka projekta, tj.
odrediti očekivanu količina vremena za završetak zadataka. Trajanje se može defiirati u minutama
(m), satima (h), danima (d), tjednima (w) ili mjesecima (mo).

Slika 7. Primjer hijerarhije zadataka projekta

Projekt može zahtijevati da zadaci budu napravljeni u određenom redoslijedu (iza jednog slijedi drugi
zadatak). Zadatak sljedbenik (eng. successor) može biti izvršen ako je dovršen prethodnik (eng.
predecessor). Bilo koji zadatak može biti prethodnik jednom ili više sljedbenika.
Odnosi, to jest zavisnosti između zadataka mogu biti:

18
 Finish-to-start (FS) –završetak prethodnika određuje početak sljedbenika
o Npr. instalacija softvera može započeti po nabavci hardvera
 Start-to-start (SS) – početak prethodnika određuje početak sljedbenika
o Npr. unos podataka započinje s početkom njihovog prikupljanja
 Finish-to-finish (FF) – završetak prethodnika određuje završetak sljedbenika
o Npr. provjera instalacije završava s dovršenjem ožičenja
 Start-to-finish (SF) - početak prethodnika određuje završetak sljedbenika
o Npr. stari sustav prestaje s radom kad započne rad novog
Ukoilko nije eksplicitno definirano, podrazumijeva se da je zavisnost dva povezana zadatka FS.

Slika 8. Zavisnost zadataka

Zavisnost zadataka prikazujej se mrežnim dijagramom (eng. project network diagram). Jedna od
metoda za određivanje trajanja projekta je metoda kritičnog puta (eng. critical path method).
Sumiraju se trajanja zadataka po svim putanjama u mreži zadataka te se određuje najduža koju
zovemo kritičnim putom. Kritični put je niz zadataka koji moraju završiti na vrijeme da bi projekt
završio u planiranom roku. Svaki zadatak na kritičnom putu je kritični zadatak, čije kašnjenje uzrokuje
kašnjenje projekta. Sljedeća slika prikazuje mrežni dijagram zadataka (pobrojani krugovi s oznakom
trajanja izvan kruga) s označenim kritičnim putom u trajanju 22 vremenske jedinice (suma svih
trajanja na tom putu). Kašnjenje početka ili dulje trajanje bilo kojeg kritičnog zadatka odgodilo bi
završetak projekta.

Slika 9. Primjer mrežnog dijagrama

U praksi se radi preglednosti češće koristi Ganttov dijagram, kao u sljedećem primjeru. Tamno su
označene grupe zadataka (aktivnosti i faze), crveno zadaci na kritičnom putu, a plavo ostali zadaci.

19
Slika 10. Primjer Gantograma

Više o metodi kritičnog puta i drugim metodama može se naučiti na predmetu Upravljanje
projektima .

3.3.2 Resursi projekta


Resursi (eng. resouces) ili sredstva su ljudi, oprema i materijalna ili financijska sredstva potrebni za
obavljanje zadataka. Nama zanimljive vrste resursa su:
 Resursi rada (eng. work resources) - ljudi (ograničeno vrijeme rada) i oprema (neograničeno
vrijeme rada)
 Resursi materijala (eng. material resources) - potrošni materijal koji predstavlja projektni
utržak i daje informaciju o brzini konzumiranja resursa
Dva važna pogleda na resurse su:

 raspoloživost - u koje vrijeme određeni resurs može raditi na zadatku i koliko posla može
obaviti i
 trošak – koliko novca će biti potrošeno na resurse.
Kod određivanja rasporeda projekta potrebno je raspodijeliti resurse imajući na umu dostupnost i
trošak resursa. Maksimalne jedinice (eng. max. units) prikazuju vrijednosti raspoloživosti resursa u
postocima, npr. 100% predstavlja jednog čovjeka punog radnog vremena, 300% predstavlja tri
čovjeka punog radnog vremena. Ako je projektni tim formiran, kao resurse možemo navesti članove
hjihovim imenima (npr. Pero Perić za razliku od Sistem analitičar u primjeru Slika 7).
Osnova po kojoj je resursu dodijeljeno obavljanje posla je kalendar resursa. Za pojedinačnu
prilagodbu kalendara (standardnog) uvažavaju se radni i neradni dani resursa. Na primjer, ako
kalendar evidentira radno vrijeme samo četvrtkom i petkom 13-17 sati, 100% raspoloživosti nekog
resursa ne znači 40 satno tjedno radno vrijeme, nego 8 sati rada tjedno.
Dodjelom resursa zadatku, dodjeljuje mu se određeni posao. Treba razlikovati posao (work) od
trajanja (duration). Posao je stvarni rad i odnosi se na zadatak - stvarni rad potreban za završetak
20
zadatka, dodijeljeni zadatak - stvarni rad nekog resursa na nekom zadatku i resurs - ukupni rad neke
osobe na svim zadacima. Trajanje je omjer posla i jedinica posla (eng. Duration = Work / Units) i ovisi
o kalendaru rada.
Metoda planiranja koja se koristi kod ažuriranja resursa zadatka jest raspoređivanje temeljem napora
(eng. effort-driven scheduling). Odnosi se samo na zadatke koji su automatski raspoređeni (eng.
Auto Schedule). Trajanje je obrnuto količini resursa.
Ako su resursi prekapacitirani, problem se rješava nekom od sljedećih metoda:
 Pomaknuti rokove (trajanje)
 Dodati dodatne resurse
 Produžiti radno vrijeme
 Povećati jedinice posla
 Smanjiti količinu posla
 …

3.3.3 Upravljanje konfiguracijom


Upravljanje konfiguracijom (eng. configuration management) igra ključnu ulogu u razvoju
tehnologije, pogotovo kada na projektu radi više ljudi. Konfiguracija je imenovani skup
konfiguracijskih elemenata u određenoj točki životnog ciklusa. Element konfiguracije je agregacija
hardvera i/ili softvera koja se tretira kao jedinka u procesu upravljanja konfiguracijom.
Sa stanovišta ovog teksta ključni elementi konfiguracije su oni koji se koriste u procesu razvoja i
održavanja programske opreme, dakle izvorni programski kod, korištene programske komponente,
model podataka, specifikacija testova, te razni oblici projektne, tehničke i korisničke dokumentacije.
Upravljanje softverskom konfiguracijom (eng. software configuration management – SCM ili S/W
CM) obuhvaća postupke i tehnike upostave osnovice (eng. baseline) konfiguracije softvera i kontrolu
revizija (verzioniranje) sofvera.
Osnovica predstavlja specifikaciju proizvoda koja je formalno provjerena i usvojena, te koja služi kao
temelj razvoja i koja se mijenja samo kroz formalnu proceduru kontrole promjena. U odnosu na nju
može postojati više verzija konfiguracije:

 verzija, inačica (version) – određeno izdanje (eng. issue, release) proizvoda


 objava, isporuka (release) – originalna verzija u primjeni, npr. zadnja v2.0
 revizija (revision) – ona koja se koristi umjesto originalne, podrazumijeva izmjene u
određenim vremenskim intervalima, npr. V1.2
 varijanta (variant) – alternativa originalu (druga hardverska platforma, različita jezična
prilagodba), živi paralelno s njim, npr. v1.1.2.1

21
Slika 11. Primjer oznaka verzija konfiguracije

Verzioniranje aplikacija
Važna tehnika upravljanja sofverskom konfiguracijom je kontrola verzija (eng. version control)
izvornog koda, ali i drugih datoteka (podsjećamo, aplikaciju čine programski kod, podaci i
dokumenacija). Kada na projektu radi više osoba, nekontrolirana istovremena izmjena istih
elemenata konfiguracije može dovesti do nekonzistentnosti pojedinih dijelova. Stoga se koriste
sustavi za kontrolu verzija koji, između ostalog, omogućavaju kontrolu pristupa zaključavanjem
dateoteka. Kao primjer takvih sustava mogu poslužiti Apache Subversion (s takozvanim SVN
klijentima) i Microsoft Team Foundation Server (TFS).
Mogućnosti sustava kontrole verzija:

 baza projekata (eng. project database) ili riznica (eng. repository) - pohranjuje sve relevantne
objekte konfiguracije
 verzioniranje - razlikovanje pohranjenih inačica objekata konfiguracije
 pomagalo za izradu (eng. make facility) - prikuplja relevantne objekte i proizvodi određenu
verziju softvera
 praćenje problema (eng. issue tracking), praćenje pogreški (eng. bug tracking) - bilježenje i
praćenje statusa tema koje se odnose na pojedine objekte konfiguracije
Na razvojnoj platformi Microsoft .NET, verzija objektne datoteke (eng. assembly) određena je s četiri
broja (definira se u razvojnoj okolini Visual Studio jedno od svojstava projekta):
<major version>.<minor version>.<build number>.<revision>

 major version - mijenja se prilikom znatne promjene u (npr. kod redizajna koji prekida
vertikalnu kompatibilnost sa starijim verzijama)
 minor version - mijenja se prilikom znatne promjene, ali uz zadržavanje kompatibilnosti s
prethodnim verzijama
 build number - predstavlja ponovno prevođenje istog koda (npr. prilikom promjene
platforme, procesora i slično)
 revision - primjenjuje se npr. prilikom izdavanja sigurnosnih zakrpa i sličnih manjih promjena
Prednosti i nedostatci automatskog odnosno ručnog označavanja verzija:

Automatsko označavanje Ručno označavanje


 potpuna kontrola nad brojevima
Prednosti  eliminacija ručnog rada (npr.
verzije
pisanja i izvedbe skripti)

22
 ne postoje dvije inačice s istom  moguća je sinkronizacija između
oznakom verzije pojedinih komponenti i
verzije cijelog sustava

 verzioniranje se mora raditi


Nedostatci  oznaka elementa ne podudara se
ručno
s oznakom cijelog sustava
 moguće je napraviti više različitih
 novi brojevi ovise o danu i
objektnih datoteka s istim
vremenu prevođenja
oznakama
 verzija se mijenja pri svakom
prevođenju, neovisno o tome
jesu li se dogodile promjene ili ne

Timski rad i kontrola verzija


Sljedeća slika opisuje timski rad na predmetu RPPP. Voditelj (ili neki drugi član ekipe) kreira prvu
verziju progamskog rješenja te ju prijavljuje (check in) u sustav za upravljanje verzijama. Članovi ekipe
preuzimaju rješenje otvaranjem u sustavu. Jednom preuzeto rješenje mogu osvježiti povlačenjem
zadnje verzije iz sustava (get latest) na lokalno računalo. Tijekom rada na nekoj datoteci, datoteka
bude zaključana drugim članovima (check out), a član koji ju je zaključao po završetku rada promjene
prijavljuje u sustav (check in) čime se ažurira zadnja verzia rješenja.
Koordinator predmeta za to rješenje kreira proceduru za automatiziranu kompilaciju (build) koja će
biti pokrenuta prije primopredaje rješenja. U slučaju pogreške članovi tima budu obaviješteni e-
poštom kako bi ispravili nedostatke. Članovi ekipe mogu naravno i sami pokretati gradnje kako bi
provjerili ispravnost prije predaje rješenja.

23
Slika 12. Primjer timske kontrole verzija

Praktični aspekti verzioniranja detaljnije su opisani u uputama za vježbe predmeta RPPP.

3.4 Zadaci
Zadatak 1. Što je projekt? Što je plan projekta?
Zadatak 2. Ako u planu projekta kalendar evidentira radno vrijeme nekog suradnika od
ponedjeljka do subote na pola radnog vremena (od 8 do 12 sati), što znači 100%
raspoloživosti tog suradnika/resursa?
Zadatak 3. Dijelovi razvojne okoline mogu biti različito razmješteni, mogu plutati ili biti usidreni.
a) Razmjestiti pojedine elemente
b) Pokazati/sakriti neke dijelove
c) Provjeriti dijelove izbornika View
d) Provjeriti postavke u Tools / Options
e) Resetirati razvojnu okolinu na tvorničke postavke.
Zadatak 4. Isprobati osnovne funkcije rada u timskom okruženju (TFS), dodavanjem projekta i
dodavanjem/izmjenom izvornih datoteka uz zaključavanje i otključavanje. Kako se rješava
problem istovremenog uređivanja datoteka?

24
4 Osnove razvojnog okvira Microsoft .NET i programskog jezika C#
Microsoft .NET Framework
Microsoft .NET Framework je okvir za razvoj softvera, nastao s idejom iste osnovice za izradu lokalnih
(desktop), internetskih (web) aplikacija i mobilnih (windows phone) aplikacija. Uključuje knjižnice za
razvoj neovisno o jeziku. Podržani su jezici Visual Basic .NET, Visual C++ .NET, C# i drugi. Sadrži
zajedničku knjižnicu osnovnih razreda Base Class Library (BCL) ili Framework Class Library (FCL),
zajedničke opće tipove podataka Common Type System (CTS), zajedničku specifikaciju jezika Common
Language Specification (CLS) podskup CTS-a zajednički za sve .NET jezike te zajednički pogon
programa - Common Language Runtime (CLR). Zamišljen je kao neovisan je o platformi, ali postoji
samo na operacijskim sustavima tvrtke Microsoft.

Jezik C#
Jezik C# je općenamjenski programski jezik razvijen od Microsofta (Anders Hejlsberg et al.) Objektno
je usmjeren (eng. object-oriented), vođen događajima (eng. event-driven), otvoren mreži (eng.
network-aware) i vizualan (eng. visual) to jest sadrži interaktivno sučelje razvojne okoline i aplikacija.
Prevođenje C#-programa odvija se u sljedećim koracima:

 C# prevoditelj prevodi C# izvorni kod (.cs datoteke) u poseban međujezik MSIL (eng. MS
Intermediate Language - izvodi se u virtualnom stroju, a ne izravno na procesoru računala) i
stvara se asemblij (eng. assembly - skup prevedenih razreda)
 Pokreće se izvršna datoteka s kodom
 Učitavaju se potrebne biblioteke (eng. dynamic link libraries - DLLs)
 Izvršava se funkcija _CorExeMain (entry point) koju je umetnuo kompajler
 Pri prvom pokretanju programa (ili ako ima promjena u kodu prilikom pokretanja) Just-In-
Time compiler (JIT) prevodi MSIL u strojni kod (eng. native code)
 Izvršava se strojni kod

4.1 Elementi jezika i struktura programa


Evo jednostavnog primjera programa (konzolne aplikacije) koji na ekran ispisuje "Pozdrav!".

Primjer Osnove\PozdravKonzola

/* korištenje knjižnice */
using System;

class Pozdrav //zaglavlje razreda


{ // tijelo razreda

static void Main(string[] args) // metoda Main


{ // blok naredbi
Console.WriteLine("Pozdrav!"); // naredba WriteLine
}
}

Program općenito sadrži jedan ili više razreda. Razred ili klasa (eng. class) sadrži članove – svojstva
(varijable) i metode, postupke (potprograme). Ne postoje slobodni potprogrami ni globalne varijable,

25
sve se zbiva unutar tijela razreda (za razliku od npr C++-a). Naredbe se kao u programskom jeziku C
odvajaju točka-zarezom (;), a blokovi programa označavaju se vitičastim zagradama {}.

4.1.1 Identifikatori i ključne riječi


Identifikatori su nazivi razreda, varijabli i slično koje određuje programer. Sastoje se od slova,
znamenki i podcrte, a prvi znak mora biti slovo npr. System, Pozdrav. Jezik C# razlikuje mala i velika
slova.
Ključne riječi (eng. keywords) su rezervirane riječi jezika, posebni identifikatori koji u jeziku imaju
unaprijed definirano značenje. Koriste se za definicije objekata, naredbe i direktive za prevođenje,
npr. using, class, static. Sve ključne riječi definirane su malim slovima.
abstract as base bool break
byte case catch char checked
class const continue decimal default
delegate do double else enum
event explicit extern false finally
fixed float for foreach get
goto if implicit in int
interface internal is lock long
namespace new null object operator
out override params private protected
public readonly ref return sbyte
sealed set short sizeof stackalloc
static string struct switch this
throw true try typeof uint
ulong unchecked unsafe ushort using
virtual void volatile while

4.1.2 Prostor imena


Izvorni kod je organiziran u prostore imena (eng. namespaces). C# program se može sastojati od više
datoteka, a svaka datoteka može uključivati jedan ili više prostora imena. Također, isti prostor imena
može se protezati (deklarirati) u više datoteka. Prostor imena sadrži definicije razreda, struktura,
sučelja, pobrojanih tipova, delegata i deklaraciju drugih prostora imena. System je prostor imena u
kojem su deklarirani svi ostali prostori imena i tipovi podataka u .NET Frameworku. Ako se u
programu eksplicitno ne definira prostor imena, C# kod je sadržan u globalnom imeniku (eng. global
namespace). Koncept prostora imena omogućuje postojanje istih imena u različitim prostorima
imena. Jedinstvenost imena tipova u prostoru imena i samog prostora imena osigurana je preko tzv.
potpunih imena (eng. fully qualified names).
Prva naredba iz prethodnog primjera definira korištenje knjižnice System (ključna riječ using).
using System;

Prostor imena System sadrži osnovne razrede za rad s tipovima podataka, sučeljima, događajima i
tako dalje. Razred Console se nalazi u prostoru imena System i njegovo potpuno ime je

26
System.Console, ali zbog navoda using System prilikom poziva postupka WriteLine nije potrebno
pisati: System.Console.WriteLine već samo Console.WriteLine.
Može se koristiti i zamjensko ime (eng. alias) – koristi se za definiranje skraćenih naziva i uklanjanje
neodređenosti kad u različitim imenicima postoje razredi istog imena.
using System; // navod imenika
using stdout = System.Console; // alias razreda
class Pozdrav {
static void Main(string[] args) {
System.Console.WriteLine("Kvalificirano!");
Console.WriteLine("Izravno!"); // zbog navoda using System
stdout.WriteLine("Zamjenski!"); // zbog aliasa using stdout =
}
}

Neki .NET Framework prostori imena korišteni u narednim primjerima su:

Namespace Opis
System Osnovni razredi i tipovi podataka (npr. int, char, float)
System.Data Razredi koji čine ADO.NET, koji se koristi za pristup bazama
podataka i rukovanje podacima.
System.Drawing Razredi za crtanje i grafiku.
System.IO Čitanje i pisanje podataka, npr. u datotekama.
System.Threading Simultano izvođenje programa, višenitnost (multithreading).
System.Windows.Forms Razredi za kreiranje grafičkog sučelja
System.Xml Razredi za obradu XML datoteka.
System.String Razredi za rad sa nizovima znakova (string).
System.Char Razredi za rad sa znakovnim tipom podataka.
System.Collection Razredi za rad s kolekcijama (listama).
System.Collections.Generic Razredi za rad s generičkim (tipiziranim) kolekcijama.

4.1.3 Postupak Main


Svaka C# aplikacija mora imati definiran postupak Main u (samo) jednom od razreda koji predstavlja
ulaznu točku (eng. entry point) aplikacije.
public static void Main(string[] args)
Ključna riječ public (koja je u primjeru ispuštena jer se podrazumijeva) govori kompajleru da bilo koji
kod može pozvati taj postupak. Static označava da je Main globalni postupak, tj. da ne treba
instancirati razred da bi se postupak mogao pozvati. Void označava da postupak ne vraća ništa u
pozivajući kod, a string[] args predstavlja polje argumenata koje postupak može primiti (može biti i
prazno polje).

27
4.1.4 Razred
Naredba class Pozdrav označava korisnički definirani razred (uvriježeni naziv je i klasa). U ovom
primjeru razred Pozdrav sadrži samo postupak Main. Razred općenito sadrži svojstva (varijable) i
metode tj. postupke (potprograme), kao u sljedećem primjeru.

Primjer Osnove\ RazredTocka

class Tocka
{
public int cx;
public int cy; //javne varijable
public Tocka(int x, int y){ //konstruktor
cx = x; cy = y;
}
public int Kvadrant(){ //javni postupak
if (cx > 0){
if (cy > 0) return 1;
else return 4;
} else {
if (cy > 0) return 2;
else return 3;
}
}
}

Objekt tipa Tocka instancira se na način:


Tocka t = new Tocka(1,-2);
Javnim varijablama se pristupa sa: t.cx, t.cy a postupak se poziva s t.Kvadrant().
Console.WriteLine("Točka ({0},{1}) je u {2}.kvadrantu.",
t.cx, t.cy, t.Kvadrant());

Svaki prevedeni razred ima manifest - definiciju sučelja koju mogu koristiti drugi programi, uključujući
one napisane u nekom drugom jeziku.

4.1.5 Standardni ulaz i izlaz


Postupak System.Console.WriteLine ispisuje niz znakova (string) na standardni izlaz (konzolu). Taj je
postupak jedan od postupaka koji se koriste za čitanje i pisanje na standardni ulaz/izlaz (standard
I/O):
 public static string ReadLine(); – čita niz znakova
Console.ReadLine();
 public static int Read(); – čita znak
Console.Read();
 public static void Write(...); - piše argument(e) i ostaje u istom retku
Console.Write("Pozdrav! ");
 public static void WriteLine(...); - piše argument(e) i znak za skok u novi redak
Console.WriteLine("Pozdrav! ");

28
4.1.6 Komentari
U jeziku C# postoje dvije vrste komentara: retkovni komentar – tekst od znakova "//" do kraja retka i
blok komentari – tekst oblika /* … */ koji se može se rasprostirati u više redaka.

4.1.7 Konzolna i grafička aplikacija


Prethodni primjer Osnove\PozdravKonzola je primjer konzolne aplikacije. Grafička aplikacija
(windows forma) koja obavlja istu funkciju kao i konzolna aplikacija ima sljedeći kod:

Primjer Osnove\PozdravGraficka

using System;
class Pozdrav
{
static void Main(string[] args)
{
System.Windows.Forms.
MessageBox.Show("Pozdrav!");
}
}

Kod je gotovo isti (direktiva using System, kao i postupak Main), samo što se umjesto konzolne
naredbe za pisanje poziva Windows dijaloški okvir MessageBox.

4.1.8 Ponovno korištenje koda


Napisani kod moguće je ponovno iskoristiti (eng. reuse) u drugom programu ili unutar istog
programa.
Primjer korištenja metode drugog razreda unutar istog programa dan je kodom:

Primjer Osnove\Reuse - Program.cs

using System;
public class Pozdrav
{
public Pozdrav()
{
Poziv("iz lokalnog");
}
public static void
Poziv(string poruka)
{
System.Console.WriteLine("Pozdrav " + poruka);
}
}

29
Primjer Osnove\Reuse - Pozdrav.cs

using System;
class Proba
{
static void Main()
{
Console.WriteLine("Glavni!");
Pozdrav.Poziv("iz glavnog");
Pozdrav p = new Pozdrav();
}
}

Izvorni kod sadržan je u dvije datoteke pa se prevodi pozivom prevoditelja u operacijskom sustavu (s
tzv. command prompta)
csc.exe program.cs pozdrav.cs
Prevođenjem nastane datoteka program.exe. Pokretanjem se ispiše
Glavni!
Pozdrav iz glavnog
Pozdrav iz lokalnog
Pri tome se poziv iz glavnog programa obavlja eksplicitnim navodom razreda i metode Pozdrav.Poziv,
a pozdrav iz lokalnog se dogodi automatski pri stvaranju novog objekta razreda pozdrav naredbom
new, koji pokrene metodu Pozdrav razreda Pozdrav. Metoda imena jednakog imenu razreda
zove se konstruktor, o čemu će više riječi biti kasnije.
Sljedeći primjer prikazuje poziv postupka VisokiC unutar istog razreda:

Primjer Osnove\vozdra.cs

public class Vozdra {


static void Main() {
System.Console.WriteLine("Pozdrav!");
VisokiC ();
}
public static void VisokiC() {
System.Console.WriteLine("C#");
}
}

Program se prevodi pozivom prevoditelja u operacijskom sustavu


csc.exe vozdra.cs
Nastane program vozdra.exe. Njegovimi pokretanjem ispisuje se
Pozdrav!
C#
Postupak VisokiC je moguće ponovno iskoristiti i u drugom programu referenciranjem na izvršnu
datoteku nastalu prevođenjem navedenog koda (vozdra.exe).

30
Izvorni kod u datoteci proba.cs:
class Proba {
static void Main () {
Vozdra.VisokiC();
}
}

prevodi se pozivom operacijskog sustava


csc /reference:vozdra.exe proba.cs

Nastane izvršna datoteka proba.exe koja poziva metodu VisokiC sadržanu u programu vozdra.exe, a
koja ispisuje:
C#

4.2 Tipovi podataka


Većina objektno orijentiranih jezika razlikuje dva tipa podataka: primitivni tipovi (eng. primitive
types) koji su dio jezika i razredi (classes) tj. tipovi koje stvara programer. Primitivni tipovi podataka
su jednostavni tipovi kao na primjer cijeli i realni brojevi (int, float, double) i znakovi (char), dok su
razredi složeni i mogu sadržavati primitivne tipove podataka i druge razrede. Međutim, manipulacija
ovim dvoma različitim tipovima može uzrokovati probleme (na primjer ako želimo napraviti listu
primitivnih tipova i razreda - ne možemo je napraviti na isti način). Više o tome može se naći u
literaturi o jeziku C#. Stoga je u jeziku C# sve objekt, štoviše svaki objekt nasljeđuje razred
System.Object (nasljeđivanje je implicitno, nije ga potrebno posebno navoditi). Ovo ujedno znači i da
svaki tip podataka nasljeđuje postupke koje sadrži System.Object kao što su Equals, GetType, ToString
i ostali.
Primjer: 3.ToString(), "abc".ToUpper();
Nadalje, dvije osnovne kategorije tipova podataka u jeziku C# su: vrijednosti (eng. value types) i
reference (eng. reference types). Vrijednosti sadrže stvarne podatke i ne mogu biti nedefinirane, to
jest mogu imati vrijednost nula ali ne mogu biti tzv. null. Tipovi koji spadaju u vrijednosti su osnovni
tipovi podataka (int, float, double, char, ...), enum i struct. U reference spadaju razredi (class types),
sučelja (interface types), delegati (delegates), polja (array types) i znakovni nizovi (strings). Više o
pojedinim tipovima u sljedećim poglavljima.

31
Slika 13. Tipovi podataka

4.2.1 Osnovni tipovi


U tablici su navedeni ugrađeni tipovi podataka jezika C#. Budući da su vrlo slični onima jezika C,
nećemo ih detaljnije obrazlagati.

C# Type .Net Framework Broj bajtova Interval vrijednosti


sbyte (System) type
System.Sbyte 1 -128 do 127
short System.Int16 2 -32768 do 32767
int System.Int32 4 -2147483648 do 2147483647
long System.Int64 8 -9223372036854775808 do
byte System.Byte 1 9223372036854775807
0 do 255
ushort System.Uint16 2 0 do 65535
uint System.UInt32 4 0 do 4294967295
ulong System.Uint64 8 0 do 18446744073709551615
float System.Single 4 ±1.5 x 10-45 do ±3.4 x 1038
double System.Double 8 7 točnih
±5.0 x 10znamenki
-324
do ±1.7 x 10308
decimal System.Decimal 12 15 točnih
±1.0 x 10-28znamenki
do
±7.9 x 1028
char System.Char 2 28 točnihznak
Unicode znamenki
(16 bit)
bool System.Boolean 1|2 true ili false

Želimo li instancirati varijablu tipa int, možemo to učiniti koristeći dugi naziv tipa System.Int32 (32
označava broj bitova koje u memoriji zauzima varijabla navedenog tipa) ili skraćeni naziv tj. alias int
(obje deklaracije su jednako valjane). Na primjer:
System.Int32 broj = 42; ili int broj = 42;

32
4.2.2 Varijable i pridruživanje vrijednosti
Varijabla je imenovana memorijska lokacija određenog tipa i poznate veličine te vrijednosti koja se u
njoj nalazi a koja se može promijeniti. Prije uporabe (zadavanja ili korištenja vrijednosti) varijablu
treba deklarirati:
int broj1; // <type> <variable-name>;
Varijabli možemo pridružiti vrijednost operatorom = ovako:
broj1 = 45; // <variable-name> = <exp>

4.2.3 Vrijednosti i reference


Analogno drugim programskim jezicima, vrijednost predstavlja sadržaj varijable osnovnnog tipa.
Na primjer, nakon sljedećeg programskog odsječka
int val1 = 0;
int val2 = val1;
val2 = 5;

varijable val1 i val2 pri deklaraciji imaju vrijednost 0, nakon čega val2 poprimi vrijednost 5.
S druge strane, reference su slične onom što bismo u programskom jeziku C zvali pokazivačima.
Nakon izvođenja sljedećeg programskog odsječka

Primjer Osnove\VrijednostiReference

class Razred
{
public int Value = 0;
}
Razred ref1 = new Razred();
Razred ref2 = ref1;
ref2.Value = 123;

obje varijable ref1 i ref2 imaju istu vrijednost 123. Razlog tome je što operator new stvara novu
instancu razreda Razred i vraća pokazivač na nju, a taj pokazivač bude prepisan u varijablu ref2 pa
ref2 pokazuje gdje i ref1.

4.2.4 Razredi String i StringBuilder


Razred System.String (isto što i string u programskom jeziku C) predstavlja nepromjenjivi niz znakova.
Jednom postavljena vrijednost ne može se promijeniti. Stoga postupci na stringu stvaraju novi
(promijenjeni) string. Općenito, objekti koji nisu string imaju postupak ToString(), npr. 123.ToString()
vraća niz znakova "123".
Neki primjeri rada sa stringovima (Primjer Osnove\String)
 string a = "Primjer za String";
 Duljina: a.Length // 17
 Uklanjanje znakova: a.Remove(7,4) // PrimjerString
 Velika slova: a.ToUpper() // PRIMJER ZA STRING
 Mala slova: a.ToLower() // primjer za string
33
 Podniz: a.Substring(11) // String
 Uklanjanje vodećih i pratećih praznina a.Trim()
 Rastavljanje stringa na dijelove:
string[] stringovi = a.Split(new char[] { ' ' });
// za niz s početka daje {"Primjer", "za", "String"}
 Spajanje u string:
string.Join("-", "spoji", "u", 1, "string");
// vraća spoji-u-1-string

Da bismo izbjegli stvaranje novog stringa svaki put kada na originalnom radimo neku promjenu,
koristi se razred StringBuilder (System.Text.StringBuilder). Sve promjene obavljaju se na originalnom
nizu znakova. Na primjer:

 StringBuilder a= new StringBuilder(“Primjer za String”);


 Umetanje: a.Insert(7,'i') // Primjeri za String
 Dodavanje: a.Append("Builder") //Primjeri za StringBuilder
 …

4.2.5 Nabrajanje - Enum


Pobrojani tip podataka (enumerator) definira korisnički tip vrijednosti koji nasljeđuje System.Enum.
Sastoji se od imenovanih konstanti. Osnovni tip pobrojanog tipa je int.

Primjer Osnove\Enum

enum Dani
{
Ponedjeljak, Utorak, Srijeda, Cetvrtak, Petak, Subota, Nedjelja
}
int x = (int) Dani.Ponedjeljak; //dani počinju od 0
int y = (int) Dani.Utorak;
Console.WriteLine("Pon={0}", x);
Console.WriteLine("Uto={0}", y);

Ako želimo da dani idu od 1 nadalje:


enum DaniPoRedu
{
Ponedjeljak=1, Utorak, Srijeda, Cetvrtak, Petak, Subota, Nedjelja
}

4.2.6 Polja
U jeziku C# polja su objekti čiji je bazni razred System.Array. Indeks prvog člana je 0. U deklaraciji
uglate zagrade se navode uz tip a ne varijablu (int[] a za razliku od int a[]). Slijede primjeri
jednodimenzionalnih polja (vektora), a zatim i drugih vrsta polja.

34
Primjer Osnove\Polja

int[] a = new int[10]; // inicijalno vrijednosti 0


int[] b = new int[] { 1, 2, 3}; // dimenzija određena inicijalizacijom
int[] c = { 1, 2, 3}; // isto kao b

Kad ne bismo instancirali ili inicijalizirali polje, varijabla bi bila null (analogno neinicijaliziranom
pokazivaču u jeziku C).
Za duljinu polja moguće je pri deklaraciji navesti i varijablu:
int n = 3;
int[] d = new int[n];

Dohvat elementa na mjestu i: b[i]


Broj elemenata u polju: b.Length
Sljedeći programski odsječak ispisuje vrijednosti članova polja.
for (int i = 0; i < b.Length; i++)
{
Console.Write(b[i]);
}

Neki od statičkih postupaka razreda System.Array su:

 Sort – sortira polje ili dio polja, opcionalno uz vlastiti usporednik


 BinarySearch – binarno pretražuje polje i vraća indeks traženog elementa, inače
o bitovni komplement indeksa prvog elementa većeg od traženog, kad traženi nije u
polju a vrijednost traženog je manja od preostalih elemenata polja
o bitovni komplement broja elemenata polja, kad traženi nije u polju a vrijednost
traženog je veća od vrijednosti najvećeg u polju
 Copy – kopira čitavo polje ili dio polja polja u novo polje
 Reverse – obrće redoslijed članova polja
int[] z = new int[] { 3, 1, 2 };
System.Array.Sort(z);
Console.WriteLine("Tražim 3 na ind.{0}", BinarySearch(z, 3));
...
Array o = new int[3];
Array.Copy(z, o, 2);
Array.Reverse(o);

35
Primjer pravokutnih višedimenzionalnih polja :
int[,] a = new int[3,5]; //inicijalno vrijednosti 0

int[,] b = new int[2,3] { {1, 2, 3}, {4, 5, 6}};


// ili int[,] b = new int[,] { { 1, 2, 3 }, { 4, 5, 6 } };

int[,] c = { {1, 2, 3}, {4, 5, 6}};


int[,,,] d = new int[5,6,7,8];

for (int i = 0; i < 2; i++)


for (int j = 0; j < 3; j++)
Console.Write (b[i, j]);

Napomena: u jeziku C# ne može se navesti dimenzija veća nego što je pobrojano elemenata, kao što
je to moguće u jeziku C.
Poseban slučaj višedimenzionalnih polja su nazubljena polja (eng. jagged array) ili polja polja, s
retcima različite duljine, korisna kad imamo takve podatke da bi korištenje pravokutnih polja
rezultiralo nepotrebnim rasipanjem memorijskog prostora, primjerice kad postoji veliki nesrazmjer u
broju podataka dva retka istog polja.
int[][] a = new int[][] {
new int[] {1,2}, // "slabo popunjeni" redak
new int[] {3,4,5,6,7,8,9} // "jako popunjeni" redak
};
int[][] b = { new int[]{1,2},
new int[]{3,4,5,6,7,8,9}
};

for (int i = 0; i < b.Length; i++){


for (int j = 0; j < b[i].Length; j++)
Console.Write(b[i][j]);
Console.WriteLine();
}

int[][] c;
c = new int[5][];
c[0] = new int[3];
...

Primijetite razliku: c[0] je napunjen nulama a c[1], c[2] i drugi su null.


Napomena: može se definirati "vanjska" dimenzija polja (broj redaka), a "unutarnja" (broj stupaca)
ostaviti prazno da bude određeno inicijalizacijom. Navod broja stupaca nije dozvoljen.
int[][] a = new int[2][] {
new int[] {1,2},
new int[] {3,4,5,6,7,8,9} };

4.2.7 Konverzija tipova


Konverzija tipova (eng. casting) to jest pretvorba jednog tipa podataka u drugi može biti implicitna ili
eksplicitna. Implicitna konverzija izvršava se bez navođenja operatora konverzije. Primjeri su

36
pretvaranje tipa int u double, int u long i slično. Eksplicitna konverzija navodi se nekim od sljedećih
načina (ne samo njima):

Primjer: Osnove\Konverzija
 operator konverzije tipa (cast):
int c = (int) 4.5;
 metoda ToString():
int a = 154;
string s = a.ToString();
 metoda Parse():
int c = Int32.Parse(s);
 razred Convert:
decimal d = Convert.ToDecimal(c);
Pretvorba nekog vrijednosnog tipa podataka u referencijski tip object naziva se boxing. Postupak
alocira instancu i kopira vrijednost u novostvoreni objekt. Obrnuti postupak, izdvajanje vrijednosnog
tipa podataka iz tipa object, naziva se unboxing. Tim se postupkom vrši kopiranje vrijednosti
(objekta) s reference.

Primjer Osnove\Boxing

int i = 123; // A value


type

object o = i; // Boxing

int j = (int) o; //
Unboxing

Slika 14. Boxing i unboxing

Osnovni tipovi podataka smještaju se na sistemski stog (eng. stack) a objekti na hrpu (eng. heap).

4.2.8 Nulabilni tipovi podataka


Varijable mogu imati nedefiniranu vrijednost. Deklaracija standardnog tipa sadrži upitnik (?), a znači
da vrijednost varijable može biti nedefinirana, to jest može biti "ni 0".
int? num = null;

if (num.HasValue == true) // isto što i if (!(num == null))


Console.WriteLine("num = " + num.Value);
else
Console.WriteLine("num = Null");

String se ne može deklarirati kao nulabilan. String može biti prazan (tzv. null-string) ali to ne znači da
je null.
37
string s = string.Empty // isto što i s = ""

Postavljanje stringa na null znači da nije ni prazan:


string s = null; // vrijedi s != "", s == null

4.2.9 Tipovi podataka var i dynamic


Varijable definirane kao tip var nemaju unaprijed definirani tip podataka, već se stvarni tip određuje
prilikom kompilacije. Deklaracija je moguća samo unutar određenog postupka.
var sb = new StringBuilder();
StringBuilder sb = new StringBuilder();

Varijable definirane kao dynamic varijable također nemaju unaprijed definirani tip podataka, već se
stvarni tip podatka određuje prilikom izvršavanja.
dynamic s;
s = "Neki tekst";
Console.WriteLine(s.GetType());
s = 12;
Console.WriteLine(s.GetType());

U ovom slučaju zaobilazi se provjera prilikom kompilacije (većinom nepoželjno osim u iznimnim
slučajevima).

4.3 Operatori
4.3.1 Aritmetički operatori, operatori usporedbe i logički operatori
Aritmetički operatori (+, -, *, /, %, ++, --) i operatori usporedbe (==, !=, >, >=, <, <=) isti su kao i u
jeziku C.
Operatori == i != mogu se koristiti sa svim tipovima podataka, a ostali usporedbeni operatori mogu se
koristiti samo s tipovima podataka koji podržavaju usporedbene operatore. Npr. bool vrijednosti ne
mogu se uspoređivati operatorima <, <=, >, >=. Pri usporedbi referenci uspoređuju se adrese
(pokazivači) na objekte. Izuzetak su nizovi znakova kao specifičan tip podatka.
Logički operatori (&, |, ^, &&, ||, !, ~) obavljaju se isključivo nad operandima tipa bool i rezultat
logičke operacije je tipa bool. U ovu grupu mogu se pribrojiti i unarni true i false.

4.3.2 Ostali operatori


Operator pridruživanja (=), skraćenog pridruživanja (+=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>=), posmaka
(<<, >>), uvjetni operator (?:), operator veličine tipa podataka (sizeof) isti su kao i u jeziku C.
Povratni tip operatora typeof u jeziku C# je objekt koji predstavlja određeni tip podataka:
System.Type type = typeof(int);

Jezik C# sadrži još neke operatore koji nisu sadržani u jeziku C:

 Operator is (expression is type)provjerava da li je izraz određenog tipa:

38
int n = 3;
if (n is int)
...
 Operator . (točka) za pristup članovima razreda: name1 . name2
 Operator indeksiranja: []
 Indirekcija i adresa: * -> [] &

4.3.3 Prioritet operatora

Category Operators
Primary (x), x.y, f(x), a[x], x++, x--, new, typeof, sizeof, checked
Unary unchecked
+, -, !, ~, ++x, --x, (T)x
Multiplicative *, /, %
Additive +, -
Shift <<, >>
Relational <, >, <=, >=, is
Equality ==
Logical AND &
Logical XOR ^
Logical OR │
Conditional AND &&
Conditional OR ││
Conditional ?:
Assignment =, *=, /=, %=, +=, -=, <<=, >>=, &=, ^=, │=

4.4 Naredbe za upravljanje programskim tokom


4.4.1 Selekcija
Selekcija određuje koji će se dio koda kada izvršavati. Razlikujemo dva tipa selekcije: if i switch.
Selekcija if ima sljedeću sintaksu:
if (expression) statement1 [else statement2]
Kao i u jeziku C može se navesti nijedan, jedan ili više else izraza. Specifičnost jezika C# jest da je izraz
naveden u if dijelu logičkog tipa (boolean), a ne cjelobrojnog kao što je to u jeziku C.
Selekcija switch ima sintaksu:
switch (expression)
{ case constant-expression: statement jump-statement
[default: statement jump-statement] }
Izraz naveden u switch mora biti nekog od tipova: sbyte, byte, short, ushort, int, uint, long, ulong,
char, string ili enum baziran na nekom od navedenih tipova.
Konceptualno, switch radi kao i if, osim što dopušta i definiciju podrazumijevanog izraza (default).

39
4.4.2 Petlje
Petlje omogućavaju kontroliranu iteraciju, odnosno ponavljanje bloka naredbi. U jeziku C# postoje
petlje while, do-while, for i foreach (sjetimo se da sve osim foreach postoje i u jeziku C). Standardne
petlje obavljaju se ovisno o logičkom uvjetu, dok foreach iterira kroz listu objekata.
While i do-while imaju sintaksu:
while (expression) statement
do statement while (expression);
gdje je logički izraz tipa boolean.
For-petlja ima sintaksu (logički izraz je također boolean):
for ([initializers]; [expression]; [iterators]) statement

Primjer:
int[] numbers = {4, 5, 6, 1, 2, 3, -2, -1, 0};

for(int=0;i<numbers.length;i++){
Console.WriteLine(numbers[i]);
}

Foreach-petlja ima sintaksu:


foreach (type identifier in expression) statement
Izraz expression se ne smije mijenjati za vrijeme foreach petlje, kao ni identifier (ali može
identifier.NekaFunkcijaKojaMijenjaVrijednost).
Dobar primjer:
foreach (int i in numbers){
Console.WriteLine(i);
}

Ali ne može:
foreach (int i in numbers){
numbers[i] += 1;
}

4.4.3 Skokovi
Kao i u jeziku C postoje naredbe za skok: break i continue.
Break iskače iz programske strukture u kojoj je naveden (npr. petlja for), a continue nastavlja s
provjerom uvjeta za sljedeću iteraciju petlje.

4.5 Zadaci
Zadatak 1. Napisati program koji iz polja stringova ispisuje one koji sadržavaju zadani podniz.
Zadatak 2. Napisati program koji učitava tekst sa standardnog ulaza, mijenja ga u velika slova te
ispisuje tekst u obrnutom poretku.

40
Zadatak 3. Demonstrirati generator pseudoslučajnih brojeva. Upotrijebiti razred
System.Random.
Zadatak 4. Iz kojeg se tipa podataka u jeziku C# izvode svi ostali tipovi podataka?
Zadatak 5. Može li se bez pogreške izvesti sljedeći programski odsječak? Zašto?
int[] list = {1,2,3,4,5};
foreach(int i in list){
i = i * 2;
}

41
5 Objektno orijentirano programiranje
Objektno orijentirano programiranje za razliku od proceduralnog programiranja zahtjeva drugačiji
način razmišljanja o problemu koji programom rješavamo. Objektno usmjerena paradigma entitete iz
stvarnog svijeta opisuje apstraktnim objektima (razredima, klasama objekata) to jest "Sve je objekt".
Objektni pristup integrira oblikovanje podataka i procesa. Tokovi podataka, entiteti i veze te procesi
budu integrirani u objekte, a složeni sustavi grade se iz pojedinačnih, objektnih komponenti.

5.1 Koncepti
Osnovni koncept objektnog programiranja jest promatranje podataka kao objekata. Objekt (eng.
object) je svaka pojava, pojam ili predmet (stvarni ili zamišljeni) o kojem se prate podaci i načini
rukovanja podatcima (procesi). Pojedinačna pojava ili instanca objekta (eng. object instance) ima
vlastito stanje i ponašanje kojim se razlikuje od drugih objekata (kao npr. ljudi). Postupak kojim se se
objekti stvarnog svijeta pojednostavljuju, obuhvaćanjem glavnih aspekata objekta uz ispuštanje
nevažnih detalja zove se apstrakcija (eng. abstraction). Apstrakcije mogu biti uređene hijerarhijski (o
tome više kasnije).
Nadalje, grupa objekata se generalizira u tip objekta (eng. abstract data type) tj. razred (eng. class) -
zajednički naziv za sve objekte sa zajedničkim svojstvima (eng. property) i postupcima rukovanja
(eng. process, method), npr. Proizvod. Opis grupe objekata koji imaju jednake atribute, operacije,
veze i semantiku. Objekt je instanca razreda, s vlastitim vrijednostima svojstava, a razred je njegova
apstraktna definicija. Razredi imaju već spomenuta svojstva to jest atribute (eng. attribute) - skup
vrijednosti koje instanca može poprimiti za neku karakteristiku. Svojstvo može biti nekog oćeg ili
korisnički definiranog tipa, npr. integer, boolean ili Adresa. Implementacije rukovanja objektima
zovemo postupcima (eng. method) ili operacijama (eng. operation) koje imaju prototip to jest
„potpis” (eng. signature), koji definira naziv, parametre i povratnu vrijednost potprograma koji ih
implementira.
Ključni koncepti objektno orijentiranog oblikovanja programa i programiranja:

 Učahurivanje (Encapsulation) - skrivanje ugradnje od korisnika objekta. Objekti su “crne


kutije” čijim se podacima rukuje putem ugrađenih metoda.
 Višeobličje (Polymorphism) - mogućnost "skrivanja" različitih ugradnji iza zajedničkog
sučelja, postupci koje objekti različitih razreda tumače zavisno o specijalizaciji
 Nasljeđivanje (Inheritance) - podrazred (subclass) nasljeđuje svojstva i metode nadrazreda
(superclass), npr. Igla i Avion nasljeđuju Proizvod
 Podsustav (Subsystem) - kombinacija paketa razreda, realizacija jednog ili više sučelja koje
definiraju ponašanje razreda
 Komponenta (Component) - nezavisan, zamjenjiv, netrivijalan dio sustava, može biti izvorni
kod, pogonska inačica ili izvršni program
 Modularnost (Modularity) - razlaganje složenih sustava u (lakše) upravljive dijelove.
 Veza, asocijacija (Association) - smislena poveznica između dva ili više dijelova

42
5.2 Razredi
5.2.1 Članovi razreda
Osnovni element objektnog programiranja je razred. U jeziku C# razredi se sastoje od sljedećih
članova (eng. members):

 Konstante (eng. constants)


 Atributi (eng. fields)2
 Konstruktori (eng. constructors)
 Finalizatori (eng. finalizers)
 Postupci (eng. methods)
 Svojstva (eng. properties)
 Indekseri (eng. indexers)
 Operatori (eng. operators)
 Događaji (eng. events)
 Statički konstruktori (eng. static constructors)
 Ugniježđeni tipovi (eng. nested types)
Razred je obrazac prema kojem se stvaraju instance razreda - objekti. Operatorom new stvara se
nova instanca razreda - objekt. Referenca this odnosi se na aktualnu instancu razreda.
Napomena: razred s početnim postupkom (Main) ne mora se eksplicitno instancirati.

5.2.2 Modifikatori tipova i članova


Postoje četiri modifikatora pristupa razredima i članovima u jeziku C#:
 public – pristup nije ograničen
 private - pristup ograničen na razred u kojem je član definiran
 protected – pristup ograničen na razred i naslijeđene razrede
 internal – pristup ograničen na program u kojem je razred definiran
Kombinacijom modifikatora protected i internal (protected internal) - pristup dozvoljen naslijeđenim
razredima (bez obzira gdje su definirani) i svima iz programa u kojem je razred definiran.
Još neki od značajnijih modifikatora su:

 abstract – razred može biti samo osnovni razred koji će drugi nasljeđivati
 const – atribut (polja) ili lokalna varijabla je konstanta
 new – modifikator koji skriva naslijeđenog člana od člana osnovnog razreda
 readonly – polje poprima vrijednost samo u deklaraciji ili pri instanciranju
 sealed – razred ne može biti naslijeđen
 static – jedini, zajednički član svih instanci razreda (ne kopija nastala s instancom)
 virtual – postupak ili dostupni član koji može biti nadjačan u naslijeđenom razredu – (prilikom
nadjačavanja dodaje se modifikator override)
Ako modifikator nije naveden onda su se primijenjuju pretpostavljeni modifikatori tj. smatra da je:

2
Razlikovati pojam polja kao članskog podatka (field) od polja programske strukture (array)
43
 internal za razrede, sučelja, delegate i događaje
 private za članske varijable, svojstva i postupke i ugniježđene razrede
 public za postupke sučelja i članove enumeracija (nije ni dozvoljen drugačiji modifikator)
Napomena: Izvedeni razred ne može imati veću dostupnost od baznog razreda.

5.2.3 Statički članovi, konstante i atributi


Static se može primijeniti na atribute, postupke, svojstva, operatore i konstruktore. Statički član
pripada tipu, a ne instanci.
Modifikator const označava da je vrijednost varijable konstantna, tj. određena pri prevođenju i ne
može se mijenjati. Konstante su po prirodi statičke, pa ih se ne može eksplicitno proglasiti statičkim ni
pristupati im preko reference na instancu objekta.
Atributima se može promijeniti vrijednost pristupom članu, osim za nepromjenjive atribute.
Nepromjenjivi atributi imaju modifikator readonly. Ne dopuštamo promjene, a vrijednosti nisu
poznate u vrijeme prevođenja.

Primjer Razredi\Razred

public int var = 0; // atribut, varijabla objekta


public const int konst = 13; // konstanta razreda
public readonly int neDiraj; // postavlja se u konstruktoru
public static int trajna = 0; // dijeljena između instanci

5.2.4 Konstruktori
Konstruktor je postupak koji se obavlja pri stvaranju instance. Standardni (eng. default) konstruktor i
preopterećenje konstruktora s argumentima. Ne vraća vrijednost, služi za inicijalizaciju instance. Ime
mu je jednako imenu razreda. Razred može imati više konstruktora.

Primjer Razredi\Razred

class Razred
{
//atributi
public readonly int neDiraj;
public int var = 0;
...
//konstruktori
public Razred(){
neDiraj = 0;
}
public Razred(int neDiraj0, int var0) {
neDiraj = neDiraj0;
var = var0;
}...
}

44
Instanciranje objekta upotrebom različitih konstruktora:
Razred r1 = new Razred();
Razred r2 = new Razred(13,666);
Razred r2 = new Razred() {var=4};

Ako nije eksplicitno napisan niti jedan konstruktor, automatski postoji podrazumijevani ("prazni")
konstruktor bez parametara. Ako je napisan barem jedan konstruktor, onda se postojanje "praznog"
ne podrazumijeva.
Stvoreni objekti, za razliku od jezika C/C++, u automatski se uklanjaju iz memorije pomoću sakupljača
smeća (eng. Garbage Collector) ako na njih ne pokazuje niti jedna referenca. Sakupljanje smeća
obavlja tijekom izvođenja programa. Ne može se unaprijed točno odrediti kada će se memorija
osloboditi, ali se može potaknuti pozivom postupka sakupljača smeća GC.Collect().
Neposredno prije uništenja objekta od strane sakupljača smeća, automatski se poziva postupak
Finalize tog objekta. Postupak Finalize se ne može direktno napisati, nego se piše nalik destruktoru u
jeziku C++ . Naziv mu je jednak je nazivu razreda s predznakom '~‘ te ne vraća vrijednost. Nije ga
potrebno pisati ako se ne koriste takozvani “unmanaged” resursi (oni koje programer stvara i
uništava samostalno).

Primjer Razredi\Razred

class Razred
{
...
~Razred(){
Console.WriteLine("Finalizer");
}
}

Detaljnije o životnom vijeku objekta će biti govora kasnije.

5.2.5 Postupci
Postupak (metoda) je programska funkcija pridružena razredu (mora biti pridružena, C# ne dopušta
pisanje globalnih metoda). Metodama se definira određeno ponašanje razreda i običaj ih je nazvati
po ponašanju koje obavljaju - u ovom primjeru Zbroji i Oduzmi.

Primjer Razredi\Postupci

class Postupak
{
private int brOp = 0; // private: skrivanje člana

public int Zbroji(int x, int y) { ++brOp; return x+y; }


public int Oduzmi(int x, int y) { ++brOp; return x-y; }
}

Primijetimo da je član brOp skriven, tj. ima privatni modifikator i moguće mu je pristupiti samo iz
samog objekta. Postupci Zbroji i Oduzmi mogu pristupiti članu brOp, ali mu se ne može pristupiti iz
glavnog programa. Skrivanje člana zove se učahurivanje ili enkapsulacija.

45
Postupak p = new Postupak();
int a = 10, b = 7;
int zbroj = p.Zbroji(a, b);
int razlika = p.Oduzmi(a, b);

Tri su načina prijenosa argumenata metode:


1. Standardno, bez modifikatora – "call by value“
public static void SwapByVal (int a, int b);
SwapByVal (a, b);

2. upotrebom modifikatora ref – argumenti su reference ("call by reference"). Prije poziva


postupka argumenti moraju biti inicijalizirani. ref parametru argument mora eksplicitno biti
predan navođenjem ref.
public static void SwapByRef (ref int a, ref int b);
SwapByRef (ref a, ref b);
3. upotrebom modifikatora out– izlazni argumenti. U trenutku poziva out postupka argumenti
ne moraju biti inicijalizirani. Pri izlasku iz postupka out argumenti moraju biti postavljeni.
public static void TestOut(out int val, int defval);
TestOut(out i, 13);

Ključnom riječi params se prenosi varijabilni broj argumenata. Koristi se kad broj argumenata nije
unaprijed poznat. Polje argumenata može biti bilo kojeg tipa.
public static void TestParams(params object[] args){
Console.WriteLine("Params: ");
for(int i= 0; i< args.Length; i++)
Console.WriteLine("{0}:{1}", i, args[iArg])
}
TestParams("jedan", "dva", 3);
TestParams();

Main je postupak koji preuzima argumente pri pozivu programa:


static void Main( string[] args )

Primjer: TestParams(args);
Postoje i opcionalni argumenti kojima se zadaje pretpostavljena vrijednost. Navode se zadnji po redu.
public static void TestDefault(
int a,
int b = 99999,
string s = "default string")
{
Console.WriteLine("a = {0}, b = {1}, s = {2}", a, b, s);
}
...
TestDefault(1, 2, "RPPP");
TestDefault(2, 15);
TestDefault(3);

46
Imenovani argumenti - Prilikom poziva navodi se naziv argumenta i vrijednost odvojeni dvotočkom.
Imenovani argumenti se navode zadnji.
TestDefault(2, s:"moj string");
TestDefault(s: "moj string", b:55, a:4);

5.2.6 Svojstva
Svojstvo je postupak pristupa zaštićenim varijablama instance i specifično je za jezik C#.
Vratimo se na prethodni Primjer Razredi\Postupci u kojemu je član brOp privatan. Želimo li ipak
omogućiti npr. dohvat vrijednosti člana, umjesto da pišemo postupak za pristup skrivenom članu
GetBrOp() koji bi vratio vrijednost brOp, koristimo javno svojstvo:
public int BrOp
{
get //omogućava dohvat vrijednosti člana
{
return brOp;
}
}

Želimo li omogućiti i izmjenu člana brOp, pored get-dijela (eng. getter) dodajemo svojstvu set-dio
(eng. setter):
public int BrOp
{
get { //omogućava dohvat vrijednosti člana
return brOp;
}
set { //omogućava promjenu vrijednosti
brOp = value;
}
}

Primjer korištenja svojstva BrOp:


Postupak_v2 p2 = new Postupak_v2();
zbroj = p2.Zbroji(a, b);
razlika = p2.Oduzmi(a, b);

brojOperacija = p2.BrOp;
Console.WriteLine("{0} + {1} = {2}, {0} - {1} = {3}, Broj operacija = {4}",
a, b, zbroj, razlika, brojOperacija);
p2.BrOp = 100;

Za trivijalne slučajeve korištenja get i set u svojstvima tj. kada svojstvo služi samo kao omotač oko
privatne varijable može se olakšati pisanje korištenjem automatskog svojstva. Interno se kreira
privatna varijabla. Automatsko svojstvo iz primjera moramo inicijalizirati u konstruktoru. Želimo li

47
ipak zaštiti član brOp od vanjskih izmjena, kod automatskog svojstva može se postaviti private
modifikator na set dio.
public int BrOp { get; set; } //ili public int BrOp { get; private set; }

public Postupak_v4(){
BrOp = 0; //automatsko svojstvo moramo inicijalizirati u konstruktoru
}

Primjer netrivijalnog (izračunatog) svojstva:

Primjer Razredi\SvojstvaIndekseri

class Temperatura{
private float T;
public float Celsius{
get { return T - 273.16f; }
set { T = value + 273.16f; }
}
public float Fahrenheit{
get { return 9f / 5 * Celsius + 32; }
set { Celsius = (5f / 9) * (value - 32); }
}
}

Temperatura X = new Temperatura();


X.Fahrenheit = 70;
Console.WriteLine("{0} = {1}", X.Fahrenheit, X.Celsius);

5.2.7 Indekseri
Indekseri omogućavaju korištenje objekta kao niza (pristup elementima operatorom [ ]). Sintaksa je
ona uobičajena za svojstva (get i set).
public Temperatura this[int index] // Indekser
{
set{
if (index >= 0 && index < nizTemperatura.Length)
nizTemperatura[index] = value;
else throw new Exception("Pogreška!");
}
get{
if (index >= 0 && index < nizTemperatura.Length)
return nizTemperatura[index];
else throw new Exception("Pogreška!");
}
}

5.2.8 Preopterećenje postupaka


Postupci jednakog imena definirani u istom razredu moraju imati različitu signaturu (prototip), tj.
različiti tip ili redoslijed argumenata. Uobičajeno obavljaju isti posao, ali nad različitim tipovima
podataka.

48
Primjer: Razredi\PreopterecenjePostupaka

static double Maximum(double x, double y, double z){


Console.WriteLine("double Maximum( double x, double y,
double z )");
return Math.Max(x, Math.Max(y, z));
}
static int Maximum(int x, int y, int z){
Console.WriteLine("int Maximum( int x, int y, int z )");
return Math.Max(x, Math.Max(y, z));
}
...
// koji Maximum se poziva (double ili int)?
Console.WriteLine("maximum1: " + Maximum(1, 3, 2));
Console.WriteLine("maximum2: " + Maximum(1.0, 3, 2));

5.2.9 Preopterećenje operatora


Operatori koji se mogu preopteretiti:
unarni: + - ! ~ ++ -- true false
binarni: + - * / % & | ^ << >> == != > < >= <=

Primjer: Razredi\PreopterecenjeOperatora

public static ComplexNumber operator +(ComplexNumber x, ComplexNumber y){


return new ComplexNumber(x.Re + y.Re, x.Im + y.Im);
}
public static ComplexNumber operator *(ComplexNumber x, ComplexNumber y){
return new ComplexNumber(x.Re * y.Re - x.Im * y.Im,
x.Re * y.Im + y.Re * x.Im);
}
...
Console.WriteLine(x + " + " + y + " = " + (x + y));
Console.WriteLine(x + " - " + y + " = " + (x - y));
Console.WriteLine(x + " * " + y + " = " + (x * y));

5.3 Nasljeđivanje
5.3.1 Nasljeđivanje općenito
Nasljeđivanje omogućuje izvođenje (deriviranje, ne pokretanje) novog razreda na temelju postojećeg
razreda. Time je izvedeni razred proširenje osnovnog razreda, odnosno nasljeđuje osnovnu
funkcionalnost osnovnog razreda (njegove članove) i definira vlastitu.

Osnovni se razred naziva još i bazni razred (eng. base class), superrazred (eng. superclass) ili roditelj
(eng. parent), a izvedeni derivirani (eng. derived), podrazred (eng. subclass) ili dijete (eng. child).
Osnovni je razred generalizacija izvedenog, odnosno izvedeni je specijalizacija osnovnog.
class DerivedClass: BaseClass { }
Prilikom nasljeđivanja, moguće je i operaciju deklariranu u osnovnom razredu poništiti u korist
operacije jednako deklarirane u naslijeđenom razredu. Ovo se svojstvo naziva višeobličje. Podrazred

49
se ponaša drukčije odnosu na nadrazred ili neki drugi podrazred. Razred je podrazred osnovnog
razreda ako je u aplikaciji moguće zamijeniti osnovni razred podrazredom, a da aplikacija normalno
nastavi s radom. Drugim riječima, ako postoji programski kod koji se odnosi na nadrazred Kupac,
umjesto njega može se supstituirati bilo koji njegov podrazred. Ovo se svojstvo naziva zamjenjivost
(eng. supstitutability). Slijedi primjer kao na slici. Nasljeđivanje se uobičajeno označava
transparentnom strelicom.

Slika 15. Primjer nasljeđivanja

Razredi Motocikl i Automobil nasljeđuju osnovni razred MotornoVozilo. Sva tri razreda imaju osnovna
svojstva Model i Snaga a izvedeni razredi dodatno Prikolica odnosno BrojVrata. Slično, objekti sva tri
razreda imaju zajedničku metodu DajGas, ali izvedeni razredi različito implementiraju konstruktor i
metodu Start, a Automobil tome dodaje i metodu VeziPojas. Pri tom izvedeni konstruktor izvedenog
razreda poziva osnovni konstruktor, te implementira vlastitu dodatnu funkcionalnost. Primjeri i
specifičnosti u ovom i narednim potpoglavljima.

50
Primjer Razredi\MotornoVozilo – osnovni razred

class MotornoVozilo
{
// atributi
public string Model{ get; set; } // svojstvo
...

// konstruktor
public MotornoVozilo(string model, double snaga) { ... }

// postupak (zajednicki svim vozilima)


public void DajGas() { ... }

// postupak koji izvedeni razredi implementiraju zasebno


public virtual void Start() { ... }
}

Primjer Razredi\MotornoVozilo – izvedeni razred

class Automobil : MotornoVozilo


{
// dodatni atribut
public int BrVrata { get; set; }

// konstruktor izvedenog razreda


public Automobil(string model, double snaga, int brVrata)
: base(model, snaga) // poziv osnovnog konstruktora
{ ... }

// nadjačavanje baznog postupka Start()


public override void Start() { ... }

// dodatni postupak za Automobil


public void VeziPojas() { ... }
}

U programu možemo napisati:


MotornoVozilo vozilo = new MotornoVozilo("BWM XP SP1", 133);
vozilo.Start();

// nasljeđivanje i višeobličje
Automobil auto = new Automobil("Fiat Tumpo", 100, 5);
auto.VeziPojas();
auto.Start();
auto.DajGas();

// zamjenjivost
vozilo = auto;
Console.WriteLine(vozilo.Model);
auto.Start();
auto.DajGas();

Varijante nasljeđivanja
51
Virtual deklarira virtualni postupak roditelja koji može biti nadjačan.
Override deklarira postupak djeteta koji nadjačava, a može koristiti nadjačani postupak.
U programskom jeziku C# moguće je naslijediti samo jedan razred, a implementirati više sučelja.
class Osoba : Partner, IAdresa, IRazred { ... }
Varijable i postupci koje su javne ili zaštićene (public i protected) se nasljeđuju, dok privatne varijable
i postupci ne mogu biti naslijeđeni. Osim toga, razred označen kao sealed ne može biti naslijeđen.
Pokušaj nasljeđivanja razreda: sealed class SealedPoint { ... }:
class MyPoint : SealedPoint
dovodi do pogreške prevođenja.
Nadjačanim članovima pristupa se iz izvedenog razreda s base.member npr. base.F();
Instanciranje objekta izvedenog razreda izaziva poziv konstruktora osnovnog razreda. Pri uništenju
objekta prvo se poziva finalizator izvedenog razreda, a zatim finalizator osnovnog razreda.

5.3.2 Apstraktni razredi


Apstraktni razred je razred koji služi za definiciju izvedenih razreda kao osiguranje da izvedeni razredi
budu prikladno tj. ispravno definirani. Osnovni razred je nedovršen, ne može se instancirati, ali može
imati konstruktor. Izvedeni razred mora u potpunosti ugraditi nedovršene postupke, osim ako i sam
nije apstraktan. Ključna riječ za definiciju apstraktnog razreda je abstract.

Slika 16. Primjer apstraktnog razreda

52
Primjer apstraktnog razreda:

Primjer Razredi\PoslovniPartner

abstract class PoslovniPartner{


public string MaticniBroj { get; private set; }
public string AdresaSjedista { get; set; }
public string AdresaIsporuke { get; set; }

public PoslovniPartner(
string maticniBroj, string adresaSjedista, string adresaIsporuke)
{
this.MaticniBroj = maticniBroj;
// obavlja se validacija preopterećenom metodom!
if (!this.ValidacijaMaticnogBroja())
throw new Exception("Pogreška unosa matičnog broja!");
this.AdresaSjedista = adresaSjedista;
this.AdresaIsporuke = adresaIsporuke;
}
...
public abstract bool ValidacijaMaticnogBroja();
...
}

Primjer nasljeđivanja apstraktnog razreda. Osoba nasljeđuje članove deifirane u PoslovniPartner, a


dodaje članove specifične za osobu: Ime i Prezime. Konstruktor Osobe koristi konstruktor baznog
razreda i dodaje inicijalizaciju vlastitih članova.
class Osoba : PoslovniPartner{

public string Ime { get; private set; }

public string Prezime { get; set;}

public Osoba(string maticniBroj, string adresaSjedista,


string adresaIsporuke, string ime, string prezime)
: base(maticniBroj, adresaSjedista, adresaIsporuke)
{
this.Ime = ime;
this.Prezime = prezime;
}
...
}

Razred Osoba implementira i apstraktni postupak ValidacijaMaticnogBroja:


public override bool ValidacijaMaticnogBroja()
{
if (MaticniBroj.Length == 13) return true;
else return false;
}

Validacija se pokreće s razine instance, naredbom this.ValidacijaMaticnogBroja(). Kao što


naš primjer pokazuje može se pokrenuti i s razine osnovnog, apstraktrnog razreda.

53
5.3.3 Dinamičko višeobličje
Za razliku od „običnog“ statičkog višeobličja kod kojeg je unaprijed jasno koja će se izvedena metoda
izvesti, kod dinamičkog višeobličja pretvorba ovisi o dinamičkoj konverziji tipa.
Može na primjer postojati hijerarhija nasljeđivanja kao na sljedećoj slici. Kadrovska služba ima listu
djelatnika za koje se obavlja isplata. Apstraktni razred Djelatnik predstavlja korijen, a PoSatu i
Pausalac listove hijerarhije.

Slika 17. Primjer hijerarhije nasljeđivanja

U listu djelatnika stavljamo instance svih razreda osim korijena. Pojedina referenca u listi može
pokazivati na različite objekte, a tip trenutnog objekta određuje postupak koji se obavlja.
Želimo li pozvati neku od metoda specifične za naslijeđene razrede, trebamo načiniti konverziju tipa
koja će odrediti raspoložive metode (npr. Dividendaj i PisiSate). Osnovna metoda Plati postoji
međutim u svim naslijeđenim razredima pa bude pozvana baš ona koja se odnosi na referencu
elementa liste, kao u naredbi lista[count].Plati();

54
Primjer Razredi\Kadrovska

public class Kadar {


private Djelatnik[] lista;

public Kadar () {
lista = new Djelatnik[6];
lista[0] = new Pausalac ("Bobi", ...);
lista[3] = new Pausalac ("Rudi", ...);
...
((Pausalac)lista[0]).Dividendaj (500.00); // cast
((PoSatu)lista[3]).PisiSate (40); // cast
}

public void Isplata() {


double iznos;
for ( int count=0; count < lista.Length; count++ ) {
Console.WriteLine( lista[count] );
iznos += lista[count].Plati(); // polymorphic
}
}

5.3.4 Agregacija i kompozicija


Umjesto nasljeđivanja, dijete može "učahuriti roditelja", referencom na instancu razreda roditelja.

Primjer Razredi\Kompozicija

class Osoba
{
PoslovniPartner poslovniPartner;
private string ime;
private string prezime;
...
}

Slika 18. Primjer razreda koji referencira roditelja

Možemo stvoriti i listu referenci na objekte drugih razreda, kao što je bio slučaj u prethodnom
primjeru liste djelatnika. Ukoliko ovi objekti mogu postojati i bez da bude instanciran objekt koji ih
okuplja, veza se naziva agregacija.

55
Odjel

+Zaposli (in d : Djelatnik)


+Otpusti(in d : Djelatnik)
1..n -Sluge
1 -Odjel
-djelatnici Djelatnik -Šef

1..n +TraziPovisicu() 1

Slika 19. Primjer agregacije

Nasuprot tome, kompozicija je takav odnos u kojem jedan razred instancira (u svom konstruktoru)
kolekciju pripadnih objekata, te uništava pripadne objekte (u svom destruktoru). U jeziku za
obliovanje UML, ove se asocijacije označavaju transparentnim, odnosno punim rombom.

StudentskaSlužba Studomat

- studomati: Studomat
1 3
+ dohvatiStudomat() + prijaviIspit()
+ dohvatiSveStudomate() + odjaviIspit()
+ azurirajStudomat()
+ azurirajSveStudomate()

Slika 20. Primjer kompozicije

Razvojne okoline kao što je Visual Studio nemaju mogućnost takvog tumačenja programskog koda te
prikazuju pojedinačne reference ili polja referenci usmjerenim strelicama.

5.3.5 Sučelja
Sučelje (eng. interface) omogućava definiciju određenog ponašanja to jest svojstava objekata koji se
mogu primijeniti na razrede neovisno o hijerarhiji. U sučelju se članovi razreda specificiraju bez
implementacije, to jest sučelje sadrži samo deklaracije operacija. Može sadržavati postupke, svojstva,
indeksere i događaje. Svaki postupak je apstraktan.
Sučelje definira obavezu ugradnje. Razred koji nasljeđuje sučelje, mora implementirati sve postupke.
Sučelje može implementirati i apstraktni razred.
Sučelja su posebno važna u jeziku C# gdje razred može naslijediti samo jedan razred, a implementirati
više sučelja.
Realizacija (eng. realisation) - veza izvornog razreda i sučelja. Sučelje može biti argument nekog
postupka čime se postiže veća općenitost.
Standardni naziv za sučelje ima veliko početno slovo I (npr. INaziv). U razredu koji realizira sučelje se
koristi proširena i skraćena (eng. lollipop) notacija.

56
Primjer Razredi\Sucelje

interface IPoint
{
// Property signatures:
int x { get; set; }
int y { get; set; }
}

class MyPoint : IPoint {


public int x{
get { return myX; }
set { myX = value; }
}
...
}

Slika 21. Sučelje i realizacija sučelja

Primjer sučelja u deklaraciji metode:


class InterfaceMain
{
public static void PrintPoint(IPoint p) {
...

Prilikom poziva metode predajemo objekt realiziranog razreda:


MyPoint tocka = new MyPoint(2, 3);

InterfaceMain.PrintPoint(tocka);

Može naravno postojati više realizacija istog sučelja i sve bi one bili ispravni argumenti.

5.4 Kolekcije
Kolekcija je skup povezanih objekata. U jeziku C# prostor imena koji sadrži sučelja i razrede za rad s
kolekcijama zove se System.Collections. Sadrži sučelja: ICollection, IEnumerator, IEnumerable,

57
IDictionary i IList koja određuju osnovne funkcionalnosti kolekcija. Razred koji implementira jedno ili
više tih sučelja naziva se kolekcija.
Razredi sadržani u System.Collections:

ArrayList implementira sučelje IList pomoću polja za pohranu objekata čija se veličina po
potrebi povećava
BitArray Radi s poljem bitova koji su predstavljeni kao Boolean vrijednosti (true
predstavlja 1 a false 0)
Hashtable Predstavlja kolekciju vrijednosti ključ-i-vrijednost parova (vrijednost je objekt
pohranjen u kolekciji) organiziranih pomoću hash vrijednosti ključa
Queue Predstavlja first-in-first-out kolekciju objekata
SortedList Predstavlja kolekciju ključ-i-vrijednost parova poredanih (sortiranih) po ključu,
uz moguć dohvat po ključu i indeksu
Stack Predstavlja last-in-first-out kolekciju objekata

Neke od njih ćemo detaljnije obraditi.

5.4.1 Kolekcija ArrayList


Kolekcija ArrayList predstavlja niz čija se duljina dinamički povećava koliko je potrebno. Isti niz može
sadržavati različite tipove podataka.
Neka svojstva i postupci:
 Contains – provjerava nalazi li se zadani objekt u kolekciji
 Clear – briše sve elemente iz kolekcije
 Insert – ubacuje zadani objekt na zadano mjesto kolekcije
 Remove – briše prvo pojavljivanje zadanog objekta iz kolekcije
 Add – dodaje zadani objekt na kraj kolekcije i vraća indeks
 IndexOf – vraća položaj zadanog objekta unutar kolekcije ili dijela kolekcije
 Item – indekser za vraćanje ili postavljanje objekata u kolekciju po indeksu

Primjer Razredi\Kolekcije

ArrayList lista = new ArrayList();

lista.Add(1);
lista.Add("abc");

Console.WriteLine("Lista ima " + lista.Count + " el.");

if (lista.Contains("abc"))
Console.WriteLine("Lista sadrži abc");

lista.Clear();

58
5.4.2 Kolekcije Stack i Queue
Stog (eng. stack) i red (eng. queue) predstavljaju nizove podataka istog tipa s ograničenim pristupom
elementima. Kod stoga je dozvoljen unos elementa samo na vrh i skidanje s vrha, a kod reda unos
elementa na dno i skidanje s vrha.
Neka svojstva i postupci kolekcije Stack:

 Push – stavlja zadani objekt na stog


 Peek – vraća objekt s vrha stoga bez brisanja sa stoga
 Pop – vraća objekt s vrha stoga uz brisanje sa stoga

Primjer Razredi\Kolekcije

Stack stog = new Stack();

stog.Push("abc");
stog.Push(123);

Console.WriteLine("Stog ima " + stog.Count + " el.");


Console.WriteLine("Skidam (pop): " + stog.Pop().ToString());
Console.WriteLine("Na vrhu(peek): " + stog.Peek().ToString());

Neka svojstva i postupci kolekcije Queue:

 Enqueue – stavlja zadani objekt na kraj reda


 Peek – vraća objekt s početka reda bez brisanja
 Dequeue – vraća objekt s početka reda uz brisanje
Programski primjer je trivijalan i sličan onom za stog.

5.4.3 Generičke kolekcije


Generički razred (eng. generics) općenito je mehanizam koji omogućava definiranje općenitog
programskog koda (razreda, postupka, sučelja, ...) za rukovanje različitim tipovima podataka.
Parametrizirani razred (eng. parametrized class, template class) je predložak koji definira porodicu
razreda. Konkretni razred povezuje se navođenjem aktualnog tipa. Na primjer, stogovna kolekcija bilo
kakvih objekata može biti povezana na tip "duboki tanjur", "plitki tanjur" itd. a zatim kao takva
ugrađena u "perilicu posuđa" koja sadrži kolekciju odabranog tipa.

59
Slika 22. Parametrizirani razred

Slično, općenita klasa "račun" može imati realizacije "cjelobrojni račun" i "decimalni račun".

Slika 23. Veza parametriziranog razreda i njegovih realizacija

U jeziku C# prostor imena System.Collection.Generic sadrži sučelja i razrede za generičke kolekcije,


koje imaju funkcionalnosti negeneričkih kolekcija. Neki razredi prostora imena
System.Collections.Generic:

 public class List<T> : IList<T>, ICollection<T>, IEnumerable<T>, IList, ICollection, IEnumerable


 public class Queue<T> : IEnumerable<T>, ICollection, IEnumerable
 public class Stack<T> : IEnumerable<T>, ICollection, IEnumerable
 public class Dictionary<TKey,TValue> :
IDictionary<TKey,TValue>,
ICollection<KeyValuePair<TKey,TValue>>,
IEnumerable<KeyValuePair<TKey,TValue>>,
IDictionary, ICollection, IEnumerable, ISerializable, IDeserializationCallback
Na primjer, List(T) je parametrizirana kolekcija objekata kojima se može pristupati preko indeksa u
listi, a ima istu funkcionalnost razreda ArrayList.
Primjer realizacije liste stringova je kako slijedi. Na sličan način realizirale bi se lista cijelih brojeva
(List<int>) ili lista vlastitih tipova podataka (npr. List<Osoba>, za prethodno definirani class Osoba).

Primjer Razredi\Generics

List<string> listaString = new List<string>();


listaString.Add("jedan");
...
string trazeni = ...
if (listaString.Contains(trazeni))
Console.WriteLine("Element postoji u listi na " +
listaString.IndexOf(trazeni) + ". mjestu.");
else
Console.WriteLine("Element ne postoji u listi!");

Prednost generičkih kolekcija je ta što su tipizirane. Negeneričke kolekcije objekte pohranjuju kao
System.Object i takva pohrana je prednost sa stanovišta fleksibilnosti, ali ima nedostataka. Glavni je

60
nedostatak je obavljanje boxinga prilikom dodavanja podataka tipa vrijednosti (dobivanje reference
na value podatak da bi se s tim podatkom moglo postupati kao sa System.Object). To utječe na
performanse za veće količine podataka. Također je potrebna konverzija (unboxing) da se od objekta
dobije smisleni podatak. Generičke kolekcije su dakle imaju bolje performanse (ne mora se obavljati
boxing/unboxing) i tipski su sigurne (eng. type safety) to jest zaštićene su od unosa podataka
nepoželjnog tipa i pogrešaka koje pritom mogu nastati (npr. pokušaj izvršavanja naredbe neprikladne
za dani tip podataka).
Evo primjera vlastitog parametriziranog razreda:

Primjer Razredi\Generics

using System.Collections.Generic;

public class Stog<T>


{
T[] elementi;
public void Stavi(T element) {...}
public T Skini() {...}
}
...
Stog<int> stogInt = new Stog<int>();
Stog<string> stogString = new Stog<string>();
Stog<Automobil> stogAutomobil = new Stog<Automobil>();

Tip parametriziranog razreda se može ograničiti ključnom riječi where. Primjeri ograničenja nekog
tipa T:
 where T:struct – tip T mora biti tip vrijednosti (nullable također isključen)
 where T:class – tip T mora biti tip reference
 where T:new() – tip T mora imati prazni konstruktor
 where T:naziv baznog razreda – tip T mora biti navedeni razred ili razred koji ga nasljeđuje
 where T:naziv sučelja – tip T mora implementirati navedeno sučelje
 where T:U – tip T mora biti tipa U ili izveden iz tipa U pri čemu je U drugi tip po kojem se vrši
parametrizacija
U sljedećem primjeru GenRazred je određen s dva tipa pri čemu prvi tip mora biti Stog iz prethodnog
primjera određen tipom koji implementira sučelja IPoint i ima prazni konstruktor:
public class GenRazred<T, U>
where T:Stog<U> where U:IPoint, new(){
...

5.4.4 Kolekcije Hashtable i Dictionary


Tablica s raspršenim adresiranjem (eng. hashtable) je kolekcija parova ključa/vrijednost organizirana
na temelju hash kodiranih ključeva. U System.Collection postoji razred Hashtable za rad s takvim
tipom podataka. Ključ i vrijednosti su tipa object. Slijedi jednostavan primjer uz napomenu da razred
Hashtable ima puno više mogućnosti, izvan dosega ovog teksta.

61
Primjer Razredi\HashTablice

Hashtable a = new Hashtable(10); //10 je predviđeni max. br. elem.

a.Add(100, "Jedan"); //ključ mora biti jedinstven!


a[200] = "Dva"; //promjena vrijednosti ili dodavanje nove

foreach(DictionaryEntry d in a){ // za sve parove ...


Console.WriteLine(d.Value);

a.Remove(200); // ukloni

a.Clear(); // isprazni

Rječnik (eng. dictionary) je kolekcija koja ima istu funkcionalnost kao tablica s raspršenim
adresiranjem, ali su ključ i vrijednost tipizirani.
Imenik System.Collection.Generic sadrži razred Dictionary(Tkey, TValue).

Primjer Razredi\HashTablice

Dictionary<int, string> a = new Dictionary<int, string>(10);

a.Add(100, "Jedan");
a[200] = "Dva";

foreach(KeyValuePair<int,string> d in a){
Console.WriteLine(d.Value);
}
...

Primijetimo razliku između naredbe Add i indeksera [] (u oba primjera).


Naredbom a.Add(key, value) dodajemo vrijednost za ključ koji ne postoji u tablici/rječniku, dok
naredbom a[key] = value pridružujemo vrijednost ključu.

5.4.5 Proširenja
Proširenje (eng. extension) omogućava dodavanje postupaka postojećem razredu bez da je potrebno
stvaranje novog razreda ili promjena postojećeg razreda. Razred za koji se piše proširenje je naveden
kao prvi parametar statičke metode u statičkom razredu, uz koji se navodi prefiks this. Proširenje se
poziva kao da se radi o postupku unutar tog razreda (iako to nije).

62
Primjer Razredi\Generics

public static class Extensions {


public static V DohvatiIliStvori<K, V>
(this Dictionary<K, V> dict, K key) where V : new()
{
if (!dict.ContainsKey(key)){
V val = new V();
dict[key] = val;
}
return dict[key];
}
}
...
var dict = new Dictionary<int, Stog<string>>();
var stog = dict.DohvatiIliStvori(1);

Primjer dodaje postupak DohvatiIliStvori rječnik parova (int, string).

5.5 Zadaci
Zadatak 1. Implementirati razred Planet. Konstruktor neka prima radijus, gravitaciju i ime
planeta. Razred sadrži i statičku varijablu za brojanje instanciranih planeta, svojstva
Ime, Radijus, Gravitacija i postupak GetCount(). Demonstrirati rad implementiranog
razreda.
Zadatak 2. Implementirati razred PlanetCollection. Razred predstavlja listu planeta. Iskoristiti
razred iz prethodnog zadatka s planetima. Napisati postupak Add za dodavanje
planeta. Planetima se može pristupati preko indeksa.
Zadatak 3. Implementirati razred PlanetCollection. Razred predstavlja listu planeta. Iskoristiti
razred iz prethodnog zadatka s planetima. Napisati postupak Add za dodavanje
planeta. Planetima se može pristupati preko indeksa.
Zadatak 4. Definirati i ugraditi razrede s dijagrama iz primjera Razredi\Kadrovska
Zadatak 5. Napisati razred DjelatnikCollection - kolekciju djelatnika. Naslijediti sučelje IList.
Napisati parametriziranu kolekciju i postaviti ograničenje da prima samo tipove
izvedene iz Djelatnik.
Zadatak 6. Za model/kod sa slike (Motorno vozilo) za primjer Razredi\MotornoVozilo
odgovoriti na sljedeća pitanja:
a) Tko što nasljeđuje?
b) Može li se „sakriti” bazno svojstvo ili metoda (public pretvoriti u private) ?
c) Što bi bilo da metoda Start nije virtualna?

63
Zadatak 7. Napisati programski kod sučelja i realizacije.

StoreHome Store

POSterminalHome -storeId: Integer


-POSlist: List

POSterminal +create()
<<use>> <<interface>> +login(UserName, Passwd)
POSterminal Store +find(StoreId)
+getPOStotals(POSid)
+getPOStotals(POSid) +updateStoreTotals(Id,Sales)
+updateStoreTotals(Id,Sales) +get(Item)
+get(Item)

StoreHome Store

POSterminalHome -storeId: Integer


-POSlist: List
POSterminal <<use>> +create()
+login(UserName, Passwd)
POSterminal +find(StoreId)
+getPOStotals(POSid)
+updateStoreTotals(Id,Sales)
Store
+get(Item)

Slika 24. Primjer različitih notacija sučelja

64
6 Tehnike programiranja

6.1 Defenzivno programiranje


Defenzivno programiranje bazira se na zaštiti programa od neispravnog unosa podataka. Ovaj
koncept može se usporediti s defenzivnom vožnjom automobila koja se temelji na načelu da vozač
nikad ne može biti siguran što će učiniti drugi vozači, pa unaprijed nastoji izbjeći nezgodu za slučaj
pogreške drugih vozača. U defenzivnom programiranju ideja vodilja je da će potprogram s
neispravnim podacima „opstati“ i onda kada su pogreškom pozivajuće procedure predani neispravni
argumenti. Pristup „smeće unutra, smeće van” (“garbage in, garbage out”) treba zamijeniti s
pristupom „smeće unutra, ništa van“, „smeće unutra, poruka o pogrešci van” ili „smeću zabranjen
ulaz”. Osnovna pravila kojih se treba držati su

 provjeriti ispravnost svih vrijednosti podataka iz vanjskih izvora (datoteka, korisnik, mreža, ...)
 provjeriti ispravnost svih vrijednosti ulaznih parametara
 odlučiti kako postupiti u slučaju neispravnih podataka
Primjer defenzivnog programiranja može se ilustrirati jednostavnim primjerom rekurzivnog računanja
funkcije za faktorijele u kojoj za negativne vrijednosti nastupa (teorijski) beskonačna rekurzija.
int faktorijel( int N )
{
if ( N == 0 ) return 1;
else return N * faktorijel( N-1 ) ;
}

Matematički neprecizno rješenje, ali takvo da sprječava beskonačnu rekurziju u slučaju negativnih
brojeva je prikazano u donjem primjeru.
int faktorijel( int N )
{
if ( N <= 0 ) return 1;
else return N * faktorijel( N-1 ) ;
}

6.2 Tehnike obrade pogrešaka


Robustnost programa je pojam koji se opisuje mogućnost daljnjeg rada programa u slučaju pogreške,
iako to ponekad znači da će program vratiti neispravan rezultat. Ispravnost programa se odnosi na
činjenicu da program nikad ne vraća neispravan rezultat, iako bi to značilo ne vratiti ništa. Kako bi se
osigurala robustnost i/ili ispravnost programa potrebno je primijeniti jednu od sljedećih tehnika
obrade pogrešaka:

 vratiti neutralnu vrijednost, primjerice 0, prazni string, NULL


 zamijeniti neispravnu vrijednost prvom sljedećom ispravnom
o npr. while (GPSfix != OK) sleep(1/100s) ...
 vratiti vrijednost vraćenu pri prethodnom pozivu
 zamijeniti neispravnu vrijednost najbližom ispravnom, npr. min(max(kut, 0),360)
 zapisati poruku o pogrešci u datoteku, kombinirano s ostalim tehnikama
 vratiti kod pogreške kojim bi se pogreška dojavila drugom dijelu koda koji bi obrađivao
pogrešku postavljanjem globalne varijable statusa, vraćanjem negativne (ili neke druge
vrijednosti za pogrešku) ili bacanjem iznimke

65
 pozvati „globalnu“ metodu za obradu pogreške
 bezuvjetno završiti program, npr. Application.Exit(CancelEventArgs)

6.3 Iznimke
Iznimka predstavlja problem ili promjenu stanja koja prekida normalan tijek izvođenja naredbi
programa. U programskom jeziku C#, iznimka je objekt instanciran iz razreda koji nasljeđuje
System.Exception iz kojeg su izvedena dva značajna razreda: SystemException, bazni razred za
iznimke koje generira CLR i ApplicationException, bazni razred za iznimke aplikacije. Značajnija
svojstva svake iznimke su:
 StackTrace – sadrži popis poziva postupaka koji su doveli do pogreške
 Message – sadrži opis pogreške
 Source – sadrži ime aplikacije koja je odgovorna za pogrešku
 TargetSite – sadrži naziv postupka koji je proizveo pogrešku
Neke od ugrađenih sistemskih iznimki su:
 ArrayTypeMismatchException: tip vrijednosti koji se pohranjuje u polje je različit od tipa polja
i implicitna konverzija se ne može obaviti
 DivideByZeroException: pokušaj dijeljenja s 0
 IndexOutOfRangeException: indeks polja je izvan deklarirane veličine polja
 InvalidCastException: nedozvoljena konverzija tipa
 OutOfMemoryException: nedostatak memorije za alociranje objekta
 OverflowException: preljev pri izračunavanju aritmetičkog izraza
 NullReferenceException: referenci nije pridružen objekt
 StackOverflowException: stog je prepunjen

6.3.1 Obrada iznimki


Obrada iznimki sprječava nepredviđeni prekid izvođenja programa. Iznimka se obrađuju tzv.
rukovateljem iznimki (exception handler) te se programski kod razdvaja u blokove try, catch i finally
pri čemu se dio s catch ili finally može izostaviti3. Za jedan try blok može postojati jedan ili više catch
blokova koji obrađuju različite vrste pogrešaka. Ako dođe do pogreške u try bloku, a postoji više catch
blokova, obavlja se onaj catch blok koji obrađuje nastali tip iznimke. Ostali catch blokovi neće se
obaviti. Ako postoji više catch blokova, posljednji se navodi blok koji obrađuje općenite iznimke (npr.
iznimke tipa System.Exception).

3
Treba napomenuti da mora postojati ili blok catch ili blok finally, to jest blok try ne može biti jedini blok. Za
razliku od programskog jezika Java u kojem je takva mogućnost ostavljena zbog varijante try-with-resourses, C#
to ne dozvoljava, a automatsko otpuštanje resursa umjesto blokom try-with-resourses u C#-u je riješeno
korištenjem bloka using.
66
try {
//dio koda koji može dovesti do iznimke
}
catch (ExcepType1 exOb){
//kod koji se obavlja u slučaju iznimke tipa ExcepType1
}
catch (ExcepType2 exOb) {
//kod koji se obavlja u slučaju iznimke tipa ExcepType2
}
...// ostali catch blokovi
finally {
//kod koji se obavlja nakon izvođenja try, odnosno catch blok
}

U sljedećem primjeru ovisno o vrijednosti slučajno generiranog broja može se dogoditi pogreška
nastala dijeljenjem s nulom nakon čega se prekida normalno izvršavanje programa te se počinje
izvršavati programski kod u bloku catch pridruženom iznimci DivideByZeroException. Ako slučajno
generirani broj nije bio nula tada dolazi do pogreške u pristupu elementu polja
(IndexOutOfRangeException). Blok finally će se izvršiti neovisno o tome da li je programski kod u
bloku try ispravno završio ili je došlo do iznimke i eventualne obrade iznimke. U ovom primjeru blok
catch s iznimkom Exception se neće nikada izvršiti, jer se uvijek događa iznimka koja je uhvaćena
jednim od prethodnih blokova catch.

Primjer  Kodiranje \ TryCatch

try
{
int x = new Random().Next(2); //0 ili 1
int y = 10 / x;

int[] a = { 1, 2, 3 };
Console.WriteLine(a[3].ToString());
}
catch(DivideByZeroException e)
{
Console.WriteLine("Dijeljenje s 0. " + e.Message);
}
catch(IndexOutOfRangeException e)
{
Console.WriteLine("Indeks izvan granica polja. " + e.Message);
}
catch (Exception e)
{
Console.WriteLine("Pogreška. " + e.Message);
}
finally
{
Console.WriteLine("Napokon.\n");
}

6.3.2 Prosljeđivanje iznimki


U bilo kojem trenutku moguće je generirati (baciti) iznimku naredbom throw pri čemu se iza naredbe
navodi objekt tip Exception ili iz njega izvedenih razreda kao što je prikazano u sljedećem
programskom odsječku:

67
throw new ApplicationException("poruka");
...
catch(Exception e){
Console.WriteLine( "Izvor: {0}", e.Source );
Console.WriteLine( "Postupak: {0}", e.TargetSite );
Console.WriteLine( "Poruka: {0}", e.Message );
Console.WriteLine( "Trag: {0}", e.StackTrace );
}

Naredbom throw moguće je proslijediti već uhvaćenu iznimku. Bitno je naglasiti da je u tom slučaju
moguće napisati throw ili throw e, gdje je e uhvaćena iznimka. Ključna razlika između ova dva
pristupa je (ne)očuvanje traga iznimke. Primjerice ako se u sljedećem primjeru dogodila iznimka, tada
u trenutku kad je uhvaćena iznimka, neposredno prije retka C, objekt e sadrži informaciju da se
iznimka dogodila u retku B kojem je prethodio redak A. Proslijeđivanjem iznimke samo s throw za
posljedicu će imati da će prilikom sljedećeg hvatanja iznimke postojati informacija da se iznimka
dogodila u retku C4 kojem je prethodio redak A. Proslijeđivanjem iznimke s throw e kod sljedećeg
hvatanja tako proslijeđene iznimke postojat će samo informacija da se iznimka dogodila u retku C,
bez informacija što je prethodilo toj iznimci5.

4
Primijeniti da više nema informacija o retku B
5
Ponašanje je u skladu s očekivanim, jer je formalno došlo do generiranja nove iznimke, a ne prosljeđivanja
68
Primjer  Kodiranje \ CustomException

static void Main(string[] args)


{
try
{
RethrowException(); //REDAK A
}
catch (Exception e)
{
Console.WriteLine("Main: " + e.Message);
}
}

static void RethrowException()


{
try
{
MetodaKojaBacaIznimku(); //REDAK B
}
catch (Exception e)
{
Console.WriteLine("Rethrow: " + e.Message);
//isprobati što je e.StackTrace za throw i throw e;
throw; //REDAK C
}
finally
{
Console.WriteLine("Finally RethrowException");
}
}

static void MetodaKojaBacaIznimku()


{
// poziv bilo koje naredbe ili postupka koji izaziva iznimku
throw new ApplicationException("Iznimka je bačena!");
}

Dodatne mogućnosti su zamjena uhvaćene iznimke s nekom drugom (bacanjem neke druge iznimke)
ili omatanje postojeće iznimke u drugu čime se dobije ugniježđena iznimka dostupna u svojstvu
InnerException.

6.3.3 Vlastite iznimke


Vlastite iznimke stvaraju se Definiranjem razreda izvedenog iz razreda ApplicationException, a mogu
se hvatati kao i ostale iznimke navođenjem tipa konkretne iznimke. Za definiranje vlastite iznimke
dovoljno je definirati dva osnovna konstruktora i nadjačati svojstvo Message kao u donjem primjeru,
iako se preporuča realizirati još 2 konstruktora po uzoru na konstruktore iz ApplicationException.

69
Primjer  Kodiranje \ CustomException

class Iznimka : ApplicationException


{
private string val;
public Iznimka {}
public Iznimka(string str) : base(str)
{
val = str;
}
public override string Message
{
get
{
return "Nije neparan " + val;
}
}
}

6.3.4 Preporuke za korištenje iznimki


Iako je moguće napisati blok catch ne specificirajući iznimku koju hvata (catch{…}), takve situacije
je potrebno izbjegavati, jer se neće moći dobiti informacije o iznimci koja je nastala. Također,
potrebno je izbjegavati prazne blokove catch, jer će time iznimka biti „progutana“ bez informacije
korisniku da se nešto krivo dogodilo, što može otežati uklanjanje eventualnih neispravnosti
programa. Podatke iznimke i kontekst nastanka iznimke potrebno je evidentirati u datoteku, bazu
podataka, dojaviti korisniku ili slično, pri čemu je razne tipove pogrešaka potrebno obrađivati na
konzistentan način kroz čitav kod6. U tu svrhu, potrebno je razmotriti izradu centraliziranog sustava
za dojavu iznimki u kodu kao u sljedećem primjeru:

Primjer  Kodiranje \ CentraliziraniSustav

public static void ReportError(Exception iznimka) {


Console.WriteLine("Neocekivana iznimka: " + iznimka.Message);
Console.WriteLine("Mjesto iznimke:");
Console.WriteLine(iznimka.StackTrace);
Console.WriteLine("\nPritisni tipku...");
Console.ReadLine();
}
...
try {
Exception ex = new ApplicatonException("\"Poruka iznimke...\"");
throw ex;
}
catch (Exception ANYexception)
{
ReportError(ANYexception);
}

Prosljeđivanje iznimki treba raditi kada želimo specijalizirati iznimku, primjerice zamijeniti je nekom
drugom ili omotati nekom drugom prikladnijom za prikaz informacije korisniku pri čemu se originalna
iznimka može dohvatiti u svojstvo InnerException.

6
Primjer jednog konzistentnog centraliziranog sustava obrade iznimki je paket Enterprise Library – Exception
Handling Block
70
Primjer  Kodiranje \ InnerException

class Primjer
{
public void F()
...
catch(Exception e){
throw new ApplicationException
("Iznimka u Primjer.F() :", e);
...
class Program
{
static void Main(string[] args){
try{
new Primjer().F();
}
catch (Exception e)
{
Console.WriteLine("Iznimka u Main: {0}\nInnerException: {1}",
e.Message, e.InnerException.Message);
...

Iznimke treba koristiti za obavijest drugim dijelovima programa o pogreškama koje se ne smiju
zanemariti, ali te pogreške trebaju predstavljati stanja koja su stvarno iznimna, to jest situacije u
kojima programski odsječak ne možete nastaviti dalje s radom, za razliku od pogrešaka koje se mogu
obraditi lokalno. Potrebno je izbjegavati bacanje iznimki u konstruktorima i finalizatorima, osim ako
ih na istom mjestu i ne hvatamo, jer to može dovesti do inkonzistentnih stanja objekta koji se stvara,
odnosno uklanja.

6.4 Tvrdnje
Tvrdnja je naredba7 kojom se program testira tako da određeni izraz mora biti istinit, a inače se
izvršavanje programa zaustavlja. Tvrdnje se koriste za uklanjanje pogrešaka (debugging) i
dokumentiranje ispravnog rada programa u fazi kodiranja, naročito u razvoju velikih, kompliciranih
programa te u razvoju programa od kojih se zahtijeva visoka pouzdanost. Za korištenjem tvrdnje u
C#-u koristi se prostor imena System.Diagnostics i postupak Debug.Assert kojem se predaje logički
izraz za koji se pretpostavlja (tvrdi) da je istinit. Ako izraz nije istiniti ispisuje se poruka oblika
„Assertion Failed“.

7
Ovisi o programskom jeziku: u C-u su to naredbe, u C#-u postupci neke klase
71
Primjer  Kodiranje \ Assert

int vrijednost = Brzina(0, -3, 0);


...
int Brzina(int sirina, int duzina, int visina) {
Debug.Assert((-90 <= sirina) && (sirina <= 90), "Neispravna sirina.");
Debug.Assert((0 <= duzina) && (duzina < 360), "Neispravna duzina.");
Debug.Assert((-500 <= visina) && (visina <= 75000),"Neispravna visina.");

//izračun brzine
//...
int brzina = 650;

Debug.Assert((0 <= brzina) && (brzina <= 600), "Neispravna brzina.");


return brzina;
}

Slika 25. Primjer poruke za tvrdnju koja nije istinita

Tvrdnje se pišu na mjestima gdje se pogreške ne očekuju, tj. ne smiju se pojaviti. Na mjestima gdje
se pogreške očekuju vrši se obrada pogreške (iznimke). Za robustan programski kod potrebno je
koristiti i tvrdnje i programski kod za obradu pogreške, pri čemu treba izbjegavati pozive metoda u
izrazima tvrdnji, npr. Debug.Assert (Obavi(), "Neobavljeno").
Preporuka je da se tvrdnje koriste za dokumentiranje i verificiranje uvjeta koji moraju vrijediti prije
pozivanja metode ili instanciranja razreda (preconditions), te uvjeta koji moraju vrijediti poslije
djelovanja metode ili rada s razredom (postconditions).

6.5 Barikade
Barikade predstavljaju sučelja koja služe kao granica prema „sigurnim“ dijelovima koda. U tu svrhu
definiraju se dijelovi softvera koji će rukovati „prljavim“ (nesigurnim) podacima i dijelovi koji rukuju
samo s „čistim” podacima. Validacijski razredi koji su odgovorni za provjeru ispravnosti podataka
sačinjavaju barikadu prema internim razredima koji rukuju s podacima za koje se pretpostavlja da su
provjereni i ispravni.

72
Slika 26. Koncept barikada

Programski primjer barikade može se ilustrirati sljedećim primjerom u kojem je varijabla index
privatna varijabla iza barikade, a u dio set svojstva Indeks služi kao sučelje barikade, jer provjerava
ulaznu vrijednost koja može biti neispravna i omogućava da ostatak razreda uvijek radi s ispravnom
varijablom index.

Primjer  Kodiranje \ Barikade

class Primjer
{
private int index; //privatna varijabla (iza barikade)
public Primjer()
{
}
public int Index //javna metoda (služi kao sučelje barikade)
{
get { return index; }
set
{ //provjeravamo je li unutar zadanih granica,
//ako ne, pridjeljujemo najbližu vrijednost
if (value <= 0) { this.index = 0; }
else if (value > 100) { this.index = 100; }
else { this.index = value; }
}
}
}

Barikade naglašavaju razliku između tvrdnji i obrade iznimaka. Metode s vanjske strane barikade
trebaju koristiti programski kod za obradu pogreške, a unutarnje metode mogu koristiti tvrdnje jer se
ovdje pogreške ne očekuju! Na razini razreda javne metode rukuju s „prljavim“ podatcima i „čiste“ ih,
a privatne metode rukuju samo s „čistim“ podatcima. U tom slučaju pojava „prljavog“ podatka u

73
privatnoj metodi nije iznimka koja se očekuje, već neispravnost tvrdnje koja ukazuje na pogrešku u
kodiranju. Općenito, podatke je potrebno pretvarati u ispravan tip odmah pri unosu.

6.6 Otkrivanje pogrešaka


Uobičajena je zabluda programera je da se ograničenja koja se odnose na konačnu verziju softvera
odnose i na razvojnu verziju. Treba biti spreman žrtvovati brzinu i resurse tokom razvoja u zamjenu
za olakšani razvoj. Koncept ofenzivnog programiranja se bazira na načelu da se pogreške u fazi
razvoja učine toliko očitim i bolnim da ih je nemoguće zanemariti.
Nekoliko je tehnika kojima se ofenzivno programiranje ostvaruje

 pisati tvrdnje koje uzrokuju prekid izvođenja pri pogrešci


 popuniti alociranu memoriju prije upotrebe radi otkrivanja eventualnih problema s njenom
alokacijom
 popuniti alocirane datoteke ili tokove podataka prije upotrebe radi detektiranja eventualnih
grešaka u formatu datoteka ili podataka
 osigurati da svaka case naredba koja propagira do default slučaja uzrokuje pogrešku koju nije
moguće zanemariti8
 napuniti objekt „smećem“ (junk data) neposredno prije njegovog brisanja, kako se podaci ne
bi zadržali u memoriji i bili vidljivi, primjerice referencom na memorijski prostor oslobođen
brisanjem objekta.
Istovremeno, potrebno je planirati uklanjanje dijelova programa koji služe kao pomoć u otkrivanju
pogrešaka u konačnoj verziji softvera, što se može obaviti:
 korištenjem alata za upravljanje verzijama
 ugrađenim pretprocesorom za uključivanje/isključivanje dijelova programskog koda u
pojedinoj verziji
 korištenjem vlastitog (samostalno napisanog) pretprocesora
 zamjenom metoda za otkrivanje pogrešaka u konačnoj verziji “praznim” metodama koje
samo vraćaju kontrolu pozivatelju.

Primjer  Kodiranje \ Debug

#define RAZVOJ //definiramo simbol


...
#if (RAZVOJ)
// kod za debugiranje
Console.WriteLine("Poruka UNUTAR koda za debugiranje!");
#endif
...
Console.WriteLine("Poruka IZVAN koda za debugiranje!");

Prethodni primjer ilustrira mogućnost korištenja pretprocesorskih direktiva u svrhu uklanjanja koda
koji se koristio za utvrđivanje pogreške iz produkcijske verzije programa. U prikazanom primjeru će
do ispisa različite poruke na ekranu u ovisnosti o tome je li definiran simbol RAZVOJ ili ne. Umjesto
korištenja pretprocesorskih direktiva u C#-u se može koristiti atribut Conditional. Kod za testiranje je
potrebno odvojiti u posebni postupak i iznad postupka navesti atribut Conditional i naziv simbola. U

8
ako to već sam programski jezik ne spriječava
74
slučaju da simbol nije definiran, u kompiliranoj verziji nije uključen poziv označenog postupka. Simbol
se može definirati u kodu, ali i kao parametar prilikom kompiliranja (Properties → Build →
Conditional compilation symbols). Primjerice, ako u donjem primjeru uklonimo definiciju simbola
RAZVOJ poziv metode Test bit će zanemaren prilikom kompilacije.

Primjer  Kodiranje \ Debug

#define RAZVOJ // definiramo simbol


...
Test();
...
[Conditional("RAZVOJ")]
static void Test()
{
Console.WriteLine("Poruka iz postupka Test");
}

6.6.1 Količina defenzivnog koda u završnoj verziji


U završnoj verziji potrebno je ostaviti programski kod koji radi provjere na opasne pogreške, a
ukloniti kod koji provjerava pogreške s trivijalnim posljedicama, pri čemu je uklanjanje potrebno
učiniti korištenjem pretprocesorskih naredbi (atributa) i simbola, a ne fizičkim brisanjem koda, jer taj
dio koda opet može zatrebati za naknadno traženje pogreške. Iz završne verzije treba ukloniti kod koji
može uzrokovati pad programa, jer utječe na robustnost programa. Stoga je potrebno ostaviti kod
koji u slučaju pogreške omogućava „elegantno” rušenje programa te primjerice omogućava
korisnicima da sačuvaju svoj rad prije nego se program sruši. Kod koji zapisuje pogreške koje se
događaju pri izvođenju je potrebno ostaviti u završnoj verziji i implementirati zapisivanje poruka o
pogreškama u datoteku ili neko drugo trajnije spremište kako bi se eventualno mogao rekonstruirati
problem i pronaći uzrok pogreške. Pritom, treba biti siguran da su sve poruke o pogreškama koje
softver dojavljuje „prijateljske“ te korisnika treba obavijestiti o „unutarnjoj pogrešci” i o postupku
dalje (npr. navesti e-mail ili broj telefona tako da korisnik ima mogućnost prijaviti pogrešku).

6.7 Životni vijek objekta


Instancirani objekt postoji dok ga sakupljač smeća (GarbageCollector) ne uništi. Objekt na sistemskoj
hrpi (heap) može biti obrisan ako na njega ne pokazuje ni jedna referenca. Činjenica da na njega ne
pokazuje niti jedna referenca je nužan, ali ne i dovoljan uvjet9, pa objekt može dugo biti aktivan. U
slučaju da objekti nekog razreda drže neke „vrijedne“ resurse, primjerice otvorenu vezu prema bazi
podataka ili alociraju veliku količinu memorije, koje je potrebno osloboditi prije sakupljača smeća,
tada za taj razred treba implementirati sučelje IDisposable koje sadrži postupak Dispose.

9
Objekti se smještaju u različite generacije, pa je moguće da se objekti više generacije uopće ne brišu dok ima
dovoljno slobodne memorije
75
Primjer  Kodiranje \ Using

public class Razred : IDisposable {


public void Dispose() {
//zatvaranje datoteke, konekcije
// i sličnih "dragocjenih" resursa
...
}
}

Ako neki razred implementira IDisposable, preporuka je da se za objekte tog razreda uvijek pozove
postupak Dispose nakon što objekt više ne bude potreban. Iako bi se za istu svrhu mogao koristiti i
bilo koji drugi postupak, prethodnost ovog sučelja očituje se u kombinaciji s ključnom riječi using i
tzv. using-blokovima. Iako se Dispose može pozvati eksplicitno, postoji mogućnost da se prethodno
dogodi iznimka te da se izvršavanje koda preusmjeri na obradu iznimki. Za objekt stvoren unutar
using bloka, Dispose se automatski poziva nakon napuštanja bloka bez obzira na razlog izlaska iz
bloka10. Using-blok se može koristiti samo za one razrede koji implementiraju IDisposable.

10
Interno, za svaki using-blok kompajler stvar kombinaciju blokova try-finally, gdje se u finally dijelu poziva
Dispose na svim objektima navedenim pod using. Slično ponašanje se u programskom jeziku Java može ostvariti
korištenjem try-with-resources.
76
Primjer  Kodiranje \ Using

public class Razred : IDisposable {


public string Id { get; set; }
public void Dispose() {
Console.WriteLine("** {0} : Dispose **", Id);
}
public Razred(string id) {
Id = id;
Console.WriteLine("----> {0} : Kreiran", Id);
}
~Razred() {
Console.WriteLine("{0} : Očišćen ---->", Id);
}
}

...
try {
Razred r1 = new Razred("A1");

using (Razred r2 = new Razred("B2"))


using (Razred r4 = new Razred("D4")) {
Razred r3 = new Razred("C3");
r3 = null;
GC.Collect();
GC.WaitForPendingFinalizers();
throw new ApplicationException("Poruka");
}
r1.Dispose();
}
catch (Exception exc) {
Console.WriteLine("Dogodila se pogreška: " + exc.Message);
}

Slika 27. Alternative uništavanja objekata11

Slika 27 prikazuje neke moguće ispise koji nastanu pokretanjem prethodnog programskog odsječka.
Postavljanjem reference r3 na null, na objekt s tekstom C3 više ne pokazuje ni jedna referenca i on je
objekt kojeg sakupljač smeća može obrisati. Međutim, iako je poziva sakupljača smeća forsiran, to još
uvijek nije jamstvo da će objekt biti obrisan, što se vidi na lijevoj slici. Nakon što je sakupljač pozvan,
očekivali bismo da je objekt C3 očišćen te da je nakon toga bačena iznimka poruke. Umjesto toga,
program izlazi iz using bloka i ide na obradu iznimke. Odmah pri napuštanju using-bloka, a prije
obrade iznimke automatski se poziva Dispose za D4 i B2. Nakon izlaska iz obrade iznimke program

11
Desni ispis dobiven je korištenjem .NET Frameworka 3.0, a lijevi u .NET Frameworku 4.0 u kojem sakupljač
smeća radi u pozadini, a ne na glavnoj niti.
77
završava i sakupljač smeća uništava sve objekte iz programa. Potrebno je uočiti da za A1 i C3 Dispose
nije pozvan.
Desna slika ilustrira situaciju u kojoj su čišćenje objekta C3 i Dispose za D4 i B2 obavljeni prije obrade
iznimke.

6.8 Zadaci
Zadatak 1. Obraditi iznimku kada se ostvari Debug.Assert
Zadatak 2. Implementirati razred Niz koji osim niza cijelih brojeva sadržava gornju i donju
granicu niza. Baciti iznimku ako je gornja granica manja od donje, te ako se preko indeksera
pristupa članu izvan gornje odnosno donje granice.
Zadatak 3. Implementirati razred Temperature koji prima razred Niz. Razred treba imati metodu
za računanje prosječne temperature. Ubaciti provjere da niz temperatura nije prazan prije
izračuna. Provjeriti i da je dobiveni prosjek unutar granica definirane gornje i donje
vrijednosti niza.

78
7 Grafičko korisničko sučelje
Forma (eng. form) je strukturirani prozor ilil okvir koji sadrži prezentacijske elemente za prikaz i unos
podataka. Svaka forma je ujedno i prozor (eng. window), ali se uobičajeno podrazumijeva da je forma
dijalog na kojem se nalazi određeni broj kontrola.
Razvojni .NET omogućava izradu grafičkog korisničkog sučelja (eng. Graphical User Interface – GUI) u
klijentskim (Windows) aplikacijama korištenjem skupa biblioteka WindowsForms te biblioteka
Windows Presentation Foundation (WPF). Iako je WPF zamišljen kao nasljednik WindowsFormsa i
omogućava naprednije mehanizme povezivanja i oblikovanja formi, ove dvije tehnologije još uvijek
paralelno žive te ćemo se unutar ovog predmeta zadržati se na tehnologiji WindowsForms. Izrada
WPF aplikacija zahtjeva ponešto kompliciraniju paradigmu i dizajn koji u primjerima koji će slijediti ne
donosi konkretnu dobit. Slično tehnologiji WindowsFoms postoji WebForms za izradu Web stranica
(obrađen u jednom od narednih poglavlja) koji se zaniva na slično principu kao WindowsForms.

7.1 WindowsForms
WindowsForms predstavlja skup baznih razreda za podršku prozorima i kontrolama sučelja. Svaka
forma nasljeđuje System.Windows.Forms iz istoimenog prostora imena. Hijerarhija razreda
WindowsForm pojednostavljeno se može prikazati kao na sljedećoj slici.

System.Object
System.MarshalByRefObject
System.ComponentModel.Component
System.Windows.Forms.Control
System.Windows.Forms.ScrollableControl
System.Windows.Forms.ContainerControl
System.Windows.Forms.Form

Slika 28. Hijerarhija razreda Windows Forms

MarshalByRefObjectautomatizira prenošenje objekata putem omotača (proxyja) preko različitih


aplikacijskih domena (.NET Remoting). Razred Component implementira sučelje IComponent
omogućava dijeljenje objekata između aplikacija nema vidljivih dijelova. Njega nasljeđuje Control koji
dodaje grafička svojstva, a iz njega je izveden razred Form kao kontrola koja sadrži druge kontrole.
U nastavku su opsani neki koncepti ključni za razvoj interaktivnih aplikacija. Elementarni i tehnološki
obojeni dijelovi, pregled kontrola, vrsta izbornika i slično izostavljeni su jer se obrađuju u okviru
domaćih zadaća i laboratorijskih vježbi.

7.2 Događaji i delegati


Događaj je mehanizam razreda za dojavu zbivanja nad objektom. Uobičajeni primjeri događaja su
klik mišem na gumb, unos teksta u kućicu formulara, učitavanje ili zatvaranje forme, itd. Osim na GUI,
događaji se koriste i šire, tako da bilo koji razred može imati događaje čime se povećava se
modularnost programa i smanjuje ovisnost razreda.
U donjem primjeru događaj od interesa će nam biti događaj Click koji se može dogoditi klikom miša
na gumb. Postupak koji obrađuje događaje zove se rukovatelj događajem (engl. event handler).

79
Uobičajeno je naziva ControlName_EventName12 s dva argumenta: objektom koji je izazvao događaj i
podacima vezanim uz događaj. Za svaki događaj može biti pridruženo više rukovatelja događaja, te će
oni biti redom pozivani nakon što se događaj ostvari.

Primjer  GUI \ JednostavnaForma \ Form1.designer.cs


this.button1.Click += new System.EventHandler(this.button1_Click);

Primjer  GUI \ JednostavnaForma \ Form1.cs


private void button1_Click(object sender, EventArgs e){
this.Text = "Pozdrav!";
label1.Text = System.DateTime.Now.ToString();
}

Oblik postupka (povratne vrijednosti i argumenti) koji obrađuje neki događaj opisan je tipom
delegata i u prethodnom slučaju taj tip delegata je System.EventHandler13. Delegati nekog tipa
delegata su objekti koji ne sadrže podatke, već reference na jedan ili više postupaka (slično
pokazivaču na funkciju u C/C++ ili razredu Observable u Javi). Postupak se pridružuje delegatu
operatorom =, a operatorom += se dodaje u već postojeću listu postupaka pridruženih tom delegatu.
Operatorom -= neki postupak se uklanja iz liste pridruženih postupaka. Događaj je delegat kojem su
dozvoljene samo operacije += i -=. Više o delegatima i događajima objašnjeno je u primjeru s
vlastitom kontrolom u poglavlju 7.5.
U slučaju pridruživanja rukovatelja događajem nekom događaju u dizajnu forme stvara se programski
kod kao u gornjem primjeru. U slučaju da ipak sami moramo napisati takav kod može se upotrijebiti
kraći zapis navodeći samo ime metode ili navođenjem lambda izraza za obradu događaja.
this.button1.Click += this.button1_Click;

this.button1.Click += (s, e) => {


//kod za obradu događaja
};

U nastavku je dan primjer dinamičkog kreiranja gumba na lokaciju gdje se dogodio klik miša na formi i
pridruživanje ispisa poruke svakom od tako kreiranih gumba.

12
Promjenom naziva kontrole, naziv rukovatelja događajem se neće promijeniti. Štoviše, moguće je da više
različitih kontrola ima isti rukovatelj događajem.
13
Pojam event handler treba razlikovati od razreda System.EventHandler.
80
Primjer  GUI \ JednostavnaForma \ Form1.cs

private void Form1_Click(object sender, EventArgs e) {


Button b = new Button();
b.Text = "Gumb " + this.Controls.Count;
Point mousePoint = PointToClient(MousePosition);
b.Location = new Point(mousePoint.X, mousePoint.Y);

b.Click += (s, ea) =>


{
MessageBox.Show(sender.ToString());
};
this.Controls.Add(b);
}

7.3 Prijenos parametara među formama


Jedna od uobičajenih situacija u radu s formama je da neka forma poziva drugu formu i prosljeđuje
joj parametre, nakon čega se izmjene na pozvanoj formi koriste za ažuriranje na pozivajućoj formi.

Slika 29. Prijenos podataka među formama

Izuzmemo li mogućnost korištenja globalnih varijabli ili nekog zajedničkog spremišta, dvije su
mogućnosti za prijenos iz jedne forme u drugu. Jedna je promijeniti konstruktor pozvane forme tako
da prima sve potrebne argumente. Ovaj način se ne preporuča, jer može uzrokovati probleme u
dizajnu forme kroz radno okruženje14. Bolji način, a i uobičajen u ugrađenim dijalozima u .NET-u (npr.
kod OpenFileDialog i sl.) je kreiranje svojstava u pozvanoj formi koja služe kao omotač oko pripadnih
kontrola pozvane forme. U sljedećem primjeru u kojem forma FormaUnos poziva formu
FormaAdresa, Drzava, Mjesto i Ulica su takva svojstva.

14
Da bi se forma mogla dizajniti u Visual Studiu mora imati prazni konstruktor. Doda li se osim željenog
konstruktora i prazni konstruktor, upravo se otvorila mogućnost da netko pozove forme, a da joj ne prenese
sve potrebne parametre.
81
Primjer  GUI \ PrijenosParametara \ FormaAdresa.cs

public partial class FormaAdresa : Form {


public string Drzava {
get {
if (comboBoxDrzava.SelectedItem == null) return null;
else return comboBoxDrzava.SelectedItem.ToString();
}
set {
comboBoxDrzava.SelectedItem = value;
}
}
...

Nakon kreiranja objekta koji će predstavljati pozvanu formu, a prije samog trenutka poziva postupka
za prikaz (postupak ShowDialog) postavljaju se vrijednost svojstava čime se mijenja sadržaj kontrola
na formi koja će upravo biti prikazana. Nakon što se forma FormaAdresa zatvori, izvršavanje koda
forme FormaUnos će se nastaviti dohvatom vrijednosti svojstava Drzava, Mjesto i Ulica forme
FormaAdresa te će se dohvaćene vrijednosti postaviti kao vrijednosti pripadajućim kontrolama forme
FormaUnos.

Primjer  GUI \ PrijenosParametara \ FormaUnos.cs


private void buttonUnos_Click(object sender, EventArgs e){
FormaAdresa f = new FormaAdresa();
f.Drzava = textBoxDrzava.Text;
f.Mjesto = textBoxMjesto.Text;
f.Ulica = textBoxAdresa.Text;

DialogResult dialogResult = f.ShowDialog();


if (dialogResult == DialogResult.OK) {
textBoxDrzava.Text = f.Drzava;
textBoxMjesto.Text = f.Mjesto;
textBoxAdresa.Text = f.Ulica;
}
}

Forma koju se pozove sa ShowDialog predstavlja dijalog i može se zatvoriti na nekoliko načina. U
većini slučajeva potrebno je ustanoviti razlog zatvaranje forme, primjerice je li korisnik odustao od
unosa ili je unio podatke i namjerava ih snimiti. Rezultat zatvaranja dijaloga sprema je rezultat poziva
postupka ShowDialog i jedna je od vrijednosti iz enumeracije DialogResult. U ovom primjeru gumbi
Spremi i Odustani na formi FormaAdresa imaju postavljena svojstva DialogResult na OK, odnosno
Cancel te je prilikom obrade akcije na tim gumbima dovoljno zatvoriti formu, a rezultat je već
postavljen.
Napomena: Zatvorena forma još uvijek čuva svoje podatke, ona nije obrisana iz memorije, već samo
više nije prikazana.

Slika 30. Pridruživanje vrijednosti enumeracije DialogResult svojstvu DialogResult gumba

82
7.4 Provjera valjanosti unosa podataka
Provjerom (validacijom) se želi upozoriti na unos neispravnih podataka. Slika 31 prikazuje primjer
validacije prilikom unosa pri čemu ime i prezime moraju biti uneseni te ne smiju sadržavati brojeve, a
OIB mora biti niz brojeva duljine 11 znakova.

Slika 31. Primjer validacije podataka prilikom unosa u zaslonsku masku

Validaciju je moguće obaviti u obradi događaja forme ili korištenjem naprednijih mehanizama
povezivanja podataka na kontrole u formi (postavljanje svojstva DataSource na objekt koji
implementira sučelje IDataErrorInfo što je objašnjeno u poglavlju 9.4). Prilikom napuštanja svake
kontrole čija je vrijednost svojstva CausesValidation istinita, podiže se događaj Validating (s
parametrima object sender, CancelEventArgs e). Obradom ovog događaja moguće je uobičajeno se
postavlja vrijednost svojstva e.Cancel. Postavljanjem vrijednosti false smatra se da je podatak
ispravan i omogućava se napuštanje kontrole. Postavljanjem e.Cancel = true blokira se napuštanje
kontrole u slučaju pogrešnog unosa ako svojstvo forme AutoValidate nije postavljeno na
EnableAllowFocusChange ili Disable.
Problem na kojem treba obratiti pažnju je da ako postoje neispravni podaci, nije moguće poduzeti
nikakvu akciju, to jest napustiti kontrolu s pogrešnim upisom sve dok se pogreška ne popravi pa čak
ni zatvoriti formu. Rješenje ovog problema je postaviti svojstvo Cancel na false u obradi događaja
zatvaranja forme (događaj FormClosing).
Osim napuštanjem pojedine kontrole, validacija svih kontrola na formi se može pokrenuti pozivanjem
postupka ValidateChildren što je svakako potrebno napraviti prije pohrane podataka u neko
spremište. Validacija prilikom napuštanja pojedine kontrole na razini svih kontrola može se isključiti
postavljanjem svojstva forme AutoValidate na false.
Komponenta ErrorProvider služi za uniformni prikaz poruka o pogrešnom unosu na način da se pored
neispravnog unosa pojavljuje ikona uskličnika dok unos nije valjan, a prelaskom preko ikone
pojavljuje se tekst poruke o pogrešci. Tekst poruke se postavlja postupkom SetError(Control, string)
čime se nekoj kontroli postavlja tekst pogreške i uzrokuje prikaz ikone za pogrešku (osim ako poslani
string nije prazni string).

83
Primjer  GUI \ PrijenosParametara \ FormaUnos.cs

private void textBoxIme_Validating(object sender, CancelEventArgs e){


if (ValidacijaNaziv((TextBox)sender)) {
e.Cancel = false;
}
else {
e.Cancel = true;
}
}
private bool ValidacijaNaziv(TextBox textBox) {
if (textBox.Text == "") {
errorProvider.SetError(textBox, "Unesite ime/prezime.");
return false;
}
else if (Regex.IsMatch(textBox.Text, "[0-9]")) {
errorProvider.SetError(textBox,
"Ime/prezime treba biti valjanog fomata.");
return false;
}
else
{
errorProvider.SetError(textBox, string.Empty);
return true;
}
}

7.5 Vlastite kontrole i vlastiti događaji


Vlastite kontrole su elementi sučelja koje stvara korisnik razvojnog pomagala – programer, nakon
čega ih se može koristiti u različitim programima, kao i druge preddefinirane kontrole, odabirom iz
kutije s alatima. Vlastite kontrole mogu nastati nasljeđivanjem iz razreda
System.Windows.Forms.UserControl pri čemu se više postojećih kontrola kombinira u logičku cjelinu
(„korisničke“ kontrole, engl. user control) ili crtanjem kontrole ispočetka („nacrtane“ kontrole, engl.
owner-draw). Treća mogućnost je nasljeđivanje kontrole koja je najsličnija onoj koja se želi napraviti i
dodavanje novih svojstava i postupaka.

Slika 32. Izgled korisničke kontrole s kombinacijom naziv + okvir

U primjeru koji slijedi prikazan je primjer „korisničke“ kontrole u kokoj su grupirani okvir za unos
teksta i tekst ispred okvira, što je tipično na raznim formama za unos. Kontrola je izdvojena u posebni
projekt tipa Class Library koji kao rezultat izgradnje daje datoteku s ekstenzijom dll.
Naziv koji će se pojaviti ispred okvira moguće je promijeniti korištenjem svojstva Labela koje vrši
dohvat i postavljanje teksta na odgovaraću labelu. Okvir za unos teksta će prilikom početka unosa
teksta promijeniti svoju boju u svijetlo plavu, a nakon unosa vratiti početnu boju, što je
implementirano obradom događa Enter na kontroli TextBox.

84
Primjer  GUI \ VlastiteKontrole \ VlastitiTextBox
public class VlastitiTextBox : UserControl {
public string Labela {
get { return labelText.Text; }
set { labelText.Text = value; }
}

private void textBox_Enter(object sender, EventArgs e) {


oldText = this.textBox.Text;
oldColor = this.BackColor;
this.BackColor = Color.DeepSkyBlue;
}
...
}

Dodatno, korisnička kontrola definirat će i neke vlastite događaje na koje će se korisnik kontrole moći
pretplatiti, što je u skladu s modelom izdavač-pretplatnik koji je već korišten za ugrađene kontrole.

Slika 33. Model izdavač-pretplatnik

Kontrola (ili bilo koji drugi razred, jer koncept nije vezan samo za grafičko sučelje) definira određene
događaje na koje se moguće pretplatiti. Pretplaćivanje vrši aplikacija nad konkretnim objektom i u
tom slučaju objekt je „izdavač“, a aplikacija „pretplatnik“.
Defiranje događaja počinje definiranjem tipa delegata kojim se opisuje prototip postupka koji
obrađuje željeni tip događaja. U ovom primjeru takav postupak će biti morati biti tipa void i primati
dva argumenta tipa object i EventArgs. Iako već slični tipovi delegata postoje (System.EventHandler ili
Action<object, EventArgs) definirat ćemo novi tip za ilustraciju kako se definira novi tip delegata.

Primjer  GUI \ VlastiteKontrole \ VlastitiTextBox

public class VlastitiTextBox : UserControl {


public delegate void TextBoxActivityHandler(object sender, EventArgs e);
...

Gore navedenom konstrukcijom interno se stvara novi razred TextBoxActivityHandler koji nasljeđuje
razred MultiCastDelegate. Nakon što je definiran tip delegata moguće je definirati članske varijable
tog tipa, npr.
85
public TextBoxActivityHandler TextBoxChanged;

nakon čega je na nekom objektu tipa VlastitiTextBox moguće napisati nešto nalik sljedećem:
vlastiti.TextBoxChanged = M1;
vlastiti.TextBoxChanged += M2;
vlastiti.TextBoxChanged += M3;
vlastiti.TextBoxChanged -= M2;
vlastiti.TextBoxChanged += M4;

vlastiti.TextBoxChanged(neki objekt, neki argumenti);

pri čemu se su M1, M2, M3 i M4 postupci oblika void Mx(object sender, EventArgs e);
Prethodni kod bi uzrokovao da TextBoxChanged sadrži skup pridruženih postupaka pa se onda tako
definirana članska varijabla naziva delegat, jer sadrži popis postupaka koji će se slijedno pozivati
nakon što se na napiše TextBoxChanged()15. U gornjem primjeru to bi uzrokovalo pozive M1, M3 i
M4. Postupak M2 ne bi bio pozvan, jer je otkazana pretplata s -=. Nedostatak ovako definiranog
delegata je mogućnost da netko u kodu napiše =, umjesto += i ukloni prethodno pridružene postupke
iz delegata. U tu svrhu dodajemo ključnu riječ ispred tipa delegata te umjesto delegata definiramo
događaj. Time nastaje članska varijabla koja se zove događaj, što je delegat na kojem je samo
dopušteno += i -=, a ne više i operacija =.

Primjer  GUI \ VlastiteKontrole \ VlastitiTextBox

public class VlastitiTextBox : UserControl {


public event TextBoxActivityHandler TextBoxChanged;

U trenutku kad vlastita kontrola želi pobuditi (propagirati) neki događaj, odnosno obavijestiti
pretplatnike o promjeni stanja, provjerit će je li netko pretplaćen na događaj (obavlja se usporedbom
članske varijable za događaj s null) i ako nije pozvati redom pretplatničke postupke. U ovom primjeru
vlastita kontrola obrađuje događaj završetka unosa u tekstualnom okviru (obrada događaja Leave),
nakon čega podiže vlastiti događaj TextBoxChanged i izaziva pozive pretplaćenih postupaka.

15
Može se povući analogija s pokazivačom na funkciju u C-u koji bi mogao pokazivati na više funkcija umjesto
na samo jednu ili razredu Observable u Javi.
86
Primjer  GUI \ VlastiteKontrole \ VlastitiTextBox

public class VlastitiTextBox : UserControl {


private void textBox_Leave(object sender, …) {

// ako postoji pretplatnik na događaj
if (TextBoxChanged != null) {
// pozovi pretplatnika
TextBoxChanged(this, e);

Slika 34. Primjer korištenje vlastita kontrole

Vlastitu kontrolu može se dovući na neku formu iz alatne trake16 i koristiti kao i ugrađene kontrole.
Svojstva i događaji koje kontrola definira pojavit će se zajedno sa svim ostalim svojstvima i
događajima koje posjeduje razred UserControl. Stoga se može i obraditi vlastiti događaj kao u
sljedećem primjeru:

Primjer  GUI \ Custom \ Form1.cs

this.vlastitiTextBoxIme.TextBoxChanged += new
VlastiteKontrole.VlastitiTextBox.TextBoxActivityHandler
(this.vlastitiTextBox_Changed);
//ili samo vlastitiTextBoxIme.TextBoxChanged += vlastitiTextBox_Changed

...
private void vlastitiTextBox_Changed(object sender, EventArgs e) {
VlastitiTextBox t = (VlastitiTextBox)sender;
t.Tekst = t.Tekst.Substring(0, 1).ToUpper()
+ t.Tekst.Substring(1).ToLower();
}

7.6 Višenitnost i paralelno programiranje u jeziku C#


Niti ili dretve (eng. threads) su osnovne jedinice u koje operacijski sustav rasporedjeljuje procesorsko
vrijeme, pri čemu se više niti može izvoditi unutar jednog procesa. Višenitnost (eng. multithreading)
predstavlja istovremeno (ili prividno istovremeno) izvođenje različitih niti.

16
Prethodno je potrebno dodati referencu na projekt s vlastitom kontrolom (desni klik na projekt → Add
reference - Project – VlastiteKontrole) ili dodati eksplicitno na alatnu traku s Desni klik na Toolbox Choose
Items Browse... VlastiteKontrole.dll
Ili, desni klik na projekt – Add reference - Project - VlastiteKontrole
87
U jeziku C# višenitnost je moguće ostvariti putem više mehanizama koji su nastajali u različitim
verzijama .NET Frameworka. Ti mehanizmi su:
 Asinkrono pozivanje postupaka: Svaki delegat ima mogućnost asinkronog poziva
(BeginInvoke, EndInvoke)
 Kontrola BackgroundWorker
 Stavljanje postupka u postojeći red niti (razred ThreadPool)
 Stvaranje nove niti pomoću razreda Thread
 Korištenjem biblioteke TPL (Task Parallel Library) stvaranjem jednog ili više zadataka (razred
Task), paralelnim izvršavanjem dijelova kod (razred Parallel) ili korištenjem paralelnih LINQ
upita (PLINQ)
 Korištenje asinkronih verzija postupaka ako takvi postoje za određenu namjenu i korištenje
ključnih riječi async i await.
Napredniji mehanizmi poput TPL-a te async i await osiguravaju jednostavniji način pisanja u odnosu
na kod koji bi direktno koristio razred Thread i njegove sinkronizacijske mehanizme.
Primjer koji slijedi ilustrira stvaranje pozadinskog zadatka korištenjem razreda Task uz korištenje
async i await.

7.6.1 Primjer potrebe za paralelnim izvršavanjem


Potrebno je napisati aplikaciju koja će izračunati vrijednost broja π na određeni broj decimala
istovremeno prikazujući status izračuna. Kompletan izvorni kod primjera priložen je kao dodatak
predavanjima, a ovdje će biti prikazani dijelovi koji se tiču pokretanja izračuna i prikaza statusa.
Unutar razreda PiCalculator napisan je postupak CalcPi koji za ulazni argument ciljanog broja
znamenki ponavlja postupak računanja 9 po 9 znamenki sve dok ne izračuna π na ciljani broj
znamenki ili dok se vrijednost varijable abort ne postavi na true. Varijabla abort predstavlja zastavicu
koja signalizira namjeru prekida postupka. Alternativno, umjesto bool varijable može se koristiti
struktura CancellationToken kao unificirana struktura za takve namjene.
Obavijest da je novih 9 znamenki izračunato proslijedit će se podizanjem vlastitog događaja naziva
ProgressChanged.
Napomena: Alternativa podizanju događaja je direktno pisanje po kontrolama forme (u tom slučaju
potrebna je referenca na formu ili na konkretnu kontrolu), ali time se povećava vezanost
kompomenti i isključuje mogućnost da se obavijest o napretku izračuna evidentira negdje drugdje, pa
čak i ne u formi).

Primjer  GUI \RacunanjePI \ ShowProgressDelegate.cs

namespace RacunanjePI {
public delegate
void ShowProgressDelegate(string pi, int totalDigits, int digitsSoFar);
}

Postupak koji će se moći pretplatiti na događaj kojeg će naš kalkulator podizati morat će biti takav da
je tipa void i da prima tri parametra: trenutnu vrijednost broja π prikazanog u stringu, ukupni broj
znamenki i dosad izračunati broj znamenki. Nužnost prototipa osigurana je definiranjem tipa

88
delegata ShowProgressDelegate i definiranjem da je događaj ProgressChanged tipa
ShowProgressDelegate. Prilikom podizanja događaja potrebno je provjeriti je li događaju pridružen
koji pretplatnik na način da se provjeri da li je događaj null ili ne.

Primjer  GUI \RacunanjePI \ PiCalculator.cs

public class PiCalculator {


public event ShowProgressDelegate ProgressChanged;
...
public string CalcPi(int digits) {
abort = false;
StringBuilder pi = new StringBuilder("3.", digits + 2);
for (int i = 0; i < digits && !abort; i += 9) {
...
//prikaz trenutnog stanja podizanjem vlastitog događaja
if (ProgressChanged != null)
ProgressChanged(pi.ToString(), digits, i + digitCount);
...
return pi.ToString();

Ako se prilikom klika na gumb forme izračun pokrene u istoj niti (prethodno obavivši pretplatu na
događaj koju PiCalculator nudi) forma neće reagirati na pomak ni na druge događaje dok se postupak
izračuna ne završi.

Primjer  GUI \RacunanjePI \ Form1.cs

void btnCalc_Click(object sender, EventArgs e) {


...
int digitsToCalc = (int)digitsUpDown.Value;

piCalculator = new PiCalculator();


piCalculator.ProgressChanged += ShowProgress;

string pi = piCalculator.CalcPi(digitsToCalc);

Slika 35. Zastoj u radu sučelja obrade na jednoj dretvi

89
7.6.2 Stvaranje zadatka u pozadini
Za definiranje zadatka u pozadini koriste se razredi Task ili Task<TResult> ovisno o tome je li
potrebno vratiti vrijednost tipa TResult ili ne. Konstruktor razreda Task očekuje delegat tipa Action ili
Action<object>.
Tip delegata Action, odnosno Action<object> propisuje da će konkretni delegat imati vezu na void
postupak bez argumenata ili s jednim argumentom tipa object. Konstruktor razreda Task<TResult>
očekuje delegat tipa Func<TResult> ili Func<object, TResult>.
Tip delegata Func<TResult>, odnosno Func<object, TResult> propisuje da će konkretni delegat imati
vezu na postupak bez argumenata ili s jednim argumentom tipa object koji vraća objekt tipa TResult.
Ostali (opcionalni) parametri konstruktora uključuju argument koji se prosljeđuje postupku te razred
za signalizaciju otkazivanja zadatka. Umjesto eksplicitno definiranih delegata uobičajeno je koristiti
lambda izraze - neimenovane funkcije koje mogu biti predane kao argument ili vraćene kao rezultat.
Nakon stvaranja objekta tipa Task ili Task<TResult> pozadinski zadatak može se pokreniti s
postupkom Start. Alternativno, isto se može napraviti odmah pri definiranju zadatka ako se koristi
Task.Run.

Primjer  GUI \RacunanjePI \ PiCalculator.cs

public Task<string> CalcPiAsync(int digitsToCalc)


{
var piTask = new Task<string>(() =>
//lambda izraz - navodimo odmah kod zadatka
{
string pi = CalcPi(digitsToCalc);
return pi;
}
);
piTask.Start();
return piTask;
}

Uobičajeno je postupke koji su asinkroni označiti sufiksom Async, ali nije nužno. Poziv gore
navedenog postupka izazvat će izvršavanje novog zadatka u pozadini, a pozivatelju će se vratiti
referenca na objekt tipa Task<string>.

7.6.3 Čekanje zadataka i asinkroni postupci


Zadatak pokrenut u pozadini može se čekati pozivom postupka Wait, ali takav postupak bi bio
blokirajući i onemogućio prikaz status napretka. Preciznije, ako bi se u obradi događaja klika na gumb
čekao zadatak na taj način, onda bi to čekanje blokirala grafičku niti nijedan drugi događaj se ne bi
mogao obraditi dok taj postupak ne završi (a osvježavanja postupka su događaji koji čekaju u redu).
Rješenje kojim se pozadinski zadatak čeka, a da pri tom glavna nit (za obradu događaja) nije blokirana
je korištenje konstrukcije await i postupka koji vraća Task ili Task<TResult>.17 Na taj način pozivatelj
čeka na dovršetak zadatka i nastavit će izvođenje kod iza await tek kad pozadinski zadatak bude

17
Interno, kompajler će generirati kôd potreban za traženu izvedbu.
90
dovršen, ali se istovremeno omogućava formi da reagira na druge događaje. Povratna vrijednost
poziva await + zadatak tipa Task<TResult> je tip TResult.
Postupak u kojem se koristi await mora se označiti s async. Rukovatelji događaj mogu se označiti s
async, odnosno moraju ako se unutra koristi await.

Primjer  GUI \RacunanjePI \ Form1.cs

private async void btnCalcAsync_Click(...) {


...
piCalculator = new PiCalculator();
piCalculator.ProgressChanged += ShowProgress;
string pi = await piCalculator.CalcPiAsync(digitsToCalc);
...

Postupak označen s async može imati povratnu vrijednot Task, Task<TResult> ili void i ne može imati
ref i out parametre (ali može pozvati postupke koji imaju out i ref). Vrijednost iz postupka oblika async
Task<TResult> je tipa TResult, to jest naredba return vraća neki TResult. Primjer alternativnog
rješenja bi bio async postupak koji čeka zadatak u pozadini. Prethodni kod se takvom promjenom ne
mijenja.

Primjer  GUI \RacunanjePI \ PiCalculator.cs

public async Task<string> CalcPiAsync(int digitsToCalc) //alternativa


{
var piTask = Task.Run<string>(() => //lambda izraz - navodimo kod zadatka
{
string pi = CalcPi(digitsToCalc);
return pi;
}); // Task.Run kreira i pokreće zadatak

string calculatedPi = await piTask; //čekamo dovršetak izračuna


return calculatedPi;
}

7.6.4 Kontrola ProgressBar


Izračun broja π grafički je animiran korištenjem kontrole ProgressBar, koja se uobičajeno koristi za za
informiranje o napredovanju nekog procesa. Važnija svojstva kontrole su:
 Minimum i Maximum za najmanju i najveću vrijednost
 Value za trenutni položaj oznake
 Step za definiranje koraka promjene položaja oznake pri pozivu postupka PerformStep
Važniji postupci su:
 Increment za promjenu vrijednosti kontrole za vrijednost navedenu kao argument
 PerformStep za promjenu vrijednost za vrijednost svojstva Step

7.6.5 Pristup kontrolama korisničkog sučelja iz drugi niti


Osnovno pravilo rada s grafičkim kontrolama je da promjenu vrijednost ili stanja kontrole u grafičkom
sučelju treba biti izvršena iz niti koja je stvorila tu kontrolu. Svojstvo InvokeRequired vraća odgovor

91
pozivatelju da li je nalazi na nekoj drugoj niti. U slučaju da jest, onda je potrebno isti postupak pozvati
korištenjem postupka BeginInvoke. 18 Postupak BeginInvoke prima delegat i proizvoljan broj objekata
koji će biti predani kao parametri postupku pridruženom navednom delegatu. Tip delegata mora biti
takav da se podudara argumentima postupka kojeg se želi pozvati na glavnoj niti.

Primjer  GUI \RacunanjePI \ PiCalculator.cs

public delegate void ShowProgressDelegate(string pi, int


totalDigits, int digitsSoFar);

void ShowProgress(string pi, int totalDigits, int digitsSoFar){


if (this.InvokeRequired){ //poziv izvan primarne niti?
ShowProgressDelegate showProgress = ShowProgress;
this.BeginInvoke(showProgress, pi, totalDigits, digitsSoFar);
} else {
piTextBox.Text = pi;
piProgressBar.Maximum = totalDigits;
piProgressBar.Value = digitsSoFar;
}
}

Napomena: u primjeru se umjesto vlastitog tipa delegata mogao koristiti i delegat tipa Action<string,
int, int>.

7.7 Zadaci
Zadatak 1. Napraviti formu za uređivanje slike. Ugraditi mogućnosti mijenjanja boje, svjetline,
itd. (vrijednosti su mijenjaju uz pomoć NumericUpDown kontrole). Omogućiti spremanje
promijenjene slike.
Zadatak 2. Napraviti formu za pogađanje države čija je zastava prikazana ( GUI \Dodatak \
Zastave).
Zadatak 3. Napraviti formu za prikaz vješala za igru Vješala. Unaprijed pripremljen niz slika
prikazati u ImageList. Slike su pohranjene u binarnom formatu u Form1.resx. Klikom
na gumb Prikaži dodaje se sljedeća slika. Klikom na gumb Dodaj se može dodati nova
slika. ( GUI \Dodatak \ Vjesala)
Zadatak 4. U sljedećoj liniji koda, što predstavlja
a) naredba this.button1.Click, što System.EventHandler
b) a što this.Spremi? this.button1.Click += new
System.EventHandler(this.Spremi);

18
Sličan koncept se koristi i npr. u Javi gdje se koristi SwingUtilities.invokeAndWait koji stavlja predani postupak
u red čekanja za izvršavanje na grafičkoj niti
92
8 Aplikacija nad bazom podataka

8.1 Ogledna baza podataka


Ogledna baza podataka korištena u primjerima koji slijede prikazana je na sljedećoj slici. Predstavlja
bazu podataka hipotetičkog sustava s tipičnim ključevima (jednostavni, serijski, kompozitni
zamijenjen surogatom) i vezama (egzistencijalna zavisnost, paralelna, rekurzivna i specijalizacija) koje
ilustriraju ključne koncepte i njihove realizacije.
Dokument predstavlja evidenciju kupnje jednog ili više Artikala koji sudjeluju kao stavke pojedinog
dokumenta. Takvi podaci bit će prikazani u obliku zaglavlje-stavke (eng. master-detail). U stavkama se
evidentira jedinična cijena artikla iako taj podataka postoji u tablici Artikl, jer se cijena artikla može
mijenjati kroz vrijeme. Kupnju je općenito obavio neki Partner koji može biti privatna ili fizička osoba.
Tvrtka i Osoba tako predstavljaju specijalizaciju Partnera. Ovakvo rješenje omogućava da dokument
ima jedan strani ključ za kupca koji nikad nije null. Pojedini dokument je mogao nastati temeljem
nekog prethodnog dokumenta pa je rekurzivna veza realizirani stranim ključem IdPrehDokumenta na
istoj tablici. Značenja ostalih atributa lako se mogu isčitati iz njihovih naziva.
Mjesto

PK,I2 IdMjesta Drzava


PK OznDrzave
FK1,I1,I3 OznDrzave
I4 NazMjesta U1 NazDrzave
PostBrMjesta ISO3Drzave
PostNazMjesta SifDrzave

Partner Dokument

PK IdPartnera PK IdDokumenta
Artikl
TipPartnera VrDokumenta
BrDokumenta PK SifArtikla
OIB
FK1,I1 IdMjestaPartnera DatDokumenta
FK2,I4,I2 IdPartnera U1 NazArtikla
AdrPartnera
FK1,I1,I3 IdPrethDokumenta JedMjere
FK2,I2 IdMjestaIsporuke
PostoPorez CijArtikla
AdrIsporuke
IznosDokumenta ZastUsluga
SlikaArtikla

Stavka

PK IdStavke
Tvrtka Osoba
FK2,I3,I2 IdDokumenta
FK1,I1 SifArtikla
PrezimeOsobe KolArtikla
U1 MatBrTvrtke
ImeOsobe JedCijArtikla
I2 NazivTvrtke
PostoRabat

Slika 36. Model ogledne baze podataka

Postupak administriranja baze podatka opisan je u zasebnim uputama za laboratorijske vježbe.

8.2 Tehnologija ADO.NET


ActiveX Data Objects .NET (ADO.NET) je tehnologija za rukovanje podacima unutar okvira .NET
Framework koja omogućuje pristup bazama podataka, ali i drugim spremištima podataka za koje
postoji odgovarajući opskrbljivač podacima (eng. provider, sinonim za pojam opskrbljivač su davatelj,
pružatelj, poslužitelj).

93
ADO.NET podržava unificirani pristup različitim tipovima spremišta podataka: strukturiranim i
nehijerarhijskim podacima (CSV datoteke, Microsoft Excel tablice, …), hijerarhijskim podacima (XML)
te relacijskim bazama podataka (MS SQL Server, Oracle, MS Acess, PostgreSQL, …).
Od verzije .NET Framework 3.5. podržan je mehanizam Entity Framework (EF) za objektno-relacijsko
preslikavanje izgrađen nad ADO.NETom. Naknadno je EF izdvojen kao paket otvorenog koda
dostupan kroz alat za distribuciju programskih paketa NuGet.

Opskrbljivači podataka
Postoje dvije osnovne kategorije davatelja podataka prilagođene različitim tehnologijama i smještene
u odgovarajuće prostore imena: System.Data.SQLClient optimiziran za rad s MS SQL Serverom te
System.Data.OleDb za davatelje za rad s bilo kojim OLE DB izvorom podataka (Object Linking and
Embedding Database je Microsoftov API za uniformni pristup izvorima podataka). Nazivi razreda u
ovim prostorima imenima imaju slične nazive (npr. SqlCommand i OleDbCommand, SqlConnection i
OleDbConnection, SqlDataReader i OleDbDataReader, …) i implementiraju zajednička sučelja i/ili
zajedničke apstrakne razrede čime se postiže neovisnost aplikacije o fizičkom smještaju podataka.
Ključni pojmovi u davatelju podataka su:

 Conection – priključak (veza) s izvorom podataka


 Command – naredba koja će se izvršiti nad izvorom podataka
 DataReader – rezultat upita nad podacima. Podaci podaci se čitaju slijedno, pri čemu je
priključak na izvor podataka otvoren za vrijeme čitanja
 ParametarCollection – parametri objekta Command
 Parametar – parametar parametrizirane SQL naredbe ili pohranjene procedure
 Transation – nedjeljiva grupa naredbi nad podacima
 DataAdapter – most između podataka na izvoru i njihove kopije u memoriji (DataSet i
DataTable)

Slika 37. Osnovni elementi davatelja podataka

94
Odnosi među elementima prikazani su na sljedećoj slici.

Slika 38. Odnosni među elementima ADO.NETa

8.3 Izravna obrada podataka


Postupak izravne obrade podataka na izvoru podataka (najčešće to bude sustav za upravljanje
bazama podataka) pojednostavljeno se može opisati sljedećim koracima:
1. Ovori priključak
2. Izvrši naredbu
3. Obradi podatke u čitaču (DataReader)
4. Zatvori čitač
5. Zatvori priključak
Naredba koja se izvršava je neka od naredbi upitnog jezika za upravljanje
podacima (SQL) ili pohranjena procedura. Ukoliko naredba dohvaća
podatke, oni će biti proslijeđeni u čitač (DataReader). Za druge naredbe u
čitaču će se naći rezultat izvođenja. Sve promjene načinjene u koraku
obrade odnose se na lokalnu pohranu i ne budu proslijeđene na poslužitelj
(za tu svrhu treba cijeli postupak ponoviti, u obrnutom smjeru,
izvršavanjem naredbe koja mijenja podatke na poslužitelju).
Slika 39. Izravna obrada podataka

8.3.1 Priključak na bazu podataka


Priključak (eng. Connection) na bazu podataka otvara i zatvara vezu s fizičkim izvorom podataka te
omogućava izvođenje naredbi unutar transakcije. Priključak je apstrahiran korištenjem sučelja

95
System.Data.IDbConnection i apstraktnog razreda System.Data.DbConnection. Konkretne
implementacije ovih sučelja i razreda su npr. OleDbConnection, SqlConnection i druge.
Važniji postupci priključka su Open i Close za prikapčanje odnosno otkapčanje s izvora podataka, a
važnija svojstva su:
 ConnectionString: jedino promenjivo svojstvo tipa string i predstavlja postavke za spajanje na
izvor podataka. Sastoji se od parova postavki oblika naziv=vrijednost međusobno odvojenih
točka-zarezom
 State: oznaka stanja priključka s jednom od vrijednosti iz enumeracije ConnectionState
{ Broken, Closed, Connecting, Executing, Fetching, Open}

Na koji izvor podataka će se vršiti spajanje određeno je svojstvom ConnectionString navođenjem


vrijednosti za Data Source i kombinacijom ostalih elemenata

 Data Source: predstavlja naziv samostojne baze podatke ili drugog izvora podataka (npr. MS
Access, Excel, …), naziv ili mrežnu adresu poslužitelja i ime njegove instance (npr.
rppp.fer.hr\SQL2014 ili 31.147.204.102\SQL2014). Za poslužitelja na lokalnom računalu može
se koristiti naziv local ili „.“ (primjerice .\SQL2014)
 AttachDBFilename: koristi se kada se želi pristupiti bazi podataka koja nije registrirana u
SUBP (npr. jednokorisnički .MDF koji nije vezan na SQL Server).
 Initial Catalog / Databse: Naziv željene baze podataka na poslužitelju s više baza podataka
 ConnectTimeout: označava vrijeme čekanja do otvaranja ili bacanja iznimke po isteku
vremena
 User ID / UID: korisničko ime u bazi podataka
 Password / PWD: lozinka za korisničko ime
 Integrated Security: Postavlja se na false (pretpostavljena vrijednost ako nije navedeno), true
ili SSPI (Security Service Provider Interface – standardizirano sučelje za sigurnost
distribuiranih aplikacija). Kada se postavi na true ili SSPI, .NET se povezuje koristeći sustav
zaštite OS Windows kao alternativa pristupu s korisničkim imenom i lozinkom.
 Persist Security Info: true ili false (pretpostavljena vrijednost). Kad je postavljen na false,
sigurnosno osjetljive postavke (npr. lozinka) se automatski uklanjaju iz ConnectionStringa
nakon što je priključak otvoren.
ConnectioonString se može ručno navesti, a može se i složiti korištenjem razreda
SqlConnectionStringBuilder. Slijedi nekoliko primjera postavki priključaka na bazu prikazanih u
primjeru ADO\Upitnik dok se opširiniji skup primjera može se pronaći na
https://www.connectionstrings.com.

 OleDB ConnectionString za MS Access:


Provider=Microsoft.ACE.OLEDB.12.0;Data Source=c:\Projects\Firma.mdb
 OleDB ConnectionString za MS Excel:
Provider=Microsoft.ACE.OLEDB.12.0;Data
Source=c:\Projects\podaci.xls;Extended Properties='Excel 8.0;HDR=Yes‘
 System.Data.SqlClient.SqlConnection:
Data Source=.\SQL2008;Initial Catalog=Firma;Integrated Security=True
Data Source=rppp.fer.hr\SQL2012,3000;Initial Catalog=Firma;User
Id=rppp;Password=šifra

96
Postavke priključka ne bi trebalo pisati unutar programskog koda iz sigurnosnih (prikaz traga stoga
može otkriti podatke o priključku) i praktičnih razloga, jer bi promjena postavki bi zahtijevala
ponovnu izgradnju programa (i eventualnu novu verziju programa ako se radi automatsko
verzioniranje). Stoga se postavke evidentiraju u konfiguracijskoj datoteci (app.config odnosno
web.config za web aplikacije) pod stavkom connectionStrings.
<configuration>
<connectionStrings>
<add name="Firma"
connectionString="Data Source=rppp.fer.hr\SQL2012,3000;
Initial Catalog=Firma; User Id=rppp;Password=šifra;"/>
</connectionStrings>
</configuration>

Tako evidentiran string za povezivanje podataka dohvatljiv je iz koda korištenjem razreda


ConfigurationManager.
string connString =
ConfigurationManager.ConnectionStrings["Firma"].ConnectionString;

8.3.2 Primjer izravne obrade


Jednostavan primjer izravne obrade podataka na poslužitelju može biti upit kojim bi se dohvatili
nazivi tri najskuplja artikala.

Primjer  ADO\UsingDataReader\PopisArtikala

string connString = "Data Source=rppp.fer.hr\\SQL2012,3000;


Initial Catalog=Firma;User Id=rppp;Password=šifra";
SqlConnection conn = new SqlConnection(connString);
SqlCommand command = new SqlCommand();

command.CommandText = "SELECT TOP 3 * FROM Artikl";


command.Connection = conn;
conn.Open(); // 1. otvori
SqlDataReader reader = command.ExecuteReader(); // 2. izvrši
while (reader.Read()) // 3. obradi
{
object NazivArtikla = reader["NazArtikla"];
...
}
reader.Close(); // 4. zatvori
conn.Close();

U primjeru se definiraju postavke priključka na bazu podataka, priprema se naredba koja će se izvršiti
na tom priključku te se priključak otvara i izvršava se upit s ExecuteReader na takvoj naredbi. Rezultat
upita je SqlDataReader koji služi za slijedno čitanje rezultata.
Za prijelaz na prvi redak u rezultatu, a zatim na svaki sljedeći koristi se postupak Read sve dok ne vrati
false što označava da se čitanjem došlo do kraja. Vrijednost pojedinog atributa (stupca) u retku
rezultata može se dohvatiti korištenjem rednog broja stupca ili, kao u primjeru, navođenjem naziva
stupca.

97
8.3.3 Zatvaranje priključka
Svaku otvorenu vezu prema bazi podataka treba zatvoriti! U slučaju da se u primjeru iz poglavlja 8.3.2
dogodila conn.Close() se ne bi izvršio te veza ostaje otvorena i ne može se ponovo iskoristiti. Budući
da sustav za upravljanje bazom podataka uobičajeno ima ograničen broj mogućih otvorenih veza to
znači da bi nakon nekog vremena bilo nemoguće više se spojiti na bazu podataka do ponovnog
pokretanja aplikacije. Jedno od rješenja je staviti naredbu za zatvaranje veze unutar finally bloka
(primjer ADO \ UsingDataReader \ PopisArtikalaTryCatch). Bolje rješenje je koristiti činjenicu da
priključak na bazu implementira sučelje IDisposable pri čemu je Dispose u ovom slučaju ekvivalentan
postupku Close, pa se može koristit using blok

Primjer  ADO \ UsingDataReader \ PopisArtikalaUsing

using (SqlConnection conn = new SqlConnection(connString)){


using (SqlCommand command = new SqlCommand()){
...
using (SqlDataReader reader = command.ExecuteReader()){
...

Napomena: rješenje s using nije uvijek moguće, primjerice ako će povezivanje/čitanje podataka
naknadno nastupiti. U tom slučaju se koristi koncept po kojem se vrši automatsko zatvaranje
priključka kad se zatvori čitač, što se inicira prilikom dohvata čitača s:
command.ExecuteReader(System.Data.CommandBehavior.CloseConnection).

8.3.4 Neovisnost o konkretnoj implementaciji


Primjeri se mogu poopćiti na način da se umjesto konkretnih implementacija koriste sučelja
IdbConnection, IDbCommand, IDataReader odnosno da se koristi DBProviderFactory.
DbProviderFactory, odnsono DbProviderFactories omogućavaju stvaranje priključaka i naredbi bez
navođenja konkretnih implementacija. Statički postupak GetFactory u razredu DbProviderFactories
vraća instancu razreda DbProviderFactory, a postupci CreateConnection, CreateCommand, … kao
rezultat vraćaju instance konkretnih implementacija, ali promatrane kroz odgovarajuće apstraktne
razrede.

Primjer  ADO \ Upitnik

string factoryName = "System.Data.SqlClient";


DbProviderFactory factory = DbProviderFactories.GetFactory(factoryName);
using (DbConnection conn = factory.CreateConnection())
{
conn.ConnectionString = ...
using (DbCommand command = factory.CreateCommand()){
command.Connection = conn;
...

Ovaj i drugi primjeri (npr. parametrizirani upiti, upiti s više kompleta rezultata, pozivi pohranjenih
procedura) dostupni su u riznici programskog koda na poslužitelju predmeta.

8.4 Lokalna obrada podataka


Za razliku od postupka opisanog u 8.3, u ovom slučaju podaci se prikupljaju u skupu podataka
(DataSet) na lokalnom računalu. Lokalni skup se mijenja aplikacijom a pri tom evidentirane promjene
budu proslijeđene na izvor podataka.

98
Postupak lokalne obrade podataka na poslužitelju pojednostavljeno se može opisati sljedećim
koracima:
1. Otvori priključak DataSet
2. Napuni DataSet
3. Zatvori priključak
DataAdapter
4. Obradi DataSet
5. Otvori priključak
6. Ažuriraj izvor podataka
Connection
7. Zatvori priključak

Slika 40. Lokalna obrada podataka


Izvor podataka

8.5 Entity Framework


Entity Framework (u daljnjem tekstu EF) je nadgradnja nad ADO.NET-om koji se rad s bazom
podataka odvija na višoj razini putem objektnog modela (preslikavanje između objektnog modela i
podataka u relacijskoj bazi podataka). Ujedno predstavlja i primjer lokalne obrade podataka pri čemu
se automatski evidentiraju promjene. Za dohvat i ažuriranje podataka automatski se stvaraju
odgovarajući SQL upiti.

8.5.1 Načini kreiranja modela u Entity Frameworku


Model u EF-u se može stvoriti koristeći jedan od sljedeća tri principa
 Database First - baza podataka već postoji i model nastaje reverznim inženjerstvom baze
podataka
 Model First - model se dizajnira koristeći grafičko sučelje, a baza podataka nastaje na osnovu
modela.
 Code First – slično kao Model First, ali bez grafičkog sučelja: model je opisan kroz ručno
napisane razrede te nema vizualnog modela. Baza podataka se stvara na osnovu napisanih
razreda. Izgled baze podataka određen je nazivima razreda, nazivima i vrstama asocijacija
između razreda te dodatnim atributima.
U primjerima koji slijedi koristi se princip Database First.

8.5.2 Stvaranje modela na osnovu postojeće baze podataka


EF model se stvara na osnovu postojeće baze podataka odabirom opcije Add New Item → ADO.NET
Entity Data Model → Generate From Database nakon čega slijedi odabir priključka na bazu podataka,
verzije EF-a, naziva postavki priključka na bazu u konfiguracijskoj datoteci, naziva prostora imena i
obabir tablica iz baze podataka koji se žele uključiti u model. Uključivanjem stranih ključeva EF
automatski stvara asocijacije između stvorenih razreda. Moguće je dodati tablice, poglede,
pohranjene procedure i funkcije. Nakon odabira stvaraju se automatski generirane datoteke, što u
konkretnom primjeru za bazu podataka firma rezultira datotekama Firma.edmx, Firma.Context.tt +
Firma.Context.cs, Firma.Designer.cs, Firma.tt i s po jednom cs datoteka za svaku tablicu. Svaka
promjena modela ponovo stvara .cs datoteke na osnovu T4 (tt) predloška! Datoteka s ekstenzijom

99
edmx je xml datoteka s nekoliko sekcija koje sadrže opis modela (csdl), opis fizičkog modela (ssdl),
preslikavanja (c-s mapping) i postavke grafičkog prikaza. Grafički prikazano, stvoreni objektni model
izgleda kao na sljedećoj slici.

Slika 41. Početni EF model

Na modelu koji je inciijalno stvoren, potrebno je napraviti nekoliko preinaka kako bi izbjegle
višeznačnosti u kôdu zbog neodređenih veza i bolje oblikovao objektni model. Mjesto i Mjesto1 su
paralelne veze, pa postoji neodređenost u nazivu jer se može sa sigurnošću reći koje je koje, pa je
potrebno promijeniti nazive asocijacija iz Mjesto u MjestoIsporuke i Mjesto1 u MjestoSjedista. Zbog
jednostavnosti u primjerima koji slijedi mjenjaju se naziva asocijacija u entitetu Dokument iz
Dokument2 u PrethodniDokument i Stavka u Stavke, odnosno u entitetu Drzava iz Mjesto u Mjesta.
Također, potrebno je označiti da se prilikom brisanja dokumenta automatski brišu i njegove stavke
(Cascade na poveznici između entiteta Dokument i Stavka).
Dodatno, potrebno je izvršiti podešavanje specijalizacije, pa entitet Partner treba označiti kao
apstraktni razred da ga se ne može nezavisno instancirati. Osoba i Tvrtka nasljeđuju Partnera, pa je
potrebno izbrisati veze Partner-Osoba i Partner-Tvrtka. Svojstvo BaseType tim entitetima se postavlja
na Partner i brišu se svojstva IdOsobe iz Osobe i IdTvrke iz Tvrtke.

100
Slika 42. Primjer podešavanja specijalizacije na primjeru Partner, Osoba, Tvrtka

Desnim klikom na pojedini entitet i odabirom opcije Table Mapping još potrebno je podesiti između
IdTvrtke i IdPartnera, odnosno između IdOsobe i IdPartnera.

Slika 43. Podešavanje preslikavanja kod naslijeđenih entiteta na primjeru Tvrtka → Partner

Nakon promjena, ciljani ogledni model korišten u primjerima koji slijede izgleda kao na sljedećoj slici.

101
Slika 44. Ciljani ogledni EF model

8.5.3 Elementi EF modela


Svaki objekt u EF modelu vezan je za neki (memorijski) kontekst u kojem je dohvaćen iz baze
podataka i sve evidencije promjena vrše se u sklopu tog konteksta. Kontekst je jedna instanca razreda
izvedenog iz razreda DbContext (u ovom primjeru to je razred FirmaEntities). Podaci pohranjeni
unutar konteksta su skupovi entiteta tipa DbSet<T>, gdje je T tip entiteta. Svaki entitet je predstavljen
parcijalnim razredom što omogućava proširenje razreda proizvoljnim elementima. U prikazanim
primjerima proširenja su odvojena unutar posebne fizičke mape  ADO \ EF_Firma \ Partial19, a ne
u automatski generiranoj datoteci, jer se sadržaj te datoteke automatski regenerira promjenom
modela. Entiteti svoje asocijacije realiziraju virtualnim svojstvima (ICollection<T> za agregacije), kako
bi se interno mogao stvoriti proxy koji pruža vlastitu implementaciju virtualnih svojstava.

8.5.4 Važnija svojstva i postupci razreda DbContext i DbSet


Važnija svojstva i postupci razreda DbContext su
 SaveChanges[Async] – spremanje promjena u bazi podataka
 Database – svojstvo koje omogućava direktni rad s BP (npr. kreiranje i brisanje BP,
izvršavanje vlastitih SQL upita i procedura)
 ChangeTracker – pristup do razreda koji prati promjene na objektima u kontekstu

19
Parcijalni razred može se nalaziti u više datoteka u različitim fizičkim mapama, ali je bitno da se u svim
datotekama koristi isti prostor imena i isti naziv razreda.
102
 Set i Set<T> - vraćaju DbSet za konkretni tip entiteta, a koristi se ako se želi napisati općeniti
postupak (inače je svaki entitet već sadržan u kontekstu kao svojstvo)
 Entry i Entry<T> - služi za dohvat informacije o nekom entitetu u kontekstu i promjenu
njegovog stanja (npr. otkazivanje promjena).
Važnija svojstva i postupci razreda DbSet<T> su:

 Add – dodavanje objekta u skup


 Remove – označavanje objekta za brisanje
 Local – kolekcija svih trenutno učitanih podataka (koristi se za povezivanje na forme)
 Find[Async] - Dohvat objekta unutar konteksta na osnovu primarnog ključa
 AsNoTracking – Dohvat podataka za koje se ne evidentiraju promjene.

8.5.5 Dodavanja novog zapisa


Dodavanje novog objekta u kontekst vrši se stvaranjem objekta pozivom odgovarajućeg konstruktora
ili statičkog postupka na odgovarajućem DbSetu i dodavanjem u odgovarajuću kolekciju. Tako
stvoreni zapis nije automatski dodan u bazu podataka, već je označen za dodavanje. Pohranom
promjena na razini konteksta EF će generirati Insert upit za svaki novi objekt. Bitno je naglasiti da se
pozivanje snimanja promjena vrši samo jednom za sve promjene i da se odvija unutar transakcije.

Primjer  ADO\EF_Firma\MainForm.cs - dodajPromijeniObrisiToolStripMenuItem_Click

using (FirmaEntities context = new FirmaEntities()){


Artikl artikl = new Artikl(){ // (1. varijanta - konstruktor)
SifArtikla = 12345678, CijArtikla = 10m,
JedMjere = "kom", NazArtikla = "Burek sa sirom"
};
context.Artikl.Add(artikl); //varijanta s imenom skupa

artikl = context.Artikl.Create(); // (2. varijanta - factory)


… //punjenje objekta
context.Set<Artikl>().Add(artikl); //varijanta s tipom skupa

await context.SaveChangesAsync(); // pohrani sve promjene

8.5.6 Ažuriranje postojećeg zapisa


Ažuriranje postojećeg zapisa vrši se tako da se entitet dohvati korištenjem postupka Find ili FindAsync
na DbSetu koji traže zapis na osnovu vrijednosti primarnog ključa20 21. Postupak Find traži zapis
unutar već učitanog konteksta, a ako ga ne pronađe obavlja se upit na bazu. Vraća null ako traženi
zapis ne postoji. Nakon dohvata potrebno je promijeniti željena svojstva i pohraniti promjene u
kontekstu.

20
Ključ može biti kompozitni jer Find ima varijabilni broj argumenata, ali se načelno ne preporuča ako će se
entitet koristiti u padajućim listama.
21
Traženje zapisa može se obaviti i korištenjem postupka Where i lambda izraza.
103
Primjer  ADO\EF_Firma\MainForm.cs - dodajPromijeniObrisiToolStripMenuItem_Click

using (FirmaEntities context = new FirmaEntities())


{
Artikl artikl = await context.Artikl.FindAsync(12345678);
artikl.CijArtikla = 11m;
await context.SaveChangesAsync();
}

8.5.7 Brisanje zapisa


Brisanje zapisa vrši se tako da je prvo potrebno dohvatiti entitet (ako već nije u kontekstu) i izbaciti
ga iz konkretnog DbSeta ili označiti ga za brisanje pomoću context.Entry, a zatim pohraniti promjene
u kontekstu.

Primjer  ADO\EF_Firma\MainForm.cs - dodajPromijeniObrisiToolStripMenuItem_Click

using (FirmaEntities context = new FirmaEntities())


{
Artikl artikl = await context.FindAsync(12345678);
context.Artikl.Remove(artikl);
//ili context.Entry(artikl).State = EntityStated.Deleted;
await context.SaveChangesAsync();
...
}

8.5.8 Upiti nad EF modelom


Upiti nad EF modelom vrše se korištenjem tzv LINQ izraza (skraćenica od Language Integrated Query)
korištenjem postupaka Where, OrderBy, OrderByDescending, ThenBy, First ,Skip, Take, Select, …
Konkretni davatelj usluge pretvara Linq upit u SQL upit, uz opasku da nije uvijek moguće sve
pretvoriti u SQL upit. Upit se izvršava tek u trenutku dohvata prvog podataka ili eksplicitnim pozivom
postupka Load (LoadAsync) te je moguće ulančavanje upita (rezultat formiranog upita je najčešće tipa
IQueryable<T>). Podaci iz vezane tablice se učitavaju pri svakom dohvatu ili eksplicitno korištenjem
postupka Include (kreira join upit u SQL-u). Primjer upita za dohvat prvih n najskupljih artikala je dan
u sljedećm programskom odsječku.

Primjer  ADO\EF_Firma\MainForm.cs - dohvatiNajskupljeToolStripMenuItem_Click

var upit = context.Artikl.


Include(a => a.Stavka).AsNoTracking().
OrderByDescending(a => a.CijArtikla).
Take(broj);
foreach (Artikl artikl in upit) { … }

8.5.9 EF i pohranjene procedure


EF omogućava naknadno dodavanje pohranjenih procedura u model na način da se u grafičkom
prikazu EF modela odabere desni klik na modelu, a zatim opcija Update Model From Database i
odabire proceduru koje se žele uključiti. Za svaku odabranu proceduru generira se postupak
istoimenog imena u kontekstu i novi tip podatka za povratnu vrijednost. Rezultat je kolekcija tipa

104
ObjectResult<T> gdje je T oblika NazivProcedure_Result.22 Rezultat implementira sučelje
IEnumerable<T> pa se može se koristiti unutar foreach petlje, ali bez vraćanja unatrag i ponavljanja.
Primjer poziva pohranjene procedure dan je u sljedećem programskom odsječku. Povratni parametri
se dohvaćaju korištenjem razreda ObjectParameter, a parametri koji su isključivo ulazni mogu se
predati navođenjem vrijednosti.

Primjer  ADO\EF_Firma\MainForm.cs - btnProc_Click

using (FirmaEntities context = new FirmaEntities())


{
ObjectParameter brojSkupljih =
new ObjectParameter("BrojSkupljih", typeof(int));
ObjectParameter brojJefitnijih =
new ObjectParameter("BrojJeftinijih", typeof(int));
foreach (var artikl in context.ap_ArtikliSkupljiOd(20000m,
brojSkupljih, brojJefitnijih))
{
sb.AppendLine(artikl.NazArtikla);
}
sb.AppendLine("Broj skupljih: " + brojSkupljih.Value);
...
}

8.6 Zadaci
Zadatak 1. Kako izgleda priključak na bazu podataka (connection string) ako je instanca SQL
Servera s nazivom SQLEXPRESS instalirana na računalu rppp.fer.hr i želimo se spojiti
na bazu podataka Firma s korisničkim imenom rppp20 i lozinkom test?
Zadatak 2. Koja naredba nad naredbom SqlCommand vraća skup vrijsdnosti, koja samo jednu
vrijednost, a koja ne vraća vrijednost?
Zadatak 3. Što reprezentira sučelje IDbCommand?
Zadatak 4. Poopćiti primjer PopisArtikala korištenjem IDbCommand sučelja.

22
Dozvoljeno je da više procedura koristi isti tip kao povratnu vrijednost što se može podesiti u
postavka procedure (desni klik na modelu \ Model Browser \ Function Imports). Primjerice, npr. tip
procedure ap_ArtikliSkupljiOd postavljen je na Artikl, umjesto generiranog
ap_ArtikliSkupljiOd_Result.

105
9 Povezivanje podataka i složene zaslonske maske

9.1 Povezivanje podataka s kontrolama na formi


Povezivanje podataka (eng. data binding) je mehanizam vezanja (povezivanja, privezivanja)
elemenata grafičkog sučelja na podatke. Povezuju se podaci s nekog izvora podataka sa svojstvima
kontrola – s bilo kojim svojstvom, a najčešće s npr. Text, Font, Color.
Želi li se na nekoj formi s nekoliko tekstualnih okvira (textbox) prikazati podatak (zapis) s više
vrijednosti, uobičajeni kod, bez mehanizma povezivanja, bi redom (pojedinačno) postavljao sadržaj
svakog okvira na odgovarajuću vrfijednost. Obrnuto, prilikom validacije i spremanja podataka
očitavao bi se svaki pojedinačni sadržaj okvira i postavljao ga u odgovarajuće vrijednosti svojstava
izvora. Postupak se dodatno komplicira kada se želi povezati skup podataka (zapisa), a ne samo jedan
podatak. Ideja povezivanja je izbjeći pojedinačno postavljanje i očitavanje vrijednosti i omogućiti
sinkroninaziciju vrijednosti na kontroli i vrijednosti na izvoru.
Povezivanje se u Windows formama ostvaruje suradnjom nekoliko vrsta objekata prikazanih na
sljedećoj slici. Kao što se vidi na slici, pojedinačni podatak može izravno biti vezan na kontrolu (npr.
vrijednosta varijable NizZnakova može biti vezana na svojstvo kontrole Text ili vrijednost varijable
TipSlova vezana na svojstvo kontrole Font).
Složeniji tipovi podataka povezuju se preko razreda CurrencyManager i konteksta povezivanja
(BindingContext) kako je opisano u nastavku.

Slika 45. Odnos tipova objekata prilikom ostvarivanja povezivanja

Izvor podataka može biti bilo koji objekt s javnim svojstvima, niz podataka, kolekcija koja
implementira sučelje IList ili složeniji tip podataka (npr. DataSet, DataTable).
Razred CurrencyManager se koristi prilikom povezivanja s kolekcijom objekata na izvoru te vodi
evidenciju o trenutnoj poziciji unutar izvora podataka, pri čemu izvor ne zna koji se element trenutno
prikazuje. Za svaki izvor podataka postoji zasebna instanca razreda CurrencyManager, a za više
kontrola iste forme koje se povezuju na isti izvor kreira se samo jedna instanca razreda
CurrencyManager.

106
Slika 46. Jedna instanca CurrencyManager kontrolira više povezivanja istog zapisa

Za održavanje aktualne vrijednosti pojedinačnog svojstva privezanog objekta brine se razred


PropertyManager (radi jednostavnosti nije prikazan na slikama).
Svaka Windows forma (ili pojedinačna kontrola) ima BindingContext koji vodi evidenciju o svim
objektima tipa CurrencyManager i PropertyManager na toj formi.
Binding predstavlja pojedinačno povezivanje - stvara i održava jednostavno povezivanje između
pojedinačnog svojstva kontrole i svojstva nekog objekta (u nekoj listi objekata).
BindingSource učahuruje izvor podataka i prati trenutnu poziciju zapisa. Ima funkcionalnosti
BindingContext i CurrencyManager.

9.1.1 Razred BindingSource


Glavni članovi razreda BindingSource su DataSource i DataMember. Svojsvo DataSource određuje što
je izvor podataka pri čemu se vrijednost svojstva DataSource može postavljati prilikom dizajna ili
prilikom izvršavanja (programski). DataSource se u dizajnu postavlja na tip podatka koji će biti
povezan, a prilikom izvršavanja odnosi se na konkretne podatke. Kada izvor podataka ima
ugniježđene podatke (više podskupova ili tablica, npr. Dokument ima Stavke) svojstvo DataMember
određuje koji podskup se uzima za izvor podataka (Dokument.Stavke).
Pojedina kontrola može se povezati na čitav BindingSource (složeno povezivanje čime se prikazuju svi
podaci) ili na jedan njegov član (jednostavno povezivanje u kojem kontrola prikazuje samo jednu
vrijednost). Primjerice na sljedećoj slici prikazan je isječak iz primjera  Binding gdje je kontroli tipa
BindingSource imena podatakBindingSource1 za tip izvora podatka postavljen tip Binding.Podatak.
Tako definiran izvor podataka iskorišten je za povezivanje na mrežu podataka dataGridView1
(složeno povezivanje gdje će mreža sadržavati sve podatke iz izvora), odnosno na tekstualni okvir
kontrole textBox1 navodeći da se svojstvo Text te kontrole povezuje sa svojstvom Prvi trenutno
povezanog objekta.

107
Slika 47. Postavljanje povezivanja u dizajnu

Za tip podatka u dizajnu može se odabrati neki od već prethodno dodanih tipova podataka ili se može
dodati novi tip u listu kroz niz koraka (naredna slika).

Slika 48. Povezivanje podataka u dizajnu - odabir tipa

Napomena: Tipovi iz EF modela (sjetimo se, mehanizam objektno-relacijskog preslikavanja) odabiru


se po istom principu (vrsta izvora je Object, a ne Database!). Konkretni podaci se pridružuju u kodu,
najčešće u obradi događaja Load forme na kojoj je izvršeno povezivanje.
Važniji članovi razreda BindingSource su:

 Svojstva:
o AllowEdit, AllowNew, AllowRemove – indikatori da li je postupak moguć
o DataSource – skup podataka
o DataMember – tablica skupa koja se povezuje
o Count – broj elemenata u listi podataka

108
o Current - aktualni element izvora (povratna vrijednost tipa object)
o Position - indeks aktualnog elementa
 Događaji
o CurrentChanged – promjena Current
o ItemChanged – ažuriran aktualni element List
o PositionChanged – promjena Position
 Postupci
o AddNew – dodavanje novog elementa na izvor
o CancelEdit – opoziv uređivanja koje je u tijeku
o EndEdit - dovršetak uređivanja koje je u tijeku, pohrana na izvoru
o Remove, RemoveAt(int index) – brisanje elementa s izvora
o MoveFirst, MoveLast, MoveNext, MovePrevious – navigacija
o ResetBindings, ResetCurrentItem – ručno osvježavanje podataka. Potrebni su ako ne
postoji mogućnost automatskog osvježavanja grafičkog sučelja uslijed promjena
vrijednosti kroz programski kod.

9.2 Osvježavanje povezivanja


Ako se nekoj povezanoj kontroli promijeni vrijednost ili neko drugo svojstvo za koje postoji vezan
podatak automatski će se promijeniti i izvor na koji je ta kotrola povezana. Slično vrijedi i za izmjene
koje se izvrše kroz BindingSource. Direktna promjena podataka povezanog izvora ne mora ažurirati
povezane kontrole što se može vidjeti u primjeru  Binding.
U primjeru je definiran razred Podatak s dva automatska svojstva Prvi i Drugi te razred
NapredniPodatak sa složenijim svojstvima A i B (programski kod slijedi).

Primjer  Binding - Podatak

namespace Binding {
public class Podatak
{
public string Prvi { get; set; }
public string Drugi { get; set; }
}
}

Na formi primjera dodane su 3 tablice, kontrole mreže s podacima (o kontroli GridView detaljnije u
jednom od narednih poglavlja) povezane na kolekcije Podataka ili NaprednihPodataka te nekoliko
tekstualni okvira (TextBox) za prikaz vrijednosti svojstava Prvi, Drugi, A i B pojedinačnog, trenutno
aktivnog podatka.
Povezivanje podataka ostvareno je sljedećim koracima
1. Na formu su iz alatne trake dovučene tri kontrole tipa BindingSource s nazivima
podatakBindingSource1, podatakaBindingSource2 i napredniPodatakBindingSource
2. Kontrolama je za vrijednost svojstva DataSource postavljeno redom Binding.Podatak,
Binding.Podatak i Binding.NapredniPodatak
3. Na formu su stavljene tri kontrole tipa GridView i četiri kontrole tipa TextBox (uz
odgovarajuće labele)
4. Kontrolama tipa GridView redom su postavljeni izvori podataka (svojstvo DataSource) na
podatakBindingSource1, podatakaBindingSource2 i napredniPodatakBindingSource
109
5. Kontrolama tipa TextBox povezivanje je postavljeno kao na Slika 50 postavljanjem željenog
izvora i svojstva za svojstvo (DataBindings)\Text.

Slika 49. Forma s različitim mogućnostima osvježavanja ovisno o tipu povezanog podatka

Slika 50. Povezivanje sadržaja tekstualnog okvira sa svojstvom na izvoru

Prilikom učitavanja forme u kodu je stvoreno nekoliko podataka i pohranjeno u List<Podatak>,


BindingList<Podatak> odnosno BindingList<NapredniPodatak>, nakon čega su te liste iskorištene kao
konkretni izvor podataka za kontrole tipa BindingSource.

110
Primjer  Binding – Form1

podatakBindingSource1.DataSource = list;
podatakBindingSource2.DataSource = bindingList;
napredniPodatakBindingSource.DataSource = napredniBindingList;

Nakon pokretanja forme klikom na pojedini podatak u tablici automatski se ažurira sadržaj tekstovnih
okvira, jer BindingSource ima funkcionalnost i CurrencyManagera i PropertyManagera (poglavlje 9.1).

9.2.1 Razredi List i BindingList


Klikom na gumb Dodaj stvaraju se novi objekti tipa Podatak i NapredniPodatak te se dodaju u list,
bindingList i napredniBindingList. Slika 51 prikazuje ponašanje forme nakon navedene akcije. Vidljivo
je da se novi podatak prikazao odmah na formi samo u slučaju da je za povezivanje korišten
BindingList.

111
Slika 51. Ponašanje forme prilikom dodavanja podatka direktno na izvorne liste

Sličan efekt može se primijetiti pokuša li se promijeniti lista postavljanjem nekog drugog elementa
umjesto prvog u listi npr. izvršavanjem sljedećeg koda:

Primjer  Binding – Form1

private void btnAzurirajListu_Click(object sender, EventArgs e)


{
Podatak p = new Podatak()
{
Prvi = "Drago",
Drugi = "Dragić"
};
list[0] = p;
bindingList[0] = p;

NapredniPodatak np = new NapredniPodatak()


{
A = "Drago",
B = "Dragić"
};
napredniBindingList[0] = np;
}

Ponašanje forme prikazano je na Slika 52. Promjene su propagirane samo u slučaju kad se koristio
BindingList. Razlog tog ponašanja je što BindingList implementira sučelje IBindingList u kojem je
definiran događaj ListChanged kojim se svim pretplaćenima (u ovom primjeru kontrole GridView su
automatski pretplaćene) šalje obavijest o promjeni liste.

112
Slika 52. Ponašanje forme prilikom promjene sadržaja liste

Kako bi se aktualni sadržaj prikazao na formi pri korištenju kolekcije koja ne podiže događa
ListChanged potrebno je eksplicitno pozvati osvježavanje svih podataka ili trenutno prikazanog
podatka (ResetBindings ili ResetCurrentItem).

113
private void btnOsvjezi_Click(object sender, EventArgs e)
{
podatakBindingSource1.ResetBindings(false);
podatakBindingSource2.ResetBindings(false);
napredniPodatakBindingSource.ResetBindings(false);
}

9.2.2 Rukovanje podacima korištenjem kontrole BindingSource


Ako bi se podatak dodavao direktno kroz BindingSource kao u sljedećem primjeru
Podatak p = new Podatak()
{
Prvi = "Hrvoje",
Drugi = "Horvat"
};

podatakBindingSource1.Add(p);
podatakBindingSource2.Add(p);

novi podatak bi bio odmah vidljiv na ekranu i automatski bi izvor podataka (povezane liste) bio
ažuriran da sadrži novi podatak.

9.2.3 Promjena povezanog podatka i sučelje INotifyPropertyChanged


Za razliku od primjera u 9.2.1 kada se mijenjala lista moguće je programski promijeniti vrijednost
pojedinog svojstva trenutno prikazanog elementa. Učini li se to za Podatak promjene neće biti
vidljive na povezanim kontrolama sve dok se eksplicitno ne pozove osvježavanje.
Za razliku od razreda Podatak, razred NapredniPodatak implementira sučelje INotifyPropertyChanged
koji omogućavaju automatsko ažuriranje povezanih kontrola, jer su povezane kontrole povezane na
događaj PropertyChanged opisan navednim sučeljem. Da bi propagiranje promjena bilo pravilno
izvedeno prilikom svake promjene svojstva potrebno je podići događaj PropertyChanged (ako postoji
barem jedan pretplatnik, što se provjerava usporedbom s null).

114
Primjer  Binding – NapredniPodatak

public class NapredniPodatak : INotifyPropertyChanged


{
public event PropertyChangedEventHandler PropertyChanged;

private string a;
private string b;

public string A {
get { return a; }
set {
a = value;
if (PropertyChanged != null){
PropertyChanged(this, new PropertyChangedEventArgs("A"));
}
}
}
...

Sažeto, vezano za povezivanje vrijedi sljedeće:


 List<Podatak> - dodavanje novih elemenata u listu ili ažuriranje elementa ne propagira
promjene na formu
 BindingList<Podatak> - dodavanje elemenata u listu ili promjena reference za neku poziciju u
listi propagira promjene na formu (događaj ListChanged u IBindingList)
 BindingList<NapredniPodatak> - isto kao i BindingList<Podatak> s tim da propagira promjenu
vrijednosti svojstva
o realizacijom sučelja NapredniPodatak : INotifyPropertyChanged
o događajem PropertyChanged prilikom promjene svojstva objekta

9.3 Povezivanje na podatke korištenjem EF modela


Kontrola BindingSource se veže na neki tip entiteta iz EF modela po uzoru na Slika 48 (npr. na
svojstvu DataSource izvora podataka → Add Project Data Source → Object → pa npr.
EF_Firma.Artikl).
Postavljenoj kontroli BindingSource konkretni podaci se pridruže uzimanjem rezultata upita na bazu
podataka pri čemu nije moguće direktno povezivanje na neki DbSet<T> iz konteksta, jer bi to
uzrokovalo rušenje programa.
Ako će se podaci povezivati samo za čitanje (npr. za prikaz padajuće liste za odabir stranog ključa),
onda se konkretni DbSet<T> ili neki drugi rezultat upita može kopirati u List<T> postupkom ToList()
pri čemu je preporučljivo koristiti opciju AsNoTracking, npr.
ctx.Partner.AsNoTracking().ToList().

Za tako dohvaćene podatke ne vodi se evidencija o promjenama, pa postupak učitavanja bude


značajno brži.
Kad se podaci moraju moći promijeniti koristi se svojstvo Local nekog DbSeta. Svojstvo vraća
kolekciju tipa ObservableCollection<T> i predstavlja sve prethodno učitane elemente u kontekst te
omogućava dodanje novih i ažuriranje postojećih elemenata. Ovakav način prilagođen je za WPF, dok
se za Windows forme ne može direktno koristiti Local već ga treba pretvoriti u BindingList pozivom
postupak ToBindingList (proširenje iz System.Data.Entity)
115
Primjer povezivanja na podatke iz baze podataka korištenjem EF prikazan je na sljedećoj slici uz
pripadajući kod.

Slika 53. Postavljanje jednostavnog povezivanja u dizajnu

Izvor podataka je postavljen na tip EF_Firma.Artikl dok je za tekstualni okvir povezano svojstvo Text
sa šifrom artikla. Dohvat podataka se vrši učitavanjem svih artikala, nakon čega oni bivaju pohranjeni
u kontekst, a zatim se vrši povezivanje na tako učitani kontekst pretvoren u BindingList.

Primjer  ADO\EF_Firma\Artikl

using System.Data.Entity; // zbog ToBindingList()


...
context = new FirmaEntities();

// pripremimo upit
var query = context.Artikl;
await query.LoadAsync(); //dovučemo podatke u kontekst

// prilagodimo podatke
BindingList<Artikl> artikli = context.Artikl.Local.ToBindingList();

// povežemo podatke
artiklBindingSource.DataSource = artikli;

9.3.1 Dinamičko postavljanje povezivanja


Jednostavno povezivanje je povezivanje kontrole koja prikazuje jednu vrijednosti i može se obaviti u
dizajnu (Slika 53) ili dinamički korištenjem svojstva DataBindings pojedine kontrole.

Primjer  ADO\EF_Firma\Artikl

textBoxJedMjere.DataBindings.Add("Text", artiklBindingSource,
"JedMjere", true);
textBoxCijArtikla.DataBindings.Add("Text", artiklBindingSource,
"CijArtikla", true);
checkBoxUsluga.DataBindings.Add("Checked", artiklBindingSource,
"ZastUsluga",true);
pictureBoxArtikl.DataBindings.Add("Image", artiklBindingSource,
"SlikaArtikla", true,
System.Windows.Forms.DataSourceUpdateMode.Never);

Složeno povezivanje je povezivanje kontrole koja prikazuje više podataka (npr. tablica, padajuća
lista, …). Osim postavljanja vrijednosti za DataSource ponekad je potrebno postaviti i vrijednosti
svojstava DataMember (ako izvor podataka ima više tablica ili podskupova, pa se ovim svojstvom bira
željena tablica ili podskup) te svojstava DisplayMember i ValueMember ako se radi o kontrolama kod

116
kojih se prikazani tekst treba razlikovati od vrijednosti (primarnog ključa) elementa. Takav primjer su
padajuće liste za odabir vrijednosti stranog ključa ili liste koje pokazuju opise podataka, a za
označavanje je potrebno koristiti neko drugo svojstvo.

Primjer  ADO\EF_Firma\ArtiklForm

//u dizajnu...
listBoxArtikli.DataSource = artiklBindingSource;
listBoxArtikli.DisplayMember = "NazArtikla";

//u kodu slijedi dohvat konkretnih podataka...


artiklBindingSource.DataSource = context.Artikl.Local.ToBindingList()

9.3.2 Navigacija podacima vlastitim metodama


U primjeru  ADO\EF_Firma\ArtiklForm prikazana je mogućnost navigacije vlastitim metodama i
eksplicitnim pozivanjem postupaka razreda BindingSource. Pomak u izvoru podataka automatski
mijenja označenog u listi, jer je lista povezana s artiklBindingSource. Vrijedi i obrnuto, pomak na
grafičkom sučelju mijenja trenutni element u izvoru podataka.

Slika 54. Forma za rukovanje artiklima

117
Primjer postupaka koji manipuliraju podacima, kao što je pomicanje na naredni zapis liste:

Primjer  ADO\EF_Firma\ArtiklForm

private void buttonNext_Click(object sender, EventArgs e)


{
artiklBindingSource.MoveNext();
...

Aktualizacijom drugog zapisa mijenja se indeks oznake u kontroli prikaza. Stoga u odgovajućem
događaju kotrole pozivamo funkciju koja osvježava informaciju o broju zapisa i statusu označenog
podatka.
private void listBoxArtikli_SelectedIndexChanged(…){
UpdateDisplay();
}
private void UpdateDisplay(){
//ažuriranje statusa na ekranu
if (listBoxArtikli.SelectedIndex != -1 && artiklBindingSource != null){
labelPosition.Text =
((artiklBindingSource.Position + 1).ToString() +
" od " + artiklBindingSource.Count.ToString());
Artikl artikl = (Artikl)artiklBindingSource.Current;
labelRowState.Text = context.Entry<Artikl>(artikl).State.ToString();
}
}

9.3.3 Rad s podacima (dodavanje, izmjena, brisanje)


Novi podatak se može dodati direktno kroz BindingSource pri čemu treba istaknuti da zapis bude
dodan u memoriju računala, ali ne i u bazu podataka ili drugi originalni izvor podataka.

Primjer  ADO\EF_Firma\ArtiklForm

private void buttonAdd_Click(object sender, EventArgs e)


{
//završimo ažuriranje postojećeg elementa
artiklBindingSource.EndEdit();
//stvorimo novi artikl i dodamo ga u binding source
Artikl artikl = new Artikl();
artiklBindingSource.Add(artikl);
//promijenimo koji element je trenutno označen
artiklBindingSource.Position = artiklBindingSource.Count-1;
textBoxSifArtikla.Focus();
}

Uređivanje zapisa provodi se promjenom sadržaja vezanih kontrola, a podaci se automatski spremaju
u memoriju klikom na neku drugu kontrolu, prelaskom na neki drugi zapis ili pozivom EndEdit() na
izvoru podataka. Kontekst preko kojeg su podaci dohvaćeni vodi evidenciju o promjenama , pa će
tako u primjeru s artiklima izmjenom trenutnog artikla status tog artikla biti Modified, a za nove
artikle će biti Added.
Artikl artikl = (Artikl)artiklBindingSource.Current;

118
Vrijednost context.Entry<Artikl>(artikl).State je Modified ako je redak bio promijenjen.
Trenutni zapis može se obrisati pozivom postupka RemoveCurrent , npr.
artiklBindingSource.RemoveCurrent();

pri čemu se taj element unutar konteksta označava za brisanje (status Deleted).
Za snimanje promjena je potrebno pozvati postupak SaveChanges ili SaveChangesAsync na
kontekstu. Navedenim postupcima snimaju se sve promjene unutar transakcije.

9.3.4 Opoziv izmjena


Izmjene podataka evidentirane su u kontekstu, pa je opoziv izmjena moguće napraviti odbacivanjem
cijelog konteksta i ponovnim učitavanjem podataka ili pojedinačnim osvježavanjem. U slučaju
pojedinačnog osvježavanja nove elemente treba odvojiti iz konteksta, a promijenjene vratiti u
početno stanje i po potrebi osvježiti povezivanje trenutnog ili svih elemenata.
Novi elementi se odvajaju postavljanjem stanje na Detached. Promjenom stanja na Unchanged
poništavaju se promjene unutar elementa. Alternativno za promijenjeni element umjesto promjene
na Unchanged može se pozvati postupak Reload.

Primjer  ADO\EF_Firma\ArtiklForm (buttonCancel_Click)

Artikl artikl = (Artikl)artiklBindingSource.Current;


DbEntityEntry<Artikl> entry = context.Entry<Artikl>(artikl);
if (entry.State == EntityState.Added){
entry.State = EntityState.Detached; // odvajanje
}
else {
entry.State = EntityState.Unchanged; // vrati stanje
artiklBindingSource.ResetCurrentItem();
}

Sve elemente iz konteksta za koje se vodi evidencija o promjenama moguće je dobiti pozivom
postupka context.ChangeTracker.Entries() na konkretnom kontekstu nakon čega treba pronaći
promijenjene, obisane i nove (dodane) zapise. Na primjer,
foreach (var entry in context.ChangeTracker.Entries()) {
if (entry.State == EntityState.Added) {
entry.State = EntityState.Detached;
}
else if (entry.State == EntityState.Deleted ||
entry.State == EntityState.Modified) {
entry.State = EntityState.Unchanged;
}

9.4 Validacija unosa podataka


Validaciju je moguće obaviti na razini pojedini kontrole obradom događaja Validating. Za validaciju
svih kontrola na formi potrebno je eksplicitno pozvati postupak ValidateChildren forme. U slučaju
pogreške postavlja se poruka korištenjem kontrole ErrorProvider.

119
Primjer  ADO\EF_Firma\ArtiklForm

private void textBoxNazArtikla_Validating(


object sender, CancelEventArgs e) {
if (string.IsNullOrWhiteSpace(textBoxNazArtikla.Text)){
errorProviderArtikl.SetError(textBoxNazArtikla,
"Form: Naziv artikla ne smije biti prazan");
e.Cancel = true;
}
else{
errorProviderArtikl.SetError(textBoxNazArtikla, string.Empty);
e.Cancel = false;
}
}

U situacijama u kojima se koristi povezivanje podataka na forme bolji i praktičniji način je korištenje
sučelja IDataErrorInfo. Sučelje sadrži

 Svojstvo Error – vraća opis pogreške cijelog objekta (vratiti prazan string ako nema
pogreške)
 Indekser (Item) – za string koji predstavlja naziv svojstva vratiti opis pogreške za konkretno
svojstvo objekta (vratiti prazan string ako nema pogreške)
Za objekte iz EF modela implementiranje ovog sučelja potrebno je obaviti u zasebnoj datoteci (što je
moguće s obzirom da su generirani razredi parcijalni).

Primjer  ADO\EF_Firma\Partial\Artikl.cs

public partial class Artikl : IDataErrorInfo {


public string Error{
get { ... }
}
public string this[string columnName]{ // „Item”
get {
...
if (columnName == "CijArtikla" && CijArtikla <= 0){
return "Cijena artikla mora biti veća od 0";
}
...

Na kontroli ErrorProvider potrebno je postaviti svojstvo DataSource na izvor koji implementira sučelje
IDataErrorInfo. Kontrola ErrorProvider će automatski pozivati indekser predajući naziv svojstva
povezanog s određenom kontrolom. Za vrijeme rada s formom ErrorProvider će prikazavati pogreške,
ali ne sprječava unos neispravnog podatka. Stoga je prije snimanja potrebno provjeriti postoji li
pogreška na povezanom objektu.

error = ((IDataErrorInfo)artiklBindingSource.Current).Error;

120
9.5 Kontrola DataGridView
Kontrola DataGridView služi za mrežnu (tabličnu) obradu podataka s različitih vrsta izvora. Izvori
mogu biti razne liste (sučelje IList), EF modeli, skupovi podataka (DataSet, DataTable), kontrola
BindingSource, i drugi. Izvor podataka se postavlja u svojstvu DataSource, a ako se radi o složenom
izvoru podataka (koji ima i ugnježđene kolekcije koje mogu biti izvori podataka) za odabir konkretnog
izvora podataka u složenom izvoru koristiti se svojstvo DataMember (vidi primjer s dokumentima i
stavkama u poglavlju 9.7.4).

Slika 55. Primjer kontrole GridView na popisu svih država

Uobičajeni način korištenja kontrole DataGridView je takav da se iz alatne trake na formu dodaju
kontrola DataGridView i kontrola BindingSource nakon čega slijedi podešavanja kontrole
BindingSource odabirom tipa podatka koji će služiti kao izvor podataka.

Slika 56. Odabit tipa podatka koji će služiti kao izvor podataka

U slučaju da među postojećim izvorima podataka u projektu nema željenog razreda (primjerice, ako
ga prvi put koristimo u projektu), onda je potrebno odabrati opciju Add Project Data Source i prvo
odabrati vrstu izvora. Za vrstu izvora se može postaviti nekoliko tipova izvora, ali u konkretnom
primjeru bit će odabran tip Object, nakon čega slijedi odabir konkretnog tipa podatka koji će biti u
izvoru podataka.

121
Slika 57. Odabir vrste izvora i konkretnog tipa podatka

Nakon što je kontrola BindingSource podešena, kontroli DataGridView postavlja se svojstvo


DataSource na prethodno podešenu kontrolu tipa BindingSource23. Kontrola DataGridView će tada
automatski dodati sve stupce iz izvora te se može krenuti u ručno podešavanje stupaca po želji.

Slika 58. Pridruživanje izvora podataka kontroli DataGridView

Prethodnim koracima definirana je vrsta izvora podatka i podešeni su stupci u kontroli DataGridView.
Konkretni podaci se pridružuju izvoru podataka (kontroli tipa BindingSource) prilikom učitavanja
forme. Ako je moguće, poželjno je koristiti async i await da se ne blokira glavnu nit. Podaci se
željenim upitom učitaju u trenutni kontekst Entity Frameworka, a zatim se tako lokalno pohranjeni
podaci pretvore u BindingList i postave kao izvor podataka.

Primjer  ADO \ EF_Firma \ DokumentStavkaForm

private async void DrzavaForm_Load(...) {


await LoadData();
}

private async Task LoadData() {


...
context = new FirmaEntities();
var query = context.Drzava.
OrderBy(d => d.OznDrzave); //definiramo upit
await query.LoadAsync(); //napunimo podatke u kontekst
// uzmemo podatke koji su dosad učitani
drzavaBindingSource.DataSource =
context.Drzava.Local.ToBindingList();
}

23
Napomena: Postupak se može skratiti tako da se kod odabira izvora za DataGridView odmah ide na Add
Project Data Source što automatski doda novi BindingSource.
122
9.6 Kontrola BindingNavigator
Kontrola BindingNavigator je komponenta grafičkog sučelja za navigaciju i rukovanje povezanim
podacima koja je pozicionirana na vrhu kontrole i nudi već unaprijed gotovu funkcionalnost
navigacije podacima u povezanom izvoru podataka.

Slika 59. Kontrola BindingNavigator

Izvor podataka se postavlja u svojstvu BindingSource. Na kontrolu je moguće dodati vlastite gumbe,
ukloniti postojeće i definirati obrade događaja za svaki gumb. Funkcionalnost ugrađenih gumba
ostvaruje se time što kontrola BindingNavigator incijalno ima već postavljena svojstva MoveFirstItem,
MoveLastItem, MoveNextItem, MovePreviousItem, PositionItem, AddNewItem i DeleteItem na
ugrađene gumbe što je po potrebi moguće promijeniti.

9.7 Složene zaslonske maske


Složene zaslonske maske oblika zaglavlje–stavke (engl. master-detail) sastoje se od dva dijela u vezi
1:N. Zaglavlje je dio forme na kojem se ažurira pojedinačni zapis, a detalji su dio forme s podacima
zavisnim o zaglavlju. Primjer takve maske je Dokument-Stavka s donje slike. Prikaz i obrada detalja
sinkronizirana je s promjenama zaglavlja, pa se tako za svaki dokument prikazuju samo njegove
stavke.

Slika 60. Primjer složene zaslonske maske oblika zaglavlje-stavke

9.7.1 Potrebne preinake na Entity Framework modelu


Primjer zaglavlje-stavke ostvaruje se tako da je izvor podataka za detalje dio izvora podataka za
zaglavlje. U prethodno prikazanom Entity Framework modelu (ekstenzija edmx) veza između entiteta
Dokument i Stavka nazvana je Stavke umjesto inicijalno Stavka. Dodatno je potrebno napraviti
preinake u već generiranom EF modelu budući da HashSet koji se automatski koristi za ostvarivanje

123
veze nije pogodan za povezivanje detalja. U konstruktoru razreda Dokument (Firma.edmx -> Firma.tt
-> Dokument.cs) treba promijeniti naredbu this.Stavke = new HashSet<Stavka>(); u
this.Stavke = new BindingList<Stavka>(); te public virtual
ICollection<Stavka> Stavke { get; set; } promijeniti u public virtual
BindingList<Stavka> Stavke { get; set; }.

Ponovnim generiranjem modela promjene će se poništiti te ih treba ponovo ručno izmijeniti.


Alternativa ponovnoj promjeni je modificiranje T4 (.tt) predloška.

9.7.2 Izračunata polja


Entiteti Dokument i Partner prošireni su svojstvom LookupText u svrhu lakšeg prikaza u padajućoj
listi, a entitet stavka je proširena svojstvom UkupnaCijena. Proširenja su obavljanja u posebnoj
datoteci, što je moguće jer su generirani entiteti parcijalni razredi.

Primjer  ADO \ EF_Firma \ Partial \ Dokument.cs

partial class Dokument{


public string LookupText {
get { return string.Format("{0} - {1} - {2}",
DatDokumenta.ToString("dd.MM.yyyy"), IdDokumenta, IznosDokumenta);
...

Primjer  ADO \ EF_Firma \ Partial \ Dokument.cs

partial class Stavka{


public decimal UkupnaCijena{
get{
return this.KolArtikla * this.JedCijArtikla *
(1 - this.PostoRabat);
...

9.7.3 Odabir vrijednosti stranog ključa


Za izvedbu padajuće liste za odabir parnera, dokumenta, artikla, … potrebno je dodati izvor podataka
koji će se naknadno popuniti vrijednostima iz baze podataka (ili nekog drugog izvora). U slučaju
padajuće liste za partnera dodaje se nova kontrola tipa BindingSource kojoj se za tip izvora postavlja
razred Partner.

Slika 61. Svojstva izvora podataka za odabir partnera

124
Slika 62. Pridruživanje izvora podataka padajućoj listi

Padajućoj listi se kao izvor podataka pridružuje tako definirana kontrola BindingSource (u primjeru sa
slike kontrola ima naziv partnerBindingSource) te se odabire svojstvo za prikaz (postavlja se pod
DisplayMember), za vrijednost (ValueMember) te svojstvo koje predstavlja naziv stranog ključa
(SelectedValue). U primjeru sa slike vrijednosti ovih svojstva su>

 Display Member: izvedeno polje LookupText iz entiteta Partner


 Value Member: IdPartnera (primarni ključ entiteta Partner)
 Selected Value: dokumentBindingSource.IdPartnera (naziv svojstva u entitetu Dokument koje
je strani ključ prema entitetu Partner).
Konkretno, odabirom nekom teksta iz padajuće liste s popisom partnera, IdPartnera koji predstavlja
vrijednost odabranog elementa u listi automatski se pridružuje u svojstva IdPartnera u trenutno
dokumentu na formi.
Dohvat konkretnih vrijednost obavlja se prilikom učitavanja forme pri čemu je bitno naglasiti da se
prvo popunjavanju izvori za padajuće lista, a tek onda izvor za zaglavlje. Budući da se podaci u
padajućoj listi ne mijenjaju, koristi se opcija AsNoTracking, pa se odbacivanjem mogućnosti
evidencije promjena entiteta ubrzava dohvat podataka za padajuće liste.

Primjer  ADO \ EF_Firma \ DokumentStavkaForm

partnerBindingSource.DataSource = await context.Partner


.AsNoTracking().ToListAsync();

Na sličan način realizirana je i padajuća lista za odabir prethodnog dokumenta. Potrebno je dodati
novi izvor podataka, jer kad bi se koristiti isti izvor kao za osnovne podatke izbor bi bio ograničen
samo na trenutno dohvaćene i povezane podatke.

Slika 63. Povezivanje padajuće liste za odabir prethodnog dokumenta

125
Za odabir prethodnog dokumenta vrijednosti svojstava padajuće liste su:

 Display Member: izvedeno polje LookupText iz entiteta Dokument


 Value Member: IdDokumenta (primarni ključ entiteta Dokument)
 Selected Value: dokumentBindingSource.IdPrethDokumenta (naziv svojstva u entitetu
Dokument koje je rekurzivna veza prema entitetu Dokument).

9.7.4 Sinkronizacija stavki


Sinkronizacija stavki ostvaruje se tako da je izvor podataka za detalje dio izvora podataka za zaglavlje.

Slika 64. Sinkronizacija stavki u primjeru zaglavlje-detalji

U konkretnom primjeru za izvor podataka u zaglavlju se koristi kontrola tipa BindingSource imena
dokumentBindingSource koja za tip podatka ima postavljen Dokument iz EF modela, a za stavke se
koristi novi izvor podataka čiji tip podatka nije Stavka iz EF modela, već dokumentBindingSource koji
je složeni izvor podataka (tip podatka Dokument) koji u sebi sadrži kolekciju Stavke (vidi poglavlje
9.7.1).

9.7.5 Početne i referentne vrijednosti stavki


Stupac sa stavkama pretvoren je iz običnog stupca u stupac tipa DataGridViewComboBoxColumn pri
čemu je za DisplayStyle odabrana opcija ComboBox. Alternativno, umjesto ComboBox, može se
odabrati opcija Nothing, pa se padajuća lista pojavljuje samo pri ažuriranju. Kao i u prethodnim
primjerima za padajuće liste (poglavlje 9.7.3) vrši se postavljanje svojstvama kojim se određuju
svojstva za prikaz i vrijednost elemenata u padajućoj listi te svojstvo koje ima ulogu stranog ključa.

126
Slika 65. Postavljanje padajuće liste za odabir artikla u stavkama

Specifičnost funkcioniranja DataGridViewa kod dodavanja stavke uzrokuje stvaranje stavke s praznim
(null), odnosno pretpostavljenim vrijednostima. Problem nastaje kad referentna vrijednost za
padajuću listu ne može biti null, odnosno ako nema elementa s pretpostavljenom vrijednošću, što
uzrokuje pogrešku s porukuom „datagridviewcomboboxcell value is not valid“. Rješenje problema je
dodati element s pretpostavljenom vrijednosti za određeni tip. Konkretno za artikle, potrebno je
dodati artikl sa šifrom 0, što je vrijednost od default(int).

Primjer  ADO \ EF_Firma \ DokumentStavkaForm

private async Task LoadDataSources() {


context = new FirmaEntities();
List<Artikl> artikli = await context.Artikl
.AsNoTracking().ToListAsync();
artikli.Insert(0, new Artikl {
SifArtikla = 0,
NazArtikla = "--------Odaberite artikl---------"
});
artiklBindingSource.DataSource = artikli;
...

Promjenom artikla u padajućoj listi potrebno je ažurirati stupac s jediničnom cijenom artikla te iznos
dokumenta. Navedeno se obavlja prilikom obrade događaja promjene ćelije u kontroli DataGridView
pri čemu treba provjeriti koji je stupac izazvao događaj.

127
Primjer  ADO \ EF_Firma \ DokumentStavkaForm

private async void dataGridViewStavke_CellEndEdit(


object sender, DataGridViewCellEventArgs e){
stavkeBindingSource.EndEdit();
Stavka stavka = (Stavka)stavkeBindingSource.Current;
if (e.ColumnIndex == 0){ // stupac s artiklom
//dohvati cijenu artikla
Artikl artikl = await context.Artikl
.FindAsync(stavka.SifArtikla);
stavka.JedCijArtikla = artikl.CijArtikla;
}
AzuriraCijenuDokumenta();
}
private void AzurirajCijenuDokumenta(){
Dokument d = (Dokument)dokumentBindingSource.Current;
d.IznosDokumenta = d.Stavke.Sum(s => s.UkupnaCijena) *
(1 + d.PostoPorez);
}

...

9.7.6 Dodavanje novog dokumenta


Dodavanje novog dokumenta obavlja se automatski klikom na ikonu s oznakom + u navigacijskoj
traci. Navedeni gumb uzrokuje dodavanje novog podatka u izvoru podataka, ako izvor podržava
dodavanje novih elemenata.

Slika 66. Gumb u navigacijskoj traci koji dodaje novi element u izvor podataka

Dodatno, moguće je postaviti predviđeni (engl. default) broj dokumenta i ažurirati prikaz navigatora
tako da onemogućimo sve kontrole osim gumba za snimanje i odustajanje od dodavanja novog
elementa.

Primjer  ADO \ EF_Firma \ DokumentStavkaForm

private void bindingNavigatorAddNewItem_Click(...) {


((Dokument)dokumentBindingSource.Current).BrDokumenta =
context.Dokument.
Max(d => d.BrDokumenta) + 1;
UpdateDisplay(true);
}

Napomena: Podaci još nisu pohranjeni u bazi podataka!

9.7.7 Spremanje podataka


Snimanje podataka aktivira se klikom na odgovarajući gumb u navigacijskoj traci. Budući da je
korisnik neposredno prije klika na gumb mogao ažurirati vrijednost neke kontrole u glavnom dijelu ili
neke ćelije u stavkama, potrebno je prvo pozvati postupak kojim se prihvaćaju promjene za kontrole
koje su trenutno aktivne24. S obzirom da u razredu Dokument nema mehanizma dojava promjena

24
Prihvaćanje promjena bi se automatski dogodilo prelaskom na drugu kontrolu i gubitkom fokusa s kontrole u
kojoj se vršilo ažuriranje, ali klikom na gumb (na navigacijskoj traci) ne dolazi do prelaska na drugu kontrolu.
128
svojstvama čime bi se automatski mogao pokrenuti izračun cijene dokumenta, cijenu je potrebno
eksplicitno izračunati. Prije snimanja potrebno je provjeriti ima li pogrešaka na dokumentu za što se
koristi svojstvo Error iz sučelja IDataErrorInfo. Promjene u kontekstu se spremaju pozivom postupka
SaveChanges ili SaveChangesAsync.

Primjer  ADO \ EF_Firma \ DokumentStavkaForm

private async void toolStripButtonSave_Click(...) {


stavkeBindingSource.EndEdit(); dokumentBindingSource.EndEdit();
AzurirajCijenuDokumenta();
Dokument dokument = (Dokument)dokumentBindingSource.Current;
string error = dokument.Error; //Error iz IDataErrorInfo
if (!string.IsNullOrWhiteSpace(error)) {
...
}
else {
await context.SaveChangesAsync();
...

Nakon snimanja EF automatski ažurira vrijednosti samopovećavajućih primarnih ključeva, kao i


ključeve roditelja u stavkama.

9.7.8 Brisanje zapisa zaglavlja (skupa s detaljima)


Na navigacijskoj traci unaprijed je definiran gumb za brisanje trenutno elementa u izvoru podataka.
Budući da je prije brisanja potrebno pitati korisnika za potvrdu, potrebno je promijeniti
pretpostavljeno ponašanje gumba za brisanje. Stoga je potrebno na kontroli BindingNavigator
svojstvo DeleteItem postaviti none, a obraditi događaja klika. Brisanje dokumenta vrši se uklanjanjem
iz izvora podataka. Alternativno se može pronaći u kontekstu s context.Entry i promijeniti mu stanje
na Deleted. Spremanje promjena u kontekstu vrši se pozivom postupka SaveChangesAsync ili
SaveChanges ovisno želi li se asinkrona varijanta ili ne. Stavke dokumenta se brišu kaskadno, što je
postavljeno u prethodno definiranom Entity Framework modelu.

Primjer  ADO \ EF_Firma \ DokumentStavkaForm

private async void bindingNavigatorDeleteItem_Click(...){


DialogResult result = MessageBox.Show
("Želite li obrisati zapis",
"Brisanje zapisa", MessageBoxButtons.YesNo);
if (result == System.Windows.Forms.DialogResult.Yes) {
dokumentBindingSource.RemoveCurrent();
await context.SaveChangesAsync();
}
}

9.7.9 Brisanje pojedinačne stavke


Za brisanje pojedinačne stavke potrebno je obaviti eksplicitno obradom događaja brisanja retka u
GridViewu. Razlog leži u načinu rada Entity Frameworka i GridViewa. Brisanje retka umjesto brisanja
stavke briše vrijednost stranog ključa stavka, što nije dopuštena situacija i izaziva pogrešku prilikom
snimanja. Programski odsječak koji ispravno briše stavke dan je u sljedećem primjeru.

129
Primjer  ADO \ EF_Firma \ DokumentStavkaForm

private void dataGridViewStavke_UserDeletingRow (...) {


List<Stavka> stavkeZaBrisanje = new List<Stavka>();

for (int i=0; i < dataGridViewStavke.SelectedRows.Count; i++){


DataGridViewRow row = dataGridViewStavke.SelectedRows[i];
stavkeZaBrisanje.Add((Stavka)row.DataBoundItem);
}
foreach(Stavka stavka in stavkeZaBrisanje){
context.Stavka.Remove (stavka);
}
e.Cancel = true;//otkaži normalno izvođenje ovog događaja
AzurirajCijenuDokumenta();
}

Stavke za brisanje su prvo kopirane u pomoćnu listu, pa tek onda brisane, jer bi brisanje stavke
unutar prve petlje uzrokovalu promjenu vrijednosti broja označenih redaka i potencijalno promijenilo
indekse stavki koje treba obrisati. Nakon što se stavke eksplicitno obrišu, potrebno je postaviti
svojstvo Cancel drugog argumenta obrade događaja na false kako bi se otkazalo normalno izvođenje
događaja (postavljanje stranog ključa na null).

9.7.10 Opoziv izmjena


Za opoziv izmjena potrebno je pomoću svojstva ChangeTracker na trenutnom kontekstu Entity
Frameworka dohvatiti sve entitete za koje se evidentira promjena. Među takvima, potrebno je
pronaći nove (dodane) i odspojiti ih iz konteksta. Ostalima koji mogu biti promijenjeni ili označeni za
brisanje potrebno je postaviti stanje na Unchanged što će uzrokovati vraćanje originalni vrijednosti.
Nakon otkazivanja promjena na izvoru je potrebno pozvati postupak ResetCurrentItem() kako bi se
promjene prikazale na povezanim kontrolama grafičkog sučelja. Razlog eksplicitnog osvježavanja je
činjenica da entiteti ne implementiraju sučelje INotifyPropertyChanged koje služi za dojavu promjena.

Primjer  ADO \ EF_Firma \ DokumentStavkaForm

private void CancelChanges(){


foreach (var entry in context.ChangeTracker.Entries()) {
if (entry.State == EntityState.Added)
{
entry.State = EntityState.Detached;
}
else if (entry.State == EntityState.Deleted
|| entry.State == EntityState.Modified)
{
entry.State = EntityState.Unchanged;
...

9.8 Zadaci
Zadatak 1. U tablicu Artikl u bazi podataka dodati sve artikle iz neke Excel datoteke. Za dodane
zapise provjeriti ima li jedinicu mjere iz skupa { "h", "kom", "kg", "l", "pak" } . Ukoliko
nema, ažurirati jedinicu mjere vrijednošću "---". Ažuriranje provesti kreiranjem
odgovarajuće SQL naredbe za svaki zapis koji treba mijenjati.
Zadatak 2. Doraditi Artikl dodavanjem tablice JedinicaMjere i veze s artiklom.

130
Zadatak 3. Ugraditi sortiranje i filtriranje podataka u Drzava.
Zadatak 4. Ugraditi sortiranje i filtriranje podataka u Artikl.
Zadatak 5. Napisati program koji će ažurirati tablicu Artikl tako da obradi sadržaj mape u kojoj se
nalaze slike naziva oblika <IdArtikla>.jpg
Zadatak 6. U formi DokumentStavke u DataGridView za prikaz stavki dodati stupac za šifru
artikla te je povezati na isti izvor kao i naziv artikla.
Zadatak 7. Implementirati validaciju za primjer DokumentStavke.
Zadatak 8. Što sve može biti izvor podataka za povezivanje na kontrolu na formi?
Zadatak 9. Što je jednostavno (Simple Binding) a što složeno povezivanje (Complex Binding) na
bazu podataka?
Zadatak 10. Koja je razlika između CodeFirst i ModelFirst tehnike izrade EF modela?

131
10 Korisničko sučelje i dijalozi
Sučelja aplikacije dijele se na korisničko sučelje (engl. user interface) koje definira interakciju s
krajnjim korisnikom te na sučelje sustava (sistemsko sučelje, engl. system interface) koje određuje
način razmjene informacija s drugim sustavima.
Osnovni mehanizmi korisničkih sučelja su
 navigacijski mehanizam – osigurava način na koji korisnici određuju što žele napraviti
 ulazni mehanizam – određuje način prihvata informacija
 izlazni mehanizam – određuje način pružanja informacija korisnicima ili drugim sustavima

10.1 Načela oblikovanja korisničkog sučelja


10.1.1 Raspored
Načelo rasporeda (engl. layout) odnosi se na organizaciju sučelja pri čemu treba voditi računa da
programska oprema mora imati standardan izgleda zaslona, logički grupirana polja zaslonske maske
te pravilno poravnavanje.
Zaslon je uobičajeno podijeljen na područje za navigaciju (najčešće izbornici na vrhu zaslona),
područje za prikaz statusa obrade i podataka (najčešće na dnu) i radno područje u sredini za rad s
podacima.
Polja za unos moraju biti logički grupirana pa se tako npr. grupiraju osobni podaci, podaci o adresama
i kontaktima, podaci o članovima obitelji, školovanju, napredovanju itd. Tako grupirani podaci mogu
se rasporediti na različite zaslonske maske (forme, prozore), grupirati unutar različitih spremnika
(panela, tabulatora) ili učahuriti u vlastite korisničke kontrole.
Kontrole moraju biti poravnate horizontalno i vertikalno pri čemu se uobičajeno koristi lijevo
poravnanje polja za unos i desno poravnanje njihovih labela. Slično, nizovi znakova se poravnavaju
lijevo, a brojevi desno.

10.1.2 Uvažavanje sadržaja


Načelo uvažavanje sadržaja (engl. content awareness) propisuje da u svakom trenutku mora biti
vidljiva informacija o

 dijelu obrade (unos, izmjena podataka, …)


 vrsti prikazanih podataka (osoba, račun, …)
 količini podataka (zapis m od n)
 mogućim akcijama (aktivne kontrole)
Elementi sučelja trebaju se odnosti na poslovne objekte, a ne na tehničke aspekte aplikacije, odnosno
objekti sučelja trebaju oponašati izgled i ponašanje stvarnih objekata koje prikazuju (npr.
dokumente).

10.1.3 Estetika
Pri izradi korisničkog sučelja treba voditi računa o preglednosti podataka i estetici (engl. aesthetics).
Stoga je poželjno izbjegavati sažimanje kontrola na uskom prostoru te minimizirati neiskorišten
prostor i prekrivanje npr. u slučaju više prikazanih „prozora“.

132
Dodatno se estetika poboljšava pravilnom upotrebom pisma i boja. Obično se preporuča koristiti
maksimalno 4 različite veličine slova na ekranu (uz opasku da web može biti „šareniji“) i do 3 vrste
fonta na jednom ekranu uz korištenje kombinacije velikih i malih slova (izbjegavati kapitalizaciju). Pri
korištenju boja potrebno je paziti na kontrast i npr. koristiti tamni tekst na svijetloj podlozi i obrnuto
uz korištenje najviše 4 različite boje na ekranu. Određene boje nisu preporučljive za prikaz teksta,
npr. plava koja se teško čita i uobičajena je za poveznice).
Neaktivne kontrole je bolje posebno obojati (npr. u sivo), nego ih skrivati.

10.1.4 Iskustvo korisnika


Sučelje mora biti prilagođenu iskustvu korisnika (engl. user experience) na način da ujedno
omogućava lako učelje korištenja početnicima te lako korištenje naprednim korisnicima korištenjem
npr. ubrzavajućih tipki (engl. accelerator key).
Svakako je potrebno ugraditi interaktivnu pomoć ovisno o kontekstu. Interaktivnom pomoći i
uputama, uz primjenu dosljednosti, korisnike treba navoditi na služenje programom. Manja potreba
za uputama i veća intuitivnost pridonose kvalitetnijem sučelju.
Poruke sučelja moraju biti ujednačene i standardizirane tako da budu jednostavne, precizne te ovisne
o kontekstu izbjegavajući računalski žargon i kratice. Poruke trebaju biti prevedene na jezik korisnika,
osim npr. iznimki operacijskog sustava ili sustava za upravljanje bazama podataka.

10.1.5 Dosljednost
Za ostvarivanje dosljednosti (engl. consistency) korisničkog sučelja potrebno je kontrole istog tipa
svuda postavljati tako da jednako izgledaju i da se jednako ponašaju te se trebaju nalaziti na
uobičajenim/očekivanim mjestima (gumbi, status, …).
Dosljednost je najvažniji faktor za pojednostavljivanje načina korištenja sustava. Dosljednost skraćuje
krivulju učenja i omogućuje korisnicima predviđanje što će se dogoditi u pojedinom trenutku. Nakon
što svladaju interakciju s jednim dijelom sustava, korisnici će se znati služiti i drugim dijelovima
sustava.
Kao i kod standarda kodiranja potrebno je postaviti i pridržavati se standarda izgleda (veličina, omjer,
oblik, boja), naslova i ponašanja kontrola i standardizacije značenja tipki (npr. CTRL+C).

10.1.6 Lakoća korištenja


Potrebno je minimizirati napor korisnika (eng. minimal user effort) prilikom korištenja na način da se
unos mora obavljati u slijedu kojim su polja fizički poredana npr. s lijeva nadesno ili od gore prema
dolje što se obično obavlja postavljanjem svojstva TabIndex pojedine kontrole.
Za pojedine podatke odnosno akcije moguće je koristiti predviđene (engl. default) vrijednosti
odnosno predviđene, nedestruktivne kontrole (npr. „Ne“ prilikom zahtjeva za potvrdu brisanja).
Tamo gdje je moguće, unos je moguće ograničiti listama predefinarnih skupova podataka odnosno
mogućim vrijendosti vezanih podataka (strani ključevi). Također, lakoća korištenja ostvaruje se
ulančavanjem procedura, to jest omogućavanjem slijeda otvaranja zaslonskih maski koje prate
poslovni proces.

133
Općenito, preporuča se poštovati pravilo tri klika (engl. three clicks rule) koje propisuje da se treba
omogućiti pristup od izbornika do podatka u najviše tri koraka (klika mišem ili tipkovničkih
kombinacija).

10.2 Vrste korisničkog sučelja


Aplikacija može imati tri vrste korisničkog sučelja
 Sučelje s pojedinačnim dokumentima (engl. Single document interface – SDI) sastavljeno od
nezavisno otvorenih i odvojenih formi, od kojih svaka sadrži po jedan „dokument“. Svaka od
formi pojavljuje se i u taskbaru. Primjer  ADO\EF_Firma\SDIForm
 Sučelje s više dokumenata (engl. Multiple document interface – MDI) u kojem se aplikacija
sastoji od jedne ili više formi sadržane unutar zajedničkog glavnog prozora. U taskbaru se vidi
samo glavni prozor. Primjer  ADO\EF_Firma\MainForm
 Sučelje s karticama dokumenata (engl. Tabbed document interface – TDI) u kojem su različiti
dokumenti sadržani unutar jednog prozora, ali na panelima kontrole Tab. Primjer takvog
sučelja je razvojna okolina Visual Studio.

10.3 Izbornici i dijalozi


Sučeljem moraju biti podržane različite vrste izbornika:
 Horizontalni izbornik (menu bar) – uvijek vidljiv i lako dohvatljiv
 Padajući (pull down) i kaskadni izbornik – inicijalno nevidljiv, sadrži grupirane opcije i
pojavljuje se aktiviranjem nekog vidljivog izbornika
 Skočni (pop-up, brzi) – nije očigledno da postoji, skače na različitim mjestima, obično na desni
klik miša
 Trake s ikonama (toolbar, toolbox, ribbon) – vidljivi, pamtljivi uz mogućnost dinamičkog
osposobljavanja i prikaza/skrivanja ikona
Opcijama izbornika poželjno je omogućiti prikaz ubrzavajućom i/ili funkcijskom tipkom. Primjerice
dodavanjem znaka & ispred slova u nazivu kontrolu se može aktivirati kombinacijom Alt + Slovo.
Tipke za obavljanje standardnih funkcija moraju biti definirane pažljivo i jednoznačno (npr. F1-Pomoć,
F2-Unos, F3-Izmjena, … ESC – Opoziv) te unaprijed treba predvidjeti i one za aktiviranje dodatnih
funkcija (npr. tipku koja će obavljati funkciju ovisno o kontekstu, F8-Obrada ili F8-Storno).
Pri kreiranju izbornika ne treba pretjerivati sa širinom i dubinom izbornika, a prikaz poruka mora biti
konzistentan uz korištenje punih rečenica pozitivnog oblika (npr. izbjegavati dvostruke negacije i
slično).

10.4 Kriterij za odabir kontrola grafičkog sučelja


Za omogućavanje unosa slobodnog teksta na raspolaganju su Textbox i razne podvarijante (npr.
RichTextBox) pri čemu tekst može biti maskiran (npr. telefonski broj, registarska oznaka). Unos više
redaka i kliznike (ScrollBar) treba koristiti samo kad je to stvarno potrebno te treba obratiti pažnju na
duljinu teksta i tip podatka.
U slučajevima kada unos nije u formi slobodnog teksta, već korisnik bira jednu ili više od ponuđenih
opcija primjerenije je koristiti neke druge kontrole. Za manji broj (do 3) unaprijed poznatih i
vremenski nepromijenjive vrijednosti preporuča se koristiti CheckBox ako su opcije istovremeno
134
moguće, a ako se vrijednosti međusobno isključuju koristi se Radio Button. Za slučaj s većim brojem
mogućih vrijendosti koriste se List Box odnosno Combo Box.
Drop-Down Box ili Combo Box koristi se za umjereno veliki broj (do nekoliko stotina) međusobno
isključibih vrijednosti pri čemu se obično aktiviranjem istovremeno na ekranu može vidjeti 20
vrijednosti. U slučaju većeg broja zapisa koristi se editabilni combo za unos filtra ili se kontrola
nadomješta posebnom formom za filtriranje i odabir (tzv. lookup).
List Box služi za odabir umjererno velikog broja (nekoliko desetaka) ne nužno isključivih vrijednosti pri
čemu se istovremeno prikazuje 8-10 vrijednosti. U slučaju većeg broja zpisa ili zapisa složenije
strukture nadomješta se rešetkom (Grid)
Kontrola NumericUpDown služi za odabir jedne vrijednosti u nevelikom slijedu (nekoliko desetaka)
diskretnih vrijednosti. U slučaju većeg broja vrijednosti potrebno je omogućiti izravan upis ili
nadomjestiti listom.
Kontrol naziva GridView ili sl. (rešetka, mreža, tablica, matrica) koristi se za prikaz zapisa s malim
brojem atributa pri čemu je bitno da se svi atributi zapisa vide odjednom. Ovisno o vrsti kontrole Grid
može sadržavati kombinaciju osnovnih elemenata.

10.5 Zadaci
Zadatak 1. Kojim načelima oblikovanja sustava pripadaju sljedeći opisi:
a) Poželjno je koristiti najviše 3 različite vrste pisma na ekranu.
b) Objekti sučelja trebaju oponašati izgled i ponašanje stvarnih objekata koje prikazuju.
c) Poželjno je elemente zaslonske maske grupirati u spremnike (container) ili panele.
Zadatak 2. Koju kontrolu grafičkog sučelja odabrati za unos pozitivne cijelobrojčane vrijednosti
(uz pretpostavku da neće biti potrebno unijeti broj veći od 10)? Koju kontrolu
odabrati ako se očekuje unos većih brojčanih vrijednosti?

135
11 Dizajn sustava

11.1 Opći dizajn


Svrha općeg dizajna (konceptualnog, visoke razine, arhitekturnog) je izrada funkcionalne
specifikacije. U ovoj fazi oblikovanja provodi se:
 Odabir tehničke arhitekture sustava:
o Koristiti centraliziranu ili distribuiranu obradu i pohranu podataka?
o Kako i koje tehnologije koristiti?
o Nabaviti gotovi softver, napraviti po mjeri ili koristiti mješavinu ova dva pristupa?
o Koje razvojni alate koristit?
 Analiza i distribucija podataka:
o Potrebno je pretvoriti konceptualni modela podataka u logički model (relacijski,
postrelacijski, objektno-relacijski), ako to nije već učinjeno ranije.
 Analiza i distribucija procesa:
o Potrebno je pretvoriti logički model procesa u fizički model za odabranu arhitekturu i
kreirati shemu aplikacije.
 Opći dizajn sučelja:
o Definirati izgled i ergonomiju aplikacije, tzv. ‘look and feel’.

11.2 Detaljni dizajn


Detaljni dizajn je dizajn na nižoj razini i predstavlja tehničku specifikaciju sustava. Izrađuje se fizički
model podataka pri čemu se logički model pretvara u fizički model podataka za odabrani SUBP te se
kreira shema baze podataka. Model se prilagađava mogućnostima i ograničeninjima odabranoh
SUBP-a te se određuju fizički parametri (volumetrija) baze podataka i ugađanje baze podataka (npr.
korištenjem indeksa).
Dizajnom programa utrvđuje se struktura programa na temelju modela procesa pri čemu se logički
proces ili skup procesa povezuje s jednim ili više modula ili razreda te se precizira programska logika.
Korištenjem nekog od jezika za za projektiranje programa (eng. Program Design Language - PDL) koji
ima oblik pseudokoda moguće je zahtjeve na programsku podršku pretvoriti u oblik koji omogućuje
programiranje.
Dizajnom sučelja opisuju se protokoli pristupa i razmjene podataka i oblikuju se zaslonke maske i
izvješća.

11.3 Dizajn arhitekture sustava


Dizajn arhitekture sastoji se od planova koji definiraju pojedine komponente sustava: računalnu
opremu, programsku podršku, komunikacije, sustav zaštite i globalnu podršku aplikacije.
Uobičajeni modeli arhitekture su poslužiteljska arhitektura (server-based) u kojoj se obrada obavlja
na poslužitelju, klijentska (client-based) u kojoj se obrada obavlja na osobnom računalu i arhitektura
klijent-poslužitelj (client-server based) kao kombinacija prethodne dvije.
Model mreže prikazuje glavne komponente sustava, njihove fizičke lokacije i način njihovog
međusobnog povezivanja.

136
Specifikacija računalne opreme (hardvera) i programske podrške (softvera) služi kao podloga (popis
stavki) za nabavku ili izradu informacijskog sustava.

11.4 Elementi arhitekture sustava


Osnovne funkcije sustava su pohrana podataka (engl. data storage, baza podataka ili neko spremište
podataka općenito), pristup podacima (eng. data access logic, npr. ADO.NET, Entity Framework, …),
elementi obrade (engl. aplication logic, npr. aplikacijski program, pohranjene procedure), te sučelje
(eng. presentation logic, npr. zaslonske maske).
Osnovne hardverske komponente sustava su klijenti, poslužitelji i mreža koja ih ih povezuje.
Poslužitelji mogu biti različitog tipa, npr. velika računala (engl. mainframe), mala računala (eng. mini
computer) ili klasični PC kao primjer mikroračunala (engl. micro computer). Klijenti mogu biti različiti
terminali bilo klasični (ansi, vt220, IBM 3270, …) ili posebne namjene (bankomati, inernet kiosci,
ručna računala, …), pametni telefoni ili mikroračunala poput PC-a koji po potrebi mogu emulirati
terminale, pristupati udaljenim radnim plohama (Remote Desktop Connection) ili izvoditi dio obrade
ovisno o vrsti obrade podataka.

11.4.1 Centralizirana obrada


Kod centralizirane obrade sve 4 osnovne funkcije sustava izvode se na poslužitelju. Podaci se
pohranjuju na poslužitelju u datotekama i bazama podataka, poslovna logika je integrirana u
programsku podršku, a korisničko sučelje je uobičajeno znakovno sučelje.
Sučelje sustava čine mrežne i druge komponente kako bi se prezentacija mogla distribuirati. Klijent
(uobičajeno) samo terminal služi za slanje poruka na server (tipkanje, klikanje miša) i primanje
povratnih poruka sa servera koje služe za određivanje prikaza na ekranu.

Slika 67. Obrada centralizirana na poslužitelju

Nadgradnja se može izvršiti zamjenom znakovnog sučelja grafičkim koje se izbodi na PC što
produljuje vijek aplikacija, ali se funkcionalnost ne može značajno poboljšati.

137
11.4.2 Dvoslojna arhitektura klijent-poslužitelj (client-server)
Klijent je jednokorisničko računalo (PC), na kojem se nalazi sučelje aplikacije i programska podrška za
obradu i pristup podacima. Klijent je spojen na mrežu te se može povezati na poslužitelje i druge
klijente.
Poslužitelj je višekorisničko računalo, na kojem se nalazi baza podataka i programska podrška koja
omogućuje pristup bazi podataka i u njoj smještenim podacima. Poslužitelj omogućuje povezivanje
klijentima i drugim poslužiteljima (npr. za potrebe replikacije podataka).
Korisnicima na klijentu izgleda kao da njihovo osobno računalo obavlja cijeli posao. Usput, to čak i
može biti tako ukoliko se na osobno računalo instalira poslužitelj baza podataka, kao što programeri
često čine tijekom razvoja.
U ovisnosti o rasporedu pojedinih komponenti na klijentskom računalu u odnosu na poslužitelj,
razlikujemo dvije varijante dvoslojne arhitekture kako je prikazano na sljedećim slikama.

Dvoslojna arhitektura s debelim klijentom


Takozvani debeli klijent (fat client), u novije vrijeme „bogati“ klijent (rich client) integrira poslovnu
logiku. Nema obrade podataka na poslužitelju ili je obrada minimalna. Klijent može imati lokalno
spremište (bazu) podataka, kao što smo već spomenuli. Elastičnost na promjene poslovne politike je
minimalna ili nikakva. Kada se promijeni neko poslovno pravilo, treba ga ugraditi u programski kod
klijenta i taj program instalirati svim korisnicima.

Slika 68. Debeli klijent

Prednosti:
 moguć je brzi početni razvoj aplikacije
 veća je samostalnost klijenta
 rasterećenje glavnog računala (poslužitelja).
Nedostaci
 poslovna logika integrirana je u klijenta
 promjena logike zahtijeva instaliranje nove verzije na svim klijentima, to jest korisničkim
računalima, kojih može biti mnogo
 razvoj velike aplikacije s vremenom postaje vrlo složen
 potreban je veći broj klijentskih računala dovoljne procesne moći.

138
Dvoslojna arhitektura s tankim klijentom
Podatkovna logika, a ponekad i većina elemenata obrade, se nalazi na poslužitelju. Osnovna namjena
tankog klijenta (thin client) je prikaz podataka, koji doduše može imati neku popratnu aplikacijsku
logiku za kontrolu korisničkog sučelja. Tipični primjer tankog klijenta je web preglednik, koji prikazuje
HTML stranicu „iza“ koje se nalazi korisniku nevidljiv pozadinski kod napisan u nekom od skriptnih
jezika (npr. Java script).

Slika 69. Tanki klijent

Prednosti su:

 manja složenost razvoja velikih aplikacija (serverski dio i klijentski dio)


 olakšana distribucija, jer se kao tanki klijent može koristiti npr. općenito dostupan web
preglednik
 lakše održavanje, jer je promjena poslovne logike centralizirana na poslužitelju te njezina
promjena ne zahtijeva ugradnju na korisnička računala
 klijentska računala ne moraju imati veliku moć obrade
Nedostaci

 veliko opterećenje glavnog računala, a to znači skupo glavno računalo


 veće mrežno opterećenje (gotovo za svaku promjenu ide se na server)
 lošija funkcionalnost kada se kao klijent koristi web preglednik u odnosu na debelog klijenta
 naglo povećanje složenosti kada treba ugraditi zahtjevno grafičko sučelje

11.4.3 Primjer arhitekture klijent-poslužitelj


Na primjer, treba napraviti aplikaciju koja će
 prikazivati listu najboljih 10 partnera poredanih po iznosu prometa
 za odabranog partnera prikazati njegove dokumente.
Rješenje sadrži dva problema koje pri tom moramo riješiti. Jedan problem odnosi se na oblikovanje
dinamičkog skupa podataka i pisanja odgovarajućih upita koji će sučelje opskrbiti potrebnim
podacima. Budući da su partneri specijalizirani na fizičke osobe i pravne osobe (tvrtke) potpuna

139
informacija o svim partnerima treba sadržavati uniju upita koji vraća fizičke osobe i upita koji vraća
pravne osobe, a koji imaju različite strukture podataka. S druge strane postavlja se pitanje kako
povezati tu uniju s odgovarajućim dokumentima?

Primjer debelog klijenta


Primjer: Arhitekture\DebeliKlijent

var osobe = context.Partner.AsNoTracking().OfType<Osoba>().


Select(o => new{
o.IdPartnera, Broj = o.OIB,
Naziv = o.PrezimeOsobe + " " + o.ImeOsobe,
Promet = o.Dokumenti.Sum(d => d.IznosDokumenta)
});
var tvrtke = context.Partner.AsNoTracking().OfType<Tvrtka>().
Select(t => new{ t.IdPartnera, Naziv = t.NazivTvrtke,
Broj = t.MatBrTvrtke,
Promet = t.Dokumenti.Sum(d => d.IznosDokumenta)}
);
var partneri = osobe.Union(tvrtke);
var najboljiPartneri = partneri.
OrderByDescending(p => p.Promet).
Take(10);
prometPartneraBindingSource.DataSource = najboljiPartneri.ToList();

U ovom primjeu uspostavljamo mehanizam za objektno relacijsko preslikavanje (Entity Framework,


EF) koji omogućuje povezivanje s bazom podataka i postavljanje upita koji vraćaju podatke. Upite
zapisujemo u programski kod klijenta koristeći LINQ (Language-Integrated Query), mehanizam koji
integrira postavljanje upita u neki od jezika podržanih .NET Framework okvirom, kao što je C#.
Dohvaćene podatke predajemo kao izvor podataka (DataSource) kontroli korisničkog sučelja za prikaz
partnera, zadnjom naredbom u primjeru. Analogno tomu, dohvaćamo podatke za dokumente i
povezujemo s kontrolom korisničkog sučelja za prikaz dokumenata. Programski kod približno je
jednake veličine kao u primjeru za dohvat liste partnera.

Primjer tankog klijenta


Slično prethodnom primjeru, opet dohvaćamo podatke o partnerima i dokumentima, ali su ovaj puta
naredbe upita koji oblikuju podatke zapisane SQL-om u pohranjenim procedurama na poslužitelju.
Klijent poziva ove procedure (koristeći EF) i rezultate predaje korisničkim kontrolama.

Primjer: Arhitekture\Tanki

dataGridViewPromet.DataSource =
context.ap_PrometPartnera();

Već i ovaj relativno jednostavan primjer dobro pokazuje zašto je klijent tanak.

Primjer projene zahtjeva korisnika


Korisnik se, naravno, predomislio i želi pregled 20 najboljih partnera, ali dobrima smatra samo one
koji kupuju i ljeti i zimi i ostvarili su barem X kn prometa u zadnje 2 godine.
 Na debelom klijentu treba promijeniti upite u izvornom kodu, prevesti ga u novu izvršnu inačicu,
te instalirati novu verziju aplikacije pojedinom korisniku. Sve to bude zahtjevno, sporo i skupo.
 Za tanki klijent treba na poslužitelju BP promijeniti pohranjenu proceduru za dohvat podataka,
što bude centralizirano, jednostavno, brže i jeftinije u odnosu na debelog klijenta.
140
Napomena: promjena modela podataka mijenja i tanki i debeli klijent!

11.4.4 Troslojna ili višeslojna arhitektura klijent-poslužitelj


Baza podataka (ili baze podataka, jer ih u većim sustavima može biti i više) i poslovna logika
distribuiraju na zasebne poslužitelje. Poslužitelj aplikacija, poslužitelj baza podataka i klijent nalaze se
u različitim slojevima.

Slika 70. Troslojna arhitektura

Poslužitelj baza podataka zadržava upravljanje podacima. Poslužitelj aplikacija preuzima upravljanje
transakcijama, "preuzeto" s podatkovnog poslužitelja, te dio ili čitavu poslovnu logiku, "preuzetu" s
klijenta. Klijent zadržava korisničko sučelje i dio poslovne logike - onaj koji se ne mijenja ili je osobnog
karaktera.
Prednosti:
 bolja raspodjela opterećenja
 veća skalabilnost - mogućnost prilagodbe povećanju opterećenja bez preopterećenja ili
promjene procedura, uslijed npr. povećanja broja korisnika ili povećanja količine podataka
Nedostaci:
 vrlo složen dizajn i razvoj
 problem raspodjele podataka, procesa, sučelja
 veće opterećenje mreže

141
Slika 71. Višeslojna arhitektura

U općem slučaju može naravno biti i više slojeva, što se najčešće postiže umetanjem dodatnih
aplikcijskih poslužitelja. Napomena: povećanje broja slojeva povećava skalabilnost, ali i složenost te
latenciju (kašnjenje, sporost odziva).

11.5 Višeslojna aplikacija


Aplikacije se može podijeliti u više razina, tj. slojeva, pri čemu se uobičajeno aplikacija dijele u slojeve
kao na sljedećoj slici.

142
Slika 72. Primjer podjele po slojevima u višeslojnoj aplikaciji

Prezentacijski sloj (PL – Presentation Layer) uobičajeno predstavlja grafičko sučelje (GUI) windows,
web ili mobilne aplikacije, no može biti i skup javnih servisa koje druge aplikacije mogu koristiti.
Poslovni sloj (BL – Business Layer) je sloj poslovne logike (BLL – Business Logic Layer) koja sadržni
poslovne klase koje se sastoje ne samo od podataka nego i od odgovarajućeg ponašanja i validacije.
Podatkovni sloj (DL -Data Layer) je sloj za pristup podacima (DAL – Data Acess Layer) koji može biti
npr. izveden korištenjem ADO.NET-a, Entity Frameworka i slično.
Spremišta podataka - Podaci mogu biti pohranjeni u različitim spremištima (Data Storage) te ako se
radi o bazi podataka, taj sloj nože dodatno imati programski kod specifičan za bazu podataka
(pohranjene procedure, pogledi, …).
Napomena: Ovisno o modelu arhitekture moguće je imati i aplikacije koje imaju više slojeva, u kojima
se jedan od navedenih slojeva dodatno razrađuje, odnosno uslojava, pri čemu treba razlikovati
pojmove fizičkog i logičkog sloja. Štoviše, moguće su situacije u kojoj se isti logički sloj (npr. poslovni
sloj) nalazi na dva fizička sloja radi povećanja performansi.

11.5.1 Odnosi među slojevima


Jedno od bitnih pitanja je kako postići neovisnost slojeva, odnosno kako projektirati slojeve tako da
promjena nekog sloja minimalno utječe na ostale slojeve. Interakcija slojeva je uvijek prema dolje
(eng. top-down interaction) tako da viši slojevi pozivaju niže slojeve, ali ne i obrnuto.
Moguće su dvije vrste interacije:

 labava (engl. loose) u kojoj neki sloj može komunicirati s bilo kojim slojem ispod čime se
povećavaju peformanse, ali i ovisnost slojeva i

143
 stroga (eng. strict) u kojem sloj smije komunicirati samo sa slojem koji je neposredno ispod.
Prednost stroge interakcije je da npr. promjena načina pristupa podacima ne mijenja ništa u
prezentacijskom sloju i izaziva minimalne promjene u poslovnom sloju.
Osim hijerarhijski uslojenih slojeva moguće je imati i poprečne komponente (engl. crosscutting
concerns) zajedničke za sve slojeve u svrhu obrade iznimki, praćenja traga izvršenja programa, raznih
zajedničkih biblioteka itd.
Provjeru podataka potrebno je uvijek napraviti na poslovnom sloju. Iako je validaciju na
prezentacijskom sloju poželjno izvesti zbog brzine aplikacije ne smije se vjerojavati podacima
pristiglim iz prezentacijskog sloja. Ti podaci mogu biti neispravni uslijed programske pogreške,
pogreške u komunikaciji ili zbog zle namjere korisnika.
Jedno od uobičajenih pitanja vezanih za validaciju je zašto treba raditi validaciju ako baza podataka
čuva integritet podataka. Jednom uočen neispravan podatak ne bi trebalo propuštati u niže slojeve, a
dodatno se postavlja i problem oblikovanja složenijih pravila i potencijalnog problema u slučaju
promjene (tipa) spremišta.

11.5.2 Poslovni objekt


Poslovni objekt posjeduje svojstva poslovnog identiteta kojim se objedinjuju podaci, ponašanje i
validacija. Poslovna logika treba biti neovisna o načinu pristupa podacima, jer aplikacija treba moći
prikazati i raditi s podacima iz raznih vrsta spremišta i po potrebi omogućiti promjenu vrste
spremišta. Potpunu neovisnost teško je ostvariti, ali se ta ovisnost može minimizirati pri čemu se
nameću pitanja gdje stvarati poslovni objekt i kako prenijeti podatke iz podatkovnog sloja u poslovni
sloj.
Jedno od jednostavnih „rješenja“ bi bilo da podatkovni sloj prilikom dohvata podataka iz raznih izvora
odmah kreira poslovnih objekt i vrati ga poslovnom sloju koji bi onda izvršio validaciju tog objekta,
odnosno da se prilikom perzistencije poslovnog objekta, poslovni objekt kao takav proslijedi
podatkovnom sloju koji bi ga onda ispravno pretvorio u odgovarajući oblik i pohranio spremište.
Inicijalni problem ovog rješenje je problem cirkularne veze DL-BL, jer bi DL trebao interakciju „prema
gore“, odnosno morao bi referencirati objekte iz BL-a, dok bi istovremeno BL referencirao DL što
programski nije moguće. Jedno od mogućih rješenja tog problema je postavljanje DL i BL u isti
projekt, ali time se narušava podjela po slojevima.
Alternativa je izdvojiti poslovni objekt u zasebni, dijeljeni (poprečni) sloj. U tom slučaju poslovni
objekt bi bio sveden samo na podatke, a logika bi ostala u BL-u čime se gubi smisao poslovnog
objekta. Dodatno, otvara se mogućnost nekontroliranog pisanja po objektu, jer ako to može DL
prilikom kreiranja objekta, onda može i programer prezentacijskog sloja (namjerno ili slučajno) čime
može doći do neželjenog ponašanja.
Stoga je zaključak da se poslovni objekt stvara u BL-u. Budući da poslovna logika treba biti neovisna o
načinu pristupa podacima razred će biti definiran kao parcijalni razred kako bi se naknadno u drugoj
datoteci mogao napisati dohvat podataka. U primjerima koji slijede poslovni objekt će biti obogaćen
validacijskim metoda, dok bi u nekom većem primjeru mogao sadržavati i neka druga pravila osim
jednostavnih validacijskih metoda (npr. smije li fizička osoba uplatiti pravnoj osobi iznos veći od n
kuna, poduzimanje određene radnje kada klijent dođe u nedozvoljeni minus, ažuriranje tablice
prilikom evidentiranja rezultata neke utakmice, … ).

144
U primjerima koji slijede validacija se provodi korištenjem sučelja IDataErrorInfo.

Primjer: Viseslojna\BLL\Artikl.cs

public partial class Artikl : IDataErrorInfo {


// prvi dio parcijalne klase
public int SifArtikla { get; set; }

public string Error
{
get
{
StringBuilder sb = new StringBuilder();
string s = this["SifArtikla"];
if (!string.IsNullOrEmpty(s))

Napomena:
Nekoliko uobičajenih pitanja je vezano za razne ORM alate kao što su Entity Framework (EF) ili
nHibernate. Ako bi se EF model generiran u podatkovnom sloju koristio direktno iz prezentacijskog
modela to bi uzrokovalo labavu interakciju između slojeva što nije poželjno u složenim, višeslojnim
aplikacijama i de facto, radi se o debelom klijentu koji ima dodatno razrađen podatkovni sloj.
Korištenjem tehnike Code First, koja temeljem programskih razreda generira razrede podataka, bi se
mogao definirati poslovni model, nakon čega bi preslikavanje u podatkovni sloj slijedilo automatski ili
uz skup definiranih pravila. Problem ovog pristupa je nemogućnost oblikovanja boljeg objektnog
modela koji ne mora biti samo čisto objektno-relacijsko preslikavanje između programa i podataka
(1:1) i nemogućnost oblikovanja složenih pravila. Dodatno, problem bude još veći ako izvor podataka
ne podržava EF ili nHibernate (što npr. ako je izvor podataka SharePoint ili web servis?).

11.5.3 Dohvat podataka kroz slojeve


Pretpostavi li se da je aplikacija dobro uslojena dohvat svih artikala u prezentacijskom sloju vrši se
pozivom odgovarajućih postupaka posrednika u poslovnom sloju koji će kasnije komunicirati s DAL
slojem. U narednim primjerima taj posrednik će biti nazivan BllProvider. Tijek postupka dohvata
prikazan je sljedećim dijagramom.

145
ArtiklForm ArtiklBLL BindingList<Artikl> ArtiklDAL Artikl

new

FetchAll

new

new

FetchAll

DalCollection

new

LoadFromDB(item)

Add(Artikl)
For each item in
DalCollection

BindingList<Artikl>

PL BL DL BL

Slika 73. Slijed poziva od prezentacijskog sloja do podatkovnog sloja

Pri pozivu sa sučelja BLLProvider će stvoriti novu kolekciju artiklala (BindingList<Artikl>) i napuniti je
podacima na osnovu rezultata poziva u razredu koji je zadužen za pristup podacima
(ArtiklDALProvider).
Sučelju će biti vraćena kolekcija poslovnih objekata (podaci + poslovna logika). Kad bi se umjesto
poslovnog objekta sučelju prenosio XML ili neki drugi oblik koji sadrži isključivo podatke prednost bi
bila općenitost i neovisnost, ali bi puno više do izražaja došli nedostatci: otežana validacija, moguće
nekonzistentni podaci, poslovna logika i validacija na raznim mjestima te bi se problem sveo na
problem diskutiran u 11.5.2.
U dizajnu tip podatka za izvor se postavlja na razred definiran u poslovnom sloju, npr. Artikl

146
Slika 74. Postavljanje izvora podataka u dizajnu

te se prilikom učitavanja forme instancira BLL provider i obavlja dohvat podataka.

Primjer: Viseslojna \ PL \ ArtiklForm

// referenca na BLL sloj


ArtiklBLL bll = new ArtiklBLL();

private void ArtiklForm_Load(object sender, EventArgs e){
artiklBindingSource.DataSource = bll.FetchAll();

Na ovaj način PL (forma) ne zna ništa o načinu pohrane, to jest ne ovisi o podatkovnom sloju.

11.5.4 Načini punjenja poslovnog objekta


Podaci se u spremištu podataka nalaze u različitim formatima (tablice u bazi podataka, liste na
SharePointu, razredi korišteni u komunikaciji s nekim servisom, …) te je potrebno odrediti način na
koji će se u poslovnoj sloju stvoriti poslovni objekt na osnovu podataka iz podatkovnog sloja. Na
sličan način potrebno je riješiti i problem proslijeđivanja podataka podatkovnom sloju kako bi se
obavilo dodavanje ili ažuriranje podatka
Jedan od jednostavnih načina je da metode poslovnog sloja koje služe za manipulaciju podacima
primaju podatak iz podatkovnog sloja, kreiraju poslovni objekt i popunjavaju ga odgovarajućim
podacima. Mana ovog pristupa je povećanje složenosti i smanjenje čitljivosti kôda metoda za
manipulaciju podataka, posebno u slučajevima kada taj kod treba prilagoditi uslijed promjena u
podatkovnom sloju.
Bolji pristup je da svaki poslovni objekt ima odgovornost za svoju perzistenciju. Postupci u kojima bi
se perzistencija obavljala primali bi konkretne objekte iz DAL sloja i bili bi označeni modifikatorom
internal kako ne bi, slučajno ili namjerno, mogli biti pozvani izvan poslovnog sloja. U ovom slučaju
praktično je definirati poslovne objekte kao parcijalne razrede i odvojiti postupke u različite datoteke.
Ovisnost o DAL-u postoji, ali je lokalizirana u odvojenoj datoteci koja se lako održava, a poslovna
logika i učahurenje je očuvano.
Navedeno rješenje moguće je poboljšati raznim tehnikama kao što je pisanje posebnog sučelja u
zajedničkom sloju koja DAL treba implementirati, pa se ovisnost svede samo na sučelje (bolje, ali
zahtjevnije za izvesti), korištenjem XML-a za prijenos podataka između DAL-a i BLL-a umjesto
konkretnih razreda iz DAL-a, definiranjem posebnih razreda za prijenos podataka (Data Transfer

147
Object - DTO) ili pisanjem tzv. factory metoda u posebnom projektu koji bi bio zadužen samo za
preslikavanje podataka među slojevima, a ovisnost bi riješavao korištenjem tehnike Dependency
Injection.
U primjerima koji slijede, punjenje podataka je izvedeno u metodama poslovnog objekta, izdvojenim
u posebnu datoteku istog imena kao i datoteke gdje je originalna definicija poslovnog objekta, ali u
podmapi DALSpecific.

11.5.5 Izvedba podatkovnog sloja pomoću ADO.NET-a


Varijanta u kojoj se brzo i bez dodatnog kopiranja podataka iz DAL-a mogu isporučiti podaci
poslovnom objektu je korištenje IDataReadera. Klasa iz podatkovnog sloja uspostavlja vezu prema
bazi i izvršava upit (u ovom slučaju pohranjenu proceduru) te kao povratnu vrijednost isporučuje
objekt tipa IDataReader. U ovom slučju nije moguće koristiti ključnu riječ using za vezu prema bazi i
naredbu, jer bi izlaskom iz using bloka došlo do zatvaranja veze prema bazi prije nego što mi objekt
tipa IDataReader bio konzumiran. Kako ipak ne bi došlo do situacije u kojoj je veza prema bazi ostala
otvorena nakon upotrebe, prilikom izvršavanja naredbe specificira se da će se veza automatski
zatvoriti zatvaranjem IDataReadera.

Primjer:  Viseslojna \ DAL.ADO \ ArtiklDAL.cs

public class ArtiklDAL {


public IDataReader FetchAll() {
SqlConnection db = ...
SqlCommand cmd = db.CreateCommand();
cmd.CommandText = "[dbo].[ap_ArtiklList_R]";
cmd.CommandType = CommandType.StoredProcedure;
db.Open();
return cmd.ExecuteReader(CommandBehavior.CloseConnection);
}

BLL sadrži referencu na DAL sloj, te uzima objekt iz DAL-a, iterira po njemu, za svaki redak stvara novi
poslovni objekt (u primjeru Artikl), ali učitavanje pojedinačnog podatka prepušta samom poslovnom
objektu pozivom vlastitog internal postupka LoadFromDB. Postupak u poslovnom sloju vraća
kolekciju poslovnih objekata.

Primjer:  Viseslojna \ BLL - BLLProviders \ ArtiklBLL.cs

public class ArtiklBLL{


private ArtiklDAL dal = new ArtiklDAL();
public BindingList<Artikl> FetchAll(){
BindingList<Artikl> list = new BindingList<Artikl>();
using (IDataReader dr = dal.FetchAll()){
while (dr.Read()){
Artikl artikl = new Artikl();
artikl.LoadFromDB(dr);
list.Add(artikl);
...

Poslovni objekt Artikl je definiran kao parcijalni razred, čime je ostvarena zadovoljavajuća neovisnost
o podatkovnom sloju (jer je potpuno teško ili nemoguće ostvariti). Umjesto pisanja kôda kojim se
kopira stupac po stupac iz rezultata moguće je napisati općeniti kôd koji bi korištenjem refleksije bilo
moguće upariti svojstva nekog objekta s istoimenim nazivi stupaca u IDataReaderu.

148
Primjer:  Viseslojna \ BLL \ DALSpecific \ Artikl.cs

public partial class Artikl{ // drugi dio parcijalne klase


internal void LoadFromDB(IDataReader dr){
this.SifArtikla = (int) dr["SifArtikla"];
...
this.CijArtikla = (decimal)dr["CijArtikla"];
...
this.SlikaArtikla = dr["SlikaArtikla"] == DBNull.Value ?
null : (byte[])dr["SlikaArtikla"];
}

11.5.6 Izvedba podatkovnog sloja pomoću Entity Frameworka


U slučaju kada IDataReader nije moguće koristiti (npr. u slučaju da se slojevi nalaze na različitim
fizičkim slojevima), postupci iz DAL sloja trebaju vratiti primjerke razreda koji sadrže samo podatke i
služe za razmjenu podataka (engl. Data Transfer Object – DTO). DTO se lako se prenosi preko mreže
dvosmjerno, ali zahtijeva stvaranje novih razreda što potencijalno vodi dupliranju posla. U
slučajevima kad se u DAL sloju može koristiti Entity Framework (EF), entiteti iz EF-a mogu biti
prikladno rješenje za DTO, jer su tzv. čisti razredi (nisu izvedeni iz nekog drugog razreda i nemaju
postupaka). U takvom scenariju DAL isporučuje objekt iz Entity Frameworka.
Postupci kojim se poslovni objekt puni podacima iz podatkovnog sloja koriste sličan princip kao i kod
IDataReadera te su potrebne samo manje preinake u poslovnom sloju, dok prezentacijski sloj ostaje
potpuno isti. BLL sadrži referencu na DAL sloj, uzima objekt iz DAL-a, iterira po njemu, a učitavanje
pojedinačnog podatka prepušta samom poslovnom objektu i kao u prethodnom slučaju vraća
kolekciju poslovnih objekata. Razlika je samo u vrsti povratnog objekta iz podatkovnog sloja i vrsti
ulaznog podatka u postupak za punjenje poslovnog objekta koji se u prikazanom primjeru mijenja iz
IDataReader u DAL.EF.Artikl. Upotrijebom ključne riječi var moguće je ostvariti da se metoda iz
poslovnog sloja za dohvat podataka uopće ne mora promijeniti.

149
Primjer:  Viseslojna \ BLL \ BLLProviders \ ArtiklBLL.cs

public class ArtiklBLL{


private ArtiklDAL dal = new ArtiklDAL();
public BindingList<Artikl> FetchAll(){
BindingList<Artikl> list = new BindingList<Artikl>();
foreach(var dalObject in dal.FetchAll()){ //dalObjekt: DAL.EF.Artikl
Artikl artikl = new Artikl();
artikl.LoadFromDB(dalObject);
list.Add(artikl);

Primjer:  Viseslojna \ BLL \ DALSpecific \ Artikl.cs

public partial class Artikl


{
internal void LoadFromDB(DAL.EF.Artikl a)
{
this.SifArtikla = a.SifArtikla;
this.NazArtikla = a.NazArtikla;
this.JedMjere = a.JedMjere;
this.CijArtikla = a.CijArtikla;
this.ZastUsluga = a.ZastUsluga;
this.SlikaArtikla = a.SlikaArtikla;
}
}

11.5.7 Dodavanje, ažuriranje i brisanje artikla


Dodavanje, ažuriranje i brisanje artikala (i ostalih sličnih poslovnih objekata) obavlja se na
pojedinačnom podatku na način da se poziva odgovarajući postupak na poslovnom sloju koji po
uspješnoj validaciji pa poziva konkretni postupak iz DAL sloja. U prikazanom primjeru DAL sloj ima
postupak s n argumenata koji opisuju artikl, a alternativno može se napisati metoda koja prima jedan
DTO objekt.

Primjer:  Viseslojna \ BLL \ BLLProviders \ ArtiklBLL.cs

public class ArtiklBLL{


public void Insert(Artikl a){
if (string.IsNullOrEmpty(a.Error)){
dal.Insert(a.SifArtikla, a.NazArtikla, a.JedMjere,
a.CijArtikla, a.ZastUsluga, a.SlikaArtikla);
}
else
throw new Exception("Validacija neuspješna: " + a.Error);
}

11.5.8 Izvedba šifrarničkih formi višeslojno.


Primjer višeslojno izvedene šifrarničke forme je uređivanje popisa Država u tablici/matrici čiji kôd se
nalazi u sljedećim datotekama

 Viseslojna \ BLL \ BLLProviders \ DrzavaBLL.cs


 Viseslojna \ BLL \ Drzava.cs
 Viseslojna \ BLL \ DALSpecific \ Drzava.cs
 Viseslojna \ DAL.ADO \ DrzavaDAL.cs
150
Programski primjer je nedovršen i služi samo za ilustraciju dohvata podataka kroz više slojeva. Razlog
zbog kojeg je primjer nedovršen su nedostatci izvedbe poslovnog sloja na dosad prilazani način.
Promjene se trebaju grupno spremati u bazu, pri čemu treba voditi evidenciju o promijenjenim,
novim i obrisanim podacima. Poslovni sloj prati promjene na BindingListi obradom događaja
ListChanged. Međutim, dio logike nalazi se i u prezentacijskom sloju, jer lista nema ugrađenu
mogućnost pohrane obrisanih podataka, pa se evidentiranje podataka koji će se obrisati ne može
izvesti u ovako realiziranom poslovnom sloju već se radi u prezentacijskom sloju prilikom obrade
događaja UserDeletingRow (događaj koji GridView podiže prilikom brisanja podatka) kao što je u
primjeru DokumentStavkaForm.
Stoga će u sljedećem poglavlju bit će prikazano bolje, generičko rješenje primjerom Firma.Win.

11.6 Zadaci
Zadatak 1. Koje su osnovne funkcije sustava?
Zadatak 2. Koje funkcije obavlja poslužitelj u dvoslojnoj arhitekturi klijent-poslužitelj?
Zadatak 3. Koje su prednosti i nedostatci debelog odnosno tankog klijenta?
Zadatak 4. Gdje se u višeslojnoj aplikaciji, savjetuje stvarati poslovni objekt, a gdje provjeriti
valjanost podataka?
Zadatak 5. Je li potrebno raditi validaciju u aplikaciji ako baza podataka čuva integritet
podataka?
Zadatak 6. Koji su nedostatci predstavljenog višeslojnog rješenja?
Zadatak 7. Proučiti predložak  \Prilozi\SpecifikacijaDizajna.dot i primjer  \Prilozi\Firma-
Dizajn.doc. Komentirati prijedlog s obzirom na općenite modele arhitekture.

151
12 Primjer višeslojne aplikacije – aplikacija FirmaWin

12.1 Nedostaci postojećeg rješenja


Rješenje prikazano u prethodnom poglavlju ima nekoliko značajnih nedostataka. Kao što je već ranije
navedeno izrada šifrarničikh formi je nepraktična, jer se evidencija promjena dijelom vodi kroz
prezentacijski sloj. Svaka forma se brine sama za sebe i nezavisno je oblikovana uključujući
navigacijsku i statusnu traku te programski kod za snimanje i otkazivanje promjena. Također,
multiplicira se jednak ili sličan kod u poslovnim objektima za evidentiranje stanja objekta, obavijesti o
promjenama stanja objekata itd.
Navedeni nedostaci predstavljaju motivaciju za izradu općenitog rješenja kojim bi se ti problemi
riješili na jednom mjestu. Uspostavimo osnovni skup sučelja i baznih razreda (Firma.Framework) koji
predstavlja radni okvir nad kojim se gradi konkretno rješenje. Također, izdvajanjem zajedničkog dijela
korisničkog sučelja a u poseban paket (Firma.Core) olakšava se i automatizira izrada prezentacijskog
sloja.

12.2 Struktura rješenja


Rješenje se sastoji od nekoliko komponenti kako je prikazano na sljedećoj slici. Svaka od njih kreira se
zasebnim projektom koji je dio istog rješenja (Cjelokupno rješenje nalazi se u mapi  FirmaWin).
Prezentacijski sloj rješenja izveden je kao Windows aplikacija, podatkovni sloj realiziran je
korištenjem Entity Frameworka, dok se poslovni sloj sastoji od dva projekta: Firma.Framework i
Firma.BLL.

Slika 75. Struktura rješenja FirmaWin

Firma.Framework predstavlja skup temeljnih razreda i sučelja za izradu poslovnih objekata koji se
nalaze u projektu Firma.BLL. Kao dio projekta Firma.BLL nalazi se mapa BusinessEntities\DALSpecific
koja predstavlja postupke koji direktno ovise o implementaciji podatkovnog sloja (nije prikazano na
slici).

152
Slojevi aplikacije za primjer Artikla se mogu detaljnije vidjeti na sljedećoj slici.

Slika 76. Slojevi aplikacije na primjeru Artikla

12.3 Firma.Framework
Komponenta Firma.Framework sadrži sljedeće temeljne razreda i sučelja za izradu poslovnih objekata

 IBllProvider – sučelje objekta iz poslovnog sloja zaduženog za rad s podacima (sučelje sadrži
postupke za snimanje i validaciju)
 IBusinessObject – sučelje poslovnog objekta
 BusinessBase – apstraktni razred s osnovnom implementacijom poslovnog objekta
 IBusinessObjectList – sučelje liste poslovnih objekata (postupci za dohvat promjena i
otkazivanje novo dodanih u listu)
 BusinessBaseList – razred s osnovnom implementacijom liste poslovnih objekata
 BusinessObjectState – enumeracija s mogućim stanjima objekta
 GenericFormAtributte – vlastiti atributi koji će se dodavati poslovnim objektima u svrhu
automatskog generiranja formi
 ValidationException – vlastiti razred za iznimke.
Međusobne ovisnosti prikazane su na sljedećoj slici, a pojedini razred ili sučelje opisano je u
poglavljima koja slijede.

153
Slika 77. Razredi i sučelja projekta Firma.Framework

12.3.1 Poslovni objekt i sučelje IBusinessObject


Poslovni objekt predstavlja implementaciju poslovnog entiteta i implementira postupke za provjeru i
promjenu stanja, validaciju (u kombinaciji s BLL providerom), pohranu kopije, osvježavanje, itd.
Validacija se obavlja u kombinaciji s BLL providerom, jer podaci iz poslovnog objekta možda ovise o
drugim vrijednostima u bazi, a ne samo o sebi (npr. jedinstvenost naziva ili šifre artikla) i zato jer BLL
provider sadrži referencu na DAL sloj.
Sučelje poslovnog objekta IBusinessObject sadrži svojstva stanja poslovnog objekta:
 State: svaki poslovni objekt je u jednom od mogućih 4 stanja (neizmjenjen, nov, moficiran,
označen za brisanje) ili još neučitan (u slučaju tzv. lazy loadinga)
 IsDirty: je li objekt općenito izmijenjen,
 InEdit: je li objekt u postupku izmjena
Veza prema BLL objektu za dohvat i spremanje je sačuvana u svojstvu BllProvider tipa IBllProvider.
Postupcima Edit i Delete naznačava se početak ažuriranja ili brisanja, a postupkom CancelChanges
otkazuju se promjene na objektu. Nakon što se izmjene na objektu pohrane u spremište, potrebno je
označiti objekt spremljenim korištenjem postupka AcceptChanges. Postupci ClearErrors, Validate
koriste se kod validacije. Postupak SetParent služi za postavljanje roditelja (tipa IBusinessObjectList)
kada je objekt dio neke liste. Postupkom Load poslovni objekt se puni podacima iz DAL sloja na

154
osnovo nekog DTO objekta, a prilikom pohrane pretvara se u odgovarajući razred iz DAL sloja
postupkom ToDtoObject.

12.3.2 Bazni razred za poslovni objekt


BusinessBase je apstraktni razred koji predstavlja poslovni objekt i implementira sučelje
IBusinessObject i sučelja potrebna za povezivanje podataka na formi:

 INotifyPropertyChanged – sučelje omogućava slanje obavijesti o promjeni svojstva povezanog


objekta
 IEditableObject – sučelje kojim se omogućava stvaranje kopije podataka prije početka
ažuriranja i vraćanja u slučaju odustajanja. U slučaju da objekt povezan na formi
implementira ovo sučelje, kontrole na formi će pozvati odgovarajuću metodu sučelje i
omogućiti poslovnom razredu da napravi kopiju objekta prije ažuriranja, odnosno da vrati
originalne vrijednosti prilikom otkazivanja promjena
 IDataErrorInfo - standardno sučelje za validaciju.
Razred BusinessBase još sadrži apstraktne postupke Validate(string propertyName),
DoLoad(IDTOObject dtoObject) i ToDtoObject() koji se pozivaju u postupcima validacije i komunikacije
s DAL slojem. Svojstvo InEditMode služi za provjeru nalazi li se objekt u stanju koje dozvoljava
ažuriranje, a ostali protected postupci se koriste kod promjene stanja objekta25

12.3.3 Sučelje liste poslovnih objekata


Sučelje IBusinessObjectList proširuje funkcionalnost u odnosu na BindingList sljedećim postupcima:
 GetChanges() – vraća listu izmijenjenih poslovnih objekata
 SaveChanges(IBllProvider bll) - promijenjene elemente šalje na snimanje konkretnom
BllProvideru
 CancelChanges() - otkazuje sve promjene u listi
 AcceptChanges() - prihvat promjena u listi te se uobičajeno poziva nakon što je snimanje
podatka uspjelo.

12.3.4 Liste poslovnih objekata


Razred BusinessBaseList<T> predstavlja generički razred za listu poslovnih objekata. Ovaj razred
nasljeđuje BindingList<T> i prekriva postupke InsertItem i RemoveItem na način da prije stavljanja
objekta u listu stavlja sebe kao roditelja poslovnog objekta te da kod brisanja čuva obrisani element u
posebnoj listi (funkcionalnost koja nije dostupna u BindingList). Dodatno BusinessBaseList
implementira GetChanges iz IBusinessObjectList te ima svojstvo IsDirty za provjeru da li se mijenjala
lista ili neki njen element te statički postupak CreateNew<T> koji stvara novu listu na osnovu
kolekcije DTO objekata.
U slučajevima kad generička implementacija nije dovoljna može se napisati vlastiti razred koji
nasljeđuje BusinessBaseList<T>, npr. DokumentList : BusinessBaseList<Dokument>.

25
InEdit provjerava li je objekt trenutno u stanju između BeginEdit i EndEdit/CancelEdit. Za razliku od InEdit,
InEditMode provjerava da li je objekt u takvom stanju da se može ažurirati i aktivira se s Edit(), pa se može reći
da je InEditMode "jači" od InEdit.
155
12.4 Dohvat liste artikala
Rad prethodno opisanih razreda može se ilustrirati na primjeru dohvata liste artikala. Slijed
izvršavanja prikazan je na sljedećoj slici.

ArtiklForm ArtiklBllProvider ArtiklDalProvider BusinessBaseList BusinessBase Artikl

new

FetchAll

new

FetchAll

IEnumerable<DTO>

CreateNew(IEnumerable<DTO> ...)

CreateNew<Artikl>

new

Load(DTO)

For each DTO Artikl

Add(Artikl)

BusinessBaseList<Artikl>

BusinessBaseList<Artikl>

Normalna interakcija

PL BLL DAL Framework Framework BLL

Slika 78. Dijagram slijeda za dohvat svih artikala

Forma prezentacijskog sučelja ima referencu na objekt iz poslovnog sloja preko kojeg vrši dohvat
podataka (BllProvider) i zahtijeva dohvat svih elemenata.

Primjer:  FirmaWin \ Firma \ Forms \ ArtiklForm

// referenca na BLL sloj


private ArtiklBllProvider artiklBll = new ArtiklBllProvider();
public LoadData() {
var data = artiklBll.FetchAll(); //naknadno FetchLazy
artiklBindingSource.DataSource = data;

Odgovarajući BLL provider vrši upit prema DAL sloju i kao rezultat dobiva kolekciju DTO objekata i
statičkim postupkom CreateNew stvara lista konkretnih poslovnih objekata (u ovom primjeru
BusinessBaseList<Artikl>).

156
Primjer:  FirmaWin \ Firma.BLL \ ArtiklBllProvider.cs

public class ArtiklBllProvider : IBllProvider {


private ArtiklDalProvider dal = new ArtiklDalProvider();

public BusinessBaseList<Artikl> FetchAll() {


var dalRecord = dal.FetchAll();
return BusinessBaseList.CreateNew<Artikl>(dalRecord);
}

U postupku CreateNew stvara se lista poslovnih objekata te se vrši iteriranje po kolekciji DTO
objekata. DTO objekti mogu biti različiti, ali im je zajedničko da implementiraju prazno sučelje
IDTOObject koje služi kao zajednički nazivnnik svih DTO objekata. Za stvaranje pojedinačnog
poslovnog objekt koristi se statički postupak CreateNew u razredu BusinessBase. Razred
BusinessBaseList i postupak CreateNew su parametrizirani su postavljenja ograničenja da tip T po
kojem se vrši parametrizacija mora imati prazni konstruktor (uvjetovano postupkom CreateNew iz
BusinessBase) te da T mora biti poslovni objekt, to jest implementirai sučelje IBusinessObject.

Primjer:  FirmaWin \ Firma.Framework \ BusinessBaseList.cs

public class BusinessBaseList<T> : BindingList<T>, IBusinessObjectList


where T : IBusinessObject, new() {
...
public static BusinessBaseList<T> CreateNew
(IEnumerable<IDTOObject> items){
BusinessBaseList<T> list = new BusinessBaseList<T>();
foreach (var dto in items)
{
T businessObject = BusinessBase.CreateNew<T>(dto);
list.Add(businessObject);
}
return list;
}

Pojedinačni objekt se stvara pozivom praznog konstruktora i pozivom postupka Load koji će pozvati
apstraktni postupak DoLoad kojeg svaki konkretni poslovni objekt mora implementirati.

Primjer:  FirmaWin \ Firma.Framework \ BusinessBase.cs

public abstract class BusinessBase : INotifyPropertyChanged,


IEditableObject, IDataErrorInfo, IBusinessObject {
public static BO CreateNew<BO>(IDTOObject dto)
where BO : IBusinessObject, new() {
BO item = new BO();
item.Load(dto);
return item;
}

public void Load(IDTOObject dtoObject) {


DoLoad(dtoObject);
SetState(BusinessObjectState.Unmodified);
}

Postupak DoLoad prima DTO objekt koji služi za komunikaciju s DAL slojem. Prilikom kreiranja liste
poslovnih objekata i stvaranja pojedinačnih objekata potpuno je nebitan tip DTO objekta te se on
proslijeđuje kakakv jest. Konkretni tip DTO objekta je bitan samo kod konkretnog učitavanja u
postupku DoLoad i kopiranju podataka iz DTO objekta u poslovni objekt. Na ovaj način izoliran je dio

157
koji ovisi o konkretnom tipu DTO objekta, a dodatno je taj postupak stavljen u zasebnu datoteku
parcijalnog razreda. Napomena: korištenjem refleksije bilo bi moguće i ovaj dio ovisnosti ukloniti.

Primjer:  Firma.BLL\ BusinessEntities \ DALSpecific \ Artikl.cs

using DAL = Firma.EF;


public partial class Artikl {
protected override void DoLoad(IDTOObject dtoObject)
{
DAL.Artikl artikl = (DAL.Artikl)dtoObject;
this.sifArtikla = artikl.SifArtikla;
this.nazArtikla = artikl.NazArtikla ?? "";
...
this.slikaArtikla = artikl.SlikaArtikla;
}

12.5 Izvedba podatkovnog sloja


Podaci koji se vraćaju iz DAL sloja su različitih tipova podataka, ali se mogu objediniti pod zajedničko
prazno sučelje kako bi se mogle napisati metode koje ovise samo o tom sučelju. Stoga su svi entiteti u
EF modelu prošireni tako da implementiraju (prazno) sučelje IDTOObject.
Postupci dohvata i spremanja podataka korištenjem EF-a su većinom slični, pa se za slučajeve kada
nije potrebno raditi dodatna filtriranja može napisati generički razred za neke uobičajene postupke
kao što su dohvat svih podataka, dohvat po primarnim ključevima i dohvata ukupnog broja podataka
u tablici, a onda se u konkretnim razredima za pojedini tip entiteta postupci mogu nadjačati i dopisati
novi po potrebi.

Slika 79. Razrada podatkovnog sloja uz generički razred za dohvat podataka

158
Primjer:  FirmaWin \ Firma.EF \ AbstractDALProvider.cs

public virtual int ItemsCount() {


using (FirmaEntities ctx = new FirmaEntities()) {
return ctx.Set<T>().Count();
}
}
public virtual List<T> FetchAll() {
using (FirmaEntities ctx = new FirmaEntities()) {
var list = ctx.Set<T>().AsNoTracking().ToList();
return list;
}
}
public virtual T FetchByPKs(params object[] keyValues) {
using (FirmaEntities ctx = new FirmaEntities()) {
T item = ctx.Set<T>().Find(keyValues);
return item;
}

public virtual void SaveChanges(IEnumerable<T> changedItems,


IEnumerable<T> newItems, IEnumerable<T> deletedItems) {
using (FirmaEntities ctx = new FirmaEntities()) {
foreach (var item in changedItems){
ctx.Set<T>().Attach(item);
ctx.Entry(item).State = System.Data.EntityState.Modified;
}
foreach (var item in newItems){
ctx.Set<T>().Add(item);
}
foreach (var item in deletedItems){
ctx.Set<T>().Attach(item);
ctx.Entry(item).State = System.Data.EntityState.Deleted;
}
ctx.SaveChanges();
}

Za spremanje podataka potrebno je pripremiti tri kolekcije podataka: nove, izmijenjene i označene za
brisanje te korištenjem odgovarajućih postupaka u EF-u dodati iz u kontekst i pohraniti promjene.

12.6 Poslovni objekt sa stavkama


Poslovni objekt sa stavkama razlikuje se od „običnog“ objekta time što osim svojih svojstava sadrži
još i listu stavki koja se pohranjuje u objektu tipa BusinessBaseList. Promjena neke od stavki može
uzrokovati promjenu vrijednosti svojstva dokumenta (npr. promjena količine nekog artikla uzrokuje
promjenu cijene dokumenta). Dodatna posebnost je izvedba validacije, pri čemu je potrebno
napraviti validaciju svojstava dokumenta, ali i svojstava svih stavki.

159
Primjer:  Firma.BLL \ BusinessEntities \ Dokument.cs

public partial class Dokument : BusinessBase {


public Dokument() {
// Inicijalizacija praznih stavaka
stavke = new BusinessBaseList<Stavka>();
stavke.ListChanged +=
new ListChangedEventHandler(Stavke_ListChanged);
datDokumenta = DateTime.Now;
}
private BusinessBaseList<Stavka> stavke;
public BusinessBaseList<Stavka> Stavke
{
get { return stavke; }
}
private void Stavke_ListChanged(...) {
{
// Kad se promijeni stavka osvježiti iznos dokumenta...
OnPropertyChanged("IznosDokumenta");
if (e.ListChangedType == ListChangedType.ItemAdded)
stavke[e.NewIndex].BllProvider = this.BllProvider;
...
public override void Validate(){
DoValidation("VrDokumenta");
DoValidation("BrDokumenta");
DoValidation("DatDokumenta");
DoValidation("IdPartnera");
DoValidation("IdPrethDokumenta");
DoValidation("PostoPorez");
foreach (var stavka in Stavke) {
if (Stavke[i].BllProvider == null) {
Stavke[i].BllProvider = this.BllProvider;
}
stavka.Validate();
}
...

12.6.1 Dohvat i spremanje složenog objekta


Dohvat složenog objekta može se ilustrirati prilikom dohvata dokumenata sa stavkama (Slika 80) i
nalikuje dohvatu jednostavnog objekta (npr. liste artikala iz poglavlja 12.4). Ključna razlika je način na
koji se popunjavaju stavke složenog objekta.

160
BusinessBaseList<Stavka>
DokumentForm DokumentBllProvider DokumentDalProvider DokumentList Dokument BusinessBase

new

FetchAll

new

FetchAll

IEnumerable<IDTOObject>

CreateNew(dtos, fetchStavkeDelegate)

CreateNew(dto, fetchStavkeDelegate)

Load

fetchStavkeDelegate

IEnumerable<IDTOObject>
CreateNew(dtos)

CreateNew<Stavka>

Stavka

Add(Stavka)

BusinessBaseList<Stavka>

Dokument

DokumentList Add(Dokument)

Normalna interakcija

Framework Framework
PL BLL DAL BLL BLL

Slika 80. Dijagram slijeda za dohvat dokumenata sa stavkama

Stavke nemaju vlastiti DAL provider te se, kako bi se izbjegla ovisnost o konkretnom DAL provideru
dokumenta, umjesto reference na provider koristi delegat koji se definira postupak koja na osnovu id
dokumenta može dohvatiti njegove stavke. Delegat je tipa Func<int, BusinessBaseList<Stavka>>, a
postupak je napisan u istom razredu gdje se vrši zahtjev za punjenjem liste i koristi isti DAL provider.

Primjer:  FirmaWin \ Firma.BLL \ DokumentBllProvider

private DokumentDalProvider dal = new DokumentDalProvider();

...
public DokumentList FetchAll(){
var dalRecord = dal.FetchAll();
return DokumentList.CreateNew(dalRecord, FetchStavke);
}
public BusinessBaseList<Stavka> FetchStavke(int idDokumenta){
var dalRecord = dal.FetchStavke (idDokumenta);
return BusinessBaseList<Stavka>.CreateNew(dalRecord);
}

Stvaranje liste vrši se na isti način kao i kod liste jednostavnih objekata.

161
Primjer:  FirmaWin \ Firma.BLL \ BusinessEntities \ DokumentList.cs

internal static Dokument CreateNew(


IEnumerable<IDTOObject> items,
Func<int, BusinessBaseList<Stavka>> fetchStavkeDelegate) {
DokumentList rez = new DokumentList();
foreach (var dto in items) {
rez.Add(Dokument.CreateNew(dto, fetchStavkeDelegate));
}
return rez;
}

Razlika se pojavljuje prilikom učitavanja poslovnog objekta iz DTO objekta time što se osim postupak
Load poziva i postupak u kojem se dohvaća stavke (koji interno opet prolazi sličan postupak dohvata
kolekcije DTO objekata za stavke te kreiranja i punjenja pojedine stavke podacima).

Primjer:  FirmaWin \ Firma.BLL \ BusinessEntities \ Dokument.cs

internal static Dokument CreateNew(IDTOObject dtoObject,


Func<int, BusinessBaseList<Stavka>> stavkeDelegate)
{
var item = new Dokument();
item.Load(dtoObject);
...
item.LoadStavke(item.IdDokumenta.Value, stavkeDelegate);
...

private void LoadStavke(int idDokumenta,


Func<int, BusinessBaseList<Stavka>> stavkeDelegate) {
stavke = stavkeDelegate(idDokumenta);
foreach (var stavka in stavke)
{
stavka.BllProvider = this.BllProvider;
}
stavke.ListChanged += new
ListChangedEventHandler(Stavke_ListChanged);
SetState(BusinessObjectState.Unmodified);
//SetState jer smo u međuvremenu mijenjali stavke
}

U DAL sloju dohvat stavki nekog dokumenta realiziran je korištenjem pohranjene procedure i
uključivanjem procedure u Entity Framework model.

162
Primjer:  FirmaWin \ Firma.EF \ DokumentDalProvider.cs

public List<ap_StavkaList_R_Result> FetchStavke(int idDokumenta)


{
using (FirmaEntities ctx = new FirmaEntities()) {
var list = ctx.ap_StavkaList_R(idDokumenta).ToList();
return list;
}
}

Primjer:
CREATE PROCEDURE [dbo].[ap_StavkaList_R]
@IdDokumenta int
AS BEGIN
SELECT S.*, A.NazArtikla, A.JedMjere AS JedMjereArtikla
FROM Stavka S
INNER JOIN Artikl A ON S.SifArtikla = A.SifArtikla
WHERE IdDokumenta = @IdDokumenta;
END

12.6.2 Spremanje složenog objekta


Spremanje složenih objekata u ovom primjeru je izvedeno korištenjem Entity Frameworka za DAL
sloj. Prilikom spremanje složenog poslovni objekt se kopira u DTO objekt ( u ovom primjeru
DAL.Dokument) zajedno sa svojim stavkama. Novi složeni objekt se dodaje u kontekst čime se
automatski dodaju i njegove stavke. Objekte označene za brisanje treba zakačiti za kontekst i označiti
za brisanje, a isto treba učiniti i za njegove stavke.

Primjer:  Firma.BLL \ Firma.EF \ DokumentDalProvider.cs

public void SaveChanges(IEnumerable<Dokument> changedItems,


IEnumerable<Dokument> newItems, IEnumerable<Dokument> deletedItems) {
...
foreach (var item in newItems) {
ctx.Dokument.Add(item);
}

foreach (var item in deletedItems) {


var ctxDokument = ctx.Dokument.Attach(item);
foreach (var stavka in ctxDokument.Stavke.ToList()) {
ctx.Stavka.Attach(stavka);
ctx.Entry(stavka).State = System.Data.EntityState.Deleted;
}
ctx.Entry(item).State = System.Data.EntityState.Deleted;
}
ctx.SaveChanges();
}

Ažuriranim dokumentima svojstva se mijenjaju tako da se iz baze podataka u kontekst dohvati traženi
dokument te mu se sa SetValues postave nove vrijednosti. Isto je potrebno napraviti i za sve one
stavke koje neće biti obrisane, odnosno za stavke koje nisu nove. Stavke iz dohvaćenog dokumenta iz
baze podataka kojih više nema u dokumentu iz poslobvog sloja treba označiti za brisanje, a nove
stavke kojih dosad nije bilo u bazi podataka treba dodati u kontekst na način da se dodaju
dohvaćenom dokumentu.

163
Primjer:  Firma.BLL \ Firma.EF \ DokumentDalProvider.cs

foreach (var dokument in changedItems) {


var dbDokument = ctx.Dokument.Find(dokument.IdDokumenta);
ctx.Entry(dbDokument).CurrentValues.SetValues(dokument);
//uzmi postojeće stavke iz baze
foreach (var dbStavka in dbDokument.Stavke.ToList()) {
var stavka = dokument.Stavke.FirstOrDefault
(s => s.IdStavke == dbStavka.IdStavke);
//da li stavka još uvijek postoji ili ju treba obrisati
if (stavka != null)
ctx.Entry(dbStavka).CurrentValues.SetValues(stavka);
else
ctx.Entry(dbStavka).State = System.Data.EntityState.Deleted;
}
foreach (var stavka in dokument.Stavke.
Where(s => s.IdStavke == default(int)).ToList())
//nove stavke nemaju id
dbDokument.Stavke.Add(stavka);

Česta situacija je da je vrijednost primarnog ključa samopovećavajuća vrijednost (tip podatka


identity) pa se vrijednost primarnog ključa za nove objekte može dobiti tek nakon pohrane u bazi.
Kod jednostavnih objekata ovo ne predstavlja problema, ali za složene objekte je bitno, jer se ta
vrijednost koristi kao vrijednost stranog ključa u stavkama. DAL sloj izveden pomoću Entity
Frameworka automatski ažurira vrijednosti identity PK-a nakon snimanja, ali u objektu unutar
konteksta, što je u primjeru višeslojne aplikacije DTO objekt. Stoga je potrebno napisati programski
kod koji se uspostavlja preslikavanje između poslovnog objekta i DTO objekta koji je proslijeđen DAL
sloju. Preslikavanje se čuva unutar kolekcije tipa Dictionary<poslovni objekt, dal objekt> te se nakon
snimanja iz DAL objekata dohvaća vrijednost primarnog ključa i pridružuje novim dokumentima i
stavkama.

Primjer:  Firma.BLL \ Firma.BLL \ DokumentBllProvider.cs

public void SaveChanges(IList changedItems) {


...
//veza poslovnog i dal objekta za naknadno postavljanje id-a
var dictUpdated = new Dictionary<Dokument, DAL.Dokument>();
...
var dtoObject = (DAL.Dokument)item.ToDtoObject();
dictUpdated[item] = dtoObject;
newItems.Add(dtoObject);
...
dal.SaveChanges(editedItems, newItems, deletedItems);
foreach (var pair in dictUpdated) {
if (!pair.Key.IdDokumenta.HasValue)
pair.Key.IdDokumenta = pair.Value.IdDokumenta;
...

12.7 Refleksija i korisnički atributi


Refleksija je proces kojim program može pregledavati i modificirati vlastitu strukturu tijekom
izvođenja. Refleksija se upotrebljava za:

 dohvat metapodataka razreda i postupaka sadržanih u atributima

164
 otkrivanje tipa podatka i pristup informacijama o učitanim asemblijima i tipovima definiranim
unutar njih
 dinamičko instanciranje objekata na temelju otkrivenog tipa (engl. dynamic invocation),
pozive postupaka tako stvorenih objekata te dohvat i izmjenu svojstava
 stvaranje i korištenje novih tipova za vrijeme izvršavanja programa.
Uobičejeno, refleksija se koristi za razvoj aplikacija za reverzno inženjerstvo, razvoj preglednika
razreda i razvoj editora svojstava razreda (primjer: prozor Properties). Prostor imena u kojima se
nalaze postupci za refleksiju je System.Reflection, a najznačajniji razredi su System.Type u kojem su
sadržane informacije o tipu podatka. Objekt tip System.Type moguće je dobiti na nekoliko načina u
ovisnosti da li je razred o kojem se žele dobiti informacije dostupan (referenciran) u trenutku
kompilacije.
Ako je ciljani razred referenciran u trenutku kompilacije, objekt tipa Type može se dobiti na jedan od
sljedeća tri načina:

 Type t = r.GetType(); gdje je r objekt razreda čijim se informacijama želi pristupiti


 Type t = typeof(NazivRazreda);
 Type t = Type.GetType("NazivProstoraImena.NazivRazreda");
Ako ciljani razred nije referenciran u trenutku kompilacije može se iskoristiti zadnje navedeni način
pristupa, ali uz navođenje punog imena razreda i naziv asemblija (uobičajeno jednak nazivu projekta)
u kojem je definiran

 Type t = Type.GetType("NazivProstoraImena.NazivRazreda,
NazivAsemblija");
Ako je razred generički, to jest, parametriziran po jednom ili više tipova, tada se iza naziva imena
dodaje znak ` i broj tipova s kojima je razred parametriziran, npr.

 Type.GetType("System.Collection.Generic.Dictionary`2");
Postupak GetType je preopterećen te po želji omogućuje ignoriranje velikih i malih slova. Za slučajeve
u kojima je ciljani razred definiran u nekom drugom asembliju, asemblij prethodno treba učitati
postupkom Assembly.Load ili Assembly.LoadFrom ovisno je li asemblij u istoj mapi kao i izvršni
program ili se navodi puna putanja do asemblija.

12.7.1 Naknadno povezivanje


Naknadno povezivanje (engl. late binding) je tehnika kojom se može stvoriti objekt nekog tipa i
pozvati njegov postupak bez poznavanja tipa u trenutku kompilacije. Postupak CreateInstance unutar
razreda Activator stvara novi objekt određenog tipa koji ne treba biti referenciran u trenutku
kompilacije programa.
Primjerice u sljedećem programskom odsječku stvorit će se objekt tipa Loto pri čemu je taj razred
definiran u nereferenciranom projektu/asembliju, pa se navodi (u ovom slučaju) relativna putanja do
njega. Program ispisuje popis svih varijabli, postupaka, svojstava, … ovog razreda (za detalje
pogledati izvorni kôd primjera), nakon čega se stvara novi objekt. Postupkom CreateInstance moguće
je pozvati bilo koji konstruktor nekog razreda, ali je na pozivatelju da preda ispravan broj argumenata
odgovarajućeg tipa (nema provjere prilikom kompilacije). Nakon što je objekt stvoren, nad njim je
moguće pozvati proizvoljni postupak.

165
Primjer:  Refleksija\Refleksija\Program.cs

Assembly asm = Assembly.LoadFrom


(@"..\..\..\ClassLibrary1\bin\Debug\ClassLibrary1.dll");
Type type = asm.GetType("ClassLibrary1.Loto");

//...prikaz informacija

//stvaranje novog objekta


object obj = Activator.CreateInstance(type, 7, 39);
MethodInfo info = type.GetMethod("IzvuciBrojeve");
object result = info.Invoke(obj, new object[] {false});
string ispis = string.Join(", ", (List<int>)result);
Console.WriteLine(ispis);

U prethodnom primjeru naziv razreda i naziv postupka su bili tvrdo kodiran, pa nije jasna prednost
ovog pristupa. Praktična korist dinamičkog povezivanja dolazi do izražaja kod izrade raznih proširenja
(engl. plug-in). U tom slučaju je dovoljno definirati sučelje, a korisniku prilikom izvršavanja programa
dopustiti odabir asemblija s konkretnom implementacijom. Refleksijom se tada može pronaći razred
koji implementira traženo sučelje, stvoriti novi objekt i pretvoriti ga cast operatorom u prethodno
definirano sučelje i koristiti kao i svaki drugi objekt.
Druga praktična korist dinamičkog instanciranja je kod implementacije razreda BusinessBase u
trenutku u kojem se potrebno napraviti kopiju objekta, pa se na ovaj način jednostavno može stvoriti
novi objekt, a zatim kopirati svojstva iz jednog objekta, u drugi.

12.7.2 Atributi
Atributi su nadodane oznake tipovima, poljima, postupcima i svojstvima. Atributi se dodaju u uglatim
zagradama [ ] prije deklaracije entiteta koji opisuju te su dostupni programski tijekom izvođenja
programa. Atributi ne mijenjaju razred ili svojstvo sami po sebi, ali je moguće da se u trenutku
izvršavanja neki program koji koristi „ukrašene“ razrede drugačije ponaša u ovisnosti postoji li neki
atribut ili ne. Primjeri preddefiniranih atributa su:
 Browsable – postavlja vidljivost svojstva ili događaja u prozoru Properties
 Category – kategorija u kojoj se svojstvo ili događaj prikazuje u Properties. Neke
preddefiniranie kategorije su Data, Behavior, Design, Action, Misc te je moguće definirati i
vlastite kategorije
 Description - opisuje svojstvo ili događaj te se taj opis prikazuje prilikom prikaza svojstava u
dizajnu
 Obsolete – označava da je neki postupak zastario i uzrokuje upozorenje pri kompilaciji s
opisom koji je naveden uz atribut
Novi atribut se definira definiranjem novog razreda izvedenim iz razreda System.Attribute i
definiranjem svojstava atributa. Uobičajeno je da naziv razreda završava s Attribute, dok se kod
korištenja taj sufiks može izostaviti kao što je prikazano u sljedećem primjeru.

166
public class PrviAttribute : System.Attribute{
public string Naziv { get; set; }
public int Broj { get; set; }
}
public class DrugiAttribute : System.Attribute{
public string X { get; set; }
}
[Prvi(Broj=5, Naziv="Test")]
[DrugiAttribute(X = "Proba")]
public class MojRazred{...}

Popis svih vlastitih atributa može se dobiti postupkom GetCustomAttributes na nekom tipu, a primjer
provjere postojanje nekog atributa na nekom članu razreda prikazan je u poglavlju 12.7.3.

12.7.3 Univerzalni i samoprilagodljivi programski moduli


Tzv. univerzalna ili generička forma predstavlja primjenu refleksije i korisničkih atributa. Inicijalni
dizajn forme sadrži samo navigacijske kontrole, dok se u konkretnoj naslijeđenoj formi dinamički
stvaraju kontrole koje omogućavaju ažuriranje konkretnog podatka.

Slika 81. Izgled generičke forme i konkretne forme izvedene iz generičke forme

Umjesto kreiranja i instanciranja namjenski oblikovane forme koja bi bila dizajnirana za pojedini tip
podatka (npr. Artikl) kreira se općenita forma koja se prilagođava predanom skupu podataka i
odgovarajućem tipu podatka. Prilikom instanciranja generičkoj formi je potrebno predati informaciju
o naslovu forme, tipu podatka koji se pokazuje na formi, referencu na objekt iz poslovnog sloja koji
može vršiti validaciju i izmjenu podataka te kolekciju podataka koja treba služiti kao izvor podataka.

167
Primjer:  FirmaWin – MainForm - Šifrarnici

private void artiklToolStripTablice_Click(...)


{
using (new StatusBusy())
{
ArtiklBllProvider bll = new ArtiklBllProvider();
GenericForm f = new GenericForm("Artikl",
bll.FetchAll(), bll, typeof(Artikl));
f.MdiParent = this;
f.Show();
}
}

Generička forma tada provjerava metapodatke poslovnog objekta i na osnovu vlastito definiranih
atributa dinamički stvara odgovarajuće kontrole za unos i izmjenu podatka (Textbox, Checkbox, …) i
natpise ispred kontrola.

Slika 82. Vlastiti atribut korišten od strane generičke forme

Vlastiti atribut korišten u ovom primjeru je GenericFormAttribute koji se sastoji od 3 svojstva:


DisplayFormat za definiranje formata prikazanog podatka, DisplayName za natpis ispred kontrole i
TextAlignement za određivanje poravnanja dinamički stvorene kontrole pri čemu je ugrađenim
atributom označeno da se ovaj atribut može koristiti samo na razini svojstava.

Primjer:  FirmaWin \ Firma.Framework \ GenericFormAttribute.cs

[AttributeUsage(AttributeTargets.Property)]
public sealed class GenericFormAttribute : Attribute {
private string displayName = string.Empty;
private string displayFormat = string.Empty;
private HorizontalAlignment ha = HorizontalAlignment.Left;
...
public GenericFormAttribute(string displayName) {

Primjer korištenja vlastitog atributa na poslovnom objektu Artikl dan je sljedećim primjerom.

168
Primjer:  FirmaWin \ Firma.BLL \ BusinessEntities \ Artikl.cs

[GenericForm("Šifra")]
public int? SifArtikla {
get { return sifArtikla; }
set { ... }
}
...
GenericForm("Cijena", "N2",
HorizontalAlignment.Right)]
public decimal? CijArtikla {
get { return cijArtikla; }
set { ... }
}

Prilikom pokretanja generička forma vrši obradu atributa refleksijom te za pojedino svojstvo dodaje
prikladnu kontrola na formu. Dohvat svih javnih svojstava danog poslovnog objekta vrši se pomoću
postupka GetProperties iz razreda Type pri čemu se odabiru samo javna svojstva definirana na razini
objekta (ne-statička svojstva). Na formu se dodaju samo ona svojstva koja imaju definiran vlastiti
atribut za prikaz na univerzalnoj formi.

169
Primjer:  FirmaWin \ Firma \ Core \ GenericForm (SetupForm)

private Type boType;


PropertyInfo[] props = boType.GetProperties(
BindingFlags.Instance | BindingFlags.Public);

foreach (PropertyInfo prop in props) {


if (Attribute.IsDefined(prop, typeof(GenericFormAttribute))) {
GenericFormAttribute atr =
(GenericFormAttribute)Attribute.GetCustomAttribute(prop,
typeof(GenericFormAttribute));

//kreiranje odgovarajuće kontrole


Control c = null;
if (prop.PropertyType.Equals(typeof(bool)) ||
prop.PropertyType.Equals(typeof(bool?))) {
c = new CheckBox();
c.DataBindings.Add(new Binding(
"CheckState", bindingSource, prop.Name, true));
}
else if (prop.PropertyType.Equals(typeof(DateTime)) ||
prop.PropertyType.Equals(typeof(DateTime?))) {
c = new DateTimePicker();
c.DataBindings.Add(new Binding("Value", bindingSource, prop.Name));
}
else {
c = new TextBox();
...
}
//kreiranje labele
Label l = new Label();
l.Text = atr.DisplayName;

// Postavljanje kontrole i labele na formu


this.Controls.Add(l);
l.Location = labelPos;
labelPos.Y += 26;

c.Width = 300;
this.Controls.Add(c);
c.Location = controlPos;
controlPos.Y += 26;

l.SendToBack();
c.Enter += new EventHandler(ControlEnter);
c.Leave += new EventHandler(ControlLeave);
}
...
}

Dodatni primjer refleksije je dohvat ili postavljanje vrijednosti nekom svojstvom, konkretno u
sljedećem primjeru postavljanje vrijednosti kontroli povezanoj na neki izvor podataka na prazni string
ili null u ovisnosti o kojem tipu podatka se radi.

170
Primjer:  FirmaWin \ Firma \ Core \ GenericForm (ControlLeave)

public static class Utils {


public static void SetNull(Control c,
string bindedProperty, object businessObject) {
Binding b = c.DataBindings[bindedProperty];
PropertyInfo p = businessObject.GetType().GetProperty(
b.BindingMemberInfo.BindingField,
BindingFlags.Instance | BindingFlags.Public);

if (p.PropertyType.Equals(typeof(string))) {
p.SetValue(businessObject, string.Empty, null);
}
else {
p.SetValue(businessObject, null, null);
}

12.8 Zadaci
Zadatak 1. Što je poslovni objekt, gdje se i kako koristi?
Zadatak 2. Što je Data Transfer Object? Gdje se i kako koristi?
Zadatak 3. Što je refleksija i kako se koristi?

171
13 Web aplikacije - ASP.NET Web Forms
Web aplikacija je programska aplikacija kojoj se pristupa preko internetskog preglednika ili drugog
programa koji implementira HTTP (Hyper Text Transfer Protocol). Svaki skup web stranica nije ujedno
i web aplikacija. Aplikacija koristi programsku logiku da bi prikazala sadržaj korisniku te se obično
izvršava neki programski kod na serveru, pa tako primjerice statički cjenik nije aplikacija, a dinamički
cjenik je aplikacija. Kao primjer jedne web aplikacija može se uzeti Ahyco (http://ahyco.fer.hr) koji
uključuje funkcije identifikacije (autorizacije) korisnika, pisanja provjere, pregleda rezultata, a sastoji
se od baze podataka i programske logike za generiranje web stranica.
Elemente web stranice čine:

 HTML (HyperText Markup Language) elementi: osnovni jezik za definiranje web stranica
 JavaScript: jezik za klijentske skripte koje izvodi preglednik
 kod u nekom drugom programskom jeziku(npr. C#, Java, PHP, …) koji se izvodi na poslužitelju
Aplikacija se izvršava i na poslužitelju i na klijentu, pri čemu klijentski dio mora biti napisan u nekom
od jezika koji Web preglednik podržava (HTML, JavaScript), a pristup resursima na strani klijenta bude
ograničen (npr. JavaScript koji se izvršava unutar preglednika ne može čitati s korisnikovog diska).
Prednost web aplikacije u odnosu na klasičnu aplikaciju je veći broj korisnika (bilo tko s pristupom
Internetu26, na bilo kojem operacijskom sustavu koji ima internetski preglednik), jednostavno
(centralizirano) održavanje i nadogradnja na novu verziju (nema potrebe za instalacijskom
procedurom kod korisnika).
Nedostaci web aplikacije su složenija izrada u odnosu na samostojne klijentske aplikacije, potreba za
posebno dizajniranim sučeljem, mogući problemi pri prikazu u različitim preglednicima, potrebna
prilagodba regionalnim posebnostima različitih korisnika te sigurnosni problemi kao što su
neovlašten pristup aplikaciji i poslužitelju te zatrpavanje prometom.

13.1 ASP.NET Web Forms


Skraćenica ASP.NET dolazi od naziva Active Server Pages .NET, što je redizajn Microsoftove
tehnologije ASP prije .NET platforme i predstavlja zajednički naziv (platformu) za dva koncepta izrade
web aplikacija: Web Forms i MVC. U oba slučaju web stranice se dinamički generiraju na poslužitelju
povrh .NET Frameworka i IIS poslužitelja (Internet Information Services) i klijentu se isporučuje HTML
i ostali elementi (stilovi, skripte, …). Poslužiteljska logika se piše u nekom od dostupnih .NET jezika
(najzastupljeniji je C#), a programski kod se unaprijed prevodi što rezultira bržim izvođenjem za
razliku od skriptnih jezika kao što je npr. PHP.
Odabirom tehnike Web Forms simulira se izrada web aplikacija nalik windows formama s
ograničenjima web tehnologije, a za razvoj je dostupno oko šezdesetak unaprijed definiranih
serverskih kontrola podijeljenih u nekoliko grupa (naredna slika).

26
Teoretski bilo tko, praktično ovisi o dizajnu sučelja i klijentskom dijelu aplikacije
172
Slika 83. Skup serverskih konstrola za ASP.NET Web Forms

13.1.1 Prevođenje i prikaz web stranica


Slika 84slika prikazuje način prevođenja i prikaza dinamičke stranice (ekstenzija aspx). Pri prvom
zahtjevu za nekom dinamičkom stranicom web poslužitelj proslijeđuje zahtjev ASP.NET platformi
(ASP.NET runtime engine) te dolazi do parsiranja aspx stranice i generiranja razreda s pozadinskim
programskim kodom. Povezivanje i kompilacija se odvija samo prvi put za pojedinu stranicu te se
čuva za sljedeće zahtjeve. Potom se instancira primjerak generirane klase koji priprema HTML kao
odgovor korisniku.

173
Slika 84. Prevođenje i prikaz web stranica

13.1.2 Web aplikacija i/ili web mjesto


Web aplikacija s tehnologijom Web Forms može se stvoriti na dva načina: odabirom projekta tip
ASP.NET Web Application ili stvaranjem novog web mjesta (ASP.NET Web site).27

Web aplikacija
Korištenjem varijante ASP.NET Web Application kreira se rješenje i odgovarajući projekt u kojem je
svaka dinamička stranica sastavljena iz 3 dijela:

 Stranica.aspx s HTML kodom i serverskim kontrolama,


 Stranica.aspx.cs koja sadrži pozadinski kod za obradu događaja (tzv. CodeBehind) i
 Stranica.aspx.designer.cs u kojem su definicije članski varijabli za korištene kontrole.
Svaka stranica pripada prostoru imena ovisno o nazivu projekta i mapi unutar koje se nalazi (npr.
naziv razreda je oblika WebAppName.Putanja.Stranica). Svaku datoteku koja treba biti dio aplikacije
treba eksplicitno dodati u projekt. Nakon kompilacije, stvara se jedna dll datoteka za cijelu web
aplikaciju, a moguće je definirati događaje prije i poslije izgradnje aplikacije (PreBuild & PostBuild).
S obzirom da datoteke treba verzionirati i automatski kompilirati potrebno je napraviti rješenje u
obliku web aplikacija pa treba odabrati opciju New → Project → ASP.NET Web Application i odabrati
predložak Empty i označiti kvačicu WebForms (Slika 85).28

27
Ova dvostruka mogućnost nastala je promjenama razmišljanja u razvoju ASP.NET-a u ranijim verzijima Visual
Studia te se zadržala do danas.
28
Stvaranje uobičajenog projekta tipa WebForms stvorilo bi predložak aplikacije koja uključuje provjeru
autentičnosti, predložak glavne stranice, uobičajene JavaScript biblioteke itd., što može biti suvišno za primjere
koji se izrađuju u okviru kolegija, a zauzimaju značajno količinu prostora i usporavaju kompilaciju na serveru.
174
Slika 85. Stvaranje nove web aplikacije

Web mjesto
U varijanti s web mjestom nije potreban projekt, odnosno rješenje, jer su sve datoteke unutar web
mjesta automatski dio „projekta“. Svaka dinamička stranica sastoji se od 2 dijela:

 Stranica.aspx s HTML kodom i serverskim kontrolama,


 Stranica.aspx.cs koja sadrži pozadinski kod za obradu događaja (u ovom slučaju taj dio se
naziva CodeFile).
Nazivi prostora imena u pojedinoj datoteci za određenu stranicu nisu automatski povezani s
putanjom, a datoteke s ra razredima nevezanim za pojedinu stranicu smještaju se u mapi App_Code.
Koristi se tzv. In Place kompilacija, a za svaku stranicu se stvar po jedna dll datoteka te App_Code.dll
kao kompilat razreda u mapi App_Code.
Napomena: ove postavke je moguće promijeniti tako da se dobije samo jedan dll proizvoljnog imena.

13.1.3 Objava aplikacije i web poslužitelji


Web aplikacija treba biti postavljena na neki poslužitelj. Prilikom razvoja koristiti se IIS Express kao
privremeni web poslužitelj koji se pokreće zajedno s pokretanjem aplikacije unutar Visual Studia. IIS
Expressu se ne može pristupiti osim s lokalnog računala. Za produkciju se koristi Internet Information
Services (IIS) – Microsoftov web poslužitelj koji se može instalirati na bilo koju verziju Windowsa.
Kako bi se aplikacija mogla smjestiti na IIS potrebno ju je prethodno objaviti (ili direktno na IIS, ili
lokalnom objavom i kopiranjem datoteka). Objava se vrši desnim klikom na web aplikaciju i
odabirom opcije Publish Web Site. Prilikom objave na odredište će biti objavljene samo datoteke
potrebne za izvršavanje aplikacije: *.aspx, *.ascx, web.config, bin/*, slike, skripte, …, ali bez izvornog
koda, tj. koda u *.cs datotekama. Podrazumijevana stranica koja će se otvoriti prilikom pokretanja
aplikacije se postavlja unutar projekta opcijom desni klik → Set as Start Page, odnosno u postavkama
IIS-a (najčešće je to već postavljeno na Default.aspx, Default.html i slično).

175
13.2 Uobičajeni postupak izrade web stranica
Svaka stranica je predstavljena razredom izveden iz System.Web.UI.Page pri čemu se, slično
kao kod Windows Forms aplikacija, na svaku stranicu dodaju Web Forms kontrole izvedene iz
System.Web.UI.Control. Za svaku kontrolu postoji zasebni razred što pridonosi konzistenciji
objektnog modela i omogućuje bolju provjeru tipova pri prevođenju.
Obrada događaja standardno se obavlja na poslužitelju. ASP.NET Framework vrši transformaciju
kontrola u HTML proširujući mogućnosti HTML kontrola dodatnom funkcionalnošću kao što su
validacija, dodatni postupci, automatski PostBack pri promjeni sadržaja kontrole i slično.
Pojam PostBack predstavlja proces slanja sadržaja stranice poslužitelju na obradu. Promjene na
stranici šalju se poslužitelju koji obrađenu stranicu vraća pregledniku. Da bi se očuvao sadržaj
stranice, podaci se prenose zajedno sa stranicom, a za rekonstrukciju sadržaja koriste se skrivena
polja (__VIEWSTATE).
Uobičajeni postupak izrade web stranice sastoji se od 4 koraka: izbor željenih kontrola (Label,
DropDownList, GridView, ….), imenovanje kontrola koje referenciramo u kodu, dizajn forme -
razmještaj kontrola i postavljanje svojstava te programiranje događaja na poslužitelju.

Uobičajeni redoslijed događaja prilikom korištenja web stranice obuhvaća sljedeće korake:

1. Klijent šalje poslužitelju zahtjev za stranicom (prvi dolazak na stranicu, vrsta zahtjeva je GET)
2. Izvršava se kod metode Page_Load (svojstvo IsPostBack je false)
3. Poslužitelj dinamički kreira web stranicu i šalje je klijentu
4. Korisniku se prikazuje web stranica u pregledniku
5. Korisnik upisuje tekst u neku od kontrola (npr. imena TextBox1) te klikom na gumb (npr.
Button1) aktivira slanje podataka na server
6. Zahtjev i podaci se šalju poslužitelju (vrsta zahtjeva je POST)
7. Izvršava se kod metode Page_Load (svojstvo IsPostBack je sada true)
8. Izvršavaju se kodovi obrade događaja kontrole (npr. Textbox1_Changed, Button1_Click)
9. Nova stranica se šalje klijentu
10. Klijentu se prikazuje osvježena web stranica u pregledniku, pri čemu se može rekonstruirati
prethodni sadržaj (npr. što je bilo upisano u TextBox1)

13.3 Struktura Web Forms aplikacije


WebForms aplikacija može imati proizvoljan broj mapa u cilju bolje organizacije projekta, pri čemu
postoji nekoliko rezerviranih imena za mape s točno određenim sadržajem. Posebne mape web
aplikacije su:

 App_Browsers: sadrži datoteke kojima se identificira pojedini preglednik i utvrđuju


njegove mogućnosti
 App_Code: sadrži pomoćne razrede i kod koji nije vezan isključivo za jednu stranicu
 App_Data: mapa za spremišta podataka (Access, SQL Express, XML datoteke, …)
 App_GlobalResources: sadrži *.resx datoteke s podacima koji se mogu dohvatiti
programski (najčešće tekstovi za različite jezike)
 App_LocalResources: sadrži *.resx datoteke vezane za pojedinu stranicu
 App_Themes: sadrži datoteke kojim se definiraju teme (izgled) web stranica i web kontrola

176
 App_WebReferences: mapa za sheme i kosture razreda za rad s pojedinim web servisom
 Bin: sadrži dll datoteke, pri čemu je sadržaj ove mape automatski je referenciran unutar
projekta
Mape se mogu se dodati u aplikaciju desnim klikom na projekt i odabirom opcije Add ASP.NET Folder.

13.3.1 Osnovne Web kontrole


Web kontrole mogu se podijeliti u tri općenite kategorije:
 kontrole za unos podataka s forme (TextBox, Button, CheckBox, DropDownList, …),
 kontrole koje omogućavaju povezivanje s izvorom podataka (GridView, DataList, Repeater,
FormView, …) i
 ostale web kontrole (Label, Hyperlink, …).
Za svaku od ovih kontrola ASP.NET će kreirati odgovarajući HTML, npr. <INPUT TYPE="text">,
<INPUT TYPE="submit">, <INPUT TYPE="checkbox">, <SELECT>…</SELECT>, <A
HREF="URL">Tekst Hypelinka ….

13.3.2 Sintaksa kontrola


Kontrole se definiraju kao XML oznake, slično kao i HTML oznake (engl. tags), pri čemu svaka kontrola
mora imati atribut id uz oznaku da se kontrola generira na serveru (runat="server").

Primjer:

<asp:Button id="Button1" runat="server" Text="Pošalji"></asp:Button>


<asp:TextBox id="Textbox1" runat="server" />

Obrada događaja obnavlja se pozadinskim kodom u kojem se mogu programski postavljati svojstva
kontrola (npr. Textbox1.Text = "ABC")

13.3.3 Sintaksa aspx stranica


Osnova svake aspx stranice je HTML sadržaj i standardne HTML oznake (html, head, title,
br, span, ….). Dodatno stranica sadrži specijalne ASP.NET oznake kao što su

 direktive (engl. directives): <%@ Page Language="C#" %>


 serverske kontrole: <asp:Button runat="server">
 odsječci koda: <script runat="server">…</script>
 izrazi za povezivanje podataka: <%# %>
 komentare koda koji se izvršava na serveru: <%-- --%>
 "mješoviti" kod (isprepleteni html i .NET kod) <%= %> i <% %> (naslijeđeno iz ASP-a i
treba izbjegavati)
U direktivama stranice moguće je dodatno postaviti svojstva kao što su kodna stranica, naziv
datoteke s pozadinskim kodom, naziv glavne stranice s predloškom itd.

13.3.4 Odvajanje dizajna od koda


Aspx stranice uobičajeno sadrže HTML, serverske kontrole, Java Script i ostale prezentacijske
elemente.

177
Slika 86. aspx stranica (izvorni kod prezentacijskog dijela)

Slika 86 prikazuje primjer jedne aspx stranice - Default.aspx . Pozadinski kod se nalazi u datotekama
Default.aspx.cs te Default.aspx.designer.cs za dio koji nastaje automatski, a predstavlja povezivanje
kontrola i metoda za obradu događaja nad kontrolama.

Primjer:  Default.aspx.cs

public partial class Default : System.Web.UI.Page {


protected void Page_Load(object sender, EventArgs e){
}
protected void Button1_Click(object sender, EventArgs e)
{
Label1.Text = "Pozdrav, trenutno vrijeme je "
+ DateTime.Now.ToString();
}
}

13.4 Životni ciklus web stranice


Sljedeća tablica prikazuje redoslijed događa i značenje pojedinog događaja u životnom ciklusu web
stranice.

Tablica 1. Redoslijed događaja u životnom ciklusu aspx stranice

Događaj Značenje
Stvaranje kontrola, primjena tema, uspostava glavne (master) stranice,
PreInit
postavljanje korisničkih profila (jezik, …)
Inicijaliziranje vrijednosti svojstava kontrola na osnovu poslanih podataka ili
Init
podataka iz ViewStatea
U ovom trenutku sve kontrole su inicijalizirane i vrijednosti povezane => piše
Load
se kod po želji
Obrada događaja Obrada konkretnog događaja koji je uzrokovao Postback (npr. klik na gumb,
promjena označene vrijednosti u listi…)
Iscrtavanje/ispisivanje pojedine kontrole na izlazni tok (HTTP response)
PreRender
Uništavanje objekata
Unload
Događaj se može dogoditi bilo kada tijekom životnog ciklusa
Error
178
Presretanje, tj. obrada pojedinog događaja može se obaviti pisanje postupka oblika Page_Događaj,
npr. Page_Init(object sender, EventArgs e)

13.5 Objekti ASP.NET aplikacije


Svaka Web aplikacija nasljeđuje System.Web.HttpApplication pri čemu su važniji članovi tog razreda

 Session – sadrži podatke o sjednici (posjetima Web aplikaciji od strane istog korisnika u
nekom vremenskom intervalu) u koju se mogu pohranjivati parovi (ključ, vrijednost) koji se
vežu za korisnika, a imaju vijek trajanja jednak trajanju sjednice (primjer dohvata vrijednosti
za neki ključ: Session["ključ"])
 Application – služi za pohranu stanja Web aplikacije slično podacima iz sjednice, ali
dostupno za sve korisnike i vijekom trajanja dok je aplikacija aktivna (npr.
Application["Naslov"])
 Request – objekt koji rukuje s podacima o zahtjevu za stranicom
 Response – objekt za odgovor na HTTP zahtjev (npr. Response.Write(“<br>”) )
 Server – objekt koji sadrži informacije o poslužitelju
 User – objekt koji sadrži podatke o trenutnom korisniku

U datoteci Global.asax mogu se obraditi važniji događaji aplikacije:

 Application_Start – događaj koji predstavlja dohvat prve stranice Web aplikacije od


uključivanja web servera
 Application_End – događaj zaustavljanja aplikacije (npr. kad se web server zaustavlja)
 Session_OnStart – događaj kada pojedini korisnik prvi puta pristupa Web aplikaciji i
uspostavlja sjednicu
 Session_End – događaj u kojem se korisnik odjavio ili je isteklo dodijeljeno mu vrijeme za
sjednicu

13.6 Glavna stranica i izbornici


13.6.1 Glavna stranica
Glavna stranica (engl. master page) je stranica s ekstenzijom .master koja služi za kreiranje
zajedničkog izgleda (okvira, dizajna) za više stranica. Uobičajeno sadrži zajedničke elemente
(izbornike, zaglavlje, podnožje, …) te može sadržavati sve što i obična stranica uz jedan ili više okvira
(ContentPlaceHolder) koji predstavljaju prostor koji će puniti stranica koja koristi glavnu stranicu.

Primjer:  FirmaWebForms \ Site.master

<asp:ContentPlaceHolder ID="Content" runat="server">

Web aplikacija može imati više master stranica, a za pojedinu stranicu se navodi koja će se glavna
stranica koristiti. Stranica koja ima evidentiranu glavnu stranicu, vlastiti sadržaj dodaje samo unutar
okvira definiranih u glavnoj stranici, tj. nije dopušten nikakav sadržaj izvan oznake asp:Content.

179
Primjer:  FirmaWebForms \ Artikli.aspx

<%@ Page Language="C#" MasterPageFile="~/Site.Master“ …%>


<asp:Content ID="Content2"
ContentPlaceHolderID="Content" runat="server">...
</asp:Content>

Glavna stranica odnosno stranica koja koristi glavnu stranicu se može dodati iz izbornika Add New
Item  Web Forms Master Page, odnosno Web Form with Master Page.

13.6.2 Izbornici
Svaki izbornik je skup poveznica na određenu stranicu. Izbornik je moguće prikazati u obliku stabla
(kontrola TreeView) ili slično kao kod Windows aplikacija gdje se opcije dinamički prikazuju iz korijena
izbornika (kontrola Menu).

Elementi izbornika mogu se navoditi direktno unutar kontrole za navigaciju ili definiranjem izvora
podataka koji se veže na određenu mapu web sjedišta. Trenutni položaj određene stranice i veza
prema nadređenim stranicama može se prikazati kontrolom SiteMapPath pri čemu je za korištenje
kontrole SiteMapPath potrebno imati definiran izvor podataka za navigaciju, odnosno mapu web
sjedišta.

Mapa web sjedišta je datoteka s ekstenzijom .sitemap i sadrži hijerarhijski popis stranica uključenih u
izbornik navigacije. Nova mapa se može dodati opcijom Add New Item  Site Map. Pretpostavljeni
naziv mape web aplikacije je Web.sitemap.

Slika 87. Kreiranje izbornika pomoću kontrole Menu navođenjem vrijednosti opcija

Slika 88. Kreiranje izbornika pomoću kontrole TreeView navođenjem izvora podataka

180
Slika 87 i Slika 88 prikazuju dvije kontrole za kreiranje izbornika (Menu i TreeView) pri čemu se
elementi mogu definirati na razini kontrole punjenjem svojstva Items ili se kontrola za izbornik može
pridružiti neki izvor podataka povezan s mapom web aplikacije. Dodatno, moguće je postaviti ostala
svojstva izbornika kao što su orijentacija prikaza prvog nivoa izbornika (svojstvo Orientation), stil i
slično.

13.6.3 Mapa web sjedišta


Mapa web sjedišta se definira kao xml datoteka s popisom elemenata za izbornik. Element siteMap
sadrži elemente siteMapNode s atributima url i title.

Primjer:  FirmaWebForms \ Web.sitemap

<siteMap xmlns=...>
<siteMapNode url="" title="Izbornik">
<siteMapNode url="~/Artikli.aspx" title="Artikli" >
<siteMapNode url="~/UnosArtikla.aspx"
title="Unos novog artikla" />
</siteMapNode>
<siteMapNode url="~/DokumentStavka.aspx"
title="Dokumenti" />
...
</siteMapNode>
</siteMap>

Mapa definirana u datoteci povezuje se s kontrolom za izbornike definiranje izvora podataka

Primjer:  FirmaWebForms \ ?

<asp:TreeView ID="TreeView1" runat="server"


DataSourceID="SiteMapDataSource1">...
</asp:TreeView>
<asp:SiteMapDataSource ID="SiteMapDataSource1" .../>

Web sjedište može imati više mapa, pri čemu nije nužno imati mapu u datoteci web.sitemap, već je
moguće u datoteci web.config definirati parametre za mape

Primjer:  FirmaWebForms \ web.config

<system.web>
<siteMap defaultProvider="GlavniIzbornik">
<providers>
<add name="GlavniIzbornik"
type="System.Web.XmlSiteMapProvider"
siteMapFile="~/Navigacija/glavni.sitemap"/>
<add name="Izbornik2"
type="System.Web.XmlSiteMapProvider"
siteMapFile="~/Navigacija/izbornik2.sitemap"/>
</providers>
</siteMap>
...

181
Prilikom definiranja i izvora podataka za mapu potrebno je navesti pružatelja usluge za mapu web
sjedišta.

Primjer:  FirmaWebForms \ ?

<asp:SiteMapDataSource
ID="Izbornik2SiteMapDataSource"
runat="server"
SiteMapProvider="Izbornik2" />

13.7 Složene kontrole i povezivanje podataka


13.7.1 Povezivanje podataka
Povezani podatak se na aspx stranici prikazuje izrazom oblika <%# expression %> pri čemu izraz
expression može biti vrijednost neke kontrole ili neki izraz koji se može evaluairati kao u sljedećem
primjeru.

Primjer:  FirmaWebForms \ ?

<asp:Label id="Label1" runat="server" Text="<%# 2+3 %>" />


<asp:label id="Label2" runat="server" Text="<%# TextBox1.Text %>" />

Ili, expression može biti vezan izraz vezan za neki izvor podataka pri čemu se koriste oblici
<%# Bind(…) %> i <%# Eval(…) %>

u čijim zagradama se nalazi naziv svojstva iz izvora podataka.


Kao izvor podataka može se koristiti bilo koji razred koji implementira sučelja IEnumerable ili
IListSource. Kontrola se može povezati s izvorom podataka programski u pozadinskom kodu u
*.aspx.cs datoteci pridruživanjem konkretnih podataka svojstvu DataSource i pozivom postupka
DataBind na kontroli ili čitavoj stranici (povezuje sve kontrole sa stranice) ili u dizajnu postavljanjem
svojstva DataSourceId na neku od posebnim kontrola za izvore podataka, npr. tipa ObjectDataSource,
EntityDataSource, …

13.7.2 Povezivanje jednostavnih lista


Povezivanje jednostavnih listi, odnosno elemenata koji u prikazu sadrže vidljivi tekst i vrijednost koja
se ne vidi, a služi kao vrijednost odabranog elementa (npr. šifra artikla kao vrijednost, a naziv artikla
kao tekst u padajućoj listi) obavlja se pridruživanjem izvora podataka kontroli i navođenjem
vrijednosti za svojstva DataValueField i DataTextField kojima se navodi koje svojstvo iz izvora
podataka će se koristiti za vrijednost odnosno za tekst koji će se prikazati. Kontrole koje se povezuju
na ovaj način su ListBox, DropDownList, RadioButtonList i CheckBoxList.

13.7.3 Kontrola ObjectDataSource


Kontrola ObjectDataSource služi za povezivanje podataka slično kao kontrola BindingSource kod
Windows formi. Važnija svojstva kontrole ObjectDataSource su:
 DataObjectTypeName – tip podatka koji se povezuje
 TypeName – razred s postupcima za dohvat, brisanje, ažuriranje i sl.
 SelectMethod – naziv postupka za dohvat podataka

182
 InsertMethod – naziv postupka za dodavanje novog podatka
 UpdateMethod – naziv postupka za ažuriranje podatka
 DeleteMehod – naziv postupka za brisanje podatka
 SelectCountMethod – naziv postupka koji vraća ukupni broj elemenata u izvoru podataka.
Ako ovaj postupak nije definiran vrši se dohvat svih (SelectMethod)
 SortParamName – naziv argumenta u postupku dohvata (SelectMethod) koji određuje
redoslijed sortiranja. Nazivu svojstva se pridodaje sufiks DESC za silazno sortiranje, a naziv
može biti prazan ako izvor ne podržava sortiranje
 EnablePaging – označava podržava li SelectMethod straničenje. Ako da, svojstvo
StartRowIndexParameterName služi za definiranje naziva argumenta za poziciju početnog
elementa (pretpostavljeno startRowIndex), a svojstvo MaximumRowsParameterName sadrži
naziv argumenta za broj podataka koje treba dohvatiti (pretpostavljeno maximumRows)

Slika 89. Primjer izvora podataka s pridruženim vrijednostima za važnija svojstva

Parametar u postupcima za dohvat, dodavanje, ažuriranje i brisanje imaju iste nazive kao što su nazivi
pojedinih stupaca ili postoji jedan parametar jednak tipu navedenom pod svojstvom
DataObjectTypeName. U primjeru su implementirani pomoćni razredi (s navedenim postupcima) za
rad s poslovnim objektima iz Firma.BLL, pa je u projekt bilo potrebno uključiti datoteke Firma.BLL.dll,
Firma.EF.dll i Firma.Framework.dll, a u datoteku Web.config dodati connection stringom pod nazivom
FirmaEntities.

183
Primjer:  FirmaWebForms \ DataControllers \ ArtiklController.cs

[DataObject]
public class ArtiklController{
[DataObjectMethod(DataObjectMethodType.Select, true)]
public BusinessBaseList<Artikl> DohvatiSveArtikle(){
ArtiklBllProvider bll = new ArtiklBllProvider();
return bll.FetchAll();
}

[DataObjectMethod(DataObjectMethodType.Select, true)]
public BusinessBaseList<Artikl> DohvatiArtikle(int startRowIndex,
int maximumRows) { ... }

public void AzurirajArtikl(Artikl a){ ... }


public void ObrisiArtikl(Artikl a){ ... }
public void DodajArtikl(Artikl a){ ... }

Čarobnjak za povezivanje izvora podataka i metoda za rad s podacima bit će jednostavniji ukoliko se
iznad postupka stavi posebni atribut DataObjectMethod koji sugerira što postupak radi (Delete, Fill,
Insert, Select, Update) te ako se iznad razreda stavi atribut DataObject.
Za prethodno definiran pomoćni razred povezivanje u aspx stranici može se ostvariti na dva načina:
1. dohvatom svih podataka

Primjer:  FirmaWebForms \ Artikli.aspx

<asp:ObjectDataSource ID="ArtikliDataSource" runat="server"


DataObjectTypeName="Firma.BusinessEntities.Artikl"
TypeName="ArtiklController"
DeleteMethod="ObrisiArtikl" InsertMethod="DodajArtikl"
SelectMethod="DohvatiSveArtikle" UpdateMethod="AzurirajArtikl"/>

2. Ili (bolje) dohvatom samo potrebnih podataka za trenutnu stranicu. U tom slučaju treba
implementirati vlastito straničenje i sortiranje. Za straničenje treba definirati postupak koji
će biti pridružen svojstvu SelectCountMethod (u primjeru je to ArtiklCount). Slično, za
sortiranje treba deinirati SortParameterName (sortParam u primjeru DrzavaController)

184
Primjer:  FirmaWebForms \ Artikli.aspx

<asp:ObjectDataSource ID="ArtikliDataSource" runat="server"


DataObjectTypeName="Firma.BusinessEntities.Artikl"
TypeName="ArtiklController"
DeleteMethod="ObrisiArtikl" InsertMethod="DodajArtikl"
SelectMethod="DohvatiArtikle" UpdateMethod="AzurirajArtikl"
EnablePaging="True" SelectCountMethod="ArtiklCount" />

13.8 Kontrola GridView


Mreža s podacima uobičajeno se realizira kontrolom GridView. Kontrola standardno prikazuje sve
stupce (polja) povezanog izvora, ali može se definirati i podskup stupaca, a pojedinačni stupac
oblikuje se korištenjem predložaka (templates). Kontrola GridView omogućuje sortiranje podataka,
pregled po stranicama te izmjenu i brisanje prikazanih podataka, pri čemu ove operacije u ovisnosti o
izvoru podatka mogu već biti realizirane ili ih treba eksplicitno implementirati.

Slika 90. Izvori podataka za kontrolu GridView

Slika 90 prikazuje različite moguće vrste izvora podataka za kontrolu GridView kao što su: baza
podataka podržana kroz ADO.NET, Entitity Framework ili LINQ model, poslovni objekt, mapa web-
sjedišta, XML datoteka i baza podataka u Accesu.
Izvor podataka se može definirati u dizajnu pridruživanjem izvora podataka svojstvu DataSourceId ili
programski (tijekom izvođenja) pridruživanjem konkretnog izvora svojstvo DataSource nakon čega
slijedi poziv metode DataBind29.
Mreža s podacima se može grafički urediti kroz Visual Studio odabirom opcija AutoFormat (odabir
između nekoliko unaprijed definiranih izgleda mreže) i EditColumns (grafičko sučelje za definiranje
stupaca mreže) ili korištenjem CSS stilova (CSS - Cascading Style Sheets). Izgled mreže je u primjeru
odvojen od koda korištenjem CSS datoteka i pridruživanjem stila svojstvu CssClass.

Primjer:  Web \ Firma.WebForms \ Artikli.aspx

<asp:GridView ... AllowPaging="True" PageSize="15"


CssClass="zebra" ... >
...
</asp:GridView>

29
DataBind nije potreban ukoliko je unutar aspx stranice definiran DataSourceId.

185
Primjer:  Web \ Firma.WebForms \ Content \ Site.css

.zebra
{
border: thin solid black;
padding: 2px;
border-spacing : 0px;
margin: 0px;
text-align: left;
}

.zebra tbody tr:nth-child(2n+1) {


background-color: lightGoldenrodYellow;
}

.zebra tbody tr:nth-child(2n) {


background-color: paleGoldenrod;
}

.zebra tbody tr th {
background-color: tan;
}

Jednostavnija alternativa je korištenjem automatskog formatiranja u dizajnu što bi produciralo


sljedeći odsječak. Ipak, preporuča se koristiti CSS stilove radi lakšeg održavanja i manje količine HTML
sadržaja u prijenosu podataka.

Primjer:  Web \ Firma.WebForms \ Artikli.aspx

< asp:GridView ... BackColor="LightGoldenrodYellow" BorderColor="Tan"


BorderWidth="1px" CellPadding="2" ForeColor="Black" GridLines="None" ...>
<AlternatingRowStyle BackColor="PaleGoldenrod" />

13.8.1 Svojstva i događaji unutar mreže s podacima


Važnija svojstva kontrole GridView su:
 DataSourceId – izvor podataka kojim će se mreža popuniti
 AutoGenerateColumns – odabir hoće li se automatski prikazati svi stupci iz povezanog izvora
 AllowSorting, AllowPaging – odabir hoće li će se prikazati kontrole za sortiranje i straničenje.
Straničenje je najčešće automatski podržano, ali je zbog performansi ipak poželjno napisati
postupak koji vraća n elemenata od neke pozicije, jer ako takav postupak ne postoji uzimaju
se svi podaci, pa se višak odbacuje. Sortiranje ovisi o izvoru podataka: kad je izvor podataka
tipa ObjectDataSource postupak za dohvat treba napisati, a taj postupak prima parametar za
sortiranje (Primjer: FirmaWebForms–DataControllers\DrzavaControler.cs)
 DataKeyNames – kolekcija (primarnih) ključeva kojima se omogućava jednoznačno
određivanje pojedinog retka mreže prilikom brisanja i ažuriranja (npr: primarni ključ u prikazu
artikala je IdArtikla). Pojedinom ključu unutar koda se pristupa pomoću kolekcije DataKeys
(npr: grid.DataKeys[index].Value)
Događaji na mreži s podacima za koje se može napisati vlastiti rukovatelj događaja su sljedeći (crveno
su označeni događaji korišteni u primjerima):

186
 DataBound – nastaje nakon što su podaci povezani s mrežom
 PageIndexChanged – nastaje nakon što je mreža s podacima obradila zahtjev za promjenu
stranice
 PageIndexChanging – nastaje nakon klika na gumb za promjenu trenutne stranice unutar
mreže s podacima, a prije nego mreža s podacima sama obradi događaj
 RowCancelingEdit – nastaje pri ažuriranju retka nakon klika na gumb Cancel, a prije nego se
izađe iz načina rada koji omogućava ažuriranje
 RowCommand – nastaje nakon klika na gumb koji se nalazi unutar mreže s podacima
 RowCreated – nastaje nakon što se stvori pojedini redak u mreži
 RowDataBound – nastaje nakon što se pojedini redak poveže s podacima iz izvora
 RowDeleted – nastaje nakon brisanja pojedinog retka
 RowDeleting – nastaje pri brisanju retka, nakon klika na gumb Delete, a prije samog brisanja
 RowEditing – nastaje pri zahtjevu za ažuriranje retka mreže nakon klika na gumb Edit, a prije
nego samog ulaza u način rada za ažuriranje
 RowUpdated – nastaje nakon ažuriranje pojedinog retka
 RowUpdating – nastaje nakon klika na gumb Update, ali prije samog ažuriranja
 SelectedIndexChanged – nastaje pri promjeni označenog retka
 SelectedIndexChanging – nastaje nakon klika na gumb Select, prije promjene označenog
retka
 Sorted – nastaje nakon što je mreža s podacima sortirana po određenom stupcu
 Sorting – nastaje klikom na stupca po kojem se želi sortirati, a prije samog sortiranja
 DataBinding,Disposed, Init, Load, Unload, PreRender – naslijeđeno iz razreda Control

13.8.2 Primjer dodavanja izvora podataka za GridView


Slika 91 prikazuje način dodavanja izvora podataka korištenjem grafičkog sučelja. Klikom na strelicu u
gornjem desnom kutu iznad grida pojavljuje se opcija Choose Data Source na kojoj za vrstu izvora
podataka treba odabrati Object. Nakon toga, u padajućoj listi je potrebno odabrati razred i postaviti
postupke za dohvat, dodavanje, ažuriranje i brisanje, nakon čega se mogu podesiti naslovi stupaca,
formati i poravnanja.

187
Slika 91. Primjer dodavanja izvora podataka za GridView

13.8.3 Definiranje i povezivanje stupaca u mreži s podacima


Za definiranje stupaca u mreži s podacima potrebno je postaviti vrijednost svojstva
AutoGenerateColumns na false, što će se obaviti automatski odabirom konkretnog izvora podataka.
Svojstvo Columns sadrži stupce koji mogu biti različitih tipova:

 BoundField - vezani stupac koji se povezuje sa svojstvom iz izvora podataka navođenjem


naziva u svojstvu DataField.
 TemplateField - stupac definiran preko predložaka
 HyperLinkField - stupac u kojem se nalazi poveznica
 CheckBoxField - stupac u kojem su elementi tipa CheckBox
 ImageField - stupac koji sadrži poveznicu na sliku
 ButtonField - stupac koji sadrži gumbe koji predstavljaju proizvoljnu naredbu za mrežu s
podacima. Gumb može biti prikazan kao poveznica koja izaziva Postback (LinkButton), gumb
u obliku slike (ImageButton) ili kao klasični gumb (Button).
 CommandField - stupac čiji elementi mogu biti gumb za označavanje (Select), gumb za
brisanje (Delete) i tri gumba za ažuriranje pojedinog retka (Edit,Update,Cancel). Tekst
pojedinog gumba može se promijeniti
 DynamicField – stupac koji koristi svojstva ASP.Net Dynamic-a i ne koristiti se u primjerima
koji slijede

188
Primjer:  Web \ Firma.WebForms \ Artikli.aspx

<asp:GridView ID="GridView1" runat="server"


AutoGenerateColumns="False"
DataKeyNames="SifArtikla" DataSourceID="ArtikliDataSource"
PageSize="15" AllowPaging="True"
onrowupdating="GridView1_RowUpdating">
<Columns>
<asp:BoundField DataField="SifArtikla" HeaderText="Šifra artikla"
ReadOnly="True" />
<asp:BoundField DataField="NazArtikla" HeaderText="Naziv" ... />
...
<asp:BoundField DataField="CijArtikla" DataFormatString="{0:N2}"
...
</Columns>
</asp:GridView>

<asp:ObjectDataSource ID="ArtikliDataSource" runat="server"


...
</asp:ObjectDataSource>

Inicijalno su svi stupci vezani (BoundField) za određeno polje iz baze (DataField). Zbog ograničene
funkcionalnosti i mogućnosti prikaza vezane stupce je ponekad potrebno pretvorititi u stupce
dizajnirane predloškom što se može obaviti u dizajnu klikom na gornji desni rub mreže s podacima i
odabirom opcije Edit Columns → klik na stupac → "Convert this field into a TemplateField".
Predloškom je moguće definirati izgled zaglavlja stupca (HeaderTemplate), izgled podnožja stupca
(FooterTemplate), izgled pojedinog retka (ItemTemplate), izgled svakog drugog retka
(AlternatingItemTemplate), izgled retka prilikom ažuriranja (EditItemTemplate) i izgled novog retka u
mreži (InsertItemTemplate).
Kod stupaca definiranih preko predložaka potrebno je eksplicitno povezati element stupca mreže sa
stupcem u izvoru postupcima Eval i/ili Bind. Od verzije 4.5. umjesto Eval i Bind mogu se koristiti Item i
BindItem koji predstavljaju „strongly typed“ varijantu postupaka pri čemu je prethodno potrebno
postaviti svojstvo ItemType na kontroli GridView.
Eval i Bind su statičke metode razreda DataBinder i služe za jednosmjerno odnosno dvosmjerno
povezivanje, a ujedno se može i navesti format prikaza, npr. <%# Bind("Cijena",
"{0:N2}") %>. Kod dvosmjernog povezivanja vrijednost koja je vezana za neku kontrolu s Bind
prenosi se na poslužitelj prilikom postbacka.
Osim s Eval, podacima se može pristupati i drugačije u ovisnosti o tipu podataka koji se prikazuju
pomoću mreže, npr.
 za DataTable: <%# ((DataRow)Container.DataItem)["FieldName"] %>,
 za List<string>: <%# ((string)Container.DataItem %>

13.8.4 Ažuriranje redaka u mreži s podacima


Ažuriranje redaka u mreži s podacima je djelomično gotova funkcionalnost koju kontrola GridView
omogućava. Za ažuriranje se brine izvor podataka, a kontrola GridView ima ugrađene gumbe Edit,
Cancel i Update kojima se može pridijeliti proizvoljni tekst te sama implementira funkcionalnost
prijelaza iz stanja prikaza u stanje ažuriranje.

189
Kada je izvor podataka EntityFramework nije potrebno postavljati dodatne postavke, jer je
automatski implementirano rukovanje podacima na izvoru podataka.
Kada je izvor podataka neki poslovni objekt, potrebno je definirati postupke za dohvat
(SelectMethod), dodavanje (InsertMethod), ažuriranje (UpdateMethod), brisanje (DeleteMethod).
Vezani stupci i kontrole koje povezivanje ostvaruju s Bind ili BindItem automatski povezuju vrijednosti
i te vrijednosti šalju kao parametre metodama za rukovanje podacima.

13.8.5 Posebno dizajnirani stupci


Zbog limitiranosti vezanih stupaca ponekad je potrebno koristiti stupce u obliku predloška
(TemplateField). Stupac sa slikom prilagođen je s posebnim izgledom za prikaz (ItemTemplate) i za
ažuriranje (EditItemTemplate).

Primjer:  Web \ Firma.WebForms \ Artikli.aspx

<Columns>
...
<asp:TemplateField>
<EditItemTemplate>
<asp:CheckBox ID="cbObrisiSliku" runat="server"
Text="Obrisati sliku?" />
<asp:FileUpload ID="UploadSlike" runat="server" />
</EditItemTemplate>

<ItemTemplate>
<asp:Image ID="Image1" runat="server"
Width="80px" Height="50px"
ImageUrl='<%# Eval("SifArtikla",
"SlikaArtikla.aspx?SifArtikla={0}") %>' />
</ItemTemplate>
</asp:TemplateField>
...</Columns>

13.8.6 Prikaz slike iz baze podataka


Slika koja se prikazuje u prethodnom stupcu s predloškom dohvaća se na poslužitelju u posebnoj
stranici čiji je sadržaj prazan, ali pozadinski kod šalje sadržaj slike na izlazni tok.

Primjer:  Web \ Firma.WebForms \ SlikaArtikla.aspx.cs

protected void Page_Load(object sender, EventArgs e){


Response.ContentType = "img/jpeg";
int SifArtikla;
if (int.TryParse(Request["SifArtikla"], out SifArtikla)){
Artikl artikl = new ArtiklBllProvider().Fetch(SifArtikla);
byte[] slika = artikl.SlikaArtikla;
if (slika != null)
Response.BinaryWrite(slika);
else
Response.WriteFile(Server.MapPath("~/bezslike.jpg"));
}
}

Bolje, ali složenije rješenje je koristiti rukovatelj (tzv. handler), razred koji implementira IHttpHandler
i zahtijeva dodatno podešavanje u web.configu.

190
13.8.7 Prijenos argumenata stranici
U prethodnom primjeru za dohvat slike korišten je parametar koji je dodan na adresu stranice.
Standardno URL neke stranice je oblika
http://server/putanja/stranica.aspx?naziv1=vrijednost1&naziv2=vrijednost2,
a parametri iza upitnika čine tzv. QueryString. Vrijednost pojedinog parametra iz QueryStringa može
se dobiti pomoću Request["Naziv parametra"].
Neka od važnijih svojstava klase Request su:

 UserHostAddress: IP adresa klijenta (posjetitelja stranice)


 UserAgent: informacije o internetskom pregledniku klijenta
 Request.Url.OriginalString: puni URL trenutne stranice
 Request.Url.LocalPath: lokacija trenutne stranice na serveru (kazalo u kojem se aplikacija
nalazi)

13.8.8 Povezivanje za posebno dizajnirane stupce


Sva povezivanja ostvarena s Bind su automatska, a ostale vrijednosti treba eksplicitno postaviti
prilikom obrade pojedinog događaja. U tu svrhu obrađuje se događaj Updating koji predstavlja
trenutak početka ažuriranja određenog retka. Vrijednosti za određeni ključ dodaju se u kolekciji
e.NewValues, koja već sadrži automatski povezane podatke. Dohvat pojedine kontrole u retku vrši se
postupkom FindControl, a u sljedećem primjeru se provjerava briše li se slika ili se uzima upravo
poslana što će se odrediti na osnovu toga da li je checkbox naziva cbObrisiSliku označen ili ne. Ako
checkbox nije označen, a neka datoteka je odabrana u kontroli FileUpload naziva UploadSlike tada se
artiklu pridružuje nova slika.

Primjer:  Web \ Firma.WebForms \ Artikli.aspx.cs

protected void GridView1_RowUpdating(… GridViewUpdateEventArgs e){


GridView grid = ((GridView) sender);
CheckBox cb = grid.Rows[e.RowIndex].FindControl("cbObrisiSliku")
as CheckBox;
if (cb.Checked)
e.NewValues["SlikaArtikla"] = null;
else
{
FileUpload fileUpload = grid.Rows[e.RowIndex]
.FindControl("UploadSlike")
as FileUpload;
if (fileUpload.FileBytes.Length > 0)
e.NewValues["SlikaArtikla"] = fileUpload.FileBytes;
...

Zbog ugrađene pogreške u kontroli ObjectDataSource vezano uz decimalnu točku/zarez, stupac za


cijenu artikla izveden je kao TemplateField bez korištenja Bind

191
Primjer:  Web \ Firma.WebForms \ Artikli.aspx

<Columns>
...
<EditItemTemplate>
<asp:TextBox ID="tbCijena" runat="server" Text='<%#
Eval("CijArtikla", "{0:N2}") %>'></asp:TextBox>
</EditItemTemplate>

… pa zahtijeva eksplicitno postavljanje vrijednosti u kodu što je izvedeno obradom događaja


RowUpdating na mreži s podacima.

Primjer:  Web \ Firma.WebForms \ Artikli.aspx.cs

protected void GridView1_RowUpdating(...) {


TextBox cijenaArtikla = grid.Rows[e.RowIndex].FindControl("tbCijena")
as TextBox;
e.NewValues["CijArtikla"] = decimal.Parse(cijenaArtikla.Text);

13.8.9 Dodavanje novih zapisa


Kontrola GridView nema automatsku mogućnost dodavanja novih zapisa, ali je moguće definirati
svaki stupac kao TemplateField i napraviti kontrole za dodavanje novog podatka u zaglavlju ili
podnožju što je napravljeno u primjeru Dokument-Stavka (poglavlje 13.9.6). Ponekad je lakše
napraviti dodavanje na posebnoj stranici, pri čemu se može napraviti “pješice” dohvatom
pojedinačnih vrijednosti ili korištenjem naprednijih kontrola: FormView ili DetailView kao u primjeru
FirmaWebForms \ UnosArtikla.aspx.
Kontrola DetailsView služi za prikaz pojedinačnog podatka s izvora u tabličnom obliku, a FormView za
prikaz pojedinačnog podatka s izvora u proizvoljnom rasporedu pri čemu se mogu koristiti iste vrste
polja (tipova stupaca) kao i kod GridViewa. Obje kontrole omogućuju 3 načina prikaza (svojstva
DefaultMode i CurrentMode):

 ReadOnly – prikaz pojedinačnog podatka


 Insert – prikazuju se polja za unos podataka i gumbi Insert i Cancel
 Edit – prikazuju se polja za ažuriranje postojećih podataka i gumbi za Update i Cancel
Detaljnije o FormViewu može se vidjeti u primjeru za master-detail (poglavlje 13.9.1). Sličan primjer,
ali bez validacije može se vidjeti u FirmaWebForms \ NovaDrzava.aspx.

13.8.10 Validacijske kontrole


Svaka validacijska kontrola odnosi se na neku kontrolu (svojstvo ControlToValidate) te ima tekst
poruke za pogrešku (ErrorMessage). Validacijska provjera se vrši pomoću automatski generiranog
JavaScripta i prije slanja podataka na server (prije postbacka).
Značajnije validacijske kontrole su RequiredFieldValidator koji provjerava je li određeno polje
popunjeno i RegularExpressionValidator koji vrši provjeru regularnog izraza definiranog u svojstvu
ValidationExpression.

192
Provjera na serveru (nakon postbacka) može se obaviti provjerom svojstva Page.IsValid, a dodatno se
mogu napisati vlastite validacijske kontrole za postupke koji se trebaju izvesti na serveru u metodi
pridruženoj događaju onservervalidate.

Primjer:  Web \ Firma.WebForms \ UnosArtikla.aspx

<asp:RegularExpressionValidator ...
ErrorMessage="Cijena artikla mora biti decimalni broj s maksimalno dvije
decimale"
ValidationExpression="\d*(\,\d{1,2})?"
ControlToValidate="tbCijenaArtikla" />

<asp:RequiredFieldValidator ... ControlToValidate="tbJedinicaMjere"


ErrorMessage="Jedinica mjere mora biti unešena." />

<asp:CustomValidator ... ErrorMessage="Šifra artikla već postoji"


onservervalidate="CustomValidatorSifArtikla_ServerValidate"
ControlToValidate="tbSifArtikla"/>

13.9 Primjer Dokument-Stavka


Slika 92 prikazuje izgled rješenja za primjer Dokument-Stavka pri čemu se u svakom trenutku vidi
pojedinačni dokument prikazan pomoću kontrole FormView i njegove stavke unutar kontrole
GridView. Primjer omogućuje obradu podataka: dodavanje, brisanje i ažuriranje - dokumenta i
njegovih stavki. Prilikom unosa podataka obavlja se validacija.

Slika 92. Primjer Dokument-Stavka

Za realizaciju ovog primjera korištena je kontrola ObjectDataSource koja služi za definiranje izvora
podataka na koji se neka složena kontrola povezuje. U ovom primjeru definirano je 5 izvora
podataka:

 DokumentDataSource: izvor podataka za dokument, pri čemu se za rukovanje podacima


koriste postupci iz DokumentControler.cs

193
 StavkeDataSource: izvor podataka za stavke pojedinačnog dokumenta korištenjem
postupaka iz DokumentControler.cs
 ArtiklDataSource: izvor podataka za odabir artikla iz padajuće liste za pojedinu stavku
dokumenta korištenjem postupka iz ArtiklLookupController.cs za dohvat artikala
 PrethodniDokumentDataSource: izvor podataka za padajuću listu za odabir prethodnog
dokumenta korištenjem postupka FetchLookup iz DokumentBllProvidera iz primjera
Firma.Win
 PartnerDataSource: izvor podataka za padajuću listu za odabir partnera korištenjem
postupka FetchLookup iz PartnerBllProvidera iz primjera Firma.Win.

13.9.1 Kontrola FormView


Kontrola FormView omogućava prikaz jednog podatka u proizvoljnom rasporedu i podržava
straničenje. Podatak koji se prikazuje određen je izvorom podataka i primarnim ključem (ključ može
biti kompozitni).

Izvor se postavlja kroz svojstvo DataSourceId (npr. DataSourceID="DokumentDataSource"), a


primarni ključ(evi) u svojstvu DataKeyNames (npr. DataKeyNames="IdDokumenta").

Kontrola FormView ima 3 načina prikaza (ReadOnly, Insert, Edit) te se za svaki od njih dizajnira
posebni predložak kao primjerice na Slika 93 i Slika 94.

Slika 93. Primjer predloška za kontrolu FormView

Slika 94. Primjer predloška za unos novog podatka korištenjem kontrole FormView

13.9.2 Dohvat dokumenta


Postupak za dohvat dokumenata označen je atributom DataObjectMethod, što nije nužno, ali
olakšava odabir u čarobnjaku za definiranje izvora podataka. Za dohvat se koriste metode iz Firma.Bll
primjera Firma.Win. Kako bi kontrola FormView mogla ispravno formirati poveznice za stranice u
ovisnosti o broju dokumenata potrebno je dohvatiti sve dokumente iz baze podataka. S obzirom da
dohvat svih dokumenata značajno utječe na performanse primijenjen je jedan postupak uobičajen u
194
takvim slučajevima, a to je tvz. lijeni dohvat (lazy fetch) u kojem su dohvaćeni samo svi primarni
ključevi dokumenata, a onda se pojedini dokument učitava u trenutku kad prvi put bude zatražena
vrijednost nekog njegovog svojstva.

Primjer:  Web \ Firma.WebForms \ App_Code \ DokumentController.cs

[DataObject]
public class DokumentController {

[DataObjectMethod(DataObjectMethodType.Select, true)]
public DokumentList DohvatiDokumente() {
var bll = new DokumentBllProvider();
var list = bll.FetchLazy();
return list;
}
...

Slika 95. Postavke izvora podataka za dokumente

13.9.3 Predložak za prikaz dokumenta


Predložak za prikaz dizajnira se unutar oznake ItemTemplate unutar kontrole FormView. Za prikaz
pojedine vrijednosti iz dokumenta koristi se postupak Eval uz opcionalno navođenje formata prikaza.

Primjer:  Web \ Firma.WebForms \ DokumentStavka.aspx

<%# Eval("IdDokumenta") %> ...


<%# Eval("IznosDokumenta", "{0:C2}") %> ...
<%# Eval("DatDokumenta", "{0:d.M.yyyy}") %>

Na dnu predloška dodani su gumbi za prelazak u neki od drugih načina prikaza (naredbe Edit i New) i
gumb za brisanje pojedinog dokumenta (naredba Delete). Nijedan od gumba ne izaziva validaciju
(CausesValidation=false) što je bitno u slučaju da se na stranici neki drugi podatak nalazi u stanju
ažuriranja, jer bi neuspješna validacija tog drugog podatka onemogućila promjenu stanja kontrole

195
FormView. Gumbu za brisanje pri inicijalizaciji (obrada događaja Init) se dodaje Javascript kod za
potvrdu brisanja (pogledati postupak DeleteButton_Init)

Primjer:  Web \ Firma.WebForms \ DokumentStavka.aspx

<asp:Button ID="EditButton" runat="server" Text="Ažuriraj"


CausesValidation="False" CommandName="Edit" />
<asp:Button ID="DeleteButton" runat="server" Text="Obriši"
CausesValidation="False" CommandName="Delete"
oninit="DeleteButton_Init"/>
<asp:Button ID="DodajButton" runat="server"
CausesValidation="False" CommandName="New"
Text="Dodavanje novog dokumenta" />

13.9.4 Predložak za ažuriranje dokumenta


Predložak za ažuriranje dokumenta dizajnira se unutar oznake EditItemTemplate unutar kontrole
FormView. Za podatke koji se mogu promijeniti koristi se kontrole TextBox, DropDownList, …, a
povezivanje se vrši postupkom Bind uz opcionalno navođenje formata prikaza. Sve vrijednosti
povezane s Bind automatski se prosljeđuju izvoru prilikom slanja podataka na poslužitelj.

Primjer:  Web \ Firma.WebForms \ DokumentStavka.aspx

<asp:TextBox ID="tbDatDokumenta" runat="server"


Text='<%# Bind("DatDokumenta", "{0:dd.MM.yyyy}") %>' />
...
<asp:DropDownList ID="ddlPartneri" runat="server"
SelectedValue='<%# Bind("IdPartnera") %>'
DataSourceID="PartnerDataSource"
DataTextField="Text" DataValueField="Key" />

Na dnu predloška dodan gumb za spremanje (naredba Update) i gumb za odustajanje (naredba
Cancel). Za sve kontrole za unos potrebno je dodati validacijske kontrole i postaviti svojstvo
ValidationGroup na vrijednost jednako kao i kod gumba za spremanje promjena.

Primjer:  Web \ Firma.WebForms \ DokumentStavka.aspx

<asp:Button ID="UpdateButton" runat="server" CausesValidation="True"


CommandName="Update" Text="Spremi" ValidationGroup="Dokument"/>

<asp:Button ID="UpdateCancelButton" runat="server"


CausesValidation="False" CommandName="Cancel" Text="Odustani"/>

13.9.5 Odabir vrijednosti stranog ključa padajućom listom


Partner, prethodni dokument i artikl u stavkama se odabiru iz padajuće liste koja je vezana na neki
izvori tipa ObjectDataSource koji koristi metode iz BllProvidera iz poslovnog sloja.
Za dohvat se koristi postupak FetchLookup30 kojim se, umjesto svih podataka, dohvaćaju samo ona
svojstva koja se prikazuju u padajućoj listi ili koriste za odabir vrijednosti. FetchLookup vraća listu
elemenata tipa LookupData koji ima Key i Text. Za vrijednost u padajućoj listi korišteno svojstvo Key,

30
Iznimno, zbog performansi padajuća lista za artikle je spremljena u memoriju, pa se koristi pomoćni razred
DataControllers\ArtiklLookupController.cs
196
a za prikaz teksta u padajućoj listi korišteno svojstvo Text iz LookupData. Odabrani element se veže
na konkretno svojstvo iz Dokumenta (npr. na IdPartnera).
U padajućim listama moguće je kombinirati fiksne vrijednosti i vrijednosti iz izvora podataka na način
da se svojstvo AppendDataBoundItems postavi na true, a fiksne vrijednosti se navode kao elementi
tipa ListItem s atributima Text31 i Value

Primjer:  Web \ Firma.WebForms \ DokumentStavka.aspx

<asp:DropDownList ID="ddlPartneri" runat="server"


SelectedValue='<%# Bind("IdPartnera") %>'
DataSourceID="PartnerDataSource"
DataTextField="Text"
DataValueField="Key" />

<asp:DropDownList ID="ddlPrethodniDokument" runat="server"


AppendDataBoundItems="True"
SelectedValue='<%# Bind("IdPrethDokumenta") %>'
DataSourceID="PrethodniDokumentDataSource"
DataTextField="Text"
DataValueField="Key">
<asp:ListItem Value="">Nema prethodnog dokumenta</asp:ListItem>
</asp:DropDownList>

13.9.6 Dodavanje novog dokumenta


Kontrola FormView se prebacuje u način rada za ažuriranje (engl. insert mode) klikom na gumb vezan
uz naredbu New i vraća se u “normalni” način prikaza klikom na gumb koji je vezan uz naredbu
Cancel.
Predložak za ažuriranje dizajnira se unutar oznake InsertTemplate unutar kontrole FormView.
Predložak je gotovo identičan predlošku za ažuriranje, a na dnu predloška je dodan gumb za potvrdu
unosa (naredba Insert) i gumb za odustajanje (naredba Cancel).
Za sve kontrole za unos potrebno je dodati validacijske kontrole i na njima postaviti svojstvo
ValidationGroup na istu vrijednost kao i kod gumba za spremanje promjena.

31
Umjesto atributa Text može se navesti tekst između otvorene i zatvorene oznake ListItem
197
Primjer:  Web \ Firma.WebForms \ DokumentStavka.aspx

<asp:TextBox ID="tbPostoPorez" runat="server"


Text='<%# Bind("PostoPorez") %>' />

<asp:RequiredFieldValidator ID="RequiredFieldValidatorPorez" runat="server"


ControlToValidate="tbPostoPorez" ErrorMessage="*"
ValidationGroup="Dokument"/>

<asp:Button ID="InsertButton" runat="server" CausesValidation="True"


CommandName="Insert" Text="Spremi"
ValidationGroup="Dokument" />

<asp:Button ID="InsertCancelButton" runat="server"


CausesValidation="False" CommandName="Cancel" Text="Odustani" />

Gumb s naredbom Insert aktivira postupak pridružen svojstvo pridružen InsertMethod na izvoru
podataka nakon čega se vrijednosti povezanih podataka šalju pridruženom postupku.

Primjer:  Web \ Firma.WebForms \ DokumentStavka.aspx

<asp:ObjectDataSource ID="DokumentDataSource" …
TypeName="DokumentController" InsertMethod="InsertDokument"

Primjer:  Web \ Firma.WebForms \ DataControllers \ DokumentController.cs

public class DokumentController {


public void InsertDokument(string VrDokumenta,int BrDokumenta,
string DatDokumenta, int IdPartnera,
string PostoPorez,int? IdPrethDokumenta) {
Dokument dokument = new Dokument();
dokument.VrDokumenta = VrDokumenta;
dokument.BrDokumenta = BrDokumenta;
dokument.DatDokumenta = DateTime.ParseExact(
DatDokumenta, "dd.MM.yyyy", null);
...
DokumentBllProvider bll = new DokumentBllProvider();
bll.Save(dokument);

Specifičnost kod dodavanja je potreba skrivanja stavki u načinu za dodavanje novog elementa, jer
novi element nema stavke, pa bi eventualno bile vidljive stavke prethodno prikaznog dokumenta.
Rješenje problema je obrada događaja ItemCreated na kontroli FormView. Događaj se pojavljuje
nakon što se kontrole iz predloška stvore, a prije povezivanja podataka. S obzirom da događaj
ItemCreated nije nužno vezan samo za dodavanje, mora se provjeriti koji je način prikaza trenutno
aktivan.
Osim skrivanja stavki kod dodavanja, potrebno je obratiti pažnju i na situaciju u kojoj je kontrola
FormView u način prikaza podataka, ali ne postoji niti jedan dokument, pa je kontrolu FormView
potrebno odmah prebaciti u način za dodavanje. Svaka kontrola iz predloška se može dohvatiti
postupkom FindControl, npr. FormViewDokument.FindControl("NazivKontrole").
Dodatno, prije početka dodavanja postavljaju se inicijalne vrijednosti za datum i broj dokumenta.

198
Primjer:  Web \ Firma.WebForms \ DokumentStavka.aspx

protected void FormViewDokument_ItemCreated(object sender, ...){


FormView form = (FormView)sender;
if (form.CurrentMode == FormViewMode.Insert)
{
TextBox tbDatum =
(TextBox) FormViewDokument.FindControl("tbDatDokumenta");
... //postavi inicijalne vrijednosti
GridViewStavke.Visible = false;
}
else if (form.CurrentMode == FormViewMode.ReadOnly
&& Form.DataItemCount == 0)
{
form.ChangeMode(FormViewMode.Insert);
GridViewStavke.Visible = false;
}

13.9.7 Brisanje dokumenta


Unutar predloška za prikaz (ItemTemplate) definiran je gumb s akcijom brisanja. Klikom na gumb
poziva se postupak pridružen brisanju kojem se prosljeđuju vrijednosti primarnog ključa
(IdDokumenta u ovom primjeru)

Primjer:  Web \ Firma.WebForms \ DokumentStavka.aspx

<asp:Button ID="DeleteButton" ... CausesValidation="False"


CommandName="Delete" Text="Obriši" .../>

<asp:FormView ID="FormViewDokument" DataKeyNames="IdDokumenta"


...
<asp:ObjectDataSource ID="DokumentDataSource" …
TypeName="DokumentController" DeleteMethod="ObrisiDokument"

Primjer:  Web \ Firma.WebForms \ DataControllers \ DokumentController.cs

public class DokumentController {


public void ObrisiDokument(int IdDokumenta){
var bll = new DokumentBllProvider();
var dokument = bll.Fetch(IdDokumenta);
dokument.Delete();
bll.Save(dokument);
}

Nakon brisanja potrebno je ponovno aktivirati povezivanje za FormView kako bi se automatski


dogodilo povezivanje vezanih podataka (stavke dokumenta).
Također, treba obratiti pažnju na nekoliko specifičnih situacija vezanih za broj stranice. Ponašanje
forme pri brisanju je takvo da broj stranice ostaje isti osim ako je došlo do brisanja podatka s
predzadnje stranice. U tom slučaju broj stranice se umanji za jedan32. Dodatno, ako se briše podatak s
posljednje stranice, potrebno je smanjiti broj stranice. Sve navedeno se postavlja prilikom obrade
događaja ItemDeleted na kontroli FormView.

32
Navedeno ponašanje nije dokumentirano, već se pokazuje u praksi
199
Primjer:  Web \ Firma.WebForms \ DokumentStavka.aspx.cs

protected void FormViewDokument_ItemDeleted(object sender,


FormViewDeletedEventArgs e){
FormView form = (FormView)sender;
if (form.PageIndex == form.PageCount - 1) {
--form.PageIndex;
}
FormViewDokument.DataBind();
}

13.9.8 Prikaz stavki dokumenta

Slika 96. Primjer postavljanja svojstava izvora podataka za stavke

Stavke se prikazuju unutar GridViewa i povezivanjem na izvor podataka tipa ObjectDataSource.


Razred DataController\DokumentController.cs vrši dohvat koristeći Firma.BLL.dll iz primjera
Firma.Win. Dohvat je parametriziran trenutno prikazanim dokumentom, tako da se ne prikazuju sve
stavke, nego samo stavke trenutnog dokumenta.

Slika 97. Parametrizacija izvora podataka za stavke vrijednošću primarnog ključa prikazanog dokumenta

200
Slika 97 prikazuje način postavljanja parametra kroz dizajn tako da se iz trenutnog dokumenta u
FormViewu uzme primarni ključ (svojstvo SelectedValue) i pridruži parametru upita. Postupak za
dohvat mora primati navedene parametre. Postavljanje parametara može se postaviti kroz dizajn ili
ručnim dopisivanjem unutar kontrole ObjectDataSource i elementa SelectParameters.

Primjer:  Web \ Firma.WebForms \ DokumentStavka.aspx

<asp:ObjectDataSource ID="StavkeDataSource" runat="server"


TypeName="DokumentController"
SelectMethod="DohvatiStavkeDokumenta"
DeleteMethod="ObrisiStavku" UpdateMethod="AzurirajStavku" >
<SelectParameters>
<asp:ControlParameter ControlID="FormViewDokument"
Name="IdDokumenta" PropertyName="SelectedValue" />
</SelectParameters>
</asp:ObjectDataSource>

13.9.9 Oblikovanje pojedinog stupca u mreži sa stavkama i dodavanje nove stavke


Svako polje (stupac) u mreži sa stavkama izvedeno je kao TemplateField pri čemu postoje tri vrste
predložaka:
 predložak za prikaz (ItemTemplate),
 predložak za ažuriranje (EditItemTemplate) i
 predložak za podnožje (FooterTemplate) kojim se omogućava dodavanje novog podatka.
Za povezivanje se koriste ista pravila kao i kod FormViewa (Eval, Bind).
Predložak za situaciju u kojoj nema podataka u mreži naziva se EmptyDataTemplate i dizajnira se za
cijeli GridView, ne za pojedini stupac. U pojedinom predlošku za stavke potrebno je koristiti
validacijske kontrole s drugačijom vrijednošću svojstva ValidationGroup radi izbjegavanja kolizije s
validacijom pri ažuriranju dokumenta.
U konkretnom primjeru Dokument-Stavka obrađuju se sljedeći događaji:
 RowDeleted i RowUpdated – potrebno da se ažurira cijena dokumenta
 RowUpdating – potrebno da se dohvati cijena odabranog artikla i prekopira u konkretnu
stavku
 RowCommand – potrebno da se obrade vlastite naredbe koje nisu iz standardnog skupa
naredbi (Edit, Update, Cancel, Delete, New). Dvije vlastite naredbe (Add, AddForEmpty) su
upotrijebljene za dodavanje novog podatka iz podnožja te iz predloška koji se prikazuje kad
nema podataka u mreži.
Ako se dodavanje odvija iz podnožja mreže tada se dohvat pojedine kontrole vrši postupkom
FindControl na retku podnožja:

Primjer:  Web \ Firma.WebForms \ DokumentStavka.aspx.cs

GridViewStavke.FooterRow.FindControl("ddlSifArtikla");

Ako se stavka dodaje iz predloška za praznu mrežu tada se koristi specifičan način dohvata pojedine
kontrole:

201
Primjer:  Web \ Firma.WebForms \ DokumentStavka.aspx.cs

GridViewStavke.Controls[0].Controls[0].FindControl("ddlSifArtikla");

Stvorenu stavku potrebno je nakon toga dodati u konkretni dokument i snimiti promjene.

Primjer:  Web \ Firma.WebForms \ DokumentStavka.aspx.cs

protected void GridViewStavke_RowCommand (... ) {


int IdDokumenta = (int)FormViewDokument.SelectedValue;
var bll = new DokumentBllProvider();
Dokument dokument = bll.Fetch(IdDokumenta);
...
Stavka stavka = new Stavka(){ SifArtikla = artikl.SifArtikla, ... };
...
dokument.Edit();
dokument.Stavke.Add(stavka);
bll.Save(dokument);

Nakon dodavanja potrebno je pozvati DataBind na GridView, a zatim i na FormView.

Primjer:  Web \ Firma.WebForms \ DokumentStavka.aspx.cs

GridViewStavke.DataBind();
FormViewDokument.DataBind();

13.10 Zadaci
Zadatak 1. Koje su osnovne funkcije sustava?
Zadatak 2. Koje funkcije obavlja poslužitelj u dvoslojnoj arhitekturi klijent-poslužitelj?
Zadatak 3. Koje su prednosti i nedostatci debelog odnosno tankog klijenta?

202
14 ASP.NET MVC
ASP.NET MVC (Model-View-Controller) je ASP.NET realizacija općenitog arhitekturnog obrasca MVC,
koja omogućuje detaljnu razradu prezentacijskog sloja u 3 komponente: model, pogled i upravljač.
Slika 98 prikazuje ovisnosti između ovih komponenti.

Slika 98. Odnos ovisnosti između modela, pogleda i upravljača

Pogled definira izgled korisničkog sučelja i ovisi samo o modelu, tj. određuje izgled prikaza nekog
objekta modela.
Upravljač predstavlja prezentacijsku (aplikacijsku) logiku. Prima ulaz iz pogleda, obrađuje ga, puni i/ili
dohvaća model, poziva niže slojeve i određuje redoslijed prikaza pogleda.

Slika 99. Razlika između poslovnog i prezetacijskog modela

U jednostavnim aplikacijama isti model može objedinjavati i poslovnu logiku i sloj pristupa podacima
i ujedno biti korišten u pogledima. Međutim, u složenijim se aplikacijama kao model koristi „pravi“
poslovni model, a eventualni model unutar projekta služi za definiranje pomoćnih modela za lakši
prikaz podataka (npr. agregirane podatke iz poslovnog modela) što predstavlja „prezentacijski
model“, a ne model u smislu poslovnog objekta. Odvajanjem poslovnog modela u zasebni projekt
naglašava se neovisnost modela i ujedno sprječava postojanje ovisnoti modela o implementaciji
korisničkog sučelja koje bi koristilo taj model.

14.1 Usporedba ASP.NET web formi s MVC-om


ASP.NET forme imaju nekoliko značajnih nedostataka u pogledu današnjih tehnologija za izradu web
aplikacija kao što su
 komplicirani životni ciklus stranice (obrada događaja slijedi uvijek nakon Page_Load u kojem
je potrebno paziti je li zahtjev Postback ili ne)
 korištenje polja ViewState za rekonstrukciju stanja kontrola ASP.NET što proizvodi veliku
količinu (promet) podataka pri svakom zahtjevu

203
 korištenje ugrađenih kontrola ograničava kontrolu nad HTML-om koji nastaje pretvorbu tih
kontrola u HTML
 slaba mogućnost testiranja, jer dolazi do čestog miješanja prezentacijske i aplikacijske logike
što rezultira i prevelikim *.cs datotekama.
ASP.NET MVC ove probleme rješava objedinjujući iskustva MVC implementacija u drugim jezicima.
Sam MVC kao koncept nastavo je u kasnim sedamdesetim godinama prošlog stoljeća kao Smalltalk
projekt unutar Xeroxa.
Prednost MVC-a nad web formama očituje se u smanjenju složenosti podjelom aplikacije u model,
pogled i upravljač, a što omogućava razvoj vođen testiranjem (engl. test-driven development).
Odvajanje pogleda predstavlja dobar radni okvir za veće razvojne timove i dizajnere, jer izgled
aplikacije nije isprepleten kodom. MVC ne koristi ViewState ni serverske kontrole čime je omogućena
potpuna kontrola ponašanja aplikacije. Svi zahtjevi su centralizirani na jedan upravljač, što se
označava pojmom Front Controller pattern, uz bogatu podršku za intepretiranje i usmjeravanje
zahtjeva.
Prednost web formi nad MVC-om dolazi do izražaja u situacijama kad je veći naglasak na održavanju
stanja web aplikacija, a količina podataka nije od presudne važnosti (primjerice kod intranet
aplikacija). Dodatno web forme podržavanju velik broj serverskih kontrola i uobičajenih događaja
nalik Windows formama, što (početni) razvoj čini lakšim zbog manje količine koda i može biti
prikladno kod manjih projekata kod kojih se zahtijeva jednostavan, brz i jeftin razvoj. Web forme
koriste tzv. Page Controller pattern u kojem je funkcionalnost vezana uz pojedinu stranicu.

14.2 Struktura ASP.NET MVC aplikacije


Uobičajene mape i datoteke unutar ASP.NET MVC aplikacije su:

 Controllers: mapa sadrži upravljače unutar aplikacije


 Views: pogledi podijeljeni u podmape za svaki upravljač i Shared za zajedničke poglede (npr.
glavna stranica)
 Models: razred koji predstavljaju pomoćne (prezentacijske modele) (ili eventualno domenske
modele u slučaju manje aplikacije)
 Content: statički sadržaj (css, teme…)
 Scripts: javascript biblioteke (ova mapa je u novijih verzijama uobičajeno smještena ispod
mape Content)
 App_Start: razredi za postavljanje glavnih postavki aplikacije poput usmjeravanja, kreiranja
kombiniranih paketa bilioteka (engl. bundle), sigurnosnim postavki, …
Osim navedenih moguće je imati i druge specifične mape za MVC (npr. Areas, App_Data, …). Unutar
projekta može se nalaziti više konfiguracijskih datoteka oblika web.config, pri čemu one ispod glavne
mape primarno služe da bi web server onemogućio posluživanje određenih vrsta datoteka.
Specifično za primjer FirmaMVC dodana je mapa DLL koja sadrži datoteke iz primjera s višeslojnom
aplikacijom za poslovni (unutar kojeg su objekti poslovnog modela) i podatkovni sloj te biblioteku
Firma.Framework. Navedene datoteke referencirane su unutar projekta FirmaMvc, a smještaj u
posebnoj mapi unutatr projekta je bio potreban kako bi se na bilo kojem računalu održala relativna
putanja referenciranih biblioteka. Dodatno u mapi HtmlHelpers nalaze se pomoćni razredi za
straničenje.
204
Slika 100. Struktura projekta FirmaMvc

14.3 Inicijalne postavke MVC aplikacije


Pri prvom pokrenutanju aplikacija poziva se postupak Application_Start iz datoteke Global.asax.cs. U
postupku se inicijaliziraju dijelovi aplikacije (područja, engl. areas), filtri, usmjeravanja (rute),
kombinirani paketi (grupira se više skriptnih datoteka i/ili više datoteka sa stilovima) i postavke
vezane za razne metode provjere autentičnosti (ako se koriste unutar aplikacije).
Sljedeći primjer ilustrira takvu inicijalizaciju, pri čemu se razredi pozvanih metoda obično nalaze u
mapi App_Start.

205
Primjer:  FirmaMVC \ Global.asax.cs

namespace FirmaMvc {
public class MvcApplication : System.Web.HttpApplication {
protected void Application_Start() {
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(
GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
...

14.3.1 Paketi datoteka


Više datoteka određenog stila pakira se u jednu datoteku u cilju poboljšanja performansi i lakšeg
održavanja pri promjeni verzija skripti, primjerice prilikom promjene verzije jQuery-a korištenjem
paketa neće biti potrebno mijenjati kod u pojedinačnim stranicama, jer se paket automatski stvara s
onom verzijom koja je dodana u projekt.

Primjer:  FirmaMVC \ BundleConfig.cs

public class BundleConfig {


public static void RegisterBundles... {
bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
"~/Scripts/jquery-{version}.js"));

bundles.Add(new StyleBundle("~/Content/css").Include(
...,
"~/Content/Site.css"));
}

Prilikom korištenjem paketa unutar nekog pogleda navodi se ime paketa kako je definirano prilikom
kreiranja, primjerice za gornje stvaranje paketa primjer poziva bi mogao biti sljedeći
@Styles.Render("~/Content/css")
@Scripts.Render("~/bundles/jquery")

14.4 Način rada MVC aplikacije


14.4.1 Usmjeravanje zahtjeva
Korisnik od MVC aplikacije traži akciju upravljača, nakon čega aplikacija (tj. pozvani upravljač)
popunjava model i vraća ažurirani pogleda korisniku. Koju akciju i koji upravljač odabrati određuje se
na osnovu URL-a i definiranih usmjeravanja koja se obično postavljaju u datoteci
App_Start\RouteConfig.cs. Svaka od postavki usmjeravanja ima svoj naziv, oblik adrese te
pretpostavljene vrijednosti u slučaju da neki dio adrese nije naveden u zahtjevu.
Uobičajeno se kao zadnja postavka usmjeravanja navodi uobičajena kombinacija formiranja zahtjeva
za akcijom na nekom upravljaču oblika http://.../NazivUpravljača/Akcija/OpcionalniParametri
(usmjeravanja Default iz prethodnog primjera). Za traženi naziv upravljača mora postojati razred
NazivController.cs u mapi Controllers. Za traženu akcijom na tom upravljaču mora postojati postupak
s potrebnim parametrima naziva jednakog traženoj akciji, pri čemu je moguće definirati više
istoimenih akcija za različite varijante zahtjeva (npr. GET i POST).

206
Primjer:  FirmaMVC \ RouteConfig.cs

public class RouteConfig {


public static void RegisterRoutes(RouteCollection routes) {
routes.MapRoute( null, "Artikl/Page{page}",
new { Controller = "Artikl", action = "Index" }
);
routes.MapRoute( null, "Mjesto/Page{page}",
new { Controller = "Mjesto", action = "Index" }
);

routes.MapRoute( "Default", {controller}/{action}/{id}",


new { controller = "Home", action = "Index",
id = UrlParameter.Optional }
);
...

Određivanje usmjeravanja određuje se prema postavkama u datoteci RouteConfig tako da se traži


prvo pravilo usmjeravanja koje može zadovoljiti URL zahtjeva.
Neki od primjera usmjeravanja u primjeru FirmaMVC su sljedeći:
 http://.../Artikl/Edit/3 → na upravljaču Artikl(Controller) poziva se postupak Edit koji za
parametar id prima vrijednost 3. (sukladno zadnjem pravilu)
 http://.../Artikl/Page10 → na upravljaču Artikl(Controller) poziva se pretpostavljeni postupak
Index koji za parametar page prima vrijednost 10. (prvo pravilo)
 http://.../Dokument/Details?page=7 → na upravljaču Dokument(Controller) poziva se
postupak Details koji za parametar page prima vrijednost 7. (treće pravilo)

14.4.2 Uobičajeni rezultati akcije upravljači


U sljedećoj tablici prikazani su tipovi rezultata koje mogu biti navedeni kao rezultat neke akcije u
upravljaču, njihovi kratki opisi i postupak/razred kojim se iz upravljača vraća taj tip rezultata. Svi
tipovi podataka izvedeni su iz ActionResult, pa se on uvijek može postaviti kao povratna vrijednost
akcije (postupka u upravljaču), a stvarni povratni tip može biti neki od izvedenih razreda.

Tip Opis rezultata/povratne vrijednosti Postupak u upravljaču

ViewResult Prikazuje pogled View


PartialViewResult Prikazuje parcijalni pogled PartialView
RedirectToRouteResult Privremeno ili trajno preusmjerava RedirectToAction
zahtjev (HTTP kod 301 ili 302), stvarajući RedirectToActionPermanent
URL na osnovu postavki usmjeravanja RedirectToRoute
RedirectToRoutePermanent
RedirectResult Privremeno ili trajno preusmjerava Redirect
rezultat na određeni URL RedirectPermanent
ContentResult Vraća tekstualni sadržaj Content
FileResult Vraća binarni sadržaj File
JsonResult Vraća objekt serijaliziran u JSON format Json
JavaScriptResult Vraća JavaScript odsječak JavaScript

207
HttpUnauthorizedResult Vraća HTTP kod 401 -
HttpNotFoundResult Vraća HTTP kod 404 HttpNotFound
HttpStatusCodeResult Vraća određeni HTTP kod -
EmptyResult Bez povratne vrijednosti -

14.4.3 Glavna stranica


Glavna stranica predstavlja zajednički okvir za sve poglede i uključuje stilske i javascript datoteke,
definira navigaciju, prostor za javascript pojedinog pogleda, jedan ili više okvira koji pogledi mogu
naknadno puniti itd.
Svaki pogled po potrebi može definirati svoju glavnu stranicu promjenom vrijednosti svojstva Layout
ili je uopće ne koristiti postavljanjem vrijednost Layout na null. Ako se unutar pojedinog pogleda ne
postavi vrijednost za Layout koristi se pretpostavljena glavna stranica. Pretpostavljena glavna stranica
za svaki pogled definirana je u datoteci Views \ _ViewStart.cshtml.

Primjer:  FirmaMVC \ Views \ _ViewStart.cshtml

@{
Layout = "~/Views/Shared/_Layout.cshtml";
}

Sadržaj pogleda koji koristi glavnu stranicu dinamički će se ugraditi u sadržaj glavne stranice na
mejsto na kojem se nalazi naredba RenderBody. Glavna stranica za primjer FirmaMVC izgleda kao u
sljedećem odsječku

208
Primjer:  FirmaMVC \ Views \ _ViewStart.cshtml

<!DOCTYPE html>
<html lang="hr_HR" xml:lang="hr_HR"…>
<head>
<title>@ViewBag.Title</title>
@Styles.Render("~/Content/css")
@Scripts.Render("~/bundles/jquery")
</head>
<body>
<div id="header">
<div class="title">RPPP - Firma.MVC</div>
</div>
<div id="navigation">
@{Html.RenderAction("Navigation", "Home");}33
</div>
<div id="content">@RenderBody()</div>
@RenderSection("scripts", false)
<script src="@Url.Content("~/Scripts/firma.js")"
type="text/javascript"></script>
</body>
</html>

14.4.4 Sintaksa pogleda


Ako drugačije nije navedeno koristi se pogled čije ime odgovara pozvanoj akciji, a nalazi se u mapi
Views \ Pozvani upravljač. Pogled ima ekstenziju cshml i predstavlja mješavinu HTML i MVC koda.
U pogledu se obično koristi jednostavni kod (npr. petlja, grananje i sl) vezan uz prikaz. MVC kod
počinje oznakom @ iza kojeg slijedi naredba ili blok naredbi unutar vitičastih zagrada i html oznake
pri čemu se za komentare koji se ne smiju u konačnici naći u generiranom html-u koriste parovi
znakova @* i *@.
Početni redak pogleda (opcionalno) sadrži podatak koji se model koristi i to se navodi s @model naziv
razreda koji se koristi za model. Konkretne vrijednosti predanog modela dobije se s @Model.
Naslov stranice postavlja se kroz ViewBag.Title što se automatski proslijedi glavnoj stranici.

Primjer:  FirmaMVC \ Views \ Mjesto \ Index.cshtml

@using FirmaMvc.HtmlHelpers
@model FirmaMvc.Models.MjestoListViewModel

@{
ViewBag.Title = "Poštanski brojevi";
}

33
U ovom retku između znaka @ i konkretne naredbe se nalaze vitičaste zagrade jer RenderAction ne vraća
string koji bi se ugradio u stranicu, već RenderAction direktno piše na izlaz. Umjesto ovoga bi moglo npr.
@Html.Action("Navigation", "Home"),
209
Tekst koji treba biti u konačnom HTML-u, a nije već dio neke HTML oznake (engl. tag) prefiksira se s
@: ili se stavlja oznaka <text>. Vlastiti prostori imena uključuju se u pojedinom pogledu s @using.
Alternativno mogu se uključiti za sve poglede u web.configu.

14.4.5 Parcijalni pogled


Parcijalni pogled nema html zaglavlje i ne koristi glavnu stranicu. Ima ostatak sadržaja jednakog
oblika kao „obični“ pogled. Parcijalni pogled predstavlja dio sadržaja koji treba ubaciti unutar nekog
drugog pogleda. Parcijalni pogled se u drugi pogled uključiti korištenjem proširenja razreda
HtmlHelper postupcima RenderPartial, Partial, RenderAction i Action, koji se koriste nad svojstvom
Html (tipa HtmlHelper) a koje je definirano za svaki pogled.
Postupak Html.RenderPartial izvršava akciju (na određenom upravljaču) koja za prikaz rezultata
koristi parcijalni pogled. Na upravljaču je umjesto return View potrebno koristiti return PartialView.
Postupku RenderPartial Može se predati model nad kojim parcijalni pogled radi, a ispis parcijalnog
pogleda ide direktno na izlazni tok.
Postupak Html.Partial radi slično kao Html.RenderPartial, ali ne koristi direktno pisanje na izlazni tok,
nego vraća string.
Html.RenderAction i Html.Action rade na sličnom principu i u postojeći pogled uključuju rezultat
poziva neke akcije. Razlika u odnosu na RenderPartial i Partial je ta da se pozivom RenderAction ili
Action formira novi zahtjev te se ne predaje model prilikom poziva.
Ilustracija korištenja parcijalnog pogleda može se vidjeti na primjeru za navigaciju. Pogled je pozvan iz
glavne stranice te se traži akcija Navigation.

Primjer:  FirmaMVC \ Controllers \ HomeController.cs

public ActionResult Navigation()


{
return PartialView();
}

Poveznice (engl. hyperlink) se stvaraju postupkom Html.ActionLink čime se automatski formira


konkretni URL na osnovu postavki usmjeravanja. Za postupak ActionLink postoji nekoliko
preopterećenih metoda za unos teksta poveznice, naziva akcije, naziva upravljača, vrijednosti
parametara i HTML izgleda (stila) same poveznice. Ako nije prva naredba u bloku započetkom s @
poziv je potrebno prefiksirati s @. U sljedećem primjeru prikazan je odsječak iz parcijalnog pogleda za
kreiranje poveznica za navigaciju kroz aplikaciju.

Primjer:  FirmaMVC \ Views \ Home \ Navigation.cshtml

<li>@Html.ActionLink("Početna stranica", "Index", "Home")</li>

<br /> Mjesta


<li>@Html.ActionLink("Popis mjesta", "Index", "Mjesto",
new { page = 1 }, null) </li>
<li>@Html.ActionLink("Unos novog mjesta", "Create", "Mjesto")</li>

14.5 Primjer prikaza mjesta


Za prikaz svih mjesta s poštanskim brojevima koristi se upravljač Mjesto(Controller) i akcija Index. S
obzirom na postavke rute, adresa koju je potrebno otvoriti u pregledniku je oblika …/Mjesto/index ili

210
samo …/Mjesto, jer je Index pretpostavljena akcija. Opcionalno se predaje broj stranice i redoslijed
sortiranja, pa primjer takvog poziva može biti npr. /Mjesto/Page27?sort=4.
Model za prikaz mjesta sadrži popis mjesta (dovoljno je da je IEnumerable) i podatke o trenutnoj
stranici, ukupnom broju stranica i redoslijedu sortiranja (svojstva u razredu PagingInfo). Za mjesto se
koristi poslovni objekt razreda Mjesto iz Firma.BLL.

Primjer:  FirmaMVC \ Models \ MjestoListViewModel.cs

public class MjestoListViewModel {


public IEnumerable<Mjesto> MjestoList { get; set; }
public PagingInfo PagingInfo { get; set; }
}

14.5.1 Akcija dohvata i pregleda svih mjesta


Upravljač dohvaća podatke i puni model nakon čega izazove prikaz pogleda istog imena s popunjenim
modelom, što je prikazano u sljedećem primjeru. Svaki upravljač nasljeđuje razred Controller.

Primjer:  FirmaMVC \ Controllers \ MjestoController.cs

public class MjestoController : Controller {


...
public ActionResult Index(int page = 1, int sort = 1){
MjestoBllProvider bll = new MjestoBllProvider();
BusinessBaseList<Mjesto> mjesta = bll.FetchAll();
PagingInfo pagingInfo = new PagingInfo{
...
TotalItems = mjesta.Count,
};
IEnumerable<Mjesto> list = mjesta.OrderBy(m => m.NazMjesta);
list = list.Skip((page - 1) * pageSize).Take(pageSize);
MjestoListViewModel model = new MjestoListViewModel{
PagingInfo = pagingInfo,
MjestoList = list
};
return View(model);
}

Za svako mjesto definiran je jedan redak tablice s podacima o mjestu. Izgled retka definiran je
korištenjem css klasa iz datoteke Content/Site.css. Vrijednost modela može se dohvatiti preko
svojstva Model koju svaki pogled ima.
Kako bi se omogućilo sortiranje i straničenje zaglavlje tablice s podacima sadrži poveznice na akciju
Index na trenutnom upravljaču s vrijednostima za broj stranice i redoslijed sortiranja. Poveznice koje
koriste GET zahtjev stvaraju se postupkom Html.ActionLink.

211
Primjer:  FirmaMVC \ Views \ Mjesto \ Index.cshtml

@model FirmaMvc.Models.MjestoListViewModel
...
<table class="zebra">
<thead>
<tr>
<th>@Html.ActionLink("Poštanski broj", "Index",
new { sort = 1, page = Model.PagingInfo.CurrentPage})</th>
<th>@Html.ActionLink("Naziv mjesta", "Index",
new { sort = 2, page = Model.PagingInfo.CurrentPage})</th>
...
...
@foreach (var item in Model.MjestoList)
{
<tr>
<td>@item.PostBrMjesta</td>
<td>@item.NazMjesta</td>
<td>@item.PostNazMjesta</td>
<td>@item.OznDrzave</td>
...
<td>@Html.ActionLink("Ažuriraj", "Edit",
new { id = item.IdMjesta})</td>
...
}
...
</table>

Poveznice za ažuriranje mjesta također se stvaraju korištenjem postupka Html.ActionLink tako da


ažuriranje vodi na akciju Edit u upravljaču Mjesto. Postupak Edit u MjestoController očekuje id kao
identifikator mjesta, a id se postavlja na konkretni IdMjesta prilikom stvaranja poveznice.

14.5.2 Ažuriranje podataka


Za ažuriranje zadužen je postupak (akcija) Edit u upravljaču Mjesto pri čemu je potrebno razlikovati
inicijalno otvaranje forme (GET zahtjev), od naknadnog slanja podataka (POST zahtjev). To se
programski rješava s dva postupka Edit (različitih argumenata) s odgovarajućim atributima HttpGet i
HttpPost.

Primjer:  FirmaMVC \ Controllers \ MjestoController.cs

public class MjestoController : Controller {

[HttpGet]
public ActionResult Edit(int id) {

}

[HttpPost]
public ActionResult Edit(Mjesto mjesto) {

}

MVC sadrži skup postupaka (Html.EditorFor, Html.TextBoxFor, Html.HiddenFor, Html.CheckBoxFor, …)


koji stvaraju odgovarajuće HTML elemente za pojedino svojstvo modela. Postupak Html.EditorFor

212
sam procjenjuje koju HTML kontrolu napraviti na osnovu tipa svojstva. Html.HiddenFor služi za
vrijednosti koje treba sačuvati, a ne smiju se mijenjati i korisnik ih ne vidi na ekranu. Ime html
kontrole u generiranom HTML-u određeno je imenom svojstva.

Primjerice, odsječak

Primjer:  FirmaMVC \ Views \ Mjesto \ Edit.cshtml

@using (Html.BeginForm()) {

@Html.HiddenFor(m => m.IdMjesta)
@Html.EditorFor(m => m.PostBrMjesta)

stvara HTML kao u sljedećem odsječku

<form action="/Mjesto/Edit/6701" method="post">


<input … id="IdMjesta" name="IdMjesta" type="hidden" value="6701" />
<input class="text-box single-line"… id="PostBrMjesta" name="PostBrMjesta"
type="text" value="10000" />

Alternativno, mogu se koristiti varijante navedenih postupaka bez sufiksa For pri čemu je potrebno
navesti željeno ime za kontrolu u generiranom HTML-u.

14.5.3 Prijenos dodatnih vrijednosti pogledu


Osim samog modela ponekad je potrebno prenijeti neke druge vrijednosti, primjerice poruke o
pogrešci ili izbor vrijednosti za padajuću listu država. Mogu se koristiti svojstva upravljača ViewData
ili ViewBag. ViewData (tipa ViewDataDictionary) sadrži parove ključ (string), vrijednost (objekt).
ViewBag (tipa dynamic) može sadržavati bilo koje svojstvo (nema sintaksne provjere prilikom
kompilacije) i interno se svodi na kolekciju parova ključ-vrijednost.

Primjer:  FirmaMVC \ Controllers \ MjestoController.cs

public ActionResult Edit(int id) {


MjestoBllProvider bll = new MjestoBllProvider();
Mjesto mjesto = bll.Fetch(id);

DrzavaBllProvider bllDrzava = new DrzavaBllProvider();


DrzavaList drzave = bllDrzava.FetchAll();
ViewBag.Drzave = drzave;
return View(mjesto);
}

Prilikom korištenja svojstva ViewBag i ViewData nema sintaksne promjene te je potrebno obaviti
ukalupljivanje (engl. cast) u (autoru) poznati tip. U prethodnom primjeru u upravljaču je unutar
ViewBaga dostavljen popis država (tip podatka DrzavaList). U pogledu je potrebno stvoriti padajuću
listu na osnovu država koje su predane pogledu i postaviti da u padajućoj lisit bude odabrana država
kojoj prikazano mjesto pripada. Za navedeni zadatak koristi se jedna od inačica postupka
Html.DropDownListFor i lambda izraz za svojstvo koje se želi povezati. Za elemente liste koristi se
kolekcija SelectListItem.

213
Primjer:  FirmaMVC \ Views \ Mjesto \ Edit.cshtml

@Html.DropDownListFor(m => m.OznDrzave,


((Firma.BusinessEntities.DrzavaList) ViewBag.Drzave).
OrderBy(d => d.NazDrzave).
Select(d =>
new SelectListItem{
Text = d.NazDrzave,
Value = d.OznDrzave
}
)
)

14.5.4 Prihvat podataka


Prilikom poziva akcije, na upravljaču se poziva istoimeni postupak. Postupak može primiti cijeli
model, objekt tipa FormCollection koji čine parovi stringova s nazivom elementa forme i
vrijednostima elemente ili može primiti bilo koji broj imenovanih parametara kojima se pokušava
pridijeliti vrijednost.
Redoslijed kojim se pokušava napraviti povezivanja je sljedeći:
1. Request.Form – prvo se pokušava pronaći kontrolama u HTML-u čije je svojstvo name
jednako argumenti postupka.
2. RouteData.Values – ako argument nije inicijaliziran u prethodnom koraku, provjerava se
postoji li u postavkama usmjeravanja odsječak koji odgovara imenu argumenta
3. Request.QueryString – ako argument nije u prethodnim koracima inicijaliziran argument se
traži u dijelu URL-a iza znaka ? (tzv. query string)
4. Request.Files - ako argument nije u prethodnim koracima inicijaliziran provjerava se postoji li
među primljenim datotekama neka koja je odabrana za slanje u kontroli imena jednakog
imenu argumenta.

14.5.5 Validacija
Za prikaz validacijskih pogrešaka unutar pogleda koriste se Html.ValidationSummary za pogreške na
razini modela i Html.ValidationMessageFor za pogreške na razini svojstva. Validacija se može
provoditi na jedan od načina:
 automatski na osnovu metapodataka modela ako model ima definirane atribute iz prostora
imena System.ComponentModel.DataAnnotations
 eksplicitnim postavljanjem pogrešaka modela pozivom postupka ModelState.AddModelError
pri čemu se kao argumenti predaju naziv svojstva i opis validacijske pogreške, a svojstvo
ModelState (tipa ModelStateDictionary) je svojstvo koje ima svaki upravljač
MVC u slučaju validacijske pogreške postavlja odgovarajuću css klasu na html element vezan uz
svojstvo s pogreškom, a nazivi css stilova za validacijske pogreške su input-validation-error, field-
validation-error i validation-summary-errors.
U primjeru Firma.MVC validacija se radi u poslovnom sloju, a za prikazan unutar web aplikacije koristi
se eksplicitno postavljanje pogreške pozivom postupka ModelState.AddModelError. S obzirom da je
način validacije isti za sve poslovne objekte kreirana je metoda s 2 argumenta, svojstvom ModelState

214
i poslovnim objektom pri čemu je metoda izvedena kao proširenje (engl. extension) klase
ModelStateDictionary.

Primjer:  FirmaMVC \ Extensions.cs

public static class Extensions {


public static void ValidateBusinessObject(
this ModelStateDictionary ModelState, BusinessBase businessObject)
{
businessObject.Validate();
foreach (var pair in businessObject.GetValidationErrors()){
if (!string.IsNullOrEmpty(pair.Value))
ModelState.AddModelError(pair.Key, pair.Value);
...

// Primjer poziva:
ModelState.ValidateBusinessObject(objekt tipa BusinessBase)

14.5.6 Proširenja za straničenje


Budući da je nepraktično prikazivati sve podatke na istoj stranici, u pojedinom pogledu će se dohvatiti
samo podaci koji pripadajuću određenoj stranici te će se prikazati poveznice za ostale stranice. Način
kreiranja poveznica, kao i njijhov oblik je isti za sve tipove podataka, pa je postupak kojim bi se
kreirale poveznice izvedeno kao proširenje razreda HtmlHelper, zaduženog za stvaranje html kontrola
u pogledu. Konkretna adresa pojedine stranice parametrizirana je delegatom tipa Func<int, int,
string> koji za dva ulazna cijela broja koji predstavljaju broj stranice i redoslijed sortiranja vraća string
koji predstavlja URL stranice koja vraća početke koji pripadaju navedenoj stranici. Tako dobivena
vrijednost koristi se za svojstvo poveznice koje se dinamički stvaraju korištenjem razreda TagBuilder.

Primjer:  FirmaMVC \ HtmlHelpers \ PagingHelpers.cs

public static MvcHtmlString PageLinks(this HtmlHelper html,


PagingInfo pagingInfo, Func<int, int, string> pageUrl)
{
...
TagBuilder tag = new TagBuilder("a");
tag.MergeAttribute("href", pageUrl(i, pagingInfo.Sort));
...
}

// Primjer poziva:  FirmaMVC \ Views \ Mjesto \ Indeks.cshtml


@Html.PageLinks(Model.PagingInfo,
(p, s) => Url.Action("Index", new {page = p, sort = s}))

Prilikom poziva postupka za stvaranje stranice potrebno je proslijediti lambda izraz za funkciju s 2
cijelobrojna argumenta koja vraća string. U gore navedenom povratna vrijednost takve funkcije će
biti rezultat postupka Url.Action pri čemu su p i s vrijednosti koje u u nekom trenutku bile u postupku
PageLinks. Primjerice, kod poziva pageUrl(i, pagingInfo.Sort) , p ima vrijednost i, a s ima vrijednost
pagingInfo.Sort.

14.5.7 Forma za brisanje mjesta


Za brisanje podataka treba koristiti POST postupak, jer se GET postupak preporuča samo za postupke
koji ne mijenjaju stanja objekta ili aplikacije. Potrebno je stvoriti po jednu POST formu i jedan submit

215
gumb za svako mjesto. Forma se kreira korištenjem jedne od nekoliko preopterećenih metoda
Html.BeginForm predajući postavke forme (akcija, upravljač, parametri…).
Kako bi se izbjeglo da se slučajnim klikom na gumb Obriši izbriše podataka naknadno će korištenjem
javascripta34 biti dodan programski kod koji će korisnika upitati želi li izbrisati podatak. Taj princip se
upotrebljava i za druge stranice na kojima je omogućeno brisanje, pa su svi gumbi ukrašeni stilskom
klasom delete. Ta klasa nema svojstava i služi samo da bi skripta mogla pronaći sve takve kontrole,
bez da ih se eksplicitno mora navoditi u kodu skripte.

Primjer:  FirmaMVC \ Views \ Mjesto \ Indeks.cshtml

@using (Html.BeginForm("Delete", "Mjesto",


new { id = item.IdMjesta, page = Model.PagingInfo.CurrentPage,
sort = Model.PagingInfo.Sort }
)) {
<input type="submit" value="Obriši" class="delete" />
}

Klikom na gumb Obriši za gore navedenu formi poziva se akcija Delete na upravljaču
MjestoController. Brisanje nema vlastiti pogled te nakon brisanja dolazi do preusmjeravanja na akciju
pregleda svih mjesta. Parametri page i sort su preneseni kako ne bi došlo do povratka na početnu
stranicu u pregledu mjesta, već upravo na onu s koje je zahtjev za brisanje upućen. Eventualno se
ispisuje tekst pogreške. ViewBag ili ViewState nisu za to prikladni, jer upravljač za brisanje nema
vlastiti pogled kojem bi proslijedio model i dodatne podatke, već dolazi do preusmjeravanja.
Eventualna pogreška prilikom brisanja stavlja se u svojstvo TempData a podaci iz TempData se brišu
nakon dovršetka http zahtjeva (dakle, imaju smisla samo kod prvog preusmjeravanja). TempData se
koristi iz pogleda ili iz glavne stranice.

Primjer:  FirmaMVC \ Controlers \ MjestoControler.cs

[HttpPost]
public ActionResult Delete(int id, int page = 1, int sort = 1)
{
try
{
MjestoBllProvider bll = new MjestoBllProvider();
Mjesto mjesto = bll.Fetch(id);
mjesto.Delete();
bll.Save(mjesto);
}
catch (Exception exc)
{
TempData["Pogreska"] = exc.Message;
}
return RedirectToAction("Index", new { page = page, sort = sort });
}

34
Skripta Firma.js s pripadajućim kôdom uključena je na glavnoj stranici
216
14.6 Prikaz artikala
Za prikaz artikala koristi se model koji sadrži kolekciju artikala (objekata iz Firma.BLL) i informacije o
stranicama i broju elemenata. Artikli se prikazuju tablično, a u svakom retku s podacima artikla
prikazuje se i slika artikla i poveznice za ažuriranje i brisanje artikala. Za ažuriranje prikazane su 2
verzije, klasično kao u primjeru mjesta ili unutar retka koristeći jQuery i Ajax.

Primjer:  FirmaMVC \ Views \ Index.cshtml

@model FirmaMvc.Models.ArtiklListViewModel
...
<table>
@foreach (var item in Model.ArtiklList)
{
<tr>
<td><img src="@Url.Action("GetImage", "Artikl",
new { id = item.SifArtikla })" ... /> </td>
<td>@item.SifArtikla</td> <td>@item.NazArtikla</td>
...
<td>@Html.ActionLink("Ažuriraj", "Edit",
new { id = item.SifArtikla,
page = Model.PagingInfo.CurrentPage })
...

14.6.1 Prikaz i dodavanje slike


Za prikaz slike koristi se HTML oznaka img kojoj je adresa slika postavljena na akciju GetImage u
upravljaču Artikl. Povratna vrijednost postupka GetImage je binarni sadržaj (FileContentResult).
Za povrat objekta tipa FileContentResult koristi se postupak File koji omogućava vraćanje rezultata iz
datoteke ili spremišta u memoriji (npr. iz polja bajtova) uz navođenje dvodjelne MIME oznake
binarnog sadržaja (npr. image/jpg).

Primjer:  FirmaMVC \ Controlers \ ArtiklControler.cs

public FileContentResult GetImage(int id)


{
var bll = new ArtiklBllProvider();
Artikl a= bll.Fetch(id);
if (a != null && a.SlikaArtikla != null)
return File(a.SlikaArtikla, "image/jpeg");
else
return null;
}

Dodavanje slike vrši se prilikom unosa ostalih podataka o artiklima (npr. na formi za ažuriranje)
dodavanje HTML kontrole input tipa file, pri čemu forma mora biti označena kao multipart/form-
data.

217
Primjer:  FirmaMVC \ Views \ Artikl \ Edit.cshtml

@using (Html.BeginForm("Edit", "Artikl",


new { page = ViewBag.CurrentPage, sort = ViewBag.Sort },
FormMethod.Post, new { enctype = "multipart/form-data" }))
{ ...
<input type="file" name="slika" />

Naziv input kontrole i naziv argumenta u postupku koji prima podatke moraju biti isti. Ulazni
argument koji predstavlja prenesenu datoteku je tipa HttpPostedFileWrapper kojim se može dobiti
informacija o veličini te dobiti ulazni tok i pročitati sadržaj.

Primjer:  FirmaMVC \ Controlers \ ArtiklControler.cs

[HttpPost]
public ActionResult Edit(Artikl artikl,
HttpPostedFileWrapper slika, bool obrisiSliku, int page = 1){
var bll = new ArtiklBllProvider();
Artikl original = bll.Fetch(artikl.SifArtikla.Value);
...
if (slika != null) {
original.SlikaArtikla = new byte[slika.ContentLength];
slika.InputStream.Read(original.SlikaArtikla, 0, slika.ContentLength);

14.6.2 Ažuriranje unutar retka


Kod klasičnog ažuriranja sa stranice na kojoj su prikazni artikli dolazi do otvaranja nove stranice, a
zatim i ponovnog vraćanja na prikaz svih artikala, što je u praksi sporo i nepraktično. Bolja, ali
složenija varijanta je da se korištenjem tehnologije Ajax pozove akcija na serveru koja vrati predložak
retka za ažuriranje te da se dinamički redak s prikazom artikla zamijeni s predloškom za ažuriranje (svi
ostali artikli su i dalje prikazani na toj stranici) te da se nakon snimanja promjena predložak za
ažuriranje ukloni i vrati redak s prikazom (ažuriranog) artikla. Nedostatak ovog pristupa je
nemogućnost ažuriranja slike, jer prijenos slike nije podržan ovim načinom (zbog zahtjeva da forma
bude multipart, a što u slučaju ajax poziva nije).
Umjesto generiranja poveznice s Html.ActionLink koristi se obična html kontrola koju će javascript iz
prikazanog primjera prepoznati po tome što je označena određenim imenom ili stilskom klasom. U
ovom primjeru koristi se stilska klasa editajax. Kako bi se poveznice mogle međusobno razlikovati
koristi se vlastiti atribut za HTML oznake data-sifartikla.

Primjer:  FirmaMVC \ Views \ Artikl \ Index.cshtml

<a class="editajax" data-sifartikla="@item.SifArtikla"


href="#" >Ažuriraj (Ajax)</a>
...
$(function () {
$(".editajax").each(function () {
SetEditAjax($(this), '@Url.Action("EditAjax",
new { page = Model.PagingInfo.CurrentPage })');
}); ...

Nakon što se stranica s prikazom artikala učita, kod pisan u jQueryju pronalazi sve takve poveznice i
pridružuje im odgovarajući događaj. jQuery kod koji se izvršava tek nakon učitavanja stranice počinje
s $(function() { …

218
Poziva se postupak vlastiti postupak SetEditAjax iz firma.js. U kodu navedene metode sprječava se
uobičajeno ponašanje poveznice (preventDefault) te se poziva akcija predana parametrom
editAjaxUrl koja izaziv GET varijanta poziva uz šifru artikla izvučenu iz vlastitog atributa. Redak u
kojem se poveznica nalazila (redak s detaljima artikla) se mijenja sadržajem rezultata pozvane akcije.

Primjer:  FirmaMVC \ Scripts \ firma.js

function SetEditAjax(btn, editAjaxUrl) {


$(btn).click(function (event) {
event.preventDefault();
var sifartikla = $(this).data('sifartikla');
var tr = $(this).parents("tr");
$.get(editAjaxUrl, { id: sifartikla }, function (data) {
tr.html(data);
});
});
}

Predložak za ažuriranje kojeg je vratio postupak EditAjax sadrži retke tablice s html kontrolama za
unos i ispis rezultata validacije. Svaka kontrola ima vlastiti atribut sifartikla kako bi se razlikovala od
ostalih na stranici. Namjena pojedine kontrole (tj. informacija kojem svojstvu artikla kontrola
pripada) u ovom primjeru evidentira se korištenjem stilskih klasa.

219
Primjer:  FirmaMVC \ Views \ Artikl \ EditAjax.cshtml

<td colspan="2">
<span class="error">@TempData["Pogreska"]</span>
@Html.ValidationSummary()
</td>
<td>
<input type="text" value="@Model.NazArtikla"
data-sifartikla="@Model.SifArtikla" class="nazartikla" />
</td>
<td>
<input type="text" value="@Model.JedMjere"
data-sifartikla="@Model.SifArtikla" class="jedmjere" />
</td>...
<input type="button" value="Spremi"
class="saveajax" data-sifartikla="@Model.SifArtikla" />

...
$(function () {
$(".saveajax").click(function () {
var sifartikla = $(this).data('sifartikla');
var nazartikla = $(".nazartikla[data-sifartikla='" +
sifartikla + "']").val();
...
var zastusluga = $(".zastusluga[data-sifartikla='" +
sifartikla + "']").is(':checked');
var url = '@Url.Action("EditAjax")';
var tr = $(this).parents("tr");

$.post(url, { SifArtikla: sifartikla, NazArtikla: nazartikla,


JedMjere: jedmjere, CijArtikla: cijena,
ZastUsluga: zastusluga, page : @ViewBag.CurrentPage },
function (data) {
tr.html(data);...

Klikom na kontrolu označenu stilskom klasom saveajax dolazi do slanja podataka (koristi se
jQueryjeva funkcija $.post) nakon čega se sadržaj trenutnog retka zamijeni rezultatom akcije.
Ako je snimanje bilo uspješno vraća se parcijalni pogled Show, a u protivnom se iscrtava parcijalni
pogled EditAjax te se ispisuju validacijske pogreške.

220
Primjer:  FirmaMVC \ Controllers \ ArtiklController.cs

[HttpPost]
public ActionResult EditAjax(Artikl artikl) {
Artikl original = ...dohvat artikla iz baze...
try{
… izmjena vrijednosti originalnom objektu
ModelState.ValidateBusinessObject(original);
if (ModelState.IsValid) {
bll.Save(original);
return RedirectToAction("Show",...
}
}
catch (Exception exc) {
TempData["Pogreska"] = exc.Message;
}
return PartialView(original);
}

Klikom na gumb Odustani sadržaj retka se mijenja rezultatom akcije Show pri čemu je potrebno
ponovno postaviti povezivanja za novoučitane elemente.

Primjer:  FirmaMVC \ Views \ Artikl \ EditAjax.cshtml

[$(function () {
$(".cancelajax").click(function () {
var sifartikla = $(this).data('sifartikla');
var url = '@Url.Action("Show",
new { page = ViewBag.CurrentPage })';
var tr = $(this).parents("tr");

$.get(url, { id: sifartikla }, function (data) {


tr.html(data);
SetEditAjax($(".editajax[data-sifartikla='" +
sifartikla + "']"),
'@Url.Action("EditAjax",
new { page = ViewBag.CurrentPage })');
SetDeleteAjax(...);
SetConfirmDelete($(".delete[data-sifartikla='"
+ sifartikla + "']"));
});
});
});

Brisanje artikla Ajax pozivom vrši se isključivo POST postupkom koji prima šifru artikla (parametar id u
postupku brisanja). Postupak brisanja vraća JSON koji je nastao iz anonimne klase koja ima dva
svojstva: svojstvo Successful za informaciju da li je postupak brisanja uspio i svojstvo ErrorMessage
koji sadrži eventualni opis pogreške.

221
Primjer:  FirmaMVC \ Controllers \ ArtiklController.cs

[HttpPost]
public JsonResult DeleteAjax(int id) {
var result = new { Successful = true,
ErrorMessage = string.Empty };
try {
...
artikl.Delete();
bll.Save(artikl);
}
catch (Exception exc) {
result = new { Successful = false,
ErrorMessage = exc.Message };
}
return Json(result);
}

Brisanje je pozvanom klikom na gumb i potvrdom brisanja, nakon čega je uslijedio POST zahtjev
(adresa zahtjeva i id su prethodno pripremljeni). U slučaju uspješnog brisanja redak u kojem je bio
artikl uklanja se iz strukture stranice, a u protivnom se ispisuje poruka o pogrešci.

Primjer:  FirmaMVC \ Scripts \ firma.js

function SetDeleteAjax(btn, deleteAjaxUrl) {


$(btn).click(function () {
var sifartikla = $(this).data('sifartikla');
if (confirm('Obrisati artikl?')) {
$.post(deleteAjaxUrl, {id: sifartikla}, function (data) {
if (data.Successful) {
var tr = $(btn).parents("tr");
$(tr).remove();
}
else {
alert(data.ErrorMessage);
}
});
...

14.7 Primjer Master-Detail


Primjer Master-Detail oponaša slične primjere iz Windows aplikacije i WebForms aplikacije
uvažavajući specifičnosti MVC-a. U svakom trenutku prikazan je jedan dokument sa stavkama,
poveznice za ažuriranje i brisanje te poveznice za odlazak na neku drugu stranicu (u ovom slučaju na
neki drugi dokument). Dodatno, odmah na ekranu s dokumentom moguće je dodati novu stavku

222
Slika 101. Prikaz dokumenta sa stavkama

Prilikom ažuriranja dokumenta moguće je ažurirati zaglavlje dokumenta i svaku stavku.

Slika 102. Ažuriranje dokumenta sa stavkama

14.7.1 Popis akcija i pogleda


Upravljač za dokument FirmaMvc – Controllers \ DokumentController.cs sastoji se od sljedećih
akcija i pogleda:

 Index – akcija koja služi kao početna stranica koja preusmjerava zahtjev na akciju Create ili
Details u ovisnosti postoji li barem jedan dokument u bazi podataka ili ne.
 Create – sadrži GET i POST varijantu za unos novog dokumenta te koristi pogled u View \
Dokument \ Create.cshtml. Postupak u POST varijanti prima Dokument kao parametar.
 Details – prikazuje jedan dokument i sve njegove stavke. Odabir dokumenta vrši se tako da je
proslijeđen parametar za broj stranice, a svaka stranica sadrži samo jedan dokument, pa se
de facto radi o dohvatu n-tog dokumenta. Za prikaz koristi se pogled View \ Dokument \
Details.cshtml
 Edit – akcija za ažuriranje dokumenta koja sadrži GET i POST varijante za ažuriranje. GET
varijanta prima id dokumenta kojeg treba ažurirati i trenutnu stranicu kako bi se nakon
ažuriranja mogao ponovo prikazati isti dokument. POST varijanta prima dokument i listu
logičkih (bool) vrijednosti naziv BrisiStavku koje predstavljaju stavke koje treba ukloniti iz
dokumenta. I-ti redak stavke u u pogledu View \ Dokument \ Edit.cshtml sadrži checkbox
naziva BrisiStavku[i] što se onda automatski povezuje u parametar polja logičkih varijabli
naziva BrisiStavku.
 Delete – akcija za brisanje dokumenta koja ima samo POST varijantu, a ovisnosti o uspješnosti
brisanja dokumenta rezultat preusmjerava na akciju Index ili Details u slučaju pogreške.
 DodajStavku – akcija za dodavanje stavke u dokument sa samo POST varijantom koja prima
podatke o novoj stavci i nakon dodavanja preusmjerava izvršavanje na akciju Details

223
 ObrisiStavku – akcija za brisanje stavke iz dokumenta sa samo POST varijantom koja prima id
dokumenta i id stavke koju treba obrisati. Nakon brisanja dolazi do preusmjeravanja na akciju
Details.
Kod većine akcija koristi se parametar page kako bi se nakon akcije mogao prikazati trenutni (a nakon
brisanja prethodni) dokument. Za dohvat dokumenta koristi se postupak FetchFromPosition iz BLL
sloja. Postupkom se vrši dohvat 1 podatka počevši od neke stranice što je zapravo dohvat n-tog
dokumenta.

14.7.2 Prikaz dokumenta


Akcija Index u ovisnosti o postojanju dokumenata preusmjerava rezultat na prikaz prvog dokumenta
ili na stranicu za stvaranje dokumenta

Primjer:  FirmaMVC \ Controllers \ DokumentController.cs

public ActionResult Index(int page = 1)


{
var bll = new DokumentBllProvider();
int count = bll.ItemsCount();
if (count > 0) {
return RedirectToAction("Details", new { page = 1 });
}
else {
return RedirectToAction("Create");
}
}

Kao model za prikaz dokument koristi se pomoćni razred koji sadrži pojedinačni dokument i
informacije o straničenju.

Primjer:  FirmaMVC \ Models \ DokumentViewModel.cs

public class DokumentViewModel {


public Dokument Dokument { get; set; }
public PagingInfo PagingInfo { get; set; }
}

Primjer:  FirmaMVC \ Models \ PagingInfo.cs

public class PagingInfo {


public int TotalItems { get; set; }
public int ItemsPerPage { get; set; }
public int CurrentPage { get; set; }
public int TotalPages {
get {
return (int)Math.Ceiling((decimal)TotalItems / ItemsPerPage);
}
}
public int Sort { get; set; }
}

Prilikom dohvata dokumenta dolazi i do dohvata njegovih stavki (što je obavljeno u poslovnom sloju),
podaci pripremaju za straničenja te se vrši priprema padajućih listi za odabir artikla u stavkama.
Padajuće liste nisu dio modela, nego se prenose kroz ViewBag.

224
Primjer:  FirmaMVC \ Controllers \ DokumentController.cs

public ActionResult Details(int page) {


...
var dokument = bllDokument.FetchFromPosition
(page-1, 1).SingleOrDefault();
...
var bllArtikl = new ArtiklBllProvider();
ViewBag.Artikli = bllArtikl.FetchLookup();
...
PagingInfo pagingInfo = new PagingInfo {
CurrentPage = page, ItemsPerPage = 1,
TotalItems = bllDokument.ItemsCount()
};
DokumentViewModel model = new DokumentViewModel {
Dokument = dokument, PagingInfo = pagingInfo
};
return View(model);

U pogledu dolazi do prikaza poveznica (stranica) za pojedinačne dokumente, prikaza dokumenta i


kreiranja poveznica za ažuriranja dokumenta i brisanje dokumenta. Za svaku stavku kreira se
poveznica za brisanje. Na dnu popisa sa stavkama nalazi se redak ispod tablice za dodavanje nove
stavke pri čemu se artikl bira iz padajuće liste kreirane sljedećim programskim odsječkom.

Primjer:  FirmaMVC \ Views \ Dokument \ Details.cshtml

@Html.DropDownList("SifArtikla",
new SelectList(ViewBag.Artikli, "Key", "Text"),
new { @class = "ddlartikl" }
)

Brisanje pojedine stavke, brisanje dokumenta i dodavanje stavki izvedeni su slično kao u primjeru za
Mjesto. Specifičnost ovog primjeru su ažuriranje dokumenta i stavki te validacija.
Napomena: Ažuriranje stavki vrši se kad i ažuriranje dokumenta, posebnom akcijom.

14.7.3 Ažuriranje dokumanta i stavki


Za vrijednosti koje se ne smiju promijeniti stvara se skrivena kontrola korištenjem postupka
Html.HiddenFor, a za padajuće liste koriste se podaci pripremljeni u upravljaču i pohranjeni u svojstvo
pogleda ViewBag.

Primjer:  FirmaMVC \ Views \ Dokument \ Edit.cshtml

...
@Html.HiddenFor(m => m.Stavke[i].JedCijArtikla)
...
@Html.DropDownList("SifArtikla",
new SelectList(ViewBag.Artikli, "Key", "Text"),
new { @class = "ddlartikl" }
)
...
Obriši stavku: @Html.CheckBox("BrisiStavku[" + i + "]")

Za svaku stavku stvorit će se checkbox kontrola koja označava treba li obrisati stavku ili ne pri čemu
su nazivi ovih kontrola oblika BrisiStavku[0…broj stavki] te upravljač prima listu logičkih vrijednosti
naziva BrisiStavku.

225
Primjer:  FirmaMVC \ Controllers \ DokumentController.cs

[HttpPost]
public ActionResult Edit(int page,
Dokument dokument, List<bool> BrisiStavku) {
...

14.7.4 Validacija podataka


Validacija se kao i u prethodnim primjerima obavlja u poslovnom sloju pri čemu se onda te pogreške
eksplicitno prenose u pogled kao u poglavlju 14.5.5. Kako bi se omogućilo automatsko bojanje
neispravnih stavke, za pogrešku nekog svojstva i-te stavke u kodu poslovnog sloja (ili na nekom
drugom mjestu gdje se radi validacija) potrebno je nadređenom dokumentu dodati pogrešku naziva
Stavka[i].NazivSvojstva kao u sljedećem primjeru.

Primjer:  Firma.Win \ Firma.BLL \ BusinessEntities \ Dokument.cs

public override void Validate() {


...
foreach (var pair in Stavke[i].GetValidationErrors()) {
if (!string.IsNullOrEmpty(pair.Value)) {
SetError("Stavke[" + i + "]." + pair.Key, pair.Value);
}
}
...

14.8 Zadaci
Zadatak 1. Čemu služi model, čemu pogled a čemu upravljač?
Zadatak 2. Gdje se u modelu arhitekture MVC nalazi poslovna logika?
Zadatak 3. Treba li validacija podataka biti bliže prezentacijskom ili sloju pohrane podataka?

226
15 Web servisi
Način razvoja dijeljenih aplikacija mijenjao se kroz povijest. U razdoblju strukturiranog razvoja mogle
su se višekratno iskoristiti samo funkcije neovisne o (globalnim) podacima. Pojavom objektno
orijentiranog razvoja podaci i ponašanje se objedinjuju u objekte, ali problem dijeljenja je ostao isti –
mogao se dijeliti samo izvorni programski kod pisan u istom jeziku. Eventualna razmjena podataka
odvijala se korištenjem TCP-a ili slične tehnologije pri čemu u toj komunikaciji nije bilo veće koristi od
objektno orijentirane tehnologije. S vremenom pojavljuju se statičke biblioteke (.lib) i dinamičke
biblioteke (.dll) funkcija koje se mogu dijeliti između više aplikacija. Početkom devedesetih pojavljuje
se Component Object Model (COM), a kasnije i DCOM (Distributed Component Object Model), a
kasnije i.NET Remoting kao načini razmjene podataka između aplikacija pri čemu umjesto dijeljenja
izvornog koda klijent i server imaju dogovoren binarni tip podataka u razmjeni35. Iako su navedeni
pristupi predstavljali napredak, patili su od značajnih problema kao što je problem verzioniranja
(engl. DLL hell). Konačno, početkom ovog stoljeća pojavljuje servisno orijentirana arhitektura (engl.
Service Oriented Architecture, SOA) kao koncept koji će omogućiti distribuirane aplikacije između
različitih platformi koje komuniciraju razmjenom podataka među servisima.
Servis je jedna ili više funkcionalnih komponenti (ili cijeli sustav) s kojim se komunicira putem javno
objavljenih i precizno definiranih sučelja. Komunikacija je moguća između heterogenih klijenata, a
sam servis funkcionira na principu crne kutije - implementacija je skrivena od javnosti. Servis prima
jedan ili više zahtjeva i vraća jedan ili više odgovora. Za komunikaciju se koriste otvoreni web
standardi: HTTP, XML, SOAP itd.
Servisno orijentirana arhitektura je skup precizno definiranih, međusobno neovisnih servisa
povezanih u logički jedinstvenu aplikaciju. Kao što objektno orijentirana aplikacija povezuje objekte,
tako servisno orijentirana arhitektura povezuje servise stvarajući distribuirani sustav u kojem
sudjeluje više autonomnih servisa međusobno šaljući poruke preko granica određenih procesom,
mrežom, i dalje.
Pri izradi servisa potrebno je voditi računa o interoperabilnosti i sigurnosti komunikacije (tko sve
može i smije pozvati servis), problemu višenitnosti, brzini obrade postupka i mogućnostima
skalabilnosti. Neuspjeh prilikom izvršavanja servisa ne smije ostaviti sustav u stanju pogreške
(konzistentnost stanja). Servis mora odlikovati pouzdanost i robusnost. Pogreške u servisu treba
obraditi, a klijent treba znati je li servis primio i obradio poruku.

15.1 Osnovna pravila servisno orijentirane arhitekture


Servisno orijentirana arhitektura mora zadovoljiti 4 osnovna pravila:
 Jasno određene granice: funkcionalnost i struktura podataka moraju biti jasno iskazana, a
implementacija ostaje crna kutija za korisnika
 Neovisnost servisa: Servis ne smije ovisiti o klijentu, nekom drugom servisu, lokaciji ili vrsti
instalacije. Verzije servisa se mogu razvijati neovisno o klijentu, a objavljene verzije se ne
mijenjanju.
 Ugovor, a ne implementacija: Korisnik servisa i implementator servisa dijele samo listu
javnih postupaka i definiciju struktura podataka. Dijeljeni podaci trebaju biti tipovno

35
Za detaljnije pogledati A Brief History of the Microsoft Distributed Stack, N. Pathak: Pro WCF 4, Apress
227
neutralni, a tipovi specifični za pojedini jezik moraju se moći pretvoriti u neutralni oblik i
obrnuto. Implementacijski postupci ostaju tajna
 Semantika, a ne samo sintaksa: Servise je potrebno smisleno imenovati i logički
kategorizirati

15.2 Standardi za web servisi


WSDL (Web Services Description Language) je XML shema za opise Web servisa. Na osnovu WSDL-a
korisnik zna koje postupke servis pruža i u kojem formatu, tj. kako te postupke pozvati i koje tipove
rezultata će dobiti kao odgovor, a komunikacija se obavlja korištenjem SOAP protokola.
SOAP (Simple Object Acess Protocol) je izvorno Microsofov protokol namijenjen za razmjenu
informacija u distribuiranim, heteregonim okruženjima. Zasniva se na korištenju XML-a i HTTP
protokola. Servisu izloženom na određenoj adresi šalje se XML oblikovan prema SOAP protokolu.
Tako oblikovan XML sadrži podatke koje treba poslati servisu, kao i informaciju koji postupak servisa
treba pozvati (servis može imati više postupaka). Za ovaj način komunikacije sa servisom kaže se da je
vođen akcijama (engl. action driven). Kao alternativa SOAP protokolu koristi se REST (engl.
Representation State Transfer) za koji se kaže da je orijentiran na resurse (engl. resource driven).
REST nije protokol poput SOAP-a, nego je arhitekturni obrazac za izgradnju i pozivanje web servisa.
Korištenjem REST-a postupak servisa koji treba pozvati označen je URL-om (adresom poziva) i
načinom slanja podataka preko HTTP-a (GET, POST, PUT, DELETE). Poslani podaci mogu biti u XML-u,
ali mogu biti poslani i u nekom drugom formatu (npr. JSON) ili korištenjem parametara u adresi (engl.
query string).

15.3 Struktura primjera s web servisima


Rješenje za primjere s web servisima sastoji se od 8
projekata i mape DLL za biblioteke (Firma.BLL i
Firma.Framework) koje se koriste u pojedinim projektima, a
izvorni kod je napisan u prethodnim primjerima.
Rješenje sadrži klasični („stari“) ASP.Net web servis (projekt
KlasicniWebServis) ekstenzije aspx koji radi isključivo sa
SOAP protokolom i odgovarajuću konzolnu aplikaciju za
demonstraciju poziva servisa (projekt
PozivKlasicnogWebServisa).
Veće mogućnosti izrade (ne samo web) servisa mogu se
ostvariti korištenjem WCF-a (Windows Communication
Framework) što je prikazano kombinacijom projekata
Slika 103. Struktura rješenja za primjere s web Ugovor (predstavlja ugovor, odnosno sučelja i razrede koji
servisima
čine strukturu WCF servisa), ImplementacijaUgovora
(konkretna implementacija sučelja iz ugovora), SmjestajWebApp i SmjestajWinApp (izlaganje WCF
servisa kao web servisa, odnosno servisa u windows aplikaciji) te projekta PrimjeriPoziva koji
demonstrira korištenje WCF servisa na 3 načina: kao da se radi o uobičajenom web servisu, korištenje
WCF servisa iz .NET klijenta te REST poziv iz .NET klijenta.
Projekt WebApi predstavlja primjer izrade REST servisa kroz radni okvir ASP.Net WebApi koji ima
veće mogućnosti za REST servise u odnosu na WCF.

228
15.4 Primjer web servisa
U primjeru WCF \ KlasicniWebServis izrađen je klasični web servis s postupkom koji vraća popis
država. Klasični web servis izrađuje se unutar web aplikacije odabirom opcije Add New Item  Web
Service čime se stvaraju dvije datoteke ekstenzija asmx i asmx.cs, a konkretni kod servisa piše se u
datoteci ekstenzije asmx.cs. Po potrebi je moguće dodati i dodatne razrede za razmjenu podataka, pa
je tako u ovom primjeru dodana datoteke Drzava s 3 svojstva (Naziv, Oznaka, ISO3 oznaka države)
koji se koriste u rezultatu.36
Kako bi neki postupak u razredu bio ujedno i postupak web servisa potrebno je dodati atribut
WebMethod.

Primjer:  WCF \ KlasicniWebServis\ ????.asmx.cs

Class PROVJERI
[WebMethod]
public List<Drzava> PopisDrzava() {
List<Drzava> list = new List<Drzava>();
var bll = new Firma.DrzavaBllProvider();
foreach (var d in bll.FetchAll()) {
list.Add(new Drzava {
Naziv = d.NazDrzave,
Oznaka = d.OznDrzave,
Iso3Oznaka = d.ISO3Drzave
});
}
return list;
}

Ovako napisan servis dostupan je na adresi oblika http://.../naziv.asmx. Otvaranjem te stranice u


pregledniku dobit će se informacije o servisu, a WSDL datoteka nalazi se na adresi
http://.../naziv.asmx?wsdl.

15.4.1 Generiranje proxy razreda za pozive web servisa


Za korištenje web servisa potrebno je generirati „omotače“, tzv. proxy razrede koji sadrže (skrivaju od
korisnika) detalje komunikacije s drugim objektima, tj. u ovom slučaju web servisom. Proxy razred
nekog servisa sadrži sve postupke koje servis ima. Proxy se može generirati kroz razvojno sučelje ili
kroz naredbeni redak korištenjem wsdl datoteke i pomoćnih programa WSDL.exe ili svcutil.exe.

36
Razred Drzava iz Firma.BLL sadrži podatke koji se žele koristiti u razmjeni podataka, ali taj razred se ne može
serijalizirati u XML bez dodatnih modifikacija.
229
Slika 104. Koncept izrade proxy klase za komunikaciju s web servisom

Referenciranje web servisa iz projekta vrši se desnim klikom na projekt i odabirom opcije Add Web
Reference → Add Service Reference i unosom imena za prostor imena pod kojim će se proxy
generirati, nakon čega na disku nastaje nekoliko datoteka (wsdl, disco, …), a u datoteci app.config se
stvaraju zapisi o postavkama komunikacije (način povezivanja, adresa servisa, maksimalna veličina
poruke, …).

Slika 105. Koraci prilikom dodavanja reference na web servis u razvojnom okruženju

15.4.2 Poziv web servisa


Razredi za podatke koji se koriste kao ulazni i izlazni podaci iz web servisa definirani su unutar web
servisa na serveru (a ne na klijentu). Dodavanjem reference na servis automatski će se generirati
omotač (proxy) onih razreda koji se koriste u komunikaciji. Generirani razredi na klijent sadržavat će
sve varijable i svojstva koja su unutar izvornog razreda na web servisu javna i namijenjena i za čitanje
i za pisanje. Postupci definirani u razredima na web servisu ne pojavljuju se u generiranim razredima.

230
S obzirom da proxy u kombinaciji s postavkama iz konfiguracijske datoteke skriva složenost
komunikacije sa servisom, poziv iz koda je nalik pozivu bilo koje druge metode u projektu.

Primjer:  WCF \ PozivKlasicnogWebServisa

var client = new FirmaServiceReference.FirmaWebServiceSoapClient();


foreach (var drzava in client.PopisDrzava())
{
Console.WriteLine("{0}-{1}, {2}", drzava.Ozn, drzava.Naziv, drzava.ISO3);
}

Web servis iz gornjeg primjera će vratiti xml koji predstavlja (serijaliziranu) listu država. Svaki svojstvo
objekta koji je sadržan u rezutatu se, ako drugačije nije navedeno, posprema kao istoimeni element u
povratnom xml-u. Promjena strukture xml-a moguće je korištenjem atributa kao u sljedećem
primjeru.

Primjer:  WCF \ KlasicniWebServis\Drzava.cs

[XmlType("D")]
public class Drzava{
[XmlElement("Ozn")]
public string Oznaka { get; set; }
public string Naziv { get; set; }
[XmlAttribute("ISO3")]
public string Iso3Oznaka { get; set; }
}

U ovom slučaju u povratnom xml-u Iso3Oznaka će se umjesto kao element pospremiti kao atribut
elementa oznake D (umjesto Drzava), a naziv elemeta koji sadrži vrijednost svojstva Oznaka bit će
Ozn. Generirani omotač na klijentu u ovom slučaju ima razred D, a ne razred Drzava. Ako se neko
svojstvo želi isključiti iz serijalizacije, potrebno je upotrijebiti atribut [XmlIgnore], što može biti i
nužno ako se neko svojstvo ne može serijalizirati u xml (npr. Hashtable).37

15.4.3 Nedostatci klasičnih web servisa


U klasičnom web servisu razredi koji se koriste u komunikaciji izneđu klijenta i servisa definirani su na
strani web servisa što može biti nepraktično kad su i klijent i servis unutar .NET okruženja pa bi mogli
imati zajednički skup razreda i postupaka. Generirani razredi sadrže samo javna svojstva i javne
varijable dostupne i za čitanje i za pisanje, pa se postupci i svojstva samo za čitanje (kao što su razna
izračunata svojstva) ne serijaliziraju te se moraju ponovo pisati na klijentu. Da bi se klijent mogao
početi razvijati web servis (i razredi na web servisu) moraju postojati i biti barem djelomično
funkcionalni kako bi se na klijentu mogla dodati referenca na web servis. Klasični web servisi imaju
slabiju mogućnost serijalizacije (primjerice, ne mogu se serijalizirati razred koji implementiraju
IDictionary poput Hashtable) i ograničeni su na HTTP protokol, što može značiti pad performansi u
situaciji kad u i klijent i servis na istom računalu pa bi mogli komunicirati i na neki drugi način.

37
Atribut Serializable nije potreban ako se radi o xml serijalizaciji. Potreban je ako se koristi binarna
serijalizacija
231
15.5 WCF servisi
WCF (engl. Windows Communication Foundation) je radni okvir za izradu naprednijih oblika (web)
servisa, pri čemu servis ne mora nužno biti web servis, to jest mogu se koristiti različiti protokoli
komunikacije (HTTP, TCP, Net pipes, …). WCF servisi baziraju se na tzv. ABC prinicipu (skraćenica
Address, Binding i Contract). Adresa i način povezivanja odvojeni su od podataka i konkretne
implementacije servisa i uobičajeno su zapisani u konfiguracijskim datotekama (web.config,
app.config). Ugovor se sastoji od skupa sučelja s postupcima servisa i razreda za podatke koji
sudjeluju u razmjeni podataka. Ugovor sadrže obje strane u komunikaciji i neovisan je o adresi i
načinu povezivanja. Na ovaj način postiže se jasnije odvajanje razreda za komunikaciju te je
jednostavnije promjena razreda, implementacije servisa ili smještaja servisa.

WCF servis se razvija na način da se prvo definira ugovor, nakon čega se mogu istovremeno razvijati i
servis i klijent, a zatim slijedi implementacija ugovora i izlaganje WCF servisa u kojem se određuju
adresa, ponašanje i način povezivanja s pristupnim točkama servisa. U primjerima koji slijede ugovor,
implementacija i smještaj su podijeljeni u tri odvojena projekta pri čemu su ugovor i implementacija
projekti tip Class Library, a smještaj je prikazan s 2 primjera projekta (web aplikacija i windows
aplikacija)

15.5.1 Protokoli povezivanja


Osim uobičajenog HTTP protokola koji omogućava integraciju s otvorenim standardima i rad s
različitim arhitekturama WCF podržava i ostale protokole kao što su
 TCP - brzi protokol u kojem se koristi binarni format, pogodan za komunikaciju za WCF-WCF
posebno u lokalnoj mreži
 imenovani cjevovodi (engl. named pipes) – brz i pouzdan način prijenosa između WCF
klijenta i WCF servisa na istom računalu koji koristi dio dijeljene memorije za komunikaciju
 MSMQ (Microsoft Message Queue) – omogućava stavljanje poruka u red, što je korisno ako
servis nije uvijek dostupan, pa može naknadno preuzeti poruku
Dodatno, WCF po potrebni dozvoljava korištenjem vlastitog protokola povezivanja. U sljedećoj tablici
prikazani su uobičajeni tipovi povezivanja za WCF servise

Naziv povezivanja Kratki opis

basicHttp(Context)Binding Način povezivanja starih asmx servisa prema standardu WS-I


Basic Profile 1.1
wsHttp(Context)Binding Napradniji WS-* profili (Preporuča se u odnosu na basic)
wsDualHttpBinding Isto kao i wsHttpBinding, ali za dvosmjernu komunikaciju
webHttpBinding Koriste se za izlaganje servisa kroz različite Http zahtjeve (za
REST servise koji vraćaju XML ili JSON)
wsFederationHttpBinding WS-* profili + federated identity
netTCP(Context)Binding Za povezivanje WCF klijenta i servisa TCP-om
netNamedPipeBinding Optimiziran za komunikaciju klijenta i servisa na istom
računalu

232
netPeerTcpBinding P2P komunikacija u kojoj svaki čvor je ujedno klijent i server
prema ostalim sudionicima
netMsmqBinding Povezivanje za asinkronu komunikaciju između klijenta i
servisa
msmqIntegrationBinding Omogućava komunikaciju sa postojećim sustavima koji
komuniciraju koristeći MSMQ

Varijante s s kontekstnim povezivanjem (*ContextBinding) omogućavaju dugotrajne servise


rekonstrukcijom konteksta pomoću cookiea ili poruke u zaglavlju SOAP poruke.

15.5.2 Primjer izrade WCF servisa


Ugovor WCF servisa definira servise i postupke servisa koji će se koristiti u komunikaciji ne
pretpostavljajući ništa o vrsti komunikacije i načinu implementacije. Servis predstavlja svako sučelje iz
ugovora koje je dekorirano atributom ServiceContract u kojem su postupci tog servisa metode
dekorirane atributom OperationContract. Oba atributa definirana su u prostoru imena
System.ServiceModel. U slučaju da je ugovor zasebni projekt tipa Class Library bit će potrebno dodati
odgovarajuću referencu (Add Reference → System.ServiceModel.dll)

Primjer:  WCF \ Ugovor \ IFirmaServis.cs

[ServiceContract]
public interface IFirmaServis {
[OperationContract]
List<Drzava38> PopisDrzava();
[OperationContract]
List<Osoba> PopisOsoba();...

Implementacija sučelja je izvedena u zasebnom projektu koji referencira projekt s ugovorom (i ostale
potrebne dll-ove). Implementacija ne ovisi o načinu povezivanja, a po potrebi se može koristiti i kao
obični dll (ne mora nužno biti izložena kao servis)

38
WCF podržava naprednije načine serijalizacije, pa se može iskoristiti razred Drzava iz poslovnog sloja, što nije
moguće kod klasičnog servisa zbog svojstva BLLProvider koji je tipa IBllProvider.
233
Primjer:  WCF \ ImplementacijaUgovora \ FirmaServis.cs

public class FirmaServis : Ugovor.IFirmaServis {

public List<Firma.BusinessEntities.Drzava> PopisDrzava() {


DrzavaBllProvider bll = new DrzavaBllProvider();
return bll.FetchAll().ToList();
...
}

public List<Ugovor.Osoba> PopisOsoba() {


var bllProvider = new Firma.PartnerBllProvider();
var osobe = bllProvider.FetchAll()
.Where(p => p.TipPartnera ==TipPartnera.Osoba)
.Select(p => new Osoba {
IdPartnera = p.IdPartnera.Value,
Ime = p.ImeOsobe,...

Konkretna implementacija nekog WCF ugovora izlaže se (udomljuje, engl. host) unutar neke web ili
windows aplikacije. U sljedećem primjeru korištena je prazna web aplikacija (ne mora nužno biti
korištena samo za WCF servis) koja referencira projekt koji sadrži implementaciju ugovora, a u
datoteci web.config navodi se naziv konkretne implementacije i relativna adresa na kojoj će se WCF
servisi nalaziti.

Primjer:  WCF \ SmjestajWebApp \ web.config

<system.serviceModel>
...
<serviceHostingEnvironment>
<serviceActivations>
<add relativeAddress="Firma.svc"
service="ImplementacijaUgovora.FirmaServis"/>
</serviceActivations>
</serviceHostingEnvironment>
</system.serviceModel>

Bez dodatnih postavki ovako definiran WCF servis je dostupan kao web servis na adresi
http://.../Firma.svc uz pretpostavljeni način povezivanja (basicHttpBinding). Servis je sada dostupan
svima koji imaju njegovu definiciju u obliku wsdl dokumenta ili dll projekta koji predstavlja ugovor
servisa.

Primjer:  WCF \ SmjestajWebApp \ web.config

<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>

234
Da bi servis automatski nudio prikaz wsdl-a potrebno je omogućiti razmjenu metapodataka (engl.
Metadata exchange - MEX) nakon čega se wsdl biti dostupan na adresi http://.../Firma.svc?wsdl.
Proxy kod za poziv servisa može se u tom slučaju kreirati naredbom

svcutil.exe http://.../Firma.svc?wsdl

15.5.3 Program WCF Test Client


Izloženi WCF servis može se isprobati jednostavnim programom WCF Test Client (npr. C:\Program
Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE\ WcfTestClient.exe). Nakon pokretanja
opcijom File → Add Service unosi se adresa servisa koji ima omogućenu razmjenu metapodataka.
WCF Test Client omogućava unos ulaznih parametara, poziv servisa i pregled rezultata servisa.

Slika 106. WCF Test Client za jednostavno testiranje WCF servisa

15.5.4 Korištenje WCF servisa dodavanjem reference


U razvojnom okruženju WCF servis se može referencirati kao i klasični web servis dodavanjem
reference na servis unosom adrese servisa ili odabirom opcije Discover ako je servis dio istog
rješenja. Referencirani servis pojavljuje se pod Service References, na disku se automatski stvara
nekoliko datoteka za generirani proxy, a u konfiguracijskog datoteci dodaju se elementi za adresu
servisa i način pozivanja.

235
Primjer:  WCF \ PrimjeriPoziva \ app.config

<system.serviceModel>
...
<binding name="BasicHttpBinding_IFirmaServis" />
...
<client>
<endpoint address="http://localhost:609339/Firma.svc"
binding="basicHttpBinding"
bindingConfiguration="BasicHttpBinding_IFirmaServis"
contract="FirmaWebServis.IFirmaServis"
name="BasicHttpBinding_IFirmaServis" />
</client>

Generirani proxy sadrži i razred koji služi kao klijent za poziv web servisa. Razred sadrži sve postupke
iz ugovora, kao i njihove asinkrone varijante.

Primjer:  WCF \ PrimjeriPoziva \ FormKlasicno

private async void btnLoad_Click(...) {


var client = new FirmaWebServis.FirmaServisClient();
var osobe = new List<Ugovor.Osoba>(
await client.PopisOsobaAsync()
);
...

Prilikom dodavanje reference na servis uobičaljeno je uključena opcija Reuse types in all referenced
assemblies. Ako projekt u kojem se referencira WCF servis ujedno ima referenciran dll ugovora WCF
servisa, onda ne dolazi do stvaranja novih razreda (u primjeru je referenciran Ugovor.dll, pa se koristi
Ugovor.Osoba), a inače se kreiraju razredi istih imena, ali različitog prostora imena oblika
NazivProjekta.NazivReference (npr. PrimjeriPoziva.FirmaWebServis pri čemu je
FirmaWebServis.Drzava ≠ Firma.Drzava).

15.5.5 Korištenje WCF servisa bez dodavanja reference


WCF servis se može koristiti i bez eksplicitnog referenciranja servisa što omogućava da se servis
koristi i nekim drugačijim protokolom osim HTTP-a. Za poziv WCF servisa korištenjem WCF
mehanizama potrebno je referencirati ugovor i definirati pristupnu točku u konfiguracijskoj datoteci
nakon čega se pomoću razreda ChannelFactory može stvoriti komunikacijski kanal s klijentom koji
sadrži sve metode iz ugovora. U sljedećem primjeru dan je primjer kreiranje klijenta za ugovor
(sučelje) Firma.IFirmaServis.

39
Web aplikacija s WCF servisom je u ovom primjeru postavljena na privremenom web serveru kojem je Visual
Studio inicijalno dodijelio slučajno odabrani port, što se po potrebi može promijeniti u projektnoj
konfiguracijskoj datoteci (.csproj)
236
Primjer:  WCF \ PrimjeriPoziva \ FormWCF

private void btnLoad_Click(...) {


string endPointName = ...
using (ChannelFactory<Ugovor.IFirmaServis> factory =
new ChannelFactory<Ugovor.IFirmaServis>(endPointName))
{
Ugovor.IFirmaServis client = factory.CreateChannel();
var osobe = client.PopisOsoba();
...

Razred ChannelFactory parametriziran je sučeljem iz ugovora, a u konstrukturu prima naziv pristupne


točke. Za poslano ime pristupne točke u konfiguracijskoj datoteci treba postojati definicija pristupne
točke koja sadrži podatke o punom nazivu razreda iz ugovora (atribut contract), naziv pristupne točke
(atribut name), adresu servisa (atribut address) i način povezivanja (atribut binding) (npr.
basicHttpBinding, wsHttpBinding, netNamePipeBinding, customBinding, …).

Primjer:  WCF \ PrimjeriPoziva \ app.config

<system.serviceModel>
<client>
<endpoint contract="Ugovor.IFirmaServis"
name="FirmaEndPointWebServis"
address="http://localhost:6093/Firma.svc"
binding="basicHttpBinding" />
<endpoint contract="Ugovor.IFirmaServis"
name="FirmaEndPointLokalno"
address="net.pipe://localhost/FirmaServis"
binding="netNamedPipeBinding" />
</client>
</system.serviceModel>

Postavke WCF klijenta navode se programski ili u konfiguracijskim datotekama. Radi bolje
preglednosti mogu se izdvojiti u posebnu konfiguracijsku datoteku koja se uključi iz datoteke
web.config (npr. <system.serviceModel> <bindings configSource="ime.config" />…). WCF postavke u
konfiguracijskoj datoteci mogu se pregledavati i mijenjati i posebnim alatom koji se može pokrenuti iz
Visual Studia odabirom opcije Tools → WCF Service Configuration Editor.

Slika 107. Grafički alat za pregled i definiranje WCF postavki

U slučaju da se lokacija i način izlaganja WCF servisa promijeni potrebno je samo promijeniti postavke
pristupne točke, što se može ilustrirati primjerom u kojem je smještaj implementacije ugovora unutar

237
Windows aplikacije pri čemu se za povezivanje koristi cjevovod (prikladno ako su i servis i klijent na
istom računalu). Za izlaganje WCF servisa koristi se razred ServiceHost.

Primjer:  WCF \ SmjestajWinApp \ Form1

ServiceHost host =
new ServiceHost(typeof(ImplementacijaUgovora.FirmaServis),
new Uri[] { new Uri("net.pipe://localhost") });

NetNamedPipeBinding binding = new NetNamedPipeBinding();

host.AddServiceEndpoint(typeof(Ugovor.IFirmaServis),
binding, "FirmaServis");
host.Open();

Na klijentu je sada potrebno samo promijeniti parametre pristupne točke, tj. odabrati pristupnu
točku s nekim drugim nazivom u konfiguracijskoj datoteci (u ovom slučaju FirmaEndPointLokalno
umjesto FirmaEndPointWebServis).

15.5.6 WCF i serijalizacija podataka


WCF za serijalizaciju u XML koristi razred DataContractSerializer, a serijaliziraju se sve javne varijable i
javna svojstva za koja je omogućeno čitanje i pisanje. Za odabir elemenata za serijalizaciju i
izostavljanje elemenata za serijalizaciju koriste se dva suprotna principa opt-out te opt-in. Princip opt-
out podrazumijeva da se serijaliziraju svi elementi koji se mogu serijalizirati, a izostavljanje pojedinog
svojstva ili varijable vrši se upotrebom atributa IgnoreDataMember. Ako se za razred postavi atribut
DataContract tada se serijaliziraju samo oni članovi koji imaju atribut DataMember (princip opt-in).
Sljedeći primjer prikazuje ova dva pristupa na istom razredu tako da u konačnici sadržaj serijaliziranih
podataka o mjestima bude identičan bez obzira koristi li se prva ili druga varijanta. Prilikom izrade
WCF servisa preporuka je koristiti atribut DataContract i princip opt-in.

Primjer:  WCF \ Ugovor \ Mjesto.cs

public class Mjesto{


public int Pbr { get; set; }
[IgnoreDataMember]
public string NazMjesta { get; set; }
public string NazDrzave { get; set; }
public string PostNazMjesta { get; set; }
}

[DataContract]
public class Mjesto {
[DataMember]
public int Pbr { get; set; }
public string NazMjesta { get; set; }
[DataMember]
public string NazDrzave { get; set; }
[DataMember]
public string PostNazMjesta { get; set; }
}

Posebnu pažnju treba obratiti na situacije kada stvarni tipovi povratnih vrijednosti nisu unaprijed
poznati kao što se slučaj u situacijama kada postupak servisa za rezultat ima tip sučelja ili neki od
baznih razreda ili Object kao vršni razred.

238
Konkretna implementacija servisa tada može vratiti izvedeni razred ili implementaciju sučelja, ali
klijent primljeni podatak ne bi mogao ispravno deserijalizirati jer ne bi imao informaciju o tipu
podataka koji odgovara serijaliziranim podacima. Za takve slučajeve potrebno je najaviti moguće
povratne tipove (osim ako se ne radi o primitivnim tipovima). Navođenje tipova vrši se korištenjem
atributa KnownType i ServiceKnownType pri čemu se navode svi poznati izvedeni razredi nekog
razreda ili sučelja. Atribut KnownType stavlja se ispred povratnog tipa, a atribut ServiceKnownType
ispred postupka servisa.
U sljedećem primjeru prikazano je navođenje svih izvedenih tipova iz razreda Partner ispred postupka
koji vraća popis partnera.

Primjer:  WCF \ Ugovor \ IFirmaServis.cs

[OperationContract]
[ServiceKnownType(typeof(Osoba))]
[ServiceKnownType(typeof(Tvrtka))]
List<Partner> PopisPartnera();

Alternativa navođenju svih poznatih tipova unutar atributa (Service)KnownType je napisati razred i
postupak koji vraća sve poznate tipove podatka, a u atributu ServiceKnowType navesti naziv
postupka koji vraća tip izvedenog razreda. Praktična alternativa može biti i navođenje poznatih tipova
u konfiguracijskog datoteci kao u donjem primjeru čime se izbjegava potreba za izmjenom izvornog
koda servisa. Poznate tipove je u tom slučaju potrebno navesti u konfiguracijskim datotekama i na
serveru i na klijentu.

Primjer:

<system.runtime.serialization>
<dataContractSerializer>
<declaredTypes>
<add type="Ugovor.Partner,Ugovor">
<knownType type="Ugovor.Osoba,Ugovor" />
<knownType type="Ugovor.Tvrtka,Ugovor" />
</add>
</declaredTypes>
</dataContractSerializer>
</system.runtime.serialization>

15.5.7 Iznimke (pogreške) u radu s WCF servisima


Pri komunikaciji s WCF servisom moguće je nekoliko tipova pogrešaka (iznimki). U slučaju da su
postavke klijenta neispravne, primjerice ako ne postoji definicija pristupna točka, doći će do iznimke
InvalidOperationException. Tijekom komunikacije može se dogoditi da načini povezivanja nisu
usklađeni (npr. servis je izložen koristeći način povezivanja basichttpbinding, a klijent je pretpostavio
da je povezivanje po WS-standardu), što izaziva iznimku CommunicationException. Ako servis nije
dostupan dogodit će se iznimka EndpointNotFoundException. Ako se na servisu dogodi nauhvaćena ili
nenavljena iznimka pa postupak servisa ne završni ispravno klijent će dobiti iznimku FaultException.
Iznimke FaultException i EndpointNotFoundException izvedene su iz razreda
CommunicationException.
U situacijama kada se može očekivati iznimka na servis, u definiciji postupka servisa moguće je
najaviti tip pogreške koji se očekuje i to korištenjem atributa FaultContract.

239
Primjer:  WCF \ Ugovor \ IFirmaServis.cs

[OperationContract]
[FaultContract(typeof(Pogreska))]
string PostupakSPogreskom(VrstaPogreske vrsta);

Za bacanje iznimke s predviđenom pogreškom koristi se FaultException kao u sljedećem primjeru.

Primjer:  WCF \ ImplementacijaUgovora \ FirmaServis.cs

throw new FaultException<Pogreska>(


new Pogreska { OpisPogreske = "Neki opis pogreške..." })

Dodatne mogućnosti obrade pogrešaka mogu se postići korištenjem sučelja IErrorHandler. Primjer
klijenta koji ispravno rukuje iznimkama prikazan je sljedećim programskim odsječkom.

Primjer:  WCF \ PrimjeriPoziva \ FormWCF.cs

try{
using (ChannelFactory<Ugovor.IFirmaServis> factory = new
ChannelFactory<Ugovor.IFirmaServis>(endPointName)) {

Ugovor.IFirmaServis client = factory.CreateChannel();


...
}
}
catch (FaultException<Pogreska> fep){
... fep.Detail.OpisPogreske ...
}
catch (FaultException fexc){
...
}
catch (EndpointNotFoundException epexc){
...
}
catch (CommunicationException commexc){
...
}
catch (InvalidOperationException invexc) {
...
}

15.5.8 Postavke povezivanja


Inicijalnim postavkama količina podataka koja predstavlja rezultat poziva servisa ograničena je na
65536 okteta. Postavke povezivanja moguće je modificirati u konfiguracijskoj datoteci unutar
elementa serviceModel → bindings → tippovezivanja definiranjem imenovanih postavki povezivanja s
promijenjenim vrijednostima. Dodatno, u definiciji pristupne točke osim tipa povezivanja navodi se i
naziv konfiguracije koja predstavlja promjenu inicijalnih postavki.

240
Primjer:  WCF \ PrimjeriPoziva \ app.config

<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="Prilagodjeno" maxBufferPoolSize="5000000"
maxReceivedMessageSize="5000000"/>
</basicHttpBinding>
</bindings>
<client>
<endpoint name="FirmaEndPointWebServis"
contract="Ugovor.IFirmaServis"
address="http://localhost:6093/Firma.svc"
binding="basicHttpBinding" bindingConfiguration="Prilagodjeno"
/>

15.6 REST servisi


Naziv REST ja skraćenica od engleskog naziva Representational State Transfer koji predstavlja
alternativu SOAP protokolu, ali ne kao protokol, već kao obrazac (način) izrade i poziva servisa.
Ključna razlika u odnosu na SOAP protokol je što se slanjem SOAP poruke na pristupnu točku prvo
provjerava sadržaj poruke te se na osnovu sadržaja određuje postupak koji se treba izvršiti, dok se
kor REST servisa ciljani postupak određuje na osnovu URL-a i HTTP metode (GET, POST, DELETE, PUT).
Prednost ovakvog pristupa je veći broj potencijalnih klijenata, jer je dovoljna podrška samo za HTTP
te XML ili JSON (JavaScript Object Notation) te nije potreno implementirati složene WS-* standarde.
Postupke koji predstavljaju dohvat podatka lakše je cacheirati, jer adresa jednoznačnom određuje
podataka. Dodatno, poruke koje se šalju i primaju od servisa su manje, jer se koristi JSON ili POX
(Plain old XML) – XML poruka bez SOAP zaglavlja. Nedostatak REST-a je nepostojanje WSDL što
uzrokuje veću mogućnost nepravilne poruke zbog neispravnog formiranja poruke. Ovaj problem se
ublažava ugrađenim razredima za serijalizaciju u JSON ili XML.

15.6.1 REST i vrste HTTP poziva


Vrste HTTP poziva su GET, POST, PUT i DELETE. GET služi za dohvat podataka pri čemu bi REST servis
treba vratiti rezultat (tj. traženi podataka) ili HTTP status 404. POST služi za kreiranje novog podatka,
a servis bi za uspješno stvoreni podatak trebao vratiti HTTP status 201. PUT služi za ažuriranje
podatka. Nakon uspješnog ažuriranja servis vraća HTTP status 200 ili 204. DELETE služi za brisanje
podatka. Ako je podatak uspješno obrisan ili je već ranije bio obrisan servis treba vratiti HTTP status
200 ili 204.

15.6.2 REST i WCF


Postojeći WCF servis može se paralelno koristiti i kao REST servis dodavanjem određenih atributa
iznad postupaka servisa u ugovoro servisa i korištenjem načina povezivanja webHttpBinding. Atributi
koji se trebaju koristiti su WebGet za GET zahtjeve i WebInvoke za ostale zahtjeve i nalaze se u
prostoru imena System.ServiceModel.Web. Atribut WebGet definira putanju do postupka i način
zadavanja parametara pri čemu parametri navedeni u putanji moraju biti tipa string.

241
Primjer:  WCF \ Ugovor \ IFirmaServis.cs

[OperationContract]
[WebGet(UriTemplate = "GetOsoba/{idOsobe}",
BodyStyle=WebMessageBodyStyle.Wrapped)]
Osoba DohvatiOsobu(string idOsobe);

U prethodnom primjeru, uz pristupnu točku na adresi rest (vidi definiciju pristupne točke u primjeru
ispod) pozivom url-a http://localhost:6093/Firma.svc/rest/GetOsoba/10 izvršit će se postupak
DohvatiOsobu za osobu s id-om osobe 10. Svojstvo BodyStyle određuje da li će rezultat biti dodatno
ugniježđen unutar xml ili json elementa.
Ako se u se u konfiguracijskoj datoteci ne navede niti jedna pristupna točka podrazumijeva se
pristupna točka s načinom povezivanja basicHttpBinding. Navođenjem bilo koje druge pristupne
točke ova podrazumijevana vrsta povezivanja se gubi te je po potrebi treba eksplicitno definirati. Ista
implementacija servisa može biti izložena na više načina povezivanja, ali na različitim adresama. U
konfiguracijskoj datoteci navodi se naziv razreda implementacije te sve pristupne točke. Za svaku
točku se navodi relativna adresa, razred ugovora i način povezivanja te opcionalno naziv postavki
ponašanja (behaviorConfiguration). Za pristupnu točku za koju adresa nije navedena koristi se prazni
string.

Primjer:  WCF \ SmjestajWebApp \ web.config

<services>
<service name="ImplementacijaUgovora.FirmaServis">
<endpoint binding="basicHttpBinding"
contract="Ugovor.IFirmaServis"/>
<endpoint address="rest" binding="webHttpBinding"
contract="Ugovor.IFirmaServis"
behaviorConfiguration="restBehaviour" />
</service>
</services>

Definiranje ponašanja (engl. behavior) omogućava podešavanje rada sa sjednicom, istovremenim


pristupom, transakcijama itd. Ponašanje se može definirati:
 na razini servisa (Service behavior) - npr. objavljivanje meta podataka, životni vijek servisa
(po sjednici, po pozivu, jedan za sve pozive), itd.
 na razini postupka (Operation behavior) - npr. impersonacija, transakcije, životni vijek objekta
 na razini ugovora (Contract behavior)
 na razini pristupne točke (Endpoint behavior) - npr. upravljanje vjerodajnicama klijenta, način
serijalizacije itd.
Primjer upotrebe ponašanja pristupne točke može se ilustrirati definiranjem izlaznog formata REST
servisa (JSON ili Xml) ili automatskim odabirom izlaznog formata na osnovu vrijednosti
Accept(Header) u zaglavlju pozivajuće poruke.

242
Primjer:  WCF \ SmjestajWebApp \ web.config

<endpointBehaviors>
<behavior name="restBehaviour">
<!-- <webHttp defaultOutgoingResponseFormat="Json" /> -->
<webHttp automaticFormatSelectionEnabled="true" />
<webHttp helpEnabled="true"/>
</behavior>
</endpointBehaviors>

Postavljanjem vrijednost helpEnabled na true u postavkama ponašanja servisa automatski se generira


stranica s popisom postupaka servisa koja je dostupna sa sufiksom /help u odnosu na adresu
pristupne točke REST verzije servisa. Opis servisa može se navesti u atributu Description, a ako nije
naveden kao opis se nudi adresa servisa.

Slika 108. Primjer automatski generirane stranice s popisom metoda servisa

Za ostale zahtjeve osim GET, koristi se atribut WebInvoke kojim se može definirati putanja do
postupka i parametri koji se prenose kroz adresu postupka40 (atribut UriTemplate), vrsta metode
HTTP zahtjeve (atribut Method pretpostavljane vrijednosti POST), format ulaza i izlaza servisa
(atributi RequestFormat i ResponseFormat) ako je potrebno da budu drugačiji od ostalih postupka i
postavki u konfiguraijskoj datoteci te stil odgovara (atribut BodyStyle) kojim se određuje da li je
rezultat dodatno omotan u jedan vršni element ili ne.

Primjer:  WCF \ Ugovor \ IFirmaServis.cs

[OperationContract]
[WebInvoke(UriTemplate="UpdateOsoba/{idOsobe}", Method = "PUT",
BodyStyle = WebMessageBodyStyle.Bare)]
Osoba AzurirajOsobu(string idOsobe, Osoba o);

40
Poruka servisa, tj. ostali podaci su sadržani u POST ili PUT dijelu zahtjeva
243
15.6.3 Primjer poziva REST servisa iz .NET klijenta
Za poziv REST servisa iz .NET klijenta koristi se razred HttpClient te proširanja iz assemblyja
System.Net.HttpFormatting za automatsku deserijalizaciju iz XML-a i JSON-a u željeni razred i
obrnuto. Razred HttpClient nudi asinkrone metode za komunikaciju s web stranicom ili web servisom
i to za sve 4 vrste HTTP zahtjeva: GetAsync, PostAsync, PutAsync, DeleteAsync, a moguća je i
automatska serijalizacija u XML ili JSON kod PUT i POST zahtjeva metodama PostAsJsonAsync,
PostAsXmlAsync, PutAsJsonAsync, PutAsXmlAsync te kod GET zahtjeva metoda ReadAsAsnyc. U
sljedećem primjeru prikazani su odsječak koda za dohvat popisa osoba pri čemu klijent želi rezultat u
JSON formatu te odsječak koda za ažuriranje podatka o osobi.

Primjer:  WCF \ PrimjeriPoziva \ FormREST

private async void btnLoad_Click... {


string serviceUrl = tbServiceUrl.Text + "/PopisOsoba";
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Accept.Clear();
...
client.DefaultRequestHeaders.Accept.Add(new
MediaTypeWithQualityHeaderValue("application/json"));

var response = await client.GetAsync(serviceUrl);


var osobe = await response.Content
.ReadAsAsync<IEnumerable<Ugovor.Osoba>>();
osobaBindingSource.DataSource = osobe;
...

private async void btnUpdate_Click... {


var osoba = osobaBindingSource.Current as Ugovor.Osoba;
string serviceUrl = tbServiceUrl.Text + "/UpdateOsoba";
HttpClient client = new HttpClient();
HttpResponseMessage response = await
client.PutAsJsonAsync(serviceUrl, osoba);
...

15.6.4 Alati za inspekciju prometa i stvaranje HTTP zahtjeva


Za inspekciju HTTP prometa i stvaranje HTTP zahtjeva, pa posljedično i za testiranje web servisa,
može se koristiti više alata od kojih se kao jednostavni i praktični izdavajaju Fiddler kao zaseban alat
koji preusmjerava promet preko svoje pristupne točke (ipv4.fiddler umjesto localhost) te Postman
kao ekstenzija za preglednik Chrome.

244
Slika 109. Primjer rada s alatom Fiddler

Slika 110. Primjer rada s proširenjem Postman za preglednik Chrome

15.6.5 WebApi
WebApi predstavlja okvir za razvoj REST servisa. Struktura projekta nalik je ASP.Net MVC-u te ima
postavke usmjeravanja i upravljače pri čemu su API upravljači izvedeni iz razreda ApiController.

245
Primjer:  WCF \ WebApi \ App_Start \ WebApiConfig.cs

config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);

Primjer:  WCF \ WebApi \ Controllers \ OsobaController.cs

public class OsobaController : ApiController


//GET api/<controller>
public IEnumerable<WebApi.Models.Osoba> Get()
public IHttpActionResult Get(int id)
public void Post(...
public void Put(int id, [FromBody]WebApi.Models.Osoba o)
public void Delete(int id)

U odnosu na WCF WebAPI je prikladan za sve preglednike i mobilne uređaje i nudi veću podršku za
REST, primjerice naprednije mogućnosti formiranja URL-a. Dodatno, izrada REST servisa je
jednostavnija s WebAPI-em. Prednost WCF-a nad WebAPI-jem je podrška za više protokola, a ne
samo HTTP i mogućnosti definiranja vlastitog protokola. WCF podržava više formata sadržaja (i tekst i
binarni sadržaj), nudi naprednije sigurnosne mehanizme. Dodatno WCF se jednostavnim dodavanjem
atributa može koristiti istovremenom i kao WCF i kao REST servis, pri čemu se proxy klijent WCF
servisa može automatski generirati na osnovu WSDL-a.

15.7 Zadaci
Zadatak 1. Što je proxy?
Zadatak 2. Koje protokole povezivanja podržava WCF?
Zadatak 3. Što je posebnost/prednost REST servisa?

246
16 Reference

16.1 Literatura
1. A Guide to the Project Management Body of Knowledge (PMBOK® Guide) , 5th edition,
Project Management Institute, 2013.
2. Code Complete, 2nd edition, Steve McConnell, Microsoft Press, 2004.
3. Configuration Management Best Practices, Aiello & Sachs, Addison-Wesley, 2010.
4. Effective Project Management: Traditional, Adaptive, Extreme 7th ed., Wysocki, R. K. , Wiley,
2014.
5. Inside C#, Tom Archer 2ed, Microsoft Press, 2002.
6. ISO/IEC/IEEE 24765:2010 Systems and software engineering—Vocabulary
7. ISO/IEC/IEEE 29148:2011 Systems and software engineering —Life cycle processes--
Requirements engineering
8. Professional Application Lifecycle Management with Visual Studio® 2013, Gousset et.al, John
Wiley & Sons, Inc., 2014.
9. Software Engineering: A Practitioner's Approach, 7th edition, Pressman R. S., R. S. Pressman
& Associates, Inc., 2010.
10. Software Engineering 10ed, Sommerville I., Pearson, 2015.

16.2 Korisne poveznice


1. http://www.codeguru.com
2. http://www.codeproject.com
3. http://www.csharphelp.com
4. http://www.c-sharpcorner.com
5. http://www.csharp-station.com
6. https://msdn.microsoft.com
7. http://sourceforge.net
8. http://www.ssw.uni-linz.ac.at/Teaching/Lectures/CSharp/Tutorial/
9. http://www.pmi.org
10. http://www.pmi-croatia.hr

247

You might also like