Documente Academic
Documente Profesional
Documente Cultură
16
Mod Descriere
Dintre șabloanele enumerate în tabelul 16-7, EditItemTemplate este unul dintre cele mai utile, deoarece vă
oferă posibilitatea de a controla experiența de editare pentru câmp. Dacă nu utilizați câmpuri șablon, sunteți
limitat la casetele de text obișnuite și nu veți avea nicio validare. Vizualizarea grilă definește, de asemenea,
două șabloane pe care le puteți utiliza în afara oricărei coloane. Acestea sunt PagerTemplate, care vă
permite să personalizați aspectul controalelor pager și EmptyDataTemplate, care vă permite să setați
conținutul care ar trebui să apară dacă GridView este legat de un obiect de date gol.
566
CAPITOLUL CONTROALELE DATELOR
16 •
Problema este că, dacă adăugați un control la un șablon, GridView creează mai multe copii ale acelui
control, câte una pentru fiecare element de date. Când se face clic pe ImageButton, aveți nevoie de o
modalitate de a determina pe ce imagine s-a făcut clic și cărui rând îi aparține.
567
CAPITOLUL CONTROALELE DATELOR
16 •
Modul de rezolvare a acestei probleme este să utilizați un eveniment din GridView, nu butonul conținut. Evenimentul
GridView.RowCommand servește acestui scop, deoarece se declanșează ori de câte ori se face clic pe orice buton în
orice șablon. Acest proces, în care un eveniment de control dintr-un șablon este transformat într-un eveniment în
controlul care conține, se numește barbotare eveniment.
Desigur, aveți nevoie în continuare de o modalitate de a transmite informații evenimentului RowCommand pentru a
identifica rândul în care a avut loc acțiunea. Secretul constă în două proprietăți de șir pe care le oferă toate controalele
butoanelor: CommandName și CommandArgument. CommandName setează un nume descriptiv pe care îl puteți utiliza
pentru a distinge clicurile pe ImageButton de clicurile pe alte controale de buton din GridView. CommandArgument
furnizează un fragment de date specifice rândului pe care le puteți utiliza pentru a identifica rândul pe care s-a făcut clic.
Puteți furniza aceste informații utilizând o expresie de legare a datelor.
Iată un câmp șablon care conține eticheta revizuită ImageButton:
Iată codul de care aveți nevoie pentru a răspunde atunci când faceți clic pe un buton Image:
Editarea cu un șablon
Unul dintre cele mai bune motive pentru a utiliza un șablon este de a oferi o experiență de editare mai bună. În capitolul
anterior, ați văzut cum GridView oferă capacități de editare automată - tot ce trebuie să faceți este să comutați un rând în
modul de editare setând proprietatea GridView.EditIndex. Cel mai simplu mod de a face acest lucru posibil este să
adăugați o coloană CommandField cu butonul ShowEditButton setat la true. Apoi, utilizatorul trebuie doar să facă clic pe
un link din rândul corespunzător pentru a începe editarea acestuia. În acest moment, fiecare etichetă din fiecare coloană
este înlocuită cu o casetă text (cu excepția cazului în care câmpul este doar în citire).
Suportul standard de editare are mai multe limitări:
Nu este întotdeauna adecvat să editați valori utilizând o casetă text: Anumite tipuri de date sunt gestionate cel mai bine cu
alte controale (cum ar fi listele verticale). Câmpurile mari au nevoie de casete de text cu mai multe linii și așa mai departe.
Nu primiți nicio validare: Ar fi bine să restricționați posibilitățile de editare, astfel încât cifrele valutare să
nu poată fi introduse ca numere negative, de exemplu. Puteți face acest lucru adăugând controale
validatoare la un EditItemTemplate.
Aspectul vizual este adesea urât: un rând de casete de text dintr-o grilă ocupă prea mult spațiu și
rareori pare profesional.
Într-o coloană șablon, nu aveți aceste probleme. În schimb, definiți în mod explicit controalele de editare și
aspectul lor utilizând EditItemTemplate. Acesta poate fi un proces oarecum laborios.
568
CAPITOLUL CONTROALELE DATELOR
16 •
Iată coloana șablon utilizată anterior pentru informații despre stoc cu un șablon de editare:
Când legați o valoare editabilă la un control, trebuie să utilizați metoda Bind() în expresia de legare a datelor
în locul metodei obișnuite Eval(). Spre deosebire de metoda Eval(), care poate fi plasată oriunde într-o
pagină, metoda Bind() trebuie utilizată pentru a seta o proprietate de control. Numai metoda Bind() creează
legătura bidirecțională, asigurându-se că valorile actualizate vor fi returnate la server.
569
CAPITOLUL CONTROALELE DATELOR
16 •
Un detaliu interesant aici este că, deși șablonul de element afișează trei câmpuri, șablonul de editare permite
modificarea doar a unuia dintre acestea. Când GridView comite o actualizare, va trimite numai parametrii limitați,
editabili. În exemplul anterior, aceasta înseamnă că GridView va trece înapoi
un parametru @ReorderLevel, dar nu un parametru @UnitsInStock sau @UnitsOnOrder. Acest lucru este
important, deoarece atunci când scrieți comanda de actualizare parametrizată, aceasta trebuie să utilizeze
numai parametrii pe care îi aveți la dispoziție. Iată controlul SqlDataSource modificat cu comanda corectă:
<asp:SqlDataSource ID="sourceProducts" runat="server" ConnectionString="<%$
ConnectionStrings:Northwind %>" SelectCommand="SELECT ProductID, ProductName,
UnitPrice, UnitsInStock, UnitsOnOrder,ReorderLevel FROM Products"
UpdateCommand="UPDATE Products SET ProductName=@ProductName,
UnitPrice=@UnitPrice, ReorderLevel=@ReorderLevel WHERE ProductID=@ProductID">
</asp:SqlDataSource>
Editare cu validare
Acum că aveți șablonul pregătit, de ce să nu adăugați o bibelouri suplimentare, cum ar fi un validator, pentru
a prinde greșelile de editare? În exemplul următor, un RangeValidator previne modificările care pun
ReorderLevel la mai puțin de 0 sau mai mult de 100:
<asp:TemplateField HeaderText="Status"> <ItemStyle width="100px"
/> <ItemTemplate> <b>In stoc:</b> <%# Eval("UnitsInStock") %><br
/> <b>La comanda:</b> <%# Eval("UnitsOnOrder") %><br />
<b>Reorder:</b> <%# Eval("ReorderLevel") %> </ItemTemplate>
<EditItemTemplate> <b>In stoc: </b> <%# Eval("UnitsInStock")
%><br /> <b>La comanda:</b> <%# Eval("UnitsOnOrder") %><br
/><br /> <b>Reordonare:</b>
Figura 16-15 prezintă validarea la locul de muncă. Dacă valoarea nu este validă, browserul nu permite ca
pagina să fie postată înapoi și nu rulează niciun cod de bază de date.
570
CAPITOLUL CONTROALELE DATELOR
16 •
Notă SqlDataSource este suficient de inteligent pentru a gestiona validarea în mod corespunzător, chiar dacă ați dezactivat validarea pe partea client (sau browserul nu o acceptă). În această situație, pagina este postat înapoi, dar SqlDataSource observă că conține date nevalide (inspectând proprietatea Page.IsValid) și nu încearcă să efectueze actualizarea. Pentru mai multe informații despre validarea pe partea client și pe partea server, consultați capitolul 9.
571
CAPITOLUL CONTROALELE DATELOR
16 •
În șablonul de editare a elementului, aveți nevoie de încă două butoane cu valorile CommandName ale Update și Cancel:
<asp:TextBox Text='<%# Bind("ReorderLevel") %>' width="25px" runat="server" id="txtReorder" /> <br /><br />
</EditItemTemplate>
Observați că butonul Anulare trebuie să aibă proprietatea CausesValidation setată la false pentru a ocoli
validarea. În acest fel, puteți anula editarea chiar dacă datele curente nu sunt valide.
Atâta timp cât utilizați aceste nume, evenimentele de editare GridView se vor declanșa, iar controalele sursei
de date vor reacționa în același mod ca și cum ați utiliza controalele de editare generate automat. Figura
16-16 prezintă butoanele de editare personalizate.
572
CAPITOLUL CONTROALELE DATELOR
16
DetailsView și FormView
GridView excelează la afișarea unui tabel dens cu mai multe rânduri de informații. Cu toate acestea, uneori doriți să oferiți o
privire detaliată asupra unei singure înregistrări. Puteți găsi o soluție utilizând o coloană șablon într-un GridView, dar ASP.NET
include două controale care sunt adaptate în acest scop: DetailsView și FormView. Ambele afișează o singură înregistrare la un
moment dat, dar pot include butoane pager opționale care vă permit să parcurgeți o serie de înregistrări (afișând una pe
pagină). Ambele vă oferă o modalitate simplă de a insera o înregistrare nouă, pe care GridView nu o permite. Și ambele
acceptă șabloane, dar FormView le solicită. Aceasta este distincția esențială dintre cele două controale.
O altă diferență este faptul că DetailsView își redă conținutul într-un tabel, în timp ce FormView vă oferă
flexibilitatea de a vă afișa conținutul fără tabel. Astfel, dacă intenționați să utilizați șabloane, FormView vă
oferă cea mai mare flexibilitate. Dar dacă doriți să evitați complexitatea șabloanelor, DetailsView vă oferă un
model mai simplu, care vă permite să construiți o afișare de date cu mai multe rânduri din obiecte de câmp,
în același mod în care GridView este construit din obiecte coloană. Acum că înțelegeți caracteristicile
GridView, puteți ajunge la curent cu DetailsView și FormView destul de repede. Acest lucru se datorează
faptului că ambele împrumută o parte din modelul GridView.
DetaliiVezi
Vizualizarea detaliilor afișează o singură înregistrare la un moment dat. Plasează fiecare câmp într-un rând separat
al unui tabel. Ați văzut în capitolul 15 cum să creați o vizualizare de bază a detaliilor pentru a afișa înregistrarea
selectată în prezent. DetailsView vă permite, de asemenea, să treceți de la o înregistrare la alta utilizând controale
de paginare, dacă ați setat proprietatea AllowPaging la true. Puteți configura controalele de paginare utilizând
proprietățile PagerStyle și PagerSettings în același mod în care modificați pagerul pentru GridView. Figura 16-17
prezintă DetailsView atunci când este legat la un set de înregistrări de produse, cu informații complete despre
produs.
573
CAPITOLUL CONTROALELE DATELOR
16
Este tentant să utilizați comenzile pagerului DetailsView pentru a crea un browser de înregistrări la îndemână.
Din păcate, această abordare poate fi destul de ineficientă. O problemă este că este necesară o postback
separată de fiecare dată când utilizatorul trece de la o înregistrare la alta (în timp ce un control grilă poate
afișa mai multe înregistrări pe aceeași pagină). Dar adevăratul dezavantaj este că de fiecare dată când
pagina este postată înapoi, setul complet de înregistrări este preluat, chiar dacă este afișată o singură
înregistrare. Acest lucru duce la o muncă suplimentară inutilă pentru serverul bazei de date. Dacă alegeți să
implementați o pagină de browser de înregistrări cu DetailsView, trebuie să activați memorarea în cache
pentru a reduce lucrul în baza de date (consultați capitolul 23).
Sfat Este aproape întotdeauna o idee mai bună să utilizați un alt control pentru a permite utilizatorului să aleagă o anumită înregistrare (de exemplu, alegând un ID dintr-o casetă listă), apoi să afișați înregistrarea completă în Vizualizare detalii utilizând o comandă parametrizată care se potrivește doar cu înregistrarea selectată. Capitolul 15 demonstrează această tehnică.
Definirea câmpurilor
DetailsView utilizează reflexia pentru a genera câmpurile pe care le afișează. Aceasta înseamnă că examinează obiectul
de date și creează un rând separat pentru fiecare câmp pe care îl găsește, la fel ca GridView. Puteți dezactiva această
generare automată a rândurilor setând AutoGenerateRows la false. Apoi, depinde de dvs. să declarați informațiile pe
care doriți să le afișați.
Interesant este că utilizați aceleași etichete de câmp pentru a construi un DetailsView pe care îl utilizați pentru a proiecta un
GridView. De exemplu, câmpurile din elementul de date sunt reprezentate cu eticheta BoundField, butoanele pot fi create cu
ButtonField și așa mai departe. Pentru lista completă, consultați tabelul anterior 16-1.
Următorul cod definește un DetailsView care afișează informații despre produs. Această etichetă creează
aceeași grilă de informații prezentată în Figura 16-17, când AutoGenerateRows a fost setată la true.
</asp:DetailsView>
Puteți utiliza eticheta BoundField pentru a seta proprietăți precum textul antetului, șirul de formatare,
comportamentul de editare etc. (consultați tabelul 16-2). În plus, puteți utiliza proprietatea ShowHeader.
Când este fals, textul antetului este lăsat în afara rândului, iar datele câmpului ocupă ambele celule.
574
CAPITOLUL CONTROALELE DATELOR
16
Sfat În loc să codificați manual fiecare câmp, puteți utiliza aceeași comandă rapidă pe care ați utilizat-o cu GridView. Pur și simplu selectați controlul la momentul proiectării și selectați Reîmprospătare schemă din eticheta inteligentă.
Modelul de câmp nu este singura parte a GridView adoptată de controlul DetailsView. De asemenea, utilizează un
set similar de stiluri, un set similar de evenimente și un model de editare similar. Singura diferență este că, în loc să
creați o coloană dedicată pentru editarea controalelor, setați pur și simplu una dintre proprietățile booleene ale
DetailsView, cum ar fi AutoGenerateDeleteButton, AutoGenerateEditButton și AutoGenerateInsertButton. Linkurile
pentru aceste activități sunt adăugate în partea de jos a Vizualizării detalii. Atunci când adăugați sau editați o
înregistrare, DetailsView utilizează controale standard ale casetei text (consultați Figura 16-18), la fel ca GridView.
Pentru mai multă flexibilitate de editare, veți dori să utilizați șabloane cu DetailsView (adăugând un TemplateField
în loc de BoundField) sau cu controlul FormView (așa cum este descris în continuare).
FormularVizualizare formular
Dacă aveți nevoie de flexibilitatea maximă a șabloanelor, FormView oferă un control doar pentru șabloane pentru
afișarea și editarea unei singure înregistrări.
Frumusețea modelului șablon FormView este că se potrivește destul de îndeaproape cu modelul
TemplateField din GridView. Aceasta înseamnă că puteți lucra cu următoarele șabloane:
575
CAPITOLUL CONTROALELE DATELOR
16
• ItemTemplate
• EditItemTemplate
• InsertItemTemplate
• FooterTemplate
• HeaderTemplate
• EmptyDataTemplate
• PagerTemplate
Notă Spre deosebire de GridView și DetailsView, care vă permit să adăugați câte obiecte TemplateField doriți, FormView permite doar o singură copie a fiecărui șablon. Dacă doriți să afișați mai multe valori, trebuie să adăugați mai multe expresii de legare la același ItemTemplate.
Puteți utiliza același conținut șablon pe care îl utilizați cu un ȘablonCâmp într-o Vizualizare grilă din
FormularVizualizare. Mai devreme în acest capitol, ați văzut cum puteți utiliza un câmp șablon pentru a
combina informațiile despre stocul unui produs într-o singură coloană (așa cum se arată în Figura 16-12). Iată
cum puteți utiliza același șablon în FormularVizualizare:
<asp:FormView ID="FormView1" runat="server" DataSourceID="sourceProducts">
<ItemTemplate> <b>In stoc:</b> <%# Eval("UnitsInStock") %> <br /> <b>La
comanda:</b> <%# Eval("UnitsOnOrder") %> <br /> <b>Reorder:</b> <%#
Eval("ReorderLevel") %> <br /> </ItemTemplate> </asp: FormularVizualizare>
La fel ca DetailsView, FormView poate afișa o singură înregistrare la un moment dat. (Dacă sursa de date are
mai multe înregistrări, o veți vedea doar pe prima.) Puteți rezolva această problemă setând proprietatea
AllowPaging la true, astfel încât linkurile de paginare să fie create automat. Aceste linkuri permit utilizatorului
să treacă de la o înregistrare la alta, ca în exemplul anterior cu DetailsView. O altă opțiune este să vă asociați
la o sursă de date care returnează o singură înregistrare. Figura 16-19 prezintă un exemplu în care un control
listă verticală vă permite să alegeți un produs, iar o a doua sursă de date afișează înregistrarea
corespunzătoare în controlul FormView.
576
CAPITOLUL CONTROALELE DATELOR
16
Iată marcajul de care aveți nevoie pentru a defini lista verticală și sursa sa de date:
Vizualizarea formular utilizează șablonul din exemplul anterior (este regiunea umbrită din pagină). Iată
marcajul pentru FormularView (fără a include șablonul) și sursa de date care obține detaliile complete pentru
produsul selectat.
<asp:SqlDataSource ID="sourceProductFull" runat="server"
ConnectionString="<%$ ConnectionStrings:Northwind %>"
SelectCommand="SELECT * FROM Produse UNDE ProductID=@ProductID">
<SelectParameters> <asp:ControlParameter Name="ProductID"
ControlID="lstProducts" PropertyName="SelectedValue" />
</SelectParameters> </asp:SqlDataSource>
</ItemTemplate> </asp:FormView>
577
CAPITOLUL CONTROALELE DATELOR
16
Notă Dacă doriți să acceptați editarea cu FormView, trebuie să adăugați controale de buton care declanșează procesele de editare și actualizare, așa cum este descris în secțiunea "Editarea cu un șablon".
Ultimul cuvânt
În acest capitol, ați luat în considerare tot ce aveți nevoie pentru a construi pagini bogate legate de date. Ați
făcut un tur detaliat al GridView și ați luat în considerare suportul său pentru formatare, selectare, sortare,
paginare, utilizarea șabloanelor și editare. De asemenea, ați luat în considerare DetailsView și FormView,
care vă permit să afișați și să editați înregistrări individuale. Folosind aceste trei controale, puteți construi
pagini all-in-one care afișează și editează date, fără a fi nevoie să scrieți pagini de cod ADO.NET. Cel mai
bun dintre toate, fiecare control al datelor este complet configurabil, ceea ce înseamnă că îl puteți adapta
pentru a se potrivi cu aproape orice aplicație web.
Descărcați de la Wow! eBook <www.wowebook.com>
578
C A P I T O L U L 17
•■■
Fișiere și fluxuri
Există un motiv bun pentru care această carte a acoperit ADO.NET înainte de a se ocupa de tehnici mai simple de acces
la date, cum ar fi scrierea și citirea fișierelor obișnuite. Accesul tradițional la fișiere este, în general, mult mai puțin util
într-o aplicație web decât este într-un program desktop. Bazele de date, pe de altă parte, sunt proiectate de la început
pentru a sprijini un număr mare de utilizatori simultani cu viteză, siguranță și eficiență. Majoritatea aplicațiilor web se vor
baza pe o bază de date pentru unele caracteristici, dar multe nu vor avea niciun motiv să utilizeze accesul direct la
fișiere.
Desigur, dezvoltatorii de ASP.NET întreprinzători pot găsi o utilizare pentru aproape orice tehnologie. Dacă această
carte nu ar acoperi accesul la fișiere, fără îndoială că mulți dezvoltatori ar fi frustrați atunci când proiectează aplicații
web cu utilizări legitime (și inovatoare) pentru fișiere obișnuite. De fapt, accesul la fișiere este atât de ușor și simplu în
.NET încât poate fi perfect pentru soluții simple, la scară mică, care nu au nevoie de un produs de bază de date cu
drepturi depline, cum ar fi SQL Server.
Acest capitol explică modul în care puteți utiliza clasele din .NET pentru a citi și modifica informațiile
sistemului de fișiere și chiar pentru a construi un browser de fișiere simplu. Veți învăța, de asemenea, cum
să creați fișiere text simple și binare proprii. În cele din urmă, veți lua în considerare modul în care puteți
permite utilizatorilor să încarce propriile fișiere pe serverul dvs.
Limitări privind denumirea fișierelor: Când creați un fișier nou, este evident că acesta nu poate avea același nume
ca un fișier existent în același director. Asta înseamnă că probabil va trebui să vă întoarceți la un sistem pentru
generarea aleatorie a numelor fișierelor. De exemplu, puteți crea un nume de fișier pe baza unui număr aleatoriu
combinat cu data și ora curente sau puteți crea un nume de fișier care încorporează un identificator global unic
(GUID). Cu ambele abordări, numele fișierelor ar fi unice din punct de vedere statistic, ceea ce înseamnă că
duplicatele ar fi extrem de puțin probabile. Cu toate acestea, numele fișierelor nu ar fi foarte semnificative. În bazele
de date, această problemă este rezolvată mai bine cu tipul de date cu incrementare automată, care completează
automat un anumit câmp cu un număr unic atunci când creați o înregistrare.
Limitări multiutilizator: Bazele de date relaționale oferă caracteristici precum blocarea și tranzacțiile pentru a
preveni inconsecvențele și pentru a se asigura că mai multe persoane pot utiliza aceleași date în același
timp. Comparativ, sistemul de fișiere al serverului web este jalnic înapoi. Deși puteți permite mai multor
utilizatori să citească un fișier simultan, este aproape imposibil să permiteți mai multor utilizatori să
actualizeze același fișier în același timp fără catastrofă.
Probleme de scalabilitate: Operațiunile de fișiere suferă de unele cheltuieli generale. Într-un scenariu simplu, accesul la
fișiere poate fi mai rapid decât conectarea la o bază de date și efectuarea unei interogări. Dar efectul cumulativ într-o
579
CAPITOLUL FIȘIERE ȘI FLUXURI
17
aplicație web mare este foarte diferit. Când mai mulți utilizatori lucrează cu fișiere în același timp, serverul dvs. web poate
încetini dramatic.
Riscuri de securitate: Dacă permiteți utilizatorului să specifice un nume de fișier sau cale, utilizatorul ar putea
concepe o modalitate de a păcăli aplicația să acceseze sau să suprascrie un fișier de sistem protejat. Chiar și
fără această capacitate, un utilizator rău intenționat sau neglijent ar putea utiliza o pagină ASP.NET care
creează sau încarcă fișiere pentru a umple hard disk-ul serverului web și a-l face să nu mai funcționeze. Toate
aceste probleme pot fi prevenite, dar necesită ceva mai multă muncă decât o soluție bazată pe baze de date.
Desigur, accesul la fișiere are utilizările sale. Poate că trebuie să accesați informațiile pe care o altă aplicație le-a
stocat într-un fișier. Sau poate trebuie să stocați informațiile într-un fișier, astfel încât alte aplicații să le poată
accesa. De exemplu, este posibil să creați o aplicație intranet care permite unui grup mic de angajați de încredere
să încarce și să gestioneze documente. Puteți stoca documentele lor într-un câmp binar într-un tabel de bază de
date, dar acest lucru ar face mai dificilă navigarea și deschiderea acestor fișiere fără a utiliza front-end-ul web.
În aceste situații, veți fi bucuroși să aflați că ASP.NET puteți utiliza toate caracteristicile de acces la fișiere
ale .NET Framework. Asta înseamnă că aplicațiile dvs. web pot explora liber sistemul de fișiere, pot
gestiona fișiere și pot crea fișiere noi cu conținut personalizat.
Cu clasele de acces la fișiere, metodele statice sunt mai convenabile de utilizat, deoarece nu necesită crearea unei instanțe a
clasei. Asta înseamnă că puteți utiliza o instrucțiune rapidă de cod cu o singură linie pentru a efectua o sarcină simplă, cum ar
fi verificarea existenței unui fișier. Pe de altă parte, dacă trebuie să preluați mai multe informații din același fișier sau director,
este mai ușor să utilizați membrii instanței. În acest fel, nu trebuie să continuați să specificați numele directorului sau fișierului
de fiecare dată când apelați o metodă. Abordarea instanței este, de asemenea, puțin mai rapidă în această situație. Acest
lucru se datorează faptului că clasele FileInfo și DirectoryInfo efectuează verificările de securitate o singură dată, atunci când
creați instanța obiectului. Clasele Director și Fișier efectuează o verificare de securitate de fiecare dată când invocați o
metodă, ceea ce adaugă mai multe cheltuieli generale.
Veți afla despre toate aceste clase în acest capitol. Dar mai întâi, merită să faceți un ocol pentru a vă uita la
o altă clasă care poate simplifica codul care se ocupă de sistemul de fișiere: clasa Path.
580
CAPITOLUL FIȘIERE ȘI FLUXURI
17
Clasa de cale
Împreună cu cele cinci clase prezentate în secțiunea anterioară, .NET include, de asemenea, o clasă de ajutor numită
Path în același spațiu de nume System.IO. Clasa Path nu include nicio funcționalitate reală de gestionare a fișierelor.
Pur și simplu oferă câteva metode statice care sunt utile atunci când manipulați șiruri de caractere care conțin căi de
fișiere și directoare.
De exemplu, clasa Path include o metodă GetFileName() care extrage numele fișierului dintr-un șir
complet. Iată un exemplu:
În C #, trebuie să aveți grijă deosebită atunci când creați șiruri de caractere care dețin căi de fișier sau căi de
director. Acest lucru se datorează faptului că în C#, caracterul de separare a directorului (\) are și un alt înțeles -
indică începutul unei secvențe speciale de caractere. Pentru a indica faptul că doriți cu adevărat o bară oblică
inversă și nu o secvență specială de caractere, aveți nevoie de două bare oblice, așa cum se arată aici:
Clasa Traseu include, de asemenea, o metodă Combine() care poate aborda un traseu relativ la capătul
unui traseu absolut. Aici este la lucru, fuzionând două corzi împreună:
Metode Descriere
581
CAPITOLUL FIȘIERE ȘI FLUXURI
17
Metode Descriere
GetDirectoryName() Returnează toate informațiile din director, care este textul dintre primul și ultimul
separator de director (\).
GetFileName() Returnează doar porțiunea de nume de fișier a unui traseu, care este porțiunea
de după ultimul separator de directoare.
GetFileNameFără Returnează doar porțiunea de nume de fișier a unei căi, dar omite extensia de
extensie() fișier la sfârșit.
GetFullPath() Modifică o cale relativă într-o cale absolută utilizând directorul curent. De exemplu,
dacă c:\Temp\ este directorul curent, apelarea GetFullPath() pe un nume de fișier,
cum ar fi test.txt returnează c:\Temp\test.txt. Această metodă nu are niciun efect
asupra unei căi absolute.
IsPathRooted() Returnează true dacă traseul este un traseu absolut și false dacă este un traseu relativ.
Metodă Descriere
CreateDirectory() Creează un director nou. Dacă specificați un director într-un alt director
inexistent, ASP.NET va crea cu grijă toate directoarele necesare.
Există() Returnează true sau false pentru a indica dacă directorul specificat există.
582
CAPITOLUL FIȘIERE ȘI FLUXURI
17
Metodă Descriere
GetDirectories() și Returnează o matrice de șiruri, câte unul pentru fiecare subdirector sau fișier
GetFiles() (în funcție de metoda utilizată) din directorul specificat. Aceste metode pot
accepta un al doilea parametru care specifică o expresie de căutare (cum ar
fi ASP*.*).
GetLogicalDrives() Returnează o matrice de șiruri, câte unul pentru fiecare unitate prezentă
pe computerul curent. Literele de unitate sunt în acest format: "c:\".
GetCurrentDirectory() și Vă permite să setați sau să regăsiți directorul curent, ceea ce este util dacă
SetCurrentDirectory() trebuie să utilizați căi relative în locul căilor complete. În general, aceste
funcții nu sunt necesare.
Mutare() Acceptă doi parametri: calea sursă și calea destinație. Directorul și tot
conținutul său pot fi mutate pe orice cale, atâta timp cât se află pe aceeași
unitate. (Dacă trebuie să mutați fișiere de pe o unitate pe alta, va trebui să
asociați în schimb o operațiune de copiere și o operațiune de ștergere.)
Metodă Descriere
Copiere() Acceptă doi parametri: numele fișierului sursă complet calificat și numele
fișierului destinație complet calificat. Pentru a permite suprascrierea, utilizați
versiunea care preia un al treilea parametru boolean și setați-l la true.
Șterge() Șterge fișierul specificat, dar nu lansează o excepție dacă fișierul nu poate fi
găsit.
GetCreationTime(), Returnează un obiect DateTime care reprezintă ora la care fișierul a fost
GetLastAccessTime() și creat, accesat sau scris ultima dată. Fiecare metodă GetXxx() are o metodă
GetLastWriteTime() SetXxx() corespunzătoare, care nu este afișată în acest tabel.
Mutare() Acceptă doi parametri: numele fișierului sursă complet calificat și numele
fișierului destinație complet calificat. Puteți muta un fișier între unități și chiar
îl puteți redenumi în timp ce îl mutați (sau îl puteți redenumi fără a-l muta).
583
CAPITOLUL FIȘIERE ȘI FLUXURI
17
Clasa Fișier include, de asemenea, câteva metode care vă permit să creați și să deschideți fișiere ca fluxuri. Veți
explora aceste caracteristici în secțiunea "Citirea și scrierea cu fluxuri" din acest capitol. Singura caracteristică care
lipsește clasei File (și clasa FileInfo oferă) este capacitatea de a prelua dimensiunea unui fișier specificat.
Metodele File and Directory sunt destul de intuitive. De exemplu, luați în considerare codul pentru o pagină
simplă care afișează unele informații despre fișierele dintr-un anumit director. Puteți utiliza acest cod pentru a
crea o pagină de administrare simplă care vă permite să revizuiți conținutul unui director FTP (vezi Figura 17-1).
Clienții ar putea folosi această pagină pentru a-și revizui documentele și pentru a elimina fișierele suspecte.
Ar trebui să începeți prin a importa spațiul de nume care are clasele IO:
folosind System.IO;
Codul pentru această pagină este următorul:
584
CAPITOLUL FIȘIERE ȘI FLUXURI
17
vid privat CreateFileList() { // Preluați lista de fișiere și afișați-o în pagină. // Acest cod dezactivează, de asemenea, butonul de
ștergere, asigurându-se că utilizatorul // trebuie să vizualizeze informațiile despre fișier înainte de a-l șterge. string[] fileList =
Directory.GetFiles(ftpDirectory); lstFiles.DataSource = fileList; lstFiles.DataBind(); lblFileInfo.Text = ""; } cmdDelete.Enabled =
fals;
Utilizați StringBuilder pentru cel mai rapid mod de a construi șirul. string fileName =
lstFiles.SelectedItem.Text; System.Text.StringBuilder displayText = nou
System.Text.StringBuilder(); displayText.Append("<b>");
displayText.Append(numefișier); displayText.Append("</b><br /><br />");
displayText.Append("Creat: ");
displayText.Append(File.GetCreationTime(fileName). ToString());
displayText.Append("<br />Ultima accesare: ");
displayText.Append(File.GetLastAccessTime(fileName). ToString());
displayText.Append("<br />");
585
CAPITOLUL FIȘIERE ȘI FLUXURI
17
Disecarea codului . . .
• De fiecare dată când pagina se încarcă, setează șirul ftpDirectory. Calea este setată la
subfolderul FTP în directorul curent de aplicații web (care este furnizat de proprietatea
Request.PhysicalApplicationPath). Aceste două detalii (directorul curent de aplicații web
și subfolderul FTP) sunt fuzionate împreună într-un singur șir de cale utilizând metoda
Combine() din clasa Path.
• Procedura CreateFileList() este ușor de codificat, deoarece utilizează
caracteristica de legare a datelor din ListBox. Matricea returnată din metoda
GetFiles() poate fi plasată în listă cu doar câteva linii de cod.
• Proprietatea AutoPostBack a ListBox este setată la true. În acest fel, atunci când utilizatorul
alege un element din listă, ListBox postează pagina înapoi imediat, astfel încât codul să
poată citi informațiile despre fișier și să reîmprospăteze detaliile fișierului de pe pagină.
586
CAPITOLUL FIȘIERE ȘI FLUXURI
17
program sau dacă contul care rulează codul nu are permisiunile necesare. Codul din acest exemplu este ușor de
corectat - pur și simplu înfășurați toate operațiunile fișierului într-un bloc de încercare / captură. (Veți avea nevoie de
trei - unul pentru codul care citește fișierele din directorul curent, unul pentru codul care preia informațiile din fișierul
selectat și unul pentru codul care șterge fișierul.) Pentru a vedea codul cu logica adăugată de tratare a erorilor,
consultați exemplele descărcabile pentru acest capitol.
PERMISIUNI DE FIȘIER
Când testați aplicația în Visual Studio, este puțin probabil să întâmpinați erori de permisiune pentru fișier. Cu
toate acestea, atunci când implementați aplicația, viața devine mai complicată. Într-un site web implementat,
ASP.NET rulează sub un cont cu privilegii limitate cu atenție. Deși contul exact depinde de versiunea IIS
(consultați capitolul 26 pentru detalii complete), este aproape întotdeauna un membru al grupului IIS_IUSRS.
Dacă încercați să accesați un fișier utilizând un cont care nu are permisiunile necesare, veți primi o
excepție de securitate. Pentru a rezolva astfel de probleme, puteți modifica permisiunile pentru un
fișier sau pentru un întreg director. Pentru aceasta, faceți clic dreapta pe fișier sau director, selectați
Proprietăți și alegeți fila Securitate. Aici puteți adăuga sau elimina utilizatori și grupuri și puteți
configura operațiunile pe care le pot efectua. Alternativ, este posibil să vă fie mai ușor să modificați
contul pe care îl folosește ASP.NET sau să schimbați apartenența la grup. Pentru mai multe informații,
consultați capitolul 26.
Membru Descriere
CreationTime, Vă permite să setați sau să regăsiți ora creării, ora ultimei accesări și ora
LastAccessTime și ultimei scrieri utilizând un obiect DateTime.
LastWriteTime
Există Returnează true sau false în funcție de existența fișierului sau directorului. Cu
alte cuvinte, puteți crea obiecte FileInfo și DirectoryInfo care nu corespund de
fapt directoarelor fizice curente, deși, evident, nu veți putea utiliza proprietăți
precum CreationTime și metode precum MoveTo().
587
CAPITOLUL FIȘIERE ȘI FLUXURI
17
Membru Descriere
NumeComplet, Nume și Returnează un șir care reprezintă numele complet calificat, directorul sau
Extensie numele fișierului (cu extensie) sau extensia singură, în funcție de
ce proprietate utilizați.
Șterge() Elimină fișierul sau directorul, dacă există. Când ștergeți un director, acesta
trebuie să fie gol sau trebuie să specificați un parametru opțional setat la true.
Reîmprospătare() Actualizează obiectul astfel încât să fie sincronizat cu orice modificări ale sistemului de fișiere care
au avut loc între timp (de exemplu, dacă un atribut a fost modificat
manual utilizând Windows Explorer).
În plus, clasele FileInfo și DirectoryInfo au câțiva membri unici, așa cum este indicat în tabelul 17-5 și
tabelul 17-6.
Membru Descriere
Părinte și rădăcină Returnează un obiect DirectoryInfo care reprezintă directorul părinte sau rădăcină. Pentru
un director precum c:\temp\myfiles, părintele este c:\temp, iar rădăcina este c:\.
GetFiles() Returnează o matrice de obiecte FileInfo care reprezintă toate fișierele conținute
în acest director.
588
CAPITOLUL FIȘIERE ȘI FLUXURI
17
Membru Descriere
CopyTo() Copiază un fișier în noua cale și în noul nume de fișier specificat ca parametru. De asemenea,
returnează un nou obiect FileInfo care reprezintă fișierul nou (copiat). Puteți furniza
un parametru suplimentar opțional true pentru a permite suprascrierea.
Când creați un obiect DirectoryInfo sau FileInfo, specificați calea completă în constructor:
Această cale poate sau nu să corespundă unui fișier fizic real sau unui director. Dacă nu, puteți utiliza
întotdeauna metoda Creare() pentru a crea fișierul sau directorul corespunzător:
Clasa DriveInfo
Clasa DriveInfo vă permite să regăsiți informații despre o unitate de pe computer. Doar câteva informații vă vor
interesa. De obicei, clasa DriveInfo este utilizată doar pentru a prelua cantitatea totală de spațiu utilizat și liber.
Tabelul 17-7 prezintă membrii DriveInfo. Spre deosebire de clasele FileInfo și DriveInfo, nu există nicio
clasă Drive cu versiuni de instanță ale acestor metode.
Membru Descriere
Dimensiune totală Obține dimensiunea totală a unității, în octeți. Aceasta include spațiul alocat și liber.
DisponibilFreeSpace Obține cantitatea totală de spațiu liber disponibil, în octeți. Spațiul disponibil poate fi mai
mic decât spațiul liber total dacă ați aplicat cote de disc care limitează spațiul pe care îl
poate utiliza procesul ASP.NET.
589
CAPITOLUL FIȘIERE ȘI FLUXURI
17
Membru Descriere
Format unitate Returnează numele sistemului de fișiere utilizat pe unitate (cum ar fi NTFS sau
FAT32) ca șir.
Tip unitate Returnează o valoare din enumerarea DriveType, care indică dacă unitatea este o
unitate fixă, de rețea, CDRom, RAM sau amovibilă (sau Necunoscut dacă tipul
unității nu poate fi determinat).
IsReady Returnează dacă unitatea este pregătită pentru operații de citire sau scriere.
Unitățile amovibile sunt considerate "nepregătite" dacă nu au niciun suport media.
De exemplu, dacă nu există niciun CD într-o unitate CD, IsReady va returna false.
În această situație, nu este sigur să interogați celelalte proprietăți DriveInfo.
Unitățile fixe sunt întotdeauna lizibile.
VolumeLabel Obține sau setează eticheta descriptivă de volum pentru unitate. Într-o unitate
formatată NTFS, eticheta de volum poate avea până la 32 de caractere. Dacă nu
este setată, această proprietate returnează o referință nulă (Nimic).
RootDirectory Returnează un obiect DirectoryInfo pentru directorul rădăcină din această unitate.
Sfat: Încercarea de a citi de pe o unitate care nu este gata (de exemplu, o unitate CD care nu are un CD în ea) va genera o excepție. Pentru a evita această problemă, verificați proprietatea DriveInfo.IsReady și încercați să citiți alte proprietăți numai dacă se întoarce adevărat.
590
CAPITOLUL FIȘIERE ȘI FLUXURI
17
591
CAPITOLUL FIȘIERE ȘI FLUXURI
17
private void ShowDirectoriesIn(string dir) { lstDirs.Items.Clear(); încercați { DirectoryInfo dirInfo = nou DirectoryInfo(dir); foreach
(DirectoryInfo dirItem in dirInfo.GetDirectories()) { lstDirs.Items.Add(dirItem.Name); }} catch (Exception err) { // Ignorați eroarea și
lăsați caseta listă goală. } }
vid protejat cmdBrowse_Click(Expeditor obiect, EventArgs e) { // Navigați la subdirectorul selectat în prezent. dacă
(lstDirs.SelectedIndex != -1) { string newDir = Path.Combine(lblCurrentDir.Text, lstDirs.SelectedItem.Text); lblCurrentDir.Text
= newDir; ShowFilesIn(newDir); ShowDirectoriesIn(newDir); } }
592
CAPITOLUL FIȘIERE ȘI FLUXURI
17
vid protejat cmdShowInfo_Click(expeditor obiect, EventArgs e) { // Afișați informații pentru fișierul selectat curent. dacă
(lstFiles.SelectedIndex != -1) { string fileName = Path.Combine(lblCurrentDir.Text, lstFiles.SelectedItem.Text);
lblFileInfo.Text = displayText.ToString();
}
}
}
Disecarea codului . . .
• Controalele listei din acest exemplu nu se afișează imediat. În schimb, pagina web
se bazează pe butoanele Răsfoire la selectat, Sus cu un nivel și Afișare informații.
• În mod implicit, numele directoarelor nu se termină cu un caracter bară oblică inversă (\) (de
exemplu, c:\Temp este utilizat în loc de c:\Temp\). Cu toate acestea, atunci când se face referire la
unitatea rădăcină, este necesară o bară oblică. Acest lucru se datorează unei inconsecvențe
interesante care datează din zilele DOS. Când se utilizează nume de directoare, c:\ se referă la
unitatea rădăcină, dar c: se referă la directorul curent, oricare ar fi acesta. Această ciudățenie poate
cauza probleme atunci când manipulați șiruri care conțin nume de fișiere, deoarece nu doriți să
593
CAPITOLUL FIȘIERE ȘI FLUXURI
17
adăugați o bară oblică suplimentară la o cale (ca în calea nevalidă c:\\myfile.txt). Pentru a rezolva
această problemă, pagina utilizează metoda Combine() din clasa Path. Această metodă unește
corect orice nume de fișier și cale împreună, adăugând \ atunci când este necesar.
• Codul include tot codul necesar de gestionare a erorilor. Dacă încercați să citiți
informațiile pentru un fișier pe care nu aveți permisiunea să îl examinați, se afișează
mesajul de eroare în locul secțiunii de detalii despre fișier. Dacă apare o eroare la
apelarea DirectoryInfo.GetFiles() sau DirectoryInfo.GetDirectories(), eroarea este pur și
simplu ignorată și fișierele sau subdirectoarele nu sunt afișate. Această eroare apare în
cazul în care contul care execută codul nu are permisiunea de a citi conținutul
directorului. De exemplu, acest lucru se întâmplă dacă încercați să accesați directorul
c:\System Volume Information în Windows și nu sunteți administrator.
lstFiles.DataSource = dirInfo.GetFiles();
lstFiles.DataMember = "Nume";
lstFiles.DataBind();
Fișiere text
Puteți scrie într-un fișier și citi dintr-un fișier utilizând un StreamWriter și un StreamReader - clase dedicate care
abstractizează procesul de interacțiune cu fișierul. Chiar nu este prea mult. Puteți crea clasele StreamWriter și
StreamReader pe cont propriu sau puteți utiliza una dintre metodele statice utile incluse în clasa Fișier, cum ar fi
CreateText() sau OpenText().
Iată un exemplu care primește un StreamWriter pentru scrierea datelor în fișierul c:\myfile.txt:
594
CAPITOLUL FIȘIERE ȘI FLUXURI
17
Când apelați metoda CreateText(), creați fișierul și primiți obiectul StreamWriter. În acest moment, fișierul este deschis și
gata să primească conținutul. Trebuie să scrieți datele în fișier și apoi să îl închideți cât mai curând posibil.
Folosind StreamWriter, puteți apela metoda WriteLine() pentru a adăuga informații la fișier. Metoda
WriteLine() este supraîncărcată, astfel încât poate scrie multe tipuri de date simple, inclusiv șiruri, numere
întregi și alte numere. În esență, toate aceste valori sunt convertite în șiruri de caractere atunci când sunt
scrise într-un fișier și trebuie convertite manual înapoi în tipurile corespunzătoare atunci când citiți fișierul.
w.WriteLine("Acest fișier generat de ASP.NET"); Scrieți un șir.
w.WriteLine(42); Scrieți un număr.
Când terminați cu fișierul, trebuie să vă asigurați că îl închideți apelând metoda Close() sau Dispose(). În caz
contrar, este posibil ca modificările să nu fie scrise corect pe disc și fișierul ar putea fi blocat deschis.
w.Close();
În cele din urmă, atunci când depanați o aplicație care scrie în fișiere, este întotdeauna o idee bună să vă
uitați la ceea ce ați scris folosind un editor de text precum Notepad. Figura 17-3 prezintă conținutul creat în
c:\myfile.txt cu codul simplu pe care l-ați luat în considerare.
Pentru a citi informațiile, utilizați clasa StreamReader corespunzătoare. Acesta furnizează o metodă
ReadLine() care obține următoarea valoare disponibilă și o returnează ca șir. ReadLine() începe de la prima
linie și avansează poziția până la sfârșitul fișierului, rând cu rând.
StreamReader r = File.OpenText(@"c:\myfile.txt"); șir de intrareȘir; inputString
= r.ReadLine(); = "Acest fișier generat de ASP.NET" inputString = r.ReadLine();
= "42"
ReadLine() returnează o referință nulă atunci când nu mai există date în fișier. Aceasta înseamnă că
puteți citi toate datele dintr-un fișier folosind un cod ca acesta:
Citiți și afișați liniile din fișier până când se ajunge la sfârșitul fișierului.
linie de șir;
face
{
linie = r.ReadLine(); if
(linie != null) { //
595
CAPITOLUL FIȘIERE ȘI FLUXURI
17
La fel ca atunci când scrieți într-un fișier, trebuie să închideți fișierul după ce ați terminat:
r.Close();
Codul pe care l-ați văzut până acum deschide un fișier în modul utilizator unic. Dacă un al doilea utilizator
încearcă să acceseze același fișier în același timp, va apărea o excepție. Puteți reduce această problemă atunci
când deschideți fișiere utilizând versiunea mai generică cu patru parametri a metodei File.Open() în loc de
File.OpenText(). Trebuie să specificați FileShare.Read pentru parametrul final. Spre deosebire de metoda
OpenText(), metoda Open() returnează un obiect FileStream și trebuie să creați manual un StreamReader care îl
împachetează. Iată codul de care aveți nevoie pentru a crea un StreamReader multiuser-friendly:
FileStream fs = File.Open(@"c:\myfile.txt", FileMode.Open, FileAccess.Read,
FileShare.Read); StreamReader r = noul StreamReader(fs);
Sfat
În capitolul 8, ați văzut cum puteți crea un cookie pentru utilizatorul curent, care poate fi păstrat pe disc ca un simplu fișier text. Aceasta este o tehnică comună pentru stocarea informațiilor într-o aplicație web, dar este destul de diferită de codul de acces la fișiere pe care l-ați văzut în acest capitol. Cookie-urile sunt create pe partea clientului, mai degrabă decât pe server. Aceasta înseamnă că codul dvs. ASP.NET le poate utiliza la solicitări ulterioare de la același utilizator, dar nu sunt adecvate atunci când stocați informații pe care trebuie să le revizuiți
ulterior, informații care sunt mai permanente sau informații care afectează mai mulți utilizatori.
Fișiere binare
De asemenea, puteți citi și scrie în fișiere binare. Datele binare utilizează spațiul mai eficient, dar creează și fișiere
care nu pot fi citite de om. Dacă deschideți un fișier în Notepad, veți vedea o mulțime de caractere ASCII extinse
(cunoscute politicos ca gibberish).
Pentru a deschide un fișier pentru scrierea binară, trebuie să creați un nou obiect BinaryWriter.
Constructorul acceptă un flux, pe care îl puteți prelua folosind metoda File.OpenWrite(). Iată codul pentru a
deschide fișierul c:\binaryfile.bin pentru scrierea binară:
FileStream fs = File.OpenWrite(@"c:\binaryfile.bin"); BinaryWriter
w = nou BinaryWriter(fs);
.NET se concentrează pe obiectele fluxului, mai degrabă decât pe sursa sau destinația datelor. Aceasta
înseamnă că puteți scrie date binare în orice tip de flux, indiferent dacă reprezintă un fișier sau un alt tip de
locație de stocare, utilizând același cod. În plus, scrierea într-un fișier binar este aproape aceeași cu scrierea
într-un fișier text.
string str = "ASP.NET Binary File Test"; int întreg
= 42;
w.Write(str);
w.Write(număr întreg);
w.Close();
596
CAPITOLUL FIȘIERE ȘI FLUXURI
17
Citirea datelor dintr-un fișier binar este ușoară, dar nu la fel de ușoară ca citirea datelor dintr-un fișier text.
Problema este că trebuie să cunoașteți tipul de date al datelor pe care doriți să le preluați. Pentru a regăsi un șir,
utilizați metoda ReadString(). Pentru a regăsi un întreg, trebuie să utilizați ReadInt32(). De aceea, exemplul de
cod precedent scrie variabile în loc de valori literale. Dacă valoarea 42 ar fi codificată ca parametru pentru
metoda Write(), nu ar fi clar dacă valoarea ar fi scrisă ca întreg pe 16 biți, întreg pe 32 de biți, zecimal sau
altceva. Din păcate, poate fi necesar să microgestionați fișierele binare în acest fel pentru a preveni erorile.
r.Close();
Încă o dată, dacă doriți să utilizați partajarea fișierelor, trebuie să utilizați File.Open() în loc de
File.OpenRead(). Apoi puteți crea un BinaryReader manual, așa cum se arată aici:
Notă Nu aveți o modalitate ușoară de a sări la o locație dintr-un fișier text sau binar fără a citi toate informațiile în ordine. Deși puteți utiliza metode precum Seek() pe fluxul subiacent, trebuie să specificați un decalaj în octeți, ceea ce implică unele calcule destul de implicate bazate pe dimensiunile tipurilor de date. Dacă aveți nevoie să stocați o cantitate mare de informații și să vă deplasați rapid prin ea, aveți nevoie de o bază de date dedicată, nu de un fișier binar.
De exemplu, iată un fragment rapid de cod care scrie un fișier cu trei linii și apoi îl regăsește într-un singur
șir:
string[] lines = șir nou[]{"Aceasta este prima linie a fișierului.", "Aceasta este a
doua linie a fișierului.", "Aceasta este a treia linie a fișierului."};
597
CAPITOLUL FIȘIERE ȘI FLUXURI
17
Metodă Descriere
ReadAllLines() Citește întregul conținut al unui fișier și îl returnează ca o matrice de șiruri, unul pentru
fiecare linie.
WriteAllText() Creează un fișier, scrie un șir furnizat în fișier și îl închide. Dacă fișierul este deja
există, este suprascris.
WriteAllLines() Creează un fișier, scrie o matrice furnizată de șiruri în fișier (separând fiecare linie
cu un returnare greu) și închide fișierul. Dacă fișierul există deja, acesta este suprascris.
WriteAllBytes() Creează un fișier, scrie o matrice de octeți furnizată în fișier și îl închide. Dacă fișierul
Descărcați de la Wow! eBook <www.wowebook.com>
Metodele rapide de acces la fișiere sunt cu siguranță convenabile pentru crearea de fișiere mici. De asemenea, se
asigură că un fișier este păstrat doar pentru o perioadă cât mai scurtă posibil, ceea ce este întotdeauna cea mai bună
abordare pentru a minimiza problemele de concurență. Dar sunt ele cu adevărat practice? Totul depinde de dimensiunea
fișierului. Dacă aveți un fișier mare (să zicem, unul care are câțiva megaocteți), citirea întregului conținut în memorie
simultan este o idee teribilă. Este mult mai bine să citiți câte o bucată de date la un moment dat și să procesați
informațiile puțin câte puțin. Chiar dacă aveți de-a face cu fișiere de dimensiuni medii (să zicem, câteva sute de
kiloocteți), poate doriți să vă îndepărtați de metodele rapide de acces la fișiere. Acest lucru se datorează faptului că
într-un site web popular este posibil să aveți mai multe solicitări care se ocupă de fișiere în același timp, iar cheltuielile
combinate de păstrare a datelor de fișiere ale fiecărui utilizator în memorie ar putea reduce performanța aplicației dvs.
598
CAPITOLUL FIȘIERE ȘI FLUXURI
17
Când utilizatorul face clic pe Trimitere, va fi creat un fișier pentru noua intrare din cartea de oaspeți. Atâta
timp cât există cel puțin o intrare în cartea de oaspeți, un control GridView va apărea în partea de sus a
paginii, așa cum se arată în Figura 17-5.
599
CAPITOLUL FIȘIERE ȘI FLUXURI
17
GridView care reprezintă cartea de oaspeți este construit folosind legarea datelor, pe care ați explorat-o în
capitolele 15 și 16. Din punct de vedere tehnic, GridView este legat de o colecție care conține instanțe ale
clasei BookEntry. Definiția clasei BookEntry este inclusă în fișierul din spatele codului pentru pagina web și
arată astfel:
clasa publică BookEntry
{
autor privat de șiruri; șir public
Autor { get { return author; } set
{ autor = valoare; } }
600
CAPITOLUL FIȘIERE ȘI FLUXURI
17
Vizualizarea grilă utilizează o singură coloană șablon, care pescuiește valorile pe care trebuie să le
afișeze. Iată cum arată (fără detaliile stilului):
De asemenea, adaugă unele informații de stil care nu sunt incluse aici (deoarece nu este necesar să înțelegeți
logica programului). De fapt, aceste stiluri au fost aplicate în Visual Studio utilizând caracteristica Auto Format din
GridView.
În ceea ce privește intrările, pagina cărții de oaspeți utilizează subfolderul (App_Data\GuestBook) pentru a
stoca o colecție de fișiere. Fiecare fișier reprezintă o intrare separată în cartea de oaspeți. Prin plasarea
folderului Cartea de oaspeti in folderul App_Data, aplicatia web se asigura ca utilizatorul nu poate accesa
direct niciunul dintre fisierele cartii de oaspeti, deoarece serverul web nu va permite acest lucru. Cu toate
acestea, o abordare și mai bună ar fi, de obicei, crearea unui tabel Cartea de oaspeți într-o bază de date și
transformarea fiecărei intrări într-o înregistrare separată. Codul paginii web este următorul:
carte de oaspeti publica partiala : System.Web.UI.Page
{
șir privat guestBookName;
601
CAPITOLUL FIȘIERE ȘI FLUXURI
17
Reîmprospătați afișajul.
GuestBookList.DataSource = GetAllEntries();
GuestBookList.DataBind(); txtName.Text = "";
txtMessage.Text = "";
privat List<BookEntry> GetAllEntries() { // Returnați un ArrayList care conține obiecte BookEntry // pentru fiecare
fișier din directorul GuestBook.
602
CAPITOLUL FIȘIERE ȘI FLUXURI
17
privat BookEntry GetEntryFromFile(FileInfo entryFile) { // Transformați informațiile fișierului într-un obiect de intrare în carte.
BookEntry newEntry = nou BookEntry(); StreamReader r = entryFile.OpenText(); newEntry.Author = r.ReadLine();
newEntry.Submitted = DateTime.Parse(r.ReadLine()); newEntry.Message = r.ReadLine(); r.Close(); } returnați newEntry;
vid privat SaveEntry(BookEntry entry) { // Creați un fișier nou pentru această intrare, cu un nume de fișier care ar trebui să fie
unic din punct de vedere statistic.
Disecarea codului . . .
• Codul utilizează fișiere text, astfel încât să puteți revizui cu ușurință informațiile pe
cont propriu cu Notepad. Puteți utiliza fișiere binare la fel de ușor, ceea ce ar
economisi o cantitate mică de spațiu.
• Numele fișierului pentru fiecare intrare este generat utilizând o combinație între data
și ora curentă (în bife) și un număr aleatoriu. Practic, acest lucru face imposibilă
generarea unui fișier cu un nume de fișier duplicat.
• Acest program utilizează tratarea erorilor pentru a se apăra împotriva posibilelor probleme. Cu toate
acestea, erorile sunt tratate într-un mod diferit, în funcție de momentul în care apar. Dacă apare o
eroare la salvarea unei intrări noi în metoda cmdSubmit_Click(), utilizatorul este avertizat cu privire la
problemă, dar afișajul nu este actualizat. În schimb, informațiile furnizate de utilizator sunt lăsate în
controale, astfel încât operațiunea de salvare să poată fi încercată din nou. Când citiți fișierele
existente în metoda cmdGetAllEntries_Click(), pot apărea două probleme și sunt tratate utilizând
603
CAPITOLUL FIȘIERE ȘI FLUXURI
17
blocuri de excepții separate. O problemă se poate întâmpla atunci când codul apelează GetFiles()
pentru a regăsi lista de fișiere. În această situație, problema este ignorată, dar nu se găsesc fișiere
și astfel nu se afișează intrări în cartea de oaspeți. Dacă acest pas reușește, poate apărea o
problemă la citirea fiecărui fișier în metoda GetEntryFromFile(). În această situație, fișierul care a
cauzat problema este ignorat, dar codul continuă și încearcă să citească fișierele rămase.
Notă Codul de tratare a erorilor din acest exemplu face o treabă bună de recuperare din pragul dezastrului și permite utilizatorului să continue să lucreze, atunci când este posibil. Cu toate acestea, este posibil ca codul de tratare a erorilor să nu facă suficient pentru a vă avertiza că există o problemă. Dacă problema este o întâmplare ciudată, acest comportament este bine. Dar dacă problema este un simptom al unei probleme mai profunde în aplicația dvs. web, ar trebui să știți despre
aceasta.
Pentru a vă asigura că problemele nu sunt trecute cu vederea, puteți alege să afișați un mesaj de
eroare în pagină atunci când apare o excepție. Chiar mai bine, codul dvs. ar putea crea în liniște o
intrare în jurnalul de evenimente care înregistrează problema (așa cum se explică în capitolul 7). În
acest fel, puteți afla despre problemele care au apărut și le puteți corecta mai târziu.
Controlul FileUpload
Din fericire, ASP.NET include un control care permite utilizatorilor site-ului web să încarce fișiere pe serverul web. Odată
ce serverul web primește datele fișierului postat, depinde de aplicația dvs. să le examineze, să le ignore sau să le salveze
într-o bază de date back-end sau într-un fișier de pe serverul web. Controlul FileUpload face acest lucru și reprezintă
eticheta HTML <input type="file">.
Declararea controlului FileUpload este ușoară. Nu expune proprietăți sau evenimente noi pe care le puteți
utiliza prin eticheta de control:
<asp:FileUpload ID="Uploader" runat="server" />
Eticheta <input type="file"> nu vă oferă prea multe opțiuni în ceea ce privește interfața cu utilizatorul (este limitată la o casetă
de text care conține un nume de fișier și un buton Răsfoire). Când utilizatorul face clic pe Răsfoire, browserul prezintă o casetă
604
CAPITOLUL FIȘIERE ȘI FLUXURI
17
de dialog Deschidere și permite utilizatorului să aleagă un fișier. Această parte este cablată în browser și nu puteți modifica
acest comportament. Odată ce utilizatorul selectează un fișier, numele fișierului este completat în caseta de text
corespunzătoare. Cu toate acestea, fișierul nu este încărcat încă, ceea ce se întâmplă mai târziu, când pagina este postată
înapoi. În acest moment, toate datele din toate controalele de intrare (inclusiv datele fișierului) sunt trimise către server. Din
acest motiv, este obișnuit să adăugați un buton pentru a posta înapoi pagina.
Pentru a obține informații despre conținutul fișierului postat, puteți accesa obiectul FileUpload.PostedFile.
Puteți salva conținutul apelând metoda PostedFile.SaveAs():
Uploader.PostedFile.SaveAs(@"c:\Uploads\newfile");
Figura 17-6 prezintă o pagină web completă care demonstrează modul de încărcare a unui fișier specificat
de utilizator. Acest exemplu introduce o întorsătură - permite încărcarea numai a acelor fișiere cu extensiile
.bmp, .gif și .jpg.
protejat void Page_Load(expeditor obiect, EventArgs e) { // Plasați fișiere într-un subfolder de site web numit Încărcări.
uploadDirectory = Path.Combine (} Request.PhysicalApplicationPath, "Uploads");
605
CAPITOLUL FIȘIERE ȘI FLUXURI
17
{
} // Verificați extensia.
extensie șir = Path.GetExtension(Uploader.PostedFile.FileName);
comutator (extensie. ToLower()) { cazul ".bmp": cazul ".gif": cazul ".jpg": pauză; implicit: lblInfo.Text = "Acest tip de
fișier nu este permis."; } restitui;
încercați {
Uploader.PostedFile.SaveAs(fullUploadPath);
lblInfo.Text = "File " + serverFileName; lblInfo.Text += "
încărcat cu succes în"; lblInfo.Text += fullUploadPath; }
captură (Excepție eroare) { lblInfo.Text = err. Mesaj; }
}
}
Disecarea codului . . .
• Fișierul salvat își păstrează numele original (partea client). Codul utilizează metoda
statică Path.GetFileName() pentru a transforma numele complet calificat furnizat de
FileUpload.PostedFile.FileName și pentru a prelua doar fișierul, fără cale.
• Obiectul FileUpload.PostedFile conține doar câteva proprietăți. O proprietate
interesantă este ContentLength, care returnează dimensiunea fișierului în octeți.
Puteți examina această setare și o puteți utiliza pentru a împiedica un utilizator să
încarce fișiere excesiv de mari.
606
CAPITOLUL FIȘIERE ȘI FLUXURI
17
În mod prestabilit, ASP.NET va respinge o solicitare mai mare de 4 MB. Cu toate acestea, puteți
modifica acest maxim modificând setarea maxRequestLength din fișierul web.config. Aceasta setează
cel mai mare fișier permis în kiloocteți. Serverul web va refuza să proceseze solicitări mai mari.
Următoarea setare de eșantion configurează serverul să accepte fișiere de până la 8 MB:
<?xml version="1.0" encoding="utf-8" ?>
<configuration> <system.web> <!-- Alte setări omise
pentru claritate. --> <httpRuntime
maxRequestLength="8192" /> </system.web>
</configuration>
Fii atent, totuși. Când permiți o încărcare de 8 MB, codul tău nu va rula până când nu primești solicitarea
completă. Aceasta înseamnă că un server rău intenționat vă poate paraliza serverul trimițând mesaje mari
de solicitare către aplicația dvs. Chiar dacă aplicația dvs. respinge în cele din urmă aceste mesaje, firele
de proces ale lucrătorului ASP.NET vor fi în continuare legate în așteptarea finalizării solicitărilor. Acest tip
de atac se numește atac de refuz al serviciului și, cu cât este mai mare dimensiunea permisă a solicitării,
cu atât site-ul dvs. devine mai susceptibil.
Ultimul cuvânt
Deși bazele de date și site-urile web se potrivesc perfect, nimic nu vă împiedică să utilizați clasele din .NET
Framework pentru a accesa alte tipuri de date, inclusiv fișiere. De fapt, codul pe care îl utilizați pentru a
interacționa cu sistemul de fișiere este același cu cel pe care l-ați utiliza într-o aplicație desktop sau în orice
program .NET. Datorită .NET Framework, puteți rezolva în sfârșit problemele comune de programare în
același mod, indiferent de tipul de aplicație pe care îl creați.
607
Descărcați de la Wow! eBook <www.wowebook.com>
C A P I T O L U L 18
•■■
XML
XML este conceput ca un format universal pentru organizarea datelor. În multe cazuri, atunci când decideți să utilizați
XML, decideți să stocați datele într-un mod standardizat, mai degrabă decât să creați propriile convenții de format noi
(și pentru alți dezvoltatori, necunoscute). Locația reală a acestor date - în memorie, într-un fișier, într-un flux de rețea -
este irelevantă.
În acest capitol, veți învăța regulile de bază ale standardului XML. Veți învăța cum să citiți conținutul XML
utilizând clasele bibliotecii .NET și cum puteți crea și citi propriile documente XML. Veți studia, de
asemenea, unele dintre celelalte standarde care acceptă și extind regulile de bază ale XML, inclusiv
spațiile de nume XML, schema XML și XSLT.
XML explicat
Cel mai bun mod de a înțelege rolul pe care îl joacă XML este să luați în considerare evoluția unui format
de fișier simplu fără XML. De exemplu, luați în considerare un program simplu care stochează elementele
produsului ca listă într-un fișier. Spuneți că atunci când creați pentru prima dată acest program, decideți că
va stoca trei informații despre produs (ID, nume și preț) și veți utiliza un format simplu de fișier text pentru
depanare și testare ușoară. Formatul de fișier pe care îl utilizați arată astfel:
1 Președinte
49.33
2 mașină
43399.55
3 coș cu fructe
proaspete
49.99
Acesta este tipul de format pe care îl puteți crea utilizând clase .NET, cum ar fi StreamWriter. Este ușor de
lucrat cu - trebuie doar să scrieți toate informațiile, în ordine, de sus în jos. Desigur, este un format destul de
fragil. Dacă decideți să stocați o informație suplimentară în fișier (cum ar fi un semnalizator care indică dacă
un element este disponibil), codul vechi nu va funcționa. În schimb, poate fi necesar să recurgeți la
adăugarea unui antet care indică versiunea fișierului:
SuperProProductList
Versiunea 2.0
609
CAPITOLUL XML
18
49.33
Adevărat
2 mașină
43399.55
Coș cu fructe
proaspete True 3
49.99
Fals
Acum, puteți verifica versiunea fișierului atunci când îl deschideți și puteți utiliza în mod corespunzător un cod diferit de
citire a fișierelor. Din păcate, pe măsură ce adăugați tot mai multe versiuni posibile, codul de citire a fișierelor va deveni
incredibil de încurcat și este posibil să rupeți accidental compatibilitatea cu unul dintre formatele de fișiere anterioare fără
să vă dați seama. O abordare mai bună ar fi crearea unui format de fișier care să indice unde începe și se oprește fiecare
înregistrare de produs. Codul dvs. ar seta apoi doar câteva valori implicite adecvate dacă găsește informațiile lipsă într-un
format de fișier mai vechi.
Iată o soluție relativ brută care îmbunătățește SuperProProductList adăugând o secvență specială de
caractere (##Start##) pentru a arăta unde începe fiecare înregistrare nouă:
49.33
Adevărat
##Start##
2 Mașină
43399.55
Adevărat
##Start## 3 Coș cu
fructe proaspete
49.99 Fals
Una peste alta, acesta nu este un efort rău. Din păcate, puteți utiliza la fel de bine formatul de fișier binar în acest
moment - fișierul text devine greu de citit și este chiar mai greu de ghicit ce informație reprezintă fiecare valoare. Pe partea
de cod, veți avea nevoie, de asemenea, de câteva abilități de bază de verificare a erorilor. De exemplu, ar trebui să faceți
codul capabil să sară peste liniile goale introduse accidental, să detecteze o etichetă ##Start## lipsă și așa mai departe,
doar pentru a oferi un nivel de protecție de bază.
Problema centrală cu această soluție autohtonă este că reinventezi roata. În timp ce încercați să scrieți
codul de acces la fișiere de bază și să creați un format de fișier rezonabil de flexibil pentru o sarcină
simplă, alți programatori din întreaga lume își creează propriile soluții private, ad-hoc. Chiar dacă
programul tău funcționează bine și îl poți înțelege, alți programatori cu siguranță nu îl vor găsi ușor.
610
CAPITOLUL XML
18
<?xml version
= "1.0"?>
<SuperProProductList>
<Product>
<ID>1</ID>
<Nume>Președinte</Nume>
<Pret>49.33</Pret>
<Disponibil>True</Disponibil>
<Stare>3</Stare>
</Produs> <Produs>
<ID>2</ID> <Name>Car</Name>
<Price>43399.55</Price>
<Available>True</Available>
<Stare>3</Stare>
</Produs> <Produs>
<ID>3</ID> <Nume>Coș cu fructe
proaspete</Nume>
<Pret>49.99</Pret>
<Disponibil>Fals</Disponibil>
<Stare>4</Stare> </Produs>
</SuperProProductList>
Acest format este clar de înțeles. Fiecare articol de produs este inclus într-un element <Produs> și fiecare
informație are propriul element cu un nume adecvat. Elementele sunt imbricate cu mai multe straturi
adâncime pentru a arăta relațiile. În esență, XML oferă sintaxa elementului de bază, iar tu (programatorul)
definești elementele pe care vrei să le folosești. De aceea, XML este adesea descris ca un metalimbaj - este
un limbaj pe care îl utilizați pentru a vă crea propriul limbaj. În exemplul SuperProProductList, acest limbaj
XML particularizat definește elemente precum <Product>, <ID>, <Name> și așa mai departe. Cel mai bine,
atunci când citiți acest document XML în majoritatea limbajelor de programare (inclusiv cele din .NET
Framework), puteți utiliza parserele XML pentru a vă ușura viața. Cu alte cuvinte, nu trebuie să vă faceți griji
cu privire la detectarea locului în care începe și se oprește un element, prăbușirea spațiului alb și așa mai
departe (deși trebuie să vă faceți griji cu privire la capitalizare, deoarece XML este sensibil la majuscule). În
schimb, puteți citi fișierul în câteva obiecte de date XML utile, care fac navigarea în întregul document mult
mai ușoară. În mod similar, acum puteți extinde SuperProProductList cu mai multe informații folosind
elemente suplimentare și orice cod pe care l-ați scris deja va continua să funcționeze fără probleme.
611
CAPITOLUL XML
18
Puteți efectua multe activități cu XML - poate inclusiv unele lucruri pentru care nu a fost proiectat niciodată. Această carte nu
este destinată să vă învețe programarea XML, ci un design bun ASP.NET aplicațiilor. Pentru majoritatea programatorilor
ASP.NET, procesarea fișierelor XML este un înlocuitor ideal pentru rutinele personalizate de acces la fișiere și funcționează
cel mai bine în situațiile în care trebuie să stocați o cantitate mică de date pentru sarcini relativ simple.
Fișierele XML nu sunt un substitut bun pentru o bază de date, deoarece au aceleași limitări ca orice alt tip
de acces la fișiere. Într-o aplicație web, doar un singur utilizator poate actualiza un fișier la un moment dat
fără a provoca dureri de cap grave, indiferent dacă fișierul conține un document XML sau conținut binar.
Produsele de baze de date oferă un set mult mai bogat de caracteristici pentru gestionarea concurenței
multiutilizator și furnizarea de performanțe optimizate. Desigur, nimic nu vă împiedică să stocați date XML
într-o bază de date, pe care multe produse de baze de date o încurajează în mod activ. De fapt, cele mai
noi versiuni ale produselor de baze de date de top, cum ar fi SQL Server și Oracle, includ chiar și
caracteristici XML extinse care acceptă unele dintre standardele pe care le veți vedea în acest capitol.
612
CAPITOLUL XML
18
• Fiecare element trebuie să fie complet închis. Cu alte cuvinte, atunci când deschideți
un subelement, trebuie să îl închideți înainte de a putea închide părintele.
<Product><ID></ID></Product> este valid, dar <Product><ID></Product></ID> nu este. Ca
regulă generală, indentați când deschideți un element nou, deoarece acest lucru vă va
permite să vedeți structura documentului și să observați dacă închideți accidental mai întâi
elementul greșit.
• Documentele XML trebuie să înceapă cu o declarație XML precum <?xml version="1.0"?>. Acest
lucru semnalează că documentul conține XML și indică orice codificare specială a textului. Cu toate
acestea, multe analizoare XML funcționează bine chiar dacă acest detaliu este omis.
Aceste cerinte ar trebui sa sune familiar - sunt aceleasi reguli pe care le-ati invatat pentru XHTML in capitolul 4. La
urma urmei, XHTML este doar un alt limbaj specializat care este construit folosind regulile standardizate ale XML.
Atâta timp cât îndepliniți aceste cerințe, documentul XML poate fi analizat și afișat ca arbore de bază.
Aceasta înseamnă că documentul este bine format, dar nu înseamnă că este valid. De exemplu, este posibil
să aveți în continuare elementele într-o ordine greșită (de exemplu, <ID><Product></Product></ID>) sau este
posibil să aveți un tip greșit de date într-un anumit câmp (de exemplu, <ID>Chair</ID><Name>2</Name).
Puteți impune aceste reguli suplimentare documentelor XML, așa cum veți vedea mai târziu în acest capitol
când luați în considerare schemele XML. Elementele sunt unitățile primare pentru organizarea informațiilor în
XML (așa cum s-a demonstrat cu exemplul SuperProProductList), dar nu sunt singura opțiune. De
asemenea, puteți utiliza atribute.
Atribute
Atributele adaugă informații suplimentare unui element. În loc să puneți informații într-un subelement, puteți
utiliza un atribut. În comunitatea XML, decizia de a folosi subelemente sau atribute – și ce informații ar
trebui să intre într-un atribut – este o chestiune de mare dezbatere, fără un consens clar. Iată exemplul
SuperProProductList cu atributele ID și Name în loc de subelementele ID și Name:
<?xml version="1.0"?>
<SuperProProductList> <Product > ID="1"
name="Chair" <Price>49.33</Price>
<Available>True</Available>
<Stare>3</Stare> </Produs>
<ID produs=" 2"
name="masina">
<Pret>43399.55</Pret>
<Disponibil>True</Disponibil>
<Stare>3</Stare> </Produs> < ID produs="3"
name="Coș cu fructe proaspete">
<Pret>49.99</Pret>
<Disponibil>Fals</Disponibil>
<Stare>4</Stare> </Produs>
</SuperProProductList>
613
CAPITOLUL XML
18
Desigur, ați văzut deja acest tip de sintaxă cu elemente HTML și controale ASP.NET serverului:
Ordinea bacșișului nu este importantă atunci când aveți de-a face cu atribute. Parserele XML tratează atributele ca o colecție de informații neordonate referitoare la un element. Pe de altă parte, ordinea elementelor este adesea importantă. Astfel, dacă aveți nevoie de o modalitate de aranjare a informațiilor și de păstrare a ordinii acestora sau dacă aveți o listă de elemente cu același nume, utilizați elemente, nu atribute.
Comentarii
De asemenea, puteți adăuga comentarii la un document XML. Comentariile merg aproape oriunde și sunt
ignorate în scopul procesării datelor. Comentariile sunt încadrate între paranteze de secvențele de caractere
<!-- și -->. Următoarea listă include trei comentarii valide:
<?xml version="1.0"?>
<SuperProProductList>
<!-- Acesta este un fișier de test. --> <ID produs="1" name="Chair">
Singurul loc în care nu puteți pune un comentariu este încorporat într-o etichetă de început sau de sfârșit (ca
în <myData <!-- Un comentariu nu ar trebui să meargă aici --></myData>).
614
CAPITOLUL XML
18
Clasele XML
.NET oferă un set bogat de clase pentru manipularea XML în mai multe spații de nume care încep cu System.Xml. Unul
dintre cele mai confuze aspecte ale utilizării XML cu .NET este să decideți ce combinație de clase ar trebui să utilizați.
Multe dintre ele oferă funcționalități similare într-un mod ușor diferit, optimizate pentru scenarii specifice sau pentru
compatibilitatea cu standarde specifice.
Majoritatea exemplelor pe care le veți explora utilizează tipurile din spațiul de nume System.XML de bază. Clasele de
aici vă permit să citiți și să scrieți fișiere XML, să manipulați datele XML în memorie și chiar să validați documente XML.
În acest capitol, veți analiza următoarele opțiuni pentru tratarea datelor XML:
• Citirea și scrierea XML direct, la fel cum citiți și scrieți fișiere text folosind
XmlTextWriter și XmlTextReader. Pentru viteză și eficiență, aceasta este cea mai
bună abordare.
• Tratarea XML ca o colecție de obiecte în memorie folosind clasa XDocument.
Dacă aveți nevoie de mai multă flexibilitate decât oferă XmlTextWriter și
XmlTextReader sau doriți doar un model mai simplu și mai simplu (și nu trebuie să
stoarceți fiecare ultimă picătură de performanță), aceasta este o alegere bună.
• Utilizarea controlului XML pentru a transforma conținutul XML în HTML afișabil. În
situația corectă - când tot ce doriți să faceți este să afișați conținut XML utilizând o
foaie de stil XSLT predefinită - această abordare oferă o comandă rapidă utilă.
Notă Când vine vorba de XML, Microsoft este un pic schizofrenic. .NET Framework include cel puțin o duzină de moduri de a citi și manipula XML, inclusiv multe care sunt prea specializate sau limitate pentru a fi acoperite în acest capitol. În secțiunile următoare, veți petrece cea mai mare parte a timpului explorând cele două modalități cele mai practice de a lucra cu XML. În primul rând, veți învăța să utilizați clasele de bază XmlTextWriter și XmlTextReader, care garantează performanțe bune. În al doilea
rând, veți explora clasa XDocument, care poate simplifica procesarea XML complicată.
615
CAPITOLUL XML
18
w.WriteStartDocument();
w.WriteStartElement ("SuperProProductList");
w.WriteComment("Acest fișier generat de clasa XmlTextWriter.");
w.WriteStartElement ("Preț");
w.WriteString("49.33");
w.WriteEndElement();
w.WriteEndElement();
w.WriteStartElement ("Preț");
w.WriteString("43399.55");
w.WriteEndElement();
w.WriteEndElement();
w.WriteStartElement ("Preț");
w.WriteString("49.99");
w.WriteEndElement();
w.WriteEndElement();
616
CAPITOLUL XML
18
Disecarea codului . . .
• Creați întregul document XML apelând metodele XmlTextWriter, în ordinea
corectă. Pentru a porni un document, începeți întotdeauna apelând
WriteStartDocument(). Pentru a-l termina, apelați WriteEndDocument().
• Următorul pas este scrierea elementelor de care aveți nevoie. Scrieți elemente în
trei pași. Mai întâi, scrieți eticheta de pornire (cum ar fi <Product>) apelând
WriteStartElement(). Apoi scrieți atribute, elemente și conținut text în interior. În cele
din urmă, scrieți eticheta finală (cum ar fi < / Product>) apelând WriteEndElement().
617
CAPITOLUL XML
18
FORMATAREA XML-ului
În mod implicit, XmlTextWriter va crea un fișier XML care are toate elementele sale grupate împreună într-o
singură linie, fără returnări sau indentări utile. Nu vedeți această limitare în Figura 18-1, deoarece Internet
Explorer utilizează o foaie de stil pentru a oferi XML un aspect mai lizibil (și mai colorat). Cu toate acestea,
dacă deschideți documentul XML în Notepad, veți vedea diferența.
Deși formatarea suplimentară nu este necesară (și nu modifică modul în care vor fi procesate datele), aceasta
poate face o diferență semnificativă dacă doriți să citiți fișierele XML în Visual Studio, Notepad sau alt editor
de text. Din fericire, XmlTextWriter acceptă formatarea; Trebuie doar să o activați, după cum urmează:
Un nod este o denumire care include comentarii, spații albe, etichete de deschidere, etichete de închidere,
conținut și chiar declarația XML din partea de sus a fișierului. Pentru a înțelege rapid nodurile, puteți utiliza
XmlTextReader pentru a parcurge întregul document de la început până la sfârșit și pentru a afișa fiecare
nod pe care îl întâlnește. Codul pentru această sarcină este următorul:
fișier șir = Path.Combine(Request.PhysicalApplicationPath,
@"App_Data\SuperProProductList.xml"); FileStream fs = nou
FileStream (fișier, FileMode.Open); XmlTextReader r = nou
XmlTextReader(fs);
Utilizați un StringWriter pentru a construi un șir de HTML care //
descrie informațiile citite din documentul XML. StringWriter writer
= nou StringWriter();
Analizați fișierul și citiți fiecare nod. în timp ce
(r.Read())
{
scriitor. Scrie("<b>Tip:</b> "); scriitor.
Scrie(r.NodeType.ToString()); scriitor.
Scrie("<br>");
Numele este disponibil la citirea etichetelor de deschidere și închidere // pentru
un element. Nu este disponibil atunci când citiți conținutul interior. dacă (r.Name
618
CAPITOLUL XML
18
if (r.AttributeCount > 0) { writer. Scrie("<b>Atribute:</b> "); pentru (int i = 0; i < r.AttributeCount; i++) { writer. Scrie(" "); scriitor.
Scrie(r.GetAttribute(i)); scriitor. Write(" "); } } scriitor. Scrie("<br>"); scriitor. Scrie("<br>");
}
Fs. Aproape();
619
CAPITOLUL XML
18
Următoarea este o listă a tuturor nodurilor găsite, scurtată pentru a include un singur produs:
Tip: XmlDeclaration
Nume: xml Valoare:
version= "1.0" Atribute:
1.0
Tip: Nume element:
SuperProProductList
Tip: Valoare comentariu: Acest fișier generat de clasa XmlTextWriter. Tip:
620
CAPITOLUL XML
18
Tip: Nume
element: Preț
Tip: Valoare text: 49.33
Tip: EndElement
Nume: Preț
Tip: EndElement
Nume: Produs
Tip: EndElement
Denumire: SuperProProductList
Dacă utilizați trucul de indentare descris anterior (în bara laterală "Formatarea XML"), veți vedea noduri
suplimentare care reprezintă biții de spațiu alb dintre elemente.
Într-o aplicație tipică, ar trebui să mergeți la pescuit pentru elementele care vă interesează. De exemplu,
este posibil să citiți informații dintr-un fișier XML, cum ar fi SuperProProductList.xml și să le utilizați pentru
a crea obiecte de produs pe baza clasei de produse afișate aici:
clasă publică Produs { public int ID {get; set;} șir public Nume {get; set;} zecimal public Preț {get; set;} }
Nimic nu este deosebit de special la această clasă - tot ce face este să vă permită să stocați trei informații conexe
(preț, nume și ID). Rețineți că această clasă utilizează proprietăți automate în locul variabilelor pentru membrii
publici, astfel încât informațiile sale pot fi afișate într-o pagină web cu ASP.NET legătură de date. O aplicație tipică
poate citi date dintr-un fișier XML și le poate plasa direct în obiectele corespunzătoare. Următorul exemplu (de
asemenea, o parte a paginii XmlWriterTest.aspx) arată cum puteți crea cu ușurință un grup de obiecte Produs pe
baza fișierului SuperProProductList.xml. Acest exemplu utilizează colecția generică Listă, deci va trebui să importați
spațiul de nume System.Collections.Generic.
Deschideți un flux în fișier.
fișier șir = Path.Combine(Request.PhysicalApplicationPath,
@"App_Data\SuperProProductList.xml"); FileStream fs = nou
FileStream (fișier, FileMode.Open); XmlTextReader r = nou
XmlTextReader(fs);
Creați o colecție generică de produse.
Listă<Produs> produse = listă nouă<produs>();
Buclă prin produse. în timp ce
(r.Read()) { if (r.NodeType ==
XmlNodeType.Element && r.Name ==
621
CAPITOLUL XML
18
"Product") {
r.citit();
Fs. Aproape();
Disecarea codului . . .
• Acest cod utilizează o structură de buclă imbricată. Bucla exterioară iterează toate
produsele, iar bucla interioară caută prin toate elementele copil ale <Produsului>. (În acest
exemplu, codul procesează elementul <Price> și ignoră orice altceva.) Structura de buclă
păstrează codul bine organizat.
• Nodul EndElement vă avertizează când un nod este complet și bucla se poate
termina. Odată ce toate informațiile sunt citite pentru un produs, obiectul
corespunzător este adăugat în colecție.
• Toate informațiile sunt preluate din fișierul XML ca șir. Astfel, trebuie să utilizați
metode precum Int32.Parse() pentru a-l converti la tipul de date potrivit.
622
CAPITOLUL XML
18
• Legarea datelor este utilizată pentru a afișa conținutul colecției. Un set GridView
pentru a genera coloane creează automat tabelul prezentat în Figura 18-3.
Notă
• XmlTextReader oferă mult mai multe proprietăți și metode. Acești membri suplimentari nu
adaugă funcționalități; Acestea permit o flexibilitate sporită. De exemplu, aveți posibilitatea să
citiți o porțiune dintr-un document XML într-un șir utilizând metode precum ReadString(),
ReadInnerXml() și ReadOuterXml(). Toți acești membri sunt documentați în referința bibliotecii
clasei din Ajutorul Visual Studio.
Notă document
• Rețineți că termenii document XML și fișier XML sunt diferiți. Un XML este o colecție de
elemente structurate în conformitate cu regulile XML. Un document XML poate fi stocat în
aproape orice mod doriți - poate fi plasat într-un fișier, într-un câmp sau într-o bază de date
sau poate exista pur și simplu în memorie.
623
CAPITOLUL XML
18
Această abordare este ideală pentru stocarea blocurilor simple de date. De exemplu, puteți modifica pagina
cărții de oaspeți din capitolul anterior pentru a stoca intrările din cartea de oaspeți într-un format XML, ceea
ce ar oferi o standardizare mai mare, dar nu ar schimba modul în care funcționează aplicația. Codul pentru
serializarea și deserializarea datelor XML se va schimba, dar restul aplicației va rămâne neatins. Clasa
XDocument oferă o abordare diferită a datelor XML. Acesta oferă un model în memorie a unui întreg
document XML. Apoi puteți naviga prin întregul document, citind, inserând sau eliminând noduri în orice
locație. (Puteți găsi XDocument și toate clasele asociate în spațiul de nume System.Xml.Linq.)
Când utilizați această abordare, începeți prin încărcarea conținutului XML dintr-un fișier (sau altă sursă)
într-un obiect XDocument. XDocument conține întregul document simultan, deci nu este o abordare practică
dacă conținutul XML are o dimensiune de câțiva megaocteți. (Dacă aveți un document XML imens, clasele
XmlTextReader și XmlTextWriter oferă cea mai bună abordare.) Cu toate acestea, XDocument excelează
cu adevărat cu capacitățile de editare pe care vi le oferă. Folosind obiectul XDocument, puteți manipula
conținutul sau structura oricărei părți a documentului XML. Când ați terminat, puteți salva conținutul înapoi
într-un fișier. Spre deosebire de XmlTextReader și XmlTextWriter, clasa XDocument nu menține o
conexiune directă la fișier.
Notă
XDocument este . Cel mai modern instrument NET pentru manipularea XML în memorie. A fost introdus ca parte a LINQ (o caracteristică despre care veți afla în capitolul 24), în .NET 3.5. Înainte de aceasta, dezvoltatorii .NET au folosit o clasă similară, dar puțin mai ciudată, numită XmlDocument. Această distincție este importantă, deoarece XmlDocument este încă lovit în .NET 4 și există câteva clase și metode vechi care o așteaptă, în loc de XDocument. Cu toate acestea, deoarece XDocument este mai puternic și mai
convenabil, Microsoft vă recomandă să îl faceți instrumentul dvs. pentru o muncă XML serioasă.
Când utilizați clasa XDocument, documentul XML este creat ca o serie de obiecte .NET legate în memorie.
Figura 18-4 prezintă modelul obiectului. (Diagrama este ușor simplificată față de ceea ce veți găsi atunci
când începeți să utilizați clasa XDocument - și anume, nu afișează atributele, fiecare dintre acestea fiind
reprezentat de un obiect XAttribute.)
624
CAPITOLUL XML
18
Pentru a începe să construiți un alt document XML, trebuie să creați obiectele XDocument, XElement și
XAttribute care îl compun. Toate aceste clase au constructori utili care vă permit să le creați și să le
inițializați într-un singur pas. De exemplu, puteți crea un element și puteți furniza conținut text care ar trebui
plasat în interior folosind cod ca acesta:
XElement element = noul XElement("Preț", "23,99");
625
CAPITOLUL XML
18
Acest lucru este deja mai bun decât XmlTextWriter, care vă obligă să începeți un element, să introduceți conținutul
acestuia și să îl închideți cu trei instrucțiuni separate. Dar economiile de cod devin și mai dramatice atunci când luați în
considerare o altă caracteristică a claselor XDocument și XElement - capacitatea lor de a crea un arbore imbricat de
noduri într-o singură instrucțiune de cod.
Iată cum funcționează. Atât clasa XDocument, cât și clasa XElement includ un constructor care ia o
matrice de parametri pentru ultimul argument. Această matrice de parametri conține o listă de noduri
imbricate.
Notă O matrice de parametri este un parametru care este precedat de cuvântul cheie params. Acest parametru este întotdeauna ultimul parametru și este întotdeauna o matrice. Avantajul este că utilizatorii nu trebuie să declare matricea - în schimb, ei pot pur și simplu să abordeze câte argumente doresc, care sunt grupate automat într-o singură matrice.
Iată un exemplu care creează un element cu trei elemente imbricate și conținutul acestora:
<Produs>
3 <Nume>Coș cu fructe
proaspete</Nume>
49.99</produs>
Puteți extinde această tehnică pentru a crea un întreg document XML, complet cu elemente, conținut text,
atribute și comentarii. De exemplu, iată codul complet care creează documentul SuperProProductList.xml în
memorie. Când documentul este complet construit, codul îl salvează într-un fișier folosind metoda
XDocument.Save().
Construiți conținutul XML în memorie.
XDocument doc = nou XDocument( nou XDeclaration("1.0",
null, "da"), nou XComment("Creat cu clasa XDocument."),
nou XElement("SuperProProductList", nou
XElement("Produs", nou XAttribute("ID", 1), nou
XAttribute("Nume", "Scaun"), noul XElement("Preț", "49.33"),
noul XElement("Produs", noul XAttribute("ID", 2), noul
XAttribute("Nume", "Mașină"), noul XElement("Preț",
"43399.55"), noul XElement("Produs", nou XAttribute("ID",
3), noul XAttribute("Nume", "Coș cu fructe proaspete"), noul
XElement("Preț", "49,99") ) )
626
CAPITOLUL XML
18
);
Acest cod creează același conținut XML ca și codul XmlTextWriter pe care l-ați luat în considerare
mai devreme. Cu toate acestea, acest cod este mai scurt și mai ușor de citit.
Disecarea codului . . .
• Fiecare parte separată a documentului XML este creată ca obiect. Elementele
sunt create ca obiecte XElement, comentariile sunt create ca obiecte XComment,
iar atributele sunt reprezentate ca obiecte XAttribute.
• Spre deosebire de codul care utilizează XmlTextWriter, nu este nevoie să
închideți în mod explicit elementele.
• Un alt detaliu frumos este modul în care indentarea instrucțiunilor de cod reflectă
imbricarea elementelor din documentul XML. Dacă un element este urmat de altul și
ambele elemente au aceeași indentare, atunci cele două elemente sunt la același nivel (de
exemplu, un element <Produs> după altul). Dacă un element este urmat de altul și al
doilea element are o indentare mai mare, acesta este plasat în interiorul elementului
precedent (de exemplu, elementul <Preț> din elementul <Produs>). Același lucru este
valabil și pentru alte tipuri de noduri, cum ar fi comentariile și atributele. Această indentare
vă permite să vă uitați la cod și să luați rapid forma generală a documentului XML.
• Una dintre cele mai bune caracteristici ale clasei XDocument este că nu se bazează pe
niciun fișier subiacent. Când utilizați metoda Save(), fișierul este creat, se deschide un
flux, informațiile sunt scrise și fișierul este închis, toate într-o singură linie de cod. Aceasta
înseamnă că aceasta este probabil singura linie pe care trebuie să o puneți în interiorul
unui bloc de manipulare a erorilor de încercare / prindere.
Figura 18-5 prezintă fișierul scris de acest cod (așa cum este afișat de Internet Explorer).
627
CAPITOLUL XML
18
Descărcați de la Wow! eBook <www.wowebook.com>
Metodă Descriere
Elemente() Obține colecția de obiecte XElement care sunt conținute de acest element. (Acesta
este doar nivelul superior - aceste elemente pot conține, la rândul lor, mai multe
elemente.) Opțional, puteți specifica un nume de element și numai acele elemente
vor fi preluate.
Element() Obține singurul XElement conținut de acest element care are un nume specific (sau
nul dacă nu există nicio potrivire). Dacă există mai multe elemente de potrivire,
această metodă primește doar primul.
628
CAPITOLUL XML
18
Metodă Descriere
Descendenți() Obține colecția de obiecte XElement care sunt conținute de acest element și
(opțional) au numele pe care îl specificați. Spre deosebire de metoda Elements(), aceasta
metoda trece prin toate straturile documentului și găsește elemente la orice
nivelul ierarhiei.
Noduri() Primește toate obiectele XNode conținute de acest element. Aceasta include elemente
și alt conținut, cum ar fi comentariile. Cu toate acestea, spre deosebire de clasa XmlTextReader,
XDocument nu consideră atributele ca fiind noduri.
DescendențiNoduri() Obține tot obiectul XNode conținut de acest element. Această metodă este ca
Descendenți() prin faptul că detaliază toate straturile elementelor imbricate.
Aceste metode vă oferă flexibilitate suplimentară pentru a filtra doar elementele care vă interesează. De
exemplu, atunci când utilizați metoda Elements(), aveți două supraîncărcări din care să alegeți. Puteți obține
toate elementele fiu (caz în care nu ați furniza parametri) sau puteți obține doar acele elemente fiu care au un
anumit nume de element (caz în care ați specifica numele elementului ca șir). De exemplu, iată cum ați obține
elementul rădăcină <SuperProProductList> dintr-un XDocument care conține SuperProProductList.xml
complet:
Utilizați metoda Element(), deoarece există un singur element de potrivire. XElement
superProProductListElement = doc. element("SuperProProductList");
Apoi puteți utiliza acest element ca punct de plecare pentru a săpa mai adânc în document. De exemplu,
dacă doriți să găsiți elementele copil <Product> în <SuperProProductList>, adăugați acest cod:
Utilizați metoda Elements(), deoarece există mai multe elemente care se potrivesc. var
productElements = superProProductListElement.Elements("Produs")
Aici, codul folosește instrucțiunea var pentru a simplifica linia de cod. (Din punct de vedere tehnic, metoda Elements()
returnează o colecție IEnumerorable<XElement>. Acest design oferă XDocument mai multă flexibilitate. Aceasta
înseamnă că metoda Elements() poate returna orice colecție dorește, atâta timp cât colecția acceptă interfața
IEnumerorable<T>.)
Introducerea textului într-un XElement este ușoară. Pur și simplu trebuie să aruncați elementul la tipul de date
corespunzător, așa cum se arată aici:
XElement priceElement = productElement.Element("Preț"); preț zecimal
= (zecimal)priceElement;
Acest lucru funcționează deoarece clasa XElement definește operatori de conversie specializați. Când proiectați un
XElement la o valoare zecimală, de exemplu, XElement își regăsește automat valoarea internă și încearcă să o
convertească în zecimală.
Setarea conținutului textului în interiorul unui element este aproape la fel de ușoară. Pur și simplu atribuiți
noul conținut proprietății Valoare, așa cum se arată aici:
priceElement.Value = (zecimal)priceElement * 2;
Puteți utiliza aceeași abordare pentru a citi și seta atribute cu clasa XAttribute.
Iată o rutină simplă de cod care imită codul de procesare XML pe care l-ați văzut mai devreme cu
XmlTextReader. Scanează elementele disponibile, creează o listă de produse și o afișează într-o grilă.
629
CAPITOLUL XML
18
Încărcați documentul.
XDocument doc = XDocument.Load(fișier);
Treceți prin toate nodurile și creați lista de obiecte Produs . Listă<Produs> produse
= listă nouă<produs>();
Produse. Adăugare(ProdusNou);
}
Afișați rezultatele.
gridResults.DataSource = produse; gridResults.DataBind();
Clasa XElement oferă destul de mulți membri. De exemplu, veți găsi membri pentru trecerea rapidă de la un nod
la altul (FirstNode, LastNode, NextNode, PreviousNode și Parent), proprietăți pentru testarea prezenței copiilor
(HasElements), atribute (HasAttributes), conținut (IsEmpty) și metode pentru inserarea, eliminarea și manipularea
în alt mod a arborelui XML al nodurilor. De exemplu, utilizați Add() pentru a plasa un nou element fiu în interiorul
elementului curent (după orice conținut existent); utilizați AddFirst() pentru a plasa un nou element fiu în interiorul
elementului curent (înaintea oricărui conținut existent); utilizați AddAfterSelf() pentru a insera un element la
același nivel imediat după elementul curent; utilizați AddBeforeSelf() pentru a insera un element la același nivel
chiar înaintea elementului curent; și așa mai departe. De asemenea, puteți utiliza Remove(), RemoveNodes(),
ReplaceWith() și ReplaceNodes() pentru a elimina sau înlocui elemente și alte noduri.
Următorul exemplu arată cum puteți adăuga un produs nou la XDocument:
Sfat: Dacă utilizați clasa XDocument sau XmlTextReader depinde de o serie de factori. În general, utilizați XDocument atunci când doriți să vă ocupați direct de XML, mai degrabă decât să utilizați XML ca o modalitate de a persista unele informații. De asemenea, vă oferă posibilitatea de a modifica structura unui document XML și vă permite să răsfoiți informațiile XML într-un mod mai flexibil (nu doar de la început până la sfârșit). Pe de altă parte, XmlTextReader este cel mai bun atunci când aveți de-a face cu fișiere
630
CAPITOLUL XML
18
Pentru a căuta un XDocument, tot ce trebuie să faceți este să utilizați metoda Descendenți() sau
Descendenți(). Ambele metode vă permit să căutați prin întregul arbore de documente într-un singur pas.
De exemplu, iată cum puteți utiliza Descendenți() pe întregul document SuperProProductList.xml pentru a
obține o listă de prețuri:
XDocument doc = XDocument.Load(fișier);
Găsiți potrivirile.
rezultate var = doc. descendenți("Preț");
Afișați rezultatele.
lblXml.Text = "<b>Found " + rezultate. Count<XElement>(). ToString() + " Meciuri "; lblXml.Text
+= " pentru eticheta de preț: </b><br /><br />"; pentru fiecare (rezultatul XElement în rezultate)
{
lblXml.Text += rezultat. Valoare + "<br />";
}
Metoda Descendenți() este excelentă dacă doriți să găsiți un element pe baza numelui său. Dacă doriți să
utilizați o căutare mai sofisticată, să potriviți doar o parte a unui nume sau să examinați doar o parte a unui
document, aveți două opțiuni. În primul rând, puteți scrie cod care trece prin toate nodurile din XDocument și
verifică fiecare. În al doilea rând, puteți utiliza caracteristica LINQ la XML pentru a efectua o interogare care
extrage obiecte XElement potrivite din XDocument. Aceasta este o potrivire naturală, deoarece clasa
XDocument a fost introdusă inițial ca parte a caracteristicii LINQ în .NET 3.5. Veți afla mult mai multe despre
LINQ, inclusiv cum să îl utilizați pentru a efectua căutări, în capitolul 24.
631
CAPITOLUL 18 XML
Validare XML
XML are un set bogat de standarde suport, dintre care multe depășesc cu mult scopul acestei cărți. Una dintre
cele mai utile din această familie de standarde este schema XML. XML Schema definește regulile la care trebuie
să se conformeze un anumit document XML, cum ar fi elementele și atributele permise, ordinea elementelor și
tipul de date al fiecărui element. Definiți aceste cerințe într-un document schemă XML (XSD).
Când creați un fișier XML pe cont propriu, nu trebuie să creați un fișier XSD corespunzător - în schimb, vă
puteți baza doar pe capacitatea codului dvs. de a se comporta corect. Deși acest lucru este suficient pentru
medii strict controlate, dacă doriți să deschideți aplicația altor programatori sau să îi permiteți să
interopereze cu alte aplicații, ar trebui să creați un XSD. Gândiți-vă astfel: XML vă permite să creați un
limbaj personalizat pentru stocarea datelor, iar XSD vă permite să definiți sintaxa limbajului pe care îl creați.
Ideea de bază din spatele spațiilor de nume XML este că fiecare limbaj de marcare XML are propriul spațiu de
nume, care este utilizat pentru a identifica în mod unic toate elementele conexe. Din punct de vedere tehnic, spațiile
de nume dezambiguizează elementele, clarificând din ce limbaj de marcare aparțin. De exemplu, puteți face
diferența între standardul SuperProProductList și catalogul de produse al altei organizații, deoarece cele două
limbaje XML ar utiliza spații de nume diferite.
Spațiile de nume sunt deosebit de utile în documentele compuse, care conțin secțiuni separate, fiecare cu
un tip diferit de XML. În acest scenariu, spațiile de nume asigură că un element dintr-un spațiu de nume nu
poate fi confundat cu un element din alt spațiu de nume, chiar dacă are același nume de element. Spațiile
de nume sunt, de asemenea, utile pentru aplicațiile care acceptă diferite tipuri de documente XML. Prin
examinarea spațiului de nume, codul dvs. poate determina cu ce tip de document XML lucrează și apoi îl
poate procesa în consecință.
Notă Spațiile
• limbi. Spațiilede
denume
numeNET
XMLsunt
nu sunt legate dede
o construcție spațiile de nume
cod utilizată .NET.
pentru Spațiile detipurilor.
organizarea nume XML
identifică XML diferite
Înainte de a putea plasa elementele XML într-un spațiu de nume, trebuie să alegeți un nume de identificare pentru acel spațiu
de nume. Majoritatea spațiilor de nume XML utilizează identificatori universali de resurse (URI). De obicei, aceste URI-uri
arată ca o adresă URL a unei pagini web. De exemplu, http://www.mycompany.com/mystandard este un nume tipic pentru un
spațiu de nume. Deși spațiul de nume pare să indice o locație validă pe web, acest lucru nu este necesar (și nu ar trebui
presupus).
Motivul pentru care URI-urile sunt utilizate pentru spațiile de nume XML se datorează faptului că este mai
probabil ca acestea să fie unice. De obicei, dacă creați un marcaj XML nou, veți utiliza un URI care indică
spre un domeniu sau un site web pe care îl controlați. În acest fel, puteți fi sigur că nimeni altcineva nu este
probabil să folosească acel URI. De exemplu, http://www.SuperProProducts.com/SuperProProductList
spațiului de nume este mult mai probabil să fie unic decât doar SuperProProductList dacă dețineți
domeniul www.SuperProProducts.com.
632
CAPITOLUL 18
XML
Sfat: Numele spațiului de nume trebuie să se potrivească exact. Dacă modificați scrierea cu majuscule într-o parte a unui spațiu de
•
nume, adăugați un caracter final sau modificați orice alt detaliu, acesta va fi interpretat ca un spațiu de nume diferit de parserul XML.
Pentru a specifica faptul că un element aparține unui anumit spațiu de nume, trebuie doar să adăugați atributul xmlns
la eticheta de pornire și să indicați spațiul de nume. De exemplu, elementul <Preț> afișat aici face parte din spațiul de
nume http://www.SuperProProducts.com/SuperProProductList:
<Price xmlns="http://www.SuperProProducts.com/SuperProProductList">
49.33 </pret>
Dacă nu faceți acest pas, elementul nu va face parte din niciun spațiu de nume.
Ar fi greoi dacă ar trebui să tastați URI-ul complet al spațiului de nume de fiecare dată când scrieți un element într-un
document XML. Din fericire, atunci când atribuiți un spațiu de nume în acest mod, acesta devine spațiu de nume
cel
implicit pentru toate elementele fiu. De exemplu, în documentul XML prezentat aici, elementul <SuperProProductList>
și toate elementele pe care le conține sunt plasate în
http://www.SuperProProducts.com/SuperProProductList spațiului de nume:
xmlns="http://www.SuperProProducts.com/SuperProProductList">
<Produs>
<ID>1</ID>
<Nume>Președinte</Nume>
<Pret>49.33</Pret>
<Disponibil>True</Disponibil>
<Stare>3</Stare>
</Produs>
În documentele compuse, veți avea marcaje din mai multe limbi XML și va trebui să plasați secțiuni diferite în spații de
nume diferite. În această situație, puteți utiliza prefixele spațiului de nume pentru a sorta diferitele spații de nume.
Prefixele spațiului de nume sunt secvențe scurte de caractere pe care le puteți insera în fața unui nume de
etichetă pentru a indica spațiul său de nume. Definiți prefixul în atributul xmlns inserând două puncte (:)
urmat de caracterele pe care doriți să le utilizați pentru prefix. Iată documentul SuperProProductList rescris
pentru a utiliza prefixul super:
<?xml version="1.0"?>
<super:SuperProProductList
>
xmlns:super="http://www.SuperProProducts.com/SuperProProductList" < produs>
super: <super:ID>1</ super:ID>
< Nume>Scaun</ Nume> super: super:< Pret>49.33 </
Pret> super: super: < Disponibil>True</ Disponibil>
super : super:<super:Stare>3</
super:Stare>
CAPITOLUL 18 XML
</super: Produs>
Deși atributul xmlns arată ca un atribut XML obișnuit, nu este. Parserul XML îl interpretează ca o declarație
de spațiu de nume. (Motivul pentru care spațiile de nume XML utilizează atribute XML este unul istoric.
Acest design a asigurat că analizoarele XML vechi care nu înțelegeau spațiile de nume puteau citi în
continuare documente XML mai noi care le foloseau.)
Notă: Atributele acționează puțin diferit față de elemente atunci când vine vorba de spații de nume. Puteți utiliza spațiul de nume
•
prefixe atât cu elemente, cât și cu atribute. Cu toate acestea, atributele nu acordă nicio atenție
spațiului de nume implicit al unui document. Aceasta înseamnă că dacă nu adăugați un prefix de
spațiu de nume la un atribut, atributul nu va fi plasat în spațiul de nume implicit. În schimb, nu va
avea spațiu de nume.
șir ns = "http://www.SuperProProducts.com/SuperProProductList";
w.WriteStartDocument();
w.WriteStartElement("SuperProProductList", ns);
Singurul truc este să vă amintiți să utilizați spațiul de nume pentru fiecare element.
Clasa XDocument se ocupă de spațiile de nume folosind o abordare similară. Mai întâi, definiți un obiect
XNamespace. Apoi, adăugați acest obiect XNamespace la începutul numelui elementului de fiecare dată
când creați un XElement (sau un XAttribute) pe care doriți să îl plasați în acel spațiu de nume. Iată un
exemplu:
XNamespace ns = "http://www.SuperProProducts.com/SuperProProductList";
XDocument doc = noul XDocument( noul XDeclaration("1.0", null, "da"), noul
XComment("Creat cu clasa XDocument."), noul XElement(ns + "
SuperProProductList", noul XElement( "Product ", ns + noul XAttribute(" ID", 1),
noul XAttribute("Nume", "Președinte"),
634
CAPITOLUL 18
XML
De asemenea, poate fi necesar să modificați codul de citire XML. Dacă utilizați XmlTextReader simplu,
viața este simplă, iar codul dvs. va funcționa fără modificări. Dacă este necesar, puteți utiliza proprietatea
XmlTextReader.NamespaceURI pentru a obține spațiul de nume al elementului curent (ceea ce este
important dacă aveți un document compus care îmbină elemente din diferite spații de nume). Dacă utilizați
clasa XDocument, trebuie să luați în considerare spațiul de nume XML atunci când căutați documentul. De
exemplu, atunci când utilizați metoda XmlElement.Element(), trebuie să furnizați numele complet calificat al
elementului adăugând obiectul XNamespace corespunzător la șir cu numele elementului:
Notă Din punct de vedere tehnic, nu este nevoie să utilizați clasa XNamespace, deși vă face codul mai clar. Când
•
adăugați XNamespace la un șir de nume de element, spațiul de nume este pur și simplu înfășurat în
acolade ondulate. Cu alte cuvinte, atunci când combinați http://www.somecompany.com/DVDList
spațiului de nume cu elementul nume Titlu, este echivalent cu șirul
{http://www.somecompany.com/DVDList}Titlu. Această sintaxă funcționează deoarece caracterele acolade
curbate nu sunt permise în numele elementelor obișnuite, deci nu există nicio posibilitate de confuzie.
La prima vedere, acest marcaj pare un pic intimidant. Cu toate acestea, nu este de fapt atât de complicat
pe cât pare. În esență, această schemă indică faptul că un document SuperProProductList constă dintr-o
listă de elemente <Product>. Fiecare element <Product> este un tip complex format dintr-un șir (Nume), o
valoare zecimală (Price) și un întreg (ID). Acest exemplu utilizează a doua versiune a documentului
SuperProProductList pentru a demonstra cum se utilizează atributele într-un fișier schemă.
Disecarea codului . . .
Examinând schema SuperProProductList.xsd, puteți afla câteva puncte importante:
• Documentele schemă utilizează propria formă de marcare XML. În exemplul anterior,
veți vedea rapid că toate elementele sunt plasate în
http://www.w3.org/2001/XMLSchema spațiul de nume utilizând prefixul spațiului de nume xs.
• Fiecare document schemă începe cu un element rădăcină <schemă>.
• Documentul schemă trebuie să specifice spațiul de nume al documentelor pe
care le poate valida. Specifică acest detaliu cu atributul targetNamespace pe
elementul rădăcină <schema>.
• Elementele din interiorul elementului <schema> descriu structura documentului țintă.
Elementul <element> reprezintă un element, în timp ce elementul <atribut> reprezintă un
atribut. Pentru a afla care este numele unui element sau atribut, uitați-vă la atributul name.
De exemplu, puteți spune destul de ușor că primul <element> are numele
SuperProProductList. Acest lucru indică faptul că primul element din documentul validat
trebuie să fie <SuperProProductList>.
• Dacă un element poate conține alte elemente sau are atribute, este considerat un tip
complex. Tipurile complexe sunt reprezentate într-o schemă de elementul
<complexType>. Cel mai simplu tip complex este o secvență, care este reprezentată
într-o schemă de elementul <secvență >. Este necesar ca elementele să fie întotdeauna în
aceeași ordine - ordinea setată în documentul schemă.
• La definirea elementelor, puteți defini numărul maxim de apariții ale unui element
(folosind atributul maxHappens) și numărul minim de apariții (folosind atributul
minHappens ). Dacă omiteți aceste detalii, valoarea implicită a ambelor este 1, ceea
ce înseamnă că fiecare element trebuie să apară exact o dată în documentul țintă.
Utilizați o valoare maxComes nelimitată dacă doriți să permiteți o listă nelimitată. De
exemplu, acest lucru permite să existe un număr nelimitat de elemente <Produs> în
catalogul SuperProProductList. Cu toate acestea, elementul <Preț> trebuie să apară
exact o dată în fiecare <Produs>
• La definirea unui atribut, puteți utiliza atributul use with a value of required pentru ca
atributul respectiv să devină obligatoriu.
636
CAPITOLUL 18
XML
În al doilea rând, trebuie să creați cititorul de validare utilizând metoda statică XmlReader.Create ().
Această metodă are mai multe supraîncărcări, dar versiunea utilizată aici necesită un FileStream (cu
documentul XML) și obiectul XmlReaderSettings care are setările de validare:
Deschideți fișierul XML.
FileStream fs = nou FileStream (fișier, FileMode.Open);
XmlReader din acest exemplu funcționează în același mod ca XmlTextReader pe care l-ați utilizat până
acum, dar adaugă capacitatea de a verifica dacă documentul XML respectă regulile schemei. Acest cititor
lansează o excepție (sau ridică un eveniment) pentru a indica erorile pe măsură ce vă deplasați prin fișierul
XML. Următorul exemplu arată cum puteți crea un cititor de validare care utilizează fișierul
SuperProProductList.xsd pentru a verifica dacă XML în SuperProProductList.xml este valid:
Setați setările de validare.
Setări XmlReaderSettings = nou XmlReaderSettings(); Setări.
Schemas.Add("http://www.SuperProProducts.com/SuperProProductList", schemaFile);
CAPITOLUL XML
18
Citiți documentul.
în timp ce (r.Read())
{
Procesați documentul aici.
Dacă se găsește o eroare, se va arunca o excepție.
}
Fs. Aproape();
Folosind fișierul curent, acest cod va reuși și veți putea accesa fiecare nod din document. Cu toate
acestea, luați în considerare ce se întâmplă dacă faceți modificarea minoră prezentată aici:
<ID produs = "A" nume = "Scaun">
Descărcați de la Wow! eBook <www.wowebook.com>
638
CAPITOLUL XML
18
Pentru a testa validarea, puteți utiliza pagina XmlValidation.aspx din probele online. Vă permite să validați o
listă SuperProProductList validă, precum și alte două versiuni, una cu date incorecte și una cu un element
incorect (a se vedea figura 18-8).
Sfat Deoarece toate obiectele XmlReader procesează XML câte o linie odată, această abordare de validare funcționează cel mai bine și utilizează cea mai mică cantitate de memorie. Dar dacă aveți deja un XDocument în memorie, îl puteți valida într-un mod similar folosind metoda XDocument.Validate().
639
CAPITOLUL 18 XML
• Notă eXtensible Stylesheet Language (XSL) este o familie de standarde pentru căutarea,
formatarea și transformarea documentelor XML. XSLT este standardul specific care se ocupă de
etapa de transformare.
XSLT este ușor de utilizat din punctul de vedere al bibliotecii de clase .NET. Tot ce trebuie să înțelegeți
este cum să creați un obiect XslCompiledTransform (găsit în spațiul de nume System.Xml.Xsl). Utilizați
metoda Load() pentru a specifica o foaie de stil și metoda Transform() pentru a afișa rezultatul într-un fișier
sau flux:
Definiți căile de fișier pe care le utilizează acest cod. Fișierul XSLT și fișierul
sursă // XML există deja, dar fișierul rezultat XML // va fi creat de acest cod.
<xsl:template match="SuperProProductList">
<html> <body> <table border="1">
640
CAPITOLUL XML
18
</xsl:foaie de stil>
Fiecare document XSLT are un element rădăcină xsl:stylesheet. Foaia de stil poate conține unul sau mai
multe șabloane (fișierul eșantion SuperProProductList.xslt are două). În acest exemplu, primul șablon caută
elementul rădăcină SuperProProductList. Când îl găsește, scoate etichetele necesare pentru a începe un
tabel HTML și apoi folosește comanda xsl: apply-templates pentru a se ramifica și a efectua procesarea
pentru orice elemente de produs conținute.
<xsl:template match="SuperProProductList"> <html>
<body> <table border="1"> <xsl:apply-templates
select="Product"/>
Când acest proces este finalizat, etichetele HTML pentru sfârșitul tabelului vor fi scrise:
</table> </body>
</html>
</xsl:template>
La procesarea fiecărui element <Product>, valoarea din atributul ID imbricat, atributul Name și elementul
<Price> este extrasă și scrisă la ieșire utilizând comanda xsl:value-of. Semnul at (@) indică faptul că
valoarea este extrasă dintr-un atribut, nu dintr-un subelement. Fiecare informație este scrisă într-un rând de
tabel.
<xsl:template match="Product"> <tr>
<td><xsl:value-of select="@ID"/></td>
<td><xsl:value-of select="@Name"/></td>
<td><xsl:value-of select="Price"/></td> </tr>
</xsl:template>
Pentru formatare mai avansată, puteți utiliza elemente HTML suplimentare pentru a formata un text cu
caractere aldine sau cursive.
Rezultatul final al acestui proces este fișierul HTML prezentat aici:
641
CAPITOLUL XML
18
<td>1</td>
<td>președinte</td>
<td>49.33</td>
</tr> <tr>
<td>2</td>
<td>Car</td>
<td>43398.55</td>
</tr> <tr>
<td>3</td> <td>Coș cu fructe
proaspete</td>
<td>49.99</td> </tr>
</table> </body> </html>
În secțiunea următoare, veți analiza modul în care această ieșire apare într-un browser de internet.
În general, dacă nu sunteți sigur că aveți nevoie de XSLT, probabil că nu. .NET Framework oferă un set
bogat de instrumente pentru căutarea și manipularea fișierelor XML folosind obiecte și cod, care este cea
mai bună abordare pentru utilizarea XML la scară mică.
Sfat
Pentru a afla mai multe despre XSLT, luați în considerare excelenta carte a lui Jeni Tennison Beginning XSLT 2.0: From Novice to Professional (Apress, 2005).
642
CAPITOLUL XML
18
Dacă atribuiți fișierul SuperProProductList.xml controlului Xml, este posibil să fiți dezamăgiți. Rezultatul este
doar un șir de text interior (prețul fiecărui produs), îngrămădite împreună fără spațiu (a se vedea figura 18-9).
Ultimul cuvânt
Acum că turul XML și ASP.NET se apropie de sfârșit, ar trebui să aveți o înțelegere de bază a ceea ce este
XML, cum arată și de ce l-ați putea folosi într-o pagină web. XML reprezintă un nou instrument pentru
spargerea barierelor dintre companii și platforme - nu este altceva decât un model universal pentru stocarea
și comunicarea tuturor tipurilor de informații.
643
CAPITOLUL 18 XML
XML în sine este o inovație remarcabilă. Cu toate acestea, pentru a profita la maximum de XML, trebuie
să îmbrățișați alte standarde care vă permit să validați XML, să îl transformați și să îl căutați pentru
informații specifice. .NET Framework furnizează clase pentru toate aceste activități în spațiile de nume
sub ramura System.Xml. Pentru a continua explorarea, începeți cu o revizuire cuprinzătoare a
standardelor XML (cum ar fi cea furnizată la http://www.w3schools.com/xml) și apoi scufundați-vă în
biblioteca clasei.
644
PART5
• ■■
• ■■
În mod obișnuit, site-ul dvs. ASP.NET este disponibil pentru oricine se conectează la serverul dvs. web, fie printr-o rețea
locală, fie prin Internet. Deși acest lucru este ideal pentru multe aplicații web (și se potrivește spiritului original al
internetului), nu este întotdeauna o alegere de design adecvată. De exemplu, un site de comerț electronic trebuie să
ofere o experiență de cumpărături sigură pentru a atrage clienți. Un site bazat pe abonament trebuie să limiteze
conținutul pentru a extrage o taxă. Și chiar și un site public larg deschis poate oferi unele resurse sau caracteristici care
nu ar trebui să fie disponibile pentru toți utilizatorii.
ASP.NET oferă un model de securitate extins care facilitează protejarea aplicațiilor web. Deși acest model de securitate
este puternic și profund flexibil, poate părea confuz din cauza numeroaselor straturi diferite pe care le include. O mare
parte din munca de securizare a aplicației dvs. nu este scrierea codului, ci determinarea locurilor potrivite pentru
implementarea strategiei de securitate.
În acest capitol, veți rezolva modelul de securitate ASP.NET încâlcit. Veți învăța două modalități de
autentificare a utilizatorilor: mai întâi, utilizând autentificarea formularelor (care este ideală pentru un site
web public care utilizează o bază de date particularizată) și apoi utilizând autentificarea Windows (care este
ideală pentru o aplicație intranet dintr-o rețea de firmă).
Înțelegerea securității
Primul pas în securizarea aplicațiilor dvs. este să decideți unde aveți nevoie de securitate și ce trebuie să protejeze. De
exemplu, poate fi necesar să blocați accesul pentru a proteja informațiile private. Sau, poate că trebuie doar să impuneți
un sistem de plată pentru conținut. Poate că nu aveți nevoie deloc de securitate, dar doriți o funcție opțională de
conectare pentru a oferi personalizare vizitatorilor frecvenți. Aceste cerințe vor determina abordarea pe care o utilizați.
Securitatea nu trebuie să fie complexă, dar trebuie să fie amplă și multistratificată. De exemplu, luați în considerare un
site de comerț electronic care permite utilizatorilor să vizualizeze rapoarte despre comenzile plasate recent. Probabil că
știți deja prima linie de apărare pe care acest site web ar trebui să o utilizeze - o pagină de conectare care forțează
utilizatorii să se identifice înainte de a putea vedea informații personale. În acest capitol, veți învăța cum să utilizați acest
tip de sistem de autentificare. Cu toate acestea, este important să vă dați seama că, pe cont propriu, acest strat de
protecție nu este suficient pentru a vă asigura cu adevărat sistemul. De asemenea, trebuie să protejați baza de date
back-end cu o parolă puternică și puteți alege chiar să criptați informațiile sensibile înainte de a le stoca (amestecând
astfel încât să nu poată fi citite fără cheia potrivită pentru a le decripta). Pași ca aceștia vă protejează site-ul web de alte
atacuri care depășesc sistemul de autentificare. De exemplu, acestea pot descuraja un angajat nemulțumit cu un cont în
rețeaua locală, un hacker care a obținut acces la rețeaua dvs. prin firewall-ul companiei sau un tehnician neglijent care
aruncă un hard disk utilizat pentru stocarea datelor fără a-l șterge mai întâi.
În plus, va trebui să vânați cu atenție punctele slabe din codul pe care l-ați scris. Un număr surprinzător de
site-uri web cad pradă unor atacuri relativ simple în care un utilizator rău intenționat pur și simplu
manipulează cu un argument șir de interogare sau un pic de HTML în pagina redată. În exemplul de comerț
electronic, trebuie să
647
CAPITOLUL 19 FUNDAMENTE DE SECURITATE
Asigurați-vă că un utilizator care se conectează cu succes nu poate vedea comenzile recente ale altui utilizator.
Imaginați-vă că ați creat o pagină ViewOrders.aspx care preia un argument șir de interogare numit userID, astfel:
http://localhost/InsecureStore/ViewOrders.aspx?userID=4191
Acest exemplu este un coșmar de securitate, deoarece orice utilizator poate modifica cu ușurință
parametrul userID editând adresa URL pentru a vedea informațiile altui utilizator. O soluție mai bună ar fi să
proiectați pagina ViewOrders.aspx astfel încât să obțină ID-ul de utilizator din identitatea de utilizator
conectată în prezent (un truc pe care îl veți învăța în acest capitol) și apoi să o utilizați pentru a construi
comanda corectă a bazei de date.
• Notă Un alt exemplu de vulnerabilitate de securitate introdusă de codificarea slabă este atacul
de injectare SQL foarte comun. Ați învățat să preveniți acest atac utilizând comenzi de baze de
date parametrizate în capitolul 14.
Atunci când proiectați având în vedere securitatea, este important să luați în considerare diferitele căi de
atac. Cu toate acestea, nu puteți anticipa întotdeauna potențialele probleme. Din acest motiv, este foarte
logic să vă stratificați securitatea. Mantra arhitecților de securitate poate fi rezumată astfel: "Nu forțați un
atacator să facă un lucru imposibil pentru a pătrunde în sistemul dvs. - forțați-l să facă mai multe".
Contul de utilizator al site-ului web: Când executați un site web în Visual Studio, serverul web de testare utilizează
contul de utilizator Windows. Drept urmare, codului i se permite să efectueze sarcini (citirea unui fișier, scrierea într-un
jurnal, conectarea la o bază de date și așa mai departe) care ar putea să nu fie permise pe un server web live. Pentru a
vă asigura că totul continuă să funcționeze atunci când implementați aplicația web, trebuie să știți mai multe despre
modul în care IIS și ASP.NET utilizează conturile Windows - un subiect pe care îl veți aborda în capitolul 26.
Configurație de securitate: O altă diferență constă în modul în care configurați setările de securitate,
cum ar fi tipul de autentificare utilizat de aplicația web și regulile de autorizare care guvernează cine
poate accesa diferite pagini. Într-un site de testare, puteți aplica aceste setări în două moduri: editând
fișierul web.config sau executând instrumentul de administrare a site-ului web (WAT), pe care l-ați
întâlnit pentru prima dată în capitolul 5. Într-o aplicație implementată, puteți continua să utilizați setările
de securitate pe care le-ați setat în timpul dezvoltării, puteți edita manual web.config sau puteți utiliza
instrumentul convenabil de configurare IIS Manager.
În plus, există unele setări de securitate care intră în joc numai atunci când configurați un site web implementat. De
exemplu, utilizați IIS Manager pentru a activa Secure Sockets Layer (SSL), un standard de criptare care se asigură că
hackerii rău intenționați nu pot trage cu urechea la mesajele trimise între utilizator și serverul web. De asemenea, aveți
posibilitatea să configurați protocoalele specifice pe care IIS le utilizează pentru autentificarea Windows (cu alte
cuvinte, standardul exact de securitate utilizat pentru a transmite informații despre utilizator și a conecta pe cineva).
Acest capitol explică modul de aplicare a securității unui site web în timpul dezvoltării. Conceptele și
detaliile pe care le veți învăța se vor aplica la fel de bine unei aplicații implementate. Cu toate acestea, dacă
doriți să ajustați configurația de securitate după ce implementați aplicația sau dacă doriți să obțineți mai
multe progrese cu setările de autentificare SSL și Windows, merită să aflați despre IIS Manager. Capitolul
26 conține toate detaliile.
648
CAPITOLUL FUNDAMENTE DE SECURITATE
19
Autentificare și autorizare
Două concepte formează baza oricărei discuții despre securitate:
Autentificare: Acesta este procesul de determinare a identității unui utilizator și de forțare a utilizatorilor să dovedească
cine pretind că sunt. De obicei, aceasta implică introducerea acreditărilor (de obicei un nume de utilizator și o parolă)
într-un fel de pagină sau fereastră de conectare. Aceste acreditări sunt apoi autentificate pentru conturile de utilizator
Windows de pe un computer, o listă de utilizatori dintr-un fișier sau o bază de date back-end.
Autorizare: Odată ce un utilizator este autentificat, autorizarea este procesul prin care se determină
dacă acel utilizator are suficiente permisiuni pentru a efectua o anumită acțiune (cum ar fi vizualizarea
unei pagini sau preluarea informațiilor dintr-o bază de date). Windows impune unele verificări de
autorizare (de exemplu, atunci când deschideți un fișier), dar codul dvs. va dori probabil să impună
propriile verificări (de exemplu, atunci când un utilizator efectuează o activitate în aplicația web, cum ar
fi trimiterea unei comenzi, atribuirea unui proiect sau oferirea unei promoții).
Autentificarea și autorizarea sunt cele două pietre de temelie ale creării unui site securizat bazat pe utilizator.
Sistemul de operare Windows oferă o analogie bună. Când porniți computerul pentru prima dată, furnizați un
ID de utilizator și o parolă, autentificându-vă astfel în sistem. După acel moment, de fiecare dată când
interacționați cu o resursă restricționată (cum ar fi un fișier, o bază de date, o cheie de registry etc.),
Windows efectuează în liniște verificări de autorizare pentru a se asigura că contul dvs. de utilizator are
drepturile necesare. Puteți utiliza două tipuri de autentificare pentru a securiza un site web ASP.NET:
Autentificarea formularelor: Cu autentificarea formularelor, ASP.NET este responsabil de autentificarea utilizatorilor,
urmărirea acestora și autorizarea fiecărei solicitări. De obicei, autentificarea formularelor funcționează împreună cu o
bază de date în care stocați informații despre utilizator (cum ar fi numele de utilizator și parolele), dar aveți
flexibilitate completă. Puteți chiar să stocați informații despre utilizator într-un fișier text simplu sau să scrieți un cod
de conectare personalizat care apelează un serviciu la distanță. Autentificarea formularelor este cel mai bun și mai
flexibil mod de a rula un site de abonament sau un magazin de comerț electronic.
Autentificare Windows: Cu autentificarea Windows, serverul web obligă fiecare utilizator să se conecteze ca
utilizator Windows. (În funcție de configurația specifică pe care o utilizați, acest proces de conectare poate
avea loc automat, așa cum se întâmplă în serverul web de testare Visual Studio sau poate necesita ca
utilizatorul să introducă un nume și o parolă într-o casetă de dialog de conectare.) Acest sistem necesită ca
toți utilizatorii să aibă conturi de utilizator Windows pe server (deși utilizatorii ar putea partaja conturi). Acest
scenariu nu este potrivit pentru o aplicație web publică, dar este adesea ideal cu un intranet sau un site
specific companiei, conceput pentru a furniza resurse pentru un set limitat de utilizatori.
Vă veți concentra pe aceste două abordări în acest capitol. În primul rând, veți explora modelul de
autentificare a formularelor, care este perfect pentru site-urile web accesibile publicului. Apoi, veți lua în
considerare autentificarea Windows, ceea ce are sens în medii de rețea mai mici, unde aveți un grup de
utilizatori cunoscuți.
Autentificarea formularelor
În programarea ASP de modă veche, dezvoltatorii trebuiau să-și creeze propriile sisteme de securitate. O abordare
comună a fost inserarea unui mic fragment de cod la începutul fiecărei pagini securizate. Acest cod ar verifica
existența unui cookie personalizat. Dacă cookie-ul nu ar exista, utilizatorul ar fi redirecționat către o pagină de
conectare, unde cookie-ul ar fi creat după o autentificare reușită.
ASP.NET utilizează aceeași abordare în modelul său de autentificare a formularelor. Sunteți în continuare responsabil
pentru crearea paginii de conectare (deși puteți utiliza un set de controale special concepute pentru a vă ajuta, așa cum
649
CAPITOLUL FUNDAMENTE DE SECURITATE
19
este descris în capitolul 20). Cu toate acestea, nu este necesar să creați manual cookie-ul de securitate sau să îl verificați în
pagini securizate, deoarece ASP.NET gestionează automat aceste sarcini. De asemenea, beneficiați de suportul ASP.NET
pentru algoritmi sofisticați de validare, care fac aproape imposibil ca utilizatorii să-și falsifice propriile cookie-uri sau să încerce
alte trucuri de hacking pentru a vă păcăli aplicația să le ofere acces.
Figura 19-1 prezintă o diagramă de securitate simplificată a modelului de autentificare a formularelor în ASP.NET.
650
CAPITOLUL FUNDAMENTE DE SECURITATE
19
Setări Web.config
Definiți tipul de securitate în fișierul web.config utilizând eticheta <autentificare>. Următorul exemplu
configurează aplicația pentru a utiliza autentificarea formularelor utilizând eticheta <autentificare>. De
asemenea, setează câteva dintre cele mai importante setări utilizând o etichetă imbricată <formulare>. Și
anume, stabilește numele cookie-ului de securitate, durata de timp în care acesta va fi considerat valid (în
minute) și pagina care permite utilizatorului să se conecteze.
<configuration> <system.web> <authentication
mode="Forms"> <forms name="MyAppCookie"
loginUrl="~/Login.aspx" protection="All"
timeout="30" path="/" /> </authentication> ...
</system.web> </configuration>
Tabelul 19-1 descrie aceste setări. Toate furnizează valori implicite, deci nu trebuie să le setați în mod
explicit. Pentru o listă completă a atributelor acceptate, consultați Ajutorul Visual Studio.
Atribut Descriere
nume Numele cookie-ului HTTP de utilizat pentru autentificare (implicit este . ASPXAUTH).
Dacă mai multe aplicații rulează pe același server web, ar trebui să dați cookie-ului de
securitate al fiecărei aplicații un nume unic.
loginUrl Pagina dvs. de conectare personalizată, unde utilizatorul este redirecționat dacă nu este
găsit niciun cookie de autentificare valid. Valoarea implicită este Login.aspx.
protecție Tipul de criptare și validare utilizat pentru cookie-ul de securitate (poate fi Toate, Niciuna,
Criptare sau Validare). Validarea asigură că cookie-ul nu este modificat în timpul
tranzitului și criptarea (de obicei Triple-DES) este utilizată pentru a codifica conținutul
acestuia. Valoarea implicită este All.
Timeout Numărul de minute de inactivitate înainte de expirarea cookie-ului. ASP.NET va reîmprospăta
cookie-ul de fiecare dată când primește o solicitare. Valoarea implicită este 30.
drum Calea pentru cookie-urile emise de aplicație. Valoarea implicită (/) este recomandată,
deoarece neconcordanțele de caz pot împiedica trimiterea cookie-ului împreună cu o solicitare.
Reguli de autorizare
Dacă efectuați aceste modificări în fișierul web.config al unei aplicații și solicitați o pagină, veți observa că nu
se întâmplă nimic neobișnuit, iar pagina web este servită în mod normal. Acest lucru se datorează faptului că,
deși ați activat autentificarea formularelor pentru aplicația dvs., nu ați restricționat utilizatorii anonimi. Cu alte
cuvinte, ați ales sistemul pe care doriți să îl utilizați pentru autentificare, dar momentan niciuna dintre paginile
dvs. nu are nevoie de autentificare.
651
CAPITOLUL FUNDAMENTE DE SECURITATE
19
Pentru a controla cine poate și cine nu poate accesa site-ul dvs. web, trebuie să adăugați reguli de control al accesului
la secțiunea <autorizare> a fișierului web.config. Iată un exemplu care dublează comportamentul implicit:
</system.web> </configuration>
Asteriscul (*) este un metacaracter care permite în mod explicit tuturor utilizatorilor să utilizeze aplicația, chiar și celor
care nu au fost autentificați. Dar chiar dacă nu includeți această linie în fișierul web.config al aplicației dvs., acesta este în
continuare comportamentul pe care îl veți vedea, deoarece setările implicite ale ASP.NET permit tuturor utilizatorilor. (Din
punct de vedere tehnic, acest comportament se întâmplă deoarece există o regulă <allow users="*"> în fișierul rădăcină
web.config. Dacă sunteți curioși, puteți găsi acest fișier într-un director precum c:\Windows\Microsoft.NET\Framework\
[Version]\Config, unde [Version] este versiunea de ASP.NET instalată, cum ar fi v4.0.30319.) Pentru a schimba acest
comportament, trebuie să adăugați în mod explicit o regulă mai restrictivă, așa cum se arată aici:
<autorizare>
<deny users="?" />
</autorizare>
Semnul întrebării (?) este un metacaracter care se potrivește cu toți utilizatorii anonimi. Prin includerea acestei reguli în
fișierul web.config, specificați că utilizatorii anonimi nu sunt permiși. Fiecare utilizator trebuie să fie autentificat și fiecare
solicitare a utilizatorului va necesita cookie-ul de securitate. Dacă solicitați acum o pagină în directorul de aplicații,
ASP.NET va detecta că solicitarea nu este autentificată. Apoi va redirecționa solicitarea către pagina de conectare
specificată de atributul loginUrl din fișierul web.config. (Dacă încercați acest pas chiar acum, procesul de redirecționare
va cauza o eroare, cu excepția cazului în care ați creat deja pagina de conectare.)
Acum luați în considerare ce se întâmplă dacă adăugați mai multe reguli la secțiunea de autorizare:
La evaluarea regulilor, ASP.NET scanează lista de sus în jos și apoi continuă cu setările din orice fișier .config
moștenit dintr-un director părinte, terminând cu setările din fișierul machine.config de bază. De îndată ce găsește o
regulă aplicabilă, își oprește căutarea. Astfel, în cazul precedent, se va stabili că regula <permite utilizatorilor = "* ">
se aplică cererii curente și nu va evalua a doua linie. Aceasta înseamnă că aceste reguli vor permite tuturor
utilizatorilor, inclusiv utilizatorilor anonimi. Dar luați în considerare ce se întâmplă dacă aceste două linii sunt
inversate:
<authorization> <deny
users="?" /> <allow
users="*" />
</authorization>
652
CAPITOLUL FUNDAMENTE DE SECURITATE
19
Acum, aceste reguli vor refuza utilizatorii anonimi (prin potrivirea primei reguli) și vor permite tuturor celorlalți
utilizatori (prin potrivirea celei de-a doua reguli).
Notă
Nu puteți modifica setările etichetei <autentificare> în fișierul web.config al unui subdirector din aplicație. În schimb, toate directoarele din aplicație trebuie să utilizeze același sistem de autentificare. Cu toate acestea, fiecare director poate avea propriile reguli de autorizare.
<authorization> <allow
users="*" />
</authorization> ...
</system.web>
653
CAPITOLUL FUNDAMENTE DE SECURITATE
19
</system.web>
</locație>
</locație>
</configurare>
În acest exemplu, toate fișierele din aplicație sunt permise, cu excepția SecuredPage.aspx și
AnotherSecuredPage.aspx, care au o regulă suplimentară de acces care refuză utilizatorii anonimi.
Observați că, chiar și atunci când utilizați mai multe secțiuni <locație> pentru a furniza seturi diferite de
reguli de autorizare, includeți în continuare o singură secțiune <autentificare>. Acest lucru se datorează
faptului că o aplicație web poate utiliza un singur tip de autentificare.
Sfat
De asemenea, puteți utiliza etichetele de locație pentru a seta reguli pentru un anumit subdirector. Depinde de dvs. dacă doriți să utilizați această abordare sau preferați să creați fișiere web.config separate pentru fiecare subdirector, așa cum este descris în secțiunea anterioară.
Veți observa că prima regulă din acest exemplu neagă toți utilizatorii anonimi. În caz contrar, următoarele reguli nu
vor avea niciun efect, deoarece utilizatorii nu vor fi obligați să se autentifice.
Următoarele reguli permit în mod explicit doi utilizatori. Tuturor celorlalte solicitări ale utilizatorilor li se va
refuza accesul, chiar dacă sunt autentificate.
<authorization> <deny users="?" /> <allow users="matthew,sarah" /> <deny users="*" /> </authorization>
654
CAPITOLUL FUNDAMENTE DE SECURITATE
19
Nu confundați aceste nume de utilizator cu numele conturilor de utilizator Windows care sunt configurate pe
serverul web. Atunci când utilizați autentificarea formularelor, modelul de securitate al aplicației este separat
de sistemul de cont de utilizator Windows. Aplicația dvs. atribuie numele de utilizator atunci când un utilizator
se conectează prin pagina de conectare. Adesea, veți alege nume de utilizator care corespund ID-urilor
dintr-o bază de date. Singura cerință este ca numele dvs. de utilizator să fie unice.
CÂȘTIGUL
Aveți un alt mod de a configura regulile de autentificare și autorizare. În loc să editați manual fișierul
web.config, puteți utiliza WAT din Visual Studio. WAT vă ghidează prin proces, deși veți găsi că este încă
important să înțelegeți ce modificări se fac de fapt în fișierul web.config. De asemenea, este adesea mai
rapid să introduceți manual o listă de reguli de autorizare, mai degrabă decât să utilizați WAT.
Pentru a utiliza WAT pentru acest tip de configurare, selectați Website ASP.NET Configuration din the menu. Apoi,
faceți clic pe fila Securitate. Veți vedea fereastra prezentată în Figura 19-2, care vă oferă linkuri pentru a seta tipul de
autentificare, a defini regulile de autorizare (utilizând secțiunea Reguli de acces) și a activa securitatea bazată pe roluri.
(Securitatea bazată pe roluri este o caracteristică opțională de nivel superior pe care o puteți utiliza cu autentificarea
formularelor. Veți afla mai multe despre cum funcționează și cum să îl activați în capitolul următor.)
Pentru a seta o aplicație pentru a utiliza autentificarea formularelor, urmați acești pași:
1. Faceți clic pe Selectați tipul de autentificare.
655
CAPITOLUL FUNDAMENTE DE SECURITATE
19
Sfat: opțiunile Selectați autentificarea sunt formulate într-un mod ușor înșelător. Este adevărat că aplicațiile care au utilizatori care se conectează de pe tot internetul vor folosi cu siguranță autentificarea formularelor. Cu toate acestea, aplicațiile care rulează într-o rețea locală pot utiliza și autentificarea formularelor - totul depinde de modul în care se conectează și dacă doriți să utilizați informațiile din conturile existente. Cu alte cuvinte, un intranet local vă oferă opțiunea de
Apoi, este timpul să definiți regulile de autorizare. Pentru aceasta, faceți clic pe linkul Creare reguli de acces.
(De asemenea, puteți modifica regulile existente făcând clic pe linkul Gestionare reguli de acces.) Folosind
pagina ușor complicată prezentată în Figura 19-3, aveți posibilitatea de a crea o regulă care să permită sau să
restricționeze anumiți utilizatori la întregul site sau la o anumită pagină sau subfolder. De exemplu, regula din
Figura 19-3 va refuza utilizatorului jenny din întregul site odată ce faceți clic pe OK pentru a-l adăuga.
656
CAPITOLUL FUNDAMENTE DE SECURITATE
19
Pentru a gestiona mai multe reguli, va trebui să faceți clic pe linkul Gestionare reguli de acces. Acum veți
avea șansa de a schimba ordinea regulilor (și, prin urmare, prioritatea, așa cum este descris mai devreme),
așa cum se arată în Figura 19-4. Dacă aveți un număr mare de reguli de creat, este posibil să descoperiți că
este mai ușor să editați manual fișierul web.config. S-ar putea să doriți doar să creați o regulă inițială pentru a
vă asigura că este în locul potrivit și apoi să copiați și să lipiți calea spre succes.
Fila Securitate este puțin copleșitoare la prima vedere, deoarece include câteva caracteristici pe care nu le-ați
prezentat încă. De exemplu, fila Securitate vă permite, de asemenea, să creați și să gestionați înregistrări și
roluri de utilizator, atât timp cât sunteți dispus să utilizați structura de bază de date predefinită de care
ASP.NET necesită. Veți afla mai multe despre aceste detalii, care fac parte dintr-o caracteristică largă numită
membru, în capitolul următor. Pentru moment, vă veți concentra pe procesul de autentificare și autorizare.
Pagina de conectare
După ce ați specificat modul de autentificare și regulile de autorizare, trebuie să construiți pagina de conectare reală, care
este o pagină .aspx obișnuită care solicită informații de la utilizator și decide dacă utilizatorul trebuie autentificat.
ASP.NET oferă o clasă specială FormsAuthentication în spațiul de nume System.Web.Security, care oferă
metode statice care ajută la gestionarea procesului. Tabelul 19-2 descrie cele mai importante metode din
această clasă.
657
CAPITOLUL FUNDAMENTE DE SECURITATE
19
Membru Descriere
FormsCookiePath O proprietate doar în citire care furnizează calea setată pentru formulare
Cookie de autentificare.
Autentificare() Verifică un nume de utilizator și o parolă în raport cu o listă de conturi care pot fi
introdus în fișierul web.config.
cookie criptat.
GetRedirectUrl() Furnizează adresa URL a paginii solicitate inițial. Ai putea folosi acest lucru
cu SetAuthCookie() pentru a conecta un utilizator la o aplicație și a face o
decideți în codul dvs. dacă să redirecționați către pagina solicitată sau să utilizați un
pagină implicită mai potrivită.
HashPasswordForStoringIn Criptează un șir de text utilizând algoritmul specificat (SHA1 sau MD5).
ConfigFile() Această valoare hash oferă o modalitate sigură de a stoca o parolă criptată
într-un fișier sau într-o bază de date.
O simplă pagină de conectare poate pune aceste metode la lucru cu puțin cod. Pentru a încerca, începeți prin activarea
autentificării formularelor și refuzarea utilizatorilor anonimi în web.config, așa cum este descris mai devreme:
<authorization> <deny
users="?" /> <allow
658
CAPITOLUL FUNDAMENTE DE SECURITATE
19
users="*" />
</authorization> ...
</system.web> </configuration>
Acum, utilizatorii vor fi redirecționați către o pagină de conectare numită Login.aspx pe care trebuie să o
creați. Figura 19-5 prezintă un exemplu de pagină simplă de conectare pe care ați putea să o construiți.
Când utilizatorul face clic pe butonul Login, pagina verifică dacă utilizatorul a tastat parola Secret și apoi
utilizează metoda RedirectFromLoginPage() pentru a conecta utilizatorul. Iată codul complet al paginii:
}
Metoda RedirectFromLoginPage() necesită doi parametri. Primul stabilește numele utilizatorului. A doua este o variabilă
booleană care specifică dacă doriți să creați un cookie persistent (unul care rămâne pe hard disk-ul utilizatorului pentru o
perioadă mai lungă de timp). Cu toate acestea, într-un efort de a fi mai sigur, ASP.NET nu mai onorează această proprietate în
659
CAPITOLUL FUNDAMENTE DE SECURITATE
19
modul în care a fost proiectată inițial. (Veți afla mai multe despre cookie-urile persistente în scurt timp, în secțiunea numită
"Cookie-uri persistente".) Evident, abordarea utilizată în pagina de conectare nu este teribil de sigură - verifică pur și simplu
dacă utilizatorul furnizează o parolă codificată. Într-o aplicație reală, probabil că ați verifica numele de utilizator și parola
împotriva informațiilor dintr-o bază de date și ați conecta utilizatorul numai dacă informațiile se potrivesc exact. Puteți scrie
acest cod cu ușurință folosind programarea ADO.NET pe care ați învățat-o în partea 4, deși necesită un pic de cod obositor.
Veți lua în considerare modalități mai practice de a îndeplini această sarcină în capitolul următor.
Puteți testa acest cod cu site-ul web FormsSecurity inclus în codul descărcabil pentru acest capitol. Dacă
solicitați fișierul SecuredPage.aspx, veți fi redirecționat către Login.aspx. După introducerea parolei corecte,
veți reveni la SecuredPage.aspx.
Puteți accesa obiectul Utilizator din codul dvs., deoarece este o proprietate a obiectului Pagină curent.
Obiectul Utilizator furnizează informații despre utilizatorul conectat în prezent. Este destul de simplu - de
fapt, utilizatorul oferă o singură proprietate și o singură metodă:
660
CAPITOLUL FUNDAMENTE DE SECURITATE
19
ÎNȚELEGEREA IDENTITĂȚILOR
Obiectul Utilizator este standardizat astfel încât să poată funcționa cu orice tip de sistem de autentificare. O
consecință a acestui design este că proprietatea User.Identity returnează un alt tip de obiect, în funcție de
tipul de autentificare pe care îl utilizați.
De exemplu, atunci când se utilizează autentificarea formularelor, obiectul identitate este o instanță a
clasei FormsIdentity. Când utilizați autentificarea Windows, obțineți în schimb un obiect WindowsIdentity.
Și dacă utilizatorul nu este conectat deloc, veți obține o identitate generică. (Toate aceste clase
implementează interfața IIdentity, care le standardizează.)
De cele mai multe ori, nu trebuie să vă faceți griji cu privire la această sleight of hand. Dar, ocazional,
poate doriți să proiectați proprietatea User.Identity la un tip mai specific pentru a obține acces la o
informație suplimentară. De exemplu, obiectul FormsIdentity furnizează tichetul de securitate (într-o
proprietate numită Ticket), care nu este disponibil prin interfața standard IIdentity. Acest tichet este o
instanță a clasei FormsAuthenticationTicket și oferă câteva detalii diverse, cum ar fi ora la care utilizatorul
s-a conectat și când va expira biletul. În mod similar, obiectul WindowsIdentity furnizează informații
suplimentare care se referă la conturile Windows (cum ar fi dacă utilizatorul curent utilizează un cont
invitat sau un cont de sistem). Veți vedea un exemplu al acestei tehnici mai târziu în acest capitol, în
secțiunea "Autentificare Windows".
Deconectarea
Orice aplicație web care utilizează autentificarea formularelor ar trebui să aibă, de asemenea, un buton
proeminent de deconectare care distruge cookie-ul de autentificare a formularelor:
Sfat: În capitolul următor, veți învăța cum să simplificați viața cu comenzile de conectare. Aceste controale vă permit să construiți pagini de conectare (și alte tipuri de interfețe de utilizator legate de securitate) fără cod. Cu toate acestea, ele necesită o altă caracteristică - calitatea de membru - pentru a funcționa.
661
CAPITOLUL FUNDAMENTE DE SECURITATE
19
Cookie-uri persistente
În unele situații, este posibil să utilizați autentificarea formularelor pentru personalizare în loc de securitate. În această
situație, puteți decide să permiteți cookie-urile persistente. Un cookie de autentificare persistent rămâne pe hard disk-ul
utilizatorului și menține utilizatorul conectat ore întregi, zile sau săptămâni - chiar dacă utilizatorul închide și redeschide
browserul.
Crearea unui cookie persistent necesită un pic mai mult cod decât crearea unui cookie standard de autentificare a
formularelor. În loc să utilizați metoda RedirectFromLoginPage(), trebuie să creați manual biletul de autentificare,
să setați timpul de expirare, să îl criptați, să îl atașați la solicitare și apoi să redirecționați utilizatorul către pagina
solicitată. Toate aceste sarcini sunt ușoare, dar este important să le îndepliniți pe toate în ordinea corectă. (Când
utilizați cookie-uri nepersistente, RedirectFromLoginPage () se ocupă automat de toate aceste sarcini.)
Cookie-urile persistente prezintă, de asemenea, un potențial risc de securitate, deoarece un alt utilizator ar putea folosi
același computer pentru a accesa paginile securizate, fără a fi obligat să se autentifice. Dacă doriți să permiteți
utilizatorului să creeze un cookie persistent, ar trebui să îl faceți opțional, deoarece utilizatorul poate dori să acceseze
site-ul dvs. de pe un computer public sau partajat. În general, site-urile care utilizează această tehnică includ o casetă
de selectare cu text, cum ar fi Păstrează-mă conectat.
Următorul cod examinează o casetă de selectare numită chkPersist. Dacă este selectat, codul creează
un cookie persistent care durează 20 de zile (deși puteți modifica acest detaliu la orice interval de timp
doriți).
Efectuați autentificarea.
dacă (txtPassword.Text.ToLower() == "secret")
{
dacă (chkPersist.Checked) { // Utilizați un cookie
persistent care durează 20 de zile.
Creați cookie-ul pentru bilet și puneți biletul în interior. Cookie HttpCookie = nou
HttpCookie (FormsAuthentication.FormsCookieName, encryptedTicket); Dați
cookie-ului și biletului de autentificare aceeași expirare. prăjitură. Expiră = bilet.
Expirare;
662
CAPITOLUL FUNDAMENTE DE SECURITATE
19
autentificare.
FormsAuthentication.RedirectFromLoginPage(
txtName.Text, false); }
Autentificare Windows
Cu autentificarea Windows, serverul web se ocupă de procesul de autentificare. ASP.NET pur și simplu pune această
identitate la dispoziția codului dvs. pentru verificările de securitate.
Când utilizați autentificarea Windows, forțați utilizatorii să se conecteze la IIS înainte de a li se permite să acceseze
conținut securizat în site-ul dvs. Informațiile de conectare ale utilizatorului pot fi transmise în mai multe moduri (în funcție
de mediul de rețea, browserul solicitant și modul în care este configurat IIS), dar rezultatul final este că utilizatorul este
autentificat utilizând un cont Windows local. De obicei, acest lucru face ca autentificarea Windows să fie cea mai potrivită
pentru scenariile intranet, în care un set limitat de utilizatori cunoscuți este deja înregistrat pe un server de rețea.
Pentru a implementa securitatea bazată pe Windows cu utilizatori cunoscuți, trebuie să urmați trei pași:
1. Setați modul de autentificare la autentificarea Windows în fișierul web.config.
(Dacă preferați un instrument grafic, puteți utiliza WAT în timpul dezvoltării sau IIS
Manager după implementare.)
2. Dezactivați accesul anonim pentru un director utilizând o regulă de autorizare.
3. Configurați conturile de utilizator Windows pe serverul dvs. web (dacă nu
sunt deja prezente).
Veți parcurge acești pași în secțiunile următoare.
Notă Serverul web încorporat Visual Studio nu acceptă utilizatori anonimi cu autentificare Windows. În schimb, Visual Studio vă conectează automat la serverul de testare utilizând contul Windows. Prin urmare, nu este nevoie să utilizați o regulă de autorizare care refuză utilizatorii anonimi. Cu toate acestea, este încă o practică bună să o adăugați, deoarece la un moment dat veți implementa aplicația la IIS și va trebui să refuzați în mod explicit utilizatorii anonimi.
Setări Web.config
Pentru a utiliza autentificarea Windows, trebuie să vă asigurați că elementul <autentificare> este setat
corespunzător în fișierul web.config. Iată cum:
<configuration>
<system.web>
<mod de autentificare = "Windows" / >
663
CAPITOLUL FUNDAMENTE DE SECURITATE
19
<autorizare>
<deny users="?" />
</authorization> ...
</system.web> </configuration>
În prezent, există o singură regulă de autorizare, care folosește semnul întrebării pentru a refuza toți utilizatorii
anonimi. Acest pas este esențial pentru autentificarea Windows (așa cum este pentru autentificarea formularelor).
Fără acest pas, utilizatorul nu va fi niciodată obligat să se conecteze.
În mod ideal, nici măcar nu veți vedea că are loc procesul de conectare. În schimb, Internet Explorer va transmite
acreditările utilizatorului Windows curent, pe care serverul web le utilizează automat. Serverul web integrat Visual Studio
funcționează întotdeauna în acest fel. IIS funcționează și în acest fel, cu condiția să fi configurat autentificarea Windows
integrată (așa cum este descris în capitolul 26) și browserul să o accepte.
De asemenea, puteți adăuga elemente <permite> și <refuzați> pentru a permite sau restricționa în mod
specific utilizatorii din anumite fișiere sau directoare. Spre deosebire de autentificarea formularelor, trebuie
să specificați numele serverului sau domeniului în care există contul. De exemplu, această regulă permite
contul de utilizator matthew, care este definit pe computerul numit WebServer:
<permite utilizatorilor = "WebServer \ matthew" / >
Pentru o comandă rapidă, puteți utiliza localhost (sau doar o perioadă) pentru a vă referi la un cont
de pe computerul curent, așa cum se arată aici:
<allow users=".\matthew" />
De asemenea, aveți posibilitatea să restricționați anumite tipuri de utilizatori, cu condiția ca conturile
lor să fie membre ale aceluiași grup Windows, utilizând atributul roluri:
În acest exemplu, toți utilizatorii care sunt membri ai grupurilor SalesAdministrator sau SalesStaff vor fi autorizați
automat să acceseze paginile ASP.NET din acest director. Cererile de la utilizatorul matthew vor fi refuzate, cu
excepția cazului în care acesta este membru al grupului SalesAdministrator sau SalesStaff. Amintiți-vă, ASP.NET
examinează regulile în ordinea în care apar și se oprește atunci când găsește o potrivire. Inversarea acestor două linii
de autorizare ar asigura că utilizatorul Matthew a fost întotdeauna refuzat, indiferent de apartenența la grup.
De asemenea, puteți examina programatic apartenența la grup a unui utilizator în codul dvs., așa cum se
arată aici. Deoarece șirul include o bară oblică inversă, trebuie să vă amintiți să îl dublați sau puteți dezactiva
C# scăpând cu un semn precedent la (@).
protejat void Page_Load(Obiect expeditor, EventArgs e)
{
dacă (User.IsInRole(@"MyDomainName\SalesAdministrators")) { // Nu
faceți nimic; pagina ar trebui accesată normal, deoarece // utilizatorul are
privilegii de administrator.
}
altceva
{
664
CAPITOLUL 19 FUNDAMENTE DE
SECURITATE
În acest exemplu, codul verifică apartenența la un grup Windows particularizat numit SalesAdministrators.
Dacă doriți să verificați dacă un utilizator este membru al unuia dintre grupurile încorporate, nu este
necesar să specificați un computer sau un nume de domeniu. În schimb, utilizați această sintaxă:
dacă (User.IsInRole(@"BUILTIN\Administrators")) {
// (Codul merge aici.)
Pentru mai multe informații despre regulile <permite> și <refuză> și configurarea fișierelor și directoarelor
individuale, consultați discuția din secțiunea "Reguli de autorizare" de mai devreme în acest capitol. Rețineți
că nu aveți nicio modalitate de a regăsi o listă de grupuri disponibile pe serverul web (care ar încălca
securitatea), dar puteți afla numele rolurilor Windows implicite încorporate utilizând enumerarea
System.Security.Principal.WindowsBuiltInRole. Tabelul 19-3 descrie aceste roluri. Nu toate se vor aplica
ASP.NET utilizării, deși Administratorul, Oaspetele și Utilizatorul probabil că se vor aplica.
Tabelul 19-3. Roluri implicite Windows
Rol Descriere
Administrator Utilizatorii cu acces complet și nerestricționat la computer sau domeniu. (În cazul în
care computerul server web utilizează controlul contului de utilizator [UAC], Windows
va reține privilegiile de administrator de la conturile de administrator, pentru a reduce
riscul de viruși și alte coduri rău intenționate.)
BackupOperator Utilizatorii care pot suprascrie anumite restricții de securitate numai ca parte a
operațiunilor de copiere de rezervă sau restaurare.
Operator de imprimare Îmi place utilizatorul, dar cu privilegii suplimentare pentru preluarea controlului asupra unei imprimante.
Replicator Îmi place utilizatorul, dar cu privilegii suplimentare pentru a accepta reproducerea fișierelor într-un domeniu.
Utilizator Utilizatorii sunt împiedicați să efectueze modificări la nivel de sistem și pot rula numai aplicații
certificate (consultați http://www.microsoft.com/windowsserver2008/en/us/isv.aspx pentru mai
multe informații).
CAPITOLUL FUNDAMENTE DE SECURITATE
19
666
CAPITOLUL FUNDAMENTE DE SECURITATE
19
Ultimul cuvânt
În acest capitol, ați aflat despre arhitectura de securitate multistratificată din ASP.NET și despre modul în
care vă puteți proteja paginile web și serviciile web utilizând o pagină de conectare particularizată sau
autentificarea Windows. În capitolul următor, veți continua să vă dezvoltați cunoștințele, luând în considerare
câteva caracteristici suplimentare care vă pot simplifica viața și vă pot îmbunătăți securitatea. Veți învăța
cum să obțineți ASP.NET pentru a crea o bază de date de bază a utilizatorilor pentru site-ul dvs. (completată
cu criptarea parolei), scutindu-vă de crearea acesteia sau de scrierea oricărui cod ADO.NET. De asemenea,
veți extinde regulile de autorizare învățând cum puteți grupa utilizatorii autentificați în formulare în grupuri
logice, fiecăruia fiindu-i atribuite propriile permisiuni.
667
Descărcați de la Wow! eBook <www.wowebook.com>
C A P I T O L U L 20
•■■
Apartenență
În capitolul anterior, ați aflat cum puteți utiliza autentificarea formularelor ASP.NET ca piatră de temelie a securității
site-ului dvs. Cu autentificarea formularelor, puteți identifica utilizatorii și le puteți restricționa accesul la paginile pe care
nu ar trebui să le acceseze. Cel mai bun dintre toate, ASP.NET gestionează întregul proces pentru dvs. prin crearea și
verificarea cookie-ului de autentificare a formularelor.
Oricât de convenabilă ar fi autentificarea formularelor, nu este o soluție completă. Depinde încă de dvs. să vă ocupați de o
varietate de sarcini conexe. De exemplu, trebuie să mențineți o listă de utilizatori și să o verificați în timpul procesului de
autentificare. De asemenea, trebuie să creați pagina de conectare, să decideți cum să separați conținutul public de cel
privat și să decideți ce ar trebui să aibă voie să facă fiecare utilizator. Aceste sarcini nu sunt insurmontabile, dar pot fi
obositoare. De aceea, Microsoft adaugă un alt strat de caracteristici cadrului său de autentificare a formularelor. Acest
strat suplimentar este cunoscut sub numele de membru.
Caracteristicile de membru se încadrează în trei mari categorii:
Gestionarea înregistrărilor utilizatorilor: În loc să vă creați propria bază de date de utilizator, dacă
utilizați caracteristicile de membru, ASP.NET puteți crea și întreține acest catalog de informații despre
utilizatori. Poate chiar implementa reguli avansate (cum ar fi solicitarea adreselor de e-mail, punerea de
întrebări de securitate și implementarea parolelor puternice).
Controale de securitate: Fiecare site web securizat are nevoie de o pagină de conectare. Cu controalele de securitate
ASP.NET, nu trebuie să vă proiectați propriile opțiuni, în schimb, puteți utiliza o versiune gata făcută direct din secțiunea
Conectare din Toolbox. Și împreună cu controlul de bază Login sunt alte controale pentru afișarea conținutului securizat,
crearea de noi utilizatori și schimbarea parolelor. Cel mai bun dintre toate, puteți personaliza modul în care funcționează
fiecare control de securitate setând proprietăți și reacționând la evenimente.
Securitate bazată pe roluri: În multe site-uri web, trebuie să acordați permisiuni diferite diferiților
utilizatori. Desigur, viața ar fi mult prea complexă dacă ar trebui să mențineți un set diferit de setări
pentru fiecare utilizator, deci este util să asamblați utilizatorii în grupuri care definesc anumite
permisiuni. Aceste grupuri se numesc roluri, iar caracteristicile de membru ASP.NET includ instrumente
pentru crearea automată a unei baze de date cu informații despre roluri.
În acest capitol, veți explora toate aceste trei zone de caracteristici și veți vedea cum puteți crea site-uri
sigure cu cod surprinzător de mic.
669
CAPITOLUL APARTENENȚĂ
20
moment, ASP.NET gestionează baza de date de utilizatori pentru dvs., adăugând informații noi despre utilizatori, verificând
acreditările atunci când utilizatorii încearcă să se conecteze și așa mai departe.
În mod clar, depozitul de date de membru are capacitatea de a reduce foarte mult cantitatea de cod pe care trebuie să o
scrieți. Puteți crea un site web securizat cu mult mai puțin cod și, prin urmare, mult mai puțină muncă. De asemenea, nu
trebuie să vă faceți griji cu privire la erorile inevitabile, deoarece modulul de membru ASP.NET este o componentă bine
cunoscută, testată cu atenție.
Deci, de ce nu ați dori să utilizați depozitul de date de membru? Există câteva motive posibile:
Nu doriți să stocați datele de utilizator într-o bază de date: teoretic, puteți stoca lista de utilizatori în orice tip
de depozit de date, de la un fișier XML la o bază de date Oracle. Din punct de vedere tehnic, fiecare magazin
de date necesită un furnizor de membru diferit. Cu toate acestea, ASP.NET include numai doi furnizori:
furnizorul SQL Server pe care îl veți utiliza în acest capitol și un furnizor pentru Active Directory. Dacă doriți
să utilizați un alt depozit de date, cum ar fi o altă bază de date relațională, va trebui să găsiți un abonament
corespunzător sau va trebui să renunțați complet la calitatea de membru.
Aveți nevoie de compatibilitate inversă: Dacă ați creat deja un tabel pentru a stoca informații despre utilizator,
poate fi prea dificil să comutați la depozitul de date de membru. Acest lucru se datorează faptului că
furnizorul de membru SQL Server se așteaptă la o structură de tabel specifică. Nu va funcționa cu tabelele
existente, deoarece vor avea o combinație subtil diferită de câmpuri și tipuri de date. Și chiar dacă nu trebuie
să păstrați structura curentă a tabelului, este posibil să descoperiți că este prea mult de lucru pentru a crea
din nou toate înregistrările de utilizator în depozitul de date de membru.
Doriți să gestionați informațiile despre utilizatori în non-ASP.NET aplicații: După cum veți vedea în acest capitol,
ASP.NET vă oferă un set puternic de obiecte pentru interacțiunea cu datele de membru. De exemplu, puteți să
actualizați înregistrările utilizatorilor, să ștergeți înregistrările utilizatorilor, să regăsiți înregistrările utilizatorilor pe
baza anumitor criterii și așa mai departe. Puteți utiliza chiar și obiectele de membru în alte tipuri de aplicații .NET (de
exemplu, puteți crea o aplicație Windows pentru a gestiona conturile de utilizator). Cu toate acestea, dacă creați o
altă aplicație în afara .NET care trebuie să efectueze aceste activități, este posibil să descoperiți că nu este la fel de
ușor, deoarece va trebui să înțelegeți structura tabelului de membri. În acest caz, este posibil să descoperiți că este
mai ușor să gestionați utilizatorii cu instrucțiuni SQL drepte care funcționează cu propriul tabel.
Dacă decideți să nu utilizați depozitul de date de membru, depinde de dvs. să scrieți ADO.NET cod pentru a prelua
înregistrările utilizatorilor și pentru a verifica acreditările utilizatorilor. Folosind aceste tehnici, vă puteți crea propriile
pagini de conectare pe calea cea grea, așa cum se explică în capitolul 19.
Înainte de a continua, trebuie să configurați site-ul web pentru a utiliza autentificarea formularelor
adăugând eticheta <formulare>. Iată ce trebuie să adăugați:
<configuration> <system.web>
<authentication mode="Forms" /> ...
</system.web> </configuration>
Opțional, puteți defini detalii suplimentare, cum ar fi locația paginii de conectare și timpul înainte de expirarea
cookie-ului de securitate, așa cum este descris în capitolul 19. De asemenea, poate doriți să adăugați o
regulă de autorizare care împiedică utilizatorii anonimi să acceseze o anumită pagină sau subfolder, astfel
încât să puteți testa mai bine securitatea site-ului dvs.
670
CAPITOLUL APARTENENȚĂ
20
Pentru a vedea cum funcționează acest lucru, vă ajută să creați un nou proiect web cu o pagină de test
simplă. Glisați controlul CreateUserWizard pe pagina dvs. din secțiunea Login din Toolbox. Acum rulați
pagina (prezentată în Figura 20-1), fără a adăuga niciun cod sau a configura controlul.
671
x
CAPITOLUL APARTENENȚĂ
20
Completați toate casetele de text cu informații despre utilizator. Rețineți că, în mod implicit, trebuie să furnizați o parolă
care include cel puțin un caracter care nu este un număr sau o literă (cum ar fi un caracter de subliniere sau un
asterisc) și are cel puțin șapte caractere. După ce ați completat toate informațiile, faceți clic pe Creare utilizator.
În acest moment, controlul CreateUserWizard utilizează clasa ASP.NET Membership din culise pentru a
crea un utilizator nou. Furnizorul implicit de membru creează fișierul aspnetdb.mdf (dacă nu există deja),
apoi adaugă noua înregistrare de utilizator. După finalizarea acestui proces, controlul CreateUserWizard
afișează un mesaj care vă informează că utilizatorul a fost creat. În mod miraculos, toate acestea au loc
automat, chiar dacă nu ați configurat nimic în fișierul web.config și nu ați creat fișierul bazei de date în avans.
Pentru a vă asigura că utilizatorul a fost într-adevăr creat, puteți verifica fișierul aspnetdb.mdf. În Exploratorul
de soluții, faceți clic dreapta pe folderul App_Data și selectați Reîmprospătare folder. Veți vedea că fișierul
aspnetdb.mdf apare imediat. Folosind Visual Studio, poți chiar să sapi în conținutul fișierului aspnetdb.mdf.
Pentru aceasta, faceți dublu clic pe fișier în Exploratorul de soluții. Visual Studio va configura o conexiune
nouă și o va adăuga la Server Explorer din stânga. Folosind Server Explorer, puteți naviga liber prin baza de
date, examinând tabelele și procedurile stocate. (Rețineți că fereastra Server Explorer se numește Database
Explorer în Visual Studio Web Developer Express, chiar dacă fereastra funcționează în același mod.)
Puteți verifica tabelul aspnet_Users pentru a găsi înregistrarea de utilizator pe care ați creat-o. Doar faceți
clic dreapta pe numele tabelului și alegeți Afișare date tabel. Veți vedea ceva asemănător înregistrării
prezentate în Figura 20-2. Printre alte detalii, veți găsi un GUID generat aleatoriu care identifică în mod unic
utilizatorul și data la care utilizatorul a utilizat ultima dată aplicația web. Nu veți vedea întrebarea despre
parolă și parolă - acestea sunt stocate într-o înregistrare legată în tabelul aspnet_Membership și sunt
criptate pentru a preveni spionarea ocazională.
672
CAPITOLUL APARTENENȚĂ
20
Notă La prima vedere, veți găsi că baza de date a membrilor include un număr amețitor de tabele. Unele dintre aceste tabele sunt pentru alte caracteristici asociate pe care le puteți utiliza sau nu, cum ar fi securitatea bazată pe roluri (discutată mai târziu în secțiunea "Securitate bazată pe roluri") și profilurile (discutate în capitolul 21).
Înainte de a vă scufunda în detaliu în restul caracteristicilor de membru ASP.NET, este important să luați în
considerare ce ar trebui să faceți dacă nu doriți depozitul implicit de date de membru. De exemplu, puteți
decide să stocați tabelele de membru într-o altă bază de date sau poate doriți să configurați una dintre
numeroasele opțiuni pentru furnizorul de membri. Veți învăța cum să faceți acest lucru în următoarele două
secțiuni.
673
CAPITOLUL APARTENENȚĂ
20
Cel mai simplu caz este dacă utilizați versiunea completă de SQL Server. În acest caz, puteți utiliza în
continuare setările implicite de membru. Cu toate acestea, trebuie să modificați șirul de conexiune.
Sfat: Setările implicite de membru și șirul de conexiune locală sunt setate în fișierul machine.config. Puteți arunca o privire la acest fișier (și chiar editați-l pentru a actualiza setările pentru toate aplicațiile web de pe computer). Căutați în directorul c:\Windows\Microsoft.NET\Framework\[Version[\Config], unde [Version] este versiunea de ASP.NET instalată, cum ar fi v4.0.30319. Sau, dacă configurați o aplicație web implementată utilizând IIS pe un sistem de operare pe 64 de biți, căutați în directorul
c:\Windows\Microsoft.NET\Framework64\[Version[\Config.
Șirul de conexiune implicit utilizat cu calitatea de membru se numește LocalSqlServer. Puteți edita această
setare direct în machine.config. Cu toate acestea, dacă trebuie doar să îl modificați pentru o singură aplicație,
este mai bine să ajustați fișierul web.config pentru aplicația dvs. Mai întâi, trebuie să eliminați toate șirurile de
conexiune existente utilizând elementul <clar>. Apoi, adăugați șirul de conexiune LocalSqlServer - dar de
data aceasta cu valoarea corectă:
<configuration> <connectionStrings> <clear /> <add name="LocalSqlServer"
providerName="System.Data.SqlClient" connectionString="Data Source=localhost;
Securitate integrată=SSPI; Catalog inițial = aspnetdb "/ > < / connectionStrings> ...
</configurare>
Această secțiune <connectionStrings> elimină toate șirurile de conexiune și apoi creează un nou șir de conexiune. Acest
nou șir de conexiune se conectează la o bază de date numită aspnetdb pe computerul local. Singura captură este că
baza de date aspnetdb nu va fi creată automat. În schimb, va trebui să îl generați cu instrumentul de linie de comandă
aspnet_regsql.exe. În loc să căutați acest fișier, cel mai simplu mod de a-l lansa este să porniți promptul de comandă
Visual Studio (deschideți meniul Start și alegeți Toate programele
→ Visual→Studio ToolsVisual
Microsoft Visual Studio
Studio Command Prompt). Apoi puteți introduce comenzi care utilizează
2010
aspnet_regsql.
Puteți utiliza aspnet_regsql în două moduri. Dacă îl utilizați fără a adăuga parametri de linie de comandă, va
apărea un expert Windows care vă conduce prin proces. Vi se va solicita să furnizați informațiile de conexiune
pentru serverul bazei de date. Baza de date va fi numită aspnetdb, care este implicită recomandată.
Cealaltă opțiune este să specificați exact ce doriți să se întâmple utilizând comutatoarele de linie de comandă. Acest
lucru este util în special atunci când implementați aplicația - puteți utiliza aspnet_regsql ca parte a unui fișier batch de
configurare, care va crea apoi automat depozitul de date de membru. Aceasta este opțiunea pe care o veți utiliza dacă
doriți să alegeți numele bazei de date sau dacă doriți să instalați doar unele dintre tabelele bazei de date. În mod
implicit, instrumentul aspnet_regsql instalează tabele care pot fi utilizate pentru autentificarea utilizatorilor, autorizarea
bazată pe roluri, profiluri și personalizarea părților web. Acest lucru vă oferă flexibilitate maximă, dar este posibil să
simțiți că este exagerat dacă nu intenționați să utilizați unele dintre aceste caracteristici.
Tabelul 20-1 descrie cele mai importante opțiuni de linie de comandă. Iată un exemplu de linie de comandă
care se conectează la o instanță SQL Server fără nume pe computerul curent (utilizând parametrul -S), se
conectează la contul Windows curent (utilizând parametrul -E), instalează toate tabelele (utilizând parametrul
-A all) și le plasează pe toate într-o bază de date numită aspnetdb (care este implicit):
674
CAPITOLUL APARTENENȚĂ
20
Sfat: Este o idee bună să instalați toate tabelele simultan (utilizând opțiunea –A toate ). În acest fel, baza dvs. de date va fi pregătită pentru caracteristica de profil discutată în capitolul următor. După ce ați terminat de testat aplicația și sunteți gata să creați baza de date finală, puteți crea o bază de date care are doar opțiunile pe care ați decis să le utilizați. (De exemplu, utilizați –A Domnul să folosească calitatea de membru și gestionarea rolurilor, dar nimic altceva.)
Comutator Descriere
-S NumeServer Specifică locația instanței SQL Server unde doriți să instalați baza de date.
-U Nume utilizator Specificați numele de utilizator și parola de care aveți nevoie pentru a vă
și - P Parolă conecta la baza de date SQL Server. De obicei, veți folosi -E în schimb.
-Un Specifică caracteristicile pe care doriți să le utilizați (și determină tabelele bazei de
date create). Opțiunile valide pentru acest parametru sunt toate, m (membru), r
(securitate bazată pe roluri), p (profiluri), c (personalizare parte web) și w (pentru
dependențele memoriei cache a bazei de date cu SQL Server 2000).
-d DatabaseName Vă permite să specificați numele bazei de date în care vor fi create tabelele.
Dacă nu specificați acest parametru, se creează automat o bază de date
denumită aspnetdb.
-sqlexportonly Creează scripturi SQL pentru adăugarea sau eliminarea caracteristicilor specificate în
baza de date, dar nu creează efectiv tabelele din baza de date. În schimb, puteți rula
scriptul după aceea. Aceasta poate fi o tehnică utilă atunci când implementați aplicația.
675
CAPITOLUL APARTENENȚĂ
20
Notă Dacă
• Rulați implementați
aspnet_regsql site-ul web
pe serverul Web.la În
o companie de găzduire
schimb, probabil că va web,
trebuiprobabil că nu
să utilizați vi Server
SQL se va permite
Express. În acest caz, baza de date va fi implementată în folderul App_Data ca parte a aplicației
web și nu vor fi necesari pași suplimentari de configurare. Dacă gazda web nu acceptă SQL
Server Express, va trebui să utilizați un instrument precum SQL Server Management Studio
pentru a pregăti un fișier script .sql care instalează baza de date. Administratorii companiei de
găzduire web pot rula apoi fișierul script pentru a crea baza de date de care aveți nevoie.
Notă Ca și în cazul șirului de conexiune, furnizorul implicit de membru este definit în fișierul machine.config. Tu
•
Puteți edita fișierul machine.config pentru a modifica aceste valori implicite pentru toate
aplicațiile de pe computer, dar nu ar trebui, deoarece vă va complica viața atunci când
implementați aplicația. În schimb, ar trebui să efectuați modificările configurând un nou furnizor
de membru în fișierul web.config al aplicației dvs.
Pentru a configura furnizorul de abonament, trebuie să adăugați elementul <membru> la aplicația web. În
elementul <abonament> definiți un nou furnizor de abonamente cu setările personalizate. Apoi, setați atributul
defaultProvider al elementului <membership> astfel încât să se refere la furnizorul de membru după nume.
</system.web> </configuration>
Desigur, partea interesantă sunt atributele pe care le utilizați în eticheta <add> pentru a configura furnizorul de abonament. Iată
un exemplu care definește un furnizor de membri cu setări de parolă relaxate. Primele trei atribute furnizează setările necesare
(numele, tipul și șirul de conexiune pentru furnizorul de abonament). Setările rămase elimină cerința pentru o întrebare de
676
CAPITOLUL APARTENENȚĂ
20
securitate și permit ca o parolă să fie la fel de scurtă ca un caracter și să conțină doar litere și cifre:
<membership defaultProvider="MyMembershipProvider">
<providers> <clear/> <add name="MyMembershipProvider"
type="System.Web.Security.SqlMembershipProvider"
connectionStringName="LocalSqlServer"
requiresQuestionAndAnswer="false"
minRequiredPasswordLength="1"
minRequiredNonalphanumericCharacters="0" /> </providers>
</membership>
Atribut Descriere
677
CAPITOLUL APARTENENȚĂ
20
Atribut Descriere
passwordFormat Setează modul în care parolele sunt stocate în baza de date. Puteți
utiliza Clear (parolele sunt stocate ca atare, fără criptare), Encrypted
(parolele sunt criptate utilizând o cheie specifică computerului) sau
Hashed (parolele sunt hashed și valoarea hash este stocată în baza de
date). Hashing-ul parolelor oferă o protecție similară criptării acestora (și
anume, dacă vă uitați la hash, veți avea dificultăți în ingineria inversă a
parolei). Cu toate acestea, atunci când parolele sunt codificate, acestea
nu pot fi recuperate niciodată, ci doar resetate.
minRequiredPasswordLength Specifică lungimea minimă a unei parole. Dacă utilizatorul
introduce mai puține caractere la crearea unui cont, încercarea va
fi respinsă cu un mesaj de eroare.
minObligatoriuCaractere Specifică numărul de caractere nonalfanumerice (alte caractere decât cifre
nonalfanumerice și litere) pe care trebuie să le aibă parola. Dacă utilizatorul introduce mai
puține dintre aceste caractere la crearea unui cont, încercarea va fi
respinsă cu un mesaj de eroare. Deși solicitarea de caractere
Descărcați de la Wow! eBook <www.wowebook.com>
enablePasswordRetrieval Determină dacă o parolă poate fi solicitată (și trimisă prin e-mail
utilizatorului), ceea ce este util dacă un utilizator uită o parolă. Această
caracteristică nu este acceptată niciodată dacă passwordFormat este
setat la hashed, deoarece parola nu este stocată în acest caz.
necesităÎntrebareșiRăspuns Determină dacă răspunsul de securitate pentru calitatea de membru va
fi necesar atunci când solicitați sau resetați o parolă de utilizator.
necesităUniqueEmail Dacă este fals, permite mai multor utilizatori să aibă aceeași adresă de
poștă electronică. Informațiile despre adresa de e-mail sunt întotdeauna
opționale. (Cu toate acestea, controlul CreateUserWizard necesită o
adresă de e-mail, cu excepția cazului în care setați RequireEmail la false.)
678
CAPITOLUL APARTENENȚĂ
20
Acum că ați văzut setările pe care le puteți modifica, merită să întrebați care sunt valorile implicite. Dacă vă
uitați la secțiunea <membership> din fișierul machine.config, iată ce veți găsi:
După cum puteți vedea, furnizorul implicit de membru este AspNetSqlMembershipProvider. Se conectează
utilizând șirul de conexiune LocalSqlServer și acceptă resetarea parolei, dar nu și recuperarea parolei.
Conturile necesită o întrebare de securitate, dar nu un e-mail unic. Parolele în sine sunt hash în baza de
date pentru securitate, astfel încât acestea nu pot fi recuperate. Parolele trebuie să aibă cel puțin șapte
caractere și cel puțin un caracter nonalfanumeric. În cele din urmă, dacă un utilizator face cinci încercări de
parolă nevalidă în zece minute, contul este dezactivat.
O opțiune este utilizarea WAT. Alegeți Configurare ASP.NET site web pentru a lansa acest instrument. Următor
faceți clic pe fila Securitate. În colțul din stânga jos, o→
casetă indică câți utilizatori se află în prezent în
baza de date (a se vedea figura 20-3). Această casetă oferă, de asemenea, linkuri care vă permit să
examinați înregistrările de utilizator existente sau să adăugați altele noi.
679
CAPITOLUL APARTENENȚĂ
20
Dacă doriți să răsfoiți lista curentă de utilizatori sau să actualizați o înregistrare de utilizator existentă, faceți
clic pe linkul Gestionare utilizatori. Pentru a adăuga utilizatori noi, faceți clic pe Creare utilizator. Veți vedea un
set de controale similare cu controlul CreateUserWizard utilizat anterior în pagina de testare (consultați Figura
20-4). După ce ați creat câțiva utilizatori, se recomandă să aruncați o altă privire la tabelele aspnet_Users și
aspnet_Membership din baza de date pentru a vedea cum arată înregistrările utilizatorilor.
680
CAPITOLUL 20
CALITATEA DE
MEMBRU
Deși WAT este o modalitate perfect sensibilă de a adăuga înregistrări de utilizator, s-ar putea să descoperiți
că interfața web este puțin lentă dacă aveți un număr mare de utilizatori de creat. O altă opțiune este să
utilizați clasa de membru, așa cum se arată aici:
Creați o înregistrare de utilizator bazată pe numele de utilizator, parola și informațiile de poștă
electronică. Membership.CreateUser(nume de utilizator, parolă, e-mail);
Primii câțiva parametri se explică de la sine - iau numele de utilizator, parola, adresa de e-mail, întrebarea
despre parolă și răspunsul la parolă. Penultimul parametru ia o valoare booleană care determină dacă contul
primește semnalizatorul IsApproved. Dacă furnizați fals, contul nu va fi aprobat și, prin urmare, nu va fi activ
(și utilizabil) până când nu îl modificați utilizând metoda Membership.UpdateUser(). În supraîncărcarea mai
simplă, care nu include acest parametru, conturile sunt întotdeauna marcate ca aprobate.
Ultimul parametru returnează o valoare din enumerarea MembershipCreateStatus. Dacă această valoare nu
este MembershipCreateStatus.Success, a apărut o eroare la crearea înregistrării. Valoarea indică starea
exactă a erorii (de exemplu, o parolă care nu a fost suficient de puternică, o adresă de poștă electronică
dublată atunci când furnizorul de apartenență nu permite dubluri etc.). În supraîncărcarea mai simplă care nu
include MembershipCreateStatus, orice eroare are ca rezultat aruncarea unui obiect de excepție care are
aceleași informații.
Sfat: În mod clar, dacă trebuie să transferați un număr mare de conturi de utilizator dintr-o bază de date particularizată în depozitul de date de membru, cea mai rapidă opțiune ar fi să scrieți o rutină care să treacă prin înregistrările existente și să utilizați metoda CreateUser () pentru a le insera pe cele noi.
Metodă Descriere
DeleteUser() Șterge un utilizator existent din baza de date. Specificați utilizatorul prin
numele de utilizator. De asemenea, puteți alege dacă doriți să ștergeți
toate datele asociate din alte tabele (implicit este să le eliminați).
GetUser() Obține un anumit utilizator din baza de date, după numele de utilizator.
GetUserNameByEmail() Regăsește un nume de utilizator pentru utilizator care corespunde unei adrese de
poștă electronică date. Rețineți că adresele de e-mail duplicate sunt permise în
mod implicit, caz în care această metodă va găsi doar prima potrivire.
682
CAPITOLUL APARTENENȚĂ
20
Metodă Descriere
FindUsersByEmail() Preia utilizatorii din baza de date de apartenență care corespund unui anumit mesaj de poștă electronică
adresă. De asemenea, puteți furniza o parte a unei adrese de poștă electronică (cum ar fi
nume domeniu), caz în care veți primi fiecare utilizator care are un e-mail
adresa care conține acest text.
GetAllUsers() Obține o colecție care reprezintă toți utilizatorii din baza de date. Un
Versiunea supraîncărcată a acestei metode vă permite să obțineți doar o porțiune
din lista completă de utilizatori (o singură pagină de utilizatori, pe baza unui index de pornire și
lungime).
GeneratePassword() Generează o parolă aleatorie de lungimea specificată. Acest lucru este util
atunci când creați programatic noi înregistrări de utilizator.
Clasa de membru oferă, de asemenea, proprietăți statice doar în citire care vă permit să regăsiți informații despre
configurația furnizorului de membru, așa cum este setat în fișierul de configurare. De exemplu, puteți prelua lungimea
necesară a parolei, numărul maxim de încercări de parolă și toate celelalte detalii descrise în tabelul 20-2.
Multe dintre aceste metode utilizează clasa MembershipUser, care reprezintă o înregistrare de utilizator. De
exemplu, când apelați GetUser(), primiți informațiile ca obiect MembershipUser. Dacă doriți să actualizați acel
utilizator, aveți posibilitatea să-i modificați proprietățile și apoi să apelați Membership.UpdateUser() cu
obiectul MembershipUser modificat.
Notă Obiectul MembershipUser combină detaliile din tabelul aspnet_Users și tabelul legat aspnet_ Apartenență. De exemplu, include întrebarea parolei. Cu toate acestea, răspunsul la parolă și parola în sine nu sunt disponibile.
Clasa MembershipUser oferă, de asemenea, propriul set mai mic de metode de instanță. Cele mai
importante sunt detaliate în tabelul 20-4.
683
CAPITOLUL APARTENENȚĂ
20
Metodă Descriere
DeblocareUtilizator() Reactivează un cont de utilizator care a fost blocat pentru prea multe conectări nevalide
Încercări.
Pentru a înțelege cum funcționează clasa de membru, puteți crea o pagină de test simplă care afișează o
listă a tuturor utilizatorilor din baza de date de membri. Figura 20-5 prezintă această pagină.
684
CAPITOLUL APARTENENȚĂ
20
Pentru a crea această pagină, trebuie doar să începeți prin definirea GridView. GridView va afișa o listă de
obiecte MembershipUser. Pentru fiecare utilizator, afișează valorile din proprietățile Nume utilizator și E-mail,
împreună cu un link Selectare. Iată marcajul care creează GridView (fără detaliile de formatare):
Când pagina este încărcată pentru prima dată, apelează metoda Membership.GetAllUsers() și leagă
rezultatele la GridView, așa cum se arată aici:
Pentru a face exemplul mai interesant, atunci când este selectată o înregistrare, obiectul MembershipUser
corespunzător este regăsit. Acest obiect este apoi adăugat la o colecție, astfel încât să poată fi legat la
DetailsView pentru afișarea automată:
protejat void gridUsers_SelectedIndexChanged(expeditor obiect, EventArgs e) { List<MembershipUser> list = new
List<MembershipUser>(); listă. Add(Membership.GetUser(gridUsers.SelectedValue.ToString())); detailsUser.DataSource = listă;
detailsUser.DataBind(); }
Iată DetailsView care face treaba (din nou, fără detaliile de formatare):
685
CAPITOLUL APARTENENȚĂ
20
686
CAPITOLUL APARTENENȚĂ
20
De fapt, un pic de muncă are loc în spatele scenei. Dacă utilizați setările implicite ale furnizorului de
abonament, parolele sunt codificate. Asta înseamnă că atunci când apelați ValidateUser(), ASP.NET hash
parola nou furnizată utilizând același algoritm hash și apoi o compară cu parola hash stocată în baza de
date.
Conturi dezactivate
Un cont poate fi dezactivat în baza de date de membri în două moduri:
Contul nu este aprobat: acest lucru se întâmplă dacă creați un cont în mod programatic și furnizați false
pentru parametrul isApproved. Puteți face acest pas dacă doriți să creați automat un cont, dar permiteți
unui administrator să îl examineze înainte de a deveni live. Pentru a activa acest cont, trebuie să
obțineți un obiect MembershipUser pentru înregistrarea de utilizator corespunzătoare, să setați
MembershipUser.IsApproved la true și să apelați Membership.UpdateUser().
Contul este blocat: Acest lucru se întâmplă dacă utilizatorul face mai multe încercări de a accesa un cont
de utilizator cu o parolă nevalidă. În acest caz, trebuie să obțineți un obiect MembershipUser pentru
utilizator și să apelați MembershipUser.UnlockUser(). De asemenea, poate doriți să apelați
MembershipUser.ResetPassword() pentru a preveni o altă blocare.
Pentru a vă ajuta cu aceste activități, poate doriți să creați o pagină administrativă ca cea prezentată în Figura 20-6. De
exemplu, puteți permite unui utilizator să examineze toate conturile care nu sunt încă aprobate și să le aprobe dând clic
pe un buton.
În mod similar, dacă doriți să dezactivați un cont în orice moment, puteți să regăsiți un obiect MembershipUser pentru
acel utilizator și să setați proprietatea IsApproved la false. Cu toate acestea, nu aveți nici o modalitate de a bloca
programatic un cont de utilizator.
Probabil că vă gândiți deja la o gamă largă de pagini pe care le puteți crea folosind clasele Membership și
MembershipUser. De exemplu, puteți crea pagini care permit utilizatorilor să solicite o resetare a parolei sau
să verifice dacă sunt blocați. Cu toate acestea, este posibil să nu fie nevoie să creați toate aceste pagini,
deoarece ASP.NET include un set bogat de controale de securitate care automatizează multe activități
obișnuite. Veți afla mai multe despre controalele de securitate în secțiunea următoare.
Controalele de securitate
Caracteristicile de bază ale calității de membru economisesc timp remarcabil. Acestea vă permit să vă concentrați
asupra programării aplicației web, fără să vă faceți griji cu privire la gestionarea securității și crearea bazei de date
perfecte sau a informațiilor despre utilizator. În schimb, puteți utiliza clasele de membru de nivel superior și
MembershipUser pentru a face tot ce aveți nevoie.
Cu toate acestea, funcția de membru ASP.NET nu se oprește aici. Clasa Membership nu numai că simplifică sarcinile
comune de securitate, ci le și standardizează. Drept urmare, alte componente și controale pot utiliza clasa Membership
pentru a se integra cu modelul de securitate ASP.NET, fără a vă face griji cu privire la specificul fiecărei aplicații web.
687
CAPITOLUL APARTENENȚĂ
20
Puteți găsi cel mai bun exemplu al acestei noi flexibilități în controalele de securitate ale ASP.NET. Aceste controale
interacționează cu furnizorul de membru utilizând metodele claselor Membership și MembershipUser pentru a
implementa biți comuni ai interfețelor cu utilizatorul, cum ar fi o pagină de conectare, un set de controale de creare a
utilizatorului și un expert de recuperare a parolei. Tabelul 20-5 listează toate controalele de securitate ASP.NET care
funcționează cu calitatea de membru. În Visual Studio, puteți găsi aceste controale în secțiunea Login din Toolbox.
Controla Descriere
Login Afișează casetele de text familiare pentru numele de utilizator și parolă, cu un buton de conectare.
LoginStatus Afișează un buton de conectare, dacă utilizatorul nu este deja conectat, care redirecționează utilizatorul către
pagina de conectare configurată. În caz contrar, afișează un buton de deconectare. Poţi
Alegeți testul utilizat pentru butoanele de conectare și deconectare, dar cam atât.
LoginView Afișează conținut diferit, în funcție de faptul dacă utilizatorul este conectat. Tu
poate chiar să utilizeze acest control pentru a afișa conținut diferit pentru diferite grupuri de utilizatori,
Descărcați de la Wow! eBook <www.wowebook.com>
sau roluri.
Recuperare parolă Permite utilizatorului să solicite o parolă prin e-mail sau să o reseteze. De obicei, utilizatorul
trebuie să furnizeze răspunsul la întrebarea de securitate pentru a obține parola.
Schimbare parolă Permite utilizatorului să seteze o nouă parolă (atâta timp cât utilizatorul poate furniza parola curentă
parola).
CreateUserWizard Permite unui utilizator să creeze o înregistrare nouă, completată cu adresa de e-mail și o
întrebare și răspuns cu parolă.
Există o modalitate simplă și o modalitate complexă de a utiliza majoritatea acestor controale. La modul cel mai simplu,
pur și simplu aruncați controlul pe o pagină, fără a scrie o linie de cod. (Ați văzut această abordare cu controlul
CreateUserWizard la începutul acestui capitol.) De asemenea, puteți să modificați proprietățile, să gestionați evenimente
și chiar să creați șabloane pentru a personaliza aceste controale.
În secțiunile următoare, veți arunca o privire mai atentă asupra controalelor de conectare,
PasswordRecovery și CreateUserWizard. Și mai târziu, în secțiunea "Securitate bazată pe roluri", veți
pune controlul LoginView la lucru pentru a afișa conținut diferit utilizatorilor din roluri diferite.
Controlul de conectare
Până în prezent, site-urile securizate pe care le-ați văzut au folosit pagini de conectare realizate manual. În multe site-uri
web, acest lucru este ceea ce veți dori - la urma urmei, vă oferă control complet pentru a ajusta interfața cu utilizatorul
exact așa cum doriți. Cu toate acestea, o pagină de conectare este standard, deci este logic ca ASP.NET să ofere
dezvoltatorilor câteva comenzi rapide suplimentare care le pot salva munca.
În acest sens, ASP.NET include un control de conectare care asociază un nume de utilizator și o casetă de
text pentru parolă cu un buton de conectare. Controlul Login adaugă, de asemenea, câteva caracteristici:
• Include controale de validare care împiedică postarea paginii înapoi până când nu au
fost introduse un nume de utilizator și o parolă. Acești validatori utilizează validarea
pe partea client dacă este acceptată de browser (cu ajutorul unui pic de JavaScript) și
validarea pe partea serverului, așa cum este descris în capitolul 9.
688
CAPITOLUL APARTENENȚĂ
20
Deși controlul de conectare se ocupă automat de procesul de conectare pentru dvs., puteți interveni cu
propriul cod personalizat. Pentru a face acest lucru, trebuie să reacționezi la unul dintre evenimentele de
control al conectării, așa cum sunt enumerate în tabelul 20-6.
Tabelul 20-6. Evenimente ale controlului de conectare
Eveniment Descriere
Eroare de conectare Ridicat atunci când încercarea de conectare eșuează (de exemplu, dacă
utilizatorul introduce parola greșită).
Autentifica Ridicat pentru a autentifica utilizatorul. Dacă gestionați acest eveniment, depinde de
dvs. să furnizați codul de conectare - controlul de conectare nu va efectua nicio acțiune.
689
CAPITOLUL APARTENENȚĂ
20
Evenimentele LoggingIn, LoggedIn și LoginError sunt utile în primul rând dacă doriți să actualizați alte controale
pentru a afișa anumite informații pe baza procesului de conectare. De exemplu, după prima eroare de
conectare, puteți alege să afișați un link care redirecționează utilizatorul către o pagină de recuperare a parolei:
În rutina de tratare a evenimentelor Autentificare, puteți verifica numele de utilizator și parola utilizând proprietățile
Nume utilizator și Parolă ale controlului Login. Apoi setați proprietatea autentificată a AuthenticateEventArgs la
adevărat sau fals. Dacă este adevărat, evenimentul LoggedIn este ridicat în continuare, apoi utilizatorul este
redirecționat către Login.DestinationPageUrl (sau pagina originală din care a venit utilizatorul dacă proprietatea
DestinationPageUrl nu este setată). Dacă setați autentificat la false, evenimentul LoginError este ridicat în
continuare și controlul afișează mesajul de eroare definit de proprietatea Login.FailureText. Iată o rutină de tratare
a evenimentelor pentru evenimentul autentificat care utilizează direct clasele de membru:
protejat void Login1_Authenticate(expeditor obiect, AuthenticateEventArgs e) { if (Membership.ValidateUser(Login1.UserName,
Login1.Password)) {
e.autentificat = adevărat; }
altceva {
e.autentificat = fals;
}}
Aceasta acoperă tot ce trebuie să știți despre interacțiunea cu controlul de conectare, dar puteți modifica multe
proprietăți pentru a configura aspectul controlului de conectare. Există chiar și un link de formatare automată pe care îl
puteți alege din fereastra Proprietăți (sau eticheta inteligentă) pentru a oferi controlului de conectare un lifting facial cu
un singur clic.
Cele mai puternice proprietăți de formatare pentru controlul Login sunt proprietățile stilului, care vă permit să
modificați fonturile, colorarea și alinierea pentru părți individuale ale controlului. Ați văzut deja stiluri la lucru
cu alte câteva controale, inclusiv Calendar (Capitolul 10) și GridView (Capitolul 16) și funcționează în același
mod cu controalele de securitate. Tabelul 20-7 detaliază proprietățile de stil ale controlului de conectare.
690
CAPITOLUL APARTENENȚĂ
20
Stil Descriere
FailureTextStyle Definește stilul pentru textul afișat dacă încercarea de conectare eșuează.
Stil casetă de selectare Definește proprietățile de stil pentru caseta de selectare Ține-mă minte.
ValidatorTextStyle Definește stiluri pentru controalele RequiredFieldValidator care validează numele de utilizator
și informații despre parolă. Aceste proprietăți de stil modifică modul în care textul de eroare
Arată. (În mod implicit, textul de eroare este pur și simplu un asterisc care apare lângă
Casetă text goală.)
HyperLinkStyle Configurează toate linkurile afișate de controlul Login. Aceasta include link-urile
care vă permit să creați o nouă înregistrare de utilizator, să regăsiți o parolă și așa mai departe. Acești
linkurile apar numai dacă ați setat CreateUserUrl și PasswordRecoveryUrl
proprietăți.
InstructionTextStyle Formatează Login.InstructionText, care este textul instrucțiunilor de ajutor pe care îl puteți adăuga
sub controlul Login. În mod implicit, controlul Login nu are text cu instrucțiuni.
Desigur, stilurile nu sunt singura caracteristică pe care o puteți modifica în controlul de conectare. Puteți ajusta mai
multe proprietăți pentru a modifica textul utilizat și pentru a adăuga linkuri. De exemplu, următoarea etichetă pentru
controlul Login ajustează formatarea și utilizează proprietățile CreateUserUrl și PasswordRecoveryUrl pentru a
adăuga linkuri la o pagină pentru înregistrarea unui utilizator nou și alta pentru recuperarea unei parole pierdute.
(Evident, va trebui să creați ambele pagini pentru ca linkurile să funcționeze.)
691
CAPITOLUL APARTENENȚĂ
20
</asp:Autentificare>
Figura 20-8 prezintă controlul de conectare reînnoit. Tabelul 20-8 explică celelalte proprietăți ale autentificării
controla.
Proprietate Descriere
InstrucțiuneText Textul afișat chiar sub titlu, dar deasupra comenzilor de conectare. În
mod implicit, controlul Login nu are text cu instrucțiuni.
692
CAPITOLUL APARTENENȚĂ
20
Proprietate Descriere
LoginButtonImageUrl URL-ul care indică imaginea pe care doriți să o afișați pentru butonul de conectare.
Trebuie să setați proprietatea LoginButtonStyle la Image pentru a utiliza această
proprietate.
DestinationPageUrl Pagina către care este redirecționat utilizatorul dacă încercarea de conectare reușește.
Această proprietate este necompletată în mod implicit, ceea ce înseamnă că controlul
Login utilizează infrastructura formularelor și redirecționează utilizatorul către pagina
solicitată inițial (sau către defaultUrl configurat în fișierul web.config).
CreateUserText Setează textul pentru un link către pagina de înregistrare a utilizatorului. Dacă
acest text nu este furnizat, acest link nu este afișat în controlul de conectare.
HelpPageText Setează textul pentru legătura către pagina de ajutor. Dacă acest text nu
este furnizat, acest link nu este afișat în controlul de conectare.
693
CAPITOLUL APARTENENȚĂ
20
Proprietate Descriere
PasswordRecoveryText Setează textul pentru legătura către pagina de recuperare a parolei. Dacă acest text nu este
furnizat, acest link nu este afișat în controlul de conectare.
Pentru a completa exemplul din Figura 20-8, trebuie să creați paginile Register.aspx și
PasswordRecovery.aspx. În secțiunile următoare, veți afla cum puteți face acest lucru cu ușurință
folosind încă două dintre controalele de securitate ASP.NET.
Controlul CreateUserWizard
Ați utilizat deja controlul CreateUserWizard pentru a crea o înregistrare de utilizator de bază la începutul acestui
capitol. Acum că ați văzut flexibilitatea controlului de conectare, nu ar trebui să fie o surpriză să aflați că aveți la
fel de multe opțiuni pentru modificarea aspectului și comportamentului controlului CreateUserWizard.
Controlul CreateUserWizard funcționează în doi pași. Primul pas colectează informațiile de utilizator necesare pentru
a genera înregistrarea utilizatorului. Al doilea pas afișează un mesaj de confirmare după crearea contului.
În general, CreateUserWizard oferă un număr amețitor de proprietăți pe care le puteți ajusta. Cu toate
acestea, ajută la înțelegerea faptului că există într-adevăr doar trei tipuri de proprietăți:
Sfat În mod implicit, utilizatorii nou creați sunt conectați automat. Puteți modifica acest comportament setând proprietatea CreateUserWizard.LoginCreatedUser la false. De asemenea, puteți seta proprietatea ContinueDestinationPageUrl pentru a seta adresa URL unde ar trebui să fie redirecționat utilizatorul după crearea noii înregistrări.
Destul de interesant, controlul CreateUserWizard moștenește de la controlul Wizard pe care l-ați explorat în capitolul 10. Drept
urmare, puteți adăuga câți pași suplimentari doriți, la fel cum puteți cu controlul Expert. Acești pași pot efectua alte activități,
694
CAPITOLUL APARTENENȚĂ
20
cum ar fi înscrierea utilizatorului pentru a primi un buletin informativ regulat. Cu toate acestea, procesul efectiv de
creare a utilizatorului trebuie să aibă loc întotdeauna într-un singur pas. De exemplu, luați în considerare marcajul
pentru CreateUserWizard de bază:
În esență, CreateUserWizard este un control Wizard care acceptă două tipuri de pași specializați: un
CreateUserWizardStep unde sunt colectate informațiile despre utilizator și este creată înregistrarea utilizatorului și un
CompleteWizardStep unde este afișat mesajul de confirmare.
Următorul exemplu arată cum puteți adăuga un WizardStep obișnuit în această secvență. În acest caz,
pasul suplimentar oferă pur și simplu câteva opțiuni suplimentare pentru utilizatorul nou creat (și anume,
alegerea de a vă abona la buletine informative automate prin e-mail).
<asp:CreateUserWizard ID="CreateUserWizard1" runat="server"
DisplaySideBar="True" ... > <WizardSteps> <asp:CreateUserWizardStep
runat="server" title="Create User"> </asp:CreateUserWizardStep>
<asp:CompleteWizardStep runat="server">
</asp:CompleteWizardStep> </WizardSteps>
</asp:CreateUserWizard>
Figura 20-9 prezintă primii doi pași. Observați că apare bara laterală (deoarece
proprietatea CreateUserWizard.DisplaySidebar este setată la true) pentru a afișa ordinea
pașilor.
695
CAPITOLUL APARTENENȚĂ
20
Depinde în continuare de dvs. să efectuați acțiunea corespunzătoare în codul dvs., reacționând la unul dintre
evenimentele CreateUserWizard. În acest caz, utilizați evenimentul FinishButtonClick, deoarece apare la ultimul pas
înainte de mesajul de finalizare. Dacă plasați pasul mai devreme în secvență, va trebui să reacționați la NextButtonClick.
În exemplul curent, poate doriți să adăugați aceste informații la tabelul de profil al utilizatorului. Veți învăța cum să utilizați
profilurile în capitolul următor.
Pentru aspect complet și putere de formatare, puteți converti unul dintre pașii CreateUserWizard într-un șablon.
Apoi, sunteți liber să rearanjați conținutul existent și să adăugați noi controale și conținut HTML. Cu toate acestea,
aveți grijă să nu eliminați niciunul dintre elementele necesare. CreateUserWizard va lansa o excepție dacă
încercați să o utilizați, dar vă lipsește una dintre casetele text necesare pentru informații despre cont.
Cel mai simplu mod de a converti un pas într-un șablon este să utilizați linkurile etichetelor inteligente. Mai întâi, selectați
CreateUserControl pe suprafața de proiectare a paginii web din Visual Studio. Apoi, faceți clic pe pictograma săgeată
care apare lângă colțul din dreapta sus pentru a afișa eticheta inteligentă. Apoi, selectați linkul Particularizare pas
utilizator sau linkul Personalizare pas finalizat, în funcție de pasul pe care doriți să îl modificați. ASP.NET va insera apoi
controalele într-un șablon din eticheta de control CreateUserWizard.
De exemplu, imaginați-vă că doriți să afișați opțiunile selectate de utilizator în pasul personalizat din
rezumatul final. În acest caz, se recomandă să adăugați un nou control Etichetă, așa cum se arată aici:
696
CAPITOLUL APARTENENȚĂ
20
V-ați abonat la: <asp:Label ID="lblSubscriptionList" runat="server"> </asp:Label> </td> </tr> <tr> <td align="right" colspan="2">
<asp:Button ID="ContinueButton" runat="server" BackColor="Alb" BorderColor="#507CD1" BorderStyle="solid"
BorderWidth="1px" CausesValidation="false" CommandName="continue" font-names="verdana" ForeColor="#284E98"
text="continue" validationgroup="createuserwizard1" /> </td> </tr> </tabel> </ContentTemplate> </asp:CompleteWizardStep>
Acum, când utilizatorul trece la ultimul pas, puteți completa eticheta cu informațiile din controlul CheckBoxList. Cu
toate acestea, deoarece controalele Etichetă și CheckBoxList sunt plasate într-un șablon, nu le puteți accesa direct
după nume. În schimb, trebuie să le extrageți din controlul CreateUserWizard. Pentru a obține eticheta, trebuie să
accesați pasul complet, să luați primul control pe care îl conține (care este șablonul de conținut) și apoi să utilizați
metoda FindControl () pentru a căuta eticheta. Pentru a obține CheckBoxList, efectuați o operațiune similară, cu
excepția faptului că utilizați metoda FindControl() a CreateWizardControl în sine, care caută toți pașii obișnuiți.
Iată codul care efectuează această activitate:
697
CAPITOLUL APARTENENȚĂ
20
Descărcați de la Wow! eBook <www.wowebook.com>
Controlul PasswordRecovery
Controlul PasswordRecovery este util atunci când utilizatorii își uită parolele. Le permite să-și recupereze
parola folosind un vrăjitor scurt.
Controlul PasswordRecovery conduce utilizatorul prin trei pași. În primul rând, solicită numele de utilizator.
Apoi, afișează întrebarea de securitate și solicită răspunsul (cu excepția cazului în care ați setat setarea
requiresQuestionAndAnswer la false în fișierul web.config, caz în care controlul PasswordRecovery omite
complet acest pas). În cele din urmă, controlul PasswordRecovery trimite un e-mail la adresa de e-mail a
utilizatorului. Dacă utilizați un format de parolă criptat sau golit (consultați tabelul 20-2), mesajul de poștă
electronică conține parola originală. Dacă utilizați formatul implicit de parolă hashed, se generează o nouă
parolă aleatorie și acea parolă este trimisă în e-mail. În orice caz, ultimul pas afișează un mesaj de
confirmare care vă informează că e-mailul a fost trimis. Figura 20-11 prezintă controlul PasswordRecovery în
acțiune.
698
CAPITOLUL APARTENENȚĂ
20
699
CAPITOLUL APARTENENȚĂ
20
Notă Puteți configura serverul SMTP selectând controlul PasswordRecovery și selectând Administrare site web din eticheta inteligentă. Apoi, selectați fila Aplicație și faceți clic pe linkul Configurare setări poștă electronică SMTP.
Dacă aplicația nu îndeplinește aceste două cerințe - nu puteți trimite mesaje de poștă electronică sau nu aveți
garanția că utilizatorii au o adresă de poștă electronică - puteți afișa noua parolă direct în pagină. Cea mai
ușoară abordare este gestionarea evenimentului PasswordRecovery.SendingMail. Mai întâi, setați
proprietatea MailMessageEventArgs.Cancel la true pentru a împiedica trimiterea mesajului. Apoi, aveți
posibilitatea să regăsiți conținutul mesajului din obiectul MailMessageEventArgs.Message și să îl afișați pe
pagină. Iată un exemplu:
protejat void PasswordRecovery1_SendingMail(expeditor obiect, MailMessageEventArgs e)
{
e.Cancel = true; PasswordRecovery1.SuccessText =
e.Message.Body;
}
Când utilizați acest rutină de tratare a evenimentelor, veți vedea un pas final precum cel prezentat în Figura 20-12.
Desigur, pentru flexibilitate completă, vă puteți crea propria pagină care resetează parolele. Trebuie doar să
utilizați metodele claselor Membership și MembershipUser descrise mai devreme.
700
CAPITOLUL APARTENENȚĂ
20
vor beneficia de un set limitat de capacități, iar altor utilizatori li se poate permite să efectueze modificări potențial periculoase
sau să utilizeze porțiunile administrative ale unui site web.
Pentru a permite acest tip de acces pe mai multe niveluri, aveți nevoie de caracteristica de autorizare bazată pe roluri a
ASP.NET. Ca și în cazul calității de membru, ASP.NET are grijă să stocheze informațiile despre rol și să le pună la
dispoziția codului dvs. Tot ce trebuie să faceți este să creați rolurile, să atribuiți utilizatori fiecărui rol și apoi să testați
apartenența la rol în codul dvs.
Înainte de a putea utiliza autorizarea bazată pe roluri, trebuie să o activați. Deși puteți efectua acest pas
folosind WAT (trebuie doar să faceți clic pe linkul Activare roluri din fila Securitate), este destul de ușor doar
să adăugați direct linia necesară în fișierul web.config:
<configuration> <system.web>
<roleManager enabled="true" /> ...
</system.web>
...
</configurare>
Ca și în cazul depozitului de date de membru, ASP.NET va crea automat informațiile despre rol în fișierul
aspnetdb.mdf utilizând SQL Server Express. Dacă doriți să utilizați o altă bază de date, trebuie să urmați
pașii discutați anterior în acest capitol pentru a crea baza de date utilizând aspnet_regsql.exe și a modifica
șirul de conexiune.
701
CAPITOLUL APARTENENȚĂ
20
Pentru a plasa un utilizator într-un rol, va trebui să reveniți la pagina principală de securitate (faceți clic pe
butonul Înapoi din pagina de gestionare a rolurilor). Apoi urmați acești pași:
1. Selectați Gestionați utilizatorii din fila Securitate. Veți vedea lista completă de
utilizatori pentru site-ul dvs. (împărțită în pagini).
2. Găsiți utilizatorul pe care doriți să îl modificați și faceți clic pe linkul
Editați rolurile de lângă acel utilizator.
3. Completați caseta de selectare pentru fiecare rol pe care doriți să îl atribuiți acelui utilizator.
Figura 20-14 prezintă un exemplu în care utilizatorului i se dă rolul de utilizator.
702
CAPITOLUL APARTENENȚĂ
20
Desigur, nu este nevoie să utilizați WAT. De asemenea, puteți utiliza clasa Roluri. Clasa Roluri servește
aceluiași scop pentru gestionarea rolurilor ca și clasa Membru pentru calitatea de membru - oferă o serie
de metode de utilitate statică care vă permit să modificați informațiile despre roluri. Tabelul 20-9 enumeră
metodele pe care le puteți utiliza.
703
CAPITOLUL APARTENENȚĂ
20
Metodă Descriere
DeleteRole() Șterge un rol existent din baza de date. Dacă doriți să ștergeți un rol
care are în prezent membri alocați, fie trebuie să eliminați
utilizatorii din rol mai întâi sau utilizați versiunea supraîncărcată a DeleteRole() care
acceptă parametrul throwOnPopulatedRole (pe care trebuie să îl setați la
fals).
AddUserToRole(), Atribuie un rol unui utilizator, atribuie mai multe roluri unui utilizator simultan, atribuie un
AddUserToRoles(), rol pentru mai mulți utilizatori sau atribuie mai multe roluri mai multor utilizatori. Dacă vrei
AddUsersToRole() și Pentru a atribui un rol unui număr mare de utilizatori, cea mai rapidă abordare este utilizarea
AddUsersToRoles() clasa Membership pentru a prelua numele de utilizator corespunzătoare (dacă
necesar), apoi utilizați AddUsersToRole() sau AddUsersToRoles()
metoda clasei Roluri pentru a aplica schimbarea tuturor simultan.
RemoveUserFromRole(), Vă permite să eliminați un utilizator dintr-un rol. Puteți efectua această operație
RemoveUserFromRoles(), pe mai mulți utilizatori simultan sau eliminați un utilizator din mai multe roluri simultan,
RemoveUsersFromRole(), în funcție de metoda pe care o utilizați.
și
RemoveUsersFromRoles()
GetUsersInRole() Preia toți utilizatorii care fac parte dintr-un anumit rol.
FindUsersInRole() Preia toți utilizatorii care fac parte dintr-un anumit rol (la fel ca
GetUsersInRole()). Cu toate acestea, vă permite să limitați rezultatele la utilizatorii care
au o anumită bucată de text în numele lor de utilizator.
De exemplu, puteți utiliza următoarea rutină de tratare a evenimentelor cu controlul CreateUserWizard pentru
a atribui un utilizator nou creat într-un anumit rol:
704
CAPITOLUL APARTENENȚĂ
20
<authorization> <deny
users="?" /> <deny
roles="Guest" /> <allow
users="*" /> </authorization>
Aceste reguli sunt interzise tuturor utilizatorilor anonimi și utilizatorilor din rolul de invitat. Rețineți că un utilizator
poate face parte din mai multe roluri, deci ordinea etichetelor <refuzați> contează. Prima regulă care se potrivește
determină dacă utilizatorul este permis sau refuzat.
În mod similar, știți cum să utilizați metoda User.IsInRole() pentru a lua o decizie de
autorizare programatică:
Controlul LoginView
LoginView este un control de vizualizare precum controlul Panel sau MultiView despre care ați aflat în capitolul 10.
Diferența este că utilizatorul nu alege ce vizualizare să fie utilizată. În schimb, vizualizarea este setată pe baza stării de
autentificare a utilizatorului.
Cel mai simplu mod de a utiliza LoginView este de a afișa conținut separat pentru utilizatorii autentificați și
anonimi. Pentru a utiliza acest design, pur și simplu completați un anumit conținut în secțiunile
<AnonymousTemplate> și <LoggedInTemplate> ale controlului. Iată un exemplu:
705
CAPITOLUL APARTENENȚĂ
20
</AnonymousTemplate> <LoggedInTemplate> <h1> Sunteți conectat</h1> <p>Acum sunteți gata să vedeți acest conținut
super-secret.</p>
</LoggedInTemplate>
</asp:LoginView>
Figura 20-15 prezintă cele două moduri în care poate apărea acest control, în funcție de faptul dacă utilizatorul este conectat în
prezent.
LoginView acceptă, de asemenea, o altă etichetă - eticheta Grupuri de roluri. În tagul Grupuri de roluri, adăugați unul sau
mai multe controale Grup de roluri. Fiecare grup de roluri este mapat în mod specific la unul sau mai multe roluri. Cu alte
cuvinte, atunci când utilizați șablonul Grupuri de roluri, puteți afișa conținut diferit pentru utilizatorii autentificați, în funcție
de rolul din care fac parte.
Iată un exemplu:
<Grupuri de roluri>
<asp:RoleGroup Roles="Utilizator, invitat"> <
ContentTemplate> <p>Dacă vedeți acest lucru, sunteți membru
al rolurilor de utilizator sau invitat.</p>
< /ContentTemplate>
</asp:RoleGroup> <asp:RoleGroup Roles="Administrator"> <ContentTemplate> <p>Felicitări, sunteți
administrator.</p> </ContentTemplate>
706
CAPITOLUL APARTENENȚĂ
20
</asp:RoleGroup> </
RoleGroups> </asp:LoginView>
Nu uitați, un utilizator poate aparține mai multor roluri. Cu toate acestea, se poate afișa un singur șablon la un
moment dat. Când potriviți rolul cu un Grup de roluri, controlul LoginView trece prin tagurile RoleGroup în
ordine și utilizează prima potrivire. Dacă nu poate găsi o potrivire, folosește obișnuitul <LoggedInTemplate>.
LoginView este un control destul de puternic. Vă oferă o modalitate eficientă de a separa conținutul securizat
de conținutul obișnuit în mod declarativ - adică fără a scrie cod personalizat pentru a ascunde și afișa
etichete. Această abordare este mai clară, mai concisă și mai puțin predispusă la erori.
Ultimul cuvânt
Caracteristicile de membru ASP.NET vă oferă mai multe servicii de nivel înalt care funcționează cu sistemele de
autentificare a formularului de bază și sistemele de autentificare Windows despre care ați aflat în capitolul 19.
În acest capitol, ați văzut cum să utilizați calitatea de membru pentru a menține o bază de date de utilizatori,
fie cu SQL Server Express Edition gratuit, fie cu o altă versiune de SQL Server. De asemenea, ați învățat cum
să utilizați controalele de securitate predefinite, care vă oferă o modalitate convenabilă și flexibilă de a adăuga
caracteristici de gestionare a utilizatorilor și de a organiza conținut securizat. În cele din urmă, ați luat în
considerare modul în care puteți utiliza gestionarea rolurilor împreună cu calitatea de membru pentru a
determina exact ce acțiuni ar trebui și nu ar trebui să aibă voie să efectueze un utilizator în aplicațiile dvs.
707
Descărcați de la Wow! eBook <www.wowebook.com>
C A P I T O L U L 21
•■■
Profiluri
Puteți stoca informații pentru utilizatorii site-ului dvs. web într-o varietate de moduri. În capitolul 8, ați învățat cum să
utilizați tehnici precum starea vizualizării, starea sesiunii și cookie-urile pentru a urmări informațiile pentru o perioadă
scurtă de timp. Dar dacă trebuie să stocați informații între vizite, singura opțiune realistă este o bază de date pe
server. Folosind abilitățile ADO.NET pe care le-ați învățat până acum, este destul de ușor să salvați informații precum
adresele clienților și preferințele utilizatorilor într-o bază de date și să le recuperați mai târziu.
Singura problemă cu abordarea bazei de date este că depinde de dvs. să scrieți tot codul pentru preluarea informațiilor și
actualizarea înregistrărilor. Acest cod nu este teribil de complex - capitolul 14 acoperă tot ce trebuie să știți - dar poate fi
obositor. ASP.NET include o caracteristică care vă permite să evitați această plictiseală, dacă puteți lucra în anumite
limitări. Această caracteristică se numește profiluri și este concepută pentru a urmări automat informațiile specifice
utilizatorului.
Când utilizați profiluri, ASP.NET se ocupă de munca lipsită de farmec de a prelua informațiile de care aveți nevoie și de
a actualiza baza de date atunci când se schimbă. Nu trebuie să scrieți niciun cod ADO.NET sau chiar să proiectați
tabelele de baze de date corespunzătoare, deoarece ASP.NET are grijă de toate detaliile. Cel mai bun dintre toate,
funcția de profiluri se integrează cu autentificarea ASP.NET, astfel încât informațiile pentru utilizatorul conectat în
prezent (denumit profilul acelui utilizator) sunt întotdeauna disponibile pentru codul paginii dvs. web.
Singurul dezavantaj al caracteristicii de profiluri este că vă obligă să utilizați o structură de bază de date prestabilită. Acest
lucru vă împiedică să utilizați tabelele pe care le-ați creat deja pentru a stoca detalii specifice utilizatorului și reprezintă o
nouă provocare dacă doriți să utilizați aceleași informații în alte aplicații sau instrumente de raportare. Dacă structura
blocată este prea restrictivă, singura alegere este să creați un furnizor de profiluri personalizate care să extindă funcția de
profiluri (care este o sarcină mai dificilă în afara domeniului de aplicare al acestei cărți) sau să renunțați complet la
profiluri și să scrieți manual propriul cod de ADO.NET.
În acest capitol, veți învăța cum să utilizați profilurile, cum funcționează sistemul de profiluri și când
profilurile au cel mai mult sens.
Înțelegerea profilurilor
Una dintre cele mai semnificative diferențe dintre profiluri și alte tipuri de gestionare a stării este că profilurile sunt concepute
pentru a stoca informații permanent, utilizând o sursă de date back-end, cum ar fi o bază de date. Majoritatea celorlalte tipuri de
gestionare a stărilor sunt concepute pentru a menține informații pentru o serie de solicitări care apar într-un interval relativ scurt
de timp (cum ar fi starea sesiunii) sau în sesiunea curentă a browserului (cum ar fi starea vizualizării și cookie-urile
nepersistente) sau pentru a transfera informații de la o pagină la alta (cum ar fi șirul de interogare și postarea pe mai multe
pagini). Dacă aveți nevoie să stocați informații pe termen lung într-o bază de date, profilurile oferă pur și simplu un model
convenabil care gestionează recuperarea și persistența acestor informații pentru dvs.
Înainte de a începe să utilizați profilurile, trebuie să le evaluați cu atenție. În secțiunile următoare, veți afla
cum se stivuiesc.
709
CAPITOLUL PROFILURI
21
Performanța profilului
Scopul funcției de profiluri ASP.NET este de a oferi o modalitate transparentă de gestionare a informațiilor specifice
utilizatorului, fără a vă forța să scrieți cod de acces personalizat la date utilizând clasele de date ADO.NET. Din păcate,
multe caracteristici care par convenabile suferă de performanțe sau scalabilitate slabă. Aceasta este o preocupare în
special pentru profiluri, deoarece acestea implică accesul la baza de date, iar accesul la baza de date poate deveni cu
ușurință un blocaj de scalabilitate pentru orice aplicație web.
Deci, profilurile suferă de probleme de scalabilitate? Această întrebare nu are un răspuns simplu. Totul depinde de
cantitatea de date pe care trebuie să o stocați și de cât de des intenționați să le accesați. Pentru a lua o decizie în
cunoștință de cauză, trebuie să știți mai multe despre modul în care funcționează profilurile.
Profilurile se conectează la ciclul de viață al paginii în două moduri:
• Prima dată când accesați obiectul Profil din codul dvs., ASP.NET preia datele
complete de profil pentru utilizatorul curent din baza de date. Dacă citiți informațiile
de profil de mai multe ori în aceeași solicitare, ASP.NET le citește o dată și apoi le
reutilizează, salvând baza de date de munca suplimentară inutilă.
• Dacă modificați orice date de profil, actualizarea este amânată până la finalizarea
procesării paginii. În acel moment (după ce evenimentele PreRender,
PreRenderComplete și Unload s-au declanșat pentru pagină), profilul este scris înapoi în
baza de date. În acest fel, mai multe modificări sunt grupate într-o singură operație. Dacă
nu modificați datele de profil, nu se efectuează lucrări suplimentare în baza de date.
În general, caracteristica profiluri ar putea avea ca rezultat două călătorii suplimentare ale bazei de date pentru fiecare solicitare
(într-un scenariu de citire-scriere) sau o călătorie suplimentară a bazei de date (dacă citiți pur și simplu datele de profil). Caracteristica
profiluri nu se integrează cu memorarea în cache, astfel încât fiecare solicitare care utilizează date de profil necesită o conexiune la
baza de date. Din punct de vedere al performanței, profilurile funcționează cel mai bine atunci când următoarele sunt adevărate:
710
CAPITOLUL PROFILURI
21
Un alt câmp indică unde începe și unde se oprește fiecare valoare, utilizând un format ca acesta:
Nume:S:0:11:Stradă:S:11:19:Oraș:S:30:6:Stat:S:36:10:Cod poștal:S:46:5:Țară:S:51:6
În esență, acest șir identifică valoarea (Nume, Stradă, Oraș etc.), modul în care este stocată (S pentru șir),
poziția inițială și lungimea. Deci prima parte a acestui șir
Nume:S:0:11
indică faptul că prima proprietate de profil este Nume, care este stocată ca șir, începe de la poziția 0 și are 11
caractere.
Deși această abordare vă oferă flexibilitatea de a stoca aproape orice combinație de date, face mai dificilă utilizarea
acestor date în alte aplicații. Puteți scrie cod personalizat pentru a analiza datele de profil pentru a găsi informațiile
dorite, dar în funcție de cantitatea de date și de tipurile de date pe care le utilizați, acesta poate fi un proces extrem
de obositor. Și chiar dacă faceți acest lucru, sunteți încă limitat în modurile în care puteți reutiliza aceste informații.
De exemplu, imaginați-vă că utilizați profiluri pentru a stoca informații despre adresa clienților. Din cauza formatului
proprietar, nu mai este posibil să generați liste de clienți într-o aplicație precum Microsoft Word sau să efectuați
interogări care filtrează sau sortează înregistrările utilizând aceste date de profil. (De exemplu, nu puteți efectua cu
ușurință o interogare pentru a găsi toți clienții care locuiesc într-un anumit oraș.) Această problemă are două soluții:
Notă: De asemenea, este important să luați în considerare tipul de date care funcționează cel mai bine într-un profil. Ca și în cazul multor alte tipuri de
•
Gestionarea stării, puteți stoca orice tipuri serializabile într-un profil, inclusiv tipuri simple și clase personalizate.
O diferență semnificativă între profiluri și alte tipuri de gestionare a stării este că profilurile sunt stocate ca
înregistrări individuale, fiecare dintre acestea fiind identificată în mod unic prin numele de utilizator. Aceasta
înseamnă că profilurile necesită utilizarea unui fel de sistem de autentificare. Nu contează ce tip de sistem de
autentificare utilizați (Windows, formulare sau un sistem de autentificare particularizat) - singura cerință este
ca utilizatorilor autentificați să li se atribuie un nume de utilizator unic. Acest nume de utilizator este utilizat
pentru a găsi înregistrarea profilului corespunzător în baza de date.
• Notă Mai târziu în acest capitol (în secțiunea "Profiluri anonime"), veți afla cum caracteristica de
identificare anonimă vă permite să stocați temporar informațiile de profil pentru utilizatorii care nu
s-au conectat.
711
CAPITOLUL PROFILURI
21
Atunci când decideți dacă să utilizați profiluri, este firesc să comparați funcția de profiluri cu tipul de cod personalizat de
acces la date pe care l-ați scris în capitolul 14 (și componentele bazei de date pe care veți învăța să le construiți în
capitolul 22). În mod clar, scrierea propriului cod ADO.NET este mult mai flexibilă. Vă permite să stocați alte tipuri de
informații și să efectuați sarcini de afaceri mai complexe. De exemplu, un site de comerț electronic ar putea utiliza în mod
realist profiluri pentru a menține informațiile despre adresa clienților (cu limitările discutate în secțiunea anterioară). Cu
toate acestea, nu veți utiliza un profil pentru a stoca informații despre comenzile anterioare. Nu numai că este mult prea
multă informație pentru a fi stocată eficient, dar este, de asemenea, incomod de manipulat.
Utilizarea SqlProfileProvider
SqlProfileProvider vă permite să stocați informații de profil într-o bază de date SQL Server. Puteți alege să creați
tabelele de profil în orice bază de date. Cu toate acestea, nu puteți modifica niciunul dintre celelalte detalii ale
schemei bazei de date, ceea ce înseamnă că sunteți blocat în anumite nume de tabele, nume de coloane și format
de serializare. De la început până la sfârșit, trebuie să efectuați următorii pași pentru a utiliza profilurile:
1. Activați autentificarea pentru o porțiune a site-ului dvs.
2. Configurați furnizorul de profil. (Acest pas este opțional dacă utilizați SQL Server
Express. Profilurile sunt activate în mod implicit.)
3. Creați tabelele de profil. (Acest pas nu este necesar dacă utilizați SQL Server
Express.)
4. Definiți câteva proprietăți ale profilului.
5. Utilizați proprietățile profilului în codul paginii dvs.
Veți aborda acești pași în secțiunile următoare.
Activarea autentificării
Deoarece profilurile sunt stocate într-o înregistrare specifică utilizatorului, trebuie să autentificați utilizatorul curent înainte de a
putea citi sau scrie informații de profil. Puteți utiliza orice tip de sistem de autentificare, inclusiv autentificarea bazată pe
Windows și autentificarea bazată pe formulare. Sistemului de profiluri nu-i pasă - pur și simplu stochează informațiile specifice
utilizatorului într-o înregistrare identificată pe baza numelui de utilizator. Văzând că fiecare sistem de autentificare identifică
utilizatorii în mod unic după numele de utilizator, orice sistem de autentificare va funcționa.
<configuration> <system.web>
<authentication mode="Windows"/>
<authorization> <deny users="?" />
</autorizație> ...
</system.web> </configuration>
Deoarece acest exemplu utilizează autentificarea Windows, nu este necesar să creați o înregistrare pentru
fiecare utilizator. În schimb, veți utiliza conturile de utilizator Windows existente care sunt definite pe serverul
web. Această abordare vă scutește, de asemenea, de crearea unei pagini de conectare, deoarece browserul
gestionează procesul de conectare. (Pentru mai multe informații despre autentificarea Windows, consultați
capitolul 19.)
712
CAPITOLUL PROFILURI
21
Dacă decideți să utilizați în schimb autentificarea formularelor, va trebui să decideți dacă doriți să efectuați
autentificarea utilizând propria listă de utilizatori particularizată (capitolul 19) sau în combinație cu
caracteristicile de membru (capitolul 20). În majoritatea cazurilor, caracteristicile de membru și profiluri sunt
utilizate împreună - la urma urmei, dacă utilizați caracteristica profiluri pentru a stoca automat informații
specifice utilizatorului, de ce să nu stocați automat și lista acreditărilor de utilizator (nume de utilizator și
parole) în aceeași bază de date?
Sfat: Exemplele descărcabile pentru acest capitol afișează profiluri în acțiune într-un site care utilizează autentificarea formularelor și într-un alt site care utilizează autentificarea Windows.
După ce ați ales sistemul de autentificare (și v-ați ocupat de orice alte treburi care ar putea fi necesare,
cum ar fi crearea unei liste de utilizatori și generarea paginii de conectare), sunteți gata să utilizați
profilurile. Nu uitați, profilurile stochează informații specifice utilizatorului, astfel încât utilizatorul trebuie să
fie autentificat înainte ca profilul său să fie disponibil. În fișierul web.config afișat anterior, o regulă de
autorizare asigură acest lucru prin negarea tuturor utilizatorilor anonimi.
713
CAPITOLUL PROFILURI
21
</configurare>
Acesta este același proces pe care l-ați utilizat în capitolul 20, deoarece atât caracteristica de membru, cât și
caracteristica profiluri utilizează șirul de conexiune LocalSqlServer. În acest exemplu, noul șir de conexiune este pentru
versiunea completă de SQL Server. Utilizează o bază de date numită aspnetdb pe computerul local. Apoi, va trebui să
creați baza de date aspnetdb utilizând utilitarul de linie de comandă aspnet_regsql.exe. Acesta este același instrument
care vă permite să generați baze de date pentru alte caracteristici ASP.NET, cum ar fi starea sesiunii bazate pe SQL
Server, calitatea de membru, rolurile, dependențele memoriei cache a bazei de date și personalizarea părților web.
Puteți găsi instrumentul aspnet_regsql.exe în folderul c:\Windows\Microsoft.NET\ Framework\[Version] (unde [Version]
este versiunea de ASP.NET instalată, cum ar fi v4.0.30319). Pentru a crea tabelele, vizualizările și procedurile stocate
necesare pentru profiluri, utilizați opțiunea de linie de comandă -A p. Celelalte detalii pe care poate fi necesar să le
furnizați includ locația serverului (-S), numele bazei de date (-d) și informațiile de autentificare pentru conectarea la baza
de date (utilizați -u și -P pentru a furniza o parolă și un nume de utilizator sau utilizați -E pentru a utiliza contul Windows
curent). Dacă omiteți locația serverului și numele bazei de date, aspnet_regsql.exe utilizează instanța implicită pe
computerul curent și creează o bază de date denumită aspnetdb.
Cel mai simplu mod de a utiliza aspnet_regsql este să deschideți promptul de comandă Visual Studio. Pentru aceasta, deschideți
meniul Start și alegeți Toate programele → Microsoft Visual Studio 2010 → Visual Instrumente Visual Studio
Linia de comandă Studio. Următorul exemplu creează o bază de date numită aspnetdb în serverul→ de baze
de date SQL Server pe computerul curent. Adaugă toate tabelele ASP.NET, inclusiv cele utilizate pentru
calitatea de membru, autentificarea bazată pe roluri și profilurile:
aspnet_regsql.exe -S (locală) -E -A toate
Dacă doriți să utilizați o altă bază de date, trebuie să specificați numele bazei de date utilizând parametrul -d.
În orice caz, ar trebui să utilizați o bază de date nouă, necompletată, care nu include alte tabele
particularizate. Acest lucru se datorează faptului că aspnet_regsql.exe creează mai multe tabele pentru
profiluri (consultați tabelul 21-1 din secțiunea următoare) și nu ar trebui să riscați să le confundați cu datele
de afaceri.
Notă
Această linie de comandă utilizează opțiunea -A all pentru a crea tabele pentru toate caracteristicile bazei de date ASP.NET, inclusiv profiluri și calitatea de membru. De asemenea, puteți alege să adăugați tabele pentru o singură caracteristică la un moment dat. Pentru mai multe informații despre -A și ceilalți parametri de linie de comandă pe care îi puteți utiliza cu aspnet_regsql, consultați tabelul 20-2 din capitolul 20.
714
CAPITOLUL PROFILURI
21
aspnet_Applications Listează toate aplicațiile web care au înregistrări în această bază de date. Este
Este posibil ca mai multe aplicații ASP.NET să utilizeze același ASPNETDB
bază de date. În acest caz, aveți opțiunea de a separa profilul
astfel încât să fie distinct pentru fiecare aplicație (oferind fiecare aplicație
un alt nume de aplicație atunci când înregistrați
furnizorul profilului) sau partajarea acestuia (oferind fiecărei aplicații același lucru
numele aplicației).
Figura 21-1 prezintă relațiile dintre cele mai importante tabele de profil.
715
CAPITOLUL PROFILURI
21
</configurare>
De obicei, veți furniza și tipul de date. (Dacă nu, proprietatea este tratată ca un șir.) Puteți specifica orice
tip de date .NET serializabil, așa cum se arată aici:
716
CAPITOLUL PROFILURI
21
Tabelul 21-2.
Atributele proprietății profilului
atribut (pentru
elementul <add>) Descriere
tip Numele clasei complet calificate care reprezintă tipul de date pentru această
proprietate. În mod implicit, acesta este System.String.
serializare Formatul de utilizat la serializarea acestei valori (String, Binary, XML sau
ProviderSpecific). Veți analiza mai atent modelul de serializare în secțiunea
"Serializarea profilului".
Numai citire O valoare booleană care determină dacă o valoare este modificabilă. Dacă este
adevărată, proprietatea poate fi citită, dar nu modificată. (Încercarea de a modifica
proprietatea va cauza o eroare de compilare.) În mod implicit, acest lucru este fals.
defaultValue O valoare implicită care va fi utilizată dacă profilul nu există sau nu include această
informație. Valoarea implicită nu are niciun efect asupra serializării - dacă setați o
proprietate de profil, ASP.NET va comite valorile curente în baza de date, chiar dacă
acestea se potrivesc cu valorile implicite.
allowAnonymous A
valoare booleană care indică dacă această proprietate poate fi utilizată cu
caracteristica de profiluri anonime discutată mai târziu în acest capitol. În mod
implicit, acest lucru este fals.
furnizor Furnizorul de profiluri care ar trebui utilizat pentru a gestiona doar această
proprietate. În mod implicit, toate proprietățile sunt gestionate utilizând furnizorul
specificat în elementul <profil>, dar puteți atribui proprietăți diferite diferiților furnizori.
Profile.FirstName = "Henry";
Figura 21-2 prezintă o pagină completă de test care permite utilizatorului să afișeze informațiile de profil
pentru utilizatorul curent sau să seteze noi informații de profil.
717
CAPITOLUL PROFILURI
21
Descărcați de la Wow! eBook <www.wowebook.com>
Prima dată când rulează această pagină, nu se regăsesc informații de profil și nu se utilizează nicio
conexiune la baza de date. Cu toate acestea, dacă faceți clic pe butonul Afișare date profil, informațiile
de profil sunt preluate și afișate pe pagină:
protejat void cmdShow_Click(expeditor obiect, EventArgs e)
{
Lbl. Text = "Prenume: " + Profile.FirstName + "<br />" + "Nume: " +
Profile.LastName + "<br />" + "Data nașterii: " +
Profile.DateOfBirth.ToString("D");
}
În acest moment, va apărea o eroare dacă baza de date de profil lipsește sau conexiunea nu poate fi
deschisă. În caz contrar, pagina dvs. va rula fără probleme și veți vedea informațiile de profil nou preluate.
Din punct de vedere tehnic, profilul complet este recuperat atunci când codul accesează proprietatea
Profile.FirstName din prima linie și este utilizat pentru instrucțiunile de cod ulterioare.
Notă Proprietățile profilului se comportă ca orice altă variabilă membră a clasei. Aceasta înseamnă că, dacă citiți o valoare de profil care nu a fost setată, veți obține o valoare inițializată implicită (cum ar fi un șir gol sau 0).
718
CAPITOLUL PROFILURI
21
Dacă faceți clic pe butonul Setare date profil, informațiile profilului sunt setate pe baza valorilor
curente ale controlului:
Acum, informațiile de profil sunt angajate în baza de date atunci când solicitarea paginii se termină. Dacă
doriți să comiteți unele sau toate informațiile mai devreme (și, eventual, să suportați mai multe călătorii în
baza de date), apelați metoda Profile.Save(). După cum puteți vedea, funcția de profiluri este de neegalat
pentru simplitate.
Sfat: obiectul Profil nu include doar proprietățile pe care le-ați definit. De asemenea, oferă proprietățile LastActivityDate (ultima dată când a fost utilizat acest profil) și LastUpdatedDate (ultima dată când acest profil a fost modificat), utilizând informații extrase din baza de date.
Serializarea profilului
Mai devreme, ați aflat cum proprietățile sunt serializate într-un singur șir. De exemplu, dacă salvați
un prenume al lui Harriet și un nume de familie al lui Smythe, ambele valori sunt aglomerate
împreună în câmpul PropertyValuesString al tabelului aspnet_Profile din baza de date, astfel:
HarrietSmythe
Câmpul NumeProprietăți (și în tabelul aspnet_Profile) oferă informațiile de care aveți nevoie pentru a analiza
fiecare valoare din câmpul PropertyValuesString. Iată ce veți vedea în câmpul NumeProprietăți din acest
exemplu:
Prenume:S:0:7:Nume:S:7:6:
Colonele (:) sunt folosite ca delimitatori. Formatul de bază este următorul:
PropertyName:StringOrBinarySerialization:StartingCharacterIndex:Length:
Ceva interesant se întâmplă dacă creați un profil cu un tip de date DateTime, astfel:
719
CAPITOLUL PROFILURI
21
(implicit) ca XML. Următoarele două proprietăți de profil sunt serializate ca șiruri obișnuite.
DataNașterii:S:0:81:Prenume:S:87:7:Nume:S:94:6:
Interesant este că aveți posibilitatea de a schimba formatul de serializare al oricărei proprietăți de profil adăugând
atributul serializeAs la declarația sa din fișierul web.config. Tabelul 21-3 enumeră opțiunile dumneavoastră.
SerializeAs Descriere
Șir Convertește textul într-o reprezentare șir. Necesită un convertor de tip care poate
Ocupați-vă de treabă.
XML Efectuează conversia textului într-o reprezentare XML, care este stocată într-un șir, utilizând pictograma
System.Xml.XmlSerialization.XmlSerializer (aceeași clasă care este utilizat cu web
servicii).
Binar Efectuează conversia textului într-o reprezentare binară proprietară care numai .NET
înțelege folosind System.Runtime.Serialization.Formatters.Binary.
Formatter binar. Aceasta este cea mai compactă opțiune, dar cea mai puțin flexibilă. Binar
datele sunt stocate în câmpul PropertyValuesBinary în loc de PropertyValues.
Acum, data viitoare când setați profilul, reprezentarea serializată din câmpul PropertyValuesString va stoca
informații pentru Prenume și Nume. Ia această formă:
Singura indicație a acestei schimbări este utilizarea literei B în loc de S în câmpul PropertyNames (și faptul că
sunt necesari mai puțini octeți de):
DataNașterii:S:0:9:Prenume: :Nume:S:9:64:
B:0:31
720
CAPITOLUL PROFILURI
21
Toate aceste detalii de serializare ridică o întrebare importantă - ce se întâmplă atunci când modificați
proprietățile profilului sau modul în care sunt serializate? Proprietățile profilului nu au suport pentru controlul
versiunilor. Cu toate acestea, puteți adăuga sau elimina proprietăți cu consecințe relativ minore. De exemplu,
ASP.NET va ignora proprietățile prezente în tabelul aspnet_Profile, dar care nu sunt definite în fișierul
web.config. Data viitoare când modificați o parte a profilului, aceste proprietăți vor fi înlocuite cu noile
informații de profil. În mod similar, dacă definiți un profil în fișierul web.config care nu există în informațiile de
profil serializate, ASP.NET va utiliza doar valoarea implicită. Cu toate acestea, este posibil ca modificările mai
dramatice, cum ar fi redenumirea unei proprietăți, modificarea tipului de date și așa mai departe, să provoace
o excepție atunci când încercați să citiți informațiile de profil. Chiar mai rău, deoarece formatul serializat al
informațiilor de profil este proprietar, nu aveți o modalitate ușoară de a migra datele de profil existente într-o
nouă structură de profil.
Sfat: Nu toate tipurile sunt serializabile din toate punctele de vedere. De exemplu, clasele care nu oferă un constructor fără parametri nu pot fi serializate în modul Xml. Clasele care nu au atributul serializabil nu pot fi serializate în modul binar. Veți lua în considerare această distincție atunci când vă gândiți cum să utilizați tipuri personalizate cu profiluri (consultați secțiunea "Profiluri și tipuri de date personalizate"), dar deocamdată rețineți că puteți
întâlni tipuri care pot fi serializate numai dacă alegeți un alt mod de serializare.
Grupuri de profil
Dacă aveți un număr mare de setări de profil și unele setări sunt legate logic între ele, se recomandă să utilizați
grupuri de profiluri pentru a obține o organizare mai bună.
De exemplu, este posibil să aveți unele proprietăți care se ocupă de preferințele utilizatorului și altele
care se ocupă de informațiile de expediere. Iată cum puteți organiza aceste proprietăți de profil
utilizând elementul <grup>:
<profile> <properties> <group name="Preferences"> <add
name="LongDisplayMode" defaultValue="true" type="Boolean" /> <add
name="ShowSummary" defaultValue="true" type="Boolean" /> </group> <group
name="Address"> <add name="Name" type="String" /> <add name="Street"
type="String" /> <add name="City" type="String" /> <add name="ZipCode"
type="String" /> <add name="State" type="String" /> <add name="Country"
type="String" /> </group> </properties> </profile>
Acum puteți accesa proprietățile prin numele grupului din codul dvs. De exemplu, iată cum puteți prelua
informațiile despre țară:
lblCountry.Text = Profile.Address.Country;
721
CAPITOLUL PROFILURI
21
Grupurile sunt de fapt doar substitutul unui om sărac pentru o structură sau clasă personalizată cu drepturi
depline. De exemplu, puteți obține același efect ca în exemplul anterior declarând o clasă de adrese
particularizată. De asemenea, veți avea posibilitatea de a adăuga alte caracteristici (cum ar fi validarea în
procedurile proprietății). Următoarea secțiune arată cum.
public Address(nume șir, stradă șir, oraș șir, cod poștal șir,
stare șir, țară șir) { Nume = nume; Stradă = stradă; Oraș = oraș;
Cod poștal = Cod poștal; Stat = stat; } Țara = țara;
adresa publică()
{}
}
Puteți plasa această clasă în directorul App_Code. Ultimul pas este să adăugați o proprietate care o utilizează:
Acum puteți crea o pagină de test care utilizează clasa Adresă. Figura 21-3 prezintă un exemplu care
vă permite pur și simplu să încărcați, să modificați și să salvați informațiile despre adresă într-un profil.
722
CAPITOLUL PROFILURI
21
723
CAPITOLUL PROFILURI
21
Disecarea codului . . .
• Când pagina se încarcă (și când utilizatorul face clic pe butonul Obțineți), informațiile
de profil sunt copiate din obiectul Profile.Address în diferitele casete de text. O metodă
privată LoadProfile() gestionează această sarcină.
• Utilizatorul poate modifica valorile adreselor din casetele de text. Cu toate acestea,
modificarea nu este comisă până când utilizatorul nu face clic pe butonul Salvare.
• Când se face clic pe butonul Salvare, se creează un nou obiect Adresă utilizând
constructorul care acceptă informații despre nume, stradă, oraș, cod poștal, stat și
țară. Acest obiect este apoi atribuit proprietății Profile.Address. În loc să utilizați
această abordare, puteți modifica fiecare proprietate a obiectului curent
Profile.Address pentru a se potrivi cu valorile text.
• Conținutul obiectului Profil este salvat automat în baza de date la încheierea
solicitării. Nu este necesară muncă suplimentară.
724
CAPITOLUL PROFILURI
21
Salvări automate
Caracteristica profiluri nu poate detecta modificări ale tipurilor complexe de date (altceva decât șiruri de caractere,
tipuri numerice simple, valori booleene etc.). Aceasta înseamnă că, dacă profilul dvs. include tipuri de date
complexe, ASP.NET salvează informațiile complete ale profilului la sfârșitul fiecărei solicitări care accesează
obiectul Profil.
Acest comportament adaugă, evident, cheltuieli generale inutile. Pentru a optimiza performanța atunci când
lucrați cu tipuri complexe, aveți mai multe opțiuni. O opțiune este să setați proprietatea profilului
corespunzător să fie doar în citire în fișierul web.config (dacă știți că proprietatea nu se schimbă niciodată).
O altă abordare este să dezactivați complet comportamentul de salvare automată adăugând atributul
automaticSaveEnabled pe elementul <profil> și setându-l la fals, așa cum se arată aici:
<profile >provider="TrueProvider" immediateSaveEnabled="false">... </profil>
Dacă alegeți această abordare, depinde de dvs. să apelați Profile.Save() pentru a comite în mod explicit
modificări. În general, această abordare este cea mai convenabilă, deoarece este ușor să identificați locurile
din codul dvs. în care modificați profilul. Doar adăugați apelul Profile.Save () la sfârșit:
Profile.Address = noua adresa(txtName.Text, txtStreet.Text, txtCity.Text, txtZip.Text,
txtState.Text, txtCountry.Text);
Profile.Save();
De exemplu, puteți modifica exemplul anterior (prezentat în Figura 21-3) pentru a salva informațiile despre adresă
numai atunci când se modifică. Cel mai simplu mod de a face acest lucru este să dezactivați salvările automate,
dar apelați Profile.Save() atunci când faceți clic pe butonul Salvare. De asemenea, puteți gestiona evenimentul
TextBox.TextChanged pentru a determina când se fac modificări și puteți salva profilul imediat în acest moment.
API-ul Profil
Deși pagina dvs. primește automat informațiile de profil pentru utilizatorul curent, acest lucru nu vă împiedică să
recuperați și să modificați profilurile altor utilizatori. De fapt, aveți două instrumente care să vă ajute - clasa ProfileBase
și clasa ProfileManager.
Obiectul Profil (furnizat de proprietatea Page.Profile) include o metodă utilă GetProfile() care regăsește
informațiile de profil pentru un anumit utilizator după numele de utilizator. Figura 21-4 prezintă un exemplu cu
un utilizator autentificat Windows.
725
CAPITOLUL PROFILURI
21
GetProfile() returnează un obiect ProfileCommon. Cu toate acestea, nu veți găsi ProfileCommon în biblioteca de clasă
.NET. Acest lucru se datorează faptului că ProfileCommon este o clasă generată dinamic pe care ASP.NET creează
pentru a stoca informațiile de profil pentru aplicația dvs. În acest exemplu, profilul definește o proprietate numită Adresă,
astfel încât să puteți prelua aceste informații utilizând proprietatea ProfileCommon.Address. Observați că, odată ce aveți
un obiect ProfileCommon, puteți interacționa cu acesta în același mod în care interacționați cu profilul pentru utilizatorul
curent (la urma urmei, este același tip de obiect). Puteți chiar să faceți modificări. Singura diferență este că modificările
nu sunt salvate automat. Dacă doriți să salvați o modificare, trebuie să apelați metoda Save() a obiectului
ProfileCommon. ProfileCommon adaugă, de asemenea, proprietățile LastActivityDate și LastUpdatedDate, pe care le
puteți utiliza pentru a determina ultima dată când un anumit profil a fost accesat și modificat.
Dacă încercați să recuperați un profil care nu există, nu veți primi o eroare. În schimb, veți ajunge pur și simplu cu
date goale (de exemplu, șiruri goale). Dacă modificați și salvați profilul, va fi creată o nouă înregistrare de profil.
}
}
Dacă trebuie să efectuați alte sarcini cu profiluri, puteți utiliza clasa ProfileManager în spațiul de nume
System.Web.Profile, care expune metodele statice utile descrise în tabelul 21-4. Multe dintre aceste metode
funcționează cu o clasă ProfileInfo, care oferă informații despre un profil. ProfileInfo include numele de
utilizator (UserName), datele ultimei actualizări și ale ultimei activități (LastUpdatedDate și LastActivityDate),
dimensiunea profilului în octeți (Dimensiune) și dacă profilul este pentru un utilizator anonim (IsAnonymous).
Nu oferă valorile reale ale profilului.
726
CAPITOLUL PROFILURI
21
Metodă Descriere
DeleteProfiles() Șterge mai multe profiluri simultan. Furnizați o colecție de nume de utilizator.
DeleteInactiveProfiles() Șterge profilurile care nu au mai fost utilizate de la o dată specificată de dvs.
De asemenea, trebuie să furnizați o valoare din enumerarea
ProfileAuthenticationOption pentru a indica tipul de profiluri pe care doriți să le
eliminați (Toate, Anonim sau Autentificat).
GetAllInactiveProfiles() Regăsește informațiile de profil pentru profilurile care nu au mai fost utilizate
din momentul specificării dvs. Trebuie să furnizați o valoare din enumerarea
ProfileAuthenticationOption. Profilurile sunt returnate ca obiecte ProfileInfo.
GetAllProfiles() Preia toate datele de profil din sursa de date ca o colecție de obiecte ProfileInfo.
Puteți alege ce tip de profiluri doriți să recuperați (Toate, Anonim sau
Autentificat). De asemenea, puteți utiliza o versiune supraîncărcată a acestei
metode care utilizează paginarea și preia doar o parte din setul complet de
înregistrări, pe baza indexului de pornire și a dimensiunii paginii solicitate.
FindProfilesByNume Regăsește o colecție de obiecte ProfileInfo care corespund unui anumit nume de
utilizator() utilizator. SqlProfileProvider utilizează o clauză LIKE atunci când încearcă să se
potrivească cu numele de utilizator, ceea ce înseamnă că puteți utiliza metacaractere,
cum ar fi simbolul %. De exemplu, în cazul în care căutați numele de utilizator
utilizator%, veți returna valori precum user1, user2, user_guest și așa mai departe.
Puteți utiliza o versiune supraîncărcată a acestei metode care utilizează paginarea.
FindInactiveProfilesByU Regăsește informațiile de profil pentru profilurile care nu au mai fost utilizate din
serName() momentul specificării dvs. De asemenea, puteți filtra anumite tipuri de profiluri (Toate,
Anonim sau Autentificat) sau puteți căuta un anumit nume de utilizator (cu potrivire
metacaractere). Valoarea returnată este o colecție de obiecte ProfileInfo.
727
CAPITOLUL PROFILURI
21
De exemplu, dacă doriți să eliminați profilul pentru utilizatorul curent, aveți nevoie doar de o singură
linie de cod:
ProfileManager.DeleteProfile(User.Identity.Name);
Și dacă doriți să afișați lista completă de utilizatori într-o pagină web (fără a include utilizatorii anonimi),
trebuie doar să adăugați un GridView cu AutoGenerateColumns setat la true și să utilizați acest cod:
Figura 21-5. Preluarea informațiilor despre toate profilurile din sursa de date
Profiluri anonime
Până în prezent, toate exemplele au presupus că utilizatorul este autentificat înainte ca orice informație de
profil să fie accesată sau stocată. De obicei, acesta este cazul. Cu toate acestea, uneori este util să creați un
profil temporar pentru un utilizator nou, necunoscut. De exemplu, majoritatea site-urilor de comerț electronic
permit utilizatorilor noi să înceapă să adauge articole într-un coș de cumpărături înainte de a se înregistra.
Dacă doriți să oferiți acest tip de comportament și alegeți să stocați articole din coșul de cumpărături într-un
profil, veți avea nevoie de o modalitate unică de a identifica în mod unic utilizatorii anonimi.
Notă: Merită să întrebați dacă are sens să stocați un coș de cumpărături într-un profil. Este un design rezonabil, funcțional, dar mulți dezvoltatori consideră că este mai ușor să controleze în mod explicit modul în care acest tip de informații sunt stocate în baza lor de date folosind codul ADO.NET personalizat în locul funcției de profil.
728
CAPITOLUL PROFILURI
21
ASP.NET oferă o caracteristică de identificare anonimă care umple acest gol. Ideea de bază este că funcția de
identificare anonimă generează automat un identificator aleatoriu pentru orice utilizator anonim. Acest identificator
aleatoriu stochează informațiile de profil în baza de date, chiar dacă nu este disponibil niciun nume de utilizator. Numele
de utilizator este urmărit pe partea clientului folosind un modul cookie (sau în adresa URL, dacă ați activat modul fără
cookie-uri). Odată ce acest cookie dispare (de exemplu, dacă utilizatorul anonim închide și redeschide browserul),
sesiunea anonimă se pierde și se creează o nouă sesiune anonimă.
Identificarea anonimă are potențialul de a lăsa o mulțime de profiluri abandonate, ceea ce irosește spațiu în
baza de date. Din acest motiv, identificarea anonimă este dezactivată în mod implicit. Cu toate acestea, îl
puteți activa utilizând elementul <anonymousIdentification> din fișierul web.config, așa cum se arată aici:
<configurare> ...
<system.web> <anonymousIdentification
enabled="true" /> ...
</system.web> </configuration>
De asemenea, trebuie să semnalizați fiecare proprietate de profil care va fi păstrată pentru utilizatorii
anonimi, adăugând atributul allowAnonymous și setându-l la true. Acest lucru vă permite să stocați doar
câteva informații de bază și să restricționați obiectele mai mari la utilizatorii autentificați.
<properties> <add name="Address" type="Address" />
allowAnonymous="true" ...
</proprietăți>
Dacă utilizați un tip complex, atributul allowAnonymous este o setare totul sau nimic. Configurați întregul
obiect pentru a accepta stocare anonimă sau nu acceptați.
Elementul <anonymousIdentification> acceptă, de asemenea, numeroase atribute opționale care vă permit
să setați numele și expirarea cookie-ului, să specificați dacă cookie-ul va fi emis numai printr-o conexiune
SSL, să controlați dacă protecția cookie-urilor (validare și criptare) este utilizată pentru a preveni manipularea
și interceptarea și să configurați suportul pentru urmărirea ID-ului fără cookie-uri. Iată un exemplu:
<anonymousIdentification enabled="true" cookieName=".
ASPXANONYMOUS" cookieTimeout="43200" cookiePath="/"
cookieRequireSSL="false" cookieSlidingExpiration="true"
cookieProtection="All" cookieless="UseCookies"/>
Pentru mai multe informații, consultați Ajutorul Visual Studio.
Sfat
Dacă utilizați identificarea anonimă, este o idee bună să ștergeți sesiunile anonime vechi în mod regulat utilizând procedura aspnet_Profile_DeleteInactiveProfiles stocată, pe care o puteți executa la intervale programate utilizând SQL Server Agent. De asemenea, puteți șterge profilurile vechi utilizând clasa ProfileManager, așa cum este descris în secțiunea anterioară.
729
CAPITOLUL PROFILURI
21
Trebuie să vă ocupați de această sarcină cu o anumită prudență. Dacă ați activat identificarea anonimă,
evenimentul MigrateAnonymous se declanșează de fiecare dată când un utilizator se conectează, chiar dacă
utilizatorul nu a introdus nicio informație în profilul anonim. Aceasta este o problemă - dacă nu sunteți atent,
puteți suprascrie cu ușurință profilul real (salvat) pentru utilizator cu profilul anonim gol. Problema este
complicată și mai mult de faptul că tipurile complexe (cum ar fi obiectul Adresă) sunt create automat de
ASP.NET, deci nu puteți verifica doar o referință nulă pentru a determina dacă utilizatorul are informații
anonime despre adresă. În exemplul anterior, codul testează pentru o proprietate Nume lipsă în obiectul
Adresă. Dacă aceste informații nu fac parte din profilul anonim, nu se migrează nicio informație. Un exemplu
mai sofisticat ar putea testa separat proprietățile individuale sau ar putea migra un profil anonim numai dacă
informațiile din profilul de utilizator lipsesc sau sunt depășite.
730
CAPITOLUL PROFILURI
21
Ultimul cuvânt
În acest capitol, ați învățat cum să utilizați profilurile și cum stochează informațiile în baza de date. Mulți
dezvoltatori ASP.NET vor prefera să-și scrie propriul cod ADO.NET pentru recuperarea și stocarea
informațiilor specifice utilizatorului. Acest lucru nu numai că vă permite să utilizați propria structură a bazei de
date, ci vă permite să adăugați propriile caracteristici, cum ar fi memorarea în cache, înregistrarea în jurnal,
validarea și criptarea. Dar profilurile sunt utile pentru construirea rapidă a aplicațiilor modeste care nu
stochează o mulțime de informații specifice utilizatorului și nu au cerințe speciale pentru modul în care sunt
stocate aceste informații.
731
PART6
• ■■
ASP.NET avansate
C A P I T O L U L 22
• ■■
Programarea bazată pe componente este o idee simplă și elegantă. Atunci când este utilizat corect, permite codului
să fie mai organizat, mai consecvent și mai reutilizabil. De asemenea, este incredibil de ușor de implementat într-o
aplicație .NET, deoarece nu trebuie să utilizați niciodată registrul Windows sau să efectuați o configurație specială.
O componentă, la modul cel mai simplu, este una sau mai multe clase care sunt compilate într-un fișier de asamblare
DLL separat. Aceste clase oferă o unitate de funcționalitate legată logic. Puteți accesa o componentă dintr-o singură
aplicație sau puteți partaja componenta între mai multe aplicații. Paginile dvs. web (sau orice altă aplicație .NET) pot
utiliza clasele din componentele dvs. în același mod în care utilizează orice altă clasă .NET. Cel mai bun dintre toate,
componenta dvs. este încapsulată, ceea ce înseamnă că oferă exact caracteristicile de care are nevoie codul dvs. și
ascunde toate celelalte detalii dezordonate.
Atunci când este combinată cu o organizare atentă, programarea bazată pe componente este baza unui design bun al
aplicațiilor ASP.NET. În acest capitol, veți examina modul în care puteți crea componente (și de ce ar trebui) și veți lua în
considerare exemple care vă arată cum să încapsulați funcționalitatea bazei de date cu un obiect de afaceri bine scris.
De asemenea, veți afla cum să legați componenta bazei de date la controalele web de pe o pagină utilizând
ObjectDataSource.
De ce să folosiți componente?
Pentru a stăpâni dezvoltarea ASP.NET, trebuie să deveniți un utilizator calificat al bibliotecii de clasă .NET. Până în prezent,
ați învățat cum să utilizați componentele .NET concepute pentru citirea fișierelor, comunicarea cu bazele de date, apelarea
serviciilor web și stocarea informațiilor despre utilizator. Deși aceste ingrediente ale bibliotecii de clasă sunt puternice, ele nu
sunt personalizabile, ceea ce este atât un avantaj, cât și un punct slab.
De exemplu, dacă doriți să regăsiți date dintr-o bază de date SQL Server, trebuie să țeseți detaliile bazei de date (cum
ar fi interogările SQL) direct în clasa din spatele codului sau (dacă utilizați SqlDataSource) în porțiunea de marcaj .aspx
a fișierului paginii web. În orice caz, dacă structura bazei de date se schimbă chiar și ușor, ați putea rămâne cu zeci de
pagini de actualizat și retestat. Pentru a rezolva aceste probleme, trebuie să creați un strat suplimentar între codul
paginii web și baza de date. Acest strat suplimentar ia forma unei componente personalizate.
Acest scenariu de bază de date este doar unul dintre motivele pentru care poate doriți să creați propriile componente.
Programarea bazată pe componente este într-adevăr doar o extensie logică a principiilor bune de organizare a codului
și oferă o listă lungă de avantaje:
Siguranță: Deoarece codul sursă nu este conținut în pagina dvs. web, nu îl puteți modifica. În schimb, sunteți limitat
la funcționalitatea oferită de componenta dvs. De exemplu, puteți configura o componentă a bazei de date pentru a
permite numai anumite operațiuni cu anumite tabele, câmpuri sau rânduri. Acest lucru este adesea mai ușor decât
configurarea permisiunilor complexe în baza de date. Deoarece aplicația trebuie să treacă prin componentă, trebuie
să joace după regulile sale.
735
CAPITOLUL PROGRAMAREA BAZATĂ PE COMPONENTE
22
O mai bună organizare: componentele elimină dezordinea din codul paginii web. De asemenea, devine
mai ușor pentru alți programatori să înțeleagă logica aplicației dvs. atunci când este împărțită în
componente separate. Fără componente, codul utilizat în mod obișnuit trebuie copiat și lipit într-o
aplicație, ceea ce face extrem de dificilă schimbarea și sincronizarea.
Depanare mai ușoară: Este imposibil să supraestimați avantajul componentelor atunci când testați și
depanați o aplicație. Programele bazate pe componente sunt împărțite în blocuri de cod mai mici și mai
strânse, facilitând izolarea exactă a locului în care apare o problemă. De asemenea, este mai ușor să
testați componentele individuale separat de restul aplicației web.
Mai multă gestionare: Programele bazate pe componente sunt mult mai ușor de îmbunătățit și modificat,
deoarece componenta și codul aplicației web pot fi modificate separat. Dusă la extrem, această
abordare vă permite să aveți o echipă de dezvoltare care lucrează la componentă și o altă echipă care
codifică site-ul web care utilizează componenta.
Reutilizarea codului: Componentele pot fi partajate cu orice aplicație ASP.NET care are nevoie de
funcționalitatea componentei. Chiar mai bine, orice aplicație .NET poate utiliza o componentă, ceea ce
înseamnă că puteți crea o "coloană vertebrală" comună a logicii care este utilizată de o aplicație web și
de o aplicație Windows obișnuită.
Simplitate: Componentele pot furniza mai multe sarcini conexe pentru o singură solicitare a clientului
(scrierea mai multor înregistrări într-o bază de date, deschiderea și citirea unui fișier într-un singur pas
sau chiar pornirea și gestionarea unei tranzacții cu baza de date). În mod similar, componentele ascund
detalii - un programator de aplicații poate utiliza o componentă a bazei de date fără a-și face griji cu
privire la numele bazei de date, locația serverului sau contul de utilizator necesar pentru conectare.
Chiar mai bine, puteți efectua o căutare utilizând anumite criterii, iar componenta însăși poate decide
dacă să utilizeze o instrucțiune SQL generată dinamic sau o procedură stocată.
Jargonul component
Programarea bazată pe componente este uneori învăluită într-o ceață de jargon specializat. Înțelegerea
acestor termeni vă ajută să sortați exact ce ar trebui să facă o componentă și, de asemenea, vă permite să
înțelegeți articolele MSDN despre proiectarea aplicațiilor. Dacă sunteți deja familiarizat cu elementele
fundamentale ale componentelor, nu ezitați să săriți mai departe.
736
CAPITOLUL PROGRAMAREA BAZATĂ PE COMPONENTE
22
Detaliul important despre designul pe trei niveluri este că informațiile călătoresc de la un singur nivel la un nivel adiacent.
Cu alte cuvinte, codul paginii dvs. web nu ar trebui să se conecteze direct la baza de date pentru a prelua informații. În
schimb, ar trebui să treacă printr-o componentă din nivelul de afaceri care se conectează la baza de date și returnează
datele.
Acest principiu de bază al organizării nu poate fi întotdeauna respectat, dar este un model bun de urmat.
Când creați o componentă, aceasta este aproape întotdeauna utilizată în al doilea nivel pentru a reduce
decalajul dintre date și interfața cu utilizatorul. Cu alte cuvinte, dacă doriți să completați o listă de categorii de
produse într-o casetă listă, codul interfeței cu utilizatorul apelează o componentă, care preia lista din baza de
date și apoi o returnează în codul dvs. Codul paginii web este izolat de baza de date, iar dacă structura bazei
de date se modifică, trebuie să modificați o componentă concisă în loc de fiecare pagină de pe site.
737
CAPITOLUL PROGRAMAREA BAZATĂ PE COMPONENTE
22
Încapsulare
Dacă proiectarea pe trei niveluri este obiectivul general al programării bazate pe componente, încapsularea este cea mai
bună regulă generală. Încapsularea este principiul că ar trebui să vă creați aplicația din "cutii negre" care ascund
informații. Deci, dacă aveți o componentă care înregistrează o achiziție pe un site de comerț electronic, acea componentă
gestionează toate detaliile și permite specificarea doar a variabilelor esențiale.
De exemplu, această componentă poate accepta un ID de utilizator și un ID de element de comandă, apoi
poate gestiona toate celelalte detalii. Codul de apelare nu trebuie să-și facă griji cu privire la modul în care
funcționează componenta sau de unde provin datele, ci trebuie doar să înțeleagă cum să utilizeze
componenta. (Acest principiu este descris într-o mulțime de moduri pitorești. De exemplu, știi cum să conduci
o mașină pentru că înțelegi interfața componentelor sale – volanul și pedalele – nu pentru că înțelegi
detaliile de nivel scăzut despre arderea internă și motor. Ca rezultat, puteți să vă transferați cunoștințele la
multe tipuri diferite de automobile, chiar dacă acestea au lucrări interne dramatic diferite.)
Obiecte de afaceri
Termenul obiect de afaceri înseamnă adesea lucruri diferite pentru oameni diferiți. În general, obiectele de
afaceri sunt componentele din al doilea nivel al aplicației dvs. care oferă stratul suplimentar între codul dvs. și
Descărcați de la Wow! eBook <www.wowebook.com>
sursa de date. Ele sunt numite obiecte de afaceri deoarece aplică reguli de afaceri. De exemplu, dacă
încercați să trimiteți o comandă de cumpărare fără articole, obiectul comercial corespunzător va arunca o
excepție și va refuza să continue. În acest caz, nu a apărut nicio eroare .NET - în schimb, ați detectat
prezența unei condiții care nu ar trebui să fie permisă în conformitate cu logica aplicației dvs. În exemplele
acestui capitol, obiectele de afaceri vor conține, de asemenea, cod de acces la date. Într-un sistem extrem de
complicat, mare și schimbabil, este posibil să doriți să subdivizați în continuare componentele și să aveți codul
interfeței cu utilizatorul vorbind cu un obiect de afaceri, care, la rândul său, vorbește cu un alt set de obiecte
care interacționează cu sursa de date. Cu toate acestea, pentru majoritatea programatorilor, acest pas
suplimentar este exagerat, mai ales cu nivelul crescut de sofisticare pe ADO.NET îl oferă.
Obiecte de date
Termenul obiect de date este, de asemenea, utilizat într-o varietate de moduri. În această carte, obiectele de
date sunt pur și simplu pachete de date pe care le utilizați pentru a trimite informații între pagina dvs. web și
obiectele dvs. de afaceri. De exemplu, puteți crea o clasă de date numită Angajat care reprezintă informațiile
dintr-o înregistrare dintr-un tabel Angajați, completată cu proprietăți precum Prenume, Nume de familie și
DataNașterii. Un obiect de date tipic este umplut cu proprietăți, dar nu oferă metode.
Componente și clase
Din punct de vedere tehnic, o componentă este doar o colecție de una sau mai multe clase (și, eventual, alte tipuri .NET,
cum ar fi structuri și enumerări) care sunt compilate împreună ca o unitate. De exemplu, System.Web.dll Microsoft este o
singură componentă (dar foarte mare) care oferă tipurile găsite în multe dintre spațiile de nume care încep cu
System.Web.
Până în prezent, exemplele de cod din această carte au folosit doar câteva tipuri de clase - în principal clase
de pagini web personalizate care moștenesc de la System.Web.UI.Page și conțin în mare parte proceduri de
gestionare a evenimentelor. Clasele componente, pe de altă parte, de obicei nu vor include nicio logică a
interfeței cu utilizatorul (ceea ce ar limita inutil utilizarea lor) și nu trebuie să moștenească de la o clasă
existentă.
738
CAPITOLUL PROGRAMAREA BAZATĂ PE COMPONENTE
22
În loc să alegeți doar Fișier → Proiect nou nou pentru a crea biblioteca clasei, o puteți adăuga la
→ aceeași soluție ca și site-ul dvs. Acest lucru facilitează depanarea codului din componentă în timp ce îl testați cu o pagină web.
(Pe cont propriu, nu există nicio modalitate de a rula o componentă, deci nu există nicio modalitate de a o testa.) Pentru a crea o
bibliotecă de clase nouă într-o soluție web existentă, începeți prin a deschide site-ul web, apoi alegeți Adăugare proiect nou.
Specificați directorul și numele proiectului în caseta de dialog Adăugare proiect nou.
Fișier
→ Figura 22-3 prezintă o soluție atât cu un site web, cât și cu o bibliotecă de clase numită
Componente. Site-ul web este scris cu caractere aldine în Exploratorul de soluții pentru a indica faptul
că se execută la pornire (când faceți clic pe butonul Start).
739
CAPITOLUL PROGRAMAREA BAZATĂ PE COMPONENTE
22
Pentru a facilita deschiderea acestei soluții, poate doriți să luați un moment pentru a o salva. Faceți clic pe numele soluției (care este
"Componente" în Figura 22-3) în Exploratorul de soluții. Apoi alegeți Salvare fișier
→ [SolutionName] ca. Puteți deschide acest fișier .sln mai târziu pentru a încărca atât site-ul web, cât și
proiectul bibliotecii de clasă. Puteți compila biblioteca de clase în orice moment, făcând clic dreapta pe
proiect în Exploratorul de soluții și alegând Construire. Acest lucru creează un fișier de asamblare DLL
(Components.dll). Nu puteți rula acest fișier direct, deoarece nu este o aplicație și nu oferă nicio interfață cu
utilizatorul.
Amintiți-vă, o componentă poate conține mai multe clase. Aveți posibilitatea să creați aceste alte clase în
același fișier sau să utilizați fișiere separate pentru o mai bună organizare. În ambele cazuri, toate clasele și
fișierele codului sursă sunt compilate împreună într-un singur ansamblu:
clasa publică SimpleTest { //
Codul clasei omis. } clasa
publică SimpleTest2 { // Codul
740
CAPITOLUL PROGRAMAREA BAZATĂ PE COMPONENTE
22
clasei omis. }
De obicei, clasele sunt plasate în interiorul unui bloc de spațiu de nume. Asta înseamnă că codul tău
va arăta astfel:
Când adăugați un nou fișier de clasă la o bibliotecă de clasă, C# adaugă automat un bloc de spațiu de nume
utilizând spațiul de nume implicit pentru proiectul dvs. Clasele din componenta dvs. sunt organizate automat
într-un spațiu de nume denumit după proiectul dvs. De exemplu, dacă ați creat un proiect numit Componente,
clasele SimpleTest și SimpleTest2 vor fi în spațiul de nume Componente (afișat aici), iar numele lor complet
calificate vor fi Components.SimpleTest și Components.SimpleTest2. Trebuie să cunoașteți numele complet
calificat pentru a vă utiliza clasele într-o altă aplicație, deoarece alte aplicații nu vor partaja același spațiu de nume.
Dacă nu vă place spațiul de nume implicit, puteți edita numele spațiului de nume în toate fișierele de cod. Cu toate
acestea, dacă abia începeți într-un proiect nou, există o modalitate mai ușoară - puteți solicita pur și simplu Visual Studio
să modifice spațiul de nume implicit pentru proiectul dvs. În acest fel, ori de câte ori adăugați un nou fișier de clasă la
proiect, Visual Studio va insera un bloc de spațiu de nume care utilizează numele spațiului de nume dorit. (Spațiul de
nume al fișierelor existente nu se modifică.) Pentru a modifica spațiul de nume implicit, începeți prin a face clic dreapta pe
proiect în Exploratorul de soluții și a alege Proprietăți. Veți vedea un afișaj cu mai multe file al setărilor aplicației. Alegeți
fila Aplicație și apoi editați spațiul de nume în caseta text Spațiu de nume implicit. De asemenea, puteți utiliza caseta text
Nume ansamblu din această fereastră pentru a configura numele dat fișierului de asamblare compilat.
Dacă aveți o componentă complexă, puteți alege să o subdivizați în spații de nume imbricate. De exemplu,
este posibil să aveți un spațiu de nume numit Components.Database și altul numit Components.Validation.
Pentru a crea un spațiu de nume imbricat în spațiul de nume implicit al proiectului, utilizați un bloc de spațiu
de nume ca acesta:
namespace Componente { namespace Database { public class SimpleDatabaseTest { // (Codul clasei a fost omis.) } } }
741
CAPITOLUL PROGRAMAREA BAZATĂ PE COMPONENTE
22
Sfat
Regula generală pentru denumirea spațiilor de nume este de a utiliza numele companiei urmat de numele tehnologiei și, opțional, urmat de o diviziune specifică caracteristicii, ca în CompanyName.TechnologyName.Feature. Exemple de spații de nume care urmează după această sintaxă includ Microsoft.Media și Microsoft.Media.Audio. Aceste convenții de spațiu de nume reduc dramatic posibilitatea ca mai multe companii să lanseze componente în aceleași spații de nume, ceea ce ar duce la conflicte de denumire. Singura excepție de la instrucțiunile de denumire este în
ansamblurile de bază care fac parte din .NET. Ei folosesc spații de nume care încep cu Sistem.
Membrii clasei
Pentru a adăuga funcționalitate clasei, adăugați metode sau proprietăți publice. Codul paginii web poate apela
apoi acești membri pentru a prelua informații sau pentru a efectua o sarcină.
Următorul exemplu arată una dintre cele mai simple componente posibile, care nu face altceva decât să
returneze un șir la codul de apelare:
clasa publică SimpleTest { șir public GetInfo(string param) { return "Ați invocat SimpleTest.GetInfo() cu '" + param + "'";
}}
În secțiunile următoare, veți afla cum să utilizați această componentă într-o aplicație web. Puțin mai târziu,
veți trece la o componentă mai complexă și practică.
742
CAPITOLUL PROGRAMAREA BAZATĂ PE COMPONENTE
22
Adăugare referință web sau Adăugare referință serviciu, care sunt utilizate pentru a conecta o aplicație la un
serviciu web și au puține în comun cu comanda denumită în mod similar Adăugare referință.)
Puteți lua una dintre cele două abordări în caseta de dialog Adăugare referință:
Adăugați o referință de proiect: Dacă proiectul bibliotecii de clase se află în aceeași soluție, utilizați fila
Proiecte. Aceasta vă arată o listă cu toate proiectele bibliotecii de clasă din soluția curentă (a se vedea
Figura 22-4). Selectați biblioteca clasei și faceți clic pe OK.
Adăugați o referință de asamblare: Dacă biblioteca dvs. de clasă se află într-o altă soluție sau aveți
doar fișierul DLL compilat (poate componenta a fost creată de un alt dezvoltator), utilizați fila Răsfoire
(consultați Figura 22-5). Navigați prin directoare până găsiți fișierul DLL, selectați-l și faceți clic pe OK.
• Notă
Construiți
Dacă utilizați
soluțiaodin
referință
meniulde
Visual
asamblare,
Studio)trebuie
înainte mai
de aîntâi
puteasă adăuga
compilați
referința.
componenta (alegeți Compilare
743
CAPITOLUL PROGRAMAREA BAZATĂ PE COMPONENTE
22
În orice caz, .NET copiază fișierul DLL compilat în subdirectorul Bin al aplicației web (a se vedea Figura 22-6).
Veți vedea, de asemenea, un fișier . PDB fișier care conține informații de depanare pentru Visual Studio.
744
CAPITOLUL PROGRAMAREA BAZATĂ PE COMPONENTE
22
Visual Studio are, de asemenea, grijă deosebită pentru a vă asigura că utilizați în continuare cea mai recentă
versiune a componentei. Dacă modificați componenta și o recompilați, Visual Studio va observa modificarea. Data
viitoare când executați aplicația web, Visual Studio va copia automat componenta nouă în subdirectorul Bin.
Dacă utilizați o referință de proiect, Visual Studio face un pas mai departe. De fiecare dată când executați proiectul
site-ului web, Visual Studio verifică dacă există modificări în fișierele de cod sursă ale componentei. Dacă oricare
dintre aceste fișiere a fost modificat, Visual Studio recompilează automat componenta și copiază noua versiune în
subdirectorul Bin din aplicația web.
Când adăugați o referință la o componentă, Visual Studio vă permite, de asemenea, să utilizați clasele sale în
codul dvs. cu verificarea sintaxei obișnuite și IntelliSense. Dacă nu adăugați referința, nu veți putea utiliza
clasele componente (și dacă încercați, Visual Studio interpretează încercările dvs. de a utiliza clasa ca greșeli
și refuză să compileze codul).
Notă Eliminarea unei referințe este puțin mai dificilă. Cel mai simplu mod este să faceți clic dreapta pe proiectul web și să alegeți Pagini de proprietate. Apoi, alegeți Referințe din listă. Veți vedea o listă cu toate referințele dvs. (inclusiv referințele de asamblare și proiect). Pentru a elimina unul, selectați-l și faceți clic pe Eliminare.
Utilizarea componentei
După ce ați adăugat referința, puteți utiliza componenta creând instanțe ale clasei SimpleTest sau
SimpleTest2, așa cum se arată aici:
folosind Componente;
Rezultatul pentru această pagină, prezentat în Figura 22-7, combină valoarea returnată din
ambele metode GetInfo().
745
CAPITOLUL PROGRAMAREA BAZATĂ PE COMPONENTE
22
Pentru a simplifica puțin acest cod, puteți alege să utilizați metode statice în clasa de componente, astfel
încât să nu fie nevoie să creați o instanță înainte de a utiliza metodele. O metodă statică GetInfo() arată
astfel:
clasa publică SimpleTest { public șir static GetInfo(string param) { return "Ați invocat SimpleTest.GetInfo() cu '" + param + "'"; } }
În acest caz, pagina web accesează metoda statică GetInfo() prin numele clasei și nu trebuie să creeze un
obiect:
Sfat că, dacă utilizați referințe de asamblare și componenta și aplicația web nu se află în aceeași soluție, nu veți
• Rețineți
vedea imediat efectul modificărilor. În schimb, trebuie să recompilați ansamblul componentelor (alegeți Construire
→ Build Solution) și apoi reconstruiți aplicația web. Dacă utilizați referințe de proiect, acest
lucru nu este necesar - Visual Studio observă fiecare modificare efectuată și recompilează
automat componenta.
746
CAPITOLUL PROGRAMAREA BAZATĂ PE COMPONENTE
22
Decizia când să utilizați metode de instanță și când să utilizați metode statice face parte din arta designului orientat pe
obiecte și necesită experiență. Metodele statice impun considerații suplimentare - și anume, clasa dvs. trebuie să fie
apatridă (un concept descris în secțiunea următoare), ceea ce înseamnă că nu poate reține informații suplimentare în
variabilele membre. Dacă da, riscă un potențial conflict dacă mai multe bucăți de cod utilizează componenta în același
timp.
Ca regulă generală, utilizați metode de instanță dacă trebuie să puteți crea mai multe instanțe ale clasei dvs. în același
timp. De exemplu, metodele de instanță au sens pentru clasa SqlConnection, deoarece puteți alege să deschideți o
conexiune la mai multe baze de date diferite pentru o singură operație. Metodele de instanță sunt, de asemenea, cea mai
bună alegere dacă doriți să configurați un obiect o dată și să îl utilizați de mai multe ori. De exemplu, clasa SqlConnection
vă permite să setați șirul de conexiune și apoi să deschideți și să închideți conexiunea cât de mult este necesar. Pe de
altă parte, luați în considerare metodele statice dacă metodele dvs. efectuează o singură sarcină discretă care nu
necesită nicio inițializare. Exemplele includ calculele din clasa de matematică și activitățile de afaceri (cum ar fi
înregistrarea unui client nou) într-o componentă de afaceri la nivel înalt.
Proprietăți și stare
Clasele SimpleTest oferă funcționalitate prin metode publice. Dacă sunteți familiarizat cu programarea bazată pe clase
(așa cum este descris în capitolul 3), vă veți aminti că clasele pot, de asemenea, să stocheze informații în variabile private
pentru membri și să ofere proceduri de proprietate care permit codului de apelare să modifice aceste informații. De
exemplu, o clasă Persoană poate avea o proprietate Prenume.
Când utilizați proprietăți și stocați informații în variabilele membrilor, utilizați designul stării. În proiectarea
statală, clasa are responsabilitatea de a menține anumite informații. În proiectarea apatridă, nu se păstrează
informații între apelurile metodei. Comparați clasa anterioară SimpleTest, care utilizează designul apatrid, cu
clasa statală SimpleTest prezentată aici:
clasa publică SimpleTest
{
date șir private; șir
public de date { get {
return data; } set { data
= value; } }
Programatorii care proiectează aplicații la scară largă (cum ar fi aplicațiile web) dezbat uneori dacă
programarea statală sau apatridă este cea mai bună. Programarea statală este abordarea cea mai naturală,
orientată spre obiecte, dar are și câteva dezavantaje. Pentru a efectua o activitate obișnuită, poate fi
necesar să setați mai multe proprietăți înainte de a apela o metodă. Fiecare dintre acești pași adaugă un pic
de cheltuieli inutile. Un design apatrid, pe de altă parte, își desfășoară adesea toată activitatea într-un singur
apel de metodă. Cu toate acestea, deoarece nicio informație nu este păstrată în stare, poate fi necesar să
specificați mai mulți parametri, ceea ce poate face programarea obositoare. Un bun exemplu de obiecte
statale versus apatride este prezentat de clasele FileInfo și File, care sunt descrise în capitolul 17.
747
CAPITOLUL PROGRAMAREA BAZATĂ PE COMPONENTE
22
Nu există un răspuns scurt cu privire la faptul dacă designul statal sau apatrid este cel mai bun și depinde adesea
de sarcina la îndemână. Componentele care sunt de înaltă performanță, componentele care utilizează tranzacții,
componentele care utilizează resurse limitate, cum ar fi o conexiune la baza de date sau componentele care
trebuie invocate de la distanță (cum ar fi serviciile web) utilizează de obicei designul fără stare, care este cea mai
simplă și mai fiabilă abordare. Deoarece nicio informație nu este păstrată în memorie, sunt utilizate mai puține
resurse de server și nu există pericolul de a pierde date valoroase dacă apare o defecțiune software sau
hardware. Următorul exemplu ilustrează diferența cu două moduri de a proiecta o clasă de cont.
Dacă aveți două obiecte CustomerAccount care expun o proprietate Sold, trebuie să efectuați doi pași
separați pentru a transfera bani dintr-un cont în altul. Conceptual, procesul funcționează astfel:
748
CAPITOLUL PROGRAMAREA BAZATĂ PE COMPONENTE
22
Problema aici este că, dacă această sarcină este întreruptă la jumătatea drumului de o eroare, veți ajunge la
cel puțin un client nemulțumit.
}
Codul de apelare nu poate utiliza aceleași obiecte elegante CustomerAccount, dar poate fi sigur că transferurile
de cont sunt protejate împotriva erorilor. Deoarece toate operațiunile bazei de date sunt efectuate simultan,
acestea pot utiliza o procedură stocată în baza de date pentru o performanță mai mare și pot utiliza o tranzacție
pentru a se asigura că retragerea și depunerea fie reușesc, fie eșuează în ansamblu.
AccountUtility.FundTransfer(accountIDne, accountIDTwo,
suma);
Într-un sistem critic pentru misiune, tranzacțiile sunt adesea necesare. Din acest motiv, clasele care
păstrează puține informații de stare sunt adesea cea mai bună abordare de proiectare, chiar dacă nu sunt la
fel de satisfăcătoare dintr-o perspectivă orientată pe obiecte.
Sfat: Există un potențial compromis. Puteți crea clase statale pentru a reprezenta elemente comune, cum ar fi conturi, clienți și așa mai departe, fără a adăuga nicio funcționalitate. Apoi, puteți utiliza aceste clase ca pachete de date pentru a trimite informații către și de la o clasă de utilitate fără stat. (Acestea sunt obiectele de date care au fost descrise la începutul acestui capitol.)
749
CAPITOLUL PROGRAMAREA BAZATĂ PE COMPONENTE
22
În acest exemplu, vă conectați la o bază de date SQL Server utilizând ADO.NET. Puteți crea singur această bază de
date sau puteți consulta eșantioanele online, care includ un script SQL care o generează automat. Pentru început,
tabelul Categorii este preîncărcat cu un set standard de categorii permise. Componenta de acces la date este simplă.
Este o singură clasă (numită DBUtil), care este plasată într-un spațiu de nume numit DatabaseComponent (care este
spațiul de nume implicit pentru proiect). Clasa DBUtil utilizează metode de instanță și păstrează unele informații de
bază (cum ar fi șirul de conexiune de utilizat), dar nu permite clientului să modifice aceste informații. Prin urmare, nu
are nevoie de proceduri de proprietate. În schimb, își desfășoară cea mai mare parte a activității în metode precum
750
CAPITOLUL PROGRAMAREA BAZATĂ PE COMPONENTE
22
GetCategories() și GetItems(). Aceste metode returnează seturile de date cu înregistrările corespunzătoare ale bazei de
date. Acest tip de design creează un strat destul de subțire peste baza de date - gestionează unele detalii, dar clientul
este încă responsabil pentru lucrul cu clase de ADO.NET familiare, cum ar fi DataSet.
Notă Pentru a utiliza acest exemplu așa cum este scris, trebuie să adăugați o referință la ansamblurile System.Configuration.dll și System.Web.dll din biblioteca clasei. În caz contrar, nu puteți utiliza WebConfigurationManager pentru a dezgropa șirul de conexiune de care aveți nevoie. Pentru a adăuga aceste referințe, selectați Proiect
•
→ Adăugați referință și căutați în fila .NET.
set public de date GetCategories() { string query = "SELECT * FROM Categories"; SqlCommand cmd = noul
SqlCommand(interogare); } returnați FillDataSet (cmd, "Categorii");
set de date publice GetItems() { string query = "SELECT * FROM Items"; SqlCommand cmd = noul
SqlCommand(interogare); } returnați FillDataSet (cmd, "Items");
751
CAPITOLUL PROGRAMAREA BAZATĂ PE COMPONENTE
22
} // Creați comanda.
string insertSQL = "INSERAȚI ÎN Categorii "; insertSQL
+= "(Nume) VALORI @Name"; SqlCommand cmd =
nou SqlCommand(insertSQL, con);
cmd.Parameters.AddWithValue("@Name", nume);
Încercați { con. Deschis();
cmd.ExecuteNonQuery(); } în
cele din urmă { con.
Aproape(); }
} // Creați comanda.
string insertSQL = "INSERAȚI ÎN ELEMENTE "; insertSQL += "(titlu,
descriere, preț, Category_ID)"; insertSQL += "VALORI (@Title,
@Description, @Price, @CategoryID)"; SqlCommand cmd = nou
SqlCommand(insertSQL, con); cmd.Parameters.AddWithValue("@Title",
titlu); cmd.Parameters.AddWithValue("@Description", descriere);
cmd.Parameters.AddWithValue("@Price", preț);
cmd.Parameters.AddWithValue("@CategoryID", categoryID);
752
CAPITOLUL PROGRAMAREA BAZATĂ PE COMPONENTE
22
}
}
}
Disecarea codului . . .
• Când este creat un obiect DBUtil, constructorul preia automat șirul de conexiune din
fișierul web.config, folosind tehnica descrisă în capitolul 5. Cu toate acestea, este
important să rețineți că acesta este fișierul web.config al aplicației web (deoarece
componenta nu are un fișier de configurare). Acesta este un design bun, deoarece
permite unui site web să utilizeze componenta bazei de date cu orice server de baze de
date. Cu toate acestea, dacă aplicația web client nu are setarea de configurare
corespunzătoare, componenta bazei de date nu va funcționa.
• Codul include metode de preluare a datelor (acele metode care încep cu Get) și
metode de actualizare a datelor (acele metode care încep cu Add).
• Această clasă include o metodă supraîncărcată numită GetItems(). Aceasta
înseamnă că clientul poate apela GetItems() fără parametri pentru a returna lista
completă sau cu un parametru care indică categoria corespunzătoare. (Capitolul
2 oferă o introducere în metodele supraîncărcate.)
• Fiecare metodă care accesează baza de date deschide și închide conexiunea.
Aceasta este o abordare mult mai bună decât încercarea de a menține o conexiune
deschisă pe toată durata de viață a clasei, ceea ce va duce cu siguranță la degradarea
performanței în scenarii multiutilizator.
Sfat
Serverul dvs. web poate deschide și închide conexiunile frecvent fără a provoca nicio încetinire. Acest lucru se datorează faptului că ADO.NET utilizează gruparea conexiunilor pentru a menține un set mic de conexiuni deschise gata de utilizare. Atâta timp cât nu modificați șirul de conexiune și atâta timp cât există conexiuni disponibile în pool, atunci când apelați SqlConnection.Open(), primiți una dintre aceste conexiuni, evitând astfel cheltuielile generale de
• Codul utilizează propria funcție privată FillDataSet() pentru a face codul mai
concis. Acest lucru nu este pus la dispoziția clienților. În schimb, metodele
GetItems() și GetCategories() utilizează funcția FillDataSet().
753
CAPITOLUL PROGRAMAREA BAZATĂ PE COMPONENTE
22
<configurare>
</configurare>
Apoi, compilați și copiați fișierul DLL component sau adăugați o referință la acesta dacă utilizați Visual Studio.
Singura sarcină rămasă este să adăugați interfața cu utilizatorul pentru pagina web care utilizează
componenta. Pentru a testa această componentă, puteți crea o pagină de test simplă. În exemplul prezentat
în Figura 22-9, această pagină permite utilizatorilor să răsfoiască lista curentă pe categorii și să adauge
elemente noi. Când utilizatorul vizitează prima dată pagina, îi solicită utilizatorului să selecteze o categorie.
Odată ce o categorie este aleasă, se afișează elementele corespunzătoare și apare un panou de comenzi,
care permite utilizatorului să adauge o nouă intrare în AdBoard în categoria curentă, așa cum se arată în
Figura 22-10.
754
CAPITOLUL PROGRAMAREA BAZATĂ PE COMPONENTE
22
Pentru a accesa componenta mai ușor, pagina web importă spațiul său de nume:
folosind DatabaseComponent;
Codul paginii creează componenta pentru a prelua informații din baza de date și o afișează legând setul de
date la lista verticală sau la controlul GridView:
755
CAPITOLUL PROGRAMAREA BAZATĂ PE COMPONENTE
22
Disecarea codului . . .
• Nu toate funcționalitățile componentei sunt utilizate în această pagină. De
exemplu, pagina nu utilizează metoda AddCategory() sau versiunea de GetItems()
care nu necesită un număr de categorie. Acest lucru este complet normal. Alte
pagini pot utiliza caracteristici diferite de componentă.
• Codul paginii web nu conține cod de acces la date. Cu toate acestea, trebuie să
înțeleagă cum să utilizeze un set de date și trebuie să cunoască nume specifice de
câmpuri pentru a crea un GridView mai atractiv, cu șabloane personalizate pentru
aspect (în loc de coloane generate automat).
• Pagina poate fi îmbunătățită cu ajutorul codului de tratare a erorilor sau al
controalelor de validare. În prezent, nu se efectuează nicio validare pentru a se
asigura că prețul este numeric sau chiar pentru a se asigura că valorile necesare sunt
furnizate.
756
CAPITOLUL PROGRAMAREA BAZATĂ PE COMPONENTE
22
Sfat
codificați direct în codul componentei, chiar dacă nu face parte din aceeași soluție. Fișierul de cod sursă corespunzător este încărcat automat în editor, atâta timp cât este disponibil (și ați compilat componenta în modul de depanare).
Dacă depanați codul în Visual Studio, veți descoperi că puteți face un singur pas din pagina web
•
Acest cod lansează un ApplicationException cu un mesaj de eroare particularizat care indică problema.
Pentru a oferi o raportare și mai bună, puteți crea propria clasă de excepții care moștenește de la
ApplicationException, așa cum este descris în capitolul 7.
757
CAPITOLUL PROGRAMAREA BAZATĂ PE COMPONENTE
22
Sfat: Componentele captează adesea excepțiile care apar în timpul activităților de nivel scăzut (cum ar fi citirea unui fișier sau interacțiunea cu o bază de date) și apoi aruncă excepții mai puțin detaliate, cum ar fi ApplicationException, pentru a notifica pagina web. În acest fel, nu există nicio șansă ca utilizatorul să vadă informațiile despre eroarea tehnică. Acest lucru este important, deoarece mesajele de eroare detaliate pot oferi hackerilor indicii despre modul în care funcționează codul
media de returnare;
}
758
CAPITOLUL PROGRAMAREA BAZATĂ PE COMPONENTE
22
Aceste interogări utilizează unele SQL care pot fi noi pentru dvs. (și anume, funcțiile agregate COUNT și
AVG). Cu toate acestea, aceste metode sunt la fel de ușor de utilizat din perspectiva clientului ca
GetItems() și GetCategories():
DBUtil DB = new DBUtil(); medie zecimalăPreț =
DB. GetAveragePrice(); int totalItems = DB.
GetTotalItems();
Este posibil să vă fi gândit că puteți returna informații, cum ar fi numărul total de elemente, printr-o procedură
de proprietate doar în citire (cum ar fi TotalItems) în loc de o metodă (în acest caz, GetTotalItems). Deși acest
lucru funcționează, procedurile de proprietate sunt mai bine lăsate la informații care sunt menținute cu clasa
(într-o variabilă privată) sau sunt ușor de reconstruit. În acest caz, este nevoie de o operațiune de bază de
date pentru a număra numărul de rânduri, iar această operațiune a bazei de date poate provoca o problemă
neobișnuită sau poate încetini performanța dacă este utilizată frecvent. Pentru a ajuta la întărirea acestui fapt,
se folosește o metodă în locul unei proprietăți.
The ObjectDataSource
Utilizarea unei componente dedicate bazei de date este o modalitate excelentă de a vă menține codul eficient și bine
organizat. De asemenea, vă este ușor să aplicați modificările mai târziu. Cu toate acestea, acest lucru are un dezavantaj - și
anume, trebuie să scrieți destul de puțin cod pentru a crea o pagină web și o componentă separată de acces la date. În
capitolul 15, ați văzut că vă puteți simplifica viața folosind componente precum SqlDataSource pentru a încapsula toate
detaliile de acces la date. Din păcate, această abordare fără cod nu va funcționa dacă utilizați o componentă separată - sau o
va face?
Se pare că există o modalitate de a obține cele mai bune din ambele lumi și de a utiliza o componentă
separată de acces la date și o legare mai ușoară a datelor paginilor web. În loc să utilizați SqlDataSource,
utilizați ObjectDataSource, care definește o legătură între pagina web și componenta. Acest lucru nu vă va
salva de la scrierea codului real de acces la date în componenta dvs., dar vă va salva de la scrierea codului
obositor în pagina dvs. web pentru a apela metodele din componenta dvs., a extrage datele, a le formata și a
le afișa în pagină.
Notă ObjectDataSource vă permite să creați pagini web fără cod, dar tot trebuie să scrieți codul în componentă. Nu ar trebui să vedeți acest lucru ca pe un dezavantaj - la urma urmei, trebuie să scrieți acest cod pentru a obține un control fin asupra a ceea ce se întâmplă și, prin urmare, pentru a optimiza performanța strategiei dvs. de acces la date.
În secțiunile următoare, veți învăța cum să luați clasa DBUtil existentă prezentată mai devreme și să o utilizați
într-o pagină web legată de date. Veți învăța cum să reproduceți exemplul prezentat în Figura 22-9 și Figura
22-10 fără a scrie niciun cod de pagină web.
759
CAPITOLUL PROGRAMAREA BAZATĂ PE COMPONENTE
22
• Clasa dumneavoastră trebuie să fie apatridă. Acest lucru se datorează faptului că ObjectDataSource
va crea o instanță numai atunci când este necesar și o va distruge la sfârșitul fiecărei solicitări.
Selectarea înregistrărilor
Puteți afla multe despre ObjectDataSource construind pagina prezentată în Figura 22-10. În secțiunile
următoare, veți aborda această provocare.
Primul pas este să creați caseta listă cu lista categoriilor. Pentru această listă, aveți nevoie de un ObjectDataSource
care se leagă la clasa DBUtil și apelează metoda GetCategories() pentru a regăsi lista completă de înregistrări de
categorii.
Pentru a furniza date în caseta listă, trebuie să definiți un ObjectDataSource și să indicați numele clasei care
conține metodele de acces la date. Faceți acest lucru specificând numele clasei complet calificat cu
proprietatea TypeName, așa cum se arată aici:
<asp:ObjectDataSource ID="sourceCategories" runat="server" ... />
TypeName = "DatabaseComponent.DBUto"
După ce ați atașat ObjectDataSource la o clasă, următorul pas este să o indicați către metodele pe care le poate
utiliza pentru a selecta și actualiza înregistrări.
ObjectDataSource definește proprietățile SelectMethod, DeleteMethod, UpdateMethod și InsertMethod pe
care le utilizați pentru a lega clasa de acces la date la diverse activități. Fiecare proprietate ia numele
metodei în clasa de acces la date. În acest exemplu, trebuie pur și simplu să activați interogarea, deci trebuie
să setați proprietatea SelectMethod, astfel încât să apeleze metoda GetCategories():
<asp:ObjectDataSource ID="sourceCategories" runat="server" TypeName="DatabaseComponent.DBUtil"
SelectMethod="GetCategories" />
După ce ați configurat ObjectDataSource, puteți lega controalele paginii web în același mod în care faceți
cu SqlDataSource. Iată eticheta de care aveți nevoie pentru caseta listă:
760
CAPITOLUL PROGRAMAREA BAZATĂ PE COMPONENTE
22
Din nou, utilizați clasa DBUtil, dar de data aceasta este metoda GetItems() de care aveți nevoie. Chiar dacă există două
versiuni supraîncărcate ale metodei GetItems() (una care ia un parametru categoryID și una care nu), nu trebuie să vă
faceți griji. ObjectDataSource utilizează automat supraîncărcarea corectă uitându-se la parametrii pe care i-ați definit.
În acest caz, utilizați un singur parametru care extrage ID-ul categoriei selectate din caseta listă și îl
transmite la metoda GetItems(). Observați că numele definit în eticheta ControlParameter se potrivește cu
numele parametrului metodei GetItems(). Aceasta este o cerință absolută. ObjectDataSource caută metoda
GetItems() utilizând reflexia și verifică dacă orice potrivire potențială are numărul de parametri, nume de
parametri și tipuri de date pe care le-ați indicat. Acest proces de căutare permite ObjectDataSource să
distingă între diferite versiuni supraîncărcate ale aceleiași metode. Dacă ObjectDataSource nu poate găsi
metoda specificată, se ridică o excepție în acest moment.
Sfat Dacă aveți vreodată îndoieli cu privire la metoda apelată în componenta de acces la date, plasați un punct de întrerupere pe metodele posibile și utilizați caracteristicile de depanare ale Visual Studio (așa cum este descris în capitolul 4).
Ultimul pas este să conectați GridView la noul ObjectDataSource utilizând DataSourceID. Iată eticheta care
o face:
<asp:GridView ID="gridItems" runat="server" DataSourceID="sourceItems"/>
Aceasta este tot ce aveți nevoie. Ar trebui să păstrați butonul Afișare, deoarece declanșează o postback a
paginii și permite ObjectDataSource să înceapă să funcționeze. (Dacă nu doriți să utilizați acest buton,
setați proprietatea AutoPostback din caseta listă la Adevărat, astfel încât să se afișeze înapoi ori de câte ori
modificați selecția.) Nu trebuie să scrieți niciun cod de gestionare a evenimentelor pentru a reacționa atunci
când se face clic pe buton. Interogările sunt executate automat, iar controalele sunt legate automat.
761
CAPITOLUL PROGRAMAREA BAZATĂ PE COMPONENTE
22
Actualizarea înregistrărilor
Ultimul pas este de a oferi utilizatorului o modalitate de a adăuga elemente noi. Cel mai simplu mod de a face acest
lucru posibil este să utilizați un control de date îmbogățit care se ocupă de înregistrări individuale, fie DetailsView, fie
FormsView. DetailsView este cel mai simplu dintre cele două, deoarece nu necesită un șablon. Este cel folosit în
exemplul următor.
În mod ideal, ați defini DetailsView folosind o etichetă ca aceasta și lăsați-o să genereze toate câmpurile
de care are nevoie pe baza sursei de date legate:
<asp:DetailsView ID="detailsAddItem" runat="server" DataSourceID="sourceItems"/>
Din păcate, acest lucru nu va funcționa în acest exemplu. Problema este că această abordare creează prea
multe domenii. În acest exemplu, nu doriți ca utilizatorul să specifice ID-ul elementului (setat automat de baza
de date) sau ID-ul categoriei (care se bazează pe categoria selectată în prezent). Deci, niciunul dintre aceste
detalii nu ar trebui să apară. Singura modalitate de a vă asigura că acest lucru este cazul este să dezactivați
generarea automată a câmpului și să definiți în mod explicit fiecare câmp dorit, așa cum se arată aici:
<asp:DetailsView ID="detailsAddItem" runat="server"
DataSourceID="sourceItems" AutoGenerateRows="False"> <Fields>
<asp:BoundField DataField="Title" HeaderText="Title" /> <asp:BoundField
DataField="Price" HeaderText="Price"/> <asp:BoundField DataField="Description"
HeaderText="Description" /> </Fields> </asp:DetailsView>
Trebuie să faceți alte câteva modificări. Pentru a permite inserarea, trebuie să setați
AutoGenerateInsertButton la True. În acest fel, DetailsView creează linkurile care vă permit să începeți să
introduceți o înregistrare nouă și apoi să o inserați. În același timp, puteți seta proprietatea DefaultMode la
Insert. În acest fel, DetailsView este întotdeauna în modul inserare și este utilizat exclusiv pentru adăugarea
de înregistrări (nu afișarea acestora), la fel ca pagina non-legată de date prezentată anterior.
<asp:DetailsView ID="detailsAddItem" runat="server"
DefaultMode = "Inser" AutoGenerateInsertButton = "True"
DataSourceID = "sourceItems" AutoGenerateRows = "False"> ...
</asp:DetailsView>
ObjectDataSource oferă același tip de suport pentru legarea datelor actualizabile ca SqlDataSource.
Primul pas este să specificați InsertMethod, care trebuie să fie o metodă publică din aceeași clasă:
InsertMethod="AddItem" >
</asp:ObjectDataSource>
762
CAPITOLUL PROGRAMAREA BAZATĂ PE COMPONENTE
22
Cu toate acestea, acest lucru are încă o problemă. Când utilizatorul efectuează o editare, DetailsView trimite cei trei
parametri pe care îi așteptați (Titlu, Preț și Descriere). Cu toate acestea, metoda AddItem() are nevoie de un al
patrulea parametru—CategoryID. Am lăsat parametrul respectiv în afara câmpurilor DetailsView, deoarece nu
doriți ca utilizatorul să poată seta ID-ul categoriei. Cu toate acestea, trebuie să o furnizați metodei. Deci, de unde
puteți obține ID-ul categoriei actuale? Cea mai ușoară alegere este să o extrageți din caseta listă, la fel cum ați
făcut pentru metoda GetItems(). Tot ce trebuie să faceți este să adăugați o etichetă ControlParameter care
definește un parametru numit CategoryID și îl leagă de proprietatea SelectedValue a casetei listă. Iată eticheta
revizuită pentru ObjectDataSource:
<asp:ObjectDataSource ID="sourceItems" runat="server" SelectMethod="GetItems"
TypeName="DatabaseComponent.DBUtil" InsertMethod="AddItem" >
<SelectParameters> ...
</SelectParameters>
<InsertParameters> <asp:ControlParameter ControlID="lstCategories" name="categoryID"
PropertyName="SelectedValue" type="Int32" /> </InsertParameters>
</asp:ObjectDataSource>
Acum aveți toți parametrii de care aveți nevoie - cei trei din DetailsView și cel suplimentar din caseta listă. Când
utilizatorul încearcă să insereze o înregistrare nouă, ObjectDataSource colectează acești patru parametri, se
asigură că se potrivesc cu semnătura pentru metoda AddItem(), le pune în ordine și apoi apelează metoda.
763
CAPITOLUL PROGRAMAREA BAZATĂ PE COMPONENTE
22
Când faceți clic pe butonul Inserare, se întâmplă destul de mult în culise. Iată o defalcare a ceea ce se
întâmplă de fapt:
1. DetailsView adună toate valorile noi și le transmite către
ObjectDataSource.
2. ObjectDataSource apelează metoda DBUtil.AddItem(), trecând toate valorile
primite de la DetailsView în pozițiile corecte (prin potrivirea numelor câmpurilor cu
numele parametrilor) și valoarea selectată din caseta listă lstCategories.
3.
Metoda DBUtil.AddItem() construiește o comandă SQL parametrizată. Apoi
deschide o conexiune la baza de date și execută comanda pentru a insera noua
înregistrare. (În acest moment, sistemul ASP.NET de legare a datelor ia o pauză
și permite apariția altor evenimente, cum ar fi Page.Load.)
4. Chiar înainte ca pagina să fie redată, începe procesul de legare a datelor. Lista
verticală solicită primul ObjectDataSource pentru lista de categorii (care
declanșează un apel la metoda DBUtil.GetCategories(), iar GridView solicită lista
de elemente din al doilea ObjectDataSource (care declanșează metoda
DBUtil.GetItems().
Deoarece pagina este întotdeauna refăcută după terminarea tuturor operațiunilor de inserare și
actualizare, veți vedea întotdeauna cele mai recente informații în controalele web. De exemplu, dacă
adăugați un element nou, îl veți vedea apărând imediat, completat cu valoarea ID unic pe care serverul
bazei de date o generează automat.
Notă În unele cazuri, poate fi necesar să furnizați un parametru suplimentar care trebuie setat programatic. În acest caz, trebuie să definiți o etichetă de parametru simplă (în loc de o etichetă ControlParameter ), cu un nume și un tip de date, dar fără valoare. Apoi, puteți răspunde la evenimentul ObjectDataSource corespunzător (cum ar fi Inserarea, actualizarea sau ștergerea) pentru a completa valoarea de care aveți nevoie la timp. Este puțin dezordonat (și te obligă să scrii cod în pagina ta web), dar uneori este o
Ultimul cuvânt
Exemplele din acest capitol demonstrează modalități sigure și solide de a crea componente și de a le integra
în site-ul dvs. După cum puteți vedea, aceste obiecte respectă regulile de încapsulare, ceea ce înseamnă că
fac o sarcină specifică de afaceri, dar nu se implică în generarea interfeței cu utilizatorul pentru aplicație. De
exemplu, clasa DBUtil utilizează ADO.NET cod pentru a regăsi înregistrări sau pentru a actualiza o bază de
date. Depinde de alte controale, cum ar fi GridView și DetailsView, pentru a furniza prezentarea.
764
C A P I T O L U L 23
•■■
Cache
ASP.NET aplicații sunt un pic contradictorii. Pe de o parte, deoarece sunt găzduite pe Internet, au cerințe
unice - și anume, trebuie să poată servi sute de clienți la fel de ușor și rapid ca și cum ar avea de-a face cu
un singur utilizator. Pe de altă parte, ASP.NET include câteva trucuri remarcabile care vă permit să proiectați
și să codificați o aplicație web în același mod în care programați o aplicație desktop. Aceste trucuri sunt utile,
dar pot duce dezvoltatorii la probleme. Problema este că ASP.NET face ușor să uitați că creați o aplicație
web - atât de ușor, încât ați putea introduce practici de programare care vă vor încetini sau paraliza aplicația
atunci când este utilizată de un număr mare de utilizatori din lumea reală.
Din fericire, există o cale de mijloc. Puteți utiliza funcțiile incredibile de economisire a timpului, cum ar fi starea de
vizualizare, controalele web și starea sesiunii despre care ați petrecut ultimele 20 de capitole ciudate învățând și puteți
crea în continuare o aplicație web robustă. Dar pentru a termina treaba în mod corespunzător, va trebui să investiți puțin
timp suplimentar pentru a profila și optimiza performanța site-ului dvs. web. Una dintre cele mai ușoare modalități de
îmbunătățire a performanței este utilizarea cache-ului, o tehnică care stochează temporar informații valoroase în
memoria serverului, astfel încât să poată fi reutilizate. Spre deosebire de celelalte tipuri de management de stat despre
care ați aflat în capitolul 8, cache-ul include câteva caracteristici încorporate care asigură performanțe bune.
Desigur, stocarea informațiilor în memorie nu este întotdeauna o idee bună. Memoria serverului este o
resursă limitată; Dacă încercați să stocați prea mult, unele dintre aceste informații vor fi paginate pe disc,
ceea ce poate încetini întregul sistem. De aceea, cache-ul ASP.NET este autolimitant. Când stocați informații
într-o memorie cache, vă puteți aștepta să le găsiți acolo la o cerere viitoare, de cele mai multe ori. Cu toate
acestea, durata de viață a acestor informații este la discreția serverului. Dacă memoria cache devine plină
sau alte aplicații consumă o cantitate mare de memorie, datele vor fi evacuate selectiv din memoria cache,
asigurându-se că aplicația continuă să funcționeze bine. Această autosuficiență face cache-ul atât de
puternic (și l-ar face extrem de complicat de implementat pe cont propriu).
765
CAPITOLUL CACHE
23
Cache date (sau pagini web) care sunt scumpe: Cu alte cuvinte, cache informații care necesită mult timp
pentru a crea. Rezultatele unei interogări a bazei de date sau conținutul unui fișier sunt exemple bune. Nu
numai că este nevoie de timp pentru a deschide o conexiune la baza de date sau un fișier, dar poate, de
asemenea, să întârzie sau să blocheze alți utilizatori care încearcă să facă același lucru în același timp.
Memorați în cache date (sau pagini web) care sunt utilizate frecvent: Nu are rost să puneți deoparte
memoria pentru informații care nu vor mai fi necesare niciodată. De exemplu, puteți alege să nu
memorați în cache paginile cu detalii despre produse, deoarece există sute de produse diferite, fiecare
cu propria pagină. Dar are mai mult sens să memorați în cache lista categoriilor de produse, deoarece
aceste informații vor fi reutilizate pentru a servi multe solicitări diferite.
Dacă țineți cont de aceste două reguli, puteți obține două avantaje din memorarea în cache simultan:
puteți îmbunătăți atât performanța, cât și scalabilitatea.
Performanța este o măsură a cât de repede funcționează o pagină web pentru un singur utilizator. Cache-ul
îmbunătățește performanța, deoarece ocolește blocajele, cum ar fi baza de date. Ca urmare, paginile web sunt procesate
și trimise înapoi clientului mai rapid.
Scalabilitatea măsoară modul în care performanța aplicației dvs. web se degradează pe măsură ce tot mai
mulți oameni o folosesc în același timp. Memorarea în cache îmbunătățește scalabilitatea, deoarece vă
permite să reutilizați aceleași informații pentru solicitările care au loc în succesiune rapidă. Cu cache-ul, tot
mai mulți oameni pot utiliza site-ul dvs. web, dar numărul de călătorii la baza de date nu se va schimba
foarte mult. Prin urmare, sarcina globală asupra sistemului va rămâne relativ constantă, astfel cum se arată
în figura 23-1.
766